diff --git a/app/forms/account.py b/app/forms/account.py index e9bee41..236547a 100644 --- a/app/forms/account.py +++ b/app/forms/account.py @@ -125,14 +125,24 @@ class UpdateAccountForm(FlaskForm): submit = SubmitField('Mettre à jour') -class DeleteAccountForm(FlaskForm): +class DeleteAccountBaseForm(FlaskForm): + transfer = BooleanField( + 'Conserver les posts sous forme anonyme', + description="Aucune information personnelle n'est conservée ; seul le texte de posts reste. Cela permet de garder un historique fidèle des échanges.", + default=True + ) delete = BooleanField( 'Confirmer la suppression', validators=[ InputRequired(), ], - description='Attention, cette opération est irréversible !' + description='Attention, cette opération est irréversible !' ) + submit = SubmitField( + 'Supprimer le compte', + ) + +class DeleteAccountForm(DeleteAccountBaseForm): old_password = PasswordField( 'Mot de passe', validators=[ @@ -140,10 +150,6 @@ class DeleteAccountForm(FlaskForm): vd.password.old_password, ], ) - submit = SubmitField( - 'Supprimer le compte', - ) - class AskResetPasswordForm(FlaskForm): email = EmailField( @@ -267,14 +273,5 @@ class AdminAccountEditGroupForm(FlaskForm): ) -class AdminDeleteAccountForm(FlaskForm): - delete = BooleanField( - 'Confirmer la suppression', - validators=[ - InputRequired(), - ], - description='Attention, cette opération est irréversible !', - ) - submit = SubmitField( - 'Supprimer le compte', - ) +class AdminDeleteAccountForm(DeleteAccountBaseForm): + pass diff --git a/app/models/trophy.py b/app/models/trophy.py index e626707..ae23de5 100644 --- a/app/models/trophy.py +++ b/app/models/trophy.py @@ -29,8 +29,9 @@ class Trophy(db.Model): self.hidden = hidden def delete(self): - for tm in TrophyMember.query.filter_by(tid=self.id).all(): - db.session.delete(tm) + for owner in owners: + owner.del_trophy(self) + db.session.add(owner) db.session.commit() db.session.delete(self) diff --git a/app/models/user.py b/app/models/user.py index c0df9e3..d4d5218 100644 --- a/app/models/user.py +++ b/app/models/user.py @@ -135,18 +135,50 @@ class Member(User): self.signature = "" self.birthday = None + def generate_guest_name(self): + """Generates a unique guest name to transfer contents to.""" + count = 0 + while Guest.query.filter_by(name=f"{self.name}_{count}").first(): + count += 1 + return f"{self.name}_{count}" + + def transfer_posts(self, other): + """ + Transfers all the posts to another user. This is generally used to + transfer ownership to a newly-created Guest before deleting an account. + """ + for t in self.topics: + t.author = other + db.session.add(t) + for p in self.programs: + p.author = other + db.session.add(p) + for c in self.comments: + c.author = other + db.session.add(c) + + def delete_posts(self): + """Deletes the user's posts.""" + for t in self.topics: + t.delete() + for p in self.programs: + p.delete() + for c in self.comments: + c.delete() + def delete(self): """ - Deletes the user and the associated information: - * Special privileges + Deletes the user, but not the posts; use either transfer_posts() or + delete_posts() before calling this. """ - for sp in SpecialPrivilege.query.filter_by(mid=self.id).all(): db.session.delete(sp) + + self.trophies = [] + db.session.add(self) db.session.commit() db.session.delete(self) - db.session.commit() # Privilege checks diff --git a/app/routes/account/account.py b/app/routes/account/account.py index 241f91a..e15b00e 100644 --- a/app/routes/account/account.py +++ b/app/routes/account/account.py @@ -3,7 +3,7 @@ from flask_login import login_required, current_user, logout_user from app import app, db from app.forms.account import UpdateAccountForm, RegistrationForm, \ DeleteAccountForm, AskResetPasswordForm, ResetPasswordForm -from app.models.user import Member +from app.models.user import Guest, Member from app.models.trophy import Title from app.utils.render import render from app.utils.send_mail import send_validation_mail, send_reset_password_mail @@ -88,9 +88,20 @@ def reset_password(token): @login_required def delete_account(): del_form = DeleteAccountForm() + if del_form.submit.data: if del_form.validate_on_submit(): - db.session.delete(current_user) + if del_form.transfer.data: + guest = Guest(current_user.generate_guest_name()) + db.session.add(guest) + db.session.commit() + current_user.transfer_posts(guest) + db.session.commit() + else: + current_user.delete_posts() + db.session.commit() + + current_user.delete() logout_user() db.session.commit() flash('Compte supprimé', 'ok') @@ -98,6 +109,7 @@ def delete_account(): else: flash('Erreur lors de la suppression du compte', 'error') del_form.delete.data = False # Force to tick to delete the account + return render('account/delete_account.html', del_form=del_form) diff --git a/app/routes/admin/account.py b/app/routes/admin/account.py index cfe7108..3cd065f 100644 --- a/app/routes/admin/account.py +++ b/app/routes/admin/account.py @@ -2,7 +2,7 @@ from flask import flash, redirect, url_for, request from flask_login import current_user from wtforms import BooleanField from app.utils.priv_required import priv_required -from app.models.user import Member +from app.models.user import Guest, Member from app.models.trophy import Trophy, Title from app.models.priv import Group from app.forms.account import AdminUpdateAccountForm, AdminDeleteAccountForm, \ @@ -123,22 +123,38 @@ def adm_edit_account(user_id): @app.route('/admin/compte//supprimer', methods=['GET', 'POST']) @priv_required('misc.admin-panel', 'delete.accounts') def adm_delete_account(user_id): + # A user deleting their own account will be disconnected user = Member.query.filter_by(id=user_id).first_or_404() - # Note: A user deleting their own account will be disconnected. + # TODO: Number of comments by *other* members which will be deleted + stats = { + 'comments': len(user.comments), + 'topics': len(user.topics), + 'programs': len(user.programs), + 'groups': len(user.groups), + 'privs': len(user.special_privileges()), + } - # TODO: Add an overview of what will be deleted. - # * How many posts will be turned into guest posts - # * Option: purely delete the posts in question - # * How many PMs will be deleted (can't unassign PMs) - # * etc. del_form = AdminDeleteAccountForm() if del_form.submit.data: if del_form.validate_on_submit(): + if del_form.transfer.data: + guest = Guest(user.generate_guest_name()) + db.session.add(guest) + db.session.commit() + user.transfer_posts(guest) + db.session.commit() + else: + user.delete_posts() + db.session.commit() + user.delete() + db.session.commit() flash('Compte supprimé', 'ok') return redirect(url_for('adm')) else: flash('Erreur lors de la suppression du compte', 'error') del_form.delete.data = False # Force to tick to delete the account - return render('admin/delete_account.html', user=user, del_form=del_form) + + return render('admin/delete_account.html', user=user, stats=stats, + del_form=del_form) diff --git a/app/templates/account/delete_account.html b/app/templates/account/delete_account.html index 5ad5260..e4f09ca 100644 --- a/app/templates/account/delete_account.html +++ b/app/templates/account/delete_account.html @@ -6,6 +6,13 @@
{{ del_form.hidden_tag() }}
+ {{ del_form.transfer.label }} + {{ del_form.transfer() }} +
{{ del_form.transfer.description }}
+ {% for error in del_form.transfer.errors %} + {{ error }} + {% endfor %} +
{{ del_form.delete.label }} {{ del_form.delete(checked=False) }}
{{ del_form.delete.description }}
diff --git a/app/templates/admin/delete_account.html b/app/templates/admin/delete_account.html index 6df1cd7..a22b3be 100644 --- a/app/templates/admin/delete_account.html +++ b/app/templates/admin/delete_account.html @@ -7,16 +7,29 @@ {% block content %}

Confirmer la suppression du compte

-

Le compte '{{ user.name }}' que vous allez supprimer est lié à :

+

Le compte '{{ user.name }}' possède les posts (migrables) suivants :

    -
  • {{ user.groups | length }} groupe{{ user.groups|length|pluralize }}
  • -
  • {% set sp = user.special_privileges() | length %} - {{- sp }} privilège{{sp|pluralize}} spéci{{sp|pluralize("al","aux")}}
  • +
  • {{ stats.comments }} commentaire{{ stats.comments | pluralize }}
  • +
  • {{ stats.topics }} topic{{ stats.topics | pluralize }}
  • +
  • {{ stats.programs }} topic{{ stats.programs | pluralize }}
  • +
+

Les propriétés suivantes seront supprimées :

+
    +
  • {{ stats.groups }} groupe{{ stats.groups | pluralize }}
  • +
  • {{ stats.privs }} privilège{{ stats.privs | pluralize }} + spéci{{ stats.privs | pluralize("al","aux") }}
{{ del_form.hidden_tag() }}
+ {{ del_form.transfer.label }} + {{ del_form.transfer() }} +
{{ del_form.transfer.description }}
+ {% for error in del_form.transfer.errors %} + {{ error }} + {% endfor %} +
{{ del_form.delete.label }} {{ del_form.delete(checked=False) }}
{{ del_form.delete.description }}