diff --git a/wagtail/wagtailadmin/views/pages.py b/wagtail/wagtailadmin/views/pages.py index 3fb905715..9e71a612f 100644 --- a/wagtail/wagtailadmin/views/pages.py +++ b/wagtail/wagtailadmin/views/pages.py @@ -7,7 +7,7 @@ from django.contrib import messages from django.contrib.contenttypes.models import ContentType from django.contrib.auth.decorators import permission_required from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger -from django.utils.translation import ugettext as _ +from django.utils.translation import ugettext as _ from django.views.decorators.vary import vary_on_headers from wagtail.wagtailadmin.edit_handlers import TabbedInterface, ObjectList @@ -199,7 +199,10 @@ def create(request, content_type_app_name, content_type_model_name, parent_page_ return redirect('wagtailadmin_explore', page.get_parent().id) else: - messages.error(request, _("The page could not be created due to errors.")) + if form.errors and form.errors.get('__all__'): + messages.error(request, _("The page could not be created: ") + ', '.join(form.errors['__all__'])) + else: + messages.error(request, _("The page could not be created due to errors.")) edit_handler = edit_handler_class(instance=page, form=form) else: form = form_class(instance=page) diff --git a/wagtail/wagtailcore/migrations/0003_fields_for_scheduled_publishing.py b/wagtail/wagtailcore/migrations/0003_fields_for_scheduled_publishing.py new file mode 100644 index 000000000..5e682aa59 --- /dev/null +++ b/wagtail/wagtailcore/migrations/0003_fields_for_scheduled_publishing.py @@ -0,0 +1,130 @@ +# -*- coding: utf-8 -*- +from south.utils import datetime_utils as datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Adding field 'PageRevision.approved_go_live_datetime' + db.add_column(u'wagtailcore_pagerevision', 'approved_go_live_datetime', + self.gf('django.db.models.fields.DateTimeField')(null=True, blank=True), + keep_default=False) + + # Adding field 'Page.go_live_datetime' + db.add_column(u'wagtailcore_page', 'go_live_datetime', + self.gf('django.db.models.fields.DateTimeField')(null=True, blank=True), + keep_default=False) + + # Adding field 'Page.expiry_datetime' + db.add_column(u'wagtailcore_page', 'expiry_datetime', + self.gf('django.db.models.fields.DateTimeField')(null=True, blank=True), + keep_default=False) + + # Adding field 'Page.expired' + db.add_column(u'wagtailcore_page', 'expired', + self.gf('django.db.models.fields.BooleanField')(default=False), + keep_default=False) + + + def backwards(self, orm): + # Deleting field 'PageRevision.approved_go_live_datetime' + db.delete_column(u'wagtailcore_pagerevision', 'approved_go_live_datetime') + + # Deleting field 'Page.go_live_datetime' + db.delete_column(u'wagtailcore_page', 'go_live_datetime') + + # Deleting field 'Page.expiry_datetime' + db.delete_column(u'wagtailcore_page', 'expiry_datetime') + + # Deleting field 'Page.expired' + db.delete_column(u'wagtailcore_page', 'expired') + + + models = { + u'auth.group': { + 'Meta': {'object_name': 'Group'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + u'auth.permission': { + 'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + u'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + u'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + u'wagtailcore.grouppagepermission': { + 'Meta': {'object_name': 'GroupPagePermission'}, + 'group': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'page_permissions'", 'to': u"orm['auth.Group']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'page': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'group_permissions'", 'to': u"orm['wagtailcore.Page']"}), + 'permission_type': ('django.db.models.fields.CharField', [], {'max_length': '20'}) + }, + u'wagtailcore.page': { + 'Meta': {'object_name': 'Page'}, + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'pages'", 'to': u"orm['contenttypes.ContentType']"}), + 'depth': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'expired': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'expiry_datetime': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'go_live_datetime': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'has_unpublished_changes': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'live': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'numchild': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'owner': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'owned_pages'", 'null': 'True', 'to': u"orm['auth.User']"}), + 'path': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), + 'search_description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'seo_title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'show_in_menus': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'url_path': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}) + }, + u'wagtailcore.pagerevision': { + 'Meta': {'object_name': 'PageRevision'}, + 'approved_go_live_datetime': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'content_json': ('django.db.models.fields.TextField', [], {}), + 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'page': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'revisions'", 'to': u"orm['wagtailcore.Page']"}), + 'submitted_for_moderation': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'}) + }, + u'wagtailcore.site': { + 'Meta': {'object_name': 'Site'}, + 'hostname': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255', 'db_index': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_default_site': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'port': ('django.db.models.fields.IntegerField', [], {'default': '80'}), + 'root_page': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'sites_rooted_here'", 'to': u"orm['wagtailcore.Page']"}) + } + } + + complete_apps = ['wagtailcore'] \ No newline at end of file diff --git a/wagtail/wagtailcore/models.py b/wagtail/wagtailcore/models.py index d322ab69b..96e670ef9 100644 --- a/wagtail/wagtailcore/models.py +++ b/wagtail/wagtailcore/models.py @@ -7,10 +7,13 @@ from django.db import models, connection, transaction from django.db.models import get_model, Q from django.http import Http404 from django.core.cache import cache +from django.core.exceptions import ValidationError from django.contrib.contenttypes.models import ContentType from django.contrib.auth.models import Group from django.conf import settings from django.template.response import TemplateResponse +from django.utils import timezone +from django.utils.translation import ugettext from django.utils.translation import ugettext_lazy as _ from wagtail.wagtailcore.util import camelcase_to_underscore @@ -234,6 +237,10 @@ class Page(MP_Node, ClusterableModel, Indexed): show_in_menus = models.BooleanField(default=False, help_text=_("Whether a link to this page will appear in automatically generated menus")) search_description = models.TextField(blank=True) + go_live_datetime = models.DateTimeField(verbose_name=_("Go live date/time"), blank=True, null=True) + expiry_datetime = models.DateTimeField(verbose_name=_("Expiry date/time"), blank=True, null=True) + expired = models.BooleanField(default=False, editable=False) + indexed_fields = { 'title': { 'type': 'string', @@ -320,7 +327,7 @@ class Page(MP_Node, ClusterableModel, Indexed): SET url_path = %s || substring(url_path from %s) WHERE path LIKE %s AND id <> %s """ - cursor.execute(update_statement, + cursor.execute(update_statement, [new_url_path, len(old_url_path) + 1, self.path + '%', self.id]) @property @@ -399,8 +406,8 @@ class Page(MP_Node, ClusterableModel, Indexed): def serve(self, request): return TemplateResponse( - request, - self.get_template(request), + request, + self.get_template(request), self.get_context(request) ) @@ -448,6 +455,16 @@ class Page(MP_Node, ClusterableModel, Indexed): if self.url_path.startswith(root_path): return ('' if current_site.id == id else root_url) + self.url_path[len(root_path) - 1:] + def clean(self): + super(Page, self).clean() + + if self.go_live_datetime and self.expiry_datetime: + if self.go_live_datetime > self.expiry_datetime: + raise ValidationError(ugettext('Go live date/time should be before expiry datetime.')) + + if self.expiry_datetime and self.expiry_datetime < timezone.now(): + raise ValidationError(ugettext('Expiry date/time should be in the future')) + @classmethod def search(cls, query_string, show_unpublished=False, search_title_only=False, extra_filters={}, prefetch_related=[], path=None): # Filters @@ -642,6 +659,7 @@ class PageRevision(models.Model): created_at = models.DateTimeField(auto_now_add=True) user = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, blank=True) content_json = models.TextField() + approved_go_live_datetime = models.DateTimeField(null=True, blank=True) objects = models.Manager() submitted_revisions = SubmittedRevisionsManager()