|
|
|
@ -18,6 +18,7 @@
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
from datetime import datetime
|
|
|
|
|
from enum import Enum
|
|
|
|
|
from warnings import warn
|
|
|
|
|
|
|
|
|
|
from flask_babelex import lazy_gettext
|
|
|
|
@ -41,6 +42,24 @@ def generate_uuid():
|
|
|
|
|
return uuid4().hex
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _(translate): # pylint: disable=invalid-name
|
|
|
|
|
"""Function to mark strings as translatable
|
|
|
|
|
|
|
|
|
|
The actual translation will be fetched later in `:meth:AuditLog.get_message`.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
return translate
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class NotificationAction(Enum):
|
|
|
|
|
follow = 1
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
notification_action_messages = {
|
|
|
|
|
NotificationAction.follow: (_('%(actor)s followed you'), _('%(actor)s followed %(item)s'))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class SettingsProxy:
|
|
|
|
|
"""Proxy object to get settings for a user
|
|
|
|
|
"""
|
|
|
|
@ -184,6 +203,15 @@ class Profile(db.Model): # pylint: disable=too-few-public-methods
|
|
|
|
|
#: The display name
|
|
|
|
|
display_name = db.Column(db.Unicode(length=80), nullable=False)
|
|
|
|
|
|
|
|
|
|
@property
|
|
|
|
|
def fqdn(self):
|
|
|
|
|
ret = ''
|
|
|
|
|
|
|
|
|
|
if self.user:
|
|
|
|
|
return f'@{self.user.username}'
|
|
|
|
|
|
|
|
|
|
return f'@{self.username}@{self.domain}'
|
|
|
|
|
|
|
|
|
|
def __repr__(self):
|
|
|
|
|
if self.user:
|
|
|
|
|
username = self.user.username
|
|
|
|
@ -194,6 +222,16 @@ class Profile(db.Model): # pylint: disable=too-few-public-methods
|
|
|
|
|
|
|
|
|
|
return f'<Profile {self.id}(@{username}{domain})>'
|
|
|
|
|
|
|
|
|
|
def __str__(self):
|
|
|
|
|
ret = ''
|
|
|
|
|
|
|
|
|
|
if self.display_name:
|
|
|
|
|
ret = self.display_name + ' '
|
|
|
|
|
|
|
|
|
|
ret += f'({self.fqdn})'
|
|
|
|
|
|
|
|
|
|
return ret
|
|
|
|
|
|
|
|
|
|
@property
|
|
|
|
|
def followed_list(self):
|
|
|
|
|
"""List of profiles this profile is following
|
|
|
|
@ -313,15 +351,6 @@ class UserSetting(db.Model): # pylint: disable=too-few-public-methods
|
|
|
|
|
return f'<UserSetting of {self.user}, {self.key}="{self.value}">'
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _(translate): # pylint: disable=invalid-name
|
|
|
|
|
"""Function to mark strings as translatable
|
|
|
|
|
|
|
|
|
|
The actual translation will be fetched later in `:meth:AuditLog.get_message`.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
return translate
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class AuditLog(db.Model):
|
|
|
|
|
"""Database model for audit log records
|
|
|
|
|
"""
|
|
|
|
@ -442,3 +471,62 @@ class UserFollow(db.Model):
|
|
|
|
|
|
|
|
|
|
#: The timestamp when the follow was accepted
|
|
|
|
|
accepted_at = db.Column(db.DateTime(), nullable=True)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class Notification(db.Model):
|
|
|
|
|
"""Database model for notifications
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
__tablename__ = 'notifications'
|
|
|
|
|
# pylint: disable=invalid-name
|
|
|
|
|
id = db.Column(db.Integer(), primary_key=True)
|
|
|
|
|
|
|
|
|
|
#: The recipient of the notification
|
|
|
|
|
profile_id = db.Column(db.Integer(), db.ForeignKey('profiles.id'), nullable=False)
|
|
|
|
|
|
|
|
|
|
profile = db.relationship('Profile',
|
|
|
|
|
backref=db.backref('notifications', lazy='dynamic'),
|
|
|
|
|
foreign_keys=[profile_id])
|
|
|
|
|
|
|
|
|
|
#: The profile that generated the notification
|
|
|
|
|
actor_id = db.Column(db.Integer(), db.ForeignKey('profiles.id'), nullable=True)
|
|
|
|
|
|
|
|
|
|
actor = db.relationship('Profile', foreign_keys=[actor_id])
|
|
|
|
|
|
|
|
|
|
#: The item (e.g. event) that generated the notification
|
|
|
|
|
item_id = db.Column(db.Integer(), nullable=True)
|
|
|
|
|
|
|
|
|
|
#: The type of the item that generated the notification
|
|
|
|
|
item_type = db.Column(db.String(length=40))
|
|
|
|
|
|
|
|
|
|
#: The type of action
|
|
|
|
|
action = db.Column(db.Enum(NotificationAction))
|
|
|
|
|
|
|
|
|
|
#: The timestamp when the notification was created
|
|
|
|
|
created_at = db.Column(db.DateTime(), default=datetime.utcnow, nullable=False)
|
|
|
|
|
|
|
|
|
|
#: The timestamp when the notification was read
|
|
|
|
|
read_at = db.Column(db.DateTime(), nullable=True)
|
|
|
|
|
|
|
|
|
|
@property
|
|
|
|
|
def item(self):
|
|
|
|
|
item_class = self._decl_class_registry.get(self.item_type)
|
|
|
|
|
|
|
|
|
|
if item_class is None:
|
|
|
|
|
warn(f'Unknown item type {self.item_type}')
|
|
|
|
|
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
return item_class.query.get(self.item_id)
|
|
|
|
|
|
|
|
|
|
@property
|
|
|
|
|
def message(self):
|
|
|
|
|
"""Get the translated message for ``key``
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
from flask_security import current_user
|
|
|
|
|
|
|
|
|
|
messages = notification_action_messages.get(self.action)
|
|
|
|
|
message = messages[0 if self.item == current_user.profile else 1]
|
|
|
|
|
|
|
|
|
|
return lazy_gettext(message, actor=self.actor, item=self.item)
|
|
|
|
|