diff --git a/tildes/tildes/models/comment/comment.py b/tildes/tildes/models/comment/comment.py index d706e3e..2eb060b 100644 --- a/tildes/tildes/models/comment/comment.py +++ b/tildes/tildes/models/comment/comment.py @@ -150,8 +150,6 @@ class Comment(DatabaseModel): else: acl.append((Allow, 'admin', 'reply')) - acl.append((Allow, Authenticated, 'mark_read')) - acl.append((Allow, self.user_id, 'edit')) acl.append((Allow, self.user_id, 'delete')) diff --git a/tildes/tildes/models/comment/comment_notification.py b/tildes/tildes/models/comment/comment_notification.py index 5e80d2f..40eff23 100644 --- a/tildes/tildes/models/comment/comment_notification.py +++ b/tildes/tildes/models/comment/comment_notification.py @@ -2,6 +2,8 @@ from datetime import datetime +from typing import Any, Sequence, Tuple +from pyramid.security import Allow, DENY_ALL from sqlalchemy import Boolean, Column, ForeignKey, Integer, TIMESTAMP from sqlalchemy.dialects.postgresql import ENUM from sqlalchemy.orm import relationship @@ -63,6 +65,14 @@ class CommentNotification(DatabaseModel): self.comment = comment self.notification_type = notification_type + + def __acl__(self) -> Sequence[Tuple[str, Any, str]]: + """Pyramid security ACL.""" + acl = [] + acl.append((Allow, self.user_id, 'mark_read')) + acl.append(DENY_ALL) + return acl + @property def is_comment_reply(self) -> bool: """Return whether this is a comment reply notification.""" diff --git a/tildes/tildes/resources/comment.py b/tildes/tildes/resources/comment.py index 231ccfa..7bcfa4e 100644 --- a/tildes/tildes/resources/comment.py +++ b/tildes/tildes/resources/comment.py @@ -1,10 +1,11 @@ """Root factories for comments.""" +from pyramid.httpexceptions import HTTPForbidden from pyramid.request import Request from webargs.pyramidparser import use_kwargs from tildes.lib.id import id36_to_id -from tildes.models.comment import Comment +from tildes.models.comment import Comment, CommentNotification from tildes.resources import get_resource from tildes.schemas.comment import CommentSchema @@ -19,3 +20,24 @@ def comment_by_id36(request: Request, comment_id36: str) -> Comment: query = request.query(Comment).filter_by(comment_id=comment_id) return get_resource(request, query) + +@use_kwargs( + CommentSchema(only=('comment_id36',)), + locations=('matchdict',), +) +def notification_by_comment_id36( + request: Request, + comment_id36: str +) -> Comment: + """Get a comment notification specified by {comment_id36} in the route (or 404).""" + + if not request.user: + raise HTTPForbidden + + comment_id = id36_to_id(comment_id36) + query = request.query(CommentNotification).filter_by( + user=request.user, + comment_id=comment_id, + ) + + return get_resource(request, query) diff --git a/tildes/tildes/routes.py b/tildes/tildes/routes.py index 38b025e..1e2afd7 100644 --- a/tildes/tildes/routes.py +++ b/tildes/tildes/routes.py @@ -6,7 +6,10 @@ from pyramid.config import Configurator from pyramid.request import Request from pyramid.security import Allow, Authenticated -from tildes.resources.comment import comment_by_id36 +from tildes.resources.comment import ( + comment_by_id36, + notification_by_comment_id36, +) from tildes.resources.group import group_by_path from tildes.resources.message import message_conversation_by_id36 from tildes.resources.topic import topic_by_id36 @@ -158,7 +161,7 @@ def add_intercooler_routes(config: Configurator) -> None: add_ic_route( 'comment_mark_read', '/comments/{comment_id36}/mark_read', - factory=comment_by_id36, + factory=notification_by_comment_id36, ) add_ic_route( diff --git a/tildes/tildes/views/api/web/comment.py b/tildes/tildes/views/api/web/comment.py index 2f33a89..ea43dd1 100644 --- a/tildes/tildes/views/api/web/comment.py +++ b/tildes/tildes/views/api/web/comment.py @@ -335,38 +335,30 @@ def put_mark_comments_read( 'new' count. If the query param mark_all_previous is Truthy all notifications prior to the target will be cleared. """ - comment = request.context + notification = request.context response = IC_NOOP - if mark_all_previous is True: + if mark_all_previous: prev_notifications = ( request.query(CommentNotification).filter( CommentNotification.is_unread == True, #noqa - CommentNotification.created_time <= - request.db_session.query(CommentNotification.created_time) - .filter( - CommentNotification.user == request.user, - CommentNotification.comment_id == comment.comment_id, - ) - .as_scalar() + CommentNotification.created_time <= notification.created_time ) .options(joinedload(CommentNotification.comment)) .all() ) - for notification in prev_notifications: - notification.is_unread = False - _increment_topic_comments_seen(request, notification.comment) + for comment_notification in prev_notifications: + comment_notification.is_unread = False + _increment_topic_comments_seen( + request, + comment_notification.comment + ) response = Response('Your comment notifications have been cleared.') else: - request.query(CommentNotification).filter( - CommentNotification.user == request.user, - CommentNotification.comment == comment, - ).update( - {CommentNotification.is_unread: False}, synchronize_session=False) - - _increment_topic_comments_seen(request, comment) + notification.is_unread = False + _increment_topic_comments_seen(request, notification.comment) return response