diff --git a/app/api/alpha/routes.py b/app/api/alpha/routes.py index e1d30dc3..5eccf6e8 100644 --- a/app/api/alpha/routes.py +++ b/app/api/alpha/routes.py @@ -4,7 +4,7 @@ from app.api.alpha.utils import get_site, post_site_block, \ get_post_list, get_post, post_post_like, put_post_save, put_post_subscribe, post_post, \ put_post, post_post_delete, post_post_report, post_post_lock, post_post_feature, post_post_remove, \ get_reply_list, post_reply_like, put_reply_save, put_reply_subscribe, post_reply, put_reply, \ - post_reply_delete, post_reply_report, \ + post_reply_delete, post_reply_report, post_reply_remove, \ get_community_list, get_community, post_community_follow, post_community_block, \ get_user, post_user_block from app.shared.auth import log_user_in @@ -237,12 +237,12 @@ def post_alpha_post_feature(): def post_alpha_post_remove(): if not enable_api(): return jsonify({'error': 'alpha api is not enabled'}) - #try: - auth = request.headers.get('Authorization') - data = request.get_json(force=True) or {} - return jsonify(post_post_remove(auth, data)) - #except Exception as ex: - # return jsonify({"error": str(ex)}), 400 + try: + auth = request.headers.get('Authorization') + data = request.get_json(force=True) or {} + return jsonify(post_post_remove(auth, data)) + except Exception as ex: + return jsonify({"error": str(ex)}), 400 # Reply @@ -342,6 +342,18 @@ def post_alpha_comment_report(): return jsonify({"error": str(ex)}), 400 +@bp.route('/api/alpha/comment/remove', methods=['POST']) +def post_alpha_comment_remove(): + if not enable_api(): + return jsonify({'error': 'alpha api is not enabled'}) + try: + auth = request.headers.get('Authorization') + data = request.get_json(force=True) or {} + return jsonify(post_reply_remove(auth, data)) + except Exception as ex: + return jsonify({"error": str(ex)}), 400 + + # User @bp.route('/api/alpha/user', methods=['GET']) def get_alpha_user(): @@ -415,7 +427,6 @@ def alpha_post(): # Reply - not yet implemented @bp.route('/api/alpha/comment', methods=['GET']) # Stage 1 if needed for search -@bp.route('/api/alpha/comment/remove', methods=['POST']) # Stage 1 @bp.route('/api/alpha/comment/mark_as_read', methods=['POST']) # No DB support @bp.route('/api/alpha/comment/distinguish', methods=['POST']) # Not really used @bp.route('/api/alpha/comment/report/resolve', methods=['PUT']) # Stage 2 diff --git a/app/api/alpha/utils/__init__.py b/app/api/alpha/utils/__init__.py index 944e8164..8b015d27 100644 --- a/app/api/alpha/utils/__init__.py +++ b/app/api/alpha/utils/__init__.py @@ -1,7 +1,7 @@ from app.api.alpha.utils.site import get_site, post_site_block from app.api.alpha.utils.misc import get_search from app.api.alpha.utils.post import get_post_list, get_post, post_post_like, put_post_save, put_post_subscribe, post_post, put_post, post_post_delete, post_post_report, post_post_lock, post_post_feature, post_post_remove -from app.api.alpha.utils.reply import get_reply_list, post_reply_like, put_reply_save, put_reply_subscribe, post_reply, put_reply, post_reply_delete, post_reply_report +from app.api.alpha.utils.reply import get_reply_list, post_reply_like, put_reply_save, put_reply_subscribe, post_reply, put_reply, post_reply_delete, post_reply_report, post_reply_remove from app.api.alpha.utils.community import get_community, get_community_list, post_community_follow, post_community_block from app.api.alpha.utils.user import get_user, post_user_block diff --git a/app/api/alpha/utils/reply.py b/app/api/alpha/utils/reply.py index 436b29fb..ea598269 100644 --- a/app/api/alpha/utils/reply.py +++ b/app/api/alpha/utils/reply.py @@ -3,7 +3,7 @@ from app.api.alpha.utils.validators import required, integer_expected, boolean_e from app.api.alpha.views import reply_view, reply_report_view from app.models import PostReply, Post from app.shared.reply import vote_for_reply, bookmark_the_post_reply, remove_the_bookmark_from_post_reply, toggle_post_reply_notification, make_reply, edit_reply, \ - delete_reply, restore_reply, report_reply + delete_reply, restore_reply, report_reply, mod_remove_reply, mod_restore_reply from app.utils import authorise_api_user, blocked_users, blocked_instances from sqlalchemy import desc @@ -191,3 +191,22 @@ def post_reply_report(auth, data): reply_json = reply_report_view(report=report, reply_id=reply_id, user_id=user_id) return reply_json + +def post_reply_remove(auth, data): + required(['comment_id', 'removed'], data) + integer_expected(['comment_id'], data) + boolean_expected(['removed'], data) + string_expected(['reason'], data) + + reply_id = data['comment_id'] + removed = data['removed'] + + if removed == True: + reason = data['reason'] if 'reason' in data else 'Removed by mod' + user_id, reply = mod_remove_reply(reply_id, reason, SRC_API, auth) + else: + reason = data['reason'] if 'reason' in data else 'Restored by mod' + user_id, reply = mod_restore_reply(reply_id, reason, SRC_API, auth) + + reply_json = reply_view(reply=reply, variant=4, user_id=user_id) + return reply_json diff --git a/app/shared/reply.py b/app/shared/reply.py index 8e6c0560..7470fad9 100644 --- a/app/shared/reply.py +++ b/app/shared/reply.py @@ -5,7 +5,7 @@ from app.constants import * from app.models import Instance, Notification, NotificationSubscription, Post, PostReply, PostReplyBookmark, Report, Site, User, utcnow from app.shared.tasks import task_selector from app.utils import gibberish, instance_banned, render_template, authorise_api_user, recently_upvoted_post_replies, recently_downvoted_post_replies, shorten_string, \ - piefed_markdown_to_lemmy_markdown, markdown_to_html, ap_datetime + piefed_markdown_to_lemmy_markdown, markdown_to_html, ap_datetime, add_to_modlog_activitypub from flask import abort, current_app, flash, redirect, request, url_for from flask_babel import _ @@ -337,3 +337,66 @@ def report_reply(reply_id, input, src, auth=None): return user_id, report else: return + + +# mod deletes +def mod_remove_reply(reply_id, reason, src, auth): + if src == SRC_API: + user = authorise_api_user(auth, return_type='model') + else: + user = current_user + + reply = PostReply.query.filter_by(id=reply_id, deleted=False).one() + if not reply.community.is_moderator(user) and not reply.community.is_instance_admin(user): + raise Exception('Does not have permission') + + reply.deleted = True + reply.deleted_by = user.id + if not reply.author.bot: + reply.post.reply_count -= 1 + reply.author.post_reply_count -= 1 + db.session.commit() + if src == SRC_WEB: + flash(_('Comment deleted.')) + + add_to_modlog_activitypub('delete_post_reply', user, community_id=reply.community_id, + link_text=shorten_string(f'comment on {shorten_string(reply.post.title)}'), + link=f'post/{reply.post_id}#comment_{reply.id}', reason=reason) + + task_selector('delete_reply', user_id=user.id, reply_id=reply.id, reason=reason) + + if src == SRC_API: + return user.id, reply + else: + return + + +def mod_restore_reply(reply_id, reason, src, auth): + if src == SRC_API: + user = authorise_api_user(auth, return_type='model') + else: + user = current_user + + reply = PostReply.query.filter_by(id=reply_id, deleted=True).one() + if not reply.community.is_moderator(user) and not reply.community.is_instance_admin(user): + raise Exception('Does not have permission') + + reply.deleted = False + reply.deleted_by = None + if not reply.author.bot: + reply.post.reply_count += 1 + reply.author.post_reply_count += 1 + db.session.commit() + if src == SRC_WEB: + flash(_('Comment restored.')) + + add_to_modlog_activitypub('restore_post_reply', user, community_id=reply.community_id, + link_text=shorten_string(f'comment on {shorten_string(reply.post.title)}'), + link=f'post/{reply.post_id}#comment_{reply.id}', reason=reason) + + task_selector('restore_reply', user_id=user.id, reply_id=reply.id, reason=reason) + + if src == SRC_API: + return user.id, reply + else: + return diff --git a/app/shared/tasks/deletes.py b/app/shared/tasks/deletes.py index 6d0aa0f6..9cf5f7f3 100644 --- a/app/shared/tasks/deletes.py +++ b/app/shared/tasks/deletes.py @@ -26,13 +26,13 @@ For Announce, remove @context from inner object, and use same fields except audi @celery.task def delete_reply(send_async, user_id, reply_id, reason=None): reply = PostReply.query.filter_by(id=reply_id).one() - delete_object(user_id, reply) + delete_object(user_id, reply, reason=reason) @celery.task def restore_reply(send_async, user_id, reply_id, reason=None): reply = PostReply.query.filter_by(id=reply_id).one() - delete_object(user_id, reply, is_restore=True) + delete_object(user_id, reply, is_restore=True, reason=reason) @celery.task