Ajout des commentaires de topics

On ne peut pas encore modifier le top comment ni commencer un topic à 
partir d'un thread externe, mais les bases sont là :)
This commit is contained in:
Darks 2019-12-03 20:32:01 +01:00
parent 089e851b4c
commit 662882cc15
Signed by: Darks
GPG Key ID: F61F10FA138E797C
12 changed files with 191 additions and 104 deletions

View File

@ -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

View File

@ -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 <a href="#">CGU</a>""",
"""J'accepte les <a href="#">CGU</a>""",
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(

View File

@ -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

View File

@ -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')

View File

@ -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."""

View File

@ -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/<forum:f>/<topicslug:t>')
def forum_topic(f, t):
return render('/forum/topic.html', f=f, t=t)
return render('/forum/forum.html', f=f, form=form)

30
app/routes/forum/topic.py Normal file
View File

@ -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/<forum:f>/<topicslug:t>', 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)

View File

@ -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;
}

View File

@ -10,25 +10,40 @@
<p>{{ f.descr }}</p>
{% if f.topics %}
<h2>Sujets</h2>
<table class=topiclist>
<tr><th>Sujet</th><th>Auteur</th><th>Date de création</th>
<th>Commentaires</th><th>Vues</th></tr>
{% for t in f.topics %}
<tr><td>{{ t.title }}</td>
<td><a href='/user/{{ t.author.norm }}'>{{ t.author.name }}</a></td>
<td>{{ t.date_created | date }}</td>
<td>{{ t.comments | length }}</td>
<td>{{ t.views }} </td></tr>
<tr><td><a href='{{ url_for('forum_topic', f=t.forum, t=t) }}'>{{ t.title }}</a></td>
<td><a href='{{ url_for('user', username=t.author.name) }}'>{{ t.author.name }}</a></td>
<td>{{ t.date_created | date }}</td>
<td>{{ t.thread.comments | length }}</td>
<td>{{ t.views }} </td></tr>
{% endfor %}
</table>
{% else %}
{% elif len(f.sub_forums) == 0 %}
<p>Il n'y a aucun topic sur ce forum ! Animons-le vite !</p>
{% endif %}
{% if len(f.sub_forums) > 0 %}
<h2>Forums</h2>
<table class=forumlist>
<tr><th>{{ f.name }}</th><th>Nombre de sujets</th></tr>
{% for sf in f.sub_forums %}
<tr><td><a href='/forum{{ sf.url }}'>{{ sf.name }}</td>
<td>{{ sf.topics | length }}</td></tr>
<tr><td>{{ sf.descr }}</td><td></td></tr>
{% endfor %}
</table>
{% endif %}
{% if len(f.sub_forums) == 0 or (current_user.is_authenticated and current_user.priv('access-admin-board')) %}
<div class=form>
<h2>Créer un nouveau sujet</h2>
<form action="" method="post" enctype="multipart/form-data">
{{ form.hidden_tag() }}
@ -40,10 +55,11 @@
{% endfor %}
</div>
{{ widget_editor.editor(form.message) }}
{{ widget_editor.text_editor(form.message) }}
<div>{{ form.submit(class_='bg-green') }}</div>
</form>
</div>
{% endif %}
</section>
{% endblock %}

View File

@ -0,0 +1,33 @@
{% extends "base/base.html" %}
{% import "widgets/editor.html" as widget_editor %}
{% block title %}
<a href='/forum'>Forum de Planète Casio</a> » <h1>{{ t.forum.name }}</h1>
{% endblock %}
{% block content %}
<section>
<h1>{{ t.title }}</h1>
<div>{{ t.thread.top_comment.text }}</div>
{% for i, c in enumerate(t.thread.comments) %}
{% if c != t.thread.top_comment %}
<div>{{ c.text }}</div>
{% elif i != 0 %}
<div>Ce message est le top comment</div>
{% endif %}
{% endfor %}
<div class=form>
<h3>Commenter le sujet</h3>
<form action="" method="post" enctype="multipart/form-data">
{{ form.hidden_tag() }}
{{ widget_editor.text_editor(form.message, label=False) }}
<div>{{ form.submit(class_='bg-green') }}</div>
</form>
</div>
</section>
{% endblock %}

View File

@ -1,9 +1,9 @@
{% macro editor(form) %}
{% macro text_editor(field, label=True) %}
<div class=editor>
{{ form.hidden_tag() }}
{{ form.label }}
{{ form.contents() }}
{% for error in form.contents.errors %}
{{ field.label if label }}
<div>Widgets. Lots of widgets :3</div>
{{ field() }}
{% for error in field.errors %}
<span class=msgerror>{{ error }}</span>
{% endfor %}
</div>

View File

@ -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()