From 8a801964565c4a3984fdaed93d348413faa6be65 Mon Sep 17 00:00:00 2001 From: Karl Hobley Date: Sat, 1 Aug 2020 19:07:59 +0100 Subject: [PATCH] Moved LogActionRegistry into wagtailcore Since logs are generated and stored in wagtailcore, I think it makes sense for the list of available actions to be managed there too. --- wagtail/admin/filters.py | 6 +- .../admin/templatetags/wagtailadmin_tags.py | 4 +- wagtail/admin/tests/test_audit_log.py | 30 --- wagtail/admin/views/reports.py | 4 +- wagtail/admin/wagtail_hooks.py | 199 ----------------- .../logging.py} | 2 +- wagtail/core/tests/test_audit_log.py | 30 +++ wagtail/core/wagtail_hooks.py | 200 ++++++++++++++++++ 8 files changed, 238 insertions(+), 237 deletions(-) rename wagtail/{admin/log_action_registry.py => core/logging.py} (97%) diff --git a/wagtail/admin/filters.py b/wagtail/admin/filters.py index a6f3a08bb5..f43e379fde 100644 --- a/wagtail/admin/filters.py +++ b/wagtail/admin/filters.py @@ -5,8 +5,8 @@ from django.db import models from django.utils.translation import gettext_lazy as _ from django_filters.widgets import SuffixedMultiWidget -from wagtail.admin.log_action_registry import registry as log_action_registry from wagtail.admin.widgets import AdminDateInput, BooleanButtonSelect, ButtonSelect, FilteredSelect +from wagtail.core.logging import page_log_action_registry from wagtail.core.models import Page, PageLogEntry, Task, TaskState, Workflow, WorkflowState @@ -185,7 +185,7 @@ def get_audit_log_users_queryset(request): class SiteHistoryReportFilterSet(WagtailFilterSet): - action = django_filters.ChoiceFilter(choices=log_action_registry.get_choices) + action = django_filters.ChoiceFilter(choices=page_log_action_registry.get_choices) timestamp = django_filters.DateFromToRangeFilter(label=_('Date'), widget=DateRangePickerWidget) label = django_filters.CharFilter(label=_('Title'), lookup_expr='icontains') user = django_filters.ModelChoiceFilter( @@ -198,7 +198,7 @@ class SiteHistoryReportFilterSet(WagtailFilterSet): class PageHistoryReportFilterSet(WagtailFilterSet): - action = django_filters.ChoiceFilter(choices=log_action_registry.get_choices) + action = django_filters.ChoiceFilter(choices=page_log_action_registry.get_choices) user = django_filters.ModelChoiceFilter( field_name='user', queryset=get_audit_log_users_queryset ) diff --git a/wagtail/admin/templatetags/wagtailadmin_tags.py b/wagtail/admin/templatetags/wagtailadmin_tags.py index e2381c2708..67b0d265cb 100644 --- a/wagtail/admin/templatetags/wagtailadmin_tags.py +++ b/wagtail/admin/templatetags/wagtailadmin_tags.py @@ -20,12 +20,12 @@ from django.utils.timesince import timesince from django.utils.translation import gettext_lazy as _ from wagtail.admin.localization import get_js_translation_strings -from wagtail.admin.log_action_registry import registry as log_action_registry from wagtail.admin.menu import admin_menu from wagtail.admin.navigation import get_explorable_root_page from wagtail.admin.search import admin_search_areas from wagtail.admin.staticfiles import versioned_static as versioned_static_func from wagtail.core import hooks +from wagtail.core.logging import page_log_action_registry from wagtail.core.models import ( Collection, CollectionViewRestriction, Locale, Page, PageLogEntry, PageViewRestriction, UserPagePermissionsProxy) @@ -583,7 +583,7 @@ def timesince_last_update(last_update, time_prefix='', use_shorthand=True): def format_action_log_message(log_entry): if not isinstance(log_entry, PageLogEntry): return '' - return log_action_registry.format_message(log_entry) + return page_log_action_registry.format_message(log_entry) @register.simple_tag diff --git a/wagtail/admin/tests/test_audit_log.py b/wagtail/admin/tests/test_audit_log.py index d2a627d113..44358e74de 100644 --- a/wagtail/admin/tests/test_audit_log.py +++ b/wagtail/admin/tests/test_audit_log.py @@ -7,41 +7,11 @@ from django.urls import reverse from django.utils import timezone from freezegun import freeze_time -from wagtail.admin.log_action_registry import LogActionRegistry from wagtail.core.models import GroupPagePermission, Page, PageLogEntry, PageViewRestriction from wagtail.tests.testapp.models import SimplePage from wagtail.tests.utils import WagtailTestUtils -def test_hook(actions): - return actions.register_action('test.custom_action', 'Custom action', 'Tested!') - - -class TestAuditLogHooks(TestCase, WagtailTestUtils): - def setUp(self): - self.root_page = Page.objects.get(id=2) - - def test_register_log_actions_hook(self): - # testapp/wagtail_hooks.py defines a 'blockquote' rich text feature with a hallo.js - # plugin, via the register_rich_text_features hook; test that we can retrieve it here - log_actions = LogActionRegistry() - actions = log_actions.get_actions() - self.assertIn('wagtail.create', actions) - - def test_action_format_message(self): - log_entry = PageLogEntry.objects.log_action(self.root_page, action='test.custom_action') - - log_actions = LogActionRegistry() - self.assertEqual(log_actions.format_message(log_entry), "Unknown test.custom_action") - self.assertNotIn('test.custom_action', log_actions.get_actions()) - - with self.register_hook('register_log_actions', test_hook): - log_actions = LogActionRegistry() - self.assertIn('test.custom_action', log_actions.get_actions()) - self.assertEqual(log_actions.format_message(log_entry), "Tested!") - self.assertEqual(log_actions.get_action_label('test.custom_action'), 'Custom action') - - class TestAuditLogAdmin(TestCase, WagtailTestUtils): def setUp(self): self.root_page = Page.objects.get(id=2) diff --git a/wagtail/admin/views/reports.py b/wagtail/admin/views/reports.py index 464fc491ac..1470ceb124 100644 --- a/wagtail/admin/views/reports.py +++ b/wagtail/admin/views/reports.py @@ -214,5 +214,5 @@ class LogEntriesView(ReportView): return PageLogEntry.objects.filter(q) def get_action_label(self, action): - from wagtail.admin.log_action_registry import registry as log_action_registry - return force_str(log_action_registry.get_action_label(action)) + from wagtail.core.logging import page_log_action_registry + return force_str(page_log_action_registry.get_action_label(action)) diff --git a/wagtail/admin/wagtail_hooks.py b/wagtail/admin/wagtail_hooks.py index 388afeae0c..2c994448a7 100644 --- a/wagtail/admin/wagtail_hooks.py +++ b/wagtail/admin/wagtail_hooks.py @@ -808,202 +808,3 @@ def register_icons(icons): @hooks.register('construct_homepage_summary_items') def add_pages_summary_item(request, items): items.insert(0, PagesSummaryItem(request)) - - -@hooks.register('register_log_actions') -def register_core_log_actions(actions): - actions.register_action('wagtail.create', _('Create'), _('Created')) - actions.register_action('wagtail.edit', _('Save draft'), _('Draft saved')) - actions.register_action('wagtail.delete', _('Delete'), _('Deleted')) - actions.register_action('wagtail.publish', _('Publish'), _('Published')) - actions.register_action('wagtail.publish.scheduled', _("Publish scheduled draft"), _('Published scheduled draft')) - actions.register_action('wagtail.unpublish', _('Unpublish'), _('Unpublished')) - actions.register_action('wagtail.unpublish.scheduled', _('Unpublish scheduled draft'), _('Unpublished scheduled draft')) - actions.register_action('wagtail.lock', _('Lock'), _('Locked')) - actions.register_action('wagtail.unlock', _('Unlock'), _('Unlocked')) - actions.register_action('wagtail.moderation.approve', _('Approve'), _('Approved')) - actions.register_action('wagtail.moderation.reject', _('Reject'), _('Rejected')) - - def revert_message(data): - try: - return _('Reverted to previous revision with id %(revision_id)s from %(created_at)s') % { - 'revision_id': data['revision']['id'], - 'created_at': data['revision']['created'], - } - except KeyError: - return _('Reverted to previous revision') - - def copy_message(data): - try: - return _('Copied from %(title)s') % { - 'title': data['source']['title'], - } - except KeyError: - return _("Copied") - - def create_alias_message(data): - try: - return _('Created an alias of %(title)s') % { - 'title': data['source']['title'], - } - except KeyError: - return _("Created an alias") - - def convert_alias_message(data): - try: - return _("Converted the alias '%(title)s' into a regular page") % { - 'title': data['page']['title'], - } - except KeyError: - return _("Converted an alias into a regular page") - - def move_message(data): - try: - return _("Moved from '%(old_parent)s' to '%(new_parent)s'") % { - 'old_parent': data['source']['title'], - 'new_parent': data['destination']['title'], - } - except KeyError: - return _('Moved') - - def reorder_message(data): - try: - return _("Reordered under '%(parent)s'") % { - 'parent': data['destination']['title'], - } - except KeyError: - return _('Reordered') - - def schedule_publish_message(data): - try: - if data['revision']['has_live_version']: - return _('Revision %(revision_id)s from %(created_at)s scheduled for publishing at %(go_live_at)s.') % { - 'revision_id': data['revision']['id'], - 'created_at': data['revision']['created'], - 'go_live_at': data['revision']['go_live_at'], - } - else: - return _('Page scheduled for publishing at %(go_live_at)s') % { - 'go_live_at': data['revision']['go_live_at'], - } - except KeyError: - return _('Page scheduled for publishing') - - def unschedule_publish_message(data): - try: - if data['revision']['has_live_version']: - return _('Revision %(revision_id)s from %(created_at)s unscheduled from publishing at %(go_live_at)s.') % { - 'revision_id': data['revision']['id'], - 'created_at': data['revision']['created'], - 'go_live_at': data['revision']['go_live_at'], - } - else: - return _('Page unscheduled for publishing at %(go_live_at)s') % { - 'go_live_at': data['revision']['go_live_at'], - } - except KeyError: - return _('Page unscheduled from publishing') - - def add_view_restriction(data): - try: - return _("Added the '%(restriction)s' view restriction") % { - 'restriction': data['restriction']['title'], - } - except KeyError: - return _('Added view restriction') - - def edit_view_restriction(data): - try: - return _("Updated the view restriction to '%(restriction)s'") % { - 'restriction': data['restriction']['title'], - } - except KeyError: - return _('Updated view restriction') - - def delete_view_restriction(data): - try: - return _("Removed the '%(restriction)s' view restriction") % { - 'restriction': data['restriction']['title'], - } - except KeyError: - return _('Removed view restriction') - - def rename_message(data): - try: - return _("Renamed from '%(old)s' to '%(new)s'") % { - 'old': data['title']['old'], - 'new': data['title']['new'], - } - except KeyError: - return _('Renamed') - - actions.register_action('wagtail.rename', _('Rename'), rename_message) - actions.register_action('wagtail.revert', _('Revert'), revert_message) - actions.register_action('wagtail.copy', _('Copy'), copy_message) - actions.register_action('wagtail.create_alias', _('Create alias'), create_alias_message) - actions.register_action('wagtail.convert_alias', _('Convert alias into regular page'), convert_alias_message) - actions.register_action('wagtail.move', _('Move'), move_message) - actions.register_action('wagtail.reorder', _('Reorder'), reorder_message) - actions.register_action('wagtail.publish.schedule', _("Schedule publication"), schedule_publish_message) - actions.register_action('wagtail.schedule.cancel', _("Unschedule publication"), unschedule_publish_message) - actions.register_action('wagtail.view_restriction.create', _("Add view restrictions"), add_view_restriction) - actions.register_action('wagtail.view_restriction.edit', _("Update view restrictions"), edit_view_restriction) - actions.register_action('wagtail.view_restriction.delete', _("Remove view restrictions"), delete_view_restriction) - - -@hooks.register('register_log_actions') -def register_workflow_log_actions(actions): - def workflow_start_message(data): - try: - return _("'%(workflow)s' started. Next step '%(task)s'") % { - 'workflow': data['workflow']['title'], - 'task': data['workflow']['next']['title'], - } - except (KeyError, TypeError): - return _('Workflow started') - - def workflow_approve_message(data): - try: - if data['workflow']['next']: - return _("Approved at '%(task)s'. Next step '%(next_task)s'") % { - 'task': data['workflow']['task']['title'], - 'next_task': data['workflow']['next']['title'], - } - else: - return _("Approved at '%(task)s'. '%(workflow)s' complete") % { - 'task': data['workflow']['task']['title'], - 'workflow': data['workflow']['title'], - } - except (KeyError, TypeError): - return _('Workflow task approved') - - def workflow_reject_message(data): - try: - return _("Rejected at '%(task)s'. Changes requested") % { - 'task': data['workflow']['task']['title'], - } - except (KeyError, TypeError): - return _('Workflow task rejected. Workflow complete') - - def workflow_resume_message(data): - try: - return _("Resubmitted '%(task)s'. Workflow resumed'") % { - 'task': data['workflow']['task']['title'], - } - except (KeyError, TypeError): - return _('Workflow task resubmitted. Workflow resumed') - - def workflow_cancel_message(data): - try: - return _("Cancelled '%(workflow)s' at '%(task)s'") % { - 'workflow': data['workflow']['title'], - 'task': data['workflow']['task']['title'], - } - except (KeyError, TypeError): - return _('Workflow cancelled') - - actions.register_action('wagtail.workflow.start', _('Workflow: start'), workflow_start_message) - actions.register_action('wagtail.workflow.approve', _('Workflow: approve task'), workflow_approve_message) - actions.register_action('wagtail.workflow.reject', _('Workflow: reject task'), workflow_reject_message) - actions.register_action('wagtail.workflow.resume', _('Workflow: resume task'), workflow_resume_message) - actions.register_action('wagtail.workflow.cancel', _('Workflow: cancel'), workflow_cancel_message) diff --git a/wagtail/admin/log_action_registry.py b/wagtail/core/logging.py similarity index 97% rename from wagtail/admin/log_action_registry.py rename to wagtail/core/logging.py index edb7e213b7..d5b7140792 100644 --- a/wagtail/admin/log_action_registry.py +++ b/wagtail/core/logging.py @@ -57,4 +57,4 @@ class LogActionRegistry: return self.get_actions()[action][0] -registry = LogActionRegistry() +page_log_action_registry = LogActionRegistry() diff --git a/wagtail/core/tests/test_audit_log.py b/wagtail/core/tests/test_audit_log.py index 8ba71be5e1..f342d37fbd 100644 --- a/wagtail/core/tests/test_audit_log.py +++ b/wagtail/core/tests/test_audit_log.py @@ -6,6 +6,7 @@ from django.test import TestCase from django.utils import timezone from freezegun import freeze_time +from wagtail.core.logging import LogActionRegistry from wagtail.core.models import ( Page, PageLogEntry, PageViewRestriction, Task, Workflow, WorkflowTask) from wagtail.tests.testapp.models import SimplePage @@ -307,3 +308,32 @@ class TestAuditLog(TestCase): restriction.restriction_type = PageViewRestriction.PASSWORD restriction.save() self.assertEqual(PageLogEntry.objects.filter(action='wagtail.view_restriction.edit').count(), 1) + + +def test_hook(actions): + return actions.register_action('test.custom_action', 'Custom action', 'Tested!') + + +class TestAuditLogHooks(TestCase, WagtailTestUtils): + def setUp(self): + self.root_page = Page.objects.get(id=2) + + def test_register_log_actions_hook(self): + # testapp/wagtail_hooks.py defines a 'blockquote' rich text feature with a hallo.js + # plugin, via the register_rich_text_features hook; test that we can retrieve it here + log_actions = LogActionRegistry() + actions = log_actions.get_actions() + self.assertIn('wagtail.create', actions) + + def test_action_format_message(self): + log_entry = PageLogEntry.objects.log_action(self.root_page, action='test.custom_action') + + log_actions = LogActionRegistry() + self.assertEqual(log_actions.format_message(log_entry), "Unknown test.custom_action") + self.assertNotIn('test.custom_action', log_actions.get_actions()) + + with self.register_hook('register_log_actions', test_hook): + log_actions = LogActionRegistry() + self.assertIn('test.custom_action', log_actions.get_actions()) + self.assertEqual(log_actions.format_message(log_entry), "Tested!") + self.assertEqual(log_actions.get_action_label('test.custom_action'), 'Custom action') diff --git a/wagtail/core/wagtail_hooks.py b/wagtail/core/wagtail_hooks.py index 537ddbd678..084780fc6d 100644 --- a/wagtail/core/wagtail_hooks.py +++ b/wagtail/core/wagtail_hooks.py @@ -2,6 +2,7 @@ from django.conf import settings from django.contrib.auth.models import Permission from django.contrib.auth.views import redirect_to_login from django.urls import reverse +from django.utils.translation import gettext as _ from django.utils.translation import ngettext from wagtail.core import hooks @@ -92,3 +93,202 @@ def describe_collection_children(collection): ) % {'count': descendant_count}, 'url': url, } + + +@hooks.register('register_log_actions') +def register_core_log_actions(actions): + actions.register_action('wagtail.create', _('Create'), _('Created')) + actions.register_action('wagtail.edit', _('Save draft'), _('Draft saved')) + actions.register_action('wagtail.delete', _('Delete'), _('Deleted')) + actions.register_action('wagtail.publish', _('Publish'), _('Published')) + actions.register_action('wagtail.publish.scheduled', _("Publish scheduled draft"), _('Published scheduled draft')) + actions.register_action('wagtail.unpublish', _('Unpublish'), _('Unpublished')) + actions.register_action('wagtail.unpublish.scheduled', _('Unpublish scheduled draft'), _('Unpublished scheduled draft')) + actions.register_action('wagtail.lock', _('Lock'), _('Locked')) + actions.register_action('wagtail.unlock', _('Unlock'), _('Unlocked')) + actions.register_action('wagtail.moderation.approve', _('Approve'), _('Approved')) + actions.register_action('wagtail.moderation.reject', _('Reject'), _('Rejected')) + + def revert_message(data): + try: + return _('Reverted to previous revision with id %(revision_id)s from %(created_at)s') % { + 'revision_id': data['revision']['id'], + 'created_at': data['revision']['created'], + } + except KeyError: + return _('Reverted to previous revision') + + def copy_message(data): + try: + return _('Copied from %(title)s') % { + 'title': data['source']['title'], + } + except KeyError: + return _("Copied") + + def create_alias_message(data): + try: + return _('Created an alias of %(title)s') % { + 'title': data['source']['title'], + } + except KeyError: + return _("Created an alias") + + def convert_alias_message(data): + try: + return _("Converted the alias '%(title)s' into a regular page") % { + 'title': data['page']['title'], + } + except KeyError: + return _("Converted an alias into a regular page") + + def move_message(data): + try: + return _("Moved from '%(old_parent)s' to '%(new_parent)s'") % { + 'old_parent': data['source']['title'], + 'new_parent': data['destination']['title'], + } + except KeyError: + return _('Moved') + + def reorder_message(data): + try: + return _("Reordered under '%(parent)s'") % { + 'parent': data['destination']['title'], + } + except KeyError: + return _('Reordered') + + def schedule_publish_message(data): + try: + if data['revision']['has_live_version']: + return _('Revision %(revision_id)s from %(created_at)s scheduled for publishing at %(go_live_at)s.') % { + 'revision_id': data['revision']['id'], + 'created_at': data['revision']['created'], + 'go_live_at': data['revision']['go_live_at'], + } + else: + return _('Page scheduled for publishing at %(go_live_at)s') % { + 'go_live_at': data['revision']['go_live_at'], + } + except KeyError: + return _('Page scheduled for publishing') + + def unschedule_publish_message(data): + try: + if data['revision']['has_live_version']: + return _('Revision %(revision_id)s from %(created_at)s unscheduled from publishing at %(go_live_at)s.') % { + 'revision_id': data['revision']['id'], + 'created_at': data['revision']['created'], + 'go_live_at': data['revision']['go_live_at'], + } + else: + return _('Page unscheduled for publishing at %(go_live_at)s') % { + 'go_live_at': data['revision']['go_live_at'], + } + except KeyError: + return _('Page unscheduled from publishing') + + def add_view_restriction(data): + try: + return _("Added the '%(restriction)s' view restriction") % { + 'restriction': data['restriction']['title'], + } + except KeyError: + return _('Added view restriction') + + def edit_view_restriction(data): + try: + return _("Updated the view restriction to '%(restriction)s'") % { + 'restriction': data['restriction']['title'], + } + except KeyError: + return _('Updated view restriction') + + def delete_view_restriction(data): + try: + return _("Removed the '%(restriction)s' view restriction") % { + 'restriction': data['restriction']['title'], + } + except KeyError: + return _('Removed view restriction') + + def rename_message(data): + try: + return _("Renamed from '%(old)s' to '%(new)s'") % { + 'old': data['title']['old'], + 'new': data['title']['new'], + } + except KeyError: + return _('Renamed') + + actions.register_action('wagtail.rename', _('Rename'), rename_message) + actions.register_action('wagtail.revert', _('Revert'), revert_message) + actions.register_action('wagtail.copy', _('Copy'), copy_message) + actions.register_action('wagtail.create_alias', _('Create alias'), create_alias_message) + actions.register_action('wagtail.convert_alias', _('Convert alias into regular page'), convert_alias_message) + actions.register_action('wagtail.move', _('Move'), move_message) + actions.register_action('wagtail.reorder', _('Reorder'), reorder_message) + actions.register_action('wagtail.publish.schedule', _("Schedule publication"), schedule_publish_message) + actions.register_action('wagtail.schedule.cancel', _("Unschedule publication"), unschedule_publish_message) + actions.register_action('wagtail.view_restriction.create', _("Add view restrictions"), add_view_restriction) + actions.register_action('wagtail.view_restriction.edit', _("Update view restrictions"), edit_view_restriction) + actions.register_action('wagtail.view_restriction.delete', _("Remove view restrictions"), delete_view_restriction) + + +@hooks.register('register_log_actions') +def register_workflow_log_actions(actions): + def workflow_start_message(data): + try: + return _("'%(workflow)s' started. Next step '%(task)s'") % { + 'workflow': data['workflow']['title'], + 'task': data['workflow']['next']['title'], + } + except (KeyError, TypeError): + return _('Workflow started') + + def workflow_approve_message(data): + try: + if data['workflow']['next']: + return _("Approved at '%(task)s'. Next step '%(next_task)s'") % { + 'task': data['workflow']['task']['title'], + 'next_task': data['workflow']['next']['title'], + } + else: + return _("Approved at '%(task)s'. '%(workflow)s' complete") % { + 'task': data['workflow']['task']['title'], + 'workflow': data['workflow']['title'], + } + except (KeyError, TypeError): + return _('Workflow task approved') + + def workflow_reject_message(data): + try: + return _("Rejected at '%(task)s'. Changes requested") % { + 'task': data['workflow']['task']['title'], + } + except (KeyError, TypeError): + return _('Workflow task rejected. Workflow complete') + + def workflow_resume_message(data): + try: + return _("Resubmitted '%(task)s'. Workflow resumed'") % { + 'task': data['workflow']['task']['title'], + } + except (KeyError, TypeError): + return _('Workflow task resubmitted. Workflow resumed') + + def workflow_cancel_message(data): + try: + return _("Cancelled '%(workflow)s' at '%(task)s'") % { + 'workflow': data['workflow']['title'], + 'task': data['workflow']['task']['title'], + } + except (KeyError, TypeError): + return _('Workflow cancelled') + + actions.register_action('wagtail.workflow.start', _('Workflow: start'), workflow_start_message) + actions.register_action('wagtail.workflow.approve', _('Workflow: approve task'), workflow_approve_message) + actions.register_action('wagtail.workflow.reject', _('Workflow: reject task'), workflow_reject_message) + actions.register_action('wagtail.workflow.resume', _('Workflow: resume task'), workflow_resume_message) + actions.register_action('wagtail.workflow.cancel', _('Workflow: cancel'), workflow_cancel_message)