First draw for trophies
This commit is contained in:
parent
d6e8f7d4d8
commit
79e3189f4b
|
@ -16,5 +16,5 @@ login.login_message = "Veuillez vous authentifier avant de continuer."
|
|||
from app import models # IDK why this is here, but it works
|
||||
from app.routes import index, search, users # To load routes at initialization
|
||||
from app.routes.account import login, account
|
||||
from app.routes.admin import index, groups, account
|
||||
from app.routes.admin import index, groups, account, trophies
|
||||
from app.utils import pluralize # To use pluralize into the templates
|
||||
|
|
|
@ -36,20 +36,11 @@ class DeleteAccountForm(FlaskForm):
|
|||
|
||||
|
||||
class AdminUpdateAccountForm(FlaskForm):
|
||||
username = StringField('Pseudonyme',
|
||||
validators=[Optional(), vd.name_valid])
|
||||
avatar = FileField('Avatar',
|
||||
validators=[Optional(), vd.avatar])
|
||||
email = StringField('Adresse email',
|
||||
validators=[Optional(), Email(), vd.email])
|
||||
email_validate = BooleanField("""Envoyer un email de validation à la
|
||||
nouvelle adresse""",
|
||||
description="""Si décoché, l'utilisateur devra demander explicitement
|
||||
un email de validation, ou faire valider son adresse email par un
|
||||
administrateur.""")
|
||||
password = PasswordField('Mot de passe',
|
||||
description="L'ancien mot de passe ne pourra pas être récupéré !",
|
||||
validators=[Optional(), vd.password])
|
||||
username = StringField('Pseudonyme', validators=[Optional(), vd.name_valid])
|
||||
avatar = FileField('Avatar', validators=[Optional(), vd.avatar])
|
||||
email = StringField('Adresse email', validators=[Optional(), Email(), vd.email])
|
||||
email_validate = BooleanField("Envoyer un email de validation à la nouvelle adresse", description="Si décoché, l'utilisateur devra demander explicitement un email de validation, ou faire valider son adresse email par un administrateur.")
|
||||
password = PasswordField('Mot de passe', description="L'ancien mot de passe ne pourra pas être récupéré !", validators=[Optional(), vd.password])
|
||||
xp = DecimalField('XP', validators=[Optional()])
|
||||
birthday = DateField('Anniversaire', validators=[Optional()])
|
||||
signature = TextAreaField('Signature', validators=[Optional()])
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
from flask_wtf import FlaskForm
|
||||
from wtforms import StringField, BooleanField, SubmitField
|
||||
from wtforms.validators import DataRequired, Optional
|
||||
from flask_wtf.file import FileField # Cuz' wtforms' FileField is shitty
|
||||
|
||||
|
||||
class CreateTrophyForm(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')
|
|
@ -0,0 +1,37 @@
|
|||
from app import db
|
||||
|
||||
|
||||
class Trophy(db.Model):
|
||||
__tablename__ = 'trophy'
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
# Trophy type (polymorphic discriminator)
|
||||
type = db.Column(db.String(20))
|
||||
__mapper_args__ = {
|
||||
'polymorphic_identity': __tablename__,
|
||||
'polymorphic_on': type
|
||||
}
|
||||
# Standalone properties
|
||||
name = db.Column(db.Text(convert_unicode=True))
|
||||
|
||||
owners = db.relationship('Member', secondary=lambda: TrophyMember,
|
||||
back_populates='trophies')
|
||||
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
|
||||
class Title(Trophy):
|
||||
__tablename__ = 'title'
|
||||
__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))
|
||||
|
||||
def __init__(self, name, title):
|
||||
self.name = name
|
||||
self.title = title
|
||||
|
||||
|
||||
# Many-to-many relation for users earning trophies
|
||||
TrophyMember = db.Table('trophy_member', db.Model.metadata,
|
||||
db.Column('tid', db.Integer, db.ForeignKey('trophy.id')),
|
||||
db.Column('uid', db.Integer, db.ForeignKey('member.id')))
|
|
@ -4,6 +4,7 @@ from flask_login import UserMixin
|
|||
from app.models.contents import Content
|
||||
from app.models.privs import SpecialPrivilege, Group, GroupMember, \
|
||||
GroupPrivilege
|
||||
from app.models.trophies import Trophy, TrophyMember
|
||||
import app.utils.unicode_names as unicode_names
|
||||
from config import V5Config
|
||||
|
||||
|
@ -90,7 +91,8 @@ class Member(User, db.Model):
|
|||
newsletter = db.Column(db.Boolean, default=False)
|
||||
|
||||
# Relations
|
||||
# trophies = db.relationship('Trophy', back_populates='member')
|
||||
trophies = db.relationship('Trophy', secondary=TrophyMember,
|
||||
back_populates='owners')
|
||||
# tests = db.relationship('Test', back_populates='author')
|
||||
|
||||
def __init__(self, name, email, password):
|
||||
|
|
|
@ -7,7 +7,7 @@ from app import app, db
|
|||
|
||||
|
||||
@app.route('/admin/edit-account/<user_id>', methods=['GET', 'POST'])
|
||||
@priv_required('edit-account')
|
||||
@priv_required('access-admin-panel', 'edit-account')
|
||||
def adm_edit_account(user_id):
|
||||
user = Member.query.filter_by(id=user_id).first_or_404()
|
||||
|
||||
|
@ -43,7 +43,7 @@ def adm_edit_account(user_id):
|
|||
|
||||
|
||||
@app.route('/admin/edit-account/<user_id>/delete', methods=['GET', 'POST'])
|
||||
@priv_required('delete-account')
|
||||
@priv_required('access-admin-panel', 'delete-account')
|
||||
def adm_delete_account(user_id):
|
||||
user = Member.query.filter_by(id=user_id).first_or_404()
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ from app.utils.render import render
|
|||
from app import app
|
||||
|
||||
|
||||
@app.route('/admin', methods=['GET', 'POST'])
|
||||
@app.route('/admin', methods=['GET'])
|
||||
@priv_required('access-admin-panel')
|
||||
def adm():
|
||||
return render('admin/index.html')
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
from flask import request, flash
|
||||
from app.utils.priv_required import priv_required
|
||||
from app.models.trophies import Trophy, Title
|
||||
from app.forms.trophies import CreateTrophyForm
|
||||
from app.utils.render import render
|
||||
from app import app, db
|
||||
|
||||
|
||||
@app.route('/admin/trophies', methods=['GET', 'POST'])
|
||||
@priv_required('access-admin-panel', )
|
||||
def adm_trophies():
|
||||
form = CreateTrophyForm()
|
||||
if request.method == "POST":
|
||||
if form.validate_on_submit():
|
||||
if form.is_title.data:
|
||||
trophy = Title(form.name.data, form.title.data)
|
||||
type = 'titre'
|
||||
else:
|
||||
trophy = Trophy(form.name.data)
|
||||
type = 'trophée'
|
||||
db.session.add(trophy)
|
||||
db.session.commit()
|
||||
flash(f'Nouveau {type} ajoutée', '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)
|
|
@ -9,6 +9,7 @@
|
|||
<p>Pages générales du panneau d'administration :</p>
|
||||
<ul>
|
||||
<li><a href="{{ url_for('adm_groups') }}">Groupes et privilèges</a></li>
|
||||
<li><a href="{{ url_for('adm_trophies') }}">Titres et trophées</a></li>
|
||||
</ul>
|
||||
</section>
|
||||
{% endblock %}
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
{% extends "base/base.html" %}
|
||||
|
||||
{% block title %}
|
||||
<a href="{{ url_for('adm') }}">Panneau d'administration</a> » <h1>Titres et trophées</h1>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<section>
|
||||
<p>Cette page présente une vue d'ensemble des titres et trophées.</p>
|
||||
|
||||
<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>
|
||||
|
||||
{% for trophy in trophies %}
|
||||
<tr><td><img src="#" 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>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
</section>
|
||||
|
||||
<section class="form">
|
||||
<form action="{{ url_for('adm_trophies') }}" method="post" enctype="multipart/form-data">
|
||||
{{ form.hidden_tag() }}
|
||||
<h2>Nouveau trophée</h2>
|
||||
|
||||
<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>
|
||||
{{ form.title.label }}
|
||||
{{ form.title }}
|
||||
<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>
|
||||
</section>
|
||||
{% endblock %}
|
|
@ -7,6 +7,7 @@ class Config(object):
|
|||
'postgresql+psycopg2://' + os.environ.get('USER') + ':@/pcv5'
|
||||
SQLALCHEMY_TRACK_MODIFICATIONS = False
|
||||
UPLOAD_FOLDER = './app/static/avatars'
|
||||
LOGIN_DISABLED = True
|
||||
|
||||
|
||||
class V5Config(object):
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
"""Ajout des titres et trophées
|
||||
|
||||
Revision ID: c961d7b7a7ea
|
||||
Revises: a6e89f3510d9
|
||||
Create Date: 2019-06-06 15:18:09.893001
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'c961d7b7a7ea'
|
||||
down_revision = 'a6e89f3510d9'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.create_table('trophy',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('type', sa.String(length=20), nullable=True),
|
||||
sa.Column('name', sa.Text(_expect_unicode=True), nullable=True),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_table('title',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('title', sa.Text(_expect_unicode=True), nullable=True),
|
||||
sa.ForeignKeyConstraint(['id'], ['trophy.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_table('trophy_member',
|
||||
sa.Column('tid', sa.Integer(), nullable=True),
|
||||
sa.Column('uid', sa.Integer(), nullable=True),
|
||||
sa.ForeignKeyConstraint(['tid'], ['trophy.id'], ),
|
||||
sa.ForeignKeyConstraint(['uid'], ['member.id'], )
|
||||
)
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_table('trophy_member')
|
||||
op.drop_table('title')
|
||||
op.drop_table('trophy')
|
||||
# ### end Alembic commands ###
|
Loading…
Reference in New Issue