titles: add displayed title (#65)

- with forms for user and admins
This commit is contained in:
Darks 2020-07-29 00:57:06 +02:00
parent b79bac1fc4
commit b108ce4cfe
Signed by: Darks
GPG Key ID: F61F10FA138E797C
11 changed files with 103 additions and 3 deletions

View File

@ -110,6 +110,14 @@ class UpdateAccountForm(FlaskForm):
Optional(),
]
)
title = SelectField(
'Titre',
coerce=int,
validators=[
Optional(),
vd.own_title,
]
)
newsletter = BooleanField(
'Inscription à la newsletter',
description='Un mail par trimestre environ, pour être prévenu des concours, évènements et nouveautés.',
@ -226,6 +234,14 @@ class AdminUpdateAccountForm(FlaskForm):
Optional(),
],
)
title = SelectField(
'Titre',
coerce=int,
validators=[
Optional(),
# Admin can set any title to any member!
]
)
newsletter = BooleanField(
'Inscription à la newsletter',
description='Un mail par trimestre environ, pour être prévenu des concours, évènements et nouveautés.',

View File

@ -7,7 +7,7 @@ from PIL import Image
from app import app, db
from app.models.privs import SpecialPrivilege, Group, GroupMember, \
GroupPrivilege
from app.models.trophies import Trophy, TrophyMember
from app.models.trophies import Trophy, TrophyMember, Title
from app.models.notification import Notification
import app.utils.unicode_names as unicode_names
from app.utils.notify import notify
@ -105,6 +105,10 @@ class Member(User):
signature = db.Column(db.UnicodeText)
birthday = db.Column(db.Date)
# Displayed title, if set
title_id = db.Column(db.Integer, db.ForeignKey('title.id'), nullable=True)
title = db.relationship('Title', foreign_keys=title_id)
# Settings
newsletter = db.Column(db.Boolean, default=False)
@ -204,6 +208,8 @@ class Member(User):
self.newsletter = data["newsletter"]
if "avatar" in data:
self.set_avatar(data["avatar"])
if "title" in data:
self.title = Title.query.get(data["title"])
# For admins only
if "email_confirmed" in data:

View File

@ -4,6 +4,7 @@ from app import app, db
from app.forms.account import UpdateAccountForm, RegistrationForm, \
DeleteAccountForm, AskResetPasswordForm, ResetPasswordForm
from app.models.users import Member
from app.models.trophies import Title
from app.utils.render import render
from app.utils.send_mail import send_validation_mail, send_reset_password_mail
from app.utils.priv_required import guest_only
@ -16,6 +17,9 @@ from config import V5Config
@login_required
def edit_account():
form = UpdateAccountForm()
titles = [(t.id, t.name) for t in current_user.trophies if isinstance(t, Title)]
titles.insert(0, (-1, "Membre"))
form.title.choices = titles
if form.submit.data:
if form.validate_on_submit():
current_user.update(
@ -25,6 +29,7 @@ def edit_account():
birthday=form.birthday.data,
signature=form.signature.data,
bio=form.biography.data,
title=form.title.data,
newsletter=form.newsletter.data
)
db.session.merge(current_user)

View File

@ -3,7 +3,7 @@ from flask_login import current_user
from wtforms import BooleanField
from app.utils.priv_required import priv_required
from app.models.users import Member
from app.models.trophies import Trophy
from app.models.trophies import Trophy, Title
from app.models.privs import Group
from app.forms.account import AdminUpdateAccountForm, AdminDeleteAccountForm, \
AdminAccountEditTrophyForm, AdminAccountEditGroupForm
@ -35,6 +35,10 @@ def adm_edit_account(user_id):
setattr(GroupForm, "user_groups", [f'g{g.id}' for g in user.groups])
group_form = GroupForm(prefix="group")
titles = [(t.id, t.name) for t in Title.query.all()]
titles.insert(0, (-1, "Membre"))
form.title.choices = titles
if form.submit.data:
if form.validate_on_submit():
newname = form.username.data
@ -52,6 +56,7 @@ def adm_edit_account(user_id):
password=form.password.data or None,
birthday=form.birthday.data,
signature=form.signature.data,
title=form.title.data,
bio=form.biography.data,
newsletter=form.newsletter.data,
xp=form.xp.data or None,

View File

@ -26,6 +26,7 @@
.form input[type='password'],
.form input[type='search'],
.form textarea,
.form select,
.trophies-panel > div {
display: block;
width: 100%; padding: 6px 8px;
@ -49,6 +50,9 @@
max-width: 100%;
resize: vertical;
}
.form select {
width: auto;
}
.form progress.entropy {
display: none; /* display with Js enabled */

View File

@ -3,6 +3,7 @@
.profile {
display: flex;
align-items: center;
width: 265px;
}
.profile-avatar {
width: 128px;

View File

@ -51,6 +51,13 @@
</div>
<h2>À propos</h2>
<div>
{{ form.title.label }}
{{ form.title }}
{% for error in form.title.errors %}
<span class="msgerror">{{ error }}</span>
{% endfor %}
</div>
<div>
{{ form.birthday.label }}
{{ form.birthday(value=current_user.birthday) }}

View File

@ -61,6 +61,13 @@
</div>
<h2>À propos</h2>
<div>
{{ form.title.label }}
{{ form.title }}
{% for error in form.title.errors %}
<span class="msgerror">{{ error }}</span>
{% endfor %}
</div>
<div>
{{ form.birthday.label }}
{{ form.birthday(value=user.birthday) }}

View File

@ -4,7 +4,7 @@
<img class="profile-avatar" src="{{ url_for('avatar', filename=user.avatar) }}" alt="Avatar de {{ user.name }}">
<div>
<div class="profile-name"><a href="{{ url_for('user', username=user.name) }}">{{ user.name }}</a></div>
<div class="profile-title">Membre</div>
<div class="profile-title">{{ user.title.name if user.title else "Membre" }}</div>
<div class="profile-points">Niveau {{ user.level[0] }} <span>({{ user.xp }})</span></div>
<div class="profile-points-small">N{{ user.level[0] }} <span>({{ user.xp }})</span></div>
<div class="profile-xp"><div style='width: {{ user.level[1] }}%;'></div></div>

View File

@ -2,9 +2,11 @@ from flask_login import current_user
from wtforms.validators import ValidationError
from PIL import Image
from app.models.users import Member, User
from app.models.trophies import Title
from app.utils.valid_name import valid_name
from app.utils.unicode_names import normalize
from math import log
from werkzeug.exceptions import NotFound
import app.utils.ldap as ldap
from config import V5Config
@ -117,3 +119,20 @@ def id_exists(object):
def css(form, css):
"""Check if input is valid and sane CSS"""
pass
def own_title(form, title):
# Everyone can use "Member"
if title.data == -1:
return True
try:
t = Title.query.get_or_404(title.data)
except NotFound:
return False
except ValueError:
return False
if t in current_user.trophies:
return True
else:
return False

View File

@ -0,0 +1,30 @@
"""Add displayed title to Member
Revision ID: 001d2eaf0413
Revises: acf72cf31eea
Create Date: 2020-07-29 00:13:27.775769
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = '001d2eaf0413'
down_revision = 'acf72cf31eea'
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('member', sa.Column('title_id', sa.Integer(), nullable=True))
op.create_foreign_key(None, 'member', 'title', ['title_id'], ['id'])
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_constraint(None, 'member', type_='foreignkey')
op.drop_column('member', 'title_id')
# ### end Alembic commands ###