diff --git a/wagtail/wagtailadmin/views/pages.py b/wagtail/wagtailadmin/views/pages.py index 53810fa3cc..9e540eae6b 100644 --- a/wagtail/wagtailadmin/views/pages.py +++ b/wagtail/wagtailadmin/views/pages.py @@ -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'): @@ -538,18 +497,11 @@ def unpublish(request, page_id): 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) + 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, @@ -761,8 +713,7 @@ def approve_moderation(request, revision_id): return redirect('wagtailadmin_home') if request.POST: - revision.publish() - page_published.send(sender=revision.page.__class__, instance=revision.page.specific) + revision.approve_moderation() messages.success(request, _("Page '{0}' published.").format(revision.page.title)) tasks.send_notification.delay(revision.id, 'approved', request.user.id) @@ -780,8 +731,7 @@ def reject_moderation(request, revision_id): return redirect('wagtailadmin_home') if request.POST: - revision.submitted_for_moderation = False - revision.save(update_fields=['submitted_for_moderation']) + 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) diff --git a/wagtail/wagtailcore/management/commands/publish_scheduled_pages.py b/wagtail/wagtailcore/management/commands/publish_scheduled_pages.py index d1ec5eecba..4d47d14e02 100644 --- a/wagtail/wagtailcore/management/commands/publish_scheduled_pages.py +++ b/wagtail/wagtailcore/management/commands/publish_scheduled_pages.py @@ -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) diff --git a/wagtail/wagtailcore/models.py b/wagtail/wagtailcore/models.py index 2632a27e80..4ba12caa21 100644 --- a/wagtail/wagtailcore/models.py +++ b/wagtail/wagtailcore/models.py @@ -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,20 @@ 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 + + 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 +880,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,6 +941,15 @@ 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 publish(self): page = self.as_page_object() if page.go_live_at and page.go_live_at > timezone.now(): @@ -935,6 +969,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)