diff --git a/app/activitypub/routes.py b/app/activitypub/routes.py index f3fc3280..fa5f9db2 100644 --- a/app/activitypub/routes.py +++ b/app/activitypub/routes.py @@ -619,6 +619,7 @@ def process_inbox_request(request_json, activitypublog_id, ip_address): post = create_post(activity_log, community, request_json, user) if post: announce_activity_to_followers(community, user, request_json) + activity_log.result = 'success' except TypeError as e: activity_log.exception_message = 'TypeError. See log file.' current_app.logger.error('TypeError: ' + str(request_json)) @@ -1081,17 +1082,16 @@ def process_inbox_request(request_json, activitypublog_id, ip_address): activity_log.result = 'success' elif request_json['object']['type'] == 'Delete': # undoing a delete activity_log.activity_type = 'Restore' - reply = PostReply.query.filter_by(ap_id=request_json['object']['object']).first() - if reply: + post = Post.query.filter_by(ap_id=request_json['object']['object']).first() + if post: deletor = find_actor_or_create(request_json['object']['actor'], create_if_not_found=False) if deletor: - if reply.author.id == deletor.id or reply.community.is_moderator(deletor) or reply.community.is_instance_admin(deletor): - reply.deleted = False - reply.deleted_by = None - if not reply.author.bot: - reply.post.reply_count += 1 - reply.author.post_reply_count += 1 - announce_activity_to_followers(reply.community, reply.author, request_json) + if post.author.id == deletor.id or post.community.is_moderator(deletor) or post.community.is_instance_admin(deletor): + post.deleted = False + post.deleted_by = None + post.author.post_count += 1 + post.community.post_count += 1 + announce_activity_to_followers(post.community, post.author, request_json) db.session.commit() activity_log.result = 'success' else: @@ -1099,7 +1099,25 @@ def process_inbox_request(request_json, activitypublog_id, ip_address): else: activity_log.exception_message = 'Restorer did not already exist' else: - activity_log.exception_message = 'Reply not found, or object was not a reply' + reply = PostReply.query.filter_by(ap_id=request_json['object']['object']).first() + if reply: + deletor = find_actor_or_create(request_json['object']['actor'], create_if_not_found=False) + if deletor: + if reply.author.id == deletor.id or reply.community.is_moderator(deletor) or reply.community.is_instance_admin(deletor): + reply.deleted = False + reply.deleted_by = None + if not reply.author.bot: + reply.post.reply_count += 1 + reply.author.post_reply_count += 1 + announce_activity_to_followers(reply.community, reply.author, request_json) + db.session.commit() + activity_log.result = 'success' + else: + activity_log.exception_message = 'Restore attempt denied' + else: + activity_log.exception_message = 'Restorer did not already exist' + else: + activity_log.exception_message = 'Object not found, or object was not a post or a reply' elif request_json['type'] == 'Delete': if isinstance(request_json['object'], str): ap_id = request_json['object'] # lemmy diff --git a/app/activitypub/util.py b/app/activitypub/util.py index 3135ead9..3649ca7e 100644 --- a/app/activitypub/util.py +++ b/app/activitypub/util.py @@ -1381,7 +1381,6 @@ def restore_post_or_comment_task(object_json, aplog_id): if restorer and community and to_restore: if to_restore.author.id == restorer.id or restorer.is_admin() or community.is_moderator(restorer) or community.is_instance_admin(restorer): if isinstance(to_restore, Post): - # TODO: restore_dependencies() to_restore.deleted = False to_restore.deleted_by = None community.post_count += 1 diff --git a/app/post/routes.py b/app/post/routes.py index ef2a8f30..6f8bd647 100644 --- a/app/post/routes.py +++ b/app/post/routes.py @@ -670,14 +670,18 @@ def add_reply(post_id: int, comment_id: int): @bp.route('/post//options', methods=['GET']) def post_options(post_id: int): post = Post.query.get_or_404(post_id) - if current_user.is_anonymous or not current_user.is_admin(): - if post.deleted: + if post.deleted: + if current_user.is_anonymous: abort(404) + if (not post.community.is_moderator() and + not current_user.is_admin() and + (post.deleted_by is not None and post.deleted_by != current_user.id)): + abort(401) existing_bookmark = [] if current_user.is_authenticated: existing_bookmark = PostBookmark.query.filter(PostBookmark.post_id == post_id, PostBookmark.user_id == current_user.id).first() - + return render_template('post/post_options.html', post=post, existing_bookmark=existing_bookmark, moderating_communities=moderating_communities(current_user.get_id()), joined_communities=joined_communities(current_user.get_id()), @@ -1092,14 +1096,19 @@ def post_delete_post(community: Community, post: Post, user_id: int, federate_al @login_required def post_restore(post_id: int): post = Post.query.get_or_404(post_id) - if post.community.is_moderator() or post.community.is_owner() or current_user.is_admin(): + if post.user_id == current_user.id or post.community.is_moderator() or post.community.is_owner() or current_user.is_admin(): + if post.deleted_by == post.user_id: + was_mod_deletion = False + else: + was_mod_deletion = True post.deleted = False + post.deleted_by = None post.author.post_count += 1 post.community.post_count += 1 db.session.commit() # Federate un-delete - if post.is_local(): + if not post.community.local_only: delete_json = { "actor": current_user.public_url(), "to": ["https://www.w3.org/ns/activitystreams#Public"], @@ -1115,31 +1124,40 @@ def post_restore(post_id: int): ], 'object': post.ap_id, 'uri': post.ap_id, - "summary": "bad post", }, "cc": [post.community.public_url()], - "audience": post.author.public_url(), + "audience": post.community.public_url(), "type": "Undo", "id": f"https://{current_app.config['SERVER_NAME']}/activities/undo/{gibberish(15)}" } + if was_mod_deletion: + delete_json['object']['summary'] = "Deleted by mod" - announce = { - "id": f"https://{current_app.config['SERVER_NAME']}/activities/announce/{gibberish(15)}", - "type": 'Announce', - "to": [ - "https://www.w3.org/ns/activitystreams#Public" - ], - "actor": post.community.public_url(), - "cc": [ - post.community.ap_followers_url - ], - '@context': default_context(), - 'object': delete_json - } + if not post.community.is_local(): # this is a remote community, send it to the instance that hosts it + if not was_mod_deletion or (was_mod_deletion and post.community.is_moderator(current_user)): + success = post_request(post.community.ap_inbox_url, delete_json, current_user.private_key, + current_user.public_url() + '#main-key') + if success is False or isinstance(success, str): + flash('Failed to send delete to remote server', 'error') - for instance in post.community.following_instances(): - if instance.inbox and not current_user.has_blocked_instance(instance.id) and not instance_banned(instance.domain): - send_to_remote_instance(instance.id, post.community.id, announce) + else: # local community - send it to followers on remote instances + announce = { + "id": f"https://{current_app.config['SERVER_NAME']}/activities/announce/{gibberish(15)}", + "type": 'Announce', + "to": [ + "https://www.w3.org/ns/activitystreams#Public" + ], + "actor": post.community.public_url(), + "cc": [ + post.community.ap_followers_url + ], + '@context': default_context(), + 'object': delete_json + } + + for instance in post.community.following_instances(): + if instance.inbox and not current_user.has_blocked_instance(instance.id) and not instance_banned(instance.domain): + send_to_remote_instance(instance.id, post.community.id, announce) if post.user_id != current_user.id: add_to_modlog('restore_post', community_id=post.community.id, link_text=shorten_string(post.title), @@ -1149,6 +1167,23 @@ def post_restore(post_id: int): return redirect(url_for('activitypub.post_ap', post_id=post.id)) +@bp.route('/post//purge', methods=['GET', 'POST']) +@login_required +def post_purge(post_id: int): + post = Post.query.get_or_404(post_id) + if not post.deleted: + abort(404) + if post.deleted_by == current_user.id or post.community.is_moderator() or current_user.is_admin(): + post.delete_dependencies() + db.session.delete(post) + db.session.commit() + flash(_('Post purged.')) + else: + abort(401) + + return redirect(url_for('user.show_profile_by_id', user_id=post.user_id)) + + @bp.route('/post//bookmark', methods=['GET', 'POST']) @login_required def post_bookmark(post_id: int): @@ -1749,9 +1784,7 @@ def post_reply_purge(post_id: int, comment_id: int): post_reply = PostReply.query.get_or_404(comment_id) if not post_reply.deleted: abort(404) - if post_reply.user_id == current_user.id and (post_reply.deleted_by is None or post_reply.deleted_by != post_reply.user_id): - abort(401) - if post_reply.user_id == current_user.id or post.community.is_moderator() or current_user.is_admin(): + if post_reply.deleted_by == current_user.id or post.community.is_moderator() or current_user.is_admin(): if not post_reply.has_replies(): post_reply.delete_dependencies() db.session.delete(post_reply) diff --git a/app/templates/post/post_options.html b/app/templates/post/post_options.html index 782e5f78..7c4a41eb 100644 --- a/app/templates/post/post_options.html +++ b/app/templates/post/post_options.html @@ -19,8 +19,10 @@ {% endif -%} {% if post.user_id == current_user.id or post.community.is_moderator() or post.community.is_owner() or current_user.is_admin() -%} {% if post.deleted -%} -
  • +
  • {{ _('Restore') }}
  • +
  • + {{ _('Purge') }}
  • {% else -%}
  • {{ _('Delete') }}