diff --git a/tildes/alembic/versions/3fbddcba0e3b_add_comment_remove_and_comment_unremove_.py b/tildes/alembic/versions/3fbddcba0e3b_add_comment_remove_and_comment_unremove_.py new file mode 100644 index 0000000..d9a796f --- /dev/null +++ b/tildes/alembic/versions/3fbddcba0e3b_add_comment_remove_and_comment_unremove_.py @@ -0,0 +1,36 @@ +"""Add COMMENT_REMOVE and COMMENT_UNREMOVE to logeventtype + +Revision ID: 3fbddcba0e3b +Revises: 6a635773de8f +Create Date: 2018-08-26 04:34:51.741972 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = "3fbddcba0e3b" +down_revision = "6a635773de8f" +branch_labels = None +depends_on = None + + +def upgrade(): + # ALTER TYPE doesn't work from inside a transaction, disable it + connection = None + if not op.get_context().as_sql: + connection = op.get_bind() + connection.execution_options(isolation_level="AUTOCOMMIT") + + op.execute("ALTER TYPE logeventtype ADD VALUE IF NOT EXISTS 'COMMENT_REMOVE'") + op.execute("ALTER TYPE logeventtype ADD VALUE IF NOT EXISTS 'COMMENT_UNREMOVE'") + + # re-activate the transaction for any future migrations + if connection is not None: + connection.execution_options(isolation_level="READ_COMMITTED") + + +def downgrade(): + # no way to remove enum values, just do nothing + pass diff --git a/tildes/tildes/enums.py b/tildes/tildes/enums.py index c69ece6..bd6b7f9 100644 --- a/tildes/tildes/enums.py +++ b/tildes/tildes/enums.py @@ -49,6 +49,8 @@ class LogEventType(enum.Enum): USER_REGISTER = enum.auto() COMMENT_POST = enum.auto() + COMMENT_REMOVE = enum.auto() + COMMENT_UNREMOVE = enum.auto() TOPIC_LOCK = enum.auto() TOPIC_MOVE = enum.auto() diff --git a/tildes/tildes/models/comment/comment.py b/tildes/tildes/models/comment/comment.py index 5a802fa..3860ba6 100644 --- a/tildes/tildes/models/comment/comment.py +++ b/tildes/tildes/models/comment/comment.py @@ -185,6 +185,9 @@ class Comment(DatabaseModel): # - logged-in users can mark comments read acl.append((Allow, Authenticated, "mark_read")) + # tools that require specifically granted permissions + acl.append((Allow, "admin", "remove")) + acl.append(DENY_ALL) return acl diff --git a/tildes/tildes/routes.py b/tildes/tildes/routes.py index 6321b6c..a29e09c 100644 --- a/tildes/tildes/routes.py +++ b/tildes/tildes/routes.py @@ -121,6 +121,9 @@ def add_intercooler_routes(config: Configurator) -> None: add_ic_route("topic_tags", "/topics/{topic_id36}/tags", factory=topic_by_id36) add_ic_route("comment", "/comments/{comment_id36}", factory=comment_by_id36) + add_ic_route( + "comment_remove", "/comments/{comment_id36}/remove", factory=comment_by_id36 + ) add_ic_route( "comment_replies", "/comments/{comment_id36}/replies", factory=comment_by_id36 ) diff --git a/tildes/tildes/templates/macros/comments.jinja2 b/tildes/tildes/templates/macros/comments.jinja2 index ea02df7..e25c65b 100644 --- a/tildes/tildes/templates/macros/comments.jinja2 +++ b/tildes/tildes/templates/macros/comments.jinja2 @@ -160,6 +160,30 @@ >Delete {% endif %} + {% if request.has_permission("remove", comment) %} +
  • + {% if not comment.is_removed %} + Remove + {% else %} + Un-remove + {% endif %} +
  • + {% endif %} + {% if request.has_permission('reply', comment) %}
  • Reply
  • {% endif %} diff --git a/tildes/tildes/views/api/web/comment.py b/tildes/tildes/views/api/web/comment.py index f46f2d2..3fb5f41 100644 --- a/tildes/tildes/views/api/web/comment.py +++ b/tildes/tildes/views/api/web/comment.py @@ -349,3 +349,27 @@ def put_mark_comments_read(request: Request, mark_all_previous: bool) -> Respons _increment_topic_comments_seen(request, notification.comment) return IC_NOOP + + +@ic_view_config(route_name="comment_remove", request_method="PUT", permission="remove") +def put_comment_remove(request: Request) -> Response: + """Remove a comment with Intercooler.""" + comment = request.context + + comment.is_removed = True + request.db_session.add(LogComment(LogEventType.COMMENT_REMOVE, request, comment)) + + return Response("Removed") + + +@ic_view_config( + route_name="comment_remove", request_method="DELETE", permission="remove" +) +def delete_comment_remove(request: Request) -> Response: + """Un-remove a comment with Intercooler.""" + comment = request.context + + comment.is_removed = False + request.db_session.add(LogComment(LogEventType.COMMENT_UNREMOVE, request, comment)) + + return Response("Un-removed")