diff --git a/taiga/projects/notifications/services.py b/taiga/projects/notifications/services.py index dfe71f15..b8453b16 100644 --- a/taiga/projects/notifications/services.py +++ b/taiga/projects/notifications/services.py @@ -131,7 +131,7 @@ def _filter_notificable(user): return user.is_active and not user.is_system -def get_users_to_notify(obj, *, discard_users=None) -> list: +def get_users_to_notify(obj, *, history=None, discard_users=None) -> list: """ Get filtered set of users to notify for specified model instance and changer. @@ -156,13 +156,21 @@ def get_users_to_notify(obj, *, discard_users=None) -> list: candidates.update(filter(_can_notify_light, obj.get_watchers())) candidates.update(filter(_can_notify_light, obj.get_participants())) + # If the history is an unassignment change we should notify that user too + if history and history.type == HistoryType.change and "assigned_to" in history.diff: + assigned_to_users = get_user_model().objects.filter(id__in=history.diff["assigned_to"]) + candidates.update(filter(_can_notify_light, assigned_to_users)) + # Remove the changer from candidates if discard_users: candidates = candidates - set(discard_users) + # Filter by object permissions candidates = set(filter(partial(_filter_by_permissions, obj), candidates)) + # Filter disabled and system users candidates = set(filter(partial(_filter_notificable), candidates)) + return frozenset(candidates) @@ -218,15 +226,9 @@ def send_notifications(obj, *, history): # Get a complete list of notifiable users for current # object and send the change notification to them. - notify_users = get_users_to_notify(obj, discard_users=[notification.owner]) + notify_users = get_users_to_notify(obj, history=history, discard_users=[notification.owner]) notification.notify_users.add(*notify_users) - # If the history is an unassignment change we should notify that user too - if history.type == HistoryType.change and "assigned_to" in history.diff: - for assigned_to in history.diff["assigned_to"]: - if assigned_to is not None and owner.id != assigned_to: - notification.notify_users.add(assigned_to) - # If we are the min interval is 0 it just work in a synchronous and spamming way if settings.CHANGE_NOTIFICATIONS_MIN_INTERVAL == 0: send_sync_notifications(notification.id) diff --git a/tests/integration/test_notifications.py b/tests/integration/test_notifications.py index cdd29270..4bc99ad1 100644 --- a/tests/integration/test_notifications.py +++ b/tests/integration/test_notifications.py @@ -762,6 +762,46 @@ def test_send_notifications_on_unassigned(client, mail): assert mail.outbox[0].to == [member2.user.email] +def test_send_notifications_on_unassigned_and_notifications_are_disabled(client, mail): + project = f.ProjectFactory.create() + role = f.RoleFactory.create(project=project, permissions=['modify_issue', 'view_issues', 'view_us', 'view_tasks', 'view_wiki_pages']) + member1 = f.MembershipFactory.create(project=project, role=role) + member2 = f.MembershipFactory.create(project=project, role=role) + + member2_notify_policy = member2.user.notify_policies.get(project=project) + member2_notify_policy.notify_level = NotifyLevel.none + member2_notify_policy.save() + + issue = f.IssueFactory.create(project=project, + owner=member1.user, + milestone=None, + status=project.default_issue_status, + severity=project.default_severity, + priority=project.default_priority, + type=project.default_issue_type) + + take_snapshot(issue, user=issue.owner) + + client.login(member1.user) + url = reverse("issues-detail", args=[issue.pk]) + data = { + "assigned_to": member2.user.id, + "version": issue.version + } + response = client.json.patch(url, json.dumps(data)) + assert len(mail.outbox) == 0 + + mail.outbox = [] + + data = { + "assigned_to": None, + "version": issue.version + 1 + } + response = client.json.patch(url, json.dumps(data)) + assert len(mail.outbox) == 0 + + + def test_not_send_notifications_on_unassigned_if_executer_and_unassigned_match(client, mail): project = f.ProjectFactory.create() role = f.RoleFactory.create(project=project, permissions=['modify_issue', 'view_issues', 'view_us', 'view_tasks', 'view_wiki_pages'])