From 4d18ae96f1652a44dd4761482c25df7836e0b891 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ji=C5=99=C3=AD=20Setni=C4=8Dka?= <setnicka@seznam.cz>
Date: Sat, 2 Jan 2021 18:18:35 +0100
Subject: [PATCH] =?UTF-8?q?P=C5=99id=C3=A1v=C3=A1n=C3=AD=20a=20odeb=C3=ADr?=
 =?UTF-8?q?=C3=A1n=C3=AD=20pr=C3=A1v=20v=20detailu=20organiz=C3=A1tor?=
 =?UTF-8?q?=C5=AF?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 mo/web/org_users.py                  | 85 ++++++++++++++++++++++++++--
 mo/web/templates/org_user.html       | 48 +++++++++++++++-
 mo/web/templates/org_users_orgs.html |  5 +-
 3 files changed, 129 insertions(+), 9 deletions(-)

diff --git a/mo/web/org_users.py b/mo/web/org_users.py
index 2d19ccb5..34700c94 100644
--- a/mo/web/org_users.py
+++ b/mo/web/org_users.py
@@ -5,6 +5,7 @@ import wtforms
 from sqlalchemy import or_
 
 from typing import Optional
+from wtforms import validators
 
 from wtforms.validators import Required
 
@@ -54,7 +55,6 @@ def org_users():
     if filter.year.data:
         participant_filter = participant_filter.filter_by(year=filter.year.data)
         participant_filter_apply = True
-    print(filter.school_code.data)
     if filter.school_code.data:
         place = db.place_by_code(filter.school_code.data)
         if place:
@@ -167,7 +167,22 @@ def org_users_orgs():
     )
 
 
-@app.route('/org/user/<int:id>/')
+class FormAddRole(FlaskForm):
+    role = wtforms.SelectField('Role', choices=[(name.name, role.name) for (name, role) in mo.rights.roles_by_type.items()])
+    place_code = wtforms.StringField('Oblast')
+    year = wtforms.IntegerField('Ročník', validators=[validators.Optional()])
+    category = wtforms.StringField("Kategorie")
+    seq = wtforms.IntegerField("Kolo", validators=[validators.Optional()])
+
+    submit = wtforms.SubmitField('Přidat roli')
+
+
+class FormRemoveRole(FlaskForm):
+    remove_role_id = wtforms.IntegerField()
+    remove = wtforms.SubmitField('Odebrat roli')
+
+
+@app.route('/org/user/<int:id>/', methods=('GET', 'POST'))
 def org_user(id: int):
     sess = db.get_session()
     user = sess.query(db.User).get(id)
@@ -176,13 +191,75 @@ def org_user(id: int):
 
     rr = mo.rights.Rights(g.user)
     rr.get_generic()
+    can_assign_rights = rr.have_right(mo.rights.Right.assign_rights)
 
     participants = sess.query(db.Participant).filter_by(user_id=user.user_id)
     rounds = sess.query(db.Participation).filter_by(user_id=user.user_id)
 
+    form_add_role = FormAddRole()
+    form_remove_role = FormRemoveRole()
+    role_errors = []
+    if can_assign_rights:
+        if form_add_role.submit.data and form_add_role.validate_on_submit():
+            new_role = db.UserRole()
+            form_add_role.populate_obj(new_role)
+
+            new_role.user_id = id
+            new_role.place = db.get_root_place()
+            new_role.assigned_by = g.user.user_id
+
+            ok = True
+            place_code = form_add_role.place_code.data
+            if place_code:
+                place = db.get_place_by_code(place_code)
+                if not place:
+                    role_errors.append("Nepovedlo se nalézt místo podle kódu")
+                    ok = False
+                else:
+                    new_role.place = place
+
+            if not rr.can_set_role(new_role):
+                role_errors.append(f'Roli "{new_role}" nelze přidělit, není podmnožinou žádné vaší role')
+                ok = False
+
+            if ok:
+                sess.add(new_role)
+                sess.flush()
+                mo.util.log(
+                    type=db.LogType.user_role,
+                    what=id,
+                    details={'action': 'new', 'role': db.row2dict(new_role)},
+                )
+                sess.commit()
+                app.logger.info(f"New role for user id {id} added: {db.row2dict(new_role)}")
+                flash(f'Role "{new_role}" úspěšně přidána', 'success')
+                return redirect(url_for('org_user', id=id))
+
+        if form_remove_role.remove_role_id.data and form_remove_role.validate_on_submit():
+            role = sess.query(db.UserRole).get(form_remove_role.remove_role_id.data)
+            if not role:
+                raise werkzeug.exceptions.NotFound
+
+            if id == g.user.user_id:
+                role_errors.append('Nelze odebrat vlastní roli')
+            elif not rr.can_set_role(role):
+                role_errors.append(f'Roli "{role}" nelze odebrat, není podmnožinou žádné vaší role')
+            else:
+                sess.delete(role)
+                mo.util.log(
+                    type=db.LogType.user_role,
+                    what=id,
+                    details={'action': 'delete', 'role': db.row2dict(role)},
+                )
+                sess.commit()
+                app.logger.info(f"Role for user {id} removed: {db.row2dict(role)}")
+                flash(f'Role "{role}" úspěšně odebrána', 'success')
+                return redirect(url_for('org_user', id=id))
+
     return render_template(
-        'org_user.html', user=user, can_edit=rr.can_edit_user(user),
-        participants=participants, rounds=rounds
+        'org_user.html', user=user, can_edit=rr.can_edit_user(user), can_assign_rights=can_assign_rights,
+        participants=participants, rounds=rounds, roles_by_type=mo.rights.roles_by_type,
+        form_add_role=form_add_role, form_remove_role=form_remove_role, role_errors=role_errors,
     )
 
 
diff --git a/mo/web/templates/org_user.html b/mo/web/templates/org_user.html
index ef1b6a22..fdd07adc 100644
--- a/mo/web/templates/org_user.html
+++ b/mo/web/templates/org_user.html
@@ -1,4 +1,5 @@
 {% extends "base.html" %}
+{% import "bootstrap/wtf.html" as wtf %}
 {% block body %}
 <h2>{% if user.is_org %}Organizátor:{% elif user.is_admin %}Správce:{% else %}Soutěžící:{% endif %} {{ user.first_name }} {{ user.last_name }}</h2>
 
@@ -20,19 +21,60 @@
 {% if user.is_org or user.is_admin %}
 <h3>Role</h3>
 
+{% if can_assign_rights %}
+<h4>Přidělení nové role</h4>
+<p>Lze přidělit jen roli, která je podmnožinou nějaké vlastní role (včetně omezení na oblast, kolo, &hellip;).</p>
+{% if role_errors %}
+<div class="alert alert-danger" role="alert">
+	{{ role_errors|join("<br>") }}
+</div>
+{% endif %}
+
+<form action="" method="POST" class="form form-inline" role="form">
+	{{ form_add_role.csrf_token() }}
+	<div class="form-group">
+		{{ wtf.form_field(form_add_role.role) }}
+	</div>
+	<div class="form-group">
+		{{ wtf.form_field(form_add_role.place_code, placeholder='Kód / #ID', size=8) }}
+	</div>
+	<div class="form-group">
+		{{ wtf.form_field(form_add_role.year, type='number', size=3, maxlength=2) }}
+	</div>
+	<div class="form-group">
+		{{ wtf.form_field(form_add_role.category, size=2, maxlength=2) }}
+	</div>
+	<div class="form-group">
+		{{ wtf.form_field(form_add_role.seq, type='number', size=3, maxlength=2) }}
+	</div>
+	<div class="form-group">
+		{{ wtf.form_field(form_add_role.submit) }}
+	</div>
+</form>
+{% endif %}
+
 <table class="data full">
 	<thead>
 		<tr>
-			<th>Role</th><th>Ročník</th><th>Kategorie</th><th>Kolo</th><th>Oblast</th>
+			<th>Role</th><th>Oblast</th><th>Ročník</th><th>Kategorie</th><th>Kolo</th><th>Akce</th>
 		</tr>
 	</thead>
 {% for role in user.roles %}
 	<tr>
-		<td>{{ role.role.name }}</td>
+		<td>{{ roles_by_type[role.role].name }}</td>
+		<td><a href="{{ url_for('org_place', id=role.place_id) }}">{{ role.place.type_name() + ": " + role.place.name or '*' }}</a></td>
 		<td>{{ role.year or '*' }}</td>
 		<td>{{ role.category or '*' }}</td>
 		<td>{{ role.seq or '*' }}</td>
-		<td>{{ role.place.type_name() + ": " + role.place.name or '*' }}</td>
+		<td>
+			{% if can_assign_rights %}
+				<form action="" method="POST">
+					{{ form_remove_role.csrf_token() }}
+					<input type="hidden" name="remove_role_id" value="{{ role.user_role_id }}">
+					<button type="submit" class="btn btn-xs btn-danger">Odebrat</button>
+				</form>
+			{% endif %}
+		</td>
 	</tr>
 {% endfor %}
 </table>
diff --git a/mo/web/templates/org_users_orgs.html b/mo/web/templates/org_users_orgs.html
index 0920ae7e..2987306b 100644
--- a/mo/web/templates/org_users_orgs.html
+++ b/mo/web/templates/org_users_orgs.html
@@ -48,10 +48,11 @@
 		<td>{{ user.first_name }}</td><td>{{ user.last_name }}</td>
 		<td><a href="mailto:{{ user.email }}">{{ user.email }}</a></td>
 		<td>{% if user.is_admin %}správce{% elif user.roles|count == 0 %}<i>žádná role</i>{% else %}
+			<ul>
 			{% for role in user.roles %}
-				{% if loop.index != 1 %}&rightarrow;{% endif %}
-				{% if role.year %}{{role.year}}-{% endif %}{% if role.category %}{{role.category}}-{% endif %}{{ role.role.name }}
+				<li>{{ role }}</li>
 			{%- endfor %}
+			</ul>
 		{% endif %}</td>
 		<td><div class='btn-group'>
 			<a class="btn btn-xs btn-default" href="{{ url_for('org_user', id=user.user_id) }}">Detail</a>
-- 
GitLab