mirror of
https://github.com/Hopiu/wagtail.git
synced 2026-05-01 03:54:48 +00:00
Merge branch 'kaedroho-page-publish-refactor'
This commit is contained in:
commit
9b26fe1a78
9 changed files with 178 additions and 123 deletions
|
|
@ -17,6 +17,7 @@ Changelog
|
|||
* Fix: 'wagtail start' command now works on Windows
|
||||
* Fix: The external image URL generator no longer stores generated images in Django's cache
|
||||
* Fix: Elasticsearch backend can now search querysets that have been filtered with an 'in' clause of a non-list type (such as a ValuesListQuerySet)
|
||||
* Fix: Logic around the has_unpublished_changes flag has been fixed, to prevent issues with the 'View draft' button failing to show in some cases
|
||||
* Fix: It is now easier to move pages to the beginning and end of their section
|
||||
* Fix: Image rendering no longer creates erroneous duplicate Rendition records when the focal point is blank.
|
||||
|
||||
|
|
|
|||
|
|
@ -187,3 +187,12 @@ Reference
|
|||
|
||||
# Find all pages that are of type AbstractEmailForm, or a descendant of it
|
||||
form_pages = Page.objects.type(AbstractEmailForm)
|
||||
|
||||
.. automethod:: unpublish
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# Unpublish current_page and all of its children
|
||||
Page.objects.descendant_of(current_page, inclusive=True).unpublish()
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ Bug fixes
|
|||
* The 'wagtail start' command now works on Windows and other environments where the ``django-admin.py`` executable is not readily accessible.
|
||||
* The external image URL generator no longer stores generated images in Django's cache; this was an unintentional side-effect of setting cache control headers.
|
||||
* The Elasticsearch backend can now search querysets that have been filtered with an 'in' clause of a non-list type (such as a ``ValuesListQuerySet``).
|
||||
* Logic around the ``has_unpublished_changes`` flag has been fixed, to prevent issues with the 'View draft' button failing to show in some cases.
|
||||
* It is now easier to move pages to the beginning and end of their section
|
||||
* Image rendering no longer creates erroneous duplicate Rendition records when the focal point is blank.
|
||||
|
||||
|
|
|
|||
|
|
@ -511,6 +511,11 @@ class TestPageEdit(TestCase, WagtailTestUtils):
|
|||
signal_page[0] = instance
|
||||
page_published.connect(page_published_handler)
|
||||
|
||||
# Set has_unpublished_changes=True on the existing record to confirm that the publish action
|
||||
# is resetting it (and not just leaving it alone)
|
||||
self.child_page.has_unpublished_changes = True
|
||||
self.child_page.save()
|
||||
|
||||
# Tests publish from edit page
|
||||
post_data = {
|
||||
'title': "I've been edited!",
|
||||
|
|
@ -559,6 +564,9 @@ class TestPageEdit(TestCase, WagtailTestUtils):
|
|||
# Instead a revision with approved_go_live_at should now exist
|
||||
self.assertTrue(PageRevision.objects.filter(page=child_page_new).exclude(approved_go_live_at__isnull=True).exists())
|
||||
|
||||
# The page SHOULD have the "has_unpublished_changes" flag set, because the changes are not visible as a live page yet
|
||||
self.assertTrue(child_page_new.has_unpublished_changes, "A page scheduled for future publishing should have has_unpublished_changes=True")
|
||||
|
||||
def test_edit_post_publish_now_an_already_scheduled(self):
|
||||
# First let's publish a page with a go_live_at in the future
|
||||
go_live_at = timezone.now() + timedelta(days=1)
|
||||
|
|
@ -968,6 +976,7 @@ class TestPageCopy(TestCase, WagtailTestUtils):
|
|||
title="Hello world!",
|
||||
slug='hello-world',
|
||||
live=True,
|
||||
has_unpublished_changes=False,
|
||||
))
|
||||
|
||||
# Create a couple of child pages
|
||||
|
|
@ -975,12 +984,14 @@ class TestPageCopy(TestCase, WagtailTestUtils):
|
|||
title="Child page",
|
||||
slug='child-page',
|
||||
live=True,
|
||||
has_unpublished_changes=True,
|
||||
))
|
||||
|
||||
self.test_unpublished_child_page = self.test_page.add_child(instance=SimplePage(
|
||||
title="Unpublished Child page",
|
||||
slug='unpublished-child-page',
|
||||
live=False,
|
||||
has_unpublished_changes=True,
|
||||
))
|
||||
|
||||
# Login
|
||||
|
|
@ -1033,6 +1044,7 @@ class TestPageCopy(TestCase, WagtailTestUtils):
|
|||
|
||||
# Check that the copy is not live
|
||||
self.assertFalse(page_copy.live)
|
||||
self.assertTrue(page_copy.has_unpublished_changes)
|
||||
|
||||
# Check that the owner of the page is set correctly
|
||||
self.assertEqual(page_copy.owner, self.user)
|
||||
|
|
@ -1060,6 +1072,7 @@ class TestPageCopy(TestCase, WagtailTestUtils):
|
|||
|
||||
# Check that the copy is not live
|
||||
self.assertFalse(page_copy.live)
|
||||
self.assertTrue(page_copy.has_unpublished_changes)
|
||||
|
||||
# Check that the owner of the page is set correctly
|
||||
self.assertEqual(page_copy.owner, self.user)
|
||||
|
|
@ -1072,10 +1085,12 @@ class TestPageCopy(TestCase, WagtailTestUtils):
|
|||
child_copy = page_copy.get_children().filter(slug='child-page').first()
|
||||
self.assertNotEqual(child_copy, None)
|
||||
self.assertFalse(child_copy.live)
|
||||
self.assertTrue(child_copy.has_unpublished_changes)
|
||||
|
||||
unpublished_child_copy = page_copy.get_children().filter(slug='unpublished-child-page').first()
|
||||
self.assertNotEqual(unpublished_child_copy, None)
|
||||
self.assertFalse(unpublished_child_copy.live)
|
||||
self.assertTrue(unpublished_child_copy.has_unpublished_changes)
|
||||
|
||||
def test_page_copy_post_copy_subpages_publish_copies(self):
|
||||
post_data = {
|
||||
|
|
@ -1097,6 +1112,7 @@ class TestPageCopy(TestCase, WagtailTestUtils):
|
|||
|
||||
# Check that the copy is live
|
||||
self.assertTrue(page_copy.live)
|
||||
self.assertFalse(page_copy.has_unpublished_changes)
|
||||
|
||||
# Check that the owner of the page is set correctly
|
||||
self.assertEqual(page_copy.owner, self.user)
|
||||
|
|
@ -1109,10 +1125,12 @@ class TestPageCopy(TestCase, WagtailTestUtils):
|
|||
child_copy = page_copy.get_children().filter(slug='child-page').first()
|
||||
self.assertNotEqual(child_copy, None)
|
||||
self.assertTrue(child_copy.live)
|
||||
self.assertTrue(child_copy.has_unpublished_changes)
|
||||
|
||||
unpublished_child_copy = page_copy.get_children().filter(slug='unpublished-child-page').first()
|
||||
self.assertNotEqual(unpublished_child_copy, None)
|
||||
self.assertFalse(unpublished_child_copy.live)
|
||||
self.assertTrue(unpublished_child_copy.has_unpublished_changes)
|
||||
|
||||
def test_page_copy_post_existing_slug(self):
|
||||
# This tests the existing slug checking on page copy
|
||||
|
|
@ -1276,9 +1294,7 @@ class TestPageUnpublish(TestCase, WagtailTestUtils):
|
|||
page_unpublished.connect(page_unpublished_handler)
|
||||
|
||||
# Post to the unpublish page
|
||||
response = self.client.post(reverse('wagtailadmin_pages_unpublish', args=(self.page.id, )), {
|
||||
'foo': "Must post something or the view won't see this as a POST request",
|
||||
})
|
||||
response = self.client.post(reverse('wagtailadmin_pages_unpublish', args=(self.page.id, )))
|
||||
|
||||
# Should be redirected to explorer page
|
||||
self.assertRedirects(response, reverse('wagtailadmin_explore', args=(self.root_page.id, )))
|
||||
|
|
@ -1308,6 +1324,7 @@ class TestApproveRejectModeration(TestCase, WagtailTestUtils):
|
|||
title="Hello world!",
|
||||
slug='hello-world',
|
||||
live=False,
|
||||
has_unpublished_changes=True,
|
||||
)
|
||||
root_page.add_child(instance=self.page)
|
||||
|
||||
|
|
@ -1327,29 +1344,45 @@ class TestApproveRejectModeration(TestCase, WagtailTestUtils):
|
|||
page_published.connect(page_published_handler)
|
||||
|
||||
# Post
|
||||
response = self.client.post(reverse('wagtailadmin_pages_approve_moderation', args=(self.revision.id, )), {
|
||||
'foo': "Must post something or the view won't see this as a POST request",
|
||||
})
|
||||
response = self.client.post(reverse('wagtailadmin_pages_approve_moderation', args=(self.revision.id, )))
|
||||
|
||||
# Check that the user was redirected to the dashboard
|
||||
self.assertRedirects(response, reverse('wagtailadmin_home'))
|
||||
|
||||
page = Page.objects.get(id=self.page.id)
|
||||
# Page must be live
|
||||
self.assertTrue(Page.objects.get(id=self.page.id).live)
|
||||
self.assertTrue(page.live, "Approving moderation failed to set live=True")
|
||||
# Page should now have no unpublished changes
|
||||
self.assertFalse(page.has_unpublished_changes, "Approving moderation failed to set has_unpublished_changes=False")
|
||||
|
||||
# Check that the page_published signal was fired
|
||||
self.assertTrue(signal_fired[0])
|
||||
self.assertEqual(signal_page[0], self.page)
|
||||
self.assertEqual(signal_page[0], signal_page[0].specific)
|
||||
|
||||
def test_approve_moderation_when_later_revision_exists(self):
|
||||
self.page.title = "Goodbye world!"
|
||||
self.page.save_revision(user=self.submitter, submitted_for_moderation=False)
|
||||
|
||||
response = self.client.post(reverse('wagtailadmin_pages_approve_moderation', args=(self.revision.id, )))
|
||||
|
||||
# Check that the user was redirected to the dashboard
|
||||
self.assertRedirects(response, reverse('wagtailadmin_home'))
|
||||
|
||||
page = Page.objects.get(id=self.page.id)
|
||||
# Page must be live
|
||||
self.assertTrue(page.live, "Approving moderation failed to set live=True")
|
||||
# Page content should be the submitted version, not the published one
|
||||
self.assertEqual(page.title, "Hello world!")
|
||||
# Page should still have unpublished changes
|
||||
self.assertTrue(page.has_unpublished_changes, "has_unpublished_changes incorrectly cleared on approve_moderation when a later revision exists")
|
||||
|
||||
def test_approve_moderation_view_bad_revision_id(self):
|
||||
"""
|
||||
This tests that the approve moderation view handles invalid revision ids correctly
|
||||
"""
|
||||
# Post
|
||||
response = self.client.post(reverse('wagtailadmin_pages_approve_moderation', args=(12345, )), {
|
||||
'foo': "Must post something or the view won't see this as a POST request",
|
||||
})
|
||||
response = self.client.post(reverse('wagtailadmin_pages_approve_moderation', args=(12345, )))
|
||||
|
||||
# Check that the user recieved a 404 response
|
||||
self.assertEqual(response.status_code, 404)
|
||||
|
|
@ -1366,9 +1399,7 @@ class TestApproveRejectModeration(TestCase, WagtailTestUtils):
|
|||
self.user.save()
|
||||
|
||||
# Post
|
||||
response = self.client.post(reverse('wagtailadmin_pages_approve_moderation', args=(self.revision.id, )), {
|
||||
'foo': "Must post something or the view won't see this as a POST request",
|
||||
})
|
||||
response = self.client.post(reverse('wagtailadmin_pages_approve_moderation', args=(self.revision.id, )))
|
||||
|
||||
# Check that the user recieved a 403 response
|
||||
self.assertEqual(response.status_code, 403)
|
||||
|
|
@ -1378,9 +1409,7 @@ class TestApproveRejectModeration(TestCase, WagtailTestUtils):
|
|||
This posts to the reject moderation view and checks that the page was rejected
|
||||
"""
|
||||
# Post
|
||||
response = self.client.post(reverse('wagtailadmin_pages_reject_moderation', args=(self.revision.id, )), {
|
||||
'foo': "Must post something or the view won't see this as a POST request",
|
||||
})
|
||||
response = self.client.post(reverse('wagtailadmin_pages_reject_moderation', args=(self.revision.id, )))
|
||||
|
||||
# Check that the user was redirected to the dashboard
|
||||
self.assertRedirects(response, reverse('wagtailadmin_home'))
|
||||
|
|
@ -1396,9 +1425,7 @@ class TestApproveRejectModeration(TestCase, WagtailTestUtils):
|
|||
This tests that the reject moderation view handles invalid revision ids correctly
|
||||
"""
|
||||
# Post
|
||||
response = self.client.post(reverse('wagtailadmin_pages_reject_moderation', args=(12345, )), {
|
||||
'foo': "Must post something or the view won't see this as a POST request",
|
||||
})
|
||||
response = self.client.post(reverse('wagtailadmin_pages_reject_moderation', args=(12345, )))
|
||||
|
||||
# Check that the user recieved a 404 response
|
||||
self.assertEqual(response.status_code, 404)
|
||||
|
|
@ -1415,9 +1442,7 @@ class TestApproveRejectModeration(TestCase, WagtailTestUtils):
|
|||
self.user.save()
|
||||
|
||||
# Post
|
||||
response = self.client.post(reverse('wagtailadmin_pages_reject_moderation', args=(self.revision.id, )), {
|
||||
'foo': "Must post something or the view won't see this as a POST request",
|
||||
})
|
||||
response = self.client.post(reverse('wagtailadmin_pages_reject_moderation', args=(self.revision.id, )))
|
||||
|
||||
# Check that the user recieved a 403 response
|
||||
self.assertEqual(response.status_code, 403)
|
||||
|
|
@ -1594,14 +1619,10 @@ class TestNotificationPreferences(TestCase, WagtailTestUtils):
|
|||
self.revision = self.child_page.get_latest_revision()
|
||||
|
||||
def approve(self):
|
||||
return self.client.post(reverse('wagtailadmin_pages_approve_moderation', args=(self.revision.id, )), {
|
||||
'foo': "Must post something or the view won't see this as a POST request",
|
||||
})
|
||||
return self.client.post(reverse('wagtailadmin_pages_approve_moderation', args=(self.revision.id, )))
|
||||
|
||||
def reject(self):
|
||||
return self.client.post(reverse('wagtailadmin_pages_reject_moderation', args=(self.revision.id, )), {
|
||||
'foo': "Must post something or the view won't see this as a POST request",
|
||||
})
|
||||
return self.client.post(reverse('wagtailadmin_pages_reject_moderation', args=(self.revision.id, )))
|
||||
|
||||
def test_vanilla_profile(self):
|
||||
# Check that the vanilla profile has rejected notifications on
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@ from wagtail.wagtailadmin import tasks, signals
|
|||
|
||||
from wagtail.wagtailcore import hooks
|
||||
from wagtail.wagtailcore.models import Page, PageRevision, get_navigation_menu_items
|
||||
from wagtail.wagtailcore.signals import page_published, page_unpublished
|
||||
|
||||
|
||||
@permission_required('wagtailadmin.access_admin')
|
||||
|
|
@ -181,39 +180,31 @@ def create(request, content_type_app_name, content_type_model_name, parent_page_
|
|||
form.clean = clean
|
||||
|
||||
if form.is_valid():
|
||||
page = form.save(commit=False) # don't save yet, as we need treebeard to assign tree params
|
||||
page = form.save(commit=False)
|
||||
|
||||
is_publishing = bool(request.POST.get('action-publish')) and parent_page_perms.can_publish_subpage()
|
||||
is_submitting = bool(request.POST.get('action-submit'))
|
||||
go_live_at = form.cleaned_data.get('go_live_at')
|
||||
future_go_live = go_live_at and go_live_at > timezone.now()
|
||||
approved_go_live_at = None
|
||||
|
||||
if is_publishing:
|
||||
page.has_unpublished_changes = False
|
||||
page.expired = False
|
||||
if future_go_live:
|
||||
page.live = False
|
||||
# Set approved_go_live_at only if is publishing
|
||||
# and the future_go_live is actually in future
|
||||
approved_go_live_at = go_live_at
|
||||
else:
|
||||
page.live = True
|
||||
else:
|
||||
# Set live to False and has_unpublished_changes to True if we are not publishing
|
||||
if not is_publishing:
|
||||
page.live = False
|
||||
page.has_unpublished_changes = True
|
||||
|
||||
parent_page.add_child(instance=page) # assign tree parameters - will cause page to be saved
|
||||
# Save page
|
||||
parent_page.add_child(instance=page)
|
||||
|
||||
# Pass approved_go_live_at to save_revision
|
||||
page.save_revision(
|
||||
# Save revision
|
||||
revision = page.save_revision(
|
||||
user=request.user,
|
||||
submitted_for_moderation=is_submitting,
|
||||
approved_go_live_at=approved_go_live_at
|
||||
)
|
||||
|
||||
# Publish
|
||||
if is_publishing:
|
||||
revision.publish()
|
||||
|
||||
# Notifications
|
||||
if is_publishing:
|
||||
page_published.send(sender=page_class, instance=page)
|
||||
messages.success(request, _("Page '{0}' published.").format(page.title))
|
||||
elif is_submitting:
|
||||
messages.success(request, _("Page '{0}' submitted for moderation.").format(page.title))
|
||||
|
|
@ -298,54 +289,32 @@ def edit(request, page_id):
|
|||
form.clean = clean
|
||||
|
||||
if form.is_valid():
|
||||
page = form.save(commit=False)
|
||||
|
||||
is_publishing = bool(request.POST.get('action-publish')) and page_perms.can_publish()
|
||||
is_submitting = bool(request.POST.get('action-submit'))
|
||||
go_live_at = form.cleaned_data.get('go_live_at')
|
||||
future_go_live = go_live_at and go_live_at > timezone.now()
|
||||
approved_go_live_at = None
|
||||
|
||||
# Save revision
|
||||
revision = page.save_revision(
|
||||
user=request.user,
|
||||
submitted_for_moderation=is_submitting,
|
||||
)
|
||||
|
||||
# Publish
|
||||
if is_publishing:
|
||||
page.has_unpublished_changes = False
|
||||
page.expired = False
|
||||
if future_go_live:
|
||||
page.live = False
|
||||
# Set approved_go_live_at only if publishing
|
||||
approved_go_live_at = go_live_at
|
||||
else:
|
||||
page.live = True
|
||||
|
||||
# We need save the page this way to workaround a bug
|
||||
# in django-modelcluster causing m2m fields to not
|
||||
# be committed to the database. See github issue #192
|
||||
form.save(commit=False)
|
||||
page.save()
|
||||
|
||||
# Clear approved_go_live_at for older revisions
|
||||
page.revisions.update(
|
||||
submitted_for_moderation=False,
|
||||
approved_go_live_at=None,
|
||||
)
|
||||
revision.publish()
|
||||
else:
|
||||
# not publishing the page
|
||||
# Set has_unpublished_changes flag
|
||||
if page.live:
|
||||
# To avoid overwriting the live version, we only save the page
|
||||
# to the revisions table
|
||||
form.save(commit=False)
|
||||
Page.objects.filter(id=page.id).update(has_unpublished_changes=True)
|
||||
else:
|
||||
page.has_unpublished_changes = True
|
||||
form.save(commit=False)
|
||||
page.save()
|
||||
|
||||
|
||||
page.save_revision(
|
||||
user=request.user,
|
||||
submitted_for_moderation=is_submitting,
|
||||
approved_go_live_at=approved_go_live_at
|
||||
)
|
||||
|
||||
# Notifications
|
||||
if is_publishing:
|
||||
page_published.send(sender=page.__class__, instance=page)
|
||||
messages.success(request, _("Page '{0}' published.").format(page.title))
|
||||
elif is_submitting:
|
||||
messages.success(request, _("Page '{0}' submitted for moderation.").format(page.title))
|
||||
|
|
@ -391,19 +360,9 @@ def delete(request, page_id):
|
|||
raise PermissionDenied
|
||||
|
||||
if request.POST:
|
||||
if page.live:
|
||||
# fetch params to pass to the page_unpublished_signal, before the
|
||||
# deletion happens
|
||||
specific_class = page.specific_class
|
||||
specific_page = page.specific
|
||||
|
||||
parent_id = page.get_parent().id
|
||||
page.delete()
|
||||
|
||||
# If the page is live, send the unpublished signal
|
||||
if page.live:
|
||||
page_unpublished.send(sender=specific_class, instance=specific_page)
|
||||
|
||||
messages.success(request, _("Page '{0}' deleted.").format(page.title))
|
||||
|
||||
for fn in hooks.get_hooks('after_delete_page'):
|
||||
|
|
@ -537,19 +496,12 @@ def unpublish(request, page_id):
|
|||
if not page.permissions_for_user(request.user).can_unpublish():
|
||||
raise PermissionDenied
|
||||
|
||||
if request.POST:
|
||||
parent_id = page.get_parent().id
|
||||
page.live = False
|
||||
page.save()
|
||||
|
||||
# Since page is unpublished clear the approved_go_live_at of all revisions
|
||||
page.revisions.update(approved_go_live_at=None)
|
||||
|
||||
page_unpublished.send(sender=page.specific_class, instance=page.specific)
|
||||
if request.method == 'POST':
|
||||
page.unpublish()
|
||||
|
||||
messages.success(request, _("Page '{0}' unpublished.").format(page.title))
|
||||
|
||||
return redirect('wagtailadmin_explore', parent_id)
|
||||
return redirect('wagtailadmin_explore', page.get_parent().id)
|
||||
|
||||
return render(request, 'wagtailadmin/pages/confirm_unpublish.html', {
|
||||
'page': page,
|
||||
|
|
@ -679,7 +631,7 @@ def copy(request, page_id):
|
|||
|
||||
# Unpublish copied pages if we need to
|
||||
if not publish_copies:
|
||||
new_page.get_descendants(inclusive=True).update(live=False)
|
||||
new_page.get_descendants(inclusive=True).unpublish()
|
||||
|
||||
# Assign user of this request as the owner of all the new pages
|
||||
new_page.get_descendants(inclusive=True).update(owner=request.user)
|
||||
|
|
@ -765,9 +717,8 @@ def approve_moderation(request, revision_id):
|
|||
messages.error(request, _("The page '{0}' is not currently awaiting moderation.").format(revision.page.title))
|
||||
return redirect('wagtailadmin_home')
|
||||
|
||||
if request.POST:
|
||||
revision.publish()
|
||||
page_published.send(sender=revision.page.__class__, instance=revision.page.specific)
|
||||
if request.method == 'POST':
|
||||
revision.approve_moderation()
|
||||
messages.success(request, _("Page '{0}' published.").format(revision.page.title))
|
||||
tasks.send_notification.delay(revision.id, 'approved', request.user.id)
|
||||
|
||||
|
|
@ -784,9 +735,8 @@ def reject_moderation(request, revision_id):
|
|||
messages.error(request, _("The page '{0}' is not currently awaiting moderation.").format( revision.page.title))
|
||||
return redirect('wagtailadmin_home')
|
||||
|
||||
if request.POST:
|
||||
revision.submitted_for_moderation = False
|
||||
revision.save(update_fields=['submitted_for_moderation'])
|
||||
if request.method == 'POST':
|
||||
revision.reject_moderation()
|
||||
messages.success(request, _("Page '{0}' rejected for publication.").format(revision.page.title))
|
||||
tasks.send_notification.delay(revision.id, 'rejected', request.user.id)
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ from django.core.management.base import BaseCommand
|
|||
from django.utils import dateparse, timezone
|
||||
|
||||
from wagtail.wagtailcore.models import Page, PageRevision
|
||||
from wagtail.wagtailcore.signals import page_published, page_unpublished
|
||||
|
||||
|
||||
def revision_date_expired(r):
|
||||
|
|
@ -56,15 +55,11 @@ class Command(BaseCommand):
|
|||
else:
|
||||
print("No expired pages to be deactivated found.")
|
||||
else:
|
||||
# need to get the list of expired pages before the update,
|
||||
# so that we can fire the page_unpublished signal on them afterwards
|
||||
expired_pages_list = list(expired_pages)
|
||||
|
||||
expired_pages.update(expired=True, live=False)
|
||||
|
||||
# Fire page_unpublished signal for all expired pages
|
||||
for page in expired_pages_list:
|
||||
page_unpublished.send(sender=page.specific_class, instance=page.specific)
|
||||
# Unpublish the expired pages
|
||||
# Cast to list to make sure the query is fully evaluated
|
||||
# before unpublishing anything
|
||||
for page in list(expired_pages):
|
||||
page.unpublish(set_expired=True)
|
||||
|
||||
# 2. get all page revisions for moderation that have been expired
|
||||
expired_revs = [
|
||||
|
|
@ -118,6 +113,3 @@ class Command(BaseCommand):
|
|||
# just run publish for the revision -- since the approved go
|
||||
# live datetime is before now it will make the page live
|
||||
rp.publish()
|
||||
|
||||
# Fire page_published signal
|
||||
page_published.send(sender=rp.page.specific_class, instance=rp.page.specific)
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@ from modelcluster.models import ClusterableModel, get_all_child_relations
|
|||
|
||||
from django.db import models, connection, transaction
|
||||
from django.db.models import Q
|
||||
from django.db.models.signals import pre_delete
|
||||
from django.dispatch.dispatcher import receiver
|
||||
from django.http import Http404
|
||||
from django.core.cache import cache
|
||||
from django.core.handlers.wsgi import WSGIRequest
|
||||
|
|
@ -29,6 +31,7 @@ from treebeard.mp_tree import MP_Node
|
|||
from wagtail.wagtailcore.utils import camelcase_to_underscore, resolve_model_string
|
||||
from wagtail.wagtailcore.query import PageQuerySet
|
||||
from wagtail.wagtailcore.url_routing import RouteResult
|
||||
from wagtail.wagtailcore.signals import page_published, page_unpublished
|
||||
|
||||
from wagtail.wagtailsearch import index
|
||||
from wagtail.wagtailsearch.backends import get_search_backend
|
||||
|
|
@ -422,6 +425,21 @@ class Page(six.with_metaclass(PageBase, MP_Node, ClusterableModel, index.Indexed
|
|||
else:
|
||||
return self.specific
|
||||
|
||||
def unpublish(self, set_expired=False, commit=True):
|
||||
if self.live:
|
||||
self.live = False
|
||||
self.has_unpublished_changes = True
|
||||
|
||||
if set_expired:
|
||||
self.expired = True
|
||||
|
||||
if commit:
|
||||
self.save()
|
||||
|
||||
page_unpublished.send(sender=self.specific_class, instance=self.specific)
|
||||
|
||||
self.revisions.update(approved_go_live_at=None)
|
||||
|
||||
def get_context(self, request, *args, **kwargs):
|
||||
return {
|
||||
'self': self,
|
||||
|
|
@ -863,6 +881,14 @@ def get_navigation_menu_items():
|
|||
return []
|
||||
|
||||
|
||||
@receiver(pre_delete, sender=Page)
|
||||
def unpublish_page_before_delete(sender, instance, **kwargs):
|
||||
# Make sure pages are unpublished before deleting
|
||||
if instance.live:
|
||||
# Don't bother to save, this page is just about to be deleted!
|
||||
instance.unpublish(commit=False)
|
||||
|
||||
|
||||
class Orderable(models.Model):
|
||||
sort_order = models.IntegerField(null=True, blank=True, editable=False)
|
||||
sort_order_field = 'sort_order'
|
||||
|
|
@ -916,11 +942,29 @@ class PageRevision(models.Model):
|
|||
|
||||
return obj
|
||||
|
||||
def approve_moderation(self):
|
||||
if self.submitted_for_moderation:
|
||||
self.publish()
|
||||
|
||||
def reject_moderation(self):
|
||||
if self.submitted_for_moderation:
|
||||
self.submitted_for_moderation = False
|
||||
self.save(update_fields=['submitted_for_moderation'])
|
||||
|
||||
def is_latest_revision(self):
|
||||
if self.id is None:
|
||||
# special case: a revision without an ID is presumed to be newly-created and is thus
|
||||
# newer than any revision that might exist in the database
|
||||
return True
|
||||
latest_revision = PageRevision.objects.filter(page_id=self.page_id).order_by('-created_at').first()
|
||||
return (latest_revision == self)
|
||||
|
||||
def publish(self):
|
||||
page = self.as_page_object()
|
||||
if page.go_live_at and page.go_live_at > timezone.now():
|
||||
# if we have a go_live in the future don't make the page live
|
||||
page.live = False
|
||||
page.has_unpublished_changes = True
|
||||
# Instead set the approved_go_live_at of this revision
|
||||
self.approved_go_live_at = page.go_live_at
|
||||
self.save()
|
||||
|
|
@ -928,6 +972,8 @@ class PageRevision(models.Model):
|
|||
page.revisions.exclude(id=self.id).update(approved_go_live_at=None)
|
||||
else:
|
||||
page.live = True
|
||||
# at this point, the page has unpublished changes iff there are newer revisions than this one
|
||||
page.has_unpublished_changes = not self.is_latest_revision()
|
||||
# If page goes live clear the approved_go_live_at of all revisions
|
||||
page.revisions.update(approved_go_live_at=None)
|
||||
page.expired = False # When a page is published it can't be expired
|
||||
|
|
@ -935,6 +981,9 @@ class PageRevision(models.Model):
|
|||
self.submitted_for_moderation = False
|
||||
page.revisions.update(submitted_for_moderation=False)
|
||||
|
||||
if page.live:
|
||||
page_published.send(sender=page.specific_class, instance=page.specific)
|
||||
|
||||
def __str__(self):
|
||||
return '"' + unicode(self.page) + '" at ' + unicode(self.created_at)
|
||||
|
||||
|
|
|
|||
|
|
@ -198,3 +198,9 @@ class PageQuerySet(MP_NodeQuerySet):
|
|||
"""
|
||||
search_backend = get_search_backend(backend)
|
||||
return search_backend.search(query_string, self, fields=None)
|
||||
|
||||
def unpublish(self):
|
||||
"""
|
||||
This unpublishes all pages in the queryset
|
||||
"""
|
||||
self.update(live=False, has_unpublished_changes=True)
|
||||
|
|
|
|||
|
|
@ -118,6 +118,7 @@ class TestPublishScheduledPagesCommand(TestCase):
|
|||
title="Hello world!",
|
||||
slug="hello-world",
|
||||
live=False,
|
||||
has_unpublished_changes=True,
|
||||
go_live_at=timezone.now() - timedelta(days=1),
|
||||
)
|
||||
self.root_page.add_child(instance=page)
|
||||
|
|
@ -132,6 +133,7 @@ class TestPublishScheduledPagesCommand(TestCase):
|
|||
|
||||
p = Page.objects.get(slug='hello-world')
|
||||
self.assertTrue(p.live)
|
||||
self.assertFalse(p.has_unpublished_changes)
|
||||
self.assertFalse(PageRevision.objects.filter(page=p).exclude(approved_go_live_at__isnull=True).exists())
|
||||
|
||||
# Check that the page_published signal was fired
|
||||
|
|
@ -139,6 +141,28 @@ class TestPublishScheduledPagesCommand(TestCase):
|
|||
self.assertEqual(signal_page[0], page)
|
||||
self.assertEqual(signal_page[0], signal_page[0].specific)
|
||||
|
||||
def test_go_live_when_newer_revision_exists(self):
|
||||
page = SimplePage(
|
||||
title="Hello world!",
|
||||
slug="hello-world",
|
||||
live=False,
|
||||
has_unpublished_changes=True,
|
||||
go_live_at=timezone.now() - timedelta(days=1),
|
||||
)
|
||||
self.root_page.add_child(instance=page)
|
||||
|
||||
page.save_revision(approved_go_live_at=timezone.now() - timedelta(days=1))
|
||||
|
||||
page.title = "Goodbye world!"
|
||||
page.save_revision(submitted_for_moderation=False)
|
||||
|
||||
management.call_command('publish_scheduled_pages')
|
||||
|
||||
p = Page.objects.get(slug='hello-world')
|
||||
self.assertTrue(p.live)
|
||||
self.assertTrue(p.has_unpublished_changes)
|
||||
self.assertEqual(p.title, "Hello world!")
|
||||
|
||||
def test_future_go_live_page_will_not_be_published(self):
|
||||
page = SimplePage(
|
||||
title="Hello world!",
|
||||
|
|
@ -174,6 +198,7 @@ class TestPublishScheduledPagesCommand(TestCase):
|
|||
title="Hello world!",
|
||||
slug="hello-world",
|
||||
live=True,
|
||||
has_unpublished_changes=False,
|
||||
expire_at=timezone.now() - timedelta(days=1),
|
||||
)
|
||||
self.root_page.add_child(instance=page)
|
||||
|
|
@ -185,6 +210,7 @@ class TestPublishScheduledPagesCommand(TestCase):
|
|||
|
||||
p = Page.objects.get(slug='hello-world')
|
||||
self.assertFalse(p.live)
|
||||
self.assertTrue(p.has_unpublished_changes)
|
||||
self.assertTrue(p.expired)
|
||||
|
||||
# Check that the page_published signal was fired
|
||||
|
|
|
|||
Loading…
Reference in a new issue