Browse Source

Ajout des notifications

notifications
Darks 11 months ago
parent
commit
15a4d38ea0
Signed by: Darks <l.gatin@neuf.fr> GPG Key ID: F61F10FA138E797C
10 changed files with 173 additions and 4 deletions
  1. +2
    -1
      app/__init__.py
  2. +1
    -0
      app/data/groups.yaml
  3. +23
    -0
      app/models/notification.py
  4. +12
    -2
      app/models/users.py
  5. +42
    -0
      app/routes/account/notification.py
  6. +3
    -0
      app/routes/admin/account.py
  7. +33
    -0
      app/templates/account/notifications.html
  8. +1
    -1
      app/templates/base/navbar/account.html
  9. +20
    -0
      app/utils/notify.py
  10. +36
    -0
      migrations/versions/ebca7362eb22_ajout_des_notifications.py

+ 2
- 1
app/__init__.py 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

+ 1
- 0
app/data/groups.yaml 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"


+ 23
- 0
app/models/notification.py 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})>'

+ 12
- 2
app/models/users.py 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):
"""


+ 42
- 0
app/routes/account/notification.py 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)

+ 3
- 0
app/routes/admin/account.py 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')


+ 33
- 0
app/templates/account/notifications.html 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 %}

+ 1
- 1
app/templates/base/navbar/account.html 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
- 0
app/utils/notify.py 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")

+ 36
- 0
migrations/versions/ebca7362eb22_ajout_des_notifications.py 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 ###

Loading…
Cancel
Save