add account deletion
This commit is contained in:
@@ -11,9 +11,9 @@ from ..auth import (
|
|||||||
digest, verify, create_session,
|
digest, verify, create_session,
|
||||||
is_logged_in, parse_username, is_password_valid,
|
is_logged_in, parse_username, is_password_valid,
|
||||||
login_required, revoke_session, get_active_user,
|
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 ..constants import PermissionLevel, InfoboxKind
|
||||||
from ..util import get_form_checkbox
|
from ..util import get_form_checkbox
|
||||||
import math
|
import math
|
||||||
@@ -55,6 +55,51 @@ def validate_and_create_avatar(input_image, filename):
|
|||||||
except WandException:
|
except WandException:
|
||||||
return False
|
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 redirect_if_logged_in(destination='topics.all_topics'):
|
||||||
def decorator(view_func):
|
def decorator(view_func):
|
||||||
@wraps(view_func)
|
@wraps(view_func)
|
||||||
@@ -378,3 +423,31 @@ def inbox(username):
|
|||||||
def bookmarks(username):
|
def bookmarks(username):
|
||||||
username = username.lower()
|
username = username.lower()
|
||||||
return 'stub'
|
return 'stub'
|
||||||
|
|
||||||
|
@bp.get('/<username>/delete-confirm/')
|
||||||
|
@login_required
|
||||||
|
@redirect_to_own
|
||||||
|
def delete_confirm(username):
|
||||||
|
return render_template('users/delete_confirm.html')
|
||||||
|
|
||||||
|
@bp.post('/<username>/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'))
|
||||||
|
|||||||
20
app/templates/users/delete_confirm.html
Normal file
20
app/templates/users/delete_confirm.html
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
{%- from 'common/macros.html' import subheader -%}
|
||||||
|
{%- extends 'base.html' -%}
|
||||||
|
{%- block title -%}account deletion confirmation{%- endblock -%}
|
||||||
|
{%- block content -%}
|
||||||
|
{%- set sub -%}
|
||||||
|
<a href="{{url_for('users.settings', username=get_active_user().username)}}">← Back to settings</a>
|
||||||
|
{%- endset -%}
|
||||||
|
{{- subheader('Confirm account deletion', sub) -}}
|
||||||
|
<div class="plank">
|
||||||
|
<form class="full-width" method="POST">
|
||||||
|
<p>Are you sure you want to delete your account on {{ config.SITE_NAME }}? <strong>This action is irreversible.</strong> 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.</p>
|
||||||
|
<p>If you wish for any and all content relating to you to be removed, you will have to <a href="{{url_for('guides.contact')}}" target="_blank">contact {{ config.SITE_NAME }}'s administrators separately.</a></p>
|
||||||
|
<p>If you are sure, please confirm your current password below.</p>
|
||||||
|
<label for="password">Confirm password</label>
|
||||||
|
{{csrf_input() | safe}}
|
||||||
|
<input type="password" id="password" name="password" required autocomplete="current-password">
|
||||||
|
<input type="submit" class="critical" value="Permanently delete account">
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
{%- endblock -%}
|
||||||
@@ -77,4 +77,8 @@
|
|||||||
<div>If badges fail to load, make sure JS is enabled.</div>
|
<div>If badges fail to load, make sure JS is enabled.</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
{%- endif -%}
|
{%- endif -%}
|
||||||
|
<fieldset class="plank">
|
||||||
|
<legend>Disown & Delete account</legend>
|
||||||
|
<a class="linkbutton critical" href="{{url_for('users.delete_confirm', username=user.username)}}">Delete account</a>
|
||||||
|
</fieldset>
|
||||||
{%- endblock -%}
|
{%- endblock -%}
|
||||||
|
|||||||
@@ -70,10 +70,12 @@
|
|||||||
{%- endfor -%}
|
{%- endfor -%}
|
||||||
</div>
|
</div>
|
||||||
{%- endif -%}
|
{%- endif -%}
|
||||||
|
{#
|
||||||
<fieldset class="plank secondary-bg minimal even no-shadow">
|
<fieldset class="plank secondary-bg minimal even no-shadow">
|
||||||
<legend>About me</legend>
|
<legend>About me</legend>
|
||||||
<p>stub</p>
|
<p>stub</p>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
#}
|
||||||
{%- if target_user.signature_rendered -%}
|
{%- if target_user.signature_rendered -%}
|
||||||
<fieldset class="plank secondary-bg minimal even no-shadow">
|
<fieldset class="plank secondary-bg minimal even no-shadow">
|
||||||
<legend>Signature</legend>
|
<legend>Signature</legend>
|
||||||
|
|||||||
Reference in New Issue
Block a user