post soft-deletion: add options to restore or purge deleted posts

This commit is contained in:
freamon 2024-10-26 03:49:16 +00:00
parent 9a033522d1
commit 502e6ff0f6
4 changed files with 90 additions and 38 deletions

View file

@ -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

View file

@ -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

View file

@ -670,9 +670,13 @@ def add_reply(post_id: int, comment_id: int):
@bp.route('/post/<int:post_id>/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:
@ -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/<int:post_id>/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/<int:post_id>/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)

View file

@ -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 -%}
<li><a href="{{ url_for('post.post_restore', post_id=post.id) }}" class="no-underline confirm_first" rel="nofollow"><span class="fe fe-delete"></span>
<li><a href="{{ url_for('post.post_restore', post_id=post.id) }}" class="no-underline confirm_first" rel="nofollow"><span class="fe fe-arrow-up"></span>
{{ _('Restore') }}</a></li>
<li><a href="{{ url_for('post.post_purge', post_id=post.id) }}" class="no-underline confirm_first" rel="nofollow"><span class="fe fe-delete red"></span>
{{ _('Purge') }}</a></li>
{% else -%}
<li><a href="{{ url_for('post.post_delete', post_id=post.id) }}" class="no-underline confirm_first" rel="nofollow"><span class="fe fe-delete"></span>
{{ _('Delete') }}</a></li>