mirror of
https://codeberg.org/rimu/pyfedi
synced 2025-01-23 19:36:56 -08:00
post soft-deletion: add options to restore or purge deleted posts
This commit is contained in:
parent
9a033522d1
commit
502e6ff0f6
4 changed files with 90 additions and 38 deletions
|
@ -619,6 +619,7 @@ def process_inbox_request(request_json, activitypublog_id, ip_address):
|
||||||
post = create_post(activity_log, community, request_json, user)
|
post = create_post(activity_log, community, request_json, user)
|
||||||
if post:
|
if post:
|
||||||
announce_activity_to_followers(community, user, request_json)
|
announce_activity_to_followers(community, user, request_json)
|
||||||
|
activity_log.result = 'success'
|
||||||
except TypeError as e:
|
except TypeError as e:
|
||||||
activity_log.exception_message = 'TypeError. See log file.'
|
activity_log.exception_message = 'TypeError. See log file.'
|
||||||
current_app.logger.error('TypeError: ' + str(request_json))
|
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'
|
activity_log.result = 'success'
|
||||||
elif request_json['object']['type'] == 'Delete': # undoing a delete
|
elif request_json['object']['type'] == 'Delete': # undoing a delete
|
||||||
activity_log.activity_type = 'Restore'
|
activity_log.activity_type = 'Restore'
|
||||||
reply = PostReply.query.filter_by(ap_id=request_json['object']['object']).first()
|
post = Post.query.filter_by(ap_id=request_json['object']['object']).first()
|
||||||
if reply:
|
if post:
|
||||||
deletor = find_actor_or_create(request_json['object']['actor'], create_if_not_found=False)
|
deletor = find_actor_or_create(request_json['object']['actor'], create_if_not_found=False)
|
||||||
if deletor:
|
if deletor:
|
||||||
if reply.author.id == deletor.id or reply.community.is_moderator(deletor) or reply.community.is_instance_admin(deletor):
|
if post.author.id == deletor.id or post.community.is_moderator(deletor) or post.community.is_instance_admin(deletor):
|
||||||
reply.deleted = False
|
post.deleted = False
|
||||||
reply.deleted_by = None
|
post.deleted_by = None
|
||||||
if not reply.author.bot:
|
post.author.post_count += 1
|
||||||
reply.post.reply_count += 1
|
post.community.post_count += 1
|
||||||
reply.author.post_reply_count += 1
|
announce_activity_to_followers(post.community, post.author, request_json)
|
||||||
announce_activity_to_followers(reply.community, reply.author, request_json)
|
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
activity_log.result = 'success'
|
activity_log.result = 'success'
|
||||||
else:
|
else:
|
||||||
|
@ -1099,7 +1099,25 @@ def process_inbox_request(request_json, activitypublog_id, ip_address):
|
||||||
else:
|
else:
|
||||||
activity_log.exception_message = 'Restorer did not already exist'
|
activity_log.exception_message = 'Restorer did not already exist'
|
||||||
else:
|
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':
|
elif request_json['type'] == 'Delete':
|
||||||
if isinstance(request_json['object'], str):
|
if isinstance(request_json['object'], str):
|
||||||
ap_id = request_json['object'] # lemmy
|
ap_id = request_json['object'] # lemmy
|
||||||
|
|
|
@ -1381,7 +1381,6 @@ def restore_post_or_comment_task(object_json, aplog_id):
|
||||||
if restorer and community and to_restore:
|
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 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):
|
if isinstance(to_restore, Post):
|
||||||
# TODO: restore_dependencies()
|
|
||||||
to_restore.deleted = False
|
to_restore.deleted = False
|
||||||
to_restore.deleted_by = None
|
to_restore.deleted_by = None
|
||||||
community.post_count += 1
|
community.post_count += 1
|
||||||
|
|
|
@ -670,14 +670,18 @@ def add_reply(post_id: int, comment_id: int):
|
||||||
@bp.route('/post/<int:post_id>/options', methods=['GET'])
|
@bp.route('/post/<int:post_id>/options', methods=['GET'])
|
||||||
def post_options(post_id: int):
|
def post_options(post_id: int):
|
||||||
post = Post.query.get_or_404(post_id)
|
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)
|
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 = []
|
existing_bookmark = []
|
||||||
if current_user.is_authenticated:
|
if current_user.is_authenticated:
|
||||||
existing_bookmark = PostBookmark.query.filter(PostBookmark.post_id == post_id, PostBookmark.user_id == current_user.id).first()
|
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,
|
return render_template('post/post_options.html', post=post, existing_bookmark=existing_bookmark,
|
||||||
moderating_communities=moderating_communities(current_user.get_id()),
|
moderating_communities=moderating_communities(current_user.get_id()),
|
||||||
joined_communities=joined_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
|
@login_required
|
||||||
def post_restore(post_id: int):
|
def post_restore(post_id: int):
|
||||||
post = Post.query.get_or_404(post_id)
|
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 = False
|
||||||
|
post.deleted_by = None
|
||||||
post.author.post_count += 1
|
post.author.post_count += 1
|
||||||
post.community.post_count += 1
|
post.community.post_count += 1
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
# Federate un-delete
|
# Federate un-delete
|
||||||
if post.is_local():
|
if not post.community.local_only:
|
||||||
delete_json = {
|
delete_json = {
|
||||||
"actor": current_user.public_url(),
|
"actor": current_user.public_url(),
|
||||||
"to": ["https://www.w3.org/ns/activitystreams#Public"],
|
"to": ["https://www.w3.org/ns/activitystreams#Public"],
|
||||||
|
@ -1115,31 +1124,40 @@ def post_restore(post_id: int):
|
||||||
],
|
],
|
||||||
'object': post.ap_id,
|
'object': post.ap_id,
|
||||||
'uri': post.ap_id,
|
'uri': post.ap_id,
|
||||||
"summary": "bad post",
|
|
||||||
},
|
},
|
||||||
"cc": [post.community.public_url()],
|
"cc": [post.community.public_url()],
|
||||||
"audience": post.author.public_url(),
|
"audience": post.community.public_url(),
|
||||||
"type": "Undo",
|
"type": "Undo",
|
||||||
"id": f"https://{current_app.config['SERVER_NAME']}/activities/undo/{gibberish(15)}"
|
"id": f"https://{current_app.config['SERVER_NAME']}/activities/undo/{gibberish(15)}"
|
||||||
}
|
}
|
||||||
|
if was_mod_deletion:
|
||||||
|
delete_json['object']['summary'] = "Deleted by mod"
|
||||||
|
|
||||||
announce = {
|
if not post.community.is_local(): # this is a remote community, send it to the instance that hosts it
|
||||||
"id": f"https://{current_app.config['SERVER_NAME']}/activities/announce/{gibberish(15)}",
|
if not was_mod_deletion or (was_mod_deletion and post.community.is_moderator(current_user)):
|
||||||
"type": 'Announce',
|
success = post_request(post.community.ap_inbox_url, delete_json, current_user.private_key,
|
||||||
"to": [
|
current_user.public_url() + '#main-key')
|
||||||
"https://www.w3.org/ns/activitystreams#Public"
|
if success is False or isinstance(success, str):
|
||||||
],
|
flash('Failed to send delete to remote server', 'error')
|
||||||
"actor": post.community.public_url(),
|
|
||||||
"cc": [
|
|
||||||
post.community.ap_followers_url
|
|
||||||
],
|
|
||||||
'@context': default_context(),
|
|
||||||
'object': delete_json
|
|
||||||
}
|
|
||||||
|
|
||||||
for instance in post.community.following_instances():
|
else: # local community - send it to followers on remote instances
|
||||||
if instance.inbox and not current_user.has_blocked_instance(instance.id) and not instance_banned(instance.domain):
|
announce = {
|
||||||
send_to_remote_instance(instance.id, post.community.id, 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:
|
if post.user_id != current_user.id:
|
||||||
add_to_modlog('restore_post', community_id=post.community.id, link_text=shorten_string(post.title),
|
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))
|
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'])
|
@bp.route('/post/<int:post_id>/bookmark', methods=['GET', 'POST'])
|
||||||
@login_required
|
@login_required
|
||||||
def post_bookmark(post_id: int):
|
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)
|
post_reply = PostReply.query.get_or_404(comment_id)
|
||||||
if not post_reply.deleted:
|
if not post_reply.deleted:
|
||||||
abort(404)
|
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):
|
if post_reply.deleted_by == current_user.id or post.community.is_moderator() or current_user.is_admin():
|
||||||
abort(401)
|
|
||||||
if post_reply.user_id == current_user.id or post.community.is_moderator() or current_user.is_admin():
|
|
||||||
if not post_reply.has_replies():
|
if not post_reply.has_replies():
|
||||||
post_reply.delete_dependencies()
|
post_reply.delete_dependencies()
|
||||||
db.session.delete(post_reply)
|
db.session.delete(post_reply)
|
||||||
|
|
|
@ -19,8 +19,10 @@
|
||||||
{% endif -%}
|
{% 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.user_id == current_user.id or post.community.is_moderator() or post.community.is_owner() or current_user.is_admin() -%}
|
||||||
{% if post.deleted -%}
|
{% 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>
|
{{ _('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 -%}
|
{% 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>
|
<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>
|
{{ _('Delete') }}</a></li>
|
||||||
|
|
Loading…
Add table
Reference in a new issue