From 662882cc15de06ce0965fb26a5f054b6cf603b1c Mon Sep 17 00:00:00 2001 From: Darks Date: Tue, 3 Dec 2019 20:32:01 +0100 Subject: [PATCH] =?UTF-8?q?Ajout=20des=20commentaires=20de=20topics=20On?= =?UTF-8?q?=20ne=20peut=20pas=20encore=20modifier=20le=20top=20comment=20n?= =?UTF-8?q?i=20commencer=20un=20topic=20=C3=A0=20partir=20d'un=20thread=20?= =?UTF-8?q?externe,=20mais=20les=20bases=20sont=20l=C3=A0=20:)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/__init__.py | 18 ++++-- app/forms/account.py | 102 +++++++++++++++--------------- app/forms/editor.py | 15 ----- app/forms/forum.py | 14 ++-- app/models/comment.py | 1 + app/routes/forum/index.py | 20 +++--- app/routes/forum/topic.py | 30 +++++++++ app/static/css/table.css | 11 +++- app/templates/forum/forum.html | 34 +++++++--- app/templates/forum/topic.html | 33 ++++++++++ app/templates/widgets/editor.html | 10 +-- app/utils/converters.py | 7 +- 12 files changed, 191 insertions(+), 104 deletions(-) delete mode 100644 app/forms/editor.py create mode 100644 app/routes/forum/topic.py create mode 100644 app/templates/forum/topic.html diff --git a/app/__init__.py b/app/__init__.py index 2f8ebdd..9bc5e67 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -11,19 +11,27 @@ app.config.from_object(Config) db = SQLAlchemy(app) migrate = Migrate(app, db) +login = LoginManager(app) +login.login_view = 'login' +login.login_message = "Veuillez vous authentifier avant de continuer." + + from app.utils.converters import * app.url_map.converters['topicslug'] = TopicSlugConverter app.url_map.converters['forum'] = ForumConverter + @app.before_request def request_time(): g.request_start_time = time.time() g.request_time = lambda: "%.5fs" % (time.time() - g.request_start_time) -login = LoginManager(app) -login.login_view = 'login' -login.login_message = "Veuillez vous authentifier avant de continuer." - +@app.context_processor +def utilities_processor(): + return dict( + len=len, + enumerate=enumerate + ) from app import models # IDK why this is here, but it works from app.models.comment import Comment @@ -35,7 +43,7 @@ from app.models.notification import Notification from app.routes import index, search, users, tools # To load routes at initialization from app.routes.account import login, account, notification from app.routes.admin import index, groups, account, trophies, forums -from app.routes.forum import index +from app.routes.forum import index, topic from app.utils import pluralize # To use pluralize into the templates from app.utils import date diff --git a/app/forms/account.py b/app/forms/account.py index 7f2c6f6..259e110 100644 --- a/app/forms/account.py +++ b/app/forms/account.py @@ -9,44 +9,44 @@ import app.utils.validators as vd class RegistrationForm(FlaskForm): username = StringField( - 'Pseudonyme', - description='Ce nom est définitif !', + 'Pseudonyme', + description='Ce nom est définitif !', validators=[ - DataRequired(), - vd.name_valid, + DataRequired(), + vd.name_valid, vd.name_available, ], ) email = StringField( - 'Adresse Email', + 'Adresse Email', validators=[ - DataRequired(), - Email(message="Addresse email invalide."), + DataRequired(), + Email(message="Addresse email invalide."), vd.email, ], ) password = PasswordField( - 'Mot de passe', + 'Mot de passe', validators=[ - DataRequired(), + DataRequired(), vd.password, ], ) password2 = PasswordField( - 'Répéter le mot de passe', + 'Répéter le mot de passe', validators=[ - DataRequired(), + DataRequired(), EqualTo('password', message="Les mots de passe doivent être identiques."), ], ) guidelines = BooleanField( - """J'accepte les CGU""", + """J'accepte les CGU""", validators=[ DataRequired(), ], ) newsletter = BooleanField( - 'Inscription à la newsletter', + 'Inscription à la newsletter', description='Un mail par trimestre environ, pour être prévenu des concours, évènements et nouveautés.', ) submit = SubmitField( @@ -56,62 +56,62 @@ class RegistrationForm(FlaskForm): class UpdateAccountForm(FlaskForm): avatar = FileField( - 'Avatar', + 'Avatar', validators=[ - Optional(), + Optional(), vd.avatar, ], ) email = StringField( - 'Adresse email', + 'Adresse email', validators=[ - Optional(), - Email(message="Addresse email invalide."), - vd.email, + Optional(), + Email(message="Addresse email invalide."), + vd.email, vd.old_password, ], ) password = PasswordField( - 'Mot de passe', + 'Mot de passe', validators=[ - Optional(), - vd.password, + Optional(), + vd.password, vd.old_password, ], ) password2 = PasswordField( - 'Répéter le mot de passe', + 'Répéter le mot de passe', validators=[ - Optional(), + Optional(), EqualTo('password', message="Les mots de passe doivent être identiques."), ], ) old_password = PasswordField( - 'Mot de passe actuel', + 'Mot de passe actuel', validators=[ Optional(), ], ) birthday = DateField( - 'Anniversaire', + 'Anniversaire', validators=[ Optional(), ], ) signature = TextAreaField( - 'Signature', + 'Signature', validators=[ Optional(), ] ) biography = TextAreaField( - 'Présentation', + 'Présentation', validators=[ Optional(), ] ) newsletter = BooleanField( - 'Inscription à la newsletter', + 'Inscription à la newsletter', description='Un mail par trimestre environ, pour être prévenu des concours, évènements et nouveautés.', ) submit = SubmitField('Mettre à jour') @@ -119,16 +119,16 @@ class UpdateAccountForm(FlaskForm): class DeleteAccountForm(FlaskForm): delete = BooleanField( - 'Confirmer la suppression', + 'Confirmer la suppression', validators=[ DataRequired(), - ], + ], description='Attention, cette opération est irréversible !' ) old_password = PasswordField( - 'Mot de passe', + 'Mot de passe', validators=[ - DataRequired(), + DataRequired(), vd.old_password, ], ) @@ -139,66 +139,66 @@ class DeleteAccountForm(FlaskForm): class AdminUpdateAccountForm(FlaskForm): username = StringField( - 'Pseudonyme', + 'Pseudonyme', validators=[ - Optional(), + Optional(), vd.name_valid, ], ) avatar = FileField( - 'Avatar', + 'Avatar', validators=[ - Optional(), + Optional(), vd.avatar, ], ) email = StringField( - 'Adresse email', + 'Adresse email', validators=[ - Optional(), - Email(message="Addresse email invalide."), + Optional(), + Email(message="Addresse email invalide."), vd.email, ], ) email_validate = BooleanField( - "Envoyer un email de validation à la nouvelle adresse", + "Envoyer un email de validation à la nouvelle adresse", description="Si décoché, l'utilisateur devra demander explicitement un email "\ "de validation, ou faire valider son adresse email par un administrateur.", ) password = PasswordField( - 'Mot de passe', - description="L'ancien mot de passe ne pourra pas être récupéré !", + 'Mot de passe', + description="L'ancien mot de passe ne pourra pas être récupéré !", validators=[ - Optional(), + Optional(), vd.password, ], ) xp = DecimalField( - 'XP', + 'XP', validators=[ Optional(), ] ) birthday = DateField( - 'Anniversaire', + 'Anniversaire', validators=[ Optional(), ], ) signature = TextAreaField( - 'Signature', + 'Signature', validators=[ Optional(), ], ) biography = TextAreaField( - 'Présentation', + 'Présentation', validators=[ Optional(), ], ) newsletter = BooleanField( - 'Inscription à la newsletter', + 'Inscription à la newsletter', description='Un mail par trimestre environ, pour être prévenu des concours, évènements et nouveautés.', ) submit = SubmitField( @@ -215,10 +215,10 @@ class AdminAccountEditTrophyForm(FlaskForm): class AdminDeleteAccountForm(FlaskForm): delete = BooleanField( - 'Confirmer la suppression', + 'Confirmer la suppression', validators=[ DataRequired(), - ], + ], description='Attention, cette opération est irréversible !', ) submit = SubmitField( diff --git a/app/forms/editor.py b/app/forms/editor.py deleted file mode 100644 index 8e7eb78..0000000 --- a/app/forms/editor.py +++ /dev/null @@ -1,15 +0,0 @@ -from flask_wtf import FlaskForm -from wtforms import TextAreaField - -class EditorForm(FlaskForm): - """ - A text editor with formatting buttons and help. A rendering macro is - defined in the template widgets/editor.html. - """ - - # TODO: How to set DataRequired() dynamically? - contents = TextAreaField() - - @property - def value(self): - return self.contents.data diff --git a/app/forms/forum.py b/app/forms/forum.py index c5d2a80..a6e0cf5 100644 --- a/app/forms/forum.py +++ b/app/forms/forum.py @@ -1,9 +1,13 @@ from flask_wtf import FlaskForm -from wtforms import StringField, FormField, SubmitField -from wtforms.validators import DataRequired -from app.forms.editor import EditorForm +from wtforms import StringField, FormField, SubmitField, TextAreaField +from wtforms.validators import DataRequired, Length class TopicCreationForm(FlaskForm): - title = StringField('Nom du sujet', validators=[DataRequired()]) - message = FormField(EditorForm, 'Premier post') + title = StringField('Nom du sujet', + validators=[DataRequired(), Length(min=3, max=32)]) + message = TextAreaField('Message principal') submit = SubmitField('Créer le sujet') + +class CommentForm(FlaskForm): + message = TextAreaField('Commentaire') + submit = SubmitField('Commenter') diff --git a/app/models/comment.py b/app/models/comment.py index 0889a21..b9e60f2 100644 --- a/app/models/comment.py +++ b/app/models/comment.py @@ -29,6 +29,7 @@ class Comment(Post): Post.__init__(self, author) self.thread = thread + self.text = text def edit(self, new_text): """Edit a Comment's contents.""" diff --git a/app/routes/forum/index.py b/app/routes/forum/index.py index 1834164..13cb0da 100644 --- a/app/routes/forum/index.py +++ b/app/routes/forum/index.py @@ -1,5 +1,5 @@ from flask_login import current_user -from flask import request +from flask import request, abort from app.utils.render import render from app.forms.forum import TopicCreationForm @@ -26,17 +26,17 @@ def forum_page(f): db.session.add(th) db.session.commit() - c = Comment(current_user, form.message.value, th) - th.set_top_comment(c) - t = Topic(f, current_user, form.title.data, th) - - db.session.add(th) + c = Comment(current_user, form.message.data, th) db.session.add(c) + db.session.commit() + + th.set_top_comment(c) + db.session.merge(th) + + t = Topic(f, current_user, form.title.data, th) db.session.add(t) db.session.commit() - return render('/forum/forum.html', f=f, form=form) + print(th, c, t) -@app.route('/forum//') -def forum_topic(f, t): - return render('/forum/topic.html', f=f, t=t) + return render('/forum/forum.html', f=f, form=form) diff --git a/app/routes/forum/topic.py b/app/routes/forum/topic.py new file mode 100644 index 0000000..5496ba4 --- /dev/null +++ b/app/routes/forum/topic.py @@ -0,0 +1,30 @@ +from flask_login import current_user +from flask import request, redirect, url_for, flash + +from app.utils.render import render +from app.forms.forum import CommentForm +from app.models.forum import Forum +from app.models.topic import Topic +from app.models.thread import Thread +from app.models.comment import Comment +from app import app, db + + +@app.route('/forum//', methods=['GET', 'POST']) +def forum_topic(f, t): + form = CommentForm() + + if form.validate_on_submit(): + c = Comment(current_user, form.message.data, t.thread) + db.session.add(c) + db.session.commit() + flash('Message envoyé', 'ok') + # Redirect to empty the form + return redirect(url_for('forum_topic', f=f, t=t)) + + # Update views + t.views += 1 + db.session.merge(t) + db.session.commit() + + return render('/forum/topic.html', t=t, form=form) diff --git a/app/static/css/table.css b/app/static/css/table.css index c70652c..4445169 100644 --- a/app/static/css/table.css +++ b/app/static/css/table.css @@ -42,13 +42,22 @@ table.forumlist tr:nth-child(4n+3) { background: rgba(0, 0, 0, .05); } + /* Topic table */ table.topiclist { - width: 90%; + width: 100%; margin: auto; } table.topiclist tr > *:nth-child(n+2) { /* This matches all children except the first column */ text-align: center; } + + +table.forumlist th > td:last-child, +table.forumlist tr > td:last-child, +table.topiclist th > td:last-child, +table.topiclist tr > td:last-child { + width: 20%; text-align: center; +} diff --git a/app/templates/forum/forum.html b/app/templates/forum/forum.html index 974e89a..45efe52 100644 --- a/app/templates/forum/forum.html +++ b/app/templates/forum/forum.html @@ -10,25 +10,40 @@

{{ f.descr }}

{% if f.topics %} +

Sujets

{% for t in f.topics %} - - - - - + + + + + {% endfor %}
SujetAuteurDate de création CommentairesVues
{{ t.title }}{{ t.author.name }}{{ t.date_created | date }}{{ t.comments | length }}{{ t.views }}
{{ t.title }}{{ t.author.name }}{{ t.date_created | date }}{{ t.thread.comments | length }}{{ t.views }}
- {% else %} + {% elif len(f.sub_forums) == 0 %}

Il n'y a aucun topic sur ce forum ! Animons-le vite !

{% endif %} + + {% if len(f.sub_forums) > 0 %} +

Forums

+ + + + {% for sf in f.sub_forums %} + + + + {% endfor %} + +
{{ f.name }}Nombre de sujets
{{ sf.name }}{{ sf.topics | length }}
{{ sf.descr }}
+ {% endif %} + + {% if len(f.sub_forums) == 0 or (current_user.is_authenticated and current_user.priv('access-admin-board')) %}
-

Créer un nouveau sujet

-
{{ form.hidden_tag() }} @@ -40,10 +55,11 @@ {% endfor %}
- {{ widget_editor.editor(form.message) }} + {{ widget_editor.text_editor(form.message) }}
{{ form.submit(class_='bg-green') }}
+ {% endif %} {% endblock %} diff --git a/app/templates/forum/topic.html b/app/templates/forum/topic.html new file mode 100644 index 0000000..1afe3b4 --- /dev/null +++ b/app/templates/forum/topic.html @@ -0,0 +1,33 @@ +{% extends "base/base.html" %} +{% import "widgets/editor.html" as widget_editor %} + +{% block title %} +Forum de Planète Casio »

{{ t.forum.name }}

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

{{ t.title }}

+ +
{{ t.thread.top_comment.text }}
+ + {% for i, c in enumerate(t.thread.comments) %} + {% if c != t.thread.top_comment %} +
{{ c.text }}
+ {% elif i != 0 %} +
Ce message est le top comment
+ {% endif %} + {% endfor %} + +
+

Commenter le sujet

+
+ {{ form.hidden_tag() }} + + {{ widget_editor.text_editor(form.message, label=False) }} + +
{{ form.submit(class_='bg-green') }}
+
+
+
+{% endblock %} diff --git a/app/templates/widgets/editor.html b/app/templates/widgets/editor.html index 2d4d911..087865a 100644 --- a/app/templates/widgets/editor.html +++ b/app/templates/widgets/editor.html @@ -1,9 +1,9 @@ -{% macro editor(form) %} +{% macro text_editor(field, label=True) %}
- {{ form.hidden_tag() }} - {{ form.label }} - {{ form.contents() }} - {% for error in form.contents.errors %} + {{ field.label if label }} +
Widgets. Lots of widgets :3
+ {{ field() }} + {% for error in field.errors %} {{ error }} {% endfor %}
diff --git a/app/utils/converters.py b/app/utils/converters.py index 001d2b4..eb5bf3a 100644 --- a/app/utils/converters.py +++ b/app/utils/converters.py @@ -18,6 +18,7 @@ For more information, see the Werkzeug documentation: from werkzeug.routing import BaseConverter, ValidationError from app.models.forum import Forum +from app.models.topic import Topic import re import sys @@ -48,10 +49,10 @@ class TopicSlugConverter(BaseConverter): if m is None: raise Exception(f"TopicSlugConverter: conversation failed") - return int(m[1], 10) + return Topic.query.get_or_404(int(m[1], 10)) - def to_url(self, topic_id): - return str(topic_id) + def to_url(self, topic): + return str(topic.id) # Export only the converter classes __all__ = "ForumConverter TopicSlugConverter".split()