Merge branch 'master-fork'

This commit is contained in:
Eragon 2019-04-02 15:07:53 +02:00
commit 183fb2d541
No known key found for this signature in database
GPG Key ID: B2B1BF4DA61BBB85
21 changed files with 141 additions and 191 deletions

View File

@ -6,7 +6,7 @@ from flask_wtf.file import FileField # Cuz' wtforms' FileField is shitty
import app.utils.validators as vd
class RegistrationForm(FlaskForm):
username = StringField('Pseudonyme', validators=[DataRequired(), vd.name])
username = StringField('Pseudonyme', validators=[DataRequired(), vd.name_valid, vd.name_available])
email = StringField('Adresse Email', validators=[DataRequired(), Email(), vd.email])
password = PasswordField('Mot de passe', validators=[DataRequired(), vd.password])
password2 = PasswordField('Répéter le mot de passe', validators=[DataRequired(), EqualTo('password')])
@ -16,8 +16,8 @@ class RegistrationForm(FlaskForm):
class UpdateAccountForm(FlaskForm):
avatar = FileField('Avatar', validators=[Optional(), vd.avatar])
email = StringField('Adresse Email', validators=[Optional(), Email(), vd.email, vd.old_password])
password = PasswordField('Mot de passe :', validators=[Optional(), vd.password, vd.old_password])
email = StringField('Adresse email', validators=[Optional(), Email(), vd.email, vd.old_password])
password = PasswordField('Mot de passe', validators=[Optional(), vd.password, vd.old_password])
password2 = PasswordField('Répéter le mot de passe', validators=[Optional(), EqualTo('password')])
old_password = PasswordField('Mot de passe actuel', validators=[Optional()])
birthday = DateField('Anniversaire', validators=[Optional()])
@ -33,10 +33,10 @@ class DeleteAccountForm(FlaskForm):
class AdminUpdateAccountForm(FlaskForm):
username = StringField('Pseudonyme', validators=[DataRequired(), vd.name])
username = StringField('Pseudonyme', validators=[Optional(), vd.name_valid])
avatar = FileField('Avatar', validators=[Optional(), vd.avatar])
email = StringField('Adresse Email', validators=[Optional(), Email(), vd.email])
password = PasswordField('Mot de passe :', validators=[Optional(), vd.password])
email = StringField('Adresse email', validators=[Optional(), Email(), vd.email])
password = PasswordField('Mot de passe', validators=[Optional(), vd.password])
xp = DecimalField('XP', validators=[Optional()])
innovation = DecimalField('Innovation', validators=[Optional()])
birthday = DateField('Anniversaire', validators=[Optional()])
@ -47,4 +47,4 @@ class AdminUpdateAccountForm(FlaskForm):
class AdminDeleteAccountForm(FlaskForm):
delete = BooleanField('Confirmer la suppression', validators=[DataRequired()], description='Attention, cette opération est irréversible!')
submit = SubmitField('Supprimer le compte')
submit = SubmitField('Supprimer le compte')

View File

@ -3,7 +3,7 @@ from wtforms import StringField, PasswordField, BooleanField, SubmitField
from wtforms.validators import DataRequired
class LoginForm(FlaskForm):
username = StringField('Pseudonyme :', validators=[DataRequired()])
password = PasswordField('Mot de passe :', validators=[DataRequired()])
remember_me = BooleanField('Se souvenir de moi :')
submit = SubmitField('Connexion')
username = StringField('Identifiant', validators=[DataRequired()])
password = PasswordField('Mot de passe', validators=[DataRequired()])
remember_me = BooleanField('Se souvenir de moi')
submit = SubmitField('Connexion')

View File

@ -73,21 +73,26 @@ def adm_groups():
users = Member.query.all()
groups = Group.query.all()
return render('admin/groups_privileges.html', users=users, groups=groups,
form=form)
@app.route('/admin/edit-account/<user_id>', methods=['GET', 'POST'])
@priv_required('edit-account')
def adm_edit_account(user_id):
user = Member.query.filter_by(id=user_id).first()
if not user:
abort(404)
user = Member.query.filter_by(id=user_id).first_or_404()
form = AdminUpdateAccountForm()
if request.method == "POST":
if form.validate_on_submit():
if form.avatar.data:
f = form.avatar.data
f.save("./app/static/"+user.avatar)
newname = form.username.data
names = list(Member.query.filter(Member.id != user.id).values(Member.name))
if newname in names:
raise Exception(f'{data["name"]} is not available')
user.update(
name = form.username.data or None,
email = form.email.data or None,

View File

@ -24,6 +24,5 @@ section h2 {
section .avatar {
display: block;
border-radius: 100%;
width: 150px; height: 150px;
width: 128px; height: 128px;
}

View File

@ -1,11 +1,9 @@
.form .avatar {
display: inline-block; vertical-align: middle;
border-radius: 100%;
width: 150px; height: 150px;
width: 128px; height: 128px;
}
.form .avatar + input[type="file"] {
display: inline-block; margin-left: 20px;
margin: 16px 0 0 0;
vertical-align: middle;
}
@ -26,17 +24,28 @@
.form input[type='email'],
.form input[type='date'],
.form input[type='password'],
.form input[type='search'],
.form textarea {
display: block;
width: 100%; padding: 6px 2.5%;
border: 1px solid #abcdef;
width: 100%; padding: 6px 8px;
border: 1px solid #c8c8c8;
/* Transitions when resizing with the mouse produces apparent lag */
transition: all .15s ease, width 0s, height 0s;
}
.form input[type='text']:focus,
.form input[type='email']:focus,
.form input[type='date']:focus,
.form input[type='password']:focus,
.form input[type='search']:focus,
.form textarea:focus {
box-shadow: 0 0 4px rgba(0, 102, 255, .9);
border-color: #91bfef;
box-shadow: 0 0 0 3px rgba(87, 143, 228, 0.4);
}
.form textarea {
max-width: 100%;
resize: vertical;
}
.form input[type="submit"] {
@ -47,4 +56,4 @@
color: red;
font-weight: 400;
margin-top: 5px;
}
}

View File

@ -1,8 +1,6 @@
/* Fonts */
@font-face { font-family: NotoSans; src: url(../fonts/noto_sans.ttf); }
@font-face { font-family: Raleway; font-weight: 200; src: url(../fonts/raleway_200.ttf); }
@font-face { font-family: Raleway; font-weight: 300; src: url(../fonts/raleway_300.ttf); }
@font-face { font-family: Cantarell; font-weight: normal; src: url(../fonts/Cantarell-Regular.otf); }
@font-face { font-family: Cantarell; font-weight: bold; src: url(../fonts/Cantarell-Bold.otf); }
@ -10,6 +8,8 @@
* {
box-sizing: border-box;
/* This transition value is replicated everywhere transitions are customized,
make sure to track them when editing */
transition: .15s ease;
}
@ -32,11 +32,11 @@ a:focus {
outline: none;
}
p {
section p {
line-height: 20px;
}
ul {
section ul {
line-height: 24px;
}
@ -71,7 +71,6 @@ input[type="submit"] {
padding: 6px 10px; border-radius: 2px;
cursor: pointer;
font-family: 'DejaVu Sans', sans-serif; font-weight: 400;
cursor: pointer;
}
input[type="button"]:hover,
input[type="submit"]:hover,

View File

@ -8,14 +8,24 @@ header {
display: flex; align-items: center; justify-content: space-between;
flex-flow: row wrap;
/* When the search field occupies the rightmost position, the calculated
position of the svg icon (on the right) might overflow from the header and
induce horizontal scrolling. */
overflow: hidden;
}
@media screen and (max-width: 1000px) {
header {
height: 75px;
}
header .title {
page-break-after: always;
}
@media screen and (max-width: 1199px) {
#spotlight {
display: none;
}
header input[type="search"] {
width: 200px;
}
}
@media screen and (max-width: 849px) {
header .form {
display: none;
}
}
header .title a {
@ -46,30 +56,25 @@ header a {
cursor: pointer;
}
header form {
header .form {
/* The search icon is draws inside the input field but its space is allocated
on the right. Apply a negative margin to compensate this:
-24px for the search icon
-2px for the spacing between the search icon and the field */
margin-right: -26px;
}
header input[type="search"] {
header .form input[type="search"] {
display: inline-block; width: 250px;
padding: 5px 35px 5px 10px;
border: 0; border-radius: 1px;
font-family: "Segoe UI", Helvetica, "Droid Sans", Arial,sans-serif;
box-shadow: 0 0 1px rgba(0, 0, 0, .4); transition: .15s ease;
border-color: #d8d8d8;
}
header input[type="search"] ~ a {
header .form input[type="search"] ~ a {
position: relative; left: -33px;
}
header input[type="search"]:focus {
box-shadow: 0 0 4px rgba(0, 102, 255, .9);
}
header input[type="search"] ~ a > svg > path {
header .form input[type="search"] ~ a > svg > path {
fill: #cccccc; transition: .15s ease;
}
header input[type="search"]:focus ~ a > svg > path {
header .form input[type="search"]:focus ~ a > svg > path {
fill: #333333;
}

View File

@ -66,7 +66,6 @@
#menu {
width: 100%; height: 0; overflow-x: hidden;
font-family: NotoSans; font-size: 12px;
background: #22292c; box-shadow: 0 0 5px rgba(0, 0, 0, 0.3);
transition: .1s ease;
position: unset;
left: unset;
@ -81,35 +80,15 @@
width: 100%;
}
#menu h2 {
margin: 10px 0 10px 40px;
font-family: Raleway; font-size: 15px;
color: #ffffff;
font-size: 15px;
}
#menu h2 > svg {
width: 30px; vertical-align: middle;
}
#menu h2 > img {
width: 64px; margin-right: 10px;
vertical-align: middle; border-radius: 50%;
width: 24px;
}
#menu h3 {
margin: 10px 0 10px 40px;
font-family: Raleway; font-size: 13px;
color: #ffffff;
}
#menu hr {
margin: 10px 15px 0 15px;
border: none;
border-bottom: 1px solid rgba(0, 0, 0, .15);
}
#menu > div > a,
#menu span {
display: block; margin: 10px 15px;
}
#menu span {
/*font-style: italic;*/ color: #b8b8b8;
display: block;
color: #b8b8b8;
font-size: 10px;
}
#menu span > a {
@ -117,24 +96,17 @@
margin: 0; font-style: normal;
font-size: 12px;
}
#menu a > img {
vertical-align: middle;
margin-right: 15px;
}
#menu a > svg {
width: 20px; height: 20px; vertical-align: middle;
margin-right: 10px;
}
#menu ul {
list-style: none;
margin: 10px 15px; padding: 0;
margin: 10px 0; padding: 0;
line-height: 20px;
color: #b8b8b8;
}
#menu li {
margin: 5px 0;
}
@media all and (max-width: 550px) {
@media all and (max-width: 549px) {
#light-menu, #spacer-menu {
height: 40px;
}
@ -188,7 +160,8 @@ header {
}
section {
margin: 10px;
width: unset;
margin: 16px;
}
.home-title {
padding: 10px;

View File

@ -94,8 +94,7 @@ nav a:focus {
left: -240px; width: 300px; /* left-to-right animation */
/*left: 60px; width: 0;*/ /* scroll animation */
height: 100%; overflow-x: hidden; overflow-y: auto;
font-family: NotoSans; font-size: 14px;
background: #22292c; box-shadow: 0 0 5px rgba(0, 0, 0, 0.3);
background: #1c2124; box-shadow: 0 0 5px rgba(0, 0, 0, 0.3);
transition: .15s ease;
}
@ -121,6 +120,7 @@ nav a:focus {
#menu > div {
width: 300px;
padding: 16px;
display: none;
}
#menu > div.opened {
@ -128,22 +128,20 @@ nav a:focus {
}
#menu h2 {
margin: 5% 0 20px 40px;
font-family: Raleway; font-size: 18px;
margin: 0 0 20px 0;
font-family: Cantarell; font-weight: bold; font-size: 18px;
color: #ffffff;
display: flex; align-items: center;
}
#menu h2 a {
margin: 0;
display: flex;
flex-direction: row;
align-items: center;
font-size: inherit; opacity: inherit;
}
#menu h2 > svg {
width: 42px; vertical-align: middle;
width: 32px; vertical-align: middle; margin-right: 8px;
}
#menu h2 img {
width: 64px; border-radius: 50%; vertical-align: middle; margin-right: 10px;
height: 48px; vertical-align: middle; margin-right: 10px;
}
#menu h2 a:hover,
#menu h2 a:focus {
@ -151,14 +149,14 @@ nav a:focus {
}
#menu h3 {
margin: 20px 0 20px 40px;
font-family: Raleway; font-size: 14px;
margin: 16px 0;
font-family: Cantarell; font-weight: bold; font-size: 15px;
color: #ffffff;
}
#menu hr {
margin: 15px;
margin: 15px 0;
border: none;
border-bottom: 1px solid rgba(0, 0, 0, .15);
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}
#menu ul {
@ -166,7 +164,7 @@ nav a:focus {
}
#menu a,
#menu li {
display: block; margin: 10px 15px;
display: block; margin: 10px 0;
transition: opacity .15s ease;
}
#menu li {
@ -175,6 +173,7 @@ nav a:focus {
#menu li > a {
display: inline;
margin: 0; font-style: normal;
font-size: 13px;
}
#menu a > img {
vertical-align: middle;
@ -186,32 +185,23 @@ nav a:focus {
}
#menu form {
padding: 0 5%;
padding: 0 8%;
}
#menu form input[type="text"],
#menu form input[type="password"] {
display: block; width: 100%;
margin: 0; padding: 5px 2%;
margin: 3px 0 8px 0; padding: 5px 2%;
font-size: 14px; color: inherit;
background: #e8e8e8; transition: background .15s ease;
border: none;
border-color: #141719;
}
#menu form input[type="text"]:focus,
#menu form input[type="password"]:focus {
background: #ffffff;
}
#menu form input[type="text"] {
border-top-left-radius: 5px;
border-top-right-radius: 5px;
}
#menu form input[type="password"] {
border-bottom-left-radius: 5px;
border-bottom-right-radius: 5px;
box-shadow: 0 0 0 3px rgba(87, 143, 228, 0.6);
border-color: #2d4b5f;
}
#menu form input[type="submit"] {
width: 100%;
margin-top: 10px; margin-bottom: 5px;
border-radius: 5px;
margin: 16px 0 5px 0;
}
#menu form label {
font-size: 13px; color: #FFFFFF; opacity: .7;

View File

@ -1,28 +1,20 @@
/*
Responsives rules
*/
@media all and (max-width: 1399px) {
body {
body, input {
font-size: 13px;
}
/*header form {
border-bottom: 1px solid #adb0b4;
}*/
header input[type="search"] {
font-size: 14px;
}
#menu li {
font-size: 10px;
font-size: 10px;
}
#menu a {
font-size: 13px;
}
}
@media all and (min-width: 1400px) {
body, input {
font-size: 13px;
@ -35,18 +27,19 @@
#menu li {
font-size: 11px;
}
#menu a {
font-size: 14px;
}
}
@media screen and (max-width: 1100px) {
@media screen and (max-width: 1199px) {
.home-pinned-content article:nth-child(5) {
display: none;
}
section {
width: 90%;
}
}
@media screen and (max-width: 800px) {
@media screen and (max-width: 849px) {
.home-pinned-content article:nth-child(4) {
display: none;
}

View File

@ -12,7 +12,7 @@ table th {
border-color: #d0d0d0;
border-style: solid;
border-width: 1px 0;
padding: 2px;
padding: 2px 6px;
}
table td {
padding: 4px 6px;

Binary file not shown.

Binary file not shown.

View File

@ -1,9 +1,11 @@
{% extends "base/base.html" %}
{% block title %}
<h1>Gestion du compte</h1>
{% endblock %}
{% block content %}
<section class="form">
<h1>Gestion du compte</h1>
<form action="{{ url_for('edit_account') }}" method="post" enctype="multipart/form-data">
{{ form.hidden_tag() }}

View File

@ -1,5 +1,4 @@
<footer>
<p>Planète Casio est un site communautaire non affilié à Casio. Toute reproduction de Planète Casio, même partielle, est interdite.</p>
<p>Planète Casio est un site communautaire non affilié à CASIO. Toute reproduction de Planète Casio, même partielle, est interdite.</p>
<p>Les programmes et autres publications présentes sur Planète Casio restent la propriété de leurs auteurs et peuvent être soumis à des licences ou des copyrights.</p>
<p>CASIO est une marque déposée par CASIO Computer Co., Ltd.</p>
</footer>

View File

@ -1,5 +1,5 @@
<div class=spacer></div>
<form action={{url_for('search')}} method="get">
<form action={{url_for('search')}} method="get" class=form>
<input type="search" name="q" id="q" placeholder="{{search_form.label}}" />
<a role=button onclick="this.parentNode.submit();" href=#>
<svg viewBox="0 0 24 24">
@ -8,16 +8,6 @@
</a>
</form>
{% if current_user.is_authenticated %}
<div class=links>
<a href="{{ url_for('user', username=current_user.name) }}" role=button title='Mon compte'>
<svg viewBox="0 0 24 24">
<path d="M12,19.2C9.5,19.2 7.29,17.92 6,16C6.03,14 10,12.9 12,12.9C14,12.9 17.97,14 18,16C16.71,17.92 14.5,19.2 12,19.2M12,5A3,3 0 0,1 15,8A3,3 0 0,1 12,11A3,3 0 0,1 9,8A3,3 0 0,1 12,5M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12C22,6.47 17.5,2 12,2Z"></path>
</svg>
</a>
</div>
{% endif %}
<div id="spotlight">
<a href="#" class="button bg-red">Jeu du mois : février 2019</a>
</div>

View File

@ -2,33 +2,29 @@
<div>
<h2>
<a href="{{ url_for('user', username=current_user.name) }}">
<img src="{{ url_for('static', filename=current_user.avatar) }}">
<div>{{ current_user.name }}</div>
</a>
<img src="{{ url_for('static', filename=current_user.avatar) }}"></a>
<a href="{{ url_for('user', username=current_user.name) }}">
{{ current_user.name }}</a>
</h2>
<a href="#">
<svg viewBox="0 0 24 24">
<path fill="#ffffff" d="M20,2A2,2 0 0,1 22,4V16A2,2 0 0,1 20,18H6L2,22V4C2,2.89 2.9,2 4,2H20M4,4V17.17L5.17,16H20V4H4M6,7H18V9H6V7M6,11H15V13H6V11Z"></path>
</svg>
Notifications
</svg>Notifications
</a>
<a href="#">
<svg viewBox="0 0 24 24">
<path fill="#ffffff" d="M2,2V11C2,12 3,13 4,13H6.2C6.6,15 7.9,16.7 11,17V19.1C8.8,19.3 8,20.4 8,21.7V22H16V21.7C16,20.4 15.2,19.3 13,19.1V17C16.1,16.7 17.4,15 17.8,13H20C21,13 22,12 22,11V2H18C17.1,2 16,3 16,4H8C8,3 6.9,2 6,2H2M4,4H6V6L6,11H4V4M18,4H20V11H18V6L18,4M8,6H16V11.5C16,13.43 15.42,15 12,15C8.59,15 8,13.43 8,11.5V6Z"></path>
</svg>
Trophées
</svg>Trophées
</a>
<a href="#">
<svg viewBox="0 0 24 24">
<path fill="#ffffff" d="M12,15.39L8.24,17.66L9.23,13.38L5.91,10.5L10.29,10.13L12,6.09L13.71,10.13L18.09,10.5L14.77,13.38L15.76,17.66M22,9.24L14.81,8.63L12,2L9.19,8.63L2,9.24L7.45,13.97L5.82,21L12,17.27L18.18,21L16.54,13.97L22,9.24Z"></path>
</svg>
Topics favoris
</svg>Topics favoris
</a>
<a href="{{ url_for('adm') }}">
<svg viewBox="0 0 24 24">
<path fill="#ffffff" d="M3,1H19A1,1 0 0,1 20,2V6A1,1 0 0,1 19,7H3A1,1 0 0,1 2,6V2A1,1 0 0,1 3,1M3,9H19A1,1 0 0,1 20,10V10.67L17.5,9.56L11,12.44V15H3A1,1 0 0,1 2,14V10A1,1 0 0,1 3,9M3,17H11C11.06,19.25 12,21.4 13.46,23H3A1,1 0 0,1 2,22V18A1,1 0 0,1 3,17M8,5H9V3H8V5M8,13H9V11H8V13M8,21H9V19H8V21M4,3V5H6V3H4M4,11V13H6V11H4M4,19V21H6V19H4M17.5,12L22,14V17C22,19.78 20.08,22.37 17.5,23C14.92,22.37 13,19.78 13,17V14L17.5,12M17.5,13.94L15,15.06V17.72C15,19.26 16.07,20.7 17.5,21.06V13.94Z"></path>
</svg>
Panel d'administration
</svg>Panel d'administration
</a>
<hr />
@ -36,24 +32,22 @@
<a href="{{ url_for('edit_account') }}">
<svg viewBox="0 0 24 24">
<path fill="#ffffff" d="M12,15.5A3.5,3.5 0 0,1 8.5,12A3.5,3.5 0 0,1 12,8.5A3.5,3.5 0 0,1 15.5,12A3.5,3.5 0 0,1 12,15.5M19.43,12.97C19.47,12.65 19.5,12.33 19.5,12C19.5,11.67 19.47,11.34 19.43,11L21.54,9.37C21.73,9.22 21.78,8.95 21.66,8.73L19.66,5.27C19.54,5.05 19.27,4.96 19.05,5.05L16.56,6.05C16.04,5.66 15.5,5.32 14.87,5.07L14.5,2.42C14.46,2.18 14.25,2 14,2H10C9.75,2 9.54,2.18 9.5,2.42L9.13,5.07C8.5,5.32 7.96,5.66 7.44,6.05L4.95,5.05C4.73,4.96 4.46,5.05 4.34,5.27L2.34,8.73C2.21,8.95 2.27,9.22 2.46,9.37L4.57,11C4.53,11.34 4.5,11.67 4.5,12C4.5,12.33 4.53,12.65 4.57,12.97L2.46,14.63C2.27,14.78 2.21,15.05 2.34,15.27L4.34,18.73C4.46,18.95 4.73,19.03 4.95,18.95L7.44,17.94C7.96,18.34 8.5,18.68 9.13,18.93L9.5,21.58C9.54,21.82 9.75,22 10,22H14C14.25,22 14.46,21.82 14.5,21.58L14.87,18.93C15.5,18.67 16.04,18.34 16.56,17.94L19.05,18.95C19.27,19.03 19.54,18.95 19.66,18.73L21.66,15.27C21.78,15.05 21.73,14.78 21.54,14.63L19.43,12.97Z"></path>
</svg>
Paramètres
</svg>Paramètres
</a>
<a href="{{ url_for('logout') }}">
<svg viewBox="0 0 24 24">
<path fill="#ffffff" d="M17,17.25V14H10V10H17V6.75L22.25,12L17,17.25M13,2A2,2 0 0,1 15,4V8H13V4H4V20H13V16H15V20A2,2 0 0,1 13,22H4A2,2 0 0,1 2,20V4A2,2 0 0,1 4,2H13Z"></path>
</svg>
Déconnexion
</svg>Déconnexion
</a>
</div>
{% else %}
<div>
<h2>
Invité
</h2>
<form method="post" action="{{url_for('login')}}" class="login">
<h2>Invité</h2>
<form method="post" action="{{url_for('login')}}" class="login form">
{{ login_form.hidden_tag() }}
{{ login_form.username.label }}
{{ login_form.username(size=32, placeholder="Identifiant") }}
{{ login_form.password.label }}
{{ login_form.password(size=32, placeholder="Mot de passe") }}
{{ login_form.submit(class_="bg-green") }}
{{ login_form.remember_me.label }} {{ login_form.remember_me() }}

View File

@ -2,32 +2,27 @@
<h2>
<svg viewBox="0 0 24 24">
<path fill="#ffffff" d="M8,3A2,2 0 0,0 6,5V9A2,2 0 0,1 4,11H3V13H4A2,2 0 0,1 6,15V19A2,2 0 0,0 8,21H10V19H8V14A2,2 0 0,0 6,12A2,2 0 0,0 8,10V5H10V3M16,3A2,2 0 0,1 18,5V9A2,2 0 0,0 20,11H21V13H20A2,2 0 0,0 18,15V19A2,2 0 0,1 16,21H14V19H16V14A2,2 0 0,1 18,12A2,2 0 0,1 16,10V5H14V3H16Z"></path>
</svg>
Programmes
</svg>Programmes
</h2>
<a href="#">
<svg viewBox="0 0 24 24">
<path fill="#ffffff" d="M7,6H17A6,6 0 0,1 23,12A6,6 0 0,1 17,18C15.22,18 13.63,17.23 12.53,16H11.47C10.37,17.23 8.78,18 7,18A6,6 0 0,1 1,12A6,6 0 0,1 7,6M6,9V11H4V13H6V15H8V13H10V11H8V9H6M15.5,12A1.5,1.5 0 0,0 14,13.5A1.5,1.5 0 0,0 15.5,15A1.5,1.5 0 0,0 17,13.5A1.5,1.5 0 0,0 15.5,12M18.5,9A1.5,1.5 0 0,0 17,10.5A1.5,1.5 0 0,0 18.5,12A1.5,1.5 0 0,0 20,10.5A1.5,1.5 0 0,0 18.5,9Z"></path>
</svg>
Jeux
</svg>Jeux
</a>
<a href="#">
<svg viewBox="0 0 24 24">
<path fill="#ffffff" d="M19,19H5V8H19M19,3H18V1H16V3H8V1H6V3H5C3.89,3 3,3.9 3,5V19A2,2 0 0,0 5,21H19A2,2 0 0,0 21,19V5A2,2 0 0,0 19,3M16.53,11.06L15.47,10L10.59,14.88L8.47,12.76L7.41,13.82L10.59,17L16.53,11.06Z"></path>
</svg>
Utilitaires
</svg>Utilitaires
</a>
<a href="#">
<svg viewBox="0 0 24 24">
<path fill="#ffffff" d="M21,16H3V4H21M21,2H3C1.89,2 1,2.89 1,4V16A2,2 0 0,0 3,18H10V20H8V22H16V20H14V18H21A2,2 0 0,0 23,16V4C23,2.89 22.1,2 21,2Z"></path>
</svg>
Logiciels
</svg>Logiciels
</a>
<a href="#">
<svg viewBox="0 0 24 24">
<path fill="#ffffff" d="M12,15.39L8.24,17.66L9.23,13.38L5.91,10.5L10.29,10.13L12,6.09L13.71,10.13L18.09,10.5L14.77,13.38L15.76,17.66M22,9.24L14.81,8.63L12,2L9.19,8.63L2,9.24L7.45,13.97L5.82,21L12,17.27L18.18,21L16.54,13.97L22,9.24Z"></path>
</svg>
Top 20
</svg>Top 20
</a>
<hr />
@ -35,8 +30,7 @@
<a href="#">
<svg viewBox="0 0 24 24">
<path fill="#ffffff" d="M9.5,3A6.5,6.5 0 0,1 16,9.5C16,11.11 15.41,12.59 14.44,13.73L14.71,14H15.5L20.5,19L19,20.5L14,15.5V14.71L13.73,14.44C12.59,15.41 11.11,16 9.5,16A6.5,6.5 0 0,1 3,9.5A6.5,6.5 0 0,1 9.5,3M9.5,5C7,5 5,7 5,9.5C5,12 7,14 9.5,14C12,14 14,12 14,9.5C14,7 12,5 9.5,5Z" />
</svg>
Recherche avancée
</svg>Recherche avancée
</a>
<hr />
@ -45,15 +39,11 @@
<ul>
<li><a href="#"><img src="{{ url_for('static', filename = 'images/fruit_ninja.gif') }}">Fruit Ninja</a></li>
<li><a href="#"><img src="{{ url_for('static', filename = 'images/clonelab.gif') }}">Clonelab</a></li>
<li><a href="#"><img src="{{ url_for('static', filename = 'images/gravity_duck.png') }}">Gravity Duck</a></li>
<li><a href="#"><img src="{{ url_for('static', filename = 'images/calcraft.gif') }}">Calcraft</a></li>
</ul>
<hr />
<h3>Coup de cœur</h3>
<ul>
<li><a href="#"><img src="{{ url_for('static', filename = 'images/fruit_ninja.gif') }}">Fruit Ninja</a></li>
<li><a href="#"><img src="{{ url_for('static', filename = 'images/clonelab.gif') }}">Clonelab</a></li>
<li><a href="#"><img src="{{ url_for('static', filename = 'images/gravity_duck.png') }}">Gravity Duck</a></li>
<li><a href="#"><img src="{{ url_for('static', filename = 'images/calcraft.gif')}}">Calcraft</a></li>
</ul>

View File

@ -2,26 +2,22 @@
<h2>
<svg viewBox="0 0 24 24">
<path fill="#ffffff" d="M22.7,19L13.6,9.9C14.5,7.6 14,4.9 12.1,3C10.1,1 7.1,0.6 4.7,1.7L9,6L6,9L1.6,4.7C0.4,7.1 0.9,10.1 2.9,12.1C4.8,14 7.5,14.5 9.8,13.6L18.9,22.7C19.3,23.1 19.9,23.1 20.3,22.7L22.6,20.4C23.1,20 23.1,19.3 22.7,19Z"></path>
</svg>
Outils
</svg>Outils
</h2>
<a href="#">
<svg viewBox="0 0 24 24">
<path fill="#ffffff" d="M2.6,10.59L8.38,4.8L10.07,6.5C9.83,7.35 10.22,8.28 11,8.73V14.27C10.4,14.61 10,15.26 10,16A2,2 0 0,0 12,18A2,2 0 0,0 14,16C14,15.26 13.6,14.61 13,14.27V9.41L15.07,11.5C15,11.65 15,11.82 15,12A2,2 0 0,0 17,14A2,2 0 0,0 19,12A2,2 0 0,0 17,10C16.82,10 16.65,10 16.5,10.07L13.93,7.5C14.19,6.57 13.71,5.55 12.78,5.16C12.35,5 11.9,4.96 11.5,5.07L9.8,3.38L10.59,2.6C11.37,1.81 12.63,1.81 13.41,2.6L21.4,10.59C22.19,11.37 22.19,12.63 21.4,13.41L13.41,21.4C12.63,22.19 11.37,22.19 10.59,21.4L2.6,13.41C1.81,12.63 1.81,11.37 2.6,10.59Z"></path>
</svg>
Forge GitLab
</svg>Forge Gitea
</a>
<a href="#">
<svg viewBox="0 0 24 24">
<path fill="#ffffff" d="M14.97,18.95L12.41,12.92C11.39,14.91 10.27,17 9.31,18.95C9.3,18.96 8.84,18.95 8.84,18.95C7.37,15.5 5.85,12.1 4.37,8.68C4.03,7.84 2.83,6.5 2,6.5C2,6.4 2,6.18 2,6.05H7.06V6.5C6.46,6.5 5.44,6.9 5.7,7.55C6.42,9.09 8.94,15.06 9.63,16.58C10.1,15.64 11.43,13.16 12,12.11C11.55,11.23 10.13,7.93 9.71,7.11C9.39,6.57 8.58,6.5 7.96,6.5C7.96,6.35 7.97,6.25 7.96,6.06L12.42,6.07V6.47C11.81,6.5 11.24,6.71 11.5,7.29C12.1,8.53 12.45,9.42 13,10.57C13.17,10.23 14.07,8.38 14.5,7.41C14.76,6.76 14.37,6.5 13.29,6.5C13.3,6.38 13.3,6.17 13.3,6.07C14.69,6.06 16.78,6.06 17.15,6.05V6.47C16.44,6.5 15.71,6.88 15.33,7.46L13.5,11.3C13.68,11.81 15.46,15.76 15.65,16.2L19.5,7.37C19.2,6.65 18.34,6.5 18,6.5C18,6.37 18,6.2 18,6.05L22,6.08V6.1L22,6.5C21.12,6.5 20.57,7 20.25,7.75C19.45,9.54 17,15.24 15.4,18.95C15.4,18.95 14.97,18.95 14.97,18.95Z"></path>
</svg>
Casio Universal Wiki
</svg>Casio Universal Wiki
</a>
<a href="#">
<svg viewBox="0 0 24 24">
<path fill="#ffffff" d="M19,8L15,12H18A6,6 0 0,1 12,18C11,18 10.03,17.75 9.2,17.3L7.74,18.76C8.97,19.54 10.43,20 12,20A8,8 0 0,0 20,12H23M6,12A6,6 0 0,1 12,6C13,6 13.97,6.25 14.8,6.7L16.26,5.24C15.03,4.46 13.57,4 12,4A8,8 0 0,0 4,12H1L5,16L9,12"></path>
</svg>
SH4 Compatibility Tool
</svg>SH4 Compatibility Tool
</a>
</div>

View File

@ -1,9 +1,14 @@
{% extends "base/base.html" %}
{% block title %}
<h1>Profil de '{{ user.name }}'</h1>
{% endblock %}
{% block content %}
<section>
<h1>
<div>
<img class="avatar" src="{{ url_for('static', filename=user.avatar) }}" alt="{{ user.name }}" style="display:inline; vertical-align:middle;"/>
Profil de {{ user.name }}</h1>
{{ user.name }}
</div>
</section>
{% endblock %}

View File

@ -2,10 +2,11 @@ from flask_login import current_user
from wtforms.validators import ValidationError
from app.models.users import User, Member
def name(form, name):
def name_valid(form, name):
if not User.valid_name(name.data):
raise ValidationError("Nom d'utilisateur invalide.")
# last check: do not ask db if useless
def name_available(form, name):
member = Member.query.filter_by(name=name.data).first()
if member is not None:
raise ValidationError('Pseudo indisponible.')