Compare commits
34 Commits
Author | SHA1 | Date |
---|---|---|
Darks | 21b863e0e2 | |
Darks | e92792c8d6 | |
Darks | 20524d28c3 | |
Eragon | 0d9ca65238 | |
Darks | 402e6699aa | |
Darks | 7e64a70eec | |
Darks | 920724718f | |
Lephenixnoir | d6ff6eb77f | |
IniKiwi | c5df575af3 | |
IniKiwi | f93443310b | |
Darks | f6aefffc3c | |
Lephe | c8f2d73bc2 | |
Lephe | d531106c78 | |
Eragon | 43381bf493 | |
Darks | 62341cf9d9 | |
Eragon | c88c993ee3 | |
Eragon | ed8550f291 | |
Eragon | f163d15066 | |
Darks | 14e81bdfb5 | |
Eragon | 4231b3084e | |
Darks | 9902719328 | |
Darks | 358a5fec9d | |
Darks | 8721a7be69 | |
Darks | 12483e70e4 | |
Darks | fabbb130b6 | |
Darks | 2530581095 | |
Darks | f06f14e814 | |
Darks | 876cae2b69 | |
Darks | a5b2933727 | |
Darks | 9de0f9f823 | |
Eldeberen | 41eaaa4c30 | |
Darks | ad1042865b | |
Darks | 2dd7863e89 | |
Darks | e15005a427 |
|
@ -22,6 +22,9 @@ test.*
|
|||
# Autosaves
|
||||
*.dia~
|
||||
|
||||
## Logging files
|
||||
*.log
|
||||
|
||||
|
||||
## Deployment files
|
||||
|
||||
|
|
|
@ -1,6 +0,0 @@
|
|||
[submodule "submodules/emoji-picker-element"]
|
||||
path = submodules/emoji-picker-element
|
||||
url = https://gitea.planet-casio.com/devs/emoji-picker-element.git
|
||||
[submodule "submodules/emoji-picker-element-data"]
|
||||
path = submodules/emoji-picker-element-data
|
||||
url = https://gitea.planet-casio.com/devs/emoji-picker-element-data.git
|
8
Makefile
8
Makefile
|
@ -7,14 +7,6 @@ run: css
|
|||
|
||||
css: $(obj)
|
||||
|
||||
emoji: emoji-data
|
||||
cd submodules/emoji-picker-element && npm install && npm run build
|
||||
cp submodules/emoji-picker-element/i18n/*.js app/static/scripts/emoji-picker-element/
|
||||
|
||||
emoji-data:
|
||||
cd submodules/emoji-picker-element-data/ && npm install && npm run build
|
||||
cp submodules/emoji-picker-element-data/fr/emojibase/data.json app/static/scripts/emoji-picker-element/
|
||||
|
||||
app/static/css/%.css: app/static/less/%.less
|
||||
$(CSSC) $< $@
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ from config import FlaskApplicationSettings, V5Config
|
|||
|
||||
app = Flask(__name__)
|
||||
app.config.from_object(FlaskApplicationSettings)
|
||||
app.v5logger = V5Config.v5logger()
|
||||
|
||||
# Check security of secret
|
||||
if FlaskApplicationSettings.SECRET_KEY == "a-random-secret-key":
|
||||
|
|
|
@ -397,8 +397,7 @@ class Member(User):
|
|||
Notify a user with a message.
|
||||
An hyperlink can be added to redirect to the notification source
|
||||
"""
|
||||
return
|
||||
n = Notification(self.id, message, href=href)
|
||||
n = Notification(self, message, href=href)
|
||||
db.session.add(n)
|
||||
db.session.commit()
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# Register routes here
|
||||
|
||||
from app.routes import index, search, users, tools, development
|
||||
from app.routes import index, search, users, tools, development, chat
|
||||
from app.routes.account import login, account, notification, polls
|
||||
from app.routes.admin import index, groups, account, forums, \
|
||||
attachments, config, members, polls, login_as
|
||||
|
|
|
@ -8,6 +8,7 @@ from app.models.trophy import Title
|
|||
from app.utils.render import render
|
||||
from app.utils.send_mail import send_validation_mail, send_reset_password_mail
|
||||
from app.utils.priv_required import guest_only
|
||||
from app.utils.glados import say, BOLD
|
||||
import app.utils.ldap as ldap
|
||||
import app.utils.validators as vd
|
||||
from itsdangerous import URLSafeTimedSerializer
|
||||
|
@ -30,6 +31,7 @@ def edit_account():
|
|||
|
||||
if form.submit.data:
|
||||
if form.is_submitted() and form.validate(extra_validators=extra_vd):
|
||||
old_username = current_user.norm
|
||||
current_user.update(
|
||||
avatar=form.avatar.data or None,
|
||||
email=form.email.data or None,
|
||||
|
@ -41,10 +43,13 @@ def edit_account():
|
|||
newsletter=form.newsletter.data,
|
||||
theme=form.theme.data
|
||||
)
|
||||
ldap.edit(old_username, current_user)
|
||||
current_user.update(password=form.password.data or None)
|
||||
db.session.merge(current_user)
|
||||
db.session.commit()
|
||||
current_user.update_trophies("on-profile-update")
|
||||
flash('Modifications effectuées', 'ok')
|
||||
app.v5logger.info(f"<{current_user.name}> has edited their account")
|
||||
return redirect(request.url)
|
||||
else:
|
||||
flash('Erreur lors de la modification', 'error')
|
||||
|
@ -62,6 +67,7 @@ def ask_reset_password():
|
|||
m = Member.query.filter_by(email=form.email.data).first()
|
||||
if m is not None:
|
||||
send_reset_password_mail(m.name, m.email)
|
||||
app.v5logger.info(f"<{m.name}> has asked a password reset token")
|
||||
flash('Un email a été envoyé à l\'adresse renseignée', 'ok')
|
||||
return redirect(url_for('login'))
|
||||
elif request.method == "POST":
|
||||
|
@ -87,6 +93,7 @@ def reset_password(token):
|
|||
db.session.merge(m)
|
||||
db.session.commit()
|
||||
flash('Modifications effectuées', 'ok')
|
||||
app.v5logger.info(f"<{m.name}> has reset their password")
|
||||
return redirect(url_for('login'))
|
||||
else:
|
||||
flash('Erreur lors de la modification', 'error')
|
||||
|
@ -102,6 +109,7 @@ def delete_account():
|
|||
|
||||
if del_form.submit.data:
|
||||
if del_form.validate_on_submit():
|
||||
name = current_user.name
|
||||
if del_form.transfer.data:
|
||||
guest = Guest(current_user.generate_guest_name())
|
||||
db.session.add(guest)
|
||||
|
@ -112,10 +120,14 @@ def delete_account():
|
|||
current_user.delete_posts()
|
||||
db.session.commit()
|
||||
|
||||
if (V5Config.USE_LDAP):
|
||||
ldap.delete_member(current_user)
|
||||
|
||||
current_user.delete()
|
||||
logout_user()
|
||||
db.session.commit()
|
||||
flash('Compte supprimé', 'ok')
|
||||
app.v5logger.info(f"<{name}> has deleted their account ({'with' if del_form.transfer.data else 'without'} guest transfer)")
|
||||
return redirect(url_for('index'))
|
||||
else:
|
||||
flash('Erreur lors de la suppression du compte', 'error')
|
||||
|
@ -141,6 +153,7 @@ def register():
|
|||
|
||||
# Email validation message
|
||||
send_validation_mail(member.name, member.email)
|
||||
app.v5logger.info(f"<{member.name}> registered")
|
||||
|
||||
return redirect(url_for('validation') + "?email=" + form.email.data)
|
||||
return render('account/register.html', title='Register',
|
||||
|
@ -178,4 +191,8 @@ def activate_account(token):
|
|||
db.session.commit()
|
||||
|
||||
flash("L'email a bien été confirmé", "ok")
|
||||
app.v5logger.info(f"<{m.name}> has activated their account")
|
||||
say(f"Un nouveau membre s’est inscrit ! Il s’agit de {BOLD}{m.name}{BOLD}.")
|
||||
say(url_for('user', username=m.name, _external=True))
|
||||
|
||||
return redirect(url_for('login'))
|
||||
|
|
|
@ -49,6 +49,7 @@ def login():
|
|||
login_user(member, remember=form.remember_me.data,
|
||||
duration=datetime.timedelta(days=7))
|
||||
member.update_trophies("on-login")
|
||||
app.v5logger.info(f"<{member.name}> has logged in")
|
||||
|
||||
# Redirect safely (https://huit.re/open-redirect)
|
||||
def is_safe_url(target):
|
||||
|
@ -71,8 +72,10 @@ def login():
|
|||
@login_required
|
||||
@check_csrf
|
||||
def logout():
|
||||
name = current_user.name
|
||||
logout_user()
|
||||
flash('Déconnexion réussie', 'info')
|
||||
app.v5logger.info(f"<{name}> has logged out")
|
||||
if request.referrer:
|
||||
return redirect(request.referrer)
|
||||
return redirect(url_for('index'))
|
||||
|
|
|
@ -28,5 +28,6 @@ def account_polls():
|
|||
db.session.commit()
|
||||
|
||||
flash(f"Le sondage {p.id} a été créé", "info")
|
||||
app.v5logger.info(f"<{current_user.name}> has created the form #{p.id}")
|
||||
|
||||
return render("account/polls.html", polls=polls, form=form)
|
||||
|
|
|
@ -9,6 +9,7 @@ from app.forms.account import AdminUpdateAccountForm, AdminDeleteAccountForm, \
|
|||
AdminAccountEditTrophyForm, AdminAccountEditGroupForm
|
||||
from app.utils.render import render
|
||||
from app.utils.notify import notify
|
||||
from app.utils import ldap as ldap
|
||||
from app import app, db
|
||||
from config import V5Config
|
||||
|
||||
|
@ -50,12 +51,12 @@ def adm_edit_account(user_id):
|
|||
# You cannot user vd.name_available because name will always be
|
||||
# invalid! Maybe you can add another validator with arguments
|
||||
raise Exception(f'{newname} is not available')
|
||||
old_username = user.norm
|
||||
user.update(
|
||||
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,
|
||||
title=form.title.data,
|
||||
|
@ -63,11 +64,14 @@ def adm_edit_account(user_id):
|
|||
newsletter=form.newsletter.data,
|
||||
xp=form.xp.data or None,
|
||||
)
|
||||
ldap.edit(old_username, user)
|
||||
user.update(password=form.password.data or None)
|
||||
db.session.merge(user)
|
||||
db.session.commit()
|
||||
# TODO: send an email to member saying his account has been modified
|
||||
user.notify(f"Vos informations personnelles ont été modifiées par {current_user.name}.")
|
||||
flash('Modifications effectuées', 'ok')
|
||||
app.v5logger.info(f"[admin] <{current_user.name}> has edited <{user.name}>'s data")
|
||||
return redirect(request.url)
|
||||
else:
|
||||
flash('Erreur lors de la modification', 'error')
|
||||
|
@ -85,6 +89,7 @@ def adm_edit_account(user_id):
|
|||
db.session.merge(user)
|
||||
db.session.commit()
|
||||
flash('Modifications effectuées', 'ok')
|
||||
app.v5logger.info(f"[admin] <{current_user.name}> has edited <{user.name}>'s trophies")
|
||||
return redirect(request.url)
|
||||
else:
|
||||
flash("Erreur lors de la modification des trophées", 'error')
|
||||
|
@ -102,6 +107,7 @@ def adm_edit_account(user_id):
|
|||
db.session.merge(user)
|
||||
db.session.commit()
|
||||
flash('Modifications effectuées', 'ok')
|
||||
app.v5logger.info(f"[admin] <{current_user.name}> has edited <{user.name}>'s groups")
|
||||
return redirect(request.url)
|
||||
else:
|
||||
flash("Erreur lors de la modification des groupes", 'error')
|
||||
|
@ -148,9 +154,13 @@ def adm_delete_account(user_id):
|
|||
user.delete_posts()
|
||||
db.session.commit()
|
||||
|
||||
if (V5Config.USE_LDAP):
|
||||
ldap.delete_member(user)
|
||||
|
||||
user.delete()
|
||||
db.session.commit()
|
||||
flash('Compte supprimé', 'ok')
|
||||
app.v5logger.info(f"[admin] <{current_user.name}> has deleted <{user.name}> account")
|
||||
return redirect(url_for('adm'))
|
||||
else:
|
||||
flash('Erreur lors de la suppression du compte', 'error')
|
||||
|
|
|
@ -42,6 +42,7 @@ def adm_login_as():
|
|||
# Create a safe token to flee when needed
|
||||
s = Serializer(app.config["SECRET_KEY"])
|
||||
vandal_token = s.dumps(current_user.id)
|
||||
vandal_name = current_user.name
|
||||
|
||||
# Login and display some messages
|
||||
login_user(user)
|
||||
|
@ -51,9 +52,11 @@ def adm_login_as():
|
|||
else:
|
||||
flash(f"Connecté en tant que {user.name}")
|
||||
|
||||
app.v5logger.info(f"[admin] <{vandal_name}> has logged in as <{user.name}>")
|
||||
|
||||
# Return the response
|
||||
resp = make_response(redirect(url_for('index')))
|
||||
resp.set_cookie('vandale', vandal_token)
|
||||
resp.set_cookie('vandale', vandal_token, path='/')
|
||||
return resp
|
||||
|
||||
# Else return form
|
||||
|
@ -76,13 +79,22 @@ def adm_logout_as():
|
|||
abort(403)
|
||||
|
||||
user = Member.query.get(id)
|
||||
|
||||
# Send a notification to vandalized user
|
||||
current_user.notify(f"{user.name} a accédé à ce compte à des fins de modération",
|
||||
url_for('user', username=user.name))
|
||||
|
||||
# Switch back to admin
|
||||
victim_name = current_user.name
|
||||
logout_user()
|
||||
login_user(user)
|
||||
|
||||
app.v5logger.info(f"[admin] <{user.name}> has logged out from <{victim_name}>'s account")
|
||||
|
||||
if request.referrer:
|
||||
resp = make_response(redirect(request.referrer))
|
||||
else:
|
||||
resp = make_response(redirect(url_for('index')))
|
||||
|
||||
resp.set_cookie('vandale', '', expires=0)
|
||||
resp.set_cookie('vandale', '', expires=0, path='/')
|
||||
return resp
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
from app import app
|
||||
from app.utils.render import render
|
||||
from flask import send_file, url_for
|
||||
|
||||
@app.route('/chat')
|
||||
def chat():
|
||||
return render('chat.html',
|
||||
styles=[
|
||||
'+css/v5shoutbox.css'],
|
||||
scripts=[
|
||||
'-scripts/trigger_menu.js',
|
||||
'-scripts/editor.js'])
|
||||
|
||||
@app.route('/v5shoutbox.js')
|
||||
def v5shoutbox_js():
|
||||
return send_file('static/scripts/v5shoutbox.js')
|
|
@ -4,6 +4,7 @@ from flask import request, redirect, url_for, abort, flash
|
|||
from app import app, db
|
||||
from config import V5Config
|
||||
from app.utils.render import render
|
||||
from app.utils.glados import say, BOLD
|
||||
from app.forms.forum import TopicCreationForm, AnonymousTopicCreationForm
|
||||
from app.models.forum import Forum
|
||||
from app.models.topic import Topic
|
||||
|
@ -73,6 +74,11 @@ def forum_page(f, page=1):
|
|||
current_user.update_trophies('new-post')
|
||||
|
||||
flash('Le sujet a bien été créé', 'ok')
|
||||
app.v5logger.info(f"<{t.author.name}> has created the topic #{t.id}")
|
||||
if f.is_default_accessible():
|
||||
say(f"Nouveau topic de {author.name} : {BOLD}{t.title}{BOLD}")
|
||||
say(url_for('forum_topic', f=f, page=(t, 1), _external=True))
|
||||
|
||||
return redirect(url_for('forum_topic', f=f, page=(t,1)))
|
||||
|
||||
# Paginate topic pages
|
||||
|
|
|
@ -5,6 +5,7 @@ from sqlalchemy import desc
|
|||
from app import app, db
|
||||
from config import V5Config
|
||||
from app.utils.render import render
|
||||
from app.utils.glados import say, BOLD
|
||||
from app.forms.forum import CommentForm, AnonymousCommentForm
|
||||
from app.models.thread import Thread
|
||||
from app.models.comment import Comment
|
||||
|
@ -54,6 +55,11 @@ def forum_topic(f, page):
|
|||
current_user.update_trophies('new-post')
|
||||
|
||||
flash('Message envoyé', 'ok')
|
||||
app.v5logger.info(f"<{c.author.name}> has posted a the comment #{c.id}")
|
||||
if f.is_default_accessible():
|
||||
say(f"Nouveau commentaire de {author.name} sur le topic : {BOLD}{t.title}{BOLD}")
|
||||
say(url_for('forum_topic', f=f, page=(t, "fin"), _anchor=str(c.id), _external=True))
|
||||
|
||||
# Redirect to empty the form
|
||||
return redirect(url_for('forum_topic', f=f, page=(t, "fin"),
|
||||
_anchor=str(c.id)))
|
||||
|
|
|
@ -5,7 +5,7 @@ from app.utils.render import render
|
|||
|
||||
@app.route('/')
|
||||
def index():
|
||||
return render('index.html')
|
||||
return render('index.html', styles=["+css/homepage.css"])
|
||||
|
||||
|
||||
@app.errorhandler(404)
|
||||
|
|
|
@ -34,7 +34,8 @@ def poll_vote(poll_id):
|
|||
db.session.add(answer)
|
||||
db.session.commit()
|
||||
|
||||
flash('Le vote a été pris en compte', 'info')
|
||||
flash('Le vote a été pris en compte', 'ok')
|
||||
app.v5logger.info(f"<{current_user.name}> has voted on the poll #{poll.id}")
|
||||
|
||||
if request.referrer:
|
||||
return redirect(request.referrer)
|
||||
|
|
|
@ -84,6 +84,10 @@ def edit_post(postid):
|
|||
for a, file in attachments:
|
||||
a.set_file(file)
|
||||
|
||||
flash('Modifications enregistrées', 'ok')
|
||||
admin_msg = "[admin] " if current_user != p.author else ""
|
||||
app.v5logger.info(f"{admin_msg}<{current_user.name}> has edited the post #{p.id}")
|
||||
|
||||
# Determine topic URL now, in case forum was changed
|
||||
if isinstance(p, Topic):
|
||||
return redirect(url_for('forum_topic', f=p.forum, page=(p,1)))
|
||||
|
@ -111,6 +115,10 @@ def delete_post(postid):
|
|||
if not current_user.can_delete_post(p):
|
||||
abort(403)
|
||||
|
||||
# Is a penalty deletion
|
||||
is_penalty = request.args.get('penalty') == 'True' \
|
||||
and current_user.priv('delete.posts')
|
||||
|
||||
# Users who need to have their trophies updated
|
||||
authors = set()
|
||||
|
||||
|
@ -126,16 +134,21 @@ def delete_post(postid):
|
|||
authors.add(comment.author)
|
||||
|
||||
if isinstance(p.author, Member):
|
||||
factor = 3 if request.args.get('penalty') == 'True' else 1
|
||||
factor = 3 if is_penalty else 1
|
||||
p.author.add_xp(xp * factor)
|
||||
db.session.merge(p.author)
|
||||
authors.add(p.author)
|
||||
|
||||
admin_msg = "[admin] " if current_user != p.author else ""
|
||||
p.delete()
|
||||
db.session.commit()
|
||||
|
||||
for author in authors:
|
||||
author.update_trophies("new-post")
|
||||
|
||||
flash("Le contenu a été supprimé", 'ok')
|
||||
penalty_msg = " (with penalty)" if is_penalty else ""
|
||||
app.v5logger.info(f"{admin_msg}<{current_user.name}> has deleted the post #{p.id}{penalty_msg}")
|
||||
|
||||
return redirect(next_page)
|
||||
|
||||
|
@ -149,6 +162,9 @@ def set_post_topcomment(postid):
|
|||
comment.thread.top_comment = comment
|
||||
db.session.add(comment.thread)
|
||||
db.session.commit()
|
||||
flash("Le post a été défini comme nouvel en-tête", 'ok')
|
||||
admin_msg = "[admin] " if current_user != comment.author else ""
|
||||
app.v5logger.info(f"{admin_msg}<{current_user.name}> has set a new top comment on thread #{comment.thread.id}")
|
||||
|
||||
return redirect(request.referrer)
|
||||
|
||||
|
@ -191,8 +207,11 @@ def move_post(postid):
|
|||
comment.thread = thread
|
||||
db.session.add(comment)
|
||||
db.session.commit()
|
||||
flash("Le topic a été déplacé", 'ok')
|
||||
admin_msg = "[admin] " if current_user != comment.author else ""
|
||||
app.v5logger.info(f"{admin_msg}<{current_user.name}> has moved the comment #{comment.id} to thread #{thread.id}")
|
||||
return redirect(url_for('forum_topic', f=t.forum, page=(t,1)))
|
||||
|
||||
|
||||
return render('post/move_post.html', comment=comment,
|
||||
search_form=search_form, move_form=move_form)
|
||||
|
||||
|
@ -212,8 +231,10 @@ def lock_thread(postid):
|
|||
db.session.commit()
|
||||
|
||||
if post.thread.locked:
|
||||
flash(f"Le thread a été verrouillé")
|
||||
flash(f"Le thread a été verrouillé", 'ok')
|
||||
app.v5logger.info(f"[admin] <{current_user.name}> has locked the thread #{post.thread.id}")
|
||||
else:
|
||||
flash(f"Le thread a été déverrouillé")
|
||||
flash(f"Le thread a été déverrouillé", 'ok')
|
||||
app.v5logger.info(f"[admin] <{current_user.name}> has unlocked the thread #{post.thread.id}")
|
||||
|
||||
return redirect(request.referrer)
|
|
@ -4,6 +4,7 @@ from app.models.program import Program
|
|||
from app.models.comment import Comment
|
||||
from app.models.thread import Thread
|
||||
from app.utils.render import render
|
||||
from app.utils.glados import say, BOLD
|
||||
from app.forms.forum import CommentForm, AnonymousCommentForm
|
||||
from config import V5Config
|
||||
|
||||
|
@ -41,6 +42,10 @@ def program_view(page):
|
|||
current_user.update_trophies('new-post')
|
||||
|
||||
flash('Message envoyé', 'ok')
|
||||
app.v5logger.info(f"<{c.author.name}> has posted a the comment #{c.id}")
|
||||
say(f"Nouveau commentaire de {author.name} sur le programme : {BOLD}{p.name}{BOLD}")
|
||||
say(url_for('program_view', page=(p, "fin"), _anchor=str(c.id), _external=True))
|
||||
|
||||
# Redirect to empty the form
|
||||
return redirect(url_for('program_view', page=(p, "fin"), _anchor=str(c.id)))
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ from app.models.comment import Comment
|
|||
from app.models.tag import Tag
|
||||
from app.models.attachment import Attachment
|
||||
from app.utils.render import render
|
||||
from app.utils.glados import say, BOLD
|
||||
from app.forms.programs import ProgramCreationForm
|
||||
|
||||
from flask_login import login_required, current_user
|
||||
|
@ -54,6 +55,10 @@ def program_submit():
|
|||
current_user.update_trophies('new-program')
|
||||
|
||||
flash('Le programme a bien été soumis', 'ok')
|
||||
return redirect(url_for('program_index'))
|
||||
app.v5logger.info(f"<{p.author.name}> has submitted the program #{c.id}")
|
||||
say(f"Nouveau programme de {current_user.name} : {BOLD}{p.name}{BOLD}")
|
||||
say(url_for('program_view', page=(p, 1), _external=True))
|
||||
|
||||
return redirect(url_for('program_view', page=(p, 1)))
|
||||
|
||||
return render('/programs/submit.html', form=form)
|
||||
|
|
|
@ -163,6 +163,7 @@ input[type="submit"]:focus {
|
|||
background: var(--links);
|
||||
color: var(--warn-text);
|
||||
border-radius: 1px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.skip-to-content-link:focus {
|
||||
transform: translateY(0%);
|
||||
|
|
|
@ -1,135 +1,100 @@
|
|||
.home-title {
|
||||
margin: 20px 0;
|
||||
padding: 10px 5%;
|
||||
background: #bf1c11;
|
||||
box-shadow: 0 2px 2px rgba(0,0,0,.3);
|
||||
border-top: 10px solid #ab170c;
|
||||
}
|
||||
.home-title h1 {
|
||||
margin-top: 0;
|
||||
color: #ffffff;
|
||||
border-color: #ffffff;
|
||||
}
|
||||
.home-title p {
|
||||
margin-bottom: 0;
|
||||
text-align: justify;
|
||||
color: #ffffff;
|
||||
}
|
||||
.home-title a {
|
||||
color: inherit;
|
||||
text-decoration: underline;
|
||||
}
|
||||
.home-pinned-content > div {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.home-pinned-content h2 {
|
||||
display: block;
|
||||
margin: 5px 0;
|
||||
font-size: 18px;
|
||||
font-family: NotoSans;
|
||||
font-weight: 200;
|
||||
line-height: 20px;
|
||||
}
|
||||
.home-pinned-content a {
|
||||
display: block;
|
||||
}
|
||||
.home-pinned-content a:hover img,
|
||||
.home-pinned-content a:focus img {
|
||||
filter: blur(3px);
|
||||
}
|
||||
.home-pinned-content a:hover div,
|
||||
.home-pinned-content a:focus div {
|
||||
padding: 200px 5% 10px 5%;
|
||||
background-image: linear-gradient(180deg,transparent 0,rgba(0,0,0,.7)40px,rgba(0,0,0,.8));
|
||||
}
|
||||
.home-pinned-content img {
|
||||
width: 100%;
|
||||
filter: blur(0px);
|
||||
}
|
||||
.home-pinned-content article {
|
||||
flex-grow: 1;
|
||||
margin: 0 1px;
|
||||
padding: 0;
|
||||
position: relative;
|
||||
max-width: 250px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.home-pinned-content article div {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
z-index: 3;
|
||||
.home-pinned-content {
|
||||
width: 90%;
|
||||
margin: 0;
|
||||
padding: 30px 5% 10px 5%;
|
||||
color: #ffffff;
|
||||
text-shadow: 1px 1px 0 rgba(0,0,0,.6);
|
||||
background-image: linear-gradient(180deg,transparent 0,rgba(0,0,0,.7)40px,rgba(0,0,0,.8));
|
||||
display: grid;
|
||||
grid-template-areas: 'banner news''welcome news''shout news''projects projects';
|
||||
grid-template-rows: auto auto minmax(200px,1fr)auto;
|
||||
grid-template-columns: 4fr 3fr;
|
||||
}
|
||||
.home-articles {
|
||||
.home-pinned-content > * {
|
||||
margin: 10px 20px;
|
||||
}
|
||||
.home-pinned-content > * h1 {
|
||||
font-size: 18px;
|
||||
}
|
||||
@media screen and (max-width:1449px) {
|
||||
.home-pinned-content {
|
||||
width: 97%;
|
||||
}
|
||||
}
|
||||
@media screen and (max-width:1199px) {
|
||||
.home-pinned-content {
|
||||
width: 100%;
|
||||
grid-template-areas: 'welcome''banner''news''shout''projects';
|
||||
grid-template-rows: auto;
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
.home-banner {
|
||||
grid-area: banner;
|
||||
text-align: center;
|
||||
}
|
||||
.home-banner img {
|
||||
max-width: 100%;
|
||||
}
|
||||
.home-welcome {
|
||||
grid-area: welcome;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
}
|
||||
.home-articles > div {
|
||||
.home-welcome h1 {
|
||||
width: 100%;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.home-welcome ul {
|
||||
padding-left: 20px;
|
||||
}
|
||||
.home-welcome div {
|
||||
flex-grow: 1;
|
||||
max-width: 48%;
|
||||
}
|
||||
.home-articles h1 {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
.home-articles h1 a {
|
||||
padding: 0;
|
||||
font-family: NotoSans;
|
||||
font-size: 16px;
|
||||
font-weight: 400;
|
||||
color: #234d5f;
|
||||
}
|
||||
.home-articles h1 a:hover,
|
||||
.home-articles h1 a:focus {
|
||||
padding-right: 10px;
|
||||
}
|
||||
.home-articles p {
|
||||
.home-welcome h2 {
|
||||
margin: 5px 0;
|
||||
text-align: justify;
|
||||
color: #808080;
|
||||
}
|
||||
.home-articles article {
|
||||
padding: 10px;
|
||||
margin: 10px 0;
|
||||
.home-news {
|
||||
grid-area: news;
|
||||
}
|
||||
.home-news ul {
|
||||
padding: 0;
|
||||
}
|
||||
.home-news li {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
background: #ffffff;
|
||||
border: 1px solid rgba(0,0,0,.2);
|
||||
flex-wrap: nowrap;
|
||||
padding: 10px 0;
|
||||
border-bottom: var(--hr-border);
|
||||
}
|
||||
.home-articles article > img {
|
||||
float: left;
|
||||
margin-right: 10px;
|
||||
flex-shrink: 0;
|
||||
.home-news li > a {
|
||||
align-self: baseline;
|
||||
}
|
||||
.home-articles article > img.screeshot {
|
||||
width: 128px;
|
||||
height: 64px;
|
||||
.home-news li img {
|
||||
max-width: 100px;
|
||||
max-height: 100px;
|
||||
margin-right: 8px;
|
||||
}
|
||||
.home-articles article > div {
|
||||
flex-shrink: 1;
|
||||
}
|
||||
.home-articles article h3 {
|
||||
.home-news li h3 {
|
||||
margin: 0;
|
||||
color: #424242;
|
||||
font-weight: normal;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
font-family: Cantarell;
|
||||
}
|
||||
.home-articles article a:hover,
|
||||
.home-articles article a:focus {
|
||||
text-decoration: underline;
|
||||
.home-news li .date {
|
||||
margin: 4px 0 10px 0;
|
||||
}
|
||||
.home-articles .metadata {
|
||||
margin: 0;
|
||||
color: #22292c;
|
||||
.home-news li div {
|
||||
font-size: 13px;
|
||||
line-height: 150%;
|
||||
}
|
||||
.home-articles .metadata a {
|
||||
color: #22292c;
|
||||
font-weight: 400;
|
||||
font-style: italic;
|
||||
@media screen and (max-width:499px) {
|
||||
.home-news li {
|
||||
flex-direction: column;
|
||||
align-items: start;
|
||||
}
|
||||
}
|
||||
.home-shoutbox {
|
||||
grid-area: shout;
|
||||
}
|
||||
.home-projects {
|
||||
grid-area: projects;
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
../../../submodules/v5shoutbox/style.css
|
|
@ -161,6 +161,7 @@ button, .button, input[type="button"], input[type="submit"] {
|
|||
background: var(--links);
|
||||
color: var(--warn-text);
|
||||
border-radius: 1px;
|
||||
overflow: hidden;
|
||||
|
||||
&:focus {
|
||||
transform: translateY(0%);
|
||||
|
|
|
@ -1,140 +1,131 @@
|
|||
/*
|
||||
home-title
|
||||
*/
|
||||
@import "vars";
|
||||
|
||||
.home-title {
|
||||
margin: 20px 0; padding: 10px 5%;
|
||||
background: #bf1c11; box-shadow: 0 2px 2px rgba(0, 0, 0, .3);
|
||||
border-top: 10px solid #ab170c;
|
||||
.home-pinned-content {
|
||||
width: 90%;
|
||||
display: grid;
|
||||
grid-template-areas:
|
||||
'banner news'
|
||||
'welcome news'
|
||||
'shout news'
|
||||
'projects projects';
|
||||
grid-template-rows: auto auto minmax(200px, 1fr) auto;
|
||||
grid-template-columns: 4fr 3fr;
|
||||
|
||||
h1 {
|
||||
margin-top: 0;
|
||||
color: #ffffff; border-color: #ffffff;
|
||||
@media screen and (max-width: @normal) {
|
||||
width: 97%;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-bottom: 0; text-align: justify;
|
||||
color: #ffffff;
|
||||
@media screen and (max-width: @small) {
|
||||
width: 100%;
|
||||
grid-template-areas:
|
||||
'welcome'
|
||||
'banner'
|
||||
'news'
|
||||
'shout'
|
||||
'projects';
|
||||
grid-template-rows: auto;
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
a {
|
||||
color: inherit; text-decoration: underline;
|
||||
& > * {
|
||||
//border: 1px solid red;
|
||||
margin: 10px 20px;
|
||||
|
||||
h1 {
|
||||
font-size: 18px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.home-banner {
|
||||
grid-area: banner;
|
||||
text-align: center;
|
||||
|
||||
img {
|
||||
max-width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
pinned-content
|
||||
*/
|
||||
.home-welcome {
|
||||
grid-area: welcome;
|
||||
|
||||
.home-pinned-content {
|
||||
& > div {
|
||||
display: flex; justify-content: space-between;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
|
||||
justify-content: center;
|
||||
|
||||
h1 {
|
||||
width: 100%;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
ul {
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
div {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
h2 {
|
||||
display: block; margin: 5px 0;
|
||||
font-size: 18px; font-family: NotoSans; font-weight: 200;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
a {
|
||||
display: block;
|
||||
|
||||
&:hover, &:focus {
|
||||
img {
|
||||
filter: blur(3px);
|
||||
}
|
||||
div {
|
||||
padding: 200px 5% 10px 5%;
|
||||
background-image: linear-gradient(180deg,transparent 0,rgba(0,0,0,.7) 40px,rgba(0,0,0,.8));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
img {
|
||||
width: 100%; filter: blur(0px);
|
||||
}
|
||||
|
||||
article {
|
||||
flex-grow: 1; margin: 0 1px; padding: 0;
|
||||
position: relative;
|
||||
max-width: 250px; overflow: hidden;
|
||||
|
||||
div {
|
||||
position: absolute; bottom: 0; z-index: 3;
|
||||
width: 90%; margin: 0;
|
||||
padding: 30px 5% 10px 5%;
|
||||
color: #ffffff; text-shadow: 1px 1px 0 rgba(0,0,0,.6);
|
||||
background-image: linear-gradient(180deg,transparent 0,rgba(0,0,0,.7) 40px,rgba(0,0,0,.8));
|
||||
}
|
||||
margin: 5px 0;
|
||||
}
|
||||
}
|
||||
|
||||
.home-news {
|
||||
grid-area: news;
|
||||
|
||||
/*
|
||||
home-articles
|
||||
*/
|
||||
|
||||
.home-articles {
|
||||
display: flex; justify-content: space-between;
|
||||
|
||||
& > div {
|
||||
flex-grow: 1; max-width: 48%;
|
||||
ul {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
h1 {
|
||||
display: flex; justify-content: space-between; align-items: center;
|
||||
li {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
flex-wrap: nowrap;
|
||||
padding: 10px 0;
|
||||
|
||||
a {
|
||||
padding: 0;
|
||||
font-family: NotoSans; font-size: 16px;
|
||||
font-weight: 400; color: /*#015078*/ /*#bf1c11*/ #234d5f;
|
||||
border-bottom: var(--hr-border);
|
||||
|
||||
&:hover, &:focus {
|
||||
padding-right: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 5px 0;
|
||||
text-align: justify;
|
||||
color: #808080;
|
||||
}
|
||||
|
||||
article {
|
||||
padding: 10px; margin: 10px 0; display: flex; align-items: center;
|
||||
background: #ffffff; border: 1px solid rgba(0, 0, 0, .2);
|
||||
|
||||
& > img {
|
||||
float: left; margin-right: 10px; flex-shrink: 0;
|
||||
|
||||
&.screeshot {
|
||||
width: 128px; height: 64px;
|
||||
}
|
||||
@media screen and (max-width: @micro) {
|
||||
flex-direction: column;
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
& > div {
|
||||
flex-shrink: 1;
|
||||
& > a {
|
||||
align-self: baseline;
|
||||
}
|
||||
|
||||
img {
|
||||
max-width: 100px;
|
||||
max-height: 100px;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
h3 {
|
||||
margin: 0;
|
||||
color: #424242; font-weight: normal;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
font-family: Cantarell;
|
||||
}
|
||||
|
||||
a:hover, a:focus {
|
||||
text-decoration: underline;
|
||||
.date {
|
||||
margin: 4px 0 10px 0;
|
||||
}
|
||||
}
|
||||
|
||||
.metadata {
|
||||
margin: 0;
|
||||
color: #22292c;
|
||||
|
||||
a {
|
||||
color: #22292c; font-weight: 400; font-style: italic;
|
||||
|
||||
div {
|
||||
font-size: 13px;
|
||||
line-height: 150%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.home-shoutbox {
|
||||
grid-area: shout;
|
||||
}
|
||||
|
||||
.home-projects {
|
||||
grid-area: projects;
|
||||
}
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
../../../submodules/v5shoutbox/v5shoutbox.js
|
|
@ -39,7 +39,7 @@
|
|||
{% endfor %}
|
||||
</div>
|
||||
<div>
|
||||
{{ form.guidelines.label }}
|
||||
<label for="guidelines">J'accepte les <a href="#">CGU</a></label>
|
||||
{{ form.guidelines() }}
|
||||
{% for error in form.guidelines.errors %}
|
||||
<span class="msgerror">{{ error }}</span>
|
||||
|
|
|
@ -14,8 +14,8 @@
|
|||
|
||||
{% include "base/flash.html" %}
|
||||
|
||||
<div id="main-content"></div>
|
||||
{% block content %}
|
||||
<div id="main-content"></div>
|
||||
{% endblock %}
|
||||
|
||||
{% include "base/footer.html" %}
|
||||
|
|
|
@ -4,5 +4,11 @@
|
|||
{% if current_user.is_authenticated and current_user.priv('misc.dev-infos') %}
|
||||
<p>Page générée en {{ "%.3f" % g.request_time() }} secondes.</p>
|
||||
{% endif %}
|
||||
<<<<<<< HEAD
|
||||
<<<<<<< HEAD
|
||||
<p>Ceci est un environnement de test. Tout contenu peut être supprimé sans avertissement préalable.</p>
|
||||
=======
|
||||
>>>>>>> e15005a... Ajout des stats sur la durée de chargement
|
||||
=======
|
||||
>>>>>>> e15005a427f95829bbbad8f0d625ab9cb0c30e69
|
||||
</footer>
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
</form>
|
||||
|
||||
<div id="spotlight">
|
||||
<a href="/forum/actus/projets/2/fin/avancees-de-la-v5" class="button bg-error">Infos sur l'avancée de la v5</a>
|
||||
<a href="/forum/projets/2/fin/avancees-de-la-v5" class="button bg-error">Infos sur l'avancée de la v5</a>
|
||||
</div>
|
||||
|
||||
{% if current_user.is_authenticated and current_user.priv('misc.dev-infos') %}
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
{% for s in scripts %}
|
||||
<script type="text/javascript" src={{url_for('static', filename=s)}}></script>
|
||||
{% endfor %}
|
||||
<script type="module" src={{url_for('static', filename='scripts/emoji-picker-element/index.js')}}></script>
|
||||
{% for m in modules %}
|
||||
<script type="module" src={{url_for('static', filename=m)}}></script>
|
||||
{% endfor %}
|
|
@ -0,0 +1,10 @@
|
|||
{% set tabtitle = "Shoutbox" %}
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="fr-FR">
|
||||
{% include "base/head.html" with context %}
|
||||
<body>
|
||||
{% include "widgets/v5shoutbox.html" %}
|
||||
{% include "base/scripts.html" %}
|
||||
</body>
|
||||
</html>
|
|
@ -8,11 +8,114 @@
|
|||
|
||||
{% block content %}
|
||||
<section class="home-pinned-content">
|
||||
<div>
|
||||
<div class="home-banner">
|
||||
<img src="https://www.planet-casio.com/storage/staff/CPC30-banner.png" />
|
||||
</div>
|
||||
<div class="home-welcome">
|
||||
<h1>Bienvenue sur Planète Casio !</h1>
|
||||
<p>Planète Casio est la communauté française de référence pour toutes les calculatrices Casio.
|
||||
Apprenez à utiliser votre machine, téléchargez et partagez des programmes, ou initiez-vous à l'informatique sur le forum.
|
||||
Ou bien venez développer des jeux avec nous pour passer le temps !</p>
|
||||
<div>
|
||||
<h2>Les calculatrices</h2>
|
||||
<ul>
|
||||
<li>Tout sur sa Casio</li>
|
||||
<li>Graph 25+E II</li>
|
||||
<li>Graph 35+E II</li>
|
||||
<li>Graph 90+E</li>
|
||||
<li>Classpad 400+E</li>
|
||||
<li>Comparer les calculatrices CASIO</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<h2>La communauté</h2>
|
||||
<ul>
|
||||
<li>S’inscrire ou se connecter</li>
|
||||
<li>Index du forum</li>
|
||||
<li>Nos jeux outils et cours</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<h2>Programmer</h2>
|
||||
<ul>
|
||||
<li>Apprendre à programmer</li>
|
||||
<li>Articles et astuces</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="home-news">
|
||||
<h1>Actualitées</h1>
|
||||
<ul>
|
||||
<li>Inscription : dans le menu "Compte" à gauche (les comptes seront ultimement reconnectés à la version originale du site)</li>
|
||||
<li>Le forum est fonctionnel, les programmes arrivent sous peu.</li>
|
||||
<li>Pour toute demande particulière, vous pouvez envoyer un email à <code>contact (at) planet-casio (dot) com</code>.</li>
|
||||
<li>
|
||||
<a href="#"><img src="https://www.planet-casio.com/images/staff/tdm9_collision2.jpg"/></a>
|
||||
<div>
|
||||
<h3><a href="#">Les collisions — partie 2</a></h3>
|
||||
<p class="date"><i>Publié par <a href="#">Shadow15510</a> le <time>09/06/2023 09:10</time></i></p>
|
||||
La deuxième partie du tutoriel du mercredi sur les collisions est maintenant disponible en vidéo.
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#"><img src="https://www.planet-casio.com/images/staff/CPC30-image-thumb-results.png"/></a>
|
||||
<div>
|
||||
<h3><a href="#">Résultats du CPC #30 — Les profondeurs !</a>
|
||||
</h3>
|
||||
<p class="date"><i>Publié par <a href="#">Lephenixnoir</a> le <time>08/06/2023 22:10</time></i></p>
|
||||
Les programmes sont profonds et avec un peu de chance les tests aussi ! </div>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#"><img src="https://www.planet-casio.com/images/staff/tituya-thumb.png"/></a>
|
||||
<div>
|
||||
<h3><a href="#">Un second renardministrateur !</a>
|
||||
</h3>
|
||||
<p class="date"><i>Publié par <a href="#">Lephenixnoir</a> le <time>07/06/2023 22:55</time></i></p>
|
||||
Vive la renardocratie ! </div>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#"><img src="https://www.planet-casio.com/images/staff/massy2.jpg"/></a>
|
||||
<div>
|
||||
<h3><a href="#">Visite chez Casio France à Massy, musée inclus</a>
|
||||
</h3>
|
||||
<p class="date"><i>Publié par <a href="#">Critor</a> le <time>05/06/2023 11:51</time></i></p>
|
||||
Compte-rendu de notre visite au sein même des locaux de Casio France à Massy en mai 2023, passage par leur musée privé inclus. </div>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#"><img src="https://www.planet-casio.com/images/staff/CPC30-image-title.jpg"/></a>
|
||||
<div>
|
||||
<h3><a href="#">Le CPC#30 - Les Profondeurs ... C'est désormais terminé ... Bravo à tous les participants.</a>
|
||||
</h3>
|
||||
<p class="date"><i>Publié par <a href="#">Slyvtt</a> le <time>03/06/2023 21:36</time></i></p>
|
||||
Aujourd'hui sonne la fin du CPC#30 avec à la clef 6 participations pour vous donner du fun. Graphs Monochromes et Couleurs sont à la fête avec des programmes en Basic et des Addins. </div>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#"><img src="https://www.planet-casio.com/images/staff/tdm9_miniature.jpg"/></a>
|
||||
<div>
|
||||
<h3><a href="#">Les Tutos du Mercredi débarquent sur Youtube !</a>
|
||||
</h3>
|
||||
<p class="date"><i>Publié par <a href="#">Shadow15510</a> le <time>29/05/2023 14:15</time></i></p>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#"><img src="https://www.planet-casio.com/images/staff/CPC30-image-thumb.jpg"/></a>
|
||||
<div>
|
||||
<h3><a href="#">Le CPC #30 - Les profondeurs !</a>
|
||||
</h3>
|
||||
<p class="date"><i>Publié par <a href="#">Lephenixnoir</a> le <time>27/05/2023 18:00</time></i></p>
|
||||
Le concours CPC revient en force et c'est le moment de se plonger (métaphoriquement <i>et</i> littéralement !) dans le game design fin et la programmation sportive. Une semaine pour les programmer tous, et dans les profondeurs les li— oups ! </div>
|
||||
</li>
|
||||
<li><p><i><a href="https://www.planet-casio.com/Fr/forums/partie6-.html">Voir toutes les news</a></i></p></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="home-shoutbox" style="border: 1px solid #737373; padding: 20px">
|
||||
<div id="v5shoutbox">
|
||||
Ici y’aura la shoutbox (plus tard)
|
||||
</div>
|
||||
</div>
|
||||
<div class="home-projects">
|
||||
<h1>Projets du moment</h1>
|
||||
<ul>
|
||||
<li>La v5, bien sûr</li>
|
||||
<li>Chaos drop</li>
|
||||
<li>Un easter egg</li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
{% import "widgets/editor.html" as widget_editor %}
|
||||
{% import "widgets/tag_selector.html" as widget_tag_selector with context %}
|
||||
|
||||
{% set tabtitle = f"Programmes - Soumettre un programme" %}
|
||||
{% set tabtitle = "Programmes - Soumettre un programme" %}
|
||||
|
||||
{% block title %}
|
||||
<a href="{{ url_for('program_index') }}">Programmes</a> » <h1>Soumettre un programme</h1>
|
||||
|
|
|
@ -32,7 +32,7 @@
|
|||
<a href="{{ url_for('move_post', postid=post.id) }}">Déplacer</a>
|
||||
{% endif %}
|
||||
|
||||
{% if can_punish %}
|
||||
{% if can_punish and post.author.type == "member"%}
|
||||
<a href="{{ url_for('delete_post', postid=post.id, penalty=False, csrf_token=csrf_token()) }}" onclick="return confirm('Le post sera supprimé.')">Supprimer{{ suffix }} (normal)</a>
|
||||
<a href="{{ url_for('delete_post', postid=post.id, penalty=True, csrf_token=csrf_token()) }}" onclick="return confirm('Le post sera supprimé avec pénalité d\'XP.')">Supprimer{{ suffix }} (pénalité)</a>
|
||||
{% elif can_delete %}
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
../../../submodules/v5shoutbox/widget.html
|
|
@ -36,4 +36,4 @@ def say(msg, channels = ["#general"]):
|
|||
|
||||
def new_topic(topic):
|
||||
""" Example wrapper for glados.say """
|
||||
say(f"Le topic {BOLD}{topic.title}{BOLD} a été créé")
|
||||
say(f"Nouveau topic de {topic.author.name}: {BOLD}{topic.title}{BOLD}")
|
||||
|
|
|
@ -16,18 +16,24 @@ def get_member(username):
|
|||
return None
|
||||
|
||||
|
||||
def edit(user, fields):
|
||||
def edit(old_username, new_member):
|
||||
""" Edit a user. Fields is {'name': ['value'], …} """
|
||||
old_username = normalize(old_username)
|
||||
conn = ldap.initialize("ldap://localhost")
|
||||
# TODO: do this
|
||||
# Connect as root
|
||||
# conn.simple_bind_s(f'cn=ldap-root,{V5Config.LDAP_ENV}',
|
||||
# V5Config.LDAP_PASSWORD)
|
||||
# old_value = {"userPassword": ["my_old_password"]}
|
||||
# new_value = {"userPassword": ["my_new_password"]}
|
||||
conn.simple_bind_s(f'cn=ldap-root,{V5Config.LDAP_ROOT}',
|
||||
V5Config.LDAP_PASSWORD)
|
||||
# Create values fields
|
||||
old_dn = f'cn={old_username},{V5Config.LDAP_ENV},{V5Config.LDAP_ROOT}'
|
||||
new_dn = f'cn={new_member.norm}'
|
||||
new_values = [
|
||||
(ldap.MOD_REPLACE, 'sn', [new_member.norm.encode('utf-8')]),
|
||||
(ldap.MOD_REPLACE, 'displayName', [new_member.name.encode('utf-8')]),
|
||||
(ldap.MOD_REPLACE, 'mail', [new_member.email.encode('utf-8')]),
|
||||
]
|
||||
|
||||
# modlist = modifyModlist(old_value, new_value)
|
||||
# conn.modify_s(dn, modlist)
|
||||
conn.modify_s(old_dn, new_values)
|
||||
conn.rename_s(old_dn, new_dn)
|
||||
|
||||
|
||||
def set_email(user, email):
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
from flask import render_template
|
||||
from flask_login import current_user
|
||||
|
||||
def render(*args, styles=[], scripts=[], **kwargs):
|
||||
def render(*args, styles=[], scripts=[], modules=[], **kwargs):
|
||||
# Pour jouer sur les feuilles de style ou les scripts :
|
||||
# render('page.html', styles=['-css/form.css', '+css/admin/forms.css'])
|
||||
|
||||
|
@ -29,6 +29,9 @@ def render(*args, styles=[], scripts=[], **kwargs):
|
|||
'scripts/tag_selector.js',
|
||||
'scripts/editor.js',
|
||||
]
|
||||
modules_ = [
|
||||
'scripts/emoji-picker-element/index.js',
|
||||
]
|
||||
|
||||
# Apply theme from user settings
|
||||
theme = current_user.theme if current_user.is_authenticated else ''
|
||||
|
@ -56,4 +59,10 @@ def render(*args, styles=[], scripts=[], **kwargs):
|
|||
if s[0] == '+':
|
||||
scripts_.append(s[1:])
|
||||
|
||||
return render_template(*args, **kwargs, styles=styles_, scripts=scripts_)
|
||||
for m in modules:
|
||||
if m[0] == '-':
|
||||
modules_.remove(m[1:])
|
||||
if m[0] == '+':
|
||||
modules_.append(m[1:])
|
||||
|
||||
return render_template(*args, **kwargs, styles=styles_, scripts=scripts_, modules=modules_)
|
||||
|
|
15
config.py
15
config.py
|
@ -1,4 +1,5 @@
|
|||
import os
|
||||
import logging
|
||||
from datetime import timedelta
|
||||
|
||||
try:
|
||||
|
@ -34,7 +35,7 @@ class FlaskApplicationSettings(object):
|
|||
+ LocalConfig.DB_NAME
|
||||
SQLALCHEMY_TRACK_MODIFICATIONS = False
|
||||
|
||||
MAIL_DEFAULT_SENDER = "noreply@v5.planet-casio.com"
|
||||
MAIL_DEFAULT_SENDER = "noreply-v5@planet-casio.com"
|
||||
MAIL_SUPPRESS_SEND = None
|
||||
|
||||
# Only send cookies over HTTPS connections (use only if HTTPS is enabled)
|
||||
|
@ -86,6 +87,18 @@ class DefaultConfig(object):
|
|||
# Tab title prefix. Useful to dissociate local/dev/prod tabs
|
||||
TABTITLE_PREFIX = ""
|
||||
|
||||
@staticmethod
|
||||
def v5logger():
|
||||
""" A fully configured logger for v5 activity logs """
|
||||
logger = logging.getLogger('v5')
|
||||
logger.setLevel(logging.DEBUG)
|
||||
formatter = logging.Formatter('[%(asctime)s] %(levelname)s: %(message)s')
|
||||
handler = logging.FileHandler('v5_activity.log')
|
||||
handler.setLevel(logging.DEBUG)
|
||||
handler.setFormatter(formatter)
|
||||
logger.addHandler(handler)
|
||||
return logger
|
||||
|
||||
|
||||
class V5Config(LocalConfig, DefaultConfig):
|
||||
"""
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
Subproject commit 1b7ae3d2e3c609a4424f90249d8fea7b879d52e5
|
|
@ -1 +0,0 @@
|
|||
Subproject commit f57420ce88b4ef69ecd6f4127c41146f53e709f7
|
Loading…
Reference in New Issue