Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Slack notifications for change in view restrictions #1729

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 75 additions & 16 deletions home/signals.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import requests

from django.conf import settings
from django.db.models.signals import post_save
from django.dispatch import receiver
from django.urls import reverse
from wagtail.core.models import PageLogEntry
from wagtail.core.models import PageLogEntry, BaseViewRestriction
from wagtail.core.signals import workflow_submitted, workflow_cancelled, workflow_approved, workflow_rejected, page_published


Expand All @@ -18,6 +20,37 @@ def get_environment_prefix():
return environment


def get_restrictions_text(page):
restrictions = page.get_view_restrictions()

if not restrictions.first():
return ''

restriction = restrictions.first()
restriction_type = restriction.restriction_type
restrictions_text = f' with restriction {restriction.get_restriction_type_display()}'

if restriction_type == BaseViewRestriction.PASSWORD and restriction.password:
restrictions_text += f': `{restriction.password}`'
elif restriction_type == BaseViewRestriction.GROUPS and restriction.groups.exists():
groups_list = list(restriction.groups.all())
restrictions_text += f': {groups_list[0]}'
for group in groups_list[1:]:
restrictions_text += f', {group}'
return restrictions_text


def get_page_text(page):
parent_programs = getattr(page.specific, 'parent_programs', None)
if parent_programs and parent_programs.first():
program = f'{parent_programs.first().title} '
else:
program = ''
page_type = f'{program}{page.specific._meta.verbose_name.title()}'

return f'<{page.full_url}|{page.title}>* ({page_type})'


def post_message_to_slack(blocks):
if not (settings.SLACK_NOTIFICATIONS_WEBHOOK and settings.SLACK_NOTIFICATIONS_CHANNEL):
return
Expand All @@ -38,6 +71,7 @@ def post_message_to_slack(blocks):
print(f'Response text: {response.text}')


@receiver(workflow_submitted)
def notify_submitted(sender, instance, user, **kwargs):
header_format = ':inbox_tray: Submission to {workflow_name}'
message_format = '*<{preview_url}|{page_title}>* ({page_type}) submitted by {user_name}'
Expand All @@ -50,6 +84,7 @@ def notify_submitted(sender, instance, user, **kwargs):
)


@receiver(workflow_cancelled)
def notify_cancelled(sender, instance, user, **kwargs):
message_format = ':heavy_multiplication_x: *{page_title}* ({page_type}) cancelled by {user_name}'

Expand All @@ -60,6 +95,7 @@ def notify_cancelled(sender, instance, user, **kwargs):
)


@receiver(workflow_approved)
def notify_approved(sender, instance, user, **kwargs):
message_format = ':white_check_mark: *<{page_url}|{page_title}>* ({page_type}) approved by {user_name}{comment}'

Expand All @@ -70,6 +106,7 @@ def notify_approved(sender, instance, user, **kwargs):
)


@receiver(workflow_rejected)
def notify_rejected(sender, instance, user, **kwargs):
message_format = ':x: *{page_title}* ({page_type}) rejected by {user_name}{comment}'

Expand Down Expand Up @@ -145,32 +182,23 @@ def workflow_notify_slack(message_format, workflow_state, user, header_format=No
post_message_to_slack(blocks)


workflow_submitted.connect(notify_submitted)
workflow_cancelled.connect(notify_cancelled)
workflow_approved.connect(notify_approved)
workflow_rejected.connect(notify_rejected)


@receiver(page_published)
def publication_notify_slack(sender, instance, revision, **kwargs):
environment = get_environment_prefix()
page = instance
parent_programs = getattr(page.specific, 'parent_programs', None)
if parent_programs and parent_programs.first():
program = f'{parent_programs.first().title} '
else:
program = ''
page_type = f'{program}{page.specific._meta.verbose_name.title()}'
page_text = get_page_text(page)
user_name = f'{revision.user.first_name} {revision.user.last_name}'
restrictions_text = get_restrictions_text(page)

latest_log_entry = PageLogEntry.objects.filter(page=page).order_by('-timestamp').first()

# If this was not published via workflow approval (so that we don't send a duplicate notification)
if latest_log_entry and latest_log_entry.action != 'wagtail.workflow.approve':
# If this is the first time the page is being published
if page.first_published_at == page.last_published_at:
message = f'{environment}*<{page.full_url}|{page.title}>* ({page_type}) published for the first time by {user_name}'
message = f'{environment}*<{page_text} published for the first time by {user_name}{restrictions_text}'
else:
message = f'{environment}*<{page.full_url}|{page.title}>* ({page_type}) published by {user_name}'
message = f'{environment}*<{page_text} published by {user_name}{restrictions_text}'
blocks = [
{
'type': 'section',
Expand All @@ -182,4 +210,35 @@ def publication_notify_slack(sender, instance, revision, **kwargs):
]
post_message_to_slack(blocks)

page_published.connect(publication_notify_slack)
# Could use PageViewRestriction as the sender, but:
# TODO: Make publication notification a part of this
# TODO: Add unpublish notification
@receiver(post_save, sender=PageLogEntry)
def log_entry_notify_slack(sender, instance, **kwargs):
# TODO: Don't fetch this information if we're not going to send any messages
environment = get_environment_prefix()
page = instance.page
page_text = get_page_text(page)
user_name = f'{instance.user.first_name} {instance.user.last_name}'

# TODO: Check whether the page is published. No need to notify about changing retrictions for draft pages.
if instance.action == 'wagtail.view_restriction.edit':
restrictions_text = get_restrictions_text(page)
message = f'{environment}*<{page_text} made private by {user_name}{restrictions_text}'
elif instance.action == 'wagtail.view_restriction.delete':
message = f'{environment}*<{page_text} made public by {user_name}'
else:
message = ''

if message:
blocks = [
{
'type': 'section',
'text': {
'type': 'mrkdwn',
'text': message,
}
},
]

post_message_to_slack(blocks)