Ajout des trophées et du panel pour les gérer

This commit is contained in:
Darks 2019-06-07 01:44:04 +02:00
parent 79e3189f4b
commit f67129a36b
Signed by: Darks
GPG Key ID: F61F10FA138E797C
9 changed files with 202 additions and 37 deletions

View File

@ -1,12 +1,16 @@
from flask_wtf import FlaskForm
from wtforms import StringField, BooleanField, SubmitField
from wtforms import StringField, SubmitField, BooleanField
from wtforms.validators import DataRequired, Optional
from flask_wtf.file import FileField # Cuz' wtforms' FileField is shitty
class CreateTrophyForm(FlaskForm):
class TrophyForm(FlaskForm):
name = StringField('Nom', validators=[DataRequired()])
icon = FileField('Icone')
is_title = BooleanField('Le trophée est aussi un titre')
title = StringField('Titre', description='Titre affiché dans le cas échéant', validators=[Optional()])
submit = SubmitField('Créer le trophée')
title = StringField('Titre', description='Titre affiché dans le cas échéant. Laisser vide pour un simple trophée.', validators=[Optional()])
css = StringField('CSS', description='CSS appliqué au titre, le cas échéant.')
submit = SubmitField('Envoyer')
class DeleteTrophyForm(FlaskForm):
delete = BooleanField('Confirmer la suppression', validators=[DataRequired()], description='Attention, cette opération est irréversible!')
submit = SubmitField('Supprimer le trophée')

View File

@ -11,7 +11,7 @@ class Trophy(db.Model):
'polymorphic_on': type
}
# Standalone properties
name = db.Column(db.Text(convert_unicode=True))
name = db.Column(db.Unicode(64), index=True)
owners = db.relationship('Member', secondary=lambda: TrophyMember,
back_populates='trophies')
@ -24,11 +24,13 @@ class Title(Trophy):
__mapper_args__ = {'polymorphic_identity': __tablename__}
id = db.Column(db.Integer, db.ForeignKey('trophy.id'), primary_key=True)
title = db.Column(db.Text(convert_unicode=True))
title = db.Column(db.Unicode(64))
css = db.Column(db.Text(convert_unicode=True))
def __init__(self, name, title):
def __init__(self, name, title, css):
self.name = name
self.title = title
self.css = css
# Many-to-many relation for users earning trophies

View File

@ -6,7 +6,7 @@ from app.utils.render import render
from app import app, db
@app.route('/admin/edit-account/<user_id>', methods=['GET', 'POST'])
@app.route('/admin/account/<user_id>/edit', methods=['GET', 'POST'])
@priv_required('access-admin-panel', 'edit-account')
def adm_edit_account(user_id):
user = Member.query.filter_by(id=user_id).first_or_404()
@ -42,7 +42,7 @@ def adm_edit_account(user_id):
return render('admin/edit_account.html', user=user, form=form)
@app.route('/admin/edit-account/<user_id>/delete', methods=['GET', 'POST'])
@app.route('/admin/account/<user_id>/delete', methods=['GET', 'POST'])
@priv_required('access-admin-panel', 'delete-account')
def adm_delete_account(user_id):
user = Member.query.filter_by(id=user_id).first_or_404()

View File

@ -1,29 +1,71 @@
from flask import request, flash
from flask import request, flash, redirect, url_for
from app.utils.priv_required import priv_required
from app.models.trophies import Trophy, Title
from app.forms.trophies import CreateTrophyForm
from app.forms.trophies import TrophyForm, DeleteTrophyForm
from app.utils.render import render
from app import app, db
@app.route('/admin/trophies', methods=['GET', 'POST'])
@priv_required('access-admin-panel', )
@priv_required('access-admin-panel', 'edit-trophies')
def adm_trophies():
form = CreateTrophyForm()
form = TrophyForm()
if request.method == "POST":
if form.validate_on_submit():
if form.is_title.data:
trophy = Title(form.name.data, form.title.data)
type = 'titre'
is_title = form.title.data != ""
if is_title:
trophy = Title(form.name.data, form.title.data, form.css.data)
else:
trophy = Trophy(form.name.data)
type = 'trophée'
db.session.add(trophy)
db.session.commit()
flash(f'Nouveau {type} ajoutée', 'ok')
flash(f'Nouveau {["trophée", "titre"][is_title]} ajouté', 'ok')
else:
flash('Erreur lors de la création du trophée', 'error')
trophies = Trophy.query.all()
return render('admin/trophies.html', trophies=trophies,
form=form)
@app.route('/admin/trophies/<trophy_id>/edit', methods=['GET', 'POST'])
@priv_required('access-admin-panel', 'edit-trophies')
def adm_edit_trophy(trophy_id):
trophy = Trophy.query.filter_by(id=trophy_id).first_or_404()
form = TrophyForm()
if request.method == "POST":
if form.validate_on_submit():
is_title = form.title.data != ""
if is_title:
trophy.name = form.name.data
trophy.title = form.title.data
trophy.css = form.css.data
else:
trophy.name = form.name.data
db.session.merge(trophy)
db.session.commit()
flash(f'{["Trophée", "Titre"][is_title]} modifié', 'ok')
return redirect(url_for('adm_trophies'))
else:
flash('Erreur lors de la création du trophée', 'error')
return render('admin/edit_trophy.html', trophy=trophy, form=form)
@app.route('/admin/trophies/<trophy_id>/delete', methods=['GET', 'POST'])
@priv_required('access-admin-panel', 'edit-trophies')
def adm_delete_trophy(trophy_id):
trophy = Trophy.query.filter_by(id=trophy_id).first_or_404()
# TODO: Add an overview of what will be deleted.
del_form = DeleteTrophyForm()
if request.method == "POST":
if del_form.validate_on_submit():
db.session.delete(trophy)
db.session.commit()
flash('Trophée supprimé', 'ok')
return redirect(url_for('adm_trophies'))
else:
flash('Erreur lors de la suppression du trophée', 'error')
del_form.delete.data = False # Force to tick to delete the account
return render('admin/delete_trophy.html', trophy=trophy, del_form=del_form)

View File

@ -0,0 +1,28 @@
{% extends "base/base.html" %}
{% block title %}
<a href="{{ url_for('adm') }}">Panneau d'administration</a> » <a href={{ url_for('adm_trophies') }}>Titres et trophées</a> » <h1>Suppression du trophée '{{ trophy.name }}'</h1>
{% endblock %}
{% block content %}
<section class="form">
<h2>Confirmer la suppression du trophée</h2>
<p>Le trophée '{{ trophy.name }}' que vous allez supprimer est lié à :</p>
<ul>
<li>{{ trophy.owners | length }} membre{{ trophy.owners|length|pluralize }}</li>
</ul>
<form action="{{ url_for('adm_delete_trophy', trophy_id=trophy.id) }}" method=post>
{{ del_form.hidden_tag() }}
<div>
{{ del_form.delete.label }}
{{ del_form.delete(checked=False) }}
<div style="font-size: 80%; color: gray">{{ del_form.delete.description }}</div>
{% for error in del_form.delete.errors %}
<span class=msgerror>{{ error }}</span>
{% endfor %}
</div>
<div>{{ del_form.submit(class_="bg-red") }}</div>
</form>
</section>
{% endblock %}

View File

@ -0,0 +1,38 @@
{% extends "base/base.html" %}
{% block title %}
<a href="{{ url_for('adm') }}">Panneau d'administration</a> » <a href={{ url_for('adm_trophies') }}>Titres et trophées</a> » <h1>Édition du trophée '{{ trophy.name }}'</h1>
{% endblock %}
{% block content %}
<section class="form">
<form action="{{ url_for('adm_edit_trophy', trophy_id=trophy.id) }}" method="post" enctype="multipart/form-data">
{{ form.hidden_tag() }}
<h2>Éditer le trophée</h2>
<div>
{{ form.name.label }}
{{ form.name(value=trophy.name) }}
{% for error in form.name.errors %}
<span class="msgerror">{{ error }}</span>
{% endfor %}
</div>
<div>
{{ form.title.label }}
{{ form.title(value=trophy.title) }}
<div class=desc>{{ form.title.description }}</div>
{% for error in form.title.errors %}
<span class="msgerror">{{ error }}</span>
{% endfor %}
</div>
<div>
{{ form.css.label }}
{{ form.css(value=trophy.css) }}
<div class=desc>{{ form.css.description }}</div>
{% for error in form.css.errors %}
<span class="msgerror">{{ error }}</span>
{% endfor %}
</div>
<div>{{ form.submit(class_="bg-green") }}</div>
</section>
{% endblock %}

View File

@ -11,13 +11,17 @@
<h2>Titres et trophées</h2>
<table style="width:90%; margin: auto;">
<tr><th>Icône</th><th>Nom</th><th>Titre</th><th>Modifier</th></tr>
<tr><th>id</th><th>Icône</th><th>Nom</th><th>Titre</th>
<th>Style</th><th>Modifier</th><th>Supprimer</th></tr>
{% for trophy in trophies %}
<tr><td><img src="#" alt="{{ trophy.name }}"></td>
<tr><td>{{ trophy.id }}</td>
<td><img src="{{ url_for('static', filename='images/account-circle.svg') }}" alt="{{ trophy.name }}"></td>
<td>{{ trophy.name }}</td>
<td>{{ trophy.title }}</td>
<td><a href="{# {{ url_for('adm_edit_trophy', trophy_id=trophy.id) }} #}">Modifier</a></td>
<td style="{{ trophy.css }}">{{ trophy.title }}</td>
<td>{{ trophy.css }}</td>
<td><a href="{{ url_for('adm_edit_trophy', trophy_id=trophy.id) }}">Modifier</a></td>
<td><a href="{{ url_for('adm_delete_trophy', trophy_id=trophy.id) }}">Supprimer</a></td>
</tr>
{% endfor %}
</table>
@ -27,30 +31,29 @@
<form action="{{ url_for('adm_trophies') }}" method="post" enctype="multipart/form-data">
{{ form.hidden_tag() }}
<h2>Nouveau trophée</h2>
<div>
<div>
{{ form.name.label }}
{{ form.name }}
{% for error in form.name.errors %}
<span class="msgerror">{{ error }}</span>
{% endfor %}
</div>
<div>
{{ form.is_title.label }}
{{ form.is_title }}
<div class=desc>{{ form.is_title.description }}</div>
{% for error in form.is_title.errors %}
<span class="msgerror">{{ error }}</span>
{% endfor %}
</div>
<div>
<div>
{{ form.title.label }}
{{ form.title }}
<div class=desc>{{ form.title.description }}</div>
<div class=desc>{{ form.title.description }}</div>
{% for error in form.title.errors %}
<span class="msgerror">{{ error }}</span>
{% endfor %}
</div>
<div>{{ form.submit(class_="bg-green") }}</div>
<div>
{{ form.css.label }}
{{ form.css }}
<div class=desc>{{ form.css.description }}</div>
{% for error in form.css.errors %}
<span class="msgerror">{{ error }}</span>
{% endfor %}
</div>
<div>{{ form.submit(class_="bg-green") }}</div>
</section>
{% endblock %}

View File

@ -1,6 +1,6 @@
from flask_login import current_user
from wtforms.validators import ValidationError
from app.models.users import User, Member
from app.models.users import Member
from app.utils.valid_name import valid_name
from app.utils.unicode_names import normalize
from config import V5Config
@ -65,3 +65,21 @@ def old_password(form, field):
raise ValidationError('Votre ancien mot de passe est requis pour cette modification.')
if not current_user.check_password(form.old_password.data):
raise ValidationError('Mot de passe actuel erroné.')
def id_exists(object):
"""Check if an id exists in a table"""
def _id_exists(form, id):
try:
id = int(id.data)
except ValueError:
raise ValidationError('L\'id n\'est pas un entier valide')
r = object.query.filter_by(id=id)
if not r:
raise ValidationError('L\'id n\'existe pas dans la BDD')
return _id_exists
def css(form, css):
"""Check if input is valid and sane CSS"""
pass

View File

@ -0,0 +1,30 @@
"""Update des titres et trophées
Revision ID: 6ae59d74cf54
Revises: c961d7b7a7ea
Create Date: 2019-06-06 23:34:53.521239
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = '6ae59d74cf54'
down_revision = 'c961d7b7a7ea'
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('title', sa.Column('css', sa.Text(_expect_unicode=True), nullable=True))
op.create_index(op.f('ix_trophy_name'), 'trophy', ['name'], unique=False)
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_index(op.f('ix_trophy_name'), table_name='trophy')
op.drop_column('title', 'css')
# ### end Alembic commands ###