diff --git a/app/routes/users.py b/app/routes/users.py index 447ce8c..f6fcc34 100644 --- a/app/routes/users.py +++ b/app/routes/users.py @@ -11,9 +11,9 @@ from ..auth import ( digest, verify, create_session, is_logged_in, parse_username, is_password_valid, login_required, revoke_session, get_active_user, - parse_display_name, revoke_all_sessions + parse_display_name, revoke_all_sessions, csrf_verified ) -from ..models import Users, Posts, Reactions, Threads, Avatars +from ..models import Users, Posts, Reactions, Threads, Avatars, PostHistory, Mentions from ..constants import PermissionLevel, InfoboxKind from ..util import get_form_checkbox import math @@ -55,6 +55,51 @@ def validate_and_create_avatar(input_image, filename): except WandException: return False +def anonymize_user(user_id): + deleted_user = Users.find({'username': 'deleteduser'}) + + from ..lib.babycode import sanitize, babycode_to_html + from ..db import db + threads = Threads.findall({'user_id': user_id}) + posts = Posts.findall({'user_id': user_id}) + + revs_q = """SELECT DISTINCT m.revision_id FROM mentions m + WHERE m.mentioned_user_id = ?""" + + mentioned_revs = db.query(revs_q, int(user_id)) + with db.transaction(): + for thread in threads: + thread.update({'user_id': int(deleted_user.id)}) + for post in posts: + post.update({'user_id': int(deleted_user.id)}) + + revs = {} + for rev in mentioned_revs: + ph = PostHistory.find({'id': int(rev['revision_id'])}) + ms = Mentions.findall({ + 'mentioned_user_id': int(user_id), + 'revision_id': int(rev['revision_id']), + }) + data = { + 'text': sanitize(ph.original_markup), + 'mentions': ms, + } + data['mentions'] = sorted(data['mentions'], key=lambda x: int(x.end_index), reverse=True) + revs[rev['revision_id']] = data + + for rev_id, data in revs.items(): + text = data['text'] + for mention in data['mentions']: + text = text[:mention.start_index] + '@deleteduser' + text[mention.end_index:] + mention.delete() + + res = babycode_to_html(text) + ph = PostHistory.find({'id': int(rev_id)}) + ph.update({ + 'original_markup': text.unescape(), + 'content': res.result, + }) + def redirect_if_logged_in(destination='topics.all_topics'): def decorator(view_func): @wraps(view_func) @@ -378,3 +423,31 @@ def inbox(username): def bookmarks(username): username = username.lower() return 'stub' + +@bp.get('//delete-confirm/') +@login_required +@redirect_to_own +def delete_confirm(username): + return render_template('users/delete_confirm.html') + +@bp.post('//delete-confirm/') +@login_required +@redirect_to_own +@csrf_verified +def delete_confirm_post(username): + user = get_active_user() + + password = request.form.get('password', '') + if not verify(user.password_hash, password): + flash('Incorrect password.', InfoboxKind.ERROR) + return redirect(url_for('.delete_confirm', username=username)) + + if user.is_admin(): + flash('You can not delete the admin account.', InfoboxKind.ERROR) + return redirect(url_for('.delete_confirm', username=username)) + + anonymize_user(user.id) + revoke_all_sessions(user.id) + user.delete() + + return redirect(url_for('topics.all_topics')) diff --git a/app/templates/users/delete_confirm.html b/app/templates/users/delete_confirm.html new file mode 100644 index 0000000..e0e96cb --- /dev/null +++ b/app/templates/users/delete_confirm.html @@ -0,0 +1,20 @@ +{%- from 'common/macros.html' import subheader -%} +{%- extends 'base.html' -%} +{%- block title -%}account deletion confirmation{%- endblock -%} +{%- block content -%} +{%- set sub -%} +← Back to settings +{%- endset -%} +{{- subheader('Confirm account deletion', sub) -}} +
+
+

Are you sure you want to delete your account on {{ config.SITE_NAME }}? This action is irreversible. Your posts and threads will remain accessible to preserve history but will be de-personalized, showing up as authored by a system user. Posts that @mention you will also mention the system user instead.

+

If you wish for any and all content relating to you to be removed, you will have to contact {{ config.SITE_NAME }}'s administrators separately.

+

If you are sure, please confirm your current password below.

+ + {{csrf_input() | safe}} + + +
+
+{%- endblock -%} diff --git a/app/templates/users/settings.html b/app/templates/users/settings.html index e5174c9..07e5e1c 100644 --- a/app/templates/users/settings.html +++ b/app/templates/users/settings.html @@ -77,4 +77,8 @@
If badges fail to load, make sure JS is enabled.
{%- endif -%} +
+ Disown & Delete account + Delete account +
{%- endblock -%} diff --git a/app/templates/users/user_page.html b/app/templates/users/user_page.html index 3332486..e864e64 100644 --- a/app/templates/users/user_page.html +++ b/app/templates/users/user_page.html @@ -70,10 +70,12 @@ {%- endfor -%} {%- endif -%} + {#
About me

stub

+ #} {%- if target_user.signature_rendered -%}
Signature