Add purge_revisions management command

* Fix typo in publish_scheduled_pages dry run
* Add purge_revisions to docs
* Resolves #5192
pull/6015/head
jacobtoppm 2020-05-05 15:19:57 +01:00 zatwierdzone przez LB
rodzic 52cb7ab77b
commit 30c398c91a
6 zmienionych plików z 164 dodań i 1 usunięć

Wyświetl plik

@ -22,6 +22,7 @@ Changelog
* Add ability to replace the default Wagtail logo in the userbar, via `branding_logo` block (Meteor0id)
* Remove sticky footer on small devices, so that content is not blocked and more easily editable (Saeed Tahmasebi)
* Add ``alt`` property to ``ImageRenditionField`` api representation (Liam Mullens)
* Add ``purge_revisions`` management command to purge old page revisions (Jacob Topp-Mugglestone, Tom Dyson)
* Fix: Support IPv6 domain (Alex Gleason, Coen van der Kamp)
* Fix: Ensure link to add a new user works when no users are visible in the users list (LB (Ben Johnston))
* Fix: `AbstractEmailForm` saved submission fields are now aligned with the email content fields, `form.cleaned_data` will be used instead of `form.fields` (Haydn Greatnews)

Wyświetl plik

@ -48,6 +48,20 @@ Options:
This is the **id** of the page to move pages to.
.. _purge_revisions:
purge_revisions
---------------
.. code-block:: console
$ manage.py purge_revisions [--days=<number of days>]
This command deletes old page revisions which are not in moderation, live, approved to go live, or the latest
revision for a page. If the ``days`` argument is supplied, only revisions older than the specified number of
days will be deleted.
.. _update_index:
update_index

Wyświetl plik

@ -31,6 +31,7 @@ Other features
* Remove sticky footer on small devices, so that content is not blocked and more easily editable (Saeed Tahmasebi)
* Add ability to replace the default Wagtail logo in the userbar, via ``branding_logo`` block (Meteor0id)
* Add `alt` property to `ImageRenditionField` api representation (Liam Mullens)
* Add `purge_revisions` management command to purge old page revisions (Jacob Topp-Mugglestone, Tom Dyson)
Bug fixes

Wyświetl plik

@ -21,7 +21,7 @@ class Command(BaseCommand):
def add_arguments(self, parser):
parser.add_argument(
'--dryrun', action='store_true', dest='dryrun', default=False,
help="Dry run -- dont't change anything.")
help="Dry run -- don't change anything.")
def handle(self, *args, **options):
dryrun = False

Wyświetl plik

@ -0,0 +1,57 @@
from django.core.management.base import BaseCommand
from django.utils import timezone
from wagtail.core.models import PageRevision
try:
from wagtail.core.models import WorkflowState
workflow_support = True
except ImportError:
workflow_support = False
class Command(BaseCommand):
help = 'Delete page revisions which are not the latest revision for a page, published or scheduled to be published, or in moderation'
def add_arguments(self, parser):
parser.add_argument('--days', type=int, help="Only delete revisions older than this number of days")
def handle(self, *args, **options):
days = options.get('days')
revisions_deleted = purge_revisions(days=days)
if revisions_deleted:
self.stdout.write(self.style.SUCCESS('Successfully deleted %s revisions' % revisions_deleted))
else:
self.stdout.write("No revisions deleted")
def purge_revisions(days=None):
# exclude revisions which have been submitted for moderation in the old system
purgeable_revisions = PageRevision.objects.exclude(
submitted_for_moderation=True
).exclude(
# and exclude revisions with an approved_go_live_at date
approved_go_live_at__isnull=False)
if workflow_support:
purgeable_revisions = purgeable_revisions.exclude(
# and exclude revisions linked to an in progress workflow state
task_states__workflow_state__status=WorkflowState.STATUS_IN_PROGRESS
)
if days:
purgeable_until = timezone.now() - timezone.timedelta(days=days)
# only include revisions which were created before the cut off date
purgeable_revisions = purgeable_revisions.filter(created_at__lt=purgeable_until)
deleted_revisions_count = 0
for revision in purgeable_revisions:
# don't delete the latest revision for any page
if not revision.is_latest_revision():
revision.delete()
deleted_revisions_count += 1
return deleted_revisions_count

Wyświetl plik

@ -1,6 +1,7 @@
from datetime import timedelta
from io import StringIO
from django.contrib.auth import get_user_model
from django.core import management
from django.db import models
from django.test import TestCase
@ -332,3 +333,92 @@ class TestPublishScheduledPagesCommand(TestCase):
p = Page.objects.get(slug='hello-world')
self.assertFalse(PageRevision.objects.filter(page=p, submitted_for_moderation=True).exists())
class TestPurgeRevisionsCommand(TestCase):
fixtures = ['test.json']
def setUp(self):
# Find root page
self.root_page = Page.objects.get(id=2)
self.page = SimplePage(
title="Hello world!",
slug="hello-world",
content="hello",
live=False,
)
self.root_page.add_child(instance=self.page)
self.page.refresh_from_db()
def run_command(self, days=None):
if days:
days_input = '--days=' + str(days)
return management.call_command('purge_revisions', days_input, stdout=StringIO())
return management.call_command('purge_revisions', stdout=StringIO())
def test_latest_revision_not_purged(self):
revision_1 = self.page.save_revision()
revision_2 = self.page.save_revision()
self.run_command()
# revision 1 should be deleted, revision 2 should not be
self.assertNotIn(revision_1, PageRevision.objects.filter(page=self.page))
self.assertIn(revision_2, PageRevision.objects.filter(page=self.page))
def test_revisions_in_moderation_not_purged(self):
self.page.save_revision(submitted_for_moderation=True)
revision = self.page.save_revision()
self.run_command()
self.assertTrue(PageRevision.objects.filter(page=self.page, submitted_for_moderation=True).exists())
try:
from wagtail.core.models import Task, Workflow, WorkflowTask
workflow = Workflow.objects.create(name='test_workflow')
task_1 = Task.objects.create(name='test_task_1')
user = get_user_model().objects.first()
WorkflowTask.objects.create(workflow=workflow, task=task_1, sort_order=1)
workflow.start(self.page, user)
self.page.save_revision()
self.run_command()
# even though no longer the latest revision, the old revision should stay as it is
# attached to an in progress workflow
self.assertIn(revision, PageRevision.objects.filter(page=self.page))
except ImportError:
pass
def test_revisions_with_approve_go_live_not_purged(self):
approved_revision = self.page.save_revision(approved_go_live_at=timezone.now() + timedelta(days=1))
self.page.save_revision()
self.run_command()
self.assertIn(approved_revision, PageRevision.objects.filter(page=self.page))
def test_purge_revisions_with_date_cutoff(self):
old_revision = self.page.save_revision()
self.page.save_revision()
self.run_command(days=30)
# revision should not be deleted, as it is younger than 30 days
self.assertIn(old_revision, PageRevision.objects.filter(page=self.page))
old_revision.created_at = timezone.now() - timedelta(days=31)
old_revision.save()
self.run_command(days=30)
# revision is now older than 30 days, so should be deleted
self.assertNotIn(old_revision, PageRevision.objects.filter(page=self.page))