aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Schadt <kingdread@gmx.de>2025-12-28 16:26:07 +0100
committerDaniel Schadt <kingdread@gmx.de>2025-12-30 19:16:32 +0100
commitd1fa0c056a623779c0c111805fd1cc91961fe3d2 (patch)
tree07bd434b51d83504bc13bdcc2fc91b00d6ab6bc7
parent1d23ffd3ad68a880a0d63a8e90902f5536915cd3 (diff)
downloadfietsboek-d1fa0c056a623779c0c111805fd1cc91961fe3d2.tar.gz
fietsboek-d1fa0c056a623779c0c111805fd1cc91961fe3d2.tar.bz2
fietsboek-d1fa0c056a623779c0c111805fd1cc91961fe3d2.zip
respect journeys' visibility levels
-rw-r--r--fietsboek/models/user.py34
-rw-r--r--fietsboek/templates/journey_form.jinja21
-rw-r--r--fietsboek/views/journey.py15
3 files changed, 46 insertions, 4 deletions
diff --git a/fietsboek/models/user.py b/fietsboek/models/user.py
index 19d5e71..6d1a53e 100644
--- a/fietsboek/models/user.py
+++ b/fietsboek/models/user.py
@@ -6,7 +6,7 @@ import hashlib
import secrets
import uuid
from functools import reduce
-from typing import TYPE_CHECKING, Optional
+from typing import TYPE_CHECKING, Optional, Self
from cryptography.exceptions import InvalidKey
from cryptography.hazmat.primitives.kdf.scrypt import Scrypt
@@ -34,6 +34,7 @@ from sqlalchemy.exc import NoResultFound
from sqlalchemy.orm import Mapped, Session, mapped_column, relationship, with_parent
from sqlalchemy.orm.attributes import flag_dirty
from sqlalchemy.orm.session import object_session
+from sqlalchemy.sql.expression import Selectable
from .meta import Base
@@ -398,6 +399,37 @@ class User(Base):
return union(*queries)
+ @staticmethod
+ def visible_journeys_query(user: Self | None = None) -> Selectable:
+ # Late import to avoid cycles
+ # pylint: disable=import-outside-toplevel
+ from .journey import Journey, Visibility
+
+ queries = []
+
+ # Step 1: Own journeys
+ if user:
+ queries.append(select(Journey).where(with_parent(user, User.journeys)))
+
+ # Step 2: Public journeys
+ queries.append(select(Journey).filter_by(visibility=Visibility.PUBLIC))
+
+ # Step 3: Journeys for logged in users
+ if user:
+ queries.append(select(Journey).filter_by(visibility=Visibility.LOGGED_IN))
+
+ # Step 4: Friends' journeys
+ if user:
+ friend_query = user._friend_query().subquery()
+ friend_ids = select(friend_query.c.id)
+ queries.append(
+ select(Journey)
+ .filter_by(visibility=Visibility.FRIENDS)
+ .where(Journey.owner_id.in_(friend_ids))
+ )
+
+ return union(*queries)
+
def _friend_query(self):
qry1 = select(User).filter(
friends_assoc.c.user_1_id == self.id, friends_assoc.c.user_2_id == User.id
diff --git a/fietsboek/templates/journey_form.jinja2 b/fietsboek/templates/journey_form.jinja2
index c641648..6fdbfaa 100644
--- a/fietsboek/templates/journey_form.jinja2
+++ b/fietsboek/templates/journey_form.jinja2
@@ -53,7 +53,6 @@
{% set visibility = journey.visibility.name if journey else "" %}
<option value="PRIVATE"{% if visibility== "PRIVATE" %} selected{% endif %}>{{ _("journeys.new.form.visibility.private") }}</option>
<option value="FRIENDS"{% if visibility== "FRIENDS" %} selected{% endif %}>{{ _("journeys.new.form.visibility.friends") }}</option>
- <option value="FRIENDS_TAGGED"{% if visibility== "FRIENDS_TAGGED" %} selected{% endif %}>{{ _("journeys.new.form.visibility.friends_tagged") }}</option>
<option value="LOGGED_IN"{% if visibility== "LOGGED_IN" %} selected{% endif %}>{{ _("journeys.new.form.visibility.logged_in") }}</option>
<option value="PUBLIC"{% if visibility== "PUBLIC" %} selected{% endif %}>{{ _("journeys.new.form.visibility.public") }}</option>
</select>
diff --git a/fietsboek/views/journey.py b/fietsboek/views/journey.py
index b763317..3a64fd3 100644
--- a/fietsboek/views/journey.py
+++ b/fietsboek/views/journey.py
@@ -7,6 +7,7 @@ from pyramid.request import Request
from pyramid.response import Response
from pyramid.view import view_config
from sqlalchemy import select
+from sqlalchemy.orm import aliased
from .. import trackmap, util
from ..models.journey import Journey, Visibility
@@ -22,7 +23,8 @@ LOGGER = logging.getLogger(__name__)
renderer="fietsboek:templates/journey_list.jinja2",
)
def journey_list(request: Request):
- journeys = request.dbsession.execute(select(Journey)).scalars()
+ query = select(aliased(Journey, User.visible_journeys_query(request.identity).subquery()))
+ journeys = request.dbsession.execute(query).scalars()
return {
"journeys": journeys,
"md_to_html": util.safe_markdown,
@@ -95,7 +97,7 @@ def do_journey_new(request: Request):
owner=request.identity,
title=request.params.get("journeyTitle"),
description=request.params.get("journeyDescription"),
- visibility=Visibility.PUBLIC,
+ visibility=_extract_visibility(request),
tracks=[],
)
@@ -130,6 +132,7 @@ def do_journey_edit(request: Request):
journey.title = request.params.get("journeyTitle")
journey.description = request.params.get("journeyDescription")
+ journey.visibility = _extract_visibility(request)
track_ids = _extract_valid_tracks(request)
journey.set_track_ids(track_ids)
@@ -139,6 +142,14 @@ def do_journey_edit(request: Request):
return HTTPFound(request.route_url("journey-details", journey_id=journey.id))
+def _extract_visibility(request: Request) -> Visibility:
+ key = request.params.get("journeyVisibility")
+ try:
+ return Visibility[key]
+ except KeyError:
+ raise HTTPBadRequest("Invalid visibility")
+
+
def _extract_valid_tracks(request: Request) -> list[int]:
user: User = request.identity