diff --git a/app/forms/account.py b/app/forms/account.py index f965237..8e7671e 100644 --- a/app/forms/account.py +++ b/app/forms/account.py @@ -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') \ No newline at end of file + submit = SubmitField('Supprimer le compte') diff --git a/app/forms/login.py b/app/forms/login.py index 2f49f7c..d8dad8e 100644 --- a/app/forms/login.py +++ b/app/forms/login.py @@ -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') \ No newline at end of file + username = StringField('Identifiant', validators=[DataRequired()]) + password = PasswordField('Mot de passe', validators=[DataRequired()]) + remember_me = BooleanField('Se souvenir de moi') + submit = SubmitField('Connexion') diff --git a/app/routes/admin.py b/app/routes/admin.py index 24a8b1f..f21b8a6 100644 --- a/app/routes/admin.py +++ b/app/routes/admin.py @@ -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/', 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, diff --git a/app/static/css/container.css b/app/static/css/container.css index ac88958..d395d09 100644 --- a/app/static/css/container.css +++ b/app/static/css/container.css @@ -24,6 +24,5 @@ section h2 { section .avatar { display: block; - border-radius: 100%; - width: 150px; height: 150px; + width: 128px; height: 128px; } diff --git a/app/static/css/form.css b/app/static/css/form.css index 1915d99..f7bcb8d 100644 --- a/app/static/css/form.css +++ b/app/static/css/form.css @@ -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; -} \ No newline at end of file +} diff --git a/app/static/css/global.css b/app/static/css/global.css index 7912e14..3cc059a 100644 --- a/app/static/css/global.css +++ b/app/static/css/global.css @@ -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, diff --git a/app/static/css/header.css b/app/static/css/header.css index e694515..126ba0c 100644 --- a/app/static/css/header.css +++ b/app/static/css/header.css @@ -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; } diff --git a/app/static/css/light.css b/app/static/css/light.css index 3ff5693..a59aea3 100644 --- a/app/static/css/light.css +++ b/app/static/css/light.css @@ -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; diff --git a/app/static/css/navbar.css b/app/static/css/navbar.css index e039c6a..51020ac 100644 --- a/app/static/css/navbar.css +++ b/app/static/css/navbar.css @@ -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; diff --git a/app/static/css/responsive.css b/app/static/css/responsive.css index 016960e..3886763 100644 --- a/app/static/css/responsive.css +++ b/app/static/css/responsive.css @@ -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; } diff --git a/app/static/css/table.css b/app/static/css/table.css index 5deaa26..56016ac 100644 --- a/app/static/css/table.css +++ b/app/static/css/table.css @@ -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; diff --git a/app/static/fonts/raleway_200.ttf b/app/static/fonts/raleway_200.ttf deleted file mode 100644 index 281a001..0000000 Binary files a/app/static/fonts/raleway_200.ttf and /dev/null differ diff --git a/app/static/fonts/raleway_300.ttf b/app/static/fonts/raleway_300.ttf deleted file mode 100644 index 9ff67e7..0000000 Binary files a/app/static/fonts/raleway_300.ttf and /dev/null differ diff --git a/app/templates/account.html b/app/templates/account.html index 4e00ae3..603ca48 100644 --- a/app/templates/account.html +++ b/app/templates/account.html @@ -1,9 +1,11 @@ {% extends "base/base.html" %} +{% block title %} +

Gestion du compte

+{% endblock %} + {% block content %}
-

Gestion du compte

-
{{ form.hidden_tag() }} diff --git a/app/templates/base/footer.html b/app/templates/base/footer.html index c7cb38d..d7a8eb1 100644 --- a/app/templates/base/footer.html +++ b/app/templates/base/footer.html @@ -1,5 +1,4 @@
-

Planète Casio est un site communautaire non affilié à Casio. Toute reproduction de Planète Casio, même partielle, est interdite.

+

Planète Casio est un site communautaire non affilié à CASIO. Toute reproduction de Planète Casio, même partielle, est interdite.

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.

-

CASIO est une marque déposée par CASIO Computer Co., Ltd.

diff --git a/app/templates/base/header.html b/app/templates/base/header.html index 5c59cfc..9036196 100644 --- a/app/templates/base/header.html +++ b/app/templates/base/header.html @@ -1,5 +1,5 @@
- + @@ -8,16 +8,6 @@
-{% if current_user.is_authenticated %} - -{% endif %} - diff --git a/app/templates/base/navbar/account.html b/app/templates/base/navbar/account.html index 6ab5fe1..50bb3ae 100644 --- a/app/templates/base/navbar/account.html +++ b/app/templates/base/navbar/account.html @@ -2,33 +2,29 @@

- -
{{ current_user.name }}
-
+ + + {{ current_user.name }}

- - Notifications + Notifications - - Trophées + Trophées - - Topics favoris + Topics favoris - - Panel d'administration + Panel d'administration
@@ -36,24 +32,22 @@ - - Paramètres + Paramètres - - Déconnexion + Déconnexion
{% else %}
-

- Invité -

-
diff --git a/app/templates/user.html b/app/templates/user.html index 8f01820..e1cc82a 100644 --- a/app/templates/user.html +++ b/app/templates/user.html @@ -1,9 +1,14 @@ {% extends "base/base.html" %} +{% block title %} +

Profil de '{{ user.name }}'

+{% endblock %} + {% block content %}
-

+
{{ user.name }} - Profil de {{ user.name }}

+ {{ user.name }} +
{% endblock %} diff --git a/app/utils/validators.py b/app/utils/validators.py index 7a34e5c..6341ad4 100644 --- a/app/utils/validators.py +++ b/app/utils/validators.py @@ -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.')