diff --git a/mo/web/org_users.py b/mo/web/org_users.py
index 683c0f792e14eb1bed26454724905c16a9ba0dbd..47b7991966d82aa10ab48430adb45fc4807cc3fa 100644
--- a/mo/web/org_users.py
+++ b/mo/web/org_users.py
@@ -664,3 +664,93 @@ def org_orgs_import():
year=mo.config.CURRENT_YEAR if form.only_this_year.data else None
)
return generic_import_page(form, imp, url_for('org_orgs_import'), template='org_global_orgs_import.html')
+
+
+class ConfirmDeleteForm(FlaskForm):
+ delete = SubmitField('Potvrdit smazání')
+
+
+@app.route('/org/user/<int:user_id>/delete', methods=('GET', 'POST'))
+def org_user_delete(user_id: int):
+ if not g.user.is_admin:
+ raise werkzeug.exceptions.Forbidden()
+
+ sess = db.get_session()
+ user = sess.query(db.User).get(user_id)
+ if not user:
+ raise werkzeug.exceptions.NotFound()
+
+ warnings = []
+ errors = []
+
+ pants = sess.query(db.Participant).filter_by(user=user).all()
+ for pant in pants:
+ warnings.append(f'Účastní se {pant.year}. ročníku')
+
+ pions = (sess.query(db.Participation)
+ .filter_by(user=user)
+ # .options(joinedload(db.Participation.contest, db.Participation.contest.round))
+ .all())
+ for pion in pions:
+ warnings.append(f'Účastní se kola {pion.contest.round.round_code()}')
+
+ num_roles = sess.query(db.UserRole).filter_by(user=user).count()
+ if num_roles > 0:
+ warnings.append(f'Má přidělené role ({num_roles})')
+
+ sols = (sess.query(db.Solution)
+ .filter_by(user=user)
+ # .options(joinedload(db.Solution.task, db.Solution.task.round))
+ .all())
+ for sol in sols:
+ errors.append(f'Odevzdal úlohu {sol.task.code} v kole {sol.task.round.round_code()}')
+
+ num_papers = sess.query(db.Paper).filter_by(for_user_obj=user).count()
+ if num_papers:
+ errors.append(f'Patří mu řešení/opravy ({num_papers})')
+
+ num_uploads = sess.query(db.Paper).filter_by(uploaded_by_obj=user).count()
+ if num_uploads:
+ errors.append(f'Nahrál řešení/opravy ({num_uploads})')
+
+ logs = sess.query(db.Log).filter_by(user=user).all()
+ num_good_logs = 0
+ num_bad_logs = 0
+ for log in logs:
+ if log.details.get('reason', "") == 'user-join':
+ num_good_logs += 1
+ else:
+ num_bad_logs += 1
+ if num_good_logs > 0:
+ warnings.append(f'Vlastní záznamy v logu z registrace ({num_good_logs})')
+ if num_bad_logs > 0:
+ errors.append(f'Vlastní záznamy v logu ({num_bad_logs})')
+
+ form = ConfirmDeleteForm()
+ if form.validate_on_submit() and not errors:
+ sess.rollback()
+
+ conn = sess.connection()
+ pant_table = db.Participation.__table__
+ pion_table = db.Participant.__table__
+ role_table = db.UserRole.__table__
+ log_table = db.Log.__table__
+ conn.execute(pant_table.delete().where(pant_table.c.user_id == user.user_id))
+ conn.execute(pion_table.delete().where(pion_table.c.user_id == user.user_id))
+ conn.execute(role_table.delete().where(role_table.c.user_id == user.user_id))
+ conn.execute(log_table.delete().where(log_table.c.changed_by == user.user_id))
+ sess.commit()
+
+ mo.util.log(
+ type=db.LogType.user,
+ what=user.user_id,
+ details={'action': 'delete', 'user': db.row2dict(user)},
+ )
+ sess.delete(user)
+ sess.commit()
+
+ app.logger.info(f"Uživatel #{user.user_id} smazán")
+ flash('Uživatel smazán.', 'danger')
+ return redirect(url_for('org_orgs') if user.is_org else url_for('org_users'))
+
+ return render_template('org_user_delete.html', user=user, form=form, warnings=warnings, errors=errors)
diff --git a/mo/web/templates/org_org.html b/mo/web/templates/org_org.html
index f242e2780b03f5d2b731b27f89c91516fd78dfbb..9c3c36f3bb0eeb23efa66dcb84a0459f3a4d0c61 100644
--- a/mo/web/templates/org_org.html
+++ b/mo/web/templates/org_org.html
@@ -31,6 +31,7 @@
{% endif %}
{% if g.user.is_admin %}
<a class="btn btn-default" href="{{ log_url('user', user.user_id) }}">Historie</a>
+ <a class="btn btn-danger" href="{{ url_for('org_user_delete', user_id=user.user_id) }}">Smazat</a>
{% endif %}
{% if can_incarnate %}
<form action="{{ url_for('incarnate', id=user.user_id) }}" method=POST class='btn-group'>
diff --git a/mo/web/templates/org_user.html b/mo/web/templates/org_user.html
index edf1f7684ee37d97e80886c2064c33a45a2b7c7d..965bcb753c81442df73353810ec62c7eb20654de 100644
--- a/mo/web/templates/org_user.html
+++ b/mo/web/templates/org_user.html
@@ -39,6 +39,7 @@
{% endif %}
{% if g.user.is_admin %}
<a class="btn btn-default" href="{{ log_url('user', user.user_id) }}">Historie</a>
+ <a class="btn btn-danger" href="{{ url_for('org_user_delete', user_id=user.user_id) }}">Smazat</a>
{% endif %}
{% if can_incarnate %}
<form action="{{ url_for('incarnate', id=user.user_id) }}" method=POST class='btn-group'>
diff --git a/mo/web/templates/org_user_delete.html b/mo/web/templates/org_user_delete.html
new file mode 100644
index 0000000000000000000000000000000000000000..5867867956ff9ac31532a0805fe87143d2083586
--- /dev/null
+++ b/mo/web/templates/org_user_delete.html
@@ -0,0 +1,28 @@
+{% extends "base.html" %}
+{% import "bootstrap/wtf.html" as wtf %}
+{% block title %}Smazat uživatele: {{ user.full_name() }}{% endblock %}
+{% block body %}
+
+{% if warnings %}
+<h3>Varováni</h3>
+<ul>
+ {% for w in warnings %}
+ <li>{{ w }}
+ {% endfor %}
+</ul>
+{% endif %}
+
+{% if errors %}
+<h3>Chyby</h3>
+<ul>
+ {% for e in errors %}
+ <li>{{ e }}
+ {% endfor %}
+</ul>
+{% endif %}
+
+{% if form and not errors %}
+{{ wtf.quick_form(form, form_type='simple', button_map={'delete': 'danger'}) }}
+{% endif %}
+
+{% endblock %}