diff --git a/.gitignore b/.gitignore index b5993a7..93d2f8b 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ __pycache__/ app/__pycache__/ app/static/avatars/ +app/static/images/trophies/ ## Devlopement files diff --git a/app/__init__.py b/app/__init__.py index 8a53d17..01ca7d3 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -5,6 +5,7 @@ from flask_login import LoginManager from flask_mail import Mail from config import Config import time +import slugify app = Flask(__name__) app.config.from_object(Config) @@ -49,3 +50,8 @@ from app.routes.forum import index, topic from app.utils import pluralize # To use pluralize into the templates from app.utils import date from app.utils import is_title + +# Add slugify into the available functions in every template +app.jinja_env.globals.update( + slugify=slugify.slugify +) diff --git a/app/data/trophies.png b/app/data/trophies.png new file mode 100644 index 0000000..c4a7c17 Binary files /dev/null and b/app/data/trophies.png differ diff --git a/app/data/trophies.yaml b/app/data/trophies.yaml index c6b6857..54e5dca 100644 --- a/app/data/trophies.yaml +++ b/app/data/trophies.yaml @@ -2,6 +2,7 @@ # name Trophy name as displayed on the site. # is_title If True, the trophy can be worn as a title next to the avatar. # description Detailed description to be shown on profile page. +# hidden Not shown unless awarded (for unique titles). # Manually awarded - @@ -185,3 +186,8 @@ is_title: True description: Foudroyer les cœurs à 5 reprises ! hidden: False +- + name: Survivant de la v42 + is_title: False + description: A connu la v42 de Planète Casio. + hidden: True diff --git a/app/forms/account.py b/app/forms/account.py index 660a7e3..936c760 100644 --- a/app/forms/account.py +++ b/app/forms/account.py @@ -189,8 +189,8 @@ class AdminUpdateAccountForm(FlaskForm): vd.email, ], ) - email_validate = BooleanField( - "Envoyer un email de validation à la nouvelle adresse", + email_confirmed = BooleanField( + "Confirmer l'email", description="Si décoché, l'utilisateur devra demander explicitement un email "\ "de validation, ou faire valider son adresse email par un administrateur.", ) diff --git a/app/models/users.py b/app/models/users.py index 1fc14ad..36ea44a 100644 --- a/app/models/users.py +++ b/app/models/users.py @@ -160,14 +160,15 @@ class Member(User): """ Update all or part of the user's metadata. The [data] dictionary accepts the following keys: - "email" str User mail ddress - "password" str Raw password - "bio" str Biograpy - "signature" str Post signature - "birthday" date Birthday date - "newsletter" bool Newsletter setting - "xp" int Experience points - "avatar" File Avatar image + "email" str User mail address + "email_confirmed" bool User mail address confirmed + "password" str Raw password + "bio" str Biograpy + "signature" str Post signature + "birthday" date Birthday date + "newsletter" bool Newsletter setting + "xp" int Experience points + "avatar" File Avatar image For future compatibility, other attributes are silently ignored. None values can be specified and are ignored. @@ -198,6 +199,8 @@ class Member(User): self.set_avatar(data["avatar"]) # For admins only + if "email_confirmed" in data: + self.email_confirmed = data["email_confirmed"] if "xp" in data: self.xp = data["xp"] diff --git a/app/routes/admin/account.py b/app/routes/admin/account.py index 1e5497a..f8ddc5a 100644 --- a/app/routes/admin/account.py +++ b/app/routes/admin/account.py @@ -48,6 +48,7 @@ def adm_edit_account(user_id): avatar=form.avatar.data or None, name=form.username.data or None, email=form.email.data or None, + email_confirmed=form.email_confirmed.data, password=form.password.data or None, birthday=form.birthday.data, signature=form.signature.data, diff --git a/app/static/css/table.css b/app/static/css/table.css index e104f78..288c05d 100644 --- a/app/static/css/table.css +++ b/app/static/css/table.css @@ -1,15 +1,18 @@ table { - border-collapse: collapse; - border-color: #d8d8d8; + border-collapse: collapse; + border-color: var(--border); border-style: solid; border-width: 0 0 1px 0; } table tr:nth-child(even) { - background: rgba(0, 0, 0, .05); + background: var(--background); +} +table tr:nth-child(even) { + background: var(--background); } table th { - background: #e0e0e0; - border-color: #d0d0d0; + background: var(--background); + border-color: var(--border); border-style: solid; border-width: 1px 0; padding: 2px 6px; diff --git a/app/static/css/theme.css b/app/static/css/theme.css index 5d54a32..c7aba0a 100644 --- a/app/static/css/theme.css +++ b/app/static/css/theme.css @@ -25,6 +25,17 @@ --hr-border: 1px solid #b0b0b0; } +table { + --border: #d8d8d8; +} +table tr:nth-child(even) { + --background: rgba(0, 0, 0, .05); +} +table th { + --background: #e0e0e0; + --border: #d0d0d0; +} + .form { --background: #ffffff; --text: #000000; diff --git a/app/static/css/widgets.css b/app/static/css/widgets.css index 4b4ee68..8d06ef7 100644 --- a/app/static/css/widgets.css +++ b/app/static/css/widgets.css @@ -61,7 +61,7 @@ border-radius: 2px; } .trophy img { - height: 50px; margin-right: 5px; + height: 48px; margin-right: 8px; } .trophy div > * { display: block; diff --git a/app/templates/account/user.html b/app/templates/account/user.html index d2473bc..dc80ac9 100644 --- a/app/templates/account/user.html +++ b/app/templates/account/user.html @@ -46,7 +46,7 @@
{% for t in trophies if t in member.trophies or t.hidden == False %}
- +
{{ t.name }} {{ t.description }} diff --git a/app/templates/admin/edit_account.html b/app/templates/admin/edit_account.html index c4b821d..af3f4d0 100644 --- a/app/templates/admin/edit_account.html +++ b/app/templates/admin/edit_account.html @@ -37,9 +37,9 @@ {% endfor %}
- {{ form.email_validate.label }} - {{ form.email_validate(checked=True) }} -
{{ form.email_validate.description }}
+ {{ form.email_confirmed.label }} + {{ form.email_confirmed(checked=user.email_confirmed) }} +
{{ form.email_confirmed.description }}
{{ form.password.label }} diff --git a/app/templates/admin/groups_privileges.html b/app/templates/admin/groups_privileges.html index fce31ad..800ca4b 100644 --- a/app/templates/admin/groups_privileges.html +++ b/app/templates/admin/groups_privileges.html @@ -13,19 +13,19 @@ - + {% for user in users %} - + + {{ g.name }} + {{ ', ' if not loop.last }} + {% endfor %} + {{ priv }} + {{- ', ' if not loop.last }} + {% endfor %} {% endfor %} diff --git a/master.py b/master.py index b19c2c3..5e617c8 100755 --- a/master.py +++ b/master.py @@ -10,6 +10,8 @@ import os import sys import yaml import readline +import slugify +from PIL import Image help_msg = """ This is the Planète Casio master shell. Type 'exit' or C-D to leave. @@ -37,7 +39,7 @@ the database. Type 'add-group #' to add a new member to a group. -Type 'create-trophies' to reset trophies and titles. +Type 'create-trophies' to reset trophies and titles and their icons. Type 'create-forums' to reset the forum tree. """ @@ -183,6 +185,29 @@ def create_trophies(): print(f"Created {len(tr)} trophies.") + # Create their icons in /app/static/images/trophies + names = [ slugify.slugify(t["name"]) for t in tr ] + src = os.path.join(app.root_path, "data", "trophies.png") + dst = os.path.join(app.root_path, "static", "images", "trophies") + + try: + os.mkdir(dst) + except FileExistsError: + pass + + img = Image.open(src) + + def trophy_iterator(img): + for y in range(img.height // 26): + for x in range(img.width // 26): + icon = img.crop((26*x+1, 26*y+1, 26*x+25, 26*y+25)) + # Skip blank squares in the source image + if len(icon.getcolors()) > 1: + yield icon.resize((48,48)) + + for (name, icon) in zip(names, trophy_iterator(img)): + icon.save(os.path.join(dst, f"{name}.png")) + def create_forums(): # Clean up forums forums("clear")
PseudoEmailGroupesPrivilèges spéciauxModifier
Privilèges spéciauxModifier
{{ user.name }}{{ user.email }}{{ user.email }} {% for g in user.groups %} - {{ g.name }} - {{ ', ' if not loop.last }} - {% endfor %} {% for priv in user.special_privileges() %} - {{ priv }} - {{- ', ' if not loop.last }} - {% endfor %} Modifier