Merge pull request 'feat: ajout du post en tant qu'invité (#36)' (#52) from dev into preprod

This commit is contained in:
Darks 2020-07-19 20:44:36 +02:00
commit 00dfd81a71
16 changed files with 149 additions and 35 deletions

View File

@ -1,6 +1,7 @@
from flask_wtf import FlaskForm
from wtforms import StringField, FormField, SubmitField, TextAreaField
from wtforms.validators import DataRequired, Length
import app.utils.validators as vd
class TopicCreationForm(FlaskForm):
title = StringField('Nom du sujet',
@ -8,6 +9,15 @@ class TopicCreationForm(FlaskForm):
message = TextAreaField('Message principal', validators=[DataRequired()])
submit = SubmitField('Créer le sujet')
class AnonymousTopicCreationForm(TopicCreationForm):
pseudo = StringField('Pseudo',
validators=[DataRequired(), vd.name_valid, vd.name_available])
class CommentForm(FlaskForm):
message = TextAreaField('Commentaire', validators=[DataRequired()])
submit = SubmitField('Commenter')
class AnonymousCommentForm(CommentForm):
pseudo = StringField('Pseudo',
validators=[DataRequired(), vd.name_valid, vd.name_available])

View File

@ -51,14 +51,15 @@ class Guest(User):
# ID of the [User] entry
id = db.Column(db.Integer, db.ForeignKey('user.id'), primary_key=True)
# Reusable username, can be the name of a member (will be distinguished at
# rendering time)
username = db.Column(db.Unicode(64), index=True)
# IP address, 47 characters is the max for an IPv6 address
ip = db.Column(db.String(47))
# Reusable username, cannot be chosen as the name of a member
# but will be distinguished at rendering time if a member take it later
name = db.Column(db.Unicode(64))
def __init__(self, name):
self.name = name
def __repr__(self):
return f'<Guest: {self.username} ({self.ip})>'
return f'<Guest: {self.username}>'
class Member(User):

View File

@ -1,5 +1,6 @@
from app import app
from flask import url_for
from config import V5Config
@app.context_processor
def utilities_processor():
@ -8,4 +9,5 @@ def utilities_processor():
len=len,
# enumerate=enumerate,
_url_for = lambda route, args, **other: url_for(route, **args, **other),
V5Config = V5Config,
)

View File

@ -61,11 +61,6 @@ def login():
@app.route('/deconnexion')
@login_required
def logout():
try:
print(request.referrer)
except Exception as e:
print('No referrer:', e)
logout_user()
flash('Déconnexion réussie', 'info')
if request.referrer:

View File

@ -24,7 +24,6 @@ def delete_notification(id=None):
if type(id) == int:
notification = Notification.query.get(id)
print(">", notification)
if notification:
# Only current user or admin can delete notifications
if notification.owner_id == current_user.id:

View File

@ -35,8 +35,6 @@ def adm_edit_account(user_id):
setattr(GroupForm, "user_groups", [f'g{g.id}' for g in user.groups])
group_form = GroupForm(prefix="group")
print(group_form.__dict__.items())
if form.submit.data:
if form.validate_on_submit():
newname = form.username.data

View File

@ -4,11 +4,12 @@ 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.forms.forum import TopicCreationForm
from app.forms.forum import TopicCreationForm, AnonymousTopicCreationForm
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.models.users import Guest
@app.route('/forum/')
@ -17,7 +18,10 @@ def forum_index():
@app.route('/forum/<forum:f>/', methods=['GET', 'POST'])
def forum_page(f):
form = TopicCreationForm()
if current_user.is_authenticated:
form = TopicCreationForm()
else:
form = AnonymousTopicCreationForm()
# TODO: do not hardcode name of news forums
if form.validate_on_submit() and (
@ -27,28 +31,35 @@ def forum_page(f):
or ("/actus" in f.url and current_user.is_authenticated
and current_user.priv('write-news'))
# Forum is not news and is a leaf:
or ("/actus" not in f.url and not f.sub_forums)):
or ("/actus" not in f.url and not f.sub_forums)) and (
V5Config.ENABLE_GUEST_POST or current_user.is_authenticated):
# First create the thread, then the comment, then the topic
th = Thread()
db.session.add(th)
db.session.commit()
c = Comment(current_user, form.message.data, th)
if current_user.is_authenticated:
author = current_user
else:
author = Guest(form.pseudo.data)
db.session.add(author)
c = Comment(author, 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)
t = Topic(f, author, form.title.data, th)
db.session.add(t)
db.session.commit()
# Update member's xp and trophies
current_user.add_xp(V5Config.XP_POINTS['topic'])
current_user.update_trophies('new-post')
if current_user.is_authenticated:
current_user.add_xp(V5Config.XP_POINTS['topic'])
current_user.update_trophies('new-post')
flash('Le sujet a bien été créé', 'ok')
return redirect(url_for('forum_topic', f=f, page=(t,1)))

View File

@ -4,11 +4,12 @@ from flask import request, redirect, url_for, flash, abort
from app import app, db
from config import V5Config
from app.utils.render import render
from app.forms.forum import CommentForm
from app.forms.forum import CommentForm, AnonymousCommentForm
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.models.users import Guest
@app.route('/forum/<forum:f>/<topicpage:page>', methods=['GET', 'POST'])
@ -19,16 +20,27 @@ def forum_topic(f, page):
if f != t.forum:
abort(404)
form = CommentForm()
if current_user.is_authenticated:
form = CommentForm()
else:
form = AnonymousCommentForm()
if form.validate_on_submit():
c = Comment(current_user, form.message.data, t.thread)
if form.validate_on_submit() and \
(V5Config.ENABLE_GUEST_POST or current_user.is_authenticated):
if current_user.is_authenticated:
author = current_user
else:
author = Guest(form.pseudo.data)
db.session.add(author)
c = Comment(author, form.message.data, t.thread)
db.session.add(c)
db.session.commit()
# Update member's xp and trophies
current_user.add_xp(V5Config.XP_POINTS['comment'])
current_user.update_trophies('new-post')
if current_user.is_authenticated:
current_user.add_xp(V5Config.XP_POINTS['comment'])
current_user.update_trophies('new-post')
flash('Message envoyé', 'ok')
# Redirect to empty the form

View File

@ -17,7 +17,7 @@
body {
margin: 0;
background: var(--background); color: var(--text);
font-family: Twemoji, 'DejaVu Sans', sans-serif;
font-family: 'DejaVu Sans', sans-serif;
}
/* General */

View File

@ -71,6 +71,15 @@ table.thread {
table.thread td.member {
width: 20%;
}
table.thread td.guest {
width: 20%; padding-top: 12px;
text-align: center;
}
table.thread td.guest em {
display: block;
font-weight: bold; font-style: normal;
margin-bottom: 8px;
}
table.thread td {
vertical-align: top;
}

View File

@ -6,7 +6,7 @@
{% block content %}
<section class="form">
<a href="/user/{{ user.name }}">Visiter la page de profil de {{ user.name }}</a>
<a href="{{ url_for('user', username=user.name) }}">Visiter la page de profil de {{ user.name }}</a>
<form action="{{ url_for('adm_edit_account', user_id=user.id) }}" method="post" enctype="multipart/form-data">
{{ form.hidden_tag() }}
<h2>Informations</h2>

View File

@ -43,11 +43,21 @@
{% if (current_user.is_authenticated and current_user.priv('write-anywhere'))
or ("/actus" in f.url and current_user.is_authenticated and current_user.priv('write-news'))
or ("/actus" not in f.url and not f.sub_forums) %}
or ("/actus" not in f.url and not f.sub_forums)
and (current_user.is_authenticated or V5Config.ENABLE_GUEST_POST) %}
<div class=form>
<h2>Créer un nouveau sujet</h2>
<form action="" method="post" enctype="multipart/form-data">
{{ form.hidden_tag() }}
{% if form.pseudo %}
<div>
{{ form.pseudo.label }}
{{ form.pseudo() }}
{% for error in form.pseudo.errors %}
<span class="msgerror">{{ error }}</span>
{% endfor %}
</div>
{% endif %}
<div>
{{ form.title.label }}

View File

@ -11,7 +11,11 @@
<section>
<h1>{{ t.title }}</h1>
<table class="thread"><tr>
<td class="member">{{ widget_member.profile(t.author ) }}</td>
{% if t.author.type == "member" %}
<td class="member">{{ widget_member.profile(t.author ) }}</td>
{% else %}
<td class="guest"><em>Invité</em>{{ t.author.name }}</td>
{% endif %}
<td>{{ t.thread.top_comment.text }}</td>
</tr></table>
@ -21,7 +25,11 @@
{% for c in comments.items %}
<tr id="{{ c.id }}">
{% if c != t.thread.top_comment %}
<td class="member">{{ widget_member.profile(c.author ) }}</td>
{% if c.author.type == "member" %}
<td class="member">{{ widget_member.profile(c.author ) }}</td>
{% else %}
<td class="guest"><em>Invité</em>{{ c.author.name }}</td>
{% endif %}
<td>
<div>{% if c.date_created != c.date_modified %}
Posté le {{ c.date_created|date }} (Modifié le {{ c.date_modified|date }})
@ -44,15 +52,25 @@
{{ widget_pagination.paginate(comments, 'forum_topic', t, {'f': t.forum}) }}
{% if current_user.is_authenticated or V5Config.ENABLE_GUEST_POST %}
<div class=form>
<h3>Commenter le sujet</h3>
<form action="" method="post" enctype="multipart/form-data">
<form action="" method="post" enctype="multipart/form-data">
{{ form.hidden_tag() }}
{% if form.pseudo %}
{{ form.pseudo.label }}
{{ form.pseudo }}
{% for error in form.pseudo.errors %}
<span class="msgerror">{{ error }}</span>
{% endfor %}
{% endif %}
{{ widget_editor.text_editor(form.message, label=False) }}
<div>{{ form.submit(class_='bg-ok') }}</div>
</form>
{% endif %}
</div>
</section>
{% endblock %}

View File

@ -18,6 +18,7 @@ class Config(object):
class DefaultConfig(object):
"""Every value here can be overrided in the local_config.py class"""
# Length allocated to privilege names (slugs)
PRIVS_MAXLEN = 64
# Forbidden user names
@ -54,7 +55,9 @@ class DefaultConfig(object):
SECRET_KEY = "a-random-secret-key"
# Avatars folder
AVATARS_FOLDER = '/avatar/folder/'
# Enable guest post
ENABLE_GUEST_POST = True
class V5Config(LocalConfig, DefaultConfig):
# Args put here cannot be overidden with local_config
# Values put here cannot be overidden with local_config
pass

View File

@ -1,3 +1,6 @@
# This is a sample file
# You can override every value available in the class DefaultConfig
class LocalConfig(object):
DB_NAME = "pcv5"
USE_LDAP = True
@ -5,3 +8,4 @@ class LocalConfig(object):
LDAP_ORGANIZATION = "o=planet-casio"
SECRET_KEY = "a-random-secret-key" # CHANGE THIS VALUE *NOW*
AVATARS_FOLDER = '/home/pc/data/avatars/'
ENABLE_GUEST_POST = True

View File

@ -0,0 +1,42 @@
"""Modification du Guest
Revision ID: d1b465f88d3b
Revises: c40b1f0ed821
Create Date: 2020-07-17 22:56:36.260431
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = 'd1b465f88d3b'
down_revision = 'c40b1f0ed821'
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('guest', sa.Column('name', sa.Unicode(length=64), nullable=True))
op.drop_index('ix_guest_username', table_name='guest')
op.drop_column('guest', 'ip')
op.drop_column('guest', 'username')
op.alter_column('member', 'avatar_id',
existing_type=sa.INTEGER(),
nullable=True,
existing_server_default=sa.text('0'))
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.alter_column('member', 'avatar_id',
existing_type=sa.INTEGER(),
nullable=False,
existing_server_default=sa.text('0'))
op.add_column('guest', sa.Column('username', sa.VARCHAR(length=64), autoincrement=False, nullable=True))
op.add_column('guest', sa.Column('ip', sa.VARCHAR(length=47), autoincrement=False, nullable=True))
op.create_index('ix_guest_username', 'guest', ['username'], unique=False)
op.drop_column('guest', 'name')
# ### end Alembic commands ###