Notification emails #80
@ -6,7 +6,7 @@ class AbuseConfig(AppConfig):
|
||||
name = 'abuse'
|
||||
|
||||
def ready(self):
|
||||
import extensions.signals # noqa: F401
|
||||
import abuse.signals # noqa: F401
|
||||
from actstream import registry
|
||||
|
||||
registry.register(self.get_model('AbuseReport'))
|
||||
|
@ -20,10 +20,13 @@ def create_action_from_report(
|
||||
sender: object,
|
||||
instance: AbuseReport,
|
||||
created: bool,
|
||||
raw: bool,
|
||||
**kwargs: object,
|
||||
) -> None:
|
||||
if not created:
|
||||
return
|
||||
if raw:
|
||||
return
|
||||
|
||||
if instance.type == ABUSE_TYPE_EXTENSION:
|
||||
verb = Verb.REPORTED_EXTENSION
|
||||
@ -31,9 +34,9 @@ def create_action_from_report(
|
||||
verb = Verb.REPORTED_REVIEW
|
||||
elif instance.type == ABUSE_TYPE_USER:
|
||||
# TODO?
|
||||
pass
|
||||
return
|
||||
else:
|
||||
logger.warning("ignoring an unexpected AbuseReport type={instance.type}")
|
||||
logger.warning(f'ignoring an unexpected AbuseReport type={instance.type}')
|
||||
return
|
||||
|
||||
action.send(
|
||||
|
@ -1,4 +1,8 @@
|
||||
class Verb:
|
||||
"""These constants are used to dispatch Action records,
|
||||
changing the values will result in a mismatch with historical values stored in db.
|
||||
"""
|
||||
|
||||
SUBMITTED_FOR_REVIEW = 'submitted for review'
|
||||
|
||||
REPORTED_EXTENSION = 'reported extension'
|
||||
|
@ -2,6 +2,8 @@
|
||||
from collections import defaultdict
|
||||
import logging
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.mail import send_mail
|
||||
from django.core.management.base import BaseCommand
|
||||
from django.utils import timezone
|
||||
|
||||
@ -30,9 +32,16 @@ class Command(BaseCommand):
|
||||
Notification.objects.bulk_update(batch, ['processed_by_mailer_at', 'sent'])
|
||||
if len(to_send) > 0:
|
||||
logger.info(f'sending an email to {recipient} about {len(to_send)} notifications')
|
||||
send_email(recipient, to_send)
|
||||
send_batch_notification_email(recipient, to_send)
|
||||
logger.info('finish')
|
||||
|
||||
|
||||
def send_email(recipient, to_send):
|
||||
pass
|
||||
def send_batch_notification_email(recipient, notifications):
|
||||
subject = 'New activity'
|
||||
message = '\n\n'.join([n.format_email_txt() for n in notifications])
|
||||
send_mail(
|
||||
subject,
|
||||
message,
|
||||
settings.DEFAULT_FROM_EMAIL,
|
||||
[recipient.email],
|
||||
)
|
||||
|
@ -6,9 +6,11 @@ import time
|
||||
from actstream.models import Action
|
||||
from django.contrib.admin.utils import NestedObjects
|
||||
from django.contrib.auth.models import AbstractUser
|
||||
from django.contrib.sites.models import Site
|
||||
from django.db import models, DEFAULT_DB_ALIAS, transaction
|
||||
from django.templatetags.static import static
|
||||
|
||||
from constants.activity import Verb
|
||||
from common.model_mixins import TrackChangesMixin
|
||||
from files.utils import get_sha256_from_value
|
||||
|
||||
@ -142,11 +144,44 @@ class User(TrackChangesMixin, AbstractUser):
|
||||
return self.groups.filter(name='moderators').exists()
|
||||
|
||||
def is_subscribed(self, notification: 'Notification') -> bool:
|
||||
# TODO user profile settings to subscribe to groups of action verbs
|
||||
return True
|
||||
|
||||
|
||||
class Notification(models.Model):
|
||||
"""Notification records are created in Action's post_save signal.
|
||||
When a user marks a notification as read, a record is deleted.
|
||||
While a notification exists it can be picked up by a send_notifications management command
|
||||
that runs periodically in background.
|
||||
If a recipient opted out from receiving notifications of particular type (based on action.verb),
|
||||
we shouldn't include it in the email, leaving sent=False.
|
||||
"""
|
||||
|
||||
recipient = models.ForeignKey(User, null=False, on_delete=models.CASCADE)
|
||||
action = models.ForeignKey(Action, null=False, on_delete=models.CASCADE)
|
||||
processed_by_mailer_at = models.DateTimeField(default=None, null=True)
|
||||
sent = models.BooleanField(default=False, null=False)
|
||||
|
||||
def format_email_txt(self):
|
||||
url = self.get_absolute_url()
|
||||
# TODO construct a proper phrase, depending on the verb, maybe use a template
|
||||
return f'{self.action.actor.full_name} {self.action.verb} {self.action.target}: {url}'
|
||||
|
||||
def get_absolute_url(self):
|
||||
if self.action.verb == Verb.RATED_EXTENSION:
|
||||
url = self.action.target.get_ratings_url()
|
||||
elif self.action.verb in [
|
||||
Verb.APPROVED,
|
||||
Verb.COMMENTED,
|
||||
Verb.REQUESTED_CHANGES,
|
||||
Verb.REQUESTED_REVIEW,
|
||||
]:
|
||||
url = self.action.target.get_review_url()
|
||||
elif self.action.action_object is not None:
|
||||
url = self.action.action_object.get_absolute_url()
|
||||
else:
|
||||
url = self.action.target.get_absolute_url()
|
||||
|
||||
# TODO? url cloacking to auto-delete visited notifications
|
||||
domain = Site.objects.get_current().domain
|
||||
return f'https://{domain}{url}'
|
||||
|
Loading…
Reference in New Issue
Block a user