Ajout des notifications

This commit is contained in:
Darks 2019-09-01 12:30:41 +02:00
parent 0c7c408e40
commit 15a4d38ea0
Signed by: Darks
GPG Key ID: F61F10FA138E797C
10 changed files with 173 additions and 4 deletions

View File

@ -26,8 +26,9 @@ from app import models # IDK why this is here, but it works
from app.models.comment import Comment
from app.models.thread import Thread
from app.models.forum import Forum
from app.models.notification import Notification
from app.routes import index, search, users # To load routes at initialization
from app.routes.account import login, account
from app.routes.account import login, account, notification
from app.routes.admin import index, groups, account, trophies
from app.utils import pluralize # To use pluralize into the templates
from app.utils import is_title

View File

@ -11,6 +11,7 @@
shoutbox-kick shoutbox-ban
unlimited-pms footer-statistics community-login
access-admin-panel edit-account delete-account edit-trophies
delete_notification
-
name: Modérateur
css: "color: green"

View File

@ -0,0 +1,23 @@
from app import db
from datetime import datetime
class Notification(db.Model):
""" A long-term `flash` notification. It is deleted when watched """
__tablename__ = 'notification'
id = db.Column(db.Integer, primary_key=True)
text = db.Column(db.UnicodeText)
href = db.Column(db.UnicodeText)
date = db.Column(db.DateTime, default=datetime.now())
owner_id = db.Column(db.Integer, db.ForeignKey('member.id'), nullable=False)
def __init__(self, owner_id, text, href=None):
""" Check weather or not the id is valid before creating the notif! """
self.text = text
self.href = href
self.owner_id = owner_id
def __repr__(self):
return f'<Notification to {self.owner.name}: {self.text} ({self.href})>'

View File

@ -5,7 +5,9 @@ from flask_login import UserMixin
from app.models.privs import SpecialPrivilege, Group, GroupMember, \
GroupPrivilege
from app.models.trophies import Trophy, TrophyMember
from app.models.notification import Notification
import app.utils.unicode_names as unicode_names
from app.utils.notify import notify
from config import V5Config
import werkzeug.security
@ -96,6 +98,7 @@ class Member(User):
newsletter = db.Column(db.Boolean, default=False)
# Relations
notifications = db.relationship('Notification', backref="owner", lazy=True)
trophies = db.relationship('Trophy', secondary=TrophyMember,
back_populates='owners')
@ -207,6 +210,14 @@ class Member(User):
return werkzeug.security.check_password_hash(self.password_hash,
password)
def notify(self, message, href=None):
""" Notify a user with a message.
An hyperlink can be added to redirect to the notification source """
n = Notification(self.id, message, href=href)
db.session.add(n)
db.session.commit()
def add_trophy(self, t):
"""
Add a trophy to the current user. Check whether the request sender has
@ -218,8 +229,7 @@ class Member(User):
t = Trophy.query.filter_by(name=t).first()
if t not in self.trophies:
self.trophies.append(t)
# TODO: implement the notification system
# self.notify(f"Vous venez de débloquer le trophée '{t.name}'")
self.notify(f"Vous avez débloqué le trophée '{t.name}'")
def del_trophy(self, t):
"""

View File

@ -0,0 +1,42 @@
from flask import redirect, url_for, request, flash
from flask_login import login_required, current_user
from app import app, db
from app.models.notification import Notification
from app.utils.render import render
@app.route('/notifications', methods=['GET'])
@login_required
def list_notifications():
notifications = current_user.notifications
return render('account/notifications.html', notifications=notifications)
@app.route('/notifications/delete/<id>', methods=['GET'])
@login_required
def delete_notification(id=None):
if type(id) == int:
notification = Notification.query.get(id)
if notification:
# Only current user or admin can delete notifications
if notification.owner_id == current_user.id:
db.session.delete(notification)
db.session.commit()
return redirect(url_for('list_notifications'))
elif 'delete_notification' in current_user.privs:
db.session.delete(notification)
db.session.commit()
# TODO: change this redirection
return redirect(url_for('list_notifications'))
else:
abort(403)
abort(404)
elif id == "all":
for n in current_user.notifications:
db.session.delete(n)
db.session.commit()
return redirect(url_for('list_notifications'))
# TODO: add something to allow an admin to delete all notifs for a user
# with a GET parameter
else:
abort(404)

View File

@ -1,4 +1,5 @@
from flask import flash, redirect, url_for
from flask_login import current_user
from wtforms import BooleanField
from app.utils.priv_required import priv_required
from app.models.users import Member
@ -6,6 +7,7 @@ from app.models.trophies import Trophy
from app.forms.account import AdminUpdateAccountForm, AdminDeleteAccountForm, \
AdminAccountEditTrophyForm
from app.utils.render import render
from app.utils.notify import notify
from app import app, db
@ -47,6 +49,7 @@ def adm_edit_account(user_id):
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')
else:
flash('Erreur lors de la modification', 'error')

View File

@ -0,0 +1,33 @@
{% extends "base/base.html" %}
{% block title %}
<h1>Notifications</h1>
{% endblock %}
{% block content %}
<section>
{% if notifications %}
<table>
<tr>
<th>Date</th>
<th>Notification</th>
<th><a href="{{ url_for('delete_notification', id='all') }}">Tout supprimer</a></th>
</tr>
{% for n in notifications %}
<tr>
<td>{{ n.date.strftime('Le %Y-%m-%d à %H:%M') }}</td>
<td>
{% if n.href %}<a href="{{ n.href }}">{% endif %}
{{ n.text }}
{% if n.href %}</a>{% endif %}
</td>
<td><a href="{{ url_for('delete_notification', id=n.id)}}">Supprimer</a>
</tr>
{% endfor %}
</table>
{% else %}
Aucune notification.
{% endif %}
</div>
</section>
{% endblock %}

View File

@ -6,7 +6,7 @@
<a href="{{ url_for('user', username=current_user.name) }}">
{{ current_user.name }}</a>
</h2>
<a href="#">
<a href="{{ url_for('list_notifications') }}">
<svg viewBox="0 0 24 24">
<path fill="#ffffff" d="M20,2A2,2 0 0,1 22,4V16A2,2 0 0,1 20,18H6L2,22V4C2,2.89 2.9,2 4,2H20M4,4V17.17L5.17,16H20V4H4M6,7H18V9H6V7M6,11H15V13H6V11Z"></path>
</svg>Notifications

20
app/utils/notify.py Normal file
View File

@ -0,0 +1,20 @@
from app import db
from app.models.notification import Notification
# from app.models.users import Member
def notify(user, message, href=None):
""" Notify a user (by id, name or object reference) with a message.
An hyperlink can be added to redirect to the notification source """
# Cuz' duck typing is quite cool
# TODO: maybe abort if no user is found
if type(user) == str:
user = Member.query.filter_by(name=user).first()
if isinstance(user, Member):
user = user.id
if user and Member.query.get(user):
n = Notification(user, message, href=href)
db.session.add(n)
db.session.commit()
else:
print("User not found")

View File

@ -0,0 +1,36 @@
"""Ajout des notifications
Revision ID: ebca7362eb22
Revises: e3b140752719
Create Date: 2019-09-01 11:36:25.962212
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = 'ebca7362eb22'
down_revision = 'e3b140752719'
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('notification',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('text', sa.UnicodeText(), nullable=True),
sa.Column('href', sa.UnicodeText(), nullable=True),
sa.Column('date', sa.DateTime(), nullable=True),
sa.Column('owner_id', sa.Integer(), nullable=False),
sa.ForeignKeyConstraint(['owner_id'], ['member.id'], ),
sa.PrimaryKeyConstraint('id')
)
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_table('notification')
# ### end Alembic commands ###