Merge from 'main' into stable_dates

This commit is contained in:
Adeodato Simó 2023-11-19 14:46:32 -03:00
commit 8dbfba17d6
No known key found for this signature in database
GPG key ID: CDF447845F1A986F
15 changed files with 289 additions and 59 deletions

View file

@ -32,6 +32,15 @@ jobs:
run: | run: |
python -m pip install --upgrade pip python -m pip install --upgrade pip
pip install -r requirements.txt pip install -r requirements.txt
- name: Check migrations up-to-date
run: |
python ./manage.py makemigrations --check
env:
SECRET_KEY: beepbeep
DOMAIN: your.domain.here
EMAIL_HOST: ""
EMAIL_HOST_USER: ""
EMAIL_HOST_PASSWORD: ""
- name: Run Tests - name: Run Tests
env: env:
SECRET_KEY: beepbeep SECRET_KEY: beepbeep

View file

@ -0,0 +1,42 @@
# Generated by Django 3.2.20 on 2023-11-13 22:39
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("bookwyrm", "0184_auto_20231106_0421"),
]
operations = [
migrations.AlterField(
model_name="notification",
name="notification_type",
field=models.CharField(
choices=[
("FAVORITE", "Favorite"),
("BOOST", "Boost"),
("REPLY", "Reply"),
("MENTION", "Mention"),
("TAG", "Tag"),
("FOLLOW", "Follow"),
("FOLLOW_REQUEST", "Follow Request"),
("IMPORT", "Import"),
("ADD", "Add"),
("REPORT", "Report"),
("LINK_DOMAIN", "Link Domain"),
("INVITE", "Invite"),
("ACCEPT", "Accept"),
("JOIN", "Join"),
("LEAVE", "Leave"),
("REMOVE", "Remove"),
("GROUP_PRIVACY", "Group Privacy"),
("GROUP_NAME", "Group Name"),
("GROUP_DESCRIPTION", "Group Description"),
("MOVE", "Move"),
],
max_length=255,
),
),
]

View file

@ -0,0 +1,48 @@
# Generated by Django 3.2.20 on 2023-11-14 10:02
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("bookwyrm", "0185_alter_notification_notification_type"),
]
operations = [
migrations.AddField(
model_name="notification",
name="related_invite_requests",
field=models.ManyToManyField(to="bookwyrm.InviteRequest"),
),
migrations.AlterField(
model_name="notification",
name="notification_type",
field=models.CharField(
choices=[
("FAVORITE", "Favorite"),
("BOOST", "Boost"),
("REPLY", "Reply"),
("MENTION", "Mention"),
("TAG", "Tag"),
("FOLLOW", "Follow"),
("FOLLOW_REQUEST", "Follow Request"),
("IMPORT", "Import"),
("ADD", "Add"),
("REPORT", "Report"),
("LINK_DOMAIN", "Link Domain"),
("INVITE_REQUEST", "Invite Request"),
("INVITE", "Invite"),
("ACCEPT", "Accept"),
("JOIN", "Join"),
("LEAVE", "Leave"),
("REMOVE", "Remove"),
("GROUP_PRIVACY", "Group Privacy"),
("GROUP_NAME", "Group Name"),
("GROUP_DESCRIPTION", "Group Description"),
("MOVE", "Move"),
],
max_length=255,
),
),
]

View file

@ -7,7 +7,7 @@ from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [
("bookwyrm", "0184_auto_20231106_0421"), ("bookwyrm", "0186_invite_request_notification"),
] ]
operations = [ operations = [

View file

@ -34,7 +34,7 @@ from .site import PasswordReset, InviteRequest
from .announcement import Announcement from .announcement import Announcement
from .antispam import EmailBlocklist, IPBlocklist, AutoMod, automod_task from .antispam import EmailBlocklist, IPBlocklist, AutoMod, automod_task
from .notification import Notification from .notification import Notification, NotificationType
from .hashtag import Hashtag from .hashtag import Hashtag

View file

@ -10,6 +10,7 @@ from django.utils.translation import gettext_lazy as _
from bookwyrm.tasks import app, MISC from bookwyrm.tasks import app, MISC
from .base_model import BookWyrmModel from .base_model import BookWyrmModel
from .notification import NotificationType
from .user import User from .user import User
@ -80,7 +81,7 @@ def automod_task():
with transaction.atomic(): with transaction.atomic():
for admin in admins: for admin in admins:
notification, _ = notification_model.objects.get_or_create( notification, _ = notification_model.objects.get_or_create(
user=admin, notification_type=notification_model.REPORT, read=False user=admin, notification_type=NotificationType.REPORT, read=False
) )
notification.related_reports.set(reports) notification.related_reports.set(reports)

View file

@ -1,5 +1,4 @@
""" do book related things with other users """ """ do book related things with other users """
from django.apps import apps
from django.db import models, IntegrityError, transaction from django.db import models, IntegrityError, transaction
from django.db.models import Q from django.db.models import Q
from bookwyrm.settings import DOMAIN from bookwyrm.settings import DOMAIN
@ -143,26 +142,28 @@ class GroupMemberInvitation(models.Model):
@transaction.atomic @transaction.atomic
def accept(self): def accept(self):
"""turn this request into the real deal""" """turn this request into the real deal"""
# pylint: disable-next=import-outside-toplevel
from .notification import Notification, NotificationType # circular dependency
GroupMember.from_request(self) GroupMember.from_request(self)
model = apps.get_model("bookwyrm.Notification", require_ready=True)
# tell the group owner # tell the group owner
model.notify( Notification.notify(
self.group.user, self.group.user,
self.user, self.user,
related_group=self.group, related_group=self.group,
notification_type=model.ACCEPT, notification_type=NotificationType.ACCEPT,
) )
# let the other members know about it # let the other members know about it
for membership in self.group.memberships.all(): for membership in self.group.memberships.all():
member = membership.user member = membership.user
if member not in (self.user, self.group.user): if member not in (self.user, self.group.user):
model.notify( Notification.notify(
member, member,
self.user, self.user,
related_group=self.group, related_group=self.group,
notification_type=model.JOIN, notification_type=NotificationType.JOIN,
) )
def reject(self): def reject(self):

View file

@ -6,7 +6,7 @@ from bookwyrm import activitypub
from .activitypub_mixin import ActivityMixin from .activitypub_mixin import ActivityMixin
from .base_model import BookWyrmModel from .base_model import BookWyrmModel
from . import fields from . import fields
from .notification import Notification from .notification import Notification, NotificationType
class Move(ActivityMixin, BookWyrmModel): class Move(ActivityMixin, BookWyrmModel):
@ -49,7 +49,6 @@ class MoveUser(Move):
# only allow if the source is listed in the target's alsoKnownAs # only allow if the source is listed in the target's alsoKnownAs
if self.user in self.target.also_known_as.all(): if self.user in self.target.also_known_as.all():
self.user.also_known_as.add(self.target.id) self.user.also_known_as.add(self.target.id)
self.user.update_active_date() self.user.update_active_date()
self.user.moved_to = self.target.remote_id self.user.moved_to = self.target.remote_id
@ -65,7 +64,7 @@ class MoveUser(Move):
for follower in self.user.followers.all(): for follower in self.user.followers.all():
if follower.local: if follower.local:
Notification.notify( Notification.notify(
follower, self.user, notification_type=Notification.MOVE follower, self.user, notification_type=NotificationType.MOVE
) )
else: else:

View file

@ -4,9 +4,10 @@ from django.dispatch import receiver
from .base_model import BookWyrmModel from .base_model import BookWyrmModel
from . import Boost, Favorite, GroupMemberInvitation, ImportJob, LinkDomain from . import Boost, Favorite, GroupMemberInvitation, ImportJob, LinkDomain
from . import ListItem, Report, Status, User, UserFollowRequest from . import ListItem, Report, Status, User, UserFollowRequest
from .site import InviteRequest
class Notification(BookWyrmModel): class NotificationType(models.TextChoices):
"""you've been tagged, liked, followed, etc""" """you've been tagged, liked, followed, etc"""
# Status interactions # Status interactions
@ -29,6 +30,7 @@ class Notification(BookWyrmModel):
# Admin # Admin
REPORT = "REPORT" REPORT = "REPORT"
LINK_DOMAIN = "LINK_DOMAIN" LINK_DOMAIN = "LINK_DOMAIN"
INVITE_REQUEST = "INVITE_REQUEST"
# Groups # Groups
INVITE = "INVITE" INVITE = "INVITE"
@ -43,12 +45,9 @@ class Notification(BookWyrmModel):
# Migrations # Migrations
MOVE = "MOVE" MOVE = "MOVE"
# pylint: disable=line-too-long
NotificationType = models.TextChoices( class Notification(BookWyrmModel):
# there has got be a better way to do this """a notification object"""
"NotificationType",
f"{FAVORITE} {REPLY} {MENTION} {TAG} {FOLLOW} {FOLLOW_REQUEST} {BOOST} {IMPORT} {ADD} {REPORT} {LINK_DOMAIN} {INVITE} {ACCEPT} {JOIN} {LEAVE} {REMOVE} {GROUP_PRIVACY} {GROUP_NAME} {GROUP_DESCRIPTION} {MOVE}",
)
user = models.ForeignKey("User", on_delete=models.CASCADE) user = models.ForeignKey("User", on_delete=models.CASCADE)
read = models.BooleanField(default=False) read = models.BooleanField(default=False)
@ -67,8 +66,9 @@ class Notification(BookWyrmModel):
related_list_items = models.ManyToManyField( related_list_items = models.ManyToManyField(
"ListItem", symmetrical=False, related_name="notifications" "ListItem", symmetrical=False, related_name="notifications"
) )
related_reports = models.ManyToManyField("Report", symmetrical=False) related_reports = models.ManyToManyField("Report")
related_link_domains = models.ManyToManyField("LinkDomain", symmetrical=False) related_link_domains = models.ManyToManyField("LinkDomain")
related_invite_requests = models.ManyToManyField("InviteRequest")
@classmethod @classmethod
@transaction.atomic @transaction.atomic
@ -93,11 +93,11 @@ class Notification(BookWyrmModel):
user=user, user=user,
related_users=related_user, related_users=related_user,
related_list_items__book_list=list_item.book_list, related_list_items__book_list=list_item.book_list,
notification_type=Notification.ADD, notification_type=NotificationType.ADD,
).first() ).first()
if not notification: if not notification:
notification = cls.objects.create( notification = cls.objects.create(
user=user, notification_type=Notification.ADD user=user, notification_type=NotificationType.ADD
) )
notification.related_users.add(related_user) notification.related_users.add(related_user)
notification.related_list_items.add(list_item) notification.related_list_items.add(list_item)
@ -124,7 +124,7 @@ def notify_on_fav(sender, instance, *args, **kwargs):
instance.status.user, instance.status.user,
instance.user, instance.user,
related_status=instance.status, related_status=instance.status,
notification_type=Notification.FAVORITE, notification_type=NotificationType.FAVORITE,
) )
@ -138,7 +138,7 @@ def notify_on_unfav(sender, instance, *args, **kwargs):
instance.status.user, instance.status.user,
instance.user, instance.user,
related_status=instance.status, related_status=instance.status,
notification_type=Notification.FAVORITE, notification_type=NotificationType.FAVORITE,
) )
@ -163,7 +163,7 @@ def notify_user_on_mention(sender, instance, *args, **kwargs):
instance.reply_parent.user, instance.reply_parent.user,
instance.user, instance.user,
related_status=instance, related_status=instance,
notification_type=Notification.REPLY, notification_type=NotificationType.REPLY,
) )
for mention_user in instance.mention_users.all(): for mention_user in instance.mention_users.all():
@ -175,7 +175,7 @@ def notify_user_on_mention(sender, instance, *args, **kwargs):
Notification.notify( Notification.notify(
mention_user, mention_user,
instance.user, instance.user,
notification_type=Notification.MENTION, notification_type=NotificationType.MENTION,
related_status=instance, related_status=instance,
) )
@ -194,7 +194,7 @@ def notify_user_on_boost(sender, instance, *args, **kwargs):
instance.boosted_status.user, instance.boosted_status.user,
instance.user, instance.user,
related_status=instance.boosted_status, related_status=instance.boosted_status,
notification_type=Notification.BOOST, notification_type=NotificationType.BOOST,
) )
@ -206,7 +206,7 @@ def notify_user_on_unboost(sender, instance, *args, **kwargs):
instance.boosted_status.user, instance.boosted_status.user,
instance.user, instance.user,
related_status=instance.boosted_status, related_status=instance.boosted_status,
notification_type=Notification.BOOST, notification_type=NotificationType.BOOST,
) )
@ -221,7 +221,7 @@ def notify_user_on_import_complete(
return return
Notification.objects.get_or_create( Notification.objects.get_or_create(
user=instance.user, user=instance.user,
notification_type=Notification.IMPORT, notification_type=NotificationType.IMPORT,
related_import=instance, related_import=instance,
) )
@ -236,11 +236,10 @@ def notify_admins_on_report(sender, instance, created, *args, **kwargs):
return return
# moderators and superusers should be notified # moderators and superusers should be notified
admins = User.admins() for admin in User.admins():
for admin in admins:
notification, _ = Notification.objects.get_or_create( notification, _ = Notification.objects.get_or_create(
user=admin, user=admin,
notification_type=Notification.REPORT, notification_type=NotificationType.REPORT,
read=False, read=False,
) )
notification.related_reports.add(instance) notification.related_reports.add(instance)
@ -256,16 +255,33 @@ def notify_admins_on_link_domain(sender, instance, created, *args, **kwargs):
return return
# moderators and superusers should be notified # moderators and superusers should be notified
admins = User.admins() for admin in User.admins():
for admin in admins:
notification, _ = Notification.objects.get_or_create( notification, _ = Notification.objects.get_or_create(
user=admin, user=admin,
notification_type=Notification.LINK_DOMAIN, notification_type=NotificationType.LINK_DOMAIN,
read=False, read=False,
) )
notification.related_link_domains.add(instance) notification.related_link_domains.add(instance)
@receiver(models.signals.post_save, sender=InviteRequest)
@transaction.atomic
# pylint: disable=unused-argument
def notify_admins_on_invite_request(sender, instance, created, *args, **kwargs):
"""need to handle a new invite request"""
if not created:
return
# moderators and superusers should be notified
for admin in User.admins():
notification, _ = Notification.objects.get_or_create(
user=admin,
notification_type=NotificationType.INVITE_REQUEST,
read=False,
)
notification.related_invite_requests.add(instance)
@receiver(models.signals.post_save, sender=GroupMemberInvitation) @receiver(models.signals.post_save, sender=GroupMemberInvitation)
# pylint: disable=unused-argument # pylint: disable=unused-argument
def notify_user_on_group_invite(sender, instance, *args, **kwargs): def notify_user_on_group_invite(sender, instance, *args, **kwargs):
@ -274,7 +290,7 @@ def notify_user_on_group_invite(sender, instance, *args, **kwargs):
instance.user, instance.user,
instance.group.user, instance.group.user,
related_group=instance.group, related_group=instance.group,
notification_type=Notification.INVITE, notification_type=NotificationType.INVITE,
) )
@ -312,11 +328,12 @@ def notify_user_on_follow(sender, instance, created, *args, **kwargs):
notification = Notification.objects.filter( notification = Notification.objects.filter(
user=instance.user_object, user=instance.user_object,
related_users=instance.user_subject, related_users=instance.user_subject,
notification_type=Notification.FOLLOW_REQUEST, notification_type=NotificationType.FOLLOW_REQUEST,
).first() ).first()
if not notification: if not notification:
notification = Notification.objects.create( notification = Notification.objects.create(
user=instance.user_object, notification_type=Notification.FOLLOW_REQUEST user=instance.user_object,
notification_type=NotificationType.FOLLOW_REQUEST,
) )
notification.related_users.set([instance.user_subject]) notification.related_users.set([instance.user_subject])
notification.read = False notification.read = False
@ -326,6 +343,6 @@ def notify_user_on_follow(sender, instance, created, *args, **kwargs):
Notification.notify( Notification.notify(
instance.user_object, instance.user_object,
instance.user_subject, instance.user_subject,
notification_type=Notification.FOLLOW, notification_type=NotificationType.FOLLOW,
read=False, read=False,
) )

View file

@ -21,6 +21,8 @@
{% include 'notifications/items/report.html' %} {% include 'notifications/items/report.html' %}
{% elif notification.notification_type == 'LINK_DOMAIN' %} {% elif notification.notification_type == 'LINK_DOMAIN' %}
{% include 'notifications/items/link_domain.html' %} {% include 'notifications/items/link_domain.html' %}
{% elif notification.notification_type == 'INVITE_REQUEST' %}
{% include 'notifications/items/invite_request.html' %}
{% elif notification.notification_type == 'INVITE' %} {% elif notification.notification_type == 'INVITE' %}
{% include 'notifications/items/invite.html' %} {% include 'notifications/items/invite.html' %}
{% elif notification.notification_type == 'ACCEPT' %} {% elif notification.notification_type == 'ACCEPT' %}

View file

@ -0,0 +1,20 @@
{% extends 'notifications/items/layout.html' %}
{% load humanize %}
{% load i18n %}
{% block primary_link %}{% spaceless %}
{% url 'settings-invite-requests' %}
{% endspaceless %}{% endblock %}
{% block icon %}
<span class="icon icon-envelope"></span>
{% endblock %}
{% block description %}
{% url 'settings-invite-requests' as path %}
{% blocktrans trimmed count counter=notification.related_invite_requests.count with display_count=notification.related_invite_requests.count|intcomma %}
New <a href="{{ path }}">invite request</a> awaiting response
{% plural %}
{{ display_count }} new <a href="{{ path }}">invite requests</a> awaiting response
{% endblocktrans %}
{% endblock %}

View file

@ -43,7 +43,7 @@ class Notification(TestCase):
def test_notification(self): def test_notification(self):
"""New notifications are unread""" """New notifications are unread"""
notification = models.Notification.objects.create( notification = models.Notification.objects.create(
user=self.local_user, notification_type=models.Notification.FAVORITE user=self.local_user, notification_type=models.NotificationType.FAVORITE
) )
self.assertFalse(notification.read) self.assertFalse(notification.read)
@ -52,7 +52,7 @@ class Notification(TestCase):
models.Notification.notify( models.Notification.notify(
self.local_user, self.local_user,
self.remote_user, self.remote_user,
notification_type=models.Notification.FAVORITE, notification_type=models.NotificationType.FAVORITE,
) )
self.assertTrue(models.Notification.objects.exists()) self.assertTrue(models.Notification.objects.exists())
@ -61,7 +61,7 @@ class Notification(TestCase):
models.Notification.notify( models.Notification.notify(
self.local_user, self.local_user,
self.remote_user, self.remote_user,
notification_type=models.Notification.FAVORITE, notification_type=models.NotificationType.FAVORITE,
) )
self.assertEqual(models.Notification.objects.count(), 1) self.assertEqual(models.Notification.objects.count(), 1)
notification = models.Notification.objects.get() notification = models.Notification.objects.get()
@ -70,7 +70,7 @@ class Notification(TestCase):
models.Notification.notify( models.Notification.notify(
self.local_user, self.local_user,
self.another_user, self.another_user,
notification_type=models.Notification.FAVORITE, notification_type=models.NotificationType.FAVORITE,
) )
self.assertEqual(models.Notification.objects.count(), 1) self.assertEqual(models.Notification.objects.count(), 1)
notification.refresh_from_db() notification.refresh_from_db()
@ -92,7 +92,7 @@ class Notification(TestCase):
models.Notification.notify( models.Notification.notify(
self.remote_user, self.remote_user,
self.local_user, self.local_user,
notification_type=models.Notification.FAVORITE, notification_type=models.NotificationType.FAVORITE,
) )
self.assertFalse(models.Notification.objects.exists()) self.assertFalse(models.Notification.objects.exists())
@ -101,7 +101,7 @@ class Notification(TestCase):
models.Notification.notify( models.Notification.notify(
self.local_user, self.local_user,
self.local_user, self.local_user,
notification_type=models.Notification.FAVORITE, notification_type=models.NotificationType.FAVORITE,
) )
self.assertFalse(models.Notification.objects.exists()) self.assertFalse(models.Notification.objects.exists())
@ -154,14 +154,14 @@ class Notification(TestCase):
models.Notification.notify( models.Notification.notify(
self.local_user, self.local_user,
self.remote_user, self.remote_user,
notification_type=models.Notification.FAVORITE, notification_type=models.NotificationType.FAVORITE,
) )
self.assertTrue(models.Notification.objects.exists()) self.assertTrue(models.Notification.objects.exists())
models.Notification.unnotify( models.Notification.unnotify(
self.local_user, self.local_user,
self.remote_user, self.remote_user,
notification_type=models.Notification.FAVORITE, notification_type=models.NotificationType.FAVORITE,
) )
self.assertFalse(models.Notification.objects.exists()) self.assertFalse(models.Notification.objects.exists())
@ -170,25 +170,112 @@ class Notification(TestCase):
models.Notification.notify( models.Notification.notify(
self.local_user, self.local_user,
self.remote_user, self.remote_user,
notification_type=models.Notification.FAVORITE, notification_type=models.NotificationType.FAVORITE,
) )
models.Notification.notify( models.Notification.notify(
self.local_user, self.local_user,
self.another_user, self.another_user,
notification_type=models.Notification.FAVORITE, notification_type=models.NotificationType.FAVORITE,
) )
self.assertTrue(models.Notification.objects.exists()) self.assertTrue(models.Notification.objects.exists())
models.Notification.unnotify( models.Notification.unnotify(
self.local_user, self.local_user,
self.remote_user, self.remote_user,
notification_type=models.Notification.FAVORITE, notification_type=models.NotificationType.FAVORITE,
) )
self.assertTrue(models.Notification.objects.exists()) self.assertTrue(models.Notification.objects.exists())
models.Notification.unnotify( models.Notification.unnotify(
self.local_user, self.local_user,
self.another_user, self.another_user,
notification_type=models.Notification.FAVORITE, notification_type=models.NotificationType.FAVORITE,
) )
self.assertFalse(models.Notification.objects.exists()) self.assertFalse(models.Notification.objects.exists())
class NotifyInviteRequest(TestCase):
"""let admins know of invite requests"""
def setUp(self):
"""ensure there is one admin"""
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"), patch(
"bookwyrm.activitystreams.populate_stream_task.delay"
), patch("bookwyrm.lists_stream.populate_lists_task.delay"):
self.local_user = models.User.objects.create_user(
"mouse@local.com",
"mouse@mouse.mouse",
"password",
local=True,
localname="mouse",
is_superuser=True,
)
def test_invite_request_triggers_notification(self):
"""requesting an invite notifies the admin"""
admin = models.User.objects.filter(is_superuser=True).first()
request = models.InviteRequest.objects.create(email="user@example.com")
self.assertEqual(models.Notification.objects.count(), 1)
notification = models.Notification.objects.first()
self.assertEqual(notification.user, admin)
self.assertEqual(
notification.notification_type, models.NotificationType.INVITE_REQUEST
)
self.assertEqual(notification.related_invite_requests.count(), 1)
self.assertEqual(notification.related_invite_requests.first(), request)
def test_notify_only_created(self):
"""updating an invite request does not trigger a notification"""
request = models.InviteRequest.objects.create(email="user@example.com")
notification = models.Notification.objects.first()
notification.delete()
self.assertEqual(models.Notification.objects.count(), 0)
request.ignored = True
request.save()
self.assertEqual(models.Notification.objects.count(), 0)
def test_notify_grouping(self):
"""invites group into the same notification, until read"""
requests = [
models.InviteRequest.objects.create(email="user1@example.com"),
models.InviteRequest.objects.create(email="user2@example.com"),
]
self.assertEqual(models.Notification.objects.count(), 1)
notification = models.Notification.objects.first()
self.assertEqual(notification.related_invite_requests.count(), 2)
self.assertCountEqual(notification.related_invite_requests.all(), requests)
notification.read = True
notification.save()
request = models.InviteRequest.objects.create(email="user3@example.com")
_, notification = models.Notification.objects.all()
self.assertEqual(models.Notification.objects.count(), 2)
self.assertEqual(notification.related_invite_requests.count(), 1)
self.assertEqual(notification.related_invite_requests.first(), request)
def test_notify_multiple_admins(self):
"""all admins are notified"""
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"), patch(
"bookwyrm.activitystreams.populate_stream_task.delay"
), patch("bookwyrm.lists_stream.populate_lists_task.delay"):
self.local_user = models.User.objects.create_user(
"admin@local.com",
"admin@example.com",
"password",
local=True,
localname="root",
is_superuser=True,
)
models.InviteRequest.objects.create(email="user@example.com")
admins = models.User.objects.filter(is_superuser=True).all()
notifications = models.Notification.objects.all()
self.assertEqual(len(notifications), 2)
self.assertCountEqual([notif.user for notif in notifications], admins)

View file

@ -13,9 +13,11 @@ from django.contrib.postgres.search import TrigramSimilarity
from django.db.models.functions import Greatest from django.db.models.functions import Greatest
from bookwyrm import forms, models from bookwyrm import forms, models
from bookwyrm.models import NotificationType
from bookwyrm.suggested_users import suggested_users from bookwyrm.suggested_users import suggested_users
from .helpers import get_user_from_username, maybe_redirect_local_path from .helpers import get_user_from_username, maybe_redirect_local_path
# pylint: disable=no-self-use # pylint: disable=no-self-use
class Group(View): class Group(View):
"""group page""" """group page"""
@ -59,11 +61,11 @@ class Group(View):
model = apps.get_model("bookwyrm.Notification", require_ready=True) model = apps.get_model("bookwyrm.Notification", require_ready=True)
for field in form.changed_data: for field in form.changed_data:
notification_type = ( notification_type = (
model.GROUP_PRIVACY NotificationType.GROUP_PRIVACY
if field == "privacy" if field == "privacy"
else model.GROUP_NAME else NotificationType.GROUP_NAME
if field == "name" if field == "name"
else model.GROUP_DESCRIPTION else NotificationType.GROUP_DESCRIPTION
if field == "description" if field == "description"
else None else None
) )
@ -251,7 +253,9 @@ def remove_member(request):
memberships = models.GroupMember.objects.filter(group=group) memberships = models.GroupMember.objects.filter(group=group)
model = apps.get_model("bookwyrm.Notification", require_ready=True) model = apps.get_model("bookwyrm.Notification", require_ready=True)
notification_type = model.LEAVE if user == request.user else model.REMOVE notification_type = (
NotificationType.LEAVE if user == request.user else NotificationType.REMOVE
)
# let the other members know about it # let the other members know about it
for membership in memberships: for membership in memberships:
member = membership.user member = membership.user
@ -264,7 +268,7 @@ def remove_member(request):
) )
# let the user (now ex-member) know as well, if they were removed # let the user (now ex-member) know as well, if they were removed
if notification_type == model.REMOVE: if notification_type == NotificationType.REMOVE:
model.notify( model.notify(
user, None, related_group=group, notification_type=notification_type user, None, related_group=group, notification_type=notification_type
) )

2
bw-dev
View file

@ -91,7 +91,7 @@ case "$CMD" in
$DOCKER_COMPOSE run --rm --service-ports web $DOCKER_COMPOSE run --rm --service-ports web
;; ;;
initdb) initdb)
initdb "@" initdb "$@"
;; ;;
resetdb) resetdb)
prod_error prod_error

View file

@ -1,4 +1,4 @@
aiohttp==3.8.5 aiohttp==3.8.6
bleach==5.0.1 bleach==5.0.1
celery==5.2.7 celery==5.2.7
colorthief==0.2.1 colorthief==0.2.1