Browse Source

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

pull/12/head
Darks 3 months ago
parent
commit
f67129a36b
Signed by: Darks <l.gatin@neuf.fr> GPG Key ID: F61F10FA138E797C

+ 9
- 5
app/forms/trophies.py 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')

+ 5
- 3
app/models/trophies.py 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

+ 2
- 2
app/routes/admin/account.py 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()

+ 51
- 9
app/routes/admin/trophies.py 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)

+ 28
- 0
app/templates/admin/delete_trophy.html 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 %}

+ 38
- 0
app/templates/admin/edit_trophy.html 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 %}

+ 20
- 17
app/templates/admin/trophies.html 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 %}

+ 19
- 1
app/utils/validators.py 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

+ 30
- 0
migrations/versions/6ae59d74cf54_update_des_titres_et_trophées.py 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 ###

Loading…
Cancel
Save