From e4c38302e08e24e4c0abd1f6acb0e33fa5dcb3dd Mon Sep 17 00:00:00 2001 From: Serafeim Papastefanos Date: Tue, 15 Apr 2014 21:55:24 +0300 Subject: [PATCH 001/298] Add fields for scheduled publishing Also add a clean method to Page to check that expiry date is in the future and that go live date is before expiry date. In order to display the correct error message the views/pages.py view has to be changed to display the error message from clean. Finally add the migration for the new fields. --- wagtail/wagtailadmin/views/pages.py | 7 +- .../0003_fields_for_scheduled_publishing.py | 130 ++++++++++++++++++ wagtail/wagtailcore/models.py | 24 +++- 3 files changed, 156 insertions(+), 5 deletions(-) create mode 100644 wagtail/wagtailcore/migrations/0003_fields_for_scheduled_publishing.py 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() From a40c71687de01c6ccf2d8c983a14dc41eec82bea Mon Sep 17 00:00:00 2001 From: Serafeim Papastefanos Date: Wed, 16 Apr 2014 10:27:07 +0300 Subject: [PATCH 002/298] Show clean model errors on edit also --- wagtail/wagtailadmin/views/pages.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/wagtail/wagtailadmin/views/pages.py b/wagtail/wagtailadmin/views/pages.py index 9e71a612f..703d9f8dd 100644 --- a/wagtail/wagtailadmin/views/pages.py +++ b/wagtail/wagtailadmin/views/pages.py @@ -279,7 +279,11 @@ def edit(request, page_id): return redirect('wagtailadmin_explore', page.get_parent().id) else: - messages.error(request, _("The page could not be saved due to validation errors")) + if form.errors and form.errors.get('__all__'): + messages.error(request, _("The page could not be saved: ") + ', '.join(form.errors['__all__'])) + else: + messages.error(request, _("The page could not be saved due to validation errors")) + edit_handler = edit_handler_class(instance=page, form=form) errors_debug = ( repr(edit_handler.form.errors) From 1ebe234a7eebe761fdbf7bbbb1bc32480e840a85 Mon Sep 17 00:00:00 2001 From: Serafeim Papastefanos Date: Wed, 16 Apr 2014 01:05:15 +0300 Subject: [PATCH 003/298] Add go_live_datetime handling on views/models The logic for publishing a page exists in the create and edit views of wagtailadmin.views.pages and in the publish methodd of hte PageRevision model. When a page is created and published (create view), if it has a go_live in the future then it will not be live but the revision that will be created will have the approved_go_live set to the corresponding datetime. If the page is just saved or submitted for moderation the normal flow will be followed. When a page is edited and published (edit view): * The approved_go_live_datetime will be cleared for all older revisions of that page. * If the edit has a go_live in the future then the new revision that will be crated will have the approved_go_live set to that datetime. Also the live attribute of the page will be set to False. If the page is edited and not published the normal flow will be followed. When a submitted for moderation page is published (publish method): * If it has a go_live in the future then the live attribute will be set to False, the approved_go_live_datetime of the revision will be set to the go_live_datetime of the page and the approved_go_live_datetime of all other revisions will be cleared. * If it does not have a go_live in the future then the page will be live and the approved_go_live_dattime of all other revisions will be cleard Finally, if a page is unpublished then then approved_go_live_datetime of all revisions of that page will be cleared. --- wagtail/wagtailadmin/views/pages.py | 44 +++++++++++++++++++++++++---- wagtail/wagtailcore/models.py | 32 ++++++++++++++++++--- 2 files changed, 67 insertions(+), 9 deletions(-) diff --git a/wagtail/wagtailadmin/views/pages.py b/wagtail/wagtailadmin/views/pages.py index 703d9f8dd..8193bdfc4 100644 --- a/wagtail/wagtailadmin/views/pages.py +++ b/wagtail/wagtailadmin/views/pages.py @@ -7,6 +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 import timezone from django.utils.translation import ugettext as _ from django.views.decorators.vary import vary_on_headers @@ -173,16 +174,31 @@ def create(request, content_type_app_name, content_type_model_name, parent_page_ 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_datetime = form.cleaned_data.get('go_live_datetime') + future_go_live = go_live_datetime and go_live_datetime > timezone.now() + approved_go_live_datetime = None - if is_publishing: + if is_publishing and not future_go_live: page.live = True page.has_unpublished_changes = False + elif is_publishing and future_go_live: + page.live = False + # Set approved_go_live_datetime only if is publishing + # and the future_go_live is actually in future + approved_go_live_datetime = go_live_datetime + page.has_unpublished_changes = False else: page.live = False page.has_unpublished_changes = True parent_page.add_child(page) # assign tree parameters - will cause page to be saved - page.save_revision(user=request.user, submitted_for_moderation=is_submitting) + + # Pass approved_go_live_datetime to save_revision + page.save_revision( + user=request.user, + submitted_for_moderation=is_submitting, + approved_go_live_datetime = approved_go_live_datetime + ) if is_publishing: messages.success(request, _("Page '{0}' published.").format(page.title)) @@ -245,12 +261,24 @@ def edit(request, page_id): if form.is_valid(): is_publishing = bool(request.POST.get('action-publish')) and page_perms.can_publish() is_submitting = bool(request.POST.get('action-submit')) + go_live_datetime = form.cleaned_data.get('go_live_datetime') + future_go_live = go_live_datetime and go_live_datetime > timezone.now() + approved_go_live_datetime = None if is_publishing: - page.live = True page.has_unpublished_changes = False + if future_go_live: + page.live = False + # Set approved_go_live_datetime only if publishing + approved_go_live_datetime = go_live_datetime + else: + page.live = True form.save() - page.revisions.update(submitted_for_moderation=False) + # Clear approved_go_live_datetime for older revisions + page.revisions.update( + submitted_for_moderation=False, + approved_go_live_datetime=None, + ) else: # not publishing the page if page.live: @@ -262,7 +290,11 @@ def edit(request, page_id): page.has_unpublished_changes = True form.save() - page.save_revision(user=request.user, submitted_for_moderation=is_submitting) + page.save_revision( + user=request.user, + submitted_for_moderation=is_submitting, + approved_go_live_datetime = approved_go_live_datetime + ) if is_publishing: messages.success(request, _("Page '{0}' published.").format(page.title)) @@ -443,6 +475,8 @@ def unpublish(request, page_id): parent_id = page.get_parent().id page.live = False page.save() + # Since page is unpublished clear the approved_go_live_datetime of all revisions + page.revisions.update(approved_go_live_datetime=None) messages.success(request, _("Page '{0}' unpublished.").format(page.title)) return redirect('wagtailadmin_explore', parent_id) diff --git a/wagtail/wagtailcore/models.py b/wagtail/wagtailcore/models.py index 96e670ef9..1ecdd1eb0 100644 --- a/wagtail/wagtailcore/models.py +++ b/wagtail/wagtailcore/models.py @@ -373,8 +373,13 @@ class Page(MP_Node, ClusterableModel, Indexed): else: raise Http404 - def save_revision(self, user=None, submitted_for_moderation=False): - self.revisions.create(content_json=self.to_json(), user=user, submitted_for_moderation=submitted_for_moderation) + def save_revision(self, user=None, submitted_for_moderation=False, approved_go_live_datetime=None): + self.revisions.create( + content_json=self.to_json(), + user=user, + submitted_for_moderation=submitted_for_moderation, + approved_go_live_datetime=approved_go_live_datetime, + ) def get_latest_revision(self): try: @@ -539,13 +544,20 @@ class Page(MP_Node, ClusterableModel, Indexed): @property def status_string(self): if not self.live: - return "draft" + if self.approved_schedule: + return "scheduled" + else: + return "draft" else: if self.has_unpublished_changes: return "live + draft" else: return "live" + @property + def approved_schedule(self): + return self.revisions.exclude(approved_go_live_datetime__isnull=True).exists() + def has_unpublished_subtree(self): """ An awkwardly-defined flag used in determining whether unprivileged editors have @@ -693,11 +705,23 @@ class PageRevision(models.Model): def publish(self): page = self.as_page_object() - page.live = True + if page.go_live_datetime and page.go_live_datetime > timezone.now(): + # if we have a go_live in the future don't make the page live + page.live = False + # Instead set the approved_go_live_datetime of this revision + self.approved_go_live_datetime = page.go_live_datetime + self.save() + # And clear the the approved_go_live_datetime of any other revisions + page.revisions.exclude(id=self.id).update(approved_go_live_datetime=None) + else: + page.live = True + # If page goes live clear the approved_go_live_datetime of all revisions + page.revisions.update(approved_go_live_datetime=None) page.save() self.submitted_for_moderation = False page.revisions.update(submitted_for_moderation=False) + PAGE_PERMISSION_TYPE_CHOICES = [ ('add', 'Add'), ('edit', 'Edit'), From 89f2d76bfdcd49cb9c132342b83e29fc29a8ac44 Mon Sep 17 00:00:00 2001 From: Serafeim Papastefanos Date: Mon, 21 Apr 2014 18:03:57 +0300 Subject: [PATCH 004/298] Set expired = False when publishing pages both ... in views and models and refactor code a bit. --- wagtail/wagtailadmin/views/pages.py | 18 ++++++++++-------- wagtail/wagtailcore/models.py | 1 + 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/wagtail/wagtailadmin/views/pages.py b/wagtail/wagtailadmin/views/pages.py index 8193bdfc4..7b181b5fd 100644 --- a/wagtail/wagtailadmin/views/pages.py +++ b/wagtail/wagtailadmin/views/pages.py @@ -178,15 +178,16 @@ def create(request, content_type_app_name, content_type_model_name, parent_page_ future_go_live = go_live_datetime and go_live_datetime > timezone.now() approved_go_live_datetime = None - if is_publishing and not future_go_live: - page.live = True - page.has_unpublished_changes = False - elif is_publishing and future_go_live: - page.live = False - # Set approved_go_live_datetime only if is publishing - # and the future_go_live is actually in future - approved_go_live_datetime = go_live_datetime + if is_publishing: page.has_unpublished_changes = False + page.expired = False + if future_go_live: + page.live = False + # Set approved_go_live_datetime only if is publishing + # and the future_go_live is actually in future + approved_go_live_datetime = go_live_datetime + else: + page.live = True else: page.live = False page.has_unpublished_changes = True @@ -267,6 +268,7 @@ def edit(request, page_id): if is_publishing: page.has_unpublished_changes = False + page.expired = False if future_go_live: page.live = False # Set approved_go_live_datetime only if publishing diff --git a/wagtail/wagtailcore/models.py b/wagtail/wagtailcore/models.py index 1ecdd1eb0..eda1a91ec 100644 --- a/wagtail/wagtailcore/models.py +++ b/wagtail/wagtailcore/models.py @@ -717,6 +717,7 @@ class PageRevision(models.Model): page.live = True # If page goes live clear the approved_go_live_datetime of all revisions page.revisions.update(approved_go_live_datetime=None) + page.expired = False # When a page is published it can't be expired page.save() self.submitted_for_moderation = False page.revisions.update(submitted_for_moderation=False) From 792b37d9564e3ae32f0acccda9427740a6b4541f Mon Sep 17 00:00:00 2001 From: Serafeim Papastefanos Date: Mon, 21 Apr 2014 18:08:47 +0300 Subject: [PATCH 005/298] Add "expired" status to pages --- wagtail/wagtailcore/models.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/wagtail/wagtailcore/models.py b/wagtail/wagtailcore/models.py index eda1a91ec..5348acf84 100644 --- a/wagtail/wagtailcore/models.py +++ b/wagtail/wagtailcore/models.py @@ -544,7 +544,9 @@ class Page(MP_Node, ClusterableModel, Indexed): @property def status_string(self): if not self.live: - if self.approved_schedule: + if self.expired: + return "expired" + elif self.approved_schedule: return "scheduled" else: return "draft" From 6839a7474ab32873bfffb2b48237d500538ca7c7 Mon Sep 17 00:00:00 2001 From: Serafeim Papastefanos Date: Mon, 21 Apr 2014 21:48:23 +0300 Subject: [PATCH 006/298] Add management command for scheduled pages The publish_scheduled_pages management command does three actions: * Gets live pages which have an expiry_datetime that has passed and set expired = True and live = False * Gets all revisions on the moderation queue which have an expiry_datetime that has passed and remove them from the moderation queue * Gets all revisions that have an approved_go_live_datetime that has passed. For each one of them the publish() method of the revision is called which will perform the required actions for making live this version of the page. Finally, a dryrun parameter has been added to the management command. If this parameter is used then the pages that pass the tests for each of the above lists will be printed. --- .../commands/publish_scheduled_pages.py | 109 ++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 wagtail/wagtailcore/management/commands/publish_scheduled_pages.py diff --git a/wagtail/wagtailcore/management/commands/publish_scheduled_pages.py b/wagtail/wagtailcore/management/commands/publish_scheduled_pages.py new file mode 100644 index 000000000..0abf3c744 --- /dev/null +++ b/wagtail/wagtailcore/management/commands/publish_scheduled_pages.py @@ -0,0 +1,109 @@ +import datetime +import json +from optparse import make_option + +from django.core.management.base import BaseCommand +from django.utils import dateparse, timezone +from wagtail.wagtailcore.models import Page, PageRevision + + +def revision_date_expired(r): + expiry_str = json.loads(r.content_json).get('expiry_datetime') + if not expiry_str: + return False + expiry_datetime = dateparse.parse_datetime(expiry_str) + if expiry_datetime < timezone.now(): + return True + else: + return False + + +class Command(BaseCommand): + option_list = BaseCommand.option_list + ( + make_option( + '--dryrun', + action='store_true', + dest='dryrun', + default=False, + help='Dry run -- don\'t change anything.'), + ) + + def handle(self, *args, **options): + dryrun = False + if options['dryrun']: + print "Will do a dry run." + dryrun = True + + # 1. get all expired pages with live = True + expired_pages = Page.objects.filter( + live=True, + expiry_datetime__lt=timezone.now() + ) + if dryrun: + if expired_pages: + print "Expired pages to be deactivated:" + print "Expiry datetime\t\tSlug\t\tName" + print "---------------\t\t----\t\t----" + for ep in expired_pages: + print "{0}\t{1}\t{2}".format( + ep.expiry_datetime.strftime("%Y-%m-%d %H:%M"), + ep.slug, + ep.title + ) + else: + print "No expired pages to be deactivated found." + else: + expired_pages.update(expired=True, live=False) + + # 2. get all page revisions for moderation that have been expired + expired_revs = [ + r for r in PageRevision.objects.filter( + submitted_for_moderation=True + ) if revision_date_expired(r) + ] + if dryrun: + print "---------------------------------" + if expired_revs: + print "Expired revisions to be dropped from moderation queue:" + print "Expiry datetime\t\tSlug\t\tName" + print "---------------\t\t----\t\t----" + for er in expired_revs: + rev_data = json.loads(er.content_json) + print "{0}\t{1}\t{2}".format( + dateparse.parse_datetime( + rev_data.get('expiry_datetime') + ).strftime("%Y-%m-%d %H:%M"), + rev_data.get('slug'), + rev_data.get('title') + ) + else: + print "No expired revision to be dropped from moderation." + else: + for er in expired_revs: + er.submitted_for_moderation = False + er.save() + + # 3. get all revisions that need to be published + revs_for_publishing = PageRevision.objects.filter( + approved_go_live_datetime__lt=timezone.now() + ) + if dryrun: + print "---------------------------------" + if revs_for_publishing: + print "Revisions to be published:" + print "Go live datetime\t\tSlug\t\tName" + print "---------------\t\t\t----\t\t----" + for rp in revs_for_publishing: + rev_data = json.loads(rp.content_json) + print "{0}\t\t{1}\t{2}".format( + rp.approved_go_live_datetime.strftime("%Y-%m-%d %H:%M"), + rev_data.get('slug'), + rev_data.get('title') + ) + else: + print "No pages to go live." + else: + for rp in revs_for_publishing: + # just run publish for the revision -- since the approved go + # live datetime is before now it will make the page live + rp.publish() From 686a9beaa9099ac8f4cca20004f35321e6bdd09e Mon Sep 17 00:00:00 2001 From: Serafeim Papastefanos Date: Tue, 22 Apr 2014 20:12:20 +0300 Subject: [PATCH 007/298] Add tests for scheduled publishing This does not actually test the management command for scheduled pages. --- wagtail/wagtailadmin/tests.py | 140 ++++++++++++++++++++++++++++++++-- 1 file changed, 134 insertions(+), 6 deletions(-) diff --git a/wagtail/wagtailadmin/tests.py b/wagtail/wagtailadmin/tests.py index 84b00b335..3df92ec22 100644 --- a/wagtail/wagtailadmin/tests.py +++ b/wagtail/wagtailadmin/tests.py @@ -1,8 +1,10 @@ +from datetime import datetime, timedelta +from django.utils import timezone from django.test import TestCase import unittest from wagtail.tests.models import SimplePage, EventPage from wagtail.tests.utils import login -from wagtail.wagtailcore.models import Page +from wagtail.wagtailcore.models import Page, PageRevision from django.core.urlresolvers import reverse @@ -49,7 +51,7 @@ class TestPageSelectTypeLocation(TestCase): response = self.client.get(reverse('wagtailadmin_pages_select_type')) self.assertEqual(response.status_code, 200) - @unittest.expectedFailure # For some reason, this returns a 302... + @unittest.expectedFailure # For some reason, this returns a 302... def test_select_location_testpage(self): response = self.client.get(reverse('wagtailadmin_pages_select_location', args=('tests', 'eventpage'))) self.assertEqual(response.status_code, 200) @@ -100,6 +102,57 @@ class TestPageCreation(TestCase): self.assertIsInstance(page, SimplePage) self.assertFalse(page.live) + def test_create_simplepage_scheduled(self): + go_live_datetime = timezone.now() + timedelta(days=1) + expiry_datetime = timezone.now() + timedelta(days=2) + post_data = { + 'title': "New page!", + 'content': "Some content", + 'slug': 'hello-world', + 'go_live_datetime': str(go_live_datetime), + 'expiry_datetime': str(expiry_datetime), + } + response = self.client.post(reverse('wagtailadmin_pages_create', args=('tests', 'simplepage', self.root_page.id)), post_data) + + # Should be redirected to explorer page + self.assertEqual(response.status_code, 302) + + # Find the page and check the scheduled times + page = Page.objects.get(path__startswith=self.root_page.path, slug='hello-world').specific + self.assertEquals(page.go_live_datetime.date(), go_live_datetime.date()) + self.assertEquals(page.expiry_datetime.date(), expiry_datetime.date()) + self.assertEquals(page.expired, False) + self.assertTrue(page.status_string, "draft") + + # No revisions with approved_go_live_datetime + self.assertFalse(PageRevision.objects.filter(page=page).exclude(approved_go_live_datetime__isnull=True).exists()) + + def test_create_simplepage_scheduled_errored(self): + post_data = { + 'title': "New page!", + 'content': "Some content", + 'slug': 'hello-world', + 'go_live_datetime': str(timezone.now() + timedelta(days=2)), + 'expiry_datetime': str(timezone.now() + timedelta(days=1)), + } + response = self.client.post(reverse('wagtailadmin_pages_create', args=('tests', 'simplepage', self.root_page.id)), post_data) + + # Should be redirected to explorer page + self.assertEqual(response.status_code, 200) + self.assertTrue(response.context['edit_handler'].form.errors) + + post_data = { + 'title': "New page!", + 'content': "Some content", + 'slug': 'hello-world', + 'expiry_datetime': str(timezone.now() + timedelta(days=-1)), + } + response = self.client.post(reverse('wagtailadmin_pages_create', args=('tests', 'simplepage', self.root_page.id)), post_data) + + # Should be redirected to explorer page + self.assertEqual(response.status_code, 200) + self.assertTrue(response.context['edit_handler'].form.errors) + def test_create_simplepage_post_publish(self): post_data = { 'title': "New page!", @@ -118,6 +171,34 @@ class TestPageCreation(TestCase): self.assertIsInstance(page, SimplePage) self.assertTrue(page.live) + def test_create_simplepage_post_publish_scheduled(self): + go_live_datetime = timezone.now() + timedelta(days=1) + expiry_datetime = timezone.now() + timedelta(days=2) + post_data = { + 'title': "New page!", + 'content': "Some content", + 'slug': 'hello-world', + 'action-publish': "Publish", + 'go_live_datetime': str(go_live_datetime), + 'expiry_datetime': str(expiry_datetime), + } + response = self.client.post(reverse('wagtailadmin_pages_create', args=('tests', 'simplepage', self.root_page.id)), post_data) + + # Should be redirected to explorer page + self.assertEqual(response.status_code, 302) + + # Find the page and check it + page = Page.objects.get(path__startswith=self.root_page.path, slug='hello-world').specific + self.assertEquals(page.go_live_datetime.date(), go_live_datetime.date()) + self.assertEquals(page.expiry_datetime.date(), expiry_datetime.date()) + self.assertEquals(page.expired, False) + + # A revision with approved_go_live_datetime should exist now + self.assertTrue(PageRevision.objects.filter(page=page).exclude(approved_go_live_datetime__isnull=True).exists()) + # But Page won't be live + self.assertFalse(page.live) + self.assertTrue(page.status_string, "scheduled") + def test_create_simplepage_post_existingslug(self): # This tests the existing slug checking on page save @@ -143,7 +224,7 @@ class TestPageCreation(TestCase): response = self.client.get(reverse('wagtailadmin_pages_create', args=('tests', 'simplepage', 100000))) self.assertEqual(response.status_code, 404) - @unittest.expectedFailure # FIXME: Crashes! + @unittest.expectedFailure # FIXME: Crashes! def test_create_nonpagetype(self): response = self.client.get(reverse('wagtailadmin_pages_create', args=('wagtailimages', 'image', self.root_page.id))) self.assertEqual(response.status_code, 404) @@ -184,7 +265,7 @@ class TestPageEdit(TestCase): 'slug': 'hello-world', } response = self.client.post(reverse('wagtailadmin_pages_edit', args=(self.child_page.id, )), post_data) - + # Should be redirected to explorer page self.assertEqual(response.status_code, 302) @@ -192,6 +273,31 @@ class TestPageEdit(TestCase): child_page_new = SimplePage.objects.get(id=self.child_page.id) self.assertTrue(child_page_new.has_unpublished_changes) + def test_edit_post_scheduled(self): + go_live_datetime = timezone.now() + timedelta(days=1) + expiry_datetime = timezone.now() + timedelta(days=2) + post_data = { + 'title': "I've been edited!", + 'content': "Some content", + 'slug': 'hello-world', + 'go_live_datetime': str(go_live_datetime), + 'expiry_datetime': str(expiry_datetime), + } + response = self.client.post(reverse('wagtailadmin_pages_edit', args=(self.child_page.id, )), post_data) + + # Should be redirected to explorer page + self.assertEqual(response.status_code, 302) + + child_page_new = SimplePage.objects.get(id=self.child_page.id) + + # The page will still be live + self.assertTrue(child_page_new.live) + # A revision with approved_go_live_datetime should not exist + self.assertFalse(PageRevision.objects.filter(page=child_page_new).exclude(approved_go_live_datetime__isnull=True).exists()) + # But a revision with go_live_datetime and expiry_datetime in their content json *should* exist + self.assertTrue(PageRevision.objects.filter(page=child_page_new, content_json__contains=str(go_live_datetime.date())).exists()) + self.assertTrue(PageRevision.objects.filter(page=child_page_new, content_json__contains=str(expiry_datetime.date())).exists()) + def test_edit_post_publish(self): # Tests publish from edit page post_data = { @@ -201,7 +307,7 @@ class TestPageEdit(TestCase): 'action-publish': "Publish", } response = self.client.post(reverse('wagtailadmin_pages_edit', args=(self.child_page.id, )), post_data) - + # Should be redirected to explorer page self.assertEqual(response.status_code, 302) @@ -212,6 +318,28 @@ class TestPageEdit(TestCase): # The page shouldn't have "has_unpublished_changes" flag set self.assertFalse(child_page_new.has_unpublished_changes) + def test_edit_post_publish_scheduled(self): + go_live_datetime = timezone.now() + timedelta(days=1) + expiry_datetime = timezone.now() + timedelta(days=2) + post_data = { + 'title': "I've been edited!", + 'content': "Some content", + 'slug': 'hello-world', + 'action-publish': "Publish", + 'go_live_datetime': str(go_live_datetime), + 'expiry_datetime': str(expiry_datetime), + } + response = self.client.post(reverse('wagtailadmin_pages_edit', args=(self.child_page.id, )), post_data) + + # Should be redirected to explorer page + self.assertEqual(response.status_code, 302) + + child_page_new = SimplePage.objects.get(id=self.child_page.id) + # The page should not be live anymore + self.assertFalse(child_page_new.live) + # Instead a revision with approved_go_live_datetime should not exist + self.assertTrue(PageRevision.objects.filter(page=child_page_new).exclude(approved_go_live_datetime__isnull=True).exists()) + class TestPageDelete(TestCase): def setUp(self): @@ -232,7 +360,7 @@ class TestPageDelete(TestCase): self.assertEqual(response.status_code, 200) def test_delete_post(self): - post_data = {'hello': 'world'} # For some reason, this test doesn't work without a bit of POST data + post_data = {'hello': 'world'} # For some reason, this test doesn't work without a bit of POST data response = self.client.post(reverse('wagtailadmin_pages_delete', args=(self.child_page.id, )), post_data) # Should be redirected to explorer page From 65f093061dc53abf24f9bb30b0be1d6160a4c0f6 Mon Sep 17 00:00:00 2001 From: Serafeim Papastefanos Date: Wed, 23 Apr 2014 11:56:13 +0300 Subject: [PATCH 008/298] Use the plain DateTimeInput widget for ... go_live_datetime and expiry_datetime. This should probably be improved in order to use a javascript datetime picker. --- wagtail/wagtailadmin/edit_handlers.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/wagtail/wagtailadmin/edit_handlers.py b/wagtail/wagtailadmin/edit_handlers.py index 245f7ad60..370f2f5bb 100644 --- a/wagtail/wagtailadmin/edit_handlers.py +++ b/wagtail/wagtailadmin/edit_handlers.py @@ -133,15 +133,18 @@ class LocalizedTimeField(forms.CharField): else: raise ValidationError(_("Please type a valid time") ) - +# For some reason we need to explicitly override the DateTimeField and set +# it to use the DateTimeInput or else it will use the LocalizedDateInput/FriendlyDateInput if hasattr(settings, 'USE_L10N') and settings.USE_L10N==True: FORM_FIELD_OVERRIDES = { models.DateField: {'widget': LocalizedDateInput}, + models.DateTimeField: {'widget': forms.DateTimeInput}, models.TimeField: {'widget': LocalizedTimeInput, 'form_class': LocalizedTimeField}, } else: # Fall back to friendly date/time FORM_FIELD_OVERRIDES = { models.DateField: {'widget': FriendlyDateInput}, + models.DateTimeField: {'widget': forms.DateTimeInput}, models.TimeField: {'widget': FriendlyTimeInput, 'form_class': FriendlyTimeField}, } From 9511b21ea73cf86958c8221eec212bd04c18198d Mon Sep 17 00:00:00 2001 From: Serafeim Papastefanos Date: Sun, 25 May 2014 22:07:59 +0300 Subject: [PATCH 009/298] Test publish scheduled pages management command --- wagtail/wagtailadmin/tests.py | 154 +++++++++++++++++++++++++++++++++- 1 file changed, 151 insertions(+), 3 deletions(-) diff --git a/wagtail/wagtailadmin/tests.py b/wagtail/wagtailadmin/tests.py index 3df92ec22..04bf38f76 100644 --- a/wagtail/wagtailadmin/tests.py +++ b/wagtail/wagtailadmin/tests.py @@ -1,11 +1,13 @@ from datetime import datetime, timedelta +from django.core import management +from django.core.urlresolvers import reverse from django.utils import timezone from django.test import TestCase import unittest +from StringIO import StringIO from wagtail.tests.models import SimplePage, EventPage from wagtail.tests.utils import login from wagtail.wagtailcore.models import Page, PageRevision -from django.core.urlresolvers import reverse class TestHome(TestCase): @@ -127,7 +129,7 @@ class TestPageCreation(TestCase): # No revisions with approved_go_live_datetime self.assertFalse(PageRevision.objects.filter(page=page).exclude(approved_go_live_datetime__isnull=True).exists()) - def test_create_simplepage_scheduled_errored(self): + def test_create_simplepage_scheduled_go_live_before_expiry(self): post_data = { 'title': "New page!", 'content': "Some content", @@ -141,6 +143,7 @@ class TestPageCreation(TestCase): self.assertEqual(response.status_code, 200) self.assertTrue(response.context['edit_handler'].form.errors) + def test_create_simplepage_scheduled_expire_in_the_past(self): post_data = { 'title': "New page!", 'content': "Some content", @@ -337,9 +340,51 @@ class TestPageEdit(TestCase): child_page_new = SimplePage.objects.get(id=self.child_page.id) # The page should not be live anymore self.assertFalse(child_page_new.live) - # Instead a revision with approved_go_live_datetime should not exist + # Instead a revision with approved_go_live_datetime should now exist self.assertTrue(PageRevision.objects.filter(page=child_page_new).exclude(approved_go_live_datetime__isnull=True).exists()) + def test_edit_post_publish_now_an_already_scheduled(self): + # First let's publish a page with a go_live_datetime in the future + go_live_datetime = timezone.now() + timedelta(days=1) + expiry_datetime = timezone.now() + timedelta(days=2) + post_data = { + 'title': "I've been edited!", + 'content': "Some content", + 'slug': 'hello-world', + 'action-publish': "Publish", + 'go_live_datetime': str(go_live_datetime), + 'expiry_datetime': str(expiry_datetime), + } + response = self.client.post(reverse('wagtailadmin_pages_edit', args=(self.child_page.id, )), post_data) + + # Should be redirected to explorer page + self.assertEqual(response.status_code, 302) + + child_page_new = SimplePage.objects.get(id=self.child_page.id) + # The page should not be live anymore + self.assertFalse(child_page_new.live) + # Instead a revision with approved_go_live_datetime should now exist + self.assertTrue(PageRevision.objects.filter(page=child_page_new).exclude(approved_go_live_datetime__isnull=True).exists()) + + # Now, let's edit it and publish it right now + go_live_datetime = timezone.now() + post_data = { + 'title': "I've been edited!", + 'content': "Some content", + 'slug': 'hello-world', + 'action-publish': "Publish", + 'go_live_datetime': "", + } + response = self.client.post(reverse('wagtailadmin_pages_edit', args=(self.child_page.id, )), post_data) + + # Should be redirected to explorer page + self.assertEqual(response.status_code, 302) + + child_page_new = SimplePage.objects.get(id=self.child_page.id) + # The page should be live now + self.assertTrue(child_page_new.live) + # And a revision with approved_go_live_datetime should not exist + self.assertFalse(PageRevision.objects.filter(page=child_page_new).exclude(approved_go_live_datetime__isnull=True).exists()) class TestPageDelete(TestCase): def setUp(self): @@ -454,3 +499,106 @@ class TestEditorHooks(TestCase): self.assertEqual(response.status_code, 200) self.assertContains(response, '') self.assertContains(response, '') + +class TestPublishScheduledPages(TestCase): + def setUp(self): + # Find root page + self.root_page = Page.objects.get(id=2) + + def test_go_live_page_will_be_published(self): + page = SimplePage() + page.title = "Hello world!" + page.slug = "hello-world" + page.live = False + page.go_live_datetime = timezone.now() - timedelta(days=1) + self.root_page.add_child(page) + + page.save_revision( + approved_go_live_datetime = timezone.now() - timedelta(days=1) + ) + p = Page.objects.get(slug='hello-world') + self.assertFalse(p.live) + self.assertTrue(PageRevision.objects.filter(page=p).exclude(approved_go_live_datetime__isnull=True).exists()) + management.call_command('publish_scheduled_pages', verbosity=3, interactive=False) + #management.call_command('publish_scheduled_pages', dryrun=True, verbosity=3, interactive=False) + p = Page.objects.get(slug='hello-world') + self.assertTrue(p.live) + self.assertFalse(PageRevision.objects.filter(page=p).exclude(approved_go_live_datetime__isnull=True).exists()) + + def test_go_live_page_will_be_published(self): + page = SimplePage() + page.title = "Hello world!" + page.slug = "hello-world" + page.live = False + page.go_live_datetime = timezone.now() - timedelta(days=1) + self.root_page.add_child(page) + + page.save_revision(approved_go_live_datetime = timezone.now() - timedelta(days=1)) + p = Page.objects.get(slug='hello-world') + self.assertFalse(p.live) + self.assertTrue(PageRevision.objects.filter(page=p).exclude(approved_go_live_datetime__isnull=True).exists()) + management.call_command('publish_scheduled_pages', ) + p = Page.objects.get(slug='hello-world') + self.assertTrue(p.live) + self.assertFalse(PageRevision.objects.filter(page=p).exclude(approved_go_live_datetime__isnull=True).exists()) + + def test_future_go_live_page_will_not_be_published(self): + page = SimplePage() + page.title = "Hello world!" + page.slug = "hello-world" + page.live = False + page.go_live_datetime = timezone.now() + timedelta(days=1) + self.root_page.add_child(page) + page.save_revision(approved_go_live_datetime = timezone.now() - timedelta(days=1)) + p = Page.objects.get(slug='hello-world') + self.assertFalse(p.live) + self.assertTrue(PageRevision.objects.filter(page=p).exclude(approved_go_live_datetime__isnull=True).exists()) + management.call_command('publish_scheduled_pages', ) + p = Page.objects.get(slug='hello-world') + self.assertFalse(p.live) + self.assertTrue(PageRevision.objects.filter(page=p).exclude(approved_go_live_datetime__isnull=True).exists()) + + def test_expired_page_will_be_unpublished(self): + page = SimplePage() + page.title = "Hello world!" + page.slug = "hello-world" + page.live = True + page.expiry_datetime = timezone.now() - timedelta(days=1) + self.root_page.add_child(page) + p = Page.objects.get(slug='hello-world') + self.assertTrue(p.live) + management.call_command('publish_scheduled_pages', ) + p = Page.objects.get(slug='hello-world') + self.assertFalse(p.live) + self.assertTrue(p.expired) + + def test_future_expired_page_will_not_be_unpublished(self): + page = SimplePage() + page.title = "Hello world!" + page.slug = "hello-world" + page.live = True + page.expiry_datetime = timezone.now() + timedelta(days=1) + self.root_page.add_child(page) + p = Page.objects.get(slug='hello-world') + self.assertTrue(p.live) + management.call_command('publish_scheduled_pages', ) + p = Page.objects.get(slug='hello-world') + self.assertTrue(p.live) + self.assertFalse(p.expired) + + def test_expired_pages_are_dropped_from_mod_queue(self): + page = SimplePage() + page.title = "Hello world!" + page.slug = "hello-world" + page.live = False + page.expiry_datetime = timezone.now() - timedelta(days=1) + self.root_page.add_child(page) + page.save_revision(submitted_for_moderation = True) + p = Page.objects.get(slug='hello-world') + self.assertFalse(p.live) + self.assertTrue(PageRevision.objects.filter(page=p, submitted_for_moderation=True).exists()) + management.call_command('publish_scheduled_pages', ) + p = Page.objects.get(slug='hello-world') + self.assertFalse(PageRevision.objects.filter(page=p, submitted_for_moderation=True).exists()) + + From 8dfd0c623f7dfa954e7f5c6307da06213932e4e7 Mon Sep 17 00:00:00 2001 From: Serafeim Papastefanos Date: Thu, 29 May 2014 19:03:19 +0300 Subject: [PATCH 010/298] Add help_text mssage to scheduled publisihing ... datetime fields (go_live_datetime and expiry_datetime) to help users fill them since no datetim control is available. --- wagtail/wagtailcore/models.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/wagtail/wagtailcore/models.py b/wagtail/wagtailcore/models.py index 5bdf1c373..434b174b6 100644 --- a/wagtail/wagtailcore/models.py +++ b/wagtail/wagtailcore/models.py @@ -235,8 +235,8 @@ 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) + go_live_datetime = models.DateTimeField(verbose_name=_("Go live date/time"), help_text=_("Please add a date-time in the form YYYY-MM-DD hh:mm."), blank=True, null=True) + expiry_datetime = models.DateTimeField(verbose_name=_("Expiry date/time"), help_text=_("Please add a date-time in the form YYYY-MM-DD hh:mm."), blank=True, null=True) expired = models.BooleanField(default=False, editable=False) indexed_fields = { From 3fa98ef82ed60a622943e54994b819da21841fee Mon Sep 17 00:00:00 2001 From: Karl Hobley Date: Tue, 3 Jun 2014 10:21:21 +0100 Subject: [PATCH 011/298] Renamed scheduled publishing fields --- wagtail/wagtailadmin/tests.py | 120 +++++++++--------- wagtail/wagtailadmin/views/pages.py | 34 ++--- .../commands/publish_scheduled_pages.py | 16 +-- .../0003_fields_for_scheduled_publishing.py | 30 ++--- wagtail/wagtailcore/models.py | 32 ++--- 5 files changed, 116 insertions(+), 116 deletions(-) diff --git a/wagtail/wagtailadmin/tests.py b/wagtail/wagtailadmin/tests.py index 3bddc864c..4d6016609 100644 --- a/wagtail/wagtailadmin/tests.py +++ b/wagtail/wagtailadmin/tests.py @@ -78,14 +78,14 @@ class TestPageCreation(TestCase): self.assertFalse(page.live) def test_create_simplepage_scheduled(self): - go_live_datetime = timezone.now() + timedelta(days=1) - expiry_datetime = timezone.now() + timedelta(days=2) + go_live_at = timezone.now() + timedelta(days=1) + expire_at = timezone.now() + timedelta(days=2) post_data = { 'title': "New page!", 'content': "Some content", 'slug': 'hello-world', - 'go_live_datetime': str(go_live_datetime).split('.')[0], - 'expiry_datetime': str(expiry_datetime).split('.')[0], + 'go_live_at': str(go_live_at).split('.')[0], + 'expire_at': str(expire_at).split('.')[0], } response = self.client.post(reverse('wagtailadmin_pages_create', args=('tests', 'simplepage', self.root_page.id)), post_data) @@ -94,21 +94,21 @@ class TestPageCreation(TestCase): # Find the page and check the scheduled times page = Page.objects.get(path__startswith=self.root_page.path, slug='hello-world').specific - self.assertEquals(page.go_live_datetime.date(), go_live_datetime.date()) - self.assertEquals(page.expiry_datetime.date(), expiry_datetime.date()) + self.assertEquals(page.go_live_at.date(), go_live_at.date()) + self.assertEquals(page.expire_at.date(), expire_at.date()) self.assertEquals(page.expired, False) self.assertTrue(page.status_string, "draft") - # No revisions with approved_go_live_datetime - self.assertFalse(PageRevision.objects.filter(page=page).exclude(approved_go_live_datetime__isnull=True).exists()) + # No revisions with approved_go_live_at + self.assertFalse(PageRevision.objects.filter(page=page).exclude(approved_go_live_at__isnull=True).exists()) def test_create_simplepage_scheduled_go_live_before_expiry(self): post_data = { 'title': "New page!", 'content': "Some content", 'slug': 'hello-world', - 'go_live_datetime': str(timezone.now() + timedelta(days=2)).split('.')[0], - 'expiry_datetime': str(timezone.now() + timedelta(days=1)).split('.')[0], + 'go_live_at': str(timezone.now() + timedelta(days=2)).split('.')[0], + 'expire_at': str(timezone.now() + timedelta(days=1)).split('.')[0], } response = self.client.post(reverse('wagtailadmin_pages_create', args=('tests', 'simplepage', self.root_page.id)), post_data) @@ -121,7 +121,7 @@ class TestPageCreation(TestCase): 'title': "New page!", 'content': "Some content", 'slug': 'hello-world', - 'expiry_datetime': str(timezone.now() + timedelta(days=-1)).split('.')[0], + 'expire_at': str(timezone.now() + timedelta(days=-1)).split('.')[0], } response = self.client.post(reverse('wagtailadmin_pages_create', args=('tests', 'simplepage', self.root_page.id)), post_data) @@ -148,15 +148,15 @@ class TestPageCreation(TestCase): self.assertTrue(page.live) def test_create_simplepage_post_publish_scheduled(self): - go_live_datetime = timezone.now() + timedelta(days=1) - expiry_datetime = timezone.now() + timedelta(days=2) + go_live_at = timezone.now() + timedelta(days=1) + expire_at = timezone.now() + timedelta(days=2) post_data = { 'title': "New page!", 'content': "Some content", 'slug': 'hello-world', 'action-publish': "Publish", - 'go_live_datetime': str(go_live_datetime).split('.')[0], - 'expiry_datetime': str(expiry_datetime).split('.')[0], + 'go_live_at': str(go_live_at).split('.')[0], + 'expire_at': str(expire_at).split('.')[0], } response = self.client.post(reverse('wagtailadmin_pages_create', args=('tests', 'simplepage', self.root_page.id)), post_data) @@ -165,12 +165,12 @@ class TestPageCreation(TestCase): # Find the page and check it page = Page.objects.get(path__startswith=self.root_page.path, slug='hello-world').specific - self.assertEquals(page.go_live_datetime.date(), go_live_datetime.date()) - self.assertEquals(page.expiry_datetime.date(), expiry_datetime.date()) + self.assertEquals(page.go_live_at.date(), go_live_at.date()) + self.assertEquals(page.expire_at.date(), expire_at.date()) self.assertEquals(page.expired, False) - # A revision with approved_go_live_datetime should exist now - self.assertTrue(PageRevision.objects.filter(page=page).exclude(approved_go_live_datetime__isnull=True).exists()) + # A revision with approved_go_live_at should exist now + self.assertTrue(PageRevision.objects.filter(page=page).exclude(approved_go_live_at__isnull=True).exists()) # But Page won't be live self.assertFalse(page.live) self.assertTrue(page.status_string, "scheduled") @@ -250,14 +250,14 @@ class TestPageEdit(TestCase): self.assertTrue(child_page_new.has_unpublished_changes) def test_edit_post_scheduled(self): - go_live_datetime = timezone.now() + timedelta(days=1) - expiry_datetime = timezone.now() + timedelta(days=2) + go_live_at = timezone.now() + timedelta(days=1) + expire_at = timezone.now() + timedelta(days=2) post_data = { 'title': "I've been edited!", 'content': "Some content", 'slug': 'hello-world', - 'go_live_datetime': str(go_live_datetime).split('.')[0], - 'expiry_datetime': str(expiry_datetime).split('.')[0], + 'go_live_at': str(go_live_at).split('.')[0], + 'expire_at': str(expire_at).split('.')[0], } response = self.client.post(reverse('wagtailadmin_pages_edit', args=(self.child_page.id, )), post_data) @@ -268,11 +268,11 @@ class TestPageEdit(TestCase): # The page will still be live self.assertTrue(child_page_new.live) - # A revision with approved_go_live_datetime should not exist - self.assertFalse(PageRevision.objects.filter(page=child_page_new).exclude(approved_go_live_datetime__isnull=True).exists()) - # But a revision with go_live_datetime and expiry_datetime in their content json *should* exist - self.assertTrue(PageRevision.objects.filter(page=child_page_new, content_json__contains=str(go_live_datetime.date())).exists()) - self.assertTrue(PageRevision.objects.filter(page=child_page_new, content_json__contains=str(expiry_datetime.date())).exists()) + # A revision with approved_go_live_at should not exist + self.assertFalse(PageRevision.objects.filter(page=child_page_new).exclude(approved_go_live_at__isnull=True).exists()) + # But a revision with go_live_at and expire_at in their content json *should* exist + self.assertTrue(PageRevision.objects.filter(page=child_page_new, content_json__contains=str(go_live_at.date())).exists()) + self.assertTrue(PageRevision.objects.filter(page=child_page_new, content_json__contains=str(expire_at.date())).exists()) def test_edit_post_publish(self): # Tests publish from edit page @@ -295,15 +295,15 @@ class TestPageEdit(TestCase): self.assertFalse(child_page_new.has_unpublished_changes) def test_edit_post_publish_scheduled(self): - go_live_datetime = timezone.now() + timedelta(days=1) - expiry_datetime = timezone.now() + timedelta(days=2) + go_live_at = timezone.now() + timedelta(days=1) + expire_at = timezone.now() + timedelta(days=2) post_data = { 'title': "I've been edited!", 'content': "Some content", 'slug': 'hello-world', 'action-publish': "Publish", - 'go_live_datetime': str(go_live_datetime).split('.')[0], - 'expiry_datetime': str(expiry_datetime).split('.')[0], + 'go_live_at': str(go_live_at).split('.')[0], + 'expire_at': str(expire_at).split('.')[0], } response = self.client.post(reverse('wagtailadmin_pages_edit', args=(self.child_page.id, )), post_data) @@ -313,20 +313,20 @@ class TestPageEdit(TestCase): child_page_new = SimplePage.objects.get(id=self.child_page.id) # The page should not be live anymore self.assertFalse(child_page_new.live) - # Instead a revision with approved_go_live_datetime should now exist - self.assertTrue(PageRevision.objects.filter(page=child_page_new).exclude(approved_go_live_datetime__isnull=True).exists()) + # 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()) def test_edit_post_publish_now_an_already_scheduled(self): - # First let's publish a page with a go_live_datetime in the future - go_live_datetime = timezone.now() + timedelta(days=1) - expiry_datetime = timezone.now() + timedelta(days=2) + # First let's publish a page with a go_live_at in the future + go_live_at = timezone.now() + timedelta(days=1) + expire_at = timezone.now() + timedelta(days=2) post_data = { 'title': "I've been edited!", 'content': "Some content", 'slug': 'hello-world', 'action-publish': "Publish", - 'go_live_datetime': str(go_live_datetime).split('.')[0], - 'expiry_datetime': str(expiry_datetime).split('.')[0], + 'go_live_at': str(go_live_at).split('.')[0], + 'expire_at': str(expire_at).split('.')[0], } response = self.client.post(reverse('wagtailadmin_pages_edit', args=(self.child_page.id, )), post_data) @@ -336,17 +336,17 @@ class TestPageEdit(TestCase): child_page_new = SimplePage.objects.get(id=self.child_page.id) # The page should not be live anymore self.assertFalse(child_page_new.live) - # Instead a revision with approved_go_live_datetime should now exist - self.assertTrue(PageRevision.objects.filter(page=child_page_new).exclude(approved_go_live_datetime__isnull=True).exists()) + # 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()) # Now, let's edit it and publish it right now - go_live_datetime = timezone.now() + go_live_at = timezone.now() post_data = { 'title': "I've been edited!", 'content': "Some content", 'slug': 'hello-world', 'action-publish': "Publish", - 'go_live_datetime': "", + 'go_live_at': "", } response = self.client.post(reverse('wagtailadmin_pages_edit', args=(self.child_page.id, )), post_data) @@ -356,8 +356,8 @@ class TestPageEdit(TestCase): child_page_new = SimplePage.objects.get(id=self.child_page.id) # The page should be live now self.assertTrue(child_page_new.live) - # And a revision with approved_go_live_datetime should not exist - self.assertFalse(PageRevision.objects.filter(page=child_page_new).exclude(approved_go_live_datetime__isnull=True).exists()) + # And a revision with approved_go_live_at should not exist + self.assertFalse(PageRevision.objects.filter(page=child_page_new).exclude(approved_go_live_at__isnull=True).exists()) class TestPageDelete(TestCase): def setUp(self): @@ -483,60 +483,60 @@ class TestPublishScheduledPages(TestCase): page.title = "Hello world!" page.slug = "hello-world" page.live = False - page.go_live_datetime = timezone.now() - timedelta(days=1) + page.go_live_at = timezone.now() - timedelta(days=1) self.root_page.add_child(instance=page) page.save_revision( - approved_go_live_datetime = timezone.now() - timedelta(days=1) + approved_go_live_at = timezone.now() - timedelta(days=1) ) p = Page.objects.get(slug='hello-world') self.assertFalse(p.live) - self.assertTrue(PageRevision.objects.filter(page=p).exclude(approved_go_live_datetime__isnull=True).exists()) + self.assertTrue(PageRevision.objects.filter(page=p).exclude(approved_go_live_at__isnull=True).exists()) management.call_command('publish_scheduled_pages', verbosity=3, interactive=False) #management.call_command('publish_scheduled_pages', dryrun=True, verbosity=3, interactive=False) p = Page.objects.get(slug='hello-world') self.assertTrue(p.live) - self.assertFalse(PageRevision.objects.filter(page=p).exclude(approved_go_live_datetime__isnull=True).exists()) + self.assertFalse(PageRevision.objects.filter(page=p).exclude(approved_go_live_at__isnull=True).exists()) def test_go_live_page_will_be_published(self): page = SimplePage() page.title = "Hello world!" page.slug = "hello-world" page.live = False - page.go_live_datetime = timezone.now() - timedelta(days=1) + page.go_live_at = timezone.now() - timedelta(days=1) self.root_page.add_child(instance=page) - page.save_revision(approved_go_live_datetime = timezone.now() - timedelta(days=1)) + page.save_revision(approved_go_live_at = timezone.now() - timedelta(days=1)) p = Page.objects.get(slug='hello-world') self.assertFalse(p.live) - self.assertTrue(PageRevision.objects.filter(page=p).exclude(approved_go_live_datetime__isnull=True).exists()) + self.assertTrue(PageRevision.objects.filter(page=p).exclude(approved_go_live_at__isnull=True).exists()) management.call_command('publish_scheduled_pages', ) p = Page.objects.get(slug='hello-world') self.assertTrue(p.live) - self.assertFalse(PageRevision.objects.filter(page=p).exclude(approved_go_live_datetime__isnull=True).exists()) + self.assertFalse(PageRevision.objects.filter(page=p).exclude(approved_go_live_at__isnull=True).exists()) def test_future_go_live_page_will_not_be_published(self): page = SimplePage() page.title = "Hello world!" page.slug = "hello-world" page.live = False - page.go_live_datetime = timezone.now() + timedelta(days=1) + page.go_live_at = timezone.now() + timedelta(days=1) self.root_page.add_child(instance=page) - page.save_revision(approved_go_live_datetime = timezone.now() - timedelta(days=1)) + page.save_revision(approved_go_live_at = timezone.now() - timedelta(days=1)) p = Page.objects.get(slug='hello-world') self.assertFalse(p.live) - self.assertTrue(PageRevision.objects.filter(page=p).exclude(approved_go_live_datetime__isnull=True).exists()) + self.assertTrue(PageRevision.objects.filter(page=p).exclude(approved_go_live_at__isnull=True).exists()) management.call_command('publish_scheduled_pages', ) p = Page.objects.get(slug='hello-world') self.assertFalse(p.live) - self.assertTrue(PageRevision.objects.filter(page=p).exclude(approved_go_live_datetime__isnull=True).exists()) + self.assertTrue(PageRevision.objects.filter(page=p).exclude(approved_go_live_at__isnull=True).exists()) def test_expired_page_will_be_unpublished(self): page = SimplePage() page.title = "Hello world!" page.slug = "hello-world" page.live = True - page.expiry_datetime = timezone.now() - timedelta(days=1) + page.expire_at = timezone.now() - timedelta(days=1) self.root_page.add_child(instance=page) p = Page.objects.get(slug='hello-world') self.assertTrue(p.live) @@ -550,7 +550,7 @@ class TestPublishScheduledPages(TestCase): page.title = "Hello world!" page.slug = "hello-world" page.live = True - page.expiry_datetime = timezone.now() + timedelta(days=1) + page.expire_at = timezone.now() + timedelta(days=1) self.root_page.add_child(instance=page) p = Page.objects.get(slug='hello-world') self.assertTrue(p.live) @@ -564,7 +564,7 @@ class TestPublishScheduledPages(TestCase): page.title = "Hello world!" page.slug = "hello-world" page.live = False - page.expiry_datetime = timezone.now() - timedelta(days=1) + page.expire_at = timezone.now() - timedelta(days=1) self.root_page.add_child(instance=page) page.save_revision(submitted_for_moderation = True) p = Page.objects.get(slug='hello-world') diff --git a/wagtail/wagtailadmin/views/pages.py b/wagtail/wagtailadmin/views/pages.py index 26ab715ec..f4ea960ff 100644 --- a/wagtail/wagtailadmin/views/pages.py +++ b/wagtail/wagtailadmin/views/pages.py @@ -133,18 +133,18 @@ def create(request, content_type_app_name, content_type_model_name, parent_page_ 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_datetime = form.cleaned_data.get('go_live_datetime') - future_go_live = go_live_datetime and go_live_datetime > timezone.now() - approved_go_live_datetime = None + 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_datetime only if is publishing + # Set approved_go_live_at only if is publishing # and the future_go_live is actually in future - approved_go_live_datetime = go_live_datetime + approved_go_live_at = go_live_at else: page.live = True else: @@ -153,11 +153,11 @@ def create(request, content_type_app_name, content_type_model_name, parent_page_ parent_page.add_child(instance=page) # assign tree parameters - will cause page to be saved - # Pass approved_go_live_datetime to save_revision + # Pass approved_go_live_at to save_revision page.save_revision( user=request.user, submitted_for_moderation=is_submitting, - approved_go_live_datetime = approved_go_live_datetime + approved_go_live_at = approved_go_live_at ) if is_publishing: @@ -222,24 +222,24 @@ def edit(request, page_id): if form.is_valid(): is_publishing = bool(request.POST.get('action-publish')) and page_perms.can_publish() is_submitting = bool(request.POST.get('action-submit')) - go_live_datetime = form.cleaned_data.get('go_live_datetime') - future_go_live = go_live_datetime and go_live_datetime > timezone.now() - approved_go_live_datetime = None + 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_datetime only if publishing - approved_go_live_datetime = go_live_datetime + # Set approved_go_live_at only if publishing + approved_go_live_at = go_live_at else: page.live = True form.save() - # Clear approved_go_live_datetime for older revisions + # Clear approved_go_live_at for older revisions page.revisions.update( submitted_for_moderation=False, - approved_go_live_datetime=None, + approved_go_live_at=None, ) else: # not publishing the page @@ -255,7 +255,7 @@ def edit(request, page_id): page.save_revision( user=request.user, submitted_for_moderation=is_submitting, - approved_go_live_datetime = approved_go_live_datetime + approved_go_live_at = approved_go_live_at ) if is_publishing: @@ -455,8 +455,8 @@ def unpublish(request, page_id): parent_id = page.get_parent().id page.live = False page.save() - # Since page is unpublished clear the approved_go_live_datetime of all revisions - page.revisions.update(approved_go_live_datetime=None) + # Since page is unpublished clear the approved_go_live_at of all revisions + page.revisions.update(approved_go_live_at=None) messages.success(request, _("Page '{0}' unpublished.").format(page.title)) return redirect('wagtailadmin_explore', parent_id) diff --git a/wagtail/wagtailcore/management/commands/publish_scheduled_pages.py b/wagtail/wagtailcore/management/commands/publish_scheduled_pages.py index 0abf3c744..b80ace728 100644 --- a/wagtail/wagtailcore/management/commands/publish_scheduled_pages.py +++ b/wagtail/wagtailcore/management/commands/publish_scheduled_pages.py @@ -8,11 +8,11 @@ from wagtail.wagtailcore.models import Page, PageRevision def revision_date_expired(r): - expiry_str = json.loads(r.content_json).get('expiry_datetime') + expiry_str = json.loads(r.content_json).get('expire_at') if not expiry_str: return False - expiry_datetime = dateparse.parse_datetime(expiry_str) - if expiry_datetime < timezone.now(): + expire_at = dateparse.parse_datetime(expiry_str) + if expire_at < timezone.now(): return True else: return False @@ -37,7 +37,7 @@ class Command(BaseCommand): # 1. get all expired pages with live = True expired_pages = Page.objects.filter( live=True, - expiry_datetime__lt=timezone.now() + expire_at__lt=timezone.now() ) if dryrun: if expired_pages: @@ -46,7 +46,7 @@ class Command(BaseCommand): print "---------------\t\t----\t\t----" for ep in expired_pages: print "{0}\t{1}\t{2}".format( - ep.expiry_datetime.strftime("%Y-%m-%d %H:%M"), + ep.expire_at.strftime("%Y-%m-%d %H:%M"), ep.slug, ep.title ) @@ -71,7 +71,7 @@ class Command(BaseCommand): rev_data = json.loads(er.content_json) print "{0}\t{1}\t{2}".format( dateparse.parse_datetime( - rev_data.get('expiry_datetime') + rev_data.get('expire_at') ).strftime("%Y-%m-%d %H:%M"), rev_data.get('slug'), rev_data.get('title') @@ -85,7 +85,7 @@ class Command(BaseCommand): # 3. get all revisions that need to be published revs_for_publishing = PageRevision.objects.filter( - approved_go_live_datetime__lt=timezone.now() + approved_go_live_at__lt=timezone.now() ) if dryrun: print "---------------------------------" @@ -96,7 +96,7 @@ class Command(BaseCommand): for rp in revs_for_publishing: rev_data = json.loads(rp.content_json) print "{0}\t\t{1}\t{2}".format( - rp.approved_go_live_datetime.strftime("%Y-%m-%d %H:%M"), + rp.approved_go_live_at.strftime("%Y-%m-%d %H:%M"), rev_data.get('slug'), rev_data.get('title') ) diff --git a/wagtail/wagtailcore/migrations/0003_fields_for_scheduled_publishing.py b/wagtail/wagtailcore/migrations/0003_fields_for_scheduled_publishing.py index 5e682aa59..639068c2b 100644 --- a/wagtail/wagtailcore/migrations/0003_fields_for_scheduled_publishing.py +++ b/wagtail/wagtailcore/migrations/0003_fields_for_scheduled_publishing.py @@ -8,18 +8,18 @@ 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', + # Adding field 'PageRevision.approved_go_live_at' + db.add_column(u'wagtailcore_pagerevision', 'approved_go_live_at', 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', + # Adding field 'Page.go_live_at' + db.add_column(u'wagtailcore_page', 'go_live_at', 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', + # Adding field 'Page.expire_at' + db.add_column(u'wagtailcore_page', 'expire_at', self.gf('django.db.models.fields.DateTimeField')(null=True, blank=True), keep_default=False) @@ -30,14 +30,14 @@ class Migration(SchemaMigration): def backwards(self, orm): - # Deleting field 'PageRevision.approved_go_live_datetime' - db.delete_column(u'wagtailcore_pagerevision', 'approved_go_live_datetime') + # Deleting field 'PageRevision.approved_go_live_at' + db.delete_column(u'wagtailcore_pagerevision', 'approved_go_live_at') - # Deleting field 'Page.go_live_datetime' - db.delete_column(u'wagtailcore_page', 'go_live_datetime') + # Deleting field 'Page.go_live_at' + db.delete_column(u'wagtailcore_page', 'go_live_at') - # Deleting field 'Page.expiry_datetime' - db.delete_column(u'wagtailcore_page', 'expiry_datetime') + # Deleting field 'Page.expire_at' + db.delete_column(u'wagtailcore_page', 'expire_at') # Deleting field 'Page.expired' db.delete_column(u'wagtailcore_page', 'expired') @@ -92,8 +92,8 @@ class Migration(SchemaMigration): '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'}), + 'expire_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'go_live_at': ('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'}), @@ -109,7 +109,7 @@ class Migration(SchemaMigration): }, u'wagtailcore.pagerevision': { 'Meta': {'object_name': 'PageRevision'}, - 'approved_go_live_datetime': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'approved_go_live_at': ('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'}), diff --git a/wagtail/wagtailcore/models.py b/wagtail/wagtailcore/models.py index 434b174b6..3b2b055bd 100644 --- a/wagtail/wagtailcore/models.py +++ b/wagtail/wagtailcore/models.py @@ -235,8 +235,8 @@ 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"), help_text=_("Please add a date-time in the form YYYY-MM-DD hh:mm."), blank=True, null=True) - expiry_datetime = models.DateTimeField(verbose_name=_("Expiry date/time"), help_text=_("Please add a date-time in the form YYYY-MM-DD hh:mm."), blank=True, null=True) + go_live_at = models.DateTimeField(verbose_name=_("Go live date/time"), help_text=_("Please add a date-time in the form YYYY-MM-DD hh:mm."), blank=True, null=True) + expire_at = models.DateTimeField(verbose_name=_("Expiry date/time"), help_text=_("Please add a date-time in the form YYYY-MM-DD hh:mm."), blank=True, null=True) expired = models.BooleanField(default=False, editable=False) indexed_fields = { @@ -382,12 +382,12 @@ class Page(MP_Node, ClusterableModel, Indexed): else: raise Http404 - def save_revision(self, user=None, submitted_for_moderation=False, approved_go_live_datetime=None): + def save_revision(self, user=None, submitted_for_moderation=False, approved_go_live_at=None): self.revisions.create( content_json=self.to_json(), user=user, submitted_for_moderation=submitted_for_moderation, - approved_go_live_datetime=approved_go_live_datetime, + approved_go_live_at=approved_go_live_at, ) def get_latest_revision(self): @@ -472,11 +472,11 @@ class Page(MP_Node, ClusterableModel, Indexed): def clean(self): super(Page, self).clean() - if self.go_live_datetime and self.expiry_datetime: - if self.go_live_datetime > self.expiry_datetime: + if self.go_live_at and self.expire_at: + if self.go_live_at > self.expire_at: raise ValidationError(ugettext('Go live date/time should be before expiry datetime.')) - if self.expiry_datetime and self.expiry_datetime < timezone.now(): + if self.expire_at and self.expire_at < timezone.now(): raise ValidationError(ugettext('Expiry date/time should be in the future')) @classmethod @@ -567,7 +567,7 @@ class Page(MP_Node, ClusterableModel, Indexed): @property def approved_schedule(self): - return self.revisions.exclude(approved_go_live_datetime__isnull=True).exists() + return self.revisions.exclude(approved_go_live_at__isnull=True).exists() def has_unpublished_subtree(self): """ @@ -742,7 +742,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) + approved_go_live_at = models.DateTimeField(null=True, blank=True) objects = models.Manager() submitted_revisions = SubmittedRevisionsManager() @@ -776,18 +776,18 @@ class PageRevision(models.Model): def publish(self): page = self.as_page_object() - if page.go_live_datetime and page.go_live_datetime > timezone.now(): + 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 - # Instead set the approved_go_live_datetime of this revision - self.approved_go_live_datetime = page.go_live_datetime + # Instead set the approved_go_live_at of this revision + self.approved_go_live_at = page.go_live_at self.save() - # And clear the the approved_go_live_datetime of any other revisions - page.revisions.exclude(id=self.id).update(approved_go_live_datetime=None) + # And clear the the approved_go_live_at of any other revisions + page.revisions.exclude(id=self.id).update(approved_go_live_at=None) else: page.live = True - # If page goes live clear the approved_go_live_datetime of all revisions - page.revisions.update(approved_go_live_datetime=None) + # 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 page.save() self.submitted_for_moderation = False From ad3c62fed520b086ffd6ccf97779bd29af0cbff2 Mon Sep 17 00:00:00 2001 From: Karl Hobley Date: Tue, 3 Jun 2014 10:22:14 +0100 Subject: [PATCH 012/298] Added go_live_at and expire_at to common page configuration --- wagtail/wagtailadmin/edit_handlers.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/wagtail/wagtailadmin/edit_handlers.py b/wagtail/wagtailadmin/edit_handlers.py index 370f2f5bb..097b5bb21 100644 --- a/wagtail/wagtailadmin/edit_handlers.py +++ b/wagtail/wagtailadmin/edit_handlers.py @@ -731,5 +731,7 @@ Page.promote_panels = [ FieldPanel('seo_title'), FieldPanel('show_in_menus'), FieldPanel('search_description'), + FieldPanel('go_live_at'), + FieldPanel('expire_at'), ], ugettext_lazy('Common page configuration')), ] From 97ab613f88ab742c57400f0985d73b62be341635 Mon Sep 17 00:00:00 2001 From: Karl Hobley Date: Tue, 3 Jun 2014 10:28:00 +0100 Subject: [PATCH 013/298] Moved tests for publish pages command into wagtailcore --- wagtail/wagtailadmin/tests.py | 107 +-------------------------------- wagtail/wagtailcore/tests.py | 109 +++++++++++++++++++++++++++++++++- 2 files changed, 108 insertions(+), 108 deletions(-) diff --git a/wagtail/wagtailadmin/tests.py b/wagtail/wagtailadmin/tests.py index 4d6016609..c657c1efb 100644 --- a/wagtail/wagtailadmin/tests.py +++ b/wagtail/wagtailadmin/tests.py @@ -1,12 +1,10 @@ -from datetime import datetime, timedelta -from django.core import management +from datetime import timedelta from django.core.urlresolvers import reverse from django.utils import timezone from django.test import TestCase from wagtail.tests.models import SimplePage, EventPage from wagtail.tests.utils import login, unittest from wagtail.wagtailcore.models import Page, PageRevision -from django.core.urlresolvers import reverse class TestHome(TestCase): @@ -472,106 +470,3 @@ class TestEditorHooks(TestCase): self.assertEqual(response.status_code, 200) self.assertContains(response, '') self.assertContains(response, '') - -class TestPublishScheduledPages(TestCase): - def setUp(self): - # Find root page - self.root_page = Page.objects.get(id=2) - - def test_go_live_page_will_be_published(self): - page = SimplePage() - page.title = "Hello world!" - page.slug = "hello-world" - page.live = False - page.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) - ) - p = Page.objects.get(slug='hello-world') - self.assertFalse(p.live) - self.assertTrue(PageRevision.objects.filter(page=p).exclude(approved_go_live_at__isnull=True).exists()) - management.call_command('publish_scheduled_pages', verbosity=3, interactive=False) - #management.call_command('publish_scheduled_pages', dryrun=True, verbosity=3, interactive=False) - p = Page.objects.get(slug='hello-world') - self.assertTrue(p.live) - self.assertFalse(PageRevision.objects.filter(page=p).exclude(approved_go_live_at__isnull=True).exists()) - - def test_go_live_page_will_be_published(self): - page = SimplePage() - page.title = "Hello world!" - page.slug = "hello-world" - page.live = False - page.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)) - p = Page.objects.get(slug='hello-world') - self.assertFalse(p.live) - self.assertTrue(PageRevision.objects.filter(page=p).exclude(approved_go_live_at__isnull=True).exists()) - management.call_command('publish_scheduled_pages', ) - p = Page.objects.get(slug='hello-world') - self.assertTrue(p.live) - self.assertFalse(PageRevision.objects.filter(page=p).exclude(approved_go_live_at__isnull=True).exists()) - - def test_future_go_live_page_will_not_be_published(self): - page = SimplePage() - page.title = "Hello world!" - page.slug = "hello-world" - page.live = False - page.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)) - p = Page.objects.get(slug='hello-world') - self.assertFalse(p.live) - self.assertTrue(PageRevision.objects.filter(page=p).exclude(approved_go_live_at__isnull=True).exists()) - management.call_command('publish_scheduled_pages', ) - p = Page.objects.get(slug='hello-world') - self.assertFalse(p.live) - self.assertTrue(PageRevision.objects.filter(page=p).exclude(approved_go_live_at__isnull=True).exists()) - - def test_expired_page_will_be_unpublished(self): - page = SimplePage() - page.title = "Hello world!" - page.slug = "hello-world" - page.live = True - page.expire_at = timezone.now() - timedelta(days=1) - self.root_page.add_child(instance=page) - p = Page.objects.get(slug='hello-world') - self.assertTrue(p.live) - management.call_command('publish_scheduled_pages', ) - p = Page.objects.get(slug='hello-world') - self.assertFalse(p.live) - self.assertTrue(p.expired) - - def test_future_expired_page_will_not_be_unpublished(self): - page = SimplePage() - page.title = "Hello world!" - page.slug = "hello-world" - page.live = True - page.expire_at = timezone.now() + timedelta(days=1) - self.root_page.add_child(instance=page) - p = Page.objects.get(slug='hello-world') - self.assertTrue(p.live) - management.call_command('publish_scheduled_pages', ) - p = Page.objects.get(slug='hello-world') - self.assertTrue(p.live) - self.assertFalse(p.expired) - - def test_expired_pages_are_dropped_from_mod_queue(self): - page = SimplePage() - page.title = "Hello world!" - page.slug = "hello-world" - page.live = False - page.expire_at = timezone.now() - timedelta(days=1) - self.root_page.add_child(instance=page) - page.save_revision(submitted_for_moderation = True) - p = Page.objects.get(slug='hello-world') - self.assertFalse(p.live) - self.assertTrue(PageRevision.objects.filter(page=p, submitted_for_moderation=True).exists()) - management.call_command('publish_scheduled_pages', ) - p = Page.objects.get(slug='hello-world') - self.assertFalse(PageRevision.objects.filter(page=p, submitted_for_moderation=True).exists()) - - diff --git a/wagtail/wagtailcore/tests.py b/wagtail/wagtailcore/tests.py index 1b12c1025..3eede0e4a 100644 --- a/wagtail/wagtailcore/tests.py +++ b/wagtail/wagtailcore/tests.py @@ -1,9 +1,12 @@ +from datetime import timedelta + from django.test import TestCase, Client from django.http import HttpRequest, Http404 - +from django.utils import timezone from django.contrib.auth.models import User +from django.core import management -from wagtail.wagtailcore.models import Page, Site, UserPagePermissionsProxy +from wagtail.wagtailcore.models import Page, Site, UserPagePermissionsProxy, PageRevision from wagtail.tests.models import EventPage, EventIndex, SimplePage @@ -776,3 +779,105 @@ class TestIssue157(TestCase): # Check url self.assertEqual(homepage.url, '/') + + +class TestPublishScheduledPagesCommand(TestCase): + def setUp(self): + # Find root page + self.root_page = Page.objects.get(id=2) + + def test_go_live_page_will_be_published(self): + page = SimplePage() + page.title = "Hello world!" + page.slug = "hello-world" + page.live = False + page.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) + ) + p = Page.objects.get(slug='hello-world') + self.assertFalse(p.live) + self.assertTrue(PageRevision.objects.filter(page=p).exclude(approved_go_live_at__isnull=True).exists()) + management.call_command('publish_scheduled_pages', verbosity=3, interactive=False) + #management.call_command('publish_scheduled_pages', dryrun=True, verbosity=3, interactive=False) + p = Page.objects.get(slug='hello-world') + self.assertTrue(p.live) + self.assertFalse(PageRevision.objects.filter(page=p).exclude(approved_go_live_at__isnull=True).exists()) + + def test_go_live_page_will_be_published(self): + page = SimplePage() + page.title = "Hello world!" + page.slug = "hello-world" + page.live = False + page.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)) + p = Page.objects.get(slug='hello-world') + self.assertFalse(p.live) + self.assertTrue(PageRevision.objects.filter(page=p).exclude(approved_go_live_at__isnull=True).exists()) + management.call_command('publish_scheduled_pages', ) + p = Page.objects.get(slug='hello-world') + self.assertTrue(p.live) + self.assertFalse(PageRevision.objects.filter(page=p).exclude(approved_go_live_at__isnull=True).exists()) + + def test_future_go_live_page_will_not_be_published(self): + page = SimplePage() + page.title = "Hello world!" + page.slug = "hello-world" + page.live = False + page.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)) + p = Page.objects.get(slug='hello-world') + self.assertFalse(p.live) + self.assertTrue(PageRevision.objects.filter(page=p).exclude(approved_go_live_at__isnull=True).exists()) + management.call_command('publish_scheduled_pages', ) + p = Page.objects.get(slug='hello-world') + self.assertFalse(p.live) + self.assertTrue(PageRevision.objects.filter(page=p).exclude(approved_go_live_at__isnull=True).exists()) + + def test_expired_page_will_be_unpublished(self): + page = SimplePage() + page.title = "Hello world!" + page.slug = "hello-world" + page.live = True + page.expire_at = timezone.now() - timedelta(days=1) + self.root_page.add_child(instance=page) + p = Page.objects.get(slug='hello-world') + self.assertTrue(p.live) + management.call_command('publish_scheduled_pages', ) + p = Page.objects.get(slug='hello-world') + self.assertFalse(p.live) + self.assertTrue(p.expired) + + def test_future_expired_page_will_not_be_unpublished(self): + page = SimplePage() + page.title = "Hello world!" + page.slug = "hello-world" + page.live = True + page.expire_at = timezone.now() + timedelta(days=1) + self.root_page.add_child(instance=page) + p = Page.objects.get(slug='hello-world') + self.assertTrue(p.live) + management.call_command('publish_scheduled_pages', ) + p = Page.objects.get(slug='hello-world') + self.assertTrue(p.live) + self.assertFalse(p.expired) + + def test_expired_pages_are_dropped_from_mod_queue(self): + page = SimplePage() + page.title = "Hello world!" + page.slug = "hello-world" + page.live = False + page.expire_at = timezone.now() - timedelta(days=1) + self.root_page.add_child(instance=page) + page.save_revision(submitted_for_moderation = True) + p = Page.objects.get(slug='hello-world') + self.assertFalse(p.live) + self.assertTrue(PageRevision.objects.filter(page=p, submitted_for_moderation=True).exists()) + management.call_command('publish_scheduled_pages', ) + p = Page.objects.get(slug='hello-world') + self.assertFalse(PageRevision.objects.filter(page=p, submitted_for_moderation=True).exists()) From e530531cb9be2c2b398ce374b73294eca27d1eeb Mon Sep 17 00:00:00 2001 From: Karl Hobley Date: Tue, 3 Jun 2014 10:37:39 +0100 Subject: [PATCH 014/298] Cleanup of scheduled publishing tests --- wagtail/wagtailadmin/tests.py | 11 ++++ wagtail/wagtailcore/tests.py | 109 ++++++++++++++++++++-------------- 2 files changed, 77 insertions(+), 43 deletions(-) diff --git a/wagtail/wagtailadmin/tests.py b/wagtail/wagtailadmin/tests.py index c657c1efb..3e4103137 100644 --- a/wagtail/wagtailadmin/tests.py +++ b/wagtail/wagtailadmin/tests.py @@ -1,7 +1,9 @@ from datetime import timedelta + from django.core.urlresolvers import reverse from django.utils import timezone from django.test import TestCase + from wagtail.tests.models import SimplePage, EventPage from wagtail.tests.utils import login, unittest from wagtail.wagtailcore.models import Page, PageRevision @@ -266,8 +268,10 @@ class TestPageEdit(TestCase): # The page will still be live self.assertTrue(child_page_new.live) + # A revision with approved_go_live_at should not exist self.assertFalse(PageRevision.objects.filter(page=child_page_new).exclude(approved_go_live_at__isnull=True).exists()) + # But a revision with go_live_at and expire_at in their content json *should* exist self.assertTrue(PageRevision.objects.filter(page=child_page_new, content_json__contains=str(go_live_at.date())).exists()) self.assertTrue(PageRevision.objects.filter(page=child_page_new, content_json__contains=str(expire_at.date())).exists()) @@ -309,8 +313,10 @@ class TestPageEdit(TestCase): self.assertEqual(response.status_code, 302) child_page_new = SimplePage.objects.get(id=self.child_page.id) + # The page should not be live anymore self.assertFalse(child_page_new.live) + # 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()) @@ -332,8 +338,10 @@ class TestPageEdit(TestCase): self.assertEqual(response.status_code, 302) child_page_new = SimplePage.objects.get(id=self.child_page.id) + # The page should not be live anymore self.assertFalse(child_page_new.live) + # 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()) @@ -352,11 +360,14 @@ class TestPageEdit(TestCase): self.assertEqual(response.status_code, 302) child_page_new = SimplePage.objects.get(id=self.child_page.id) + # The page should be live now self.assertTrue(child_page_new.live) + # And a revision with approved_go_live_at should not exist self.assertFalse(PageRevision.objects.filter(page=child_page_new).exclude(approved_go_live_at__isnull=True).exists()) + class TestPageDelete(TestCase): def setUp(self): # Find root page diff --git a/wagtail/wagtailcore/tests.py b/wagtail/wagtailcore/tests.py index 3eede0e4a..32ec3a826 100644 --- a/wagtail/wagtailcore/tests.py +++ b/wagtail/wagtailcore/tests.py @@ -787,97 +787,120 @@ class TestPublishScheduledPagesCommand(TestCase): self.root_page = Page.objects.get(id=2) def test_go_live_page_will_be_published(self): - page = SimplePage() - page.title = "Hello world!" - page.slug = "hello-world" - page.live = False - page.go_live_at = timezone.now() - timedelta(days=1) + page = SimplePage( + title="Hello world!", + slug="hello-world", + live=False, + 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.save_revision(approved_go_live_at=timezone.now() - timedelta(days=1)) + p = Page.objects.get(slug='hello-world') self.assertFalse(p.live) self.assertTrue(PageRevision.objects.filter(page=p).exclude(approved_go_live_at__isnull=True).exists()) - management.call_command('publish_scheduled_pages', verbosity=3, interactive=False) - #management.call_command('publish_scheduled_pages', dryrun=True, verbosity=3, interactive=False) + + management.call_command('publish_scheduled_pages') + p = Page.objects.get(slug='hello-world') self.assertTrue(p.live) self.assertFalse(PageRevision.objects.filter(page=p).exclude(approved_go_live_at__isnull=True).exists()) def test_go_live_page_will_be_published(self): - page = SimplePage() - page.title = "Hello world!" - page.slug = "hello-world" - page.live = False - page.go_live_at = timezone.now() - timedelta(days=1) + page = SimplePage( + title="Hello world!", + slug="hello-world", + live=False, + 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.save_revision(approved_go_live_at=timezone.now() - timedelta(days=1)) + p = Page.objects.get(slug='hello-world') self.assertFalse(p.live) self.assertTrue(PageRevision.objects.filter(page=p).exclude(approved_go_live_at__isnull=True).exists()) - management.call_command('publish_scheduled_pages', ) + + management.call_command('publish_scheduled_pages') + p = Page.objects.get(slug='hello-world') self.assertTrue(p.live) self.assertFalse(PageRevision.objects.filter(page=p).exclude(approved_go_live_at__isnull=True).exists()) def test_future_go_live_page_will_not_be_published(self): - page = SimplePage() - page.title = "Hello world!" - page.slug = "hello-world" - page.live = False - page.go_live_at = timezone.now() + timedelta(days=1) + page = SimplePage( + title="Hello world!", + slug="hello-world", + live=False, + 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.save_revision(approved_go_live_at=timezone.now() - timedelta(days=1)) + p = Page.objects.get(slug='hello-world') self.assertFalse(p.live) self.assertTrue(PageRevision.objects.filter(page=p).exclude(approved_go_live_at__isnull=True).exists()) - management.call_command('publish_scheduled_pages', ) + + management.call_command('publish_scheduled_pages') + p = Page.objects.get(slug='hello-world') self.assertFalse(p.live) self.assertTrue(PageRevision.objects.filter(page=p).exclude(approved_go_live_at__isnull=True).exists()) def test_expired_page_will_be_unpublished(self): - page = SimplePage() - page.title = "Hello world!" - page.slug = "hello-world" - page.live = True - page.expire_at = timezone.now() - timedelta(days=1) + page = SimplePage( + title="Hello world!", + slug="hello-world", + live=True, + expire_at=timezone.now() - timedelta(days=1), + ) self.root_page.add_child(instance=page) + p = Page.objects.get(slug='hello-world') self.assertTrue(p.live) - management.call_command('publish_scheduled_pages', ) + + management.call_command('publish_scheduled_pages') + p = Page.objects.get(slug='hello-world') self.assertFalse(p.live) self.assertTrue(p.expired) def test_future_expired_page_will_not_be_unpublished(self): - page = SimplePage() - page.title = "Hello world!" - page.slug = "hello-world" - page.live = True - page.expire_at = timezone.now() + timedelta(days=1) + page = SimplePage( + title="Hello world!", + slug="hello-world", + live=True, + expire_at=timezone.now() + timedelta(days=1), + ) self.root_page.add_child(instance=page) + p = Page.objects.get(slug='hello-world') self.assertTrue(p.live) - management.call_command('publish_scheduled_pages', ) + + management.call_command('publish_scheduled_pages') + p = Page.objects.get(slug='hello-world') self.assertTrue(p.live) self.assertFalse(p.expired) def test_expired_pages_are_dropped_from_mod_queue(self): - page = SimplePage() - page.title = "Hello world!" - page.slug = "hello-world" - page.live = False - page.expire_at = timezone.now() - timedelta(days=1) + page = SimplePage( + title="Hello world!", + slug="hello-world", + live=False, + expire_at=timezone.now() - timedelta(days=1), + ) self.root_page.add_child(instance=page) - page.save_revision(submitted_for_moderation = True) + + page.save_revision(submitted_for_moderation=True) + p = Page.objects.get(slug='hello-world') self.assertFalse(p.live) self.assertTrue(PageRevision.objects.filter(page=p, submitted_for_moderation=True).exists()) - management.call_command('publish_scheduled_pages', ) + + management.call_command('publish_scheduled_pages') + p = Page.objects.get(slug='hello-world') self.assertFalse(PageRevision.objects.filter(page=p, submitted_for_moderation=True).exists()) From c7e6c429543c883bec025cf41f0f477a0ce5a67e Mon Sep 17 00:00:00 2001 From: Karl Hobley Date: Tue, 3 Jun 2014 12:56:15 +0100 Subject: [PATCH 015/298] A couple of minor coding style fixes --- wagtail/wagtailadmin/views/pages.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/wagtail/wagtailadmin/views/pages.py b/wagtail/wagtailadmin/views/pages.py index f4ea960ff..55040366c 100644 --- a/wagtail/wagtailadmin/views/pages.py +++ b/wagtail/wagtailadmin/views/pages.py @@ -157,7 +157,7 @@ def create(request, content_type_app_name, content_type_model_name, parent_page_ page.save_revision( user=request.user, submitted_for_moderation=is_submitting, - approved_go_live_at = approved_go_live_at + approved_go_live_at=approved_go_live_at ) if is_publishing: @@ -255,7 +255,7 @@ def edit(request, page_id): page.save_revision( user=request.user, submitted_for_moderation=is_submitting, - approved_go_live_at = approved_go_live_at + approved_go_live_at=approved_go_live_at ) if is_publishing: From f4245815ba546153e9551afa6db9a9397a042348 Mon Sep 17 00:00:00 2001 From: Karl Hobley Date: Tue, 17 Jun 2014 09:37:35 +0100 Subject: [PATCH 016/298] Added sitemap generator --- wagtail/contrib/wagtailsitemaps/__init__.py | 0 wagtail/contrib/wagtailsitemaps/models.py | 0 .../wagtailsitemaps/sitemap_generator.py | 25 +++++++++++++++++++ .../templates/wagtailsitemaps/sitemap.xml | 13 ++++++++++ wagtail/contrib/wagtailsitemaps/views.py | 23 +++++++++++++++++ 5 files changed, 61 insertions(+) create mode 100644 wagtail/contrib/wagtailsitemaps/__init__.py create mode 100644 wagtail/contrib/wagtailsitemaps/models.py create mode 100644 wagtail/contrib/wagtailsitemaps/sitemap_generator.py create mode 100644 wagtail/contrib/wagtailsitemaps/templates/wagtailsitemaps/sitemap.xml create mode 100644 wagtail/contrib/wagtailsitemaps/views.py diff --git a/wagtail/contrib/wagtailsitemaps/__init__.py b/wagtail/contrib/wagtailsitemaps/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/wagtail/contrib/wagtailsitemaps/models.py b/wagtail/contrib/wagtailsitemaps/models.py new file mode 100644 index 000000000..e69de29bb diff --git a/wagtail/contrib/wagtailsitemaps/sitemap_generator.py b/wagtail/contrib/wagtailsitemaps/sitemap_generator.py new file mode 100644 index 000000000..53b5dd664 --- /dev/null +++ b/wagtail/contrib/wagtailsitemaps/sitemap_generator.py @@ -0,0 +1,25 @@ +from django.template.loader import render_to_string + + +class Sitemap(object): + template = 'wagtailsitemaps/sitemap.xml' + + def __init__(self, site): + self.site = site + + def get_pages(self): + return self.site.root_page.get_descendants(inclusive=True).live().order_by('path') + + def get_urls(self): + for page in self.get_pages(): + latest_revision = page.get_latest_revision() + + yield { + 'location': page.url, + 'lastmod': latest_revision.created_at if latest_revision else None + } + + def render(self): + return render_to_string(self.template, { + 'urlset': self.get_urls() + }) diff --git a/wagtail/contrib/wagtailsitemaps/templates/wagtailsitemaps/sitemap.xml b/wagtail/contrib/wagtailsitemaps/templates/wagtailsitemaps/sitemap.xml new file mode 100644 index 000000000..30ca3c024 --- /dev/null +++ b/wagtail/contrib/wagtailsitemaps/templates/wagtailsitemaps/sitemap.xml @@ -0,0 +1,13 @@ + + +{% spaceless %} +{% for url in urlset %} + + {{ url.location }} + {% if url.lastmod %}{{ url.lastmod|date:"Y-m-d" }}{% endif %} + {% if url.changefreq %}{{ url.changefreq }}{% endif %} + {% if url.priority %}{{ url.priority }}{% endif %} + +{% endfor %} +{% endspaceless %} + diff --git a/wagtail/contrib/wagtailsitemaps/views.py b/wagtail/contrib/wagtailsitemaps/views.py new file mode 100644 index 000000000..aa5fc2e71 --- /dev/null +++ b/wagtail/contrib/wagtailsitemaps/views.py @@ -0,0 +1,23 @@ +from django.shortcuts import render +from django.http import HttpResponse +from django.core.cache import cache + +from .sitemap_generator import Sitemap + + +def sitemap(request): + cache_key = 'wagtail-sitemap:' + str(request.site.id) + sitemap_xml = cache.get(cache_key) + + if not sitemap_xml: + # Rerender sitemap + sitemap = Sitemap(request.site) + sitemap_xml = sitemap.render() + + cache.set(cache_key, sitemap_xml, 6000) + + # Build response + response = HttpResponse(sitemap_xml) + response['Content-Type'] = "text/xml; charset=utf-8" + + return response From 4788144c86b1dbdc5e230add8407652acefd0316 Mon Sep 17 00:00:00 2001 From: Dave Cranwell Date: Tue, 17 Jun 2014 15:09:10 +0100 Subject: [PATCH 017/298] first bash at customisable tabs like in PR #210 --- wagtail/wagtailadmin/edit_handlers.py | 3 +- .../edit_handlers/tabbed_interface.html | 4 +- wagtail/wagtailadmin/views/pages.py | 40 +++++++++++++++++-- 3 files changed, 40 insertions(+), 7 deletions(-) diff --git a/wagtail/wagtailadmin/edit_handlers.py b/wagtail/wagtailadmin/edit_handlers.py index 245f7ad60..ecf692025 100644 --- a/wagtail/wagtailadmin/edit_handlers.py +++ b/wagtail/wagtailadmin/edit_handlers.py @@ -447,10 +447,11 @@ class BaseObjectList(BaseCompositeEditHandler): template = "wagtailadmin/edit_handlers/object_list.html" -def ObjectList(children, heading=""): +def ObjectList(children, heading="", classes=None): return type('_ObjectList', (BaseObjectList,), { 'children': children, 'heading': heading, + 'classes': classes }) diff --git a/wagtail/wagtailadmin/templates/wagtailadmin/edit_handlers/tabbed_interface.html b/wagtail/wagtailadmin/templates/wagtailadmin/edit_handlers/tabbed_interface.html index 5bbccb03b..813e40f53 100644 --- a/wagtail/wagtailadmin/templates/wagtailadmin/edit_handlers/tabbed_interface.html +++ b/wagtail/wagtailadmin/templates/wagtailadmin/edit_handlers/tabbed_interface.html @@ -1,12 +1,12 @@
{% for child in self.children %} -
+
{{ child.render_as_object }}
{% endfor %} diff --git a/wagtail/wagtailadmin/views/pages.py b/wagtail/wagtailadmin/views/pages.py index 69e6b7f4f..dedf3bff3 100644 --- a/wagtail/wagtailadmin/views/pages.py +++ b/wagtail/wagtailadmin/views/pages.py @@ -155,6 +155,9 @@ def create(request, content_type_app_name, content_type_model_name, parent_page_ page.save_revision(user=request.user, submitted_for_moderation=is_submitting) if is_publishing: + message = mark_safe(render_to_string(self.template, { + 'self': self + })) messages.success(request, _("Page '{0}' published.").format(page.title)) elif is_submitting: messages.success(request, _("Page '{0}' submitted for moderation.").format(page.title)) @@ -527,12 +530,41 @@ def set_page_position(request, page_to_move_id): PAGE_EDIT_HANDLERS = {} +def get_default_panels(page_class): + panels = [] + + try: + panels.append(ObjectList(page_class.content_panels, heading='Content')) + except AttributeError: + pass + + try: + panels.append(ObjectList(page_class.promote_panels, heading='Promote')) + except AttributeError: + pass + + try: + panels.append(ObjectList(page_class.settings_panels, heading='Settings', classes='settings')) + except AttributeError: + pass + + return panels + + +def get_panels(page_class): + try: + return page_class.panels + except AttributeError: + return get_default_panels(page_class) + + +def set_panels(page_class, panels): + page_class.panels = panels + + def get_page_edit_handler(page_class): if page_class not in PAGE_EDIT_HANDLERS: - PAGE_EDIT_HANDLERS[page_class] = TabbedInterface([ - ObjectList(page_class.content_panels, heading='Content'), - ObjectList(page_class.promote_panels, heading='Promote') - ]) + PAGE_EDIT_HANDLERS[page_class] = TabbedInterface(get_panels(page_class)) return PAGE_EDIT_HANDLERS[page_class] From 9efb6a38d75269caaeae71809c3c8b6409deadee Mon Sep 17 00:00:00 2001 From: Dave Cranwell Date: Tue, 17 Jun 2014 16:37:22 +0100 Subject: [PATCH 018/298] styling for scenario where tabs run onto multiple lines --- wagtail/wagtailadmin/edit_handlers.py | 2 +- .../wagtailadmin/scss/components/icons.scss | 1 - .../wagtailadmin/scss/components/tabs.scss | 49 ++++++++++++++----- wagtail/wagtailadmin/views/pages.py | 20 ++++---- 4 files changed, 49 insertions(+), 23 deletions(-) diff --git a/wagtail/wagtailadmin/edit_handlers.py b/wagtail/wagtailadmin/edit_handlers.py index ecf692025..d3be76161 100644 --- a/wagtail/wagtailadmin/edit_handlers.py +++ b/wagtail/wagtailadmin/edit_handlers.py @@ -447,7 +447,7 @@ class BaseObjectList(BaseCompositeEditHandler): template = "wagtailadmin/edit_handlers/object_list.html" -def ObjectList(children, heading="", classes=None): +def ObjectList(children, heading="", classes=""): return type('_ObjectList', (BaseObjectList,), { 'children': children, 'heading': heading, diff --git a/wagtail/wagtailadmin/static/wagtailadmin/scss/components/icons.scss b/wagtail/wagtailadmin/static/wagtailadmin/scss/components/icons.scss index 4de3aa131..9411cb1a8 100644 --- a/wagtail/wagtailadmin/static/wagtailadmin/scss/components/icons.scss +++ b/wagtail/wagtailadmin/static/wagtailadmin/scss/components/icons.scss @@ -96,7 +96,6 @@ .icon-unlocked:before { content: "p"; } - .icon-doc-full-inverse:before { content: "r"; } diff --git a/wagtail/wagtailadmin/static/wagtailadmin/scss/components/tabs.scss b/wagtail/wagtailadmin/static/wagtailadmin/scss/components/tabs.scss index b0dec687d..c008ed15e 100644 --- a/wagtail/wagtailadmin/static/wagtailadmin/scss/components/tabs.scss +++ b/wagtail/wagtailadmin/static/wagtailadmin/scss/components/tabs.scss @@ -1,13 +1,15 @@ .tab-nav{ @include clearfix(); padding:0; + background:$color-grey-4; li{ list-style-type:none; - width:48%; + width:50%; float:left; padding:0; position:relative; + margin-bottom:-1px; &:before,&:after{ display:none; @@ -18,13 +20,12 @@ @include box-shadow(inset 0px -2px 3px 0 rgba(0,0,0,0.1)); background-color:$color-grey-4; outline:none; - line-height:3em; text-transform:uppercase; font-weight:700; font-size:1.2em; text-decoration:none; display:block; - padding:0 20px; + padding:0.7em $mobile-nice-padding; color:$color-grey-2; border-top:0.3em solid $color-grey-4; border-bottom:1px solid transparent; @@ -57,6 +58,20 @@ } } + li.settings a{ + padding-left:30px; + padding-right:30px; + + &:before{ + font-family:wagtail; + vertical-align:middle; + text-transform:none; + content:"w"; + margin-right:0.5em; + font-size:1.2em; + } + } + li.active a{ @include box-shadow(none); color:$color-grey-1; @@ -66,7 +81,6 @@ /* For cases where tab-nav should merge with header */ &.merged{ - background-color:$color-header-bg; margin-top:0; } } @@ -82,14 +96,27 @@ } @media screen and (min-width: $breakpoint-mobile){ - .tab-nav li{ - width:auto; - padding:0; - margin-left:0.7em; - } + .tab-nav{ + /* For cases where tab-nav should merge with header */ + &.merged{ + background-color:$color-header-bg; + } - .tab-nav a{ - padding:0 50px; + li{ + width:auto; + padding:0; + margin-left:0.7em; + + &.tab-right{ + float:right; + margin-right:0.7em; + } + } + + a{ + padding-left:$desktop-nice-padding - 10; + padding-right:$desktop-nice-padding - 10; + } } .modal-content .tab-nav li{ diff --git a/wagtail/wagtailadmin/views/pages.py b/wagtail/wagtailadmin/views/pages.py index dedf3bff3..086c61f0f 100644 --- a/wagtail/wagtailadmin/views/pages.py +++ b/wagtail/wagtailadmin/views/pages.py @@ -531,40 +531,40 @@ PAGE_EDIT_HANDLERS = {} def get_default_panels(page_class): - panels = [] + handlers = [] try: - panels.append(ObjectList(page_class.content_panels, heading='Content')) + handlers.append(ObjectList(page_class.content_panels, heading='Content')) except AttributeError: pass try: - panels.append(ObjectList(page_class.promote_panels, heading='Promote')) + handlers.append(ObjectList(page_class.promote_panels, heading='Promote')) except AttributeError: pass try: - panels.append(ObjectList(page_class.settings_panels, heading='Settings', classes='settings')) + handlers.append(ObjectList(page_class.settings_panels, heading='Settings', classes='tab-right settings')) except AttributeError: pass - return panels + return handlers -def get_panels(page_class): +def get_which_page_edit_handler(page_class): try: - return page_class.panels + return page_class.handlers except AttributeError: return get_default_panels(page_class) -def set_panels(page_class, panels): - page_class.panels = panels +def set_page_edit_handler(page_class, handlers): + page_class.handlers = handlers def get_page_edit_handler(page_class): if page_class not in PAGE_EDIT_HANDLERS: - PAGE_EDIT_HANDLERS[page_class] = TabbedInterface(get_panels(page_class)) + PAGE_EDIT_HANDLERS[page_class] = TabbedInterface(get_which_page_edit_handler(page_class)) return PAGE_EDIT_HANDLERS[page_class] From b95d6a0fab18a17a59dc981f7c6213e083c07d6d Mon Sep 17 00:00:00 2001 From: Dave Cranwell Date: Tue, 17 Jun 2014 16:58:12 +0100 Subject: [PATCH 019/298] removed partial work from other branch --- wagtail/wagtailadmin/views/pages.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/wagtail/wagtailadmin/views/pages.py b/wagtail/wagtailadmin/views/pages.py index 086c61f0f..15aa81808 100644 --- a/wagtail/wagtailadmin/views/pages.py +++ b/wagtail/wagtailadmin/views/pages.py @@ -155,9 +155,6 @@ def create(request, content_type_app_name, content_type_model_name, parent_page_ page.save_revision(user=request.user, submitted_for_moderation=is_submitting) if is_publishing: - message = mark_safe(render_to_string(self.template, { - 'self': self - })) messages.success(request, _("Page '{0}' published.").format(page.title)) elif is_submitting: messages.success(request, _("Page '{0}' submitted for moderation.").format(page.title)) From fb9a876e92c0cbd53a3e30367efcbc05e35b0cdc Mon Sep 17 00:00:00 2001 From: Dave Cranwell Date: Tue, 17 Jun 2014 17:20:22 +0100 Subject: [PATCH 020/298] moved set_page_edit_hanlder to edit_handlders.py. Still not happy it's invoked the right way though --- wagtail/wagtailadmin/edit_handlers.py | 4 ++++ wagtail/wagtailadmin/views/pages.py | 4 ---- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/wagtail/wagtailadmin/edit_handlers.py b/wagtail/wagtailadmin/edit_handlers.py index d3be76161..b7090ff22 100644 --- a/wagtail/wagtailadmin/edit_handlers.py +++ b/wagtail/wagtailadmin/edit_handlers.py @@ -261,6 +261,10 @@ def extract_panel_definitions_from_model_class(model, exclude=None): return panels +def set_page_edit_handler(page_class, handlers): + page_class.handlers = handlers + + class EditHandler(object): """ Abstract class providing sensible default behaviours for objects implementing diff --git a/wagtail/wagtailadmin/views/pages.py b/wagtail/wagtailadmin/views/pages.py index 15aa81808..67d0e7525 100644 --- a/wagtail/wagtailadmin/views/pages.py +++ b/wagtail/wagtailadmin/views/pages.py @@ -555,10 +555,6 @@ def get_which_page_edit_handler(page_class): return get_default_panels(page_class) -def set_page_edit_handler(page_class, handlers): - page_class.handlers = handlers - - def get_page_edit_handler(page_class): if page_class not in PAGE_EDIT_HANDLERS: PAGE_EDIT_HANDLERS[page_class] = TabbedInterface(get_which_page_edit_handler(page_class)) From 553e7062b66bbfaea9d7ba3be5a0d4640269a451 Mon Sep 17 00:00:00 2001 From: Dave Cranwell Date: Tue, 17 Jun 2014 17:37:28 +0100 Subject: [PATCH 021/298] added FieldRowPanel from @kaedroho 5dc7a220f8799a7c6871b119724f7e07cb54f4fb --- wagtail/wagtailadmin/edit_handlers.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/wagtail/wagtailadmin/edit_handlers.py b/wagtail/wagtailadmin/edit_handlers.py index 245f7ad60..fb5f4b848 100644 --- a/wagtail/wagtailadmin/edit_handlers.py +++ b/wagtail/wagtailadmin/edit_handlers.py @@ -454,6 +454,17 @@ def ObjectList(children, heading=""): }) +class BaseFieldRowPanel(BaseCompositeEditHandler): + template = "wagtailadmin/edit_handlers/field_row_panel.html" + + +def FieldRowPanel(children, classname=None): + return type('_FieldRowPanel', (BaseFieldRowPanel,), { + 'children': children, + 'classname': classname, + }) + + class BaseMultiFieldPanel(BaseCompositeEditHandler): template = "wagtailadmin/edit_handlers/multi_field_panel.html" From 68da7f0c2c44d2b8661e6849cde56e29141ef98f Mon Sep 17 00:00:00 2001 From: Dave Cranwell Date: Tue, 17 Jun 2014 17:39:48 +0100 Subject: [PATCH 022/298] corrected commit ref to kaedroho@5dc7a220f8799a7c6871b119724f7e07cb54f4fb --- .../wagtailadmin/edit_handlers/field_row_panel.html | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 wagtail/wagtailadmin/templates/wagtailadmin/edit_handlers/field_row_panel.html diff --git a/wagtail/wagtailadmin/templates/wagtailadmin/edit_handlers/field_row_panel.html b/wagtail/wagtailadmin/templates/wagtailadmin/edit_handlers/field_row_panel.html new file mode 100644 index 000000000..e7371f0b9 --- /dev/null +++ b/wagtail/wagtailadmin/templates/wagtailadmin/edit_handlers/field_row_panel.html @@ -0,0 +1,7 @@ +
    + {% for child in self.children %} +
  • + {{ child.render_as_field }} +
  • + {% endfor %} +
From 6a33834d2948d69ce75c8ef3413cb9acc093557f Mon Sep 17 00:00:00 2001 From: Dave Cranwell Date: Thu, 19 Jun 2014 13:30:39 +0100 Subject: [PATCH 023/298] ongoing styling of tabs an publishing fields, inc introduction of salmon colour --- .../templates/wagtailstyleguide/base.html | 13 +++++++--- wagtail/wagtailadmin/edit_handlers.py | 17 ++++++++++-- .../wagtailadmin/scss/components/forms.scss | 8 ++++++ .../wagtailadmin/scss/components/tabs.scss | 5 ++-- .../scss/layouts/page-editor.scss | 26 +++++++++++++------ .../wagtailadmin/scss/layouts/styleguide.scss | 6 +++++ .../static/wagtailadmin/scss/variables.scss | 2 ++ 7 files changed, 61 insertions(+), 16 deletions(-) diff --git a/wagtail/contrib/wagtailstyleguide/templates/wagtailstyleguide/base.html b/wagtail/contrib/wagtailstyleguide/templates/wagtailstyleguide/base.html index e7bd88606..e0d478fe1 100644 --- a/wagtail/contrib/wagtailstyleguide/templates/wagtailstyleguide/base.html +++ b/wagtail/contrib/wagtailstyleguide/templates/wagtailstyleguide/base.html @@ -42,9 +42,10 @@
  • color-teal
  • color-teal-darker
  • color-teal-dark
  • -
  • color-red
  • -
  • color-orange
  • -
  • color-green
  • + +
      +
    • color-salmon
    • +
    • color-salmon-light
    • color-grey-1
    • @@ -54,6 +55,12 @@
    • color-grey-4
    • color-grey-5
    +
      +
    • color-red
    • +
    • color-orange
    • +
    • color-green
    • +
    +
    diff --git a/wagtail/wagtailadmin/edit_handlers.py b/wagtail/wagtailadmin/edit_handlers.py index 175c63225..bfdeaf812 100644 --- a/wagtail/wagtailadmin/edit_handlers.py +++ b/wagtail/wagtailadmin/edit_handlers.py @@ -602,17 +602,30 @@ def InlinePanel(base_model, relation_name, panels=None, label='', help_text=''): }) +# This allows users to include the publishing panel in their own per-model override +# without having to write these fields out by hand, potentially losing 'classname' +# and therefore the associated styling of the publishing panel +def PublishingPanel(): + return MultiFieldPanel([ + FieldPanel('go_live_at'), + FieldPanel('expire_at'), + ], ugettext_lazy('Scheduled publishing'), classname="publishing") + + # Now that we've defined EditHandlers, we can set up wagtailcore.Page to have some. Page.content_panels = [ FieldPanel('title', classname="full title"), ] + Page.promote_panels = [ MultiFieldPanel([ FieldPanel('slug'), FieldPanel('seo_title'), FieldPanel('show_in_menus'), FieldPanel('search_description'), - FieldPanel('go_live_at'), - FieldPanel('expire_at'), ], ugettext_lazy('Common page configuration')), ] + +Page.settings_panels = [ + PublishingPanel() +] diff --git a/wagtail/wagtailadmin/static/wagtailadmin/scss/components/forms.scss b/wagtail/wagtailadmin/static/wagtailadmin/scss/components/forms.scss index 5a870fbcf..0327c940a 100644 --- a/wagtail/wagtailadmin/static/wagtailadmin/scss/components/forms.scss +++ b/wagtail/wagtailadmin/static/wagtailadmin/scss/components/forms.scss @@ -1,3 +1,9 @@ +/* + These are the generic stylings for forms of any type. + If you're styling something specific to the page editing interface, + it probably ought to go in layouts/page-editor.scss +*/ + form { ul, li{ list-style-type:none; @@ -292,6 +298,7 @@ button.icon{ overflow:hidden; > li{ + @include row(); position:relative; background-color:white; padding:1em 10em 1em 1.5em; /* 10em padding leaves room for controls */ @@ -447,6 +454,7 @@ li.focused > .help{ } } + .fields li{ @include row(); padding-top:0.5em; diff --git a/wagtail/wagtailadmin/static/wagtailadmin/scss/components/tabs.scss b/wagtail/wagtailadmin/static/wagtailadmin/scss/components/tabs.scss index af0c496b1..dab30d114 100644 --- a/wagtail/wagtailadmin/static/wagtailadmin/scss/components/tabs.scss +++ b/wagtail/wagtailadmin/static/wagtailadmin/scss/components/tabs.scss @@ -2,7 +2,6 @@ @include row(); padding:0; background:$color-grey-4; - overflow:hidden; li{ list-style-type:none; @@ -22,10 +21,10 @@ text-decoration:none; display:block; padding:0.7em; - padding-bottom:1em; - margin-bottom:-0.3em; color:white; border-top:0.3em solid lighten($color-teal-darker, 3%); + max-height:1.2em; + overflow:hidden; &:hover{ color:white; diff --git a/wagtail/wagtailadmin/static/wagtailadmin/scss/layouts/page-editor.scss b/wagtail/wagtailadmin/static/wagtailadmin/scss/layouts/page-editor.scss index 5e3120c82..c9fe5b54b 100644 --- a/wagtail/wagtailadmin/static/wagtailadmin/scss/layouts/page-editor.scss +++ b/wagtail/wagtailadmin/static/wagtailadmin/scss/layouts/page-editor.scss @@ -25,9 +25,6 @@ } } -.objects{ - background:url("#{$static-root}bg-dark-diag.svg"); -} .object{ position:relative; overflow:hidden; @@ -80,7 +77,7 @@ > h2, &.single-field label{ -webkit-font-smoothing: auto; - background:$color-grey-3; + background:$color-salmon-light; text-transform:uppercase; padding:0.9em 0 0.9em 4em; font-size:0.95em; @@ -92,10 +89,10 @@ left:0; right:0; z-index:1; - text-shadow:1px 1px 1px rgba(255,255,255,0.5); - @include box-shadow(0 0 7px 0 rgba(0,0,0,0.4)); + overflow:hidden; &:before{ + text-shadow:none; font-family:wagtail; text-transform:none; content:"q"; @@ -108,10 +105,11 @@ line-height:1.8em; left:0px; width:1.7em; - opacity:0.15; + color:white; padding:0; margin:0; cursor:pointer; + background-color:$color-salmon; } } @@ -186,6 +184,17 @@ } } + /* special panel for the publishing fields, requires a bit more pizzazz */ + &.publishing{ + h2:before{ + content:"7"; + font-size:2.5em; + line-height:1.4em; + width:1.3em; + } + + } + &.title input, &.title textarea{ font-size:2em; @@ -235,16 +244,17 @@ top:0px; left:0px; width:3.3em; - background-color:$color-teal; padding:0; margin:0 0 0 -20px; cursor:pointer; a{ + @include border-radius(0); font-size: 0em; line-height: 0; overflow: visible; display:block; + background-color:$color-salmon; &:before{ position:relative; diff --git a/wagtail/wagtailadmin/static/wagtailadmin/scss/layouts/styleguide.scss b/wagtail/wagtailadmin/static/wagtailadmin/scss/layouts/styleguide.scss index 38d4281da..09cb627ed 100644 --- a/wagtail/wagtailadmin/static/wagtailadmin/scss/layouts/styleguide.scss +++ b/wagtail/wagtailadmin/static/wagtailadmin/scss/layouts/styleguide.scss @@ -68,6 +68,12 @@ section{ .color-grey-5{ background-color:$color-grey-5; } + .color-salmon{ + background-color:$color-salmon; + } + .color-salmon-light{ + background-color:$color-salmon-light; + } } diff --git a/wagtail/wagtailadmin/static/wagtailadmin/scss/variables.scss b/wagtail/wagtailadmin/static/wagtailadmin/scss/variables.scss index 0f6567989..9479b8a28 100644 --- a/wagtail/wagtailadmin/static/wagtailadmin/scss/variables.scss +++ b/wagtail/wagtailadmin/static/wagtailadmin/scss/variables.scss @@ -30,6 +30,8 @@ $color-teal-dark: #246060; $color-red: #f7474e; $color-orange:#e9b04d; $color-green: #189370; +$color-salmon: #f37e77; +$color-salmon-light: #fcf2f2; /* darker to lighter */ $color-grey-1: #333333; From d1b321f859e3a9f19e636c9ec4a0af9ac4992b27 Mon Sep 17 00:00:00 2001 From: Dave Cranwell Date: Thu, 19 Jun 2014 13:33:48 +0100 Subject: [PATCH 024/298] made red more distinct from salmon --- wagtail/wagtailadmin/static/wagtailadmin/scss/variables.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wagtail/wagtailadmin/static/wagtailadmin/scss/variables.scss b/wagtail/wagtailadmin/static/wagtailadmin/scss/variables.scss index 9479b8a28..e27817175 100644 --- a/wagtail/wagtailadmin/static/wagtailadmin/scss/variables.scss +++ b/wagtail/wagtailadmin/static/wagtailadmin/scss/variables.scss @@ -27,7 +27,7 @@ $breakpoint-desktop-larger: 100em; /* 1600px */ $color-teal: #43b1b0; $color-teal-darker: darken($color-teal, 10%); $color-teal-dark: #246060; -$color-red: #f7474e; +$color-red: #cd3238; $color-orange:#e9b04d; $color-green: #189370; $color-salmon: #f37e77; From 62fc679720aeb18c79e9d17fa5e4c6b67b105340 Mon Sep 17 00:00:00 2001 From: Dave Cranwell Date: Thu, 19 Jun 2014 13:47:09 +0100 Subject: [PATCH 025/298] further tweaks to colours and form layout --- .../static/wagtailadmin/scss/components/datetimepicker.scss | 2 +- .../static/wagtailadmin/scss/components/forms.scss | 2 +- .../static/wagtailadmin/scss/layouts/page-editor.scss | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/wagtail/wagtailadmin/static/wagtailadmin/scss/components/datetimepicker.scss b/wagtail/wagtailadmin/static/wagtailadmin/scss/components/datetimepicker.scss index 078c93469..4902c2418 100644 --- a/wagtail/wagtailadmin/static/wagtailadmin/scss/components/datetimepicker.scss +++ b/wagtail/wagtailadmin/static/wagtailadmin/scss/components/datetimepicker.scss @@ -246,7 +246,7 @@ .xdsoft_calendar td.xdsoft_default, .xdsoft_calendar td.xdsoft_current, .xdsoft_timepicker .xdsoft_time_box > div > div.xdsoft_current{ - background: $color-orange; + background: $color-salmon; color:#fff; font-weight: 700; } diff --git a/wagtail/wagtailadmin/static/wagtailadmin/scss/components/forms.scss b/wagtail/wagtailadmin/static/wagtailadmin/scss/components/forms.scss index 0327c940a..1d9b8b7a7 100644 --- a/wagtail/wagtailadmin/static/wagtailadmin/scss/components/forms.scss +++ b/wagtail/wagtailadmin/static/wagtailadmin/scss/components/forms.scss @@ -456,7 +456,7 @@ li.focused > .help{ .fields li{ - @include row(); + @include clearfix(); padding-top:0.5em; padding-bottom:1.2em; } diff --git a/wagtail/wagtailadmin/static/wagtailadmin/scss/layouts/page-editor.scss b/wagtail/wagtailadmin/static/wagtailadmin/scss/layouts/page-editor.scss index c9fe5b54b..c75f0b6bc 100644 --- a/wagtail/wagtailadmin/static/wagtailadmin/scss/layouts/page-editor.scss +++ b/wagtail/wagtailadmin/static/wagtailadmin/scss/layouts/page-editor.scss @@ -79,7 +79,7 @@ -webkit-font-smoothing: auto; background:$color-salmon-light; text-transform:uppercase; - padding:0.9em 0 0.9em 4em; + padding:0.9em 0 0.9em 4.1em; font-size:0.95em; margin:0 0 0.2em 0; line-height:1.5em; @@ -188,9 +188,9 @@ &.publishing{ h2:before{ content:"7"; - font-size:2.5em; + font-size:2.4em; line-height:1.4em; - width:1.3em; + width:1.4em; } } From d778c3693d152d9c4965b3a02597c6494b077e81 Mon Sep 17 00:00:00 2001 From: Nick Smith Date: Thu, 19 Jun 2014 12:17:07 +0100 Subject: [PATCH 026/298] respect DOM re-ordering of formsets even if the parent form errored --- wagtail/wagtailadmin/edit_handlers.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/wagtail/wagtailadmin/edit_handlers.py b/wagtail/wagtailadmin/edit_handlers.py index a15440016..d7a5726c3 100644 --- a/wagtail/wagtailadmin/edit_handlers.py +++ b/wagtail/wagtailadmin/edit_handlers.py @@ -562,6 +562,11 @@ class BaseInlinePanel(EditHandler): child_edit_handler_class(instance=subform.instance, form=subform) ) + # if this formset is valid, it may have been re-ordered; respect that + # in case the parent form errored and we need to re-render + if self.formset.can_order and self.formset.is_valid(): + self.children = sorted(self.children, key=lambda x: x.form.cleaned_data['ORDER']) + empty_form = self.formset.empty_form empty_form.fields['DELETE'].widget = forms.HiddenInput() if self.formset.can_order: From 269dfe62f5517013d54810832a846ac143339b60 Mon Sep 17 00:00:00 2001 From: Dave Cranwell Date: Thu, 19 Jun 2014 16:58:48 +0100 Subject: [PATCH 027/298] refactoring how/where fields get their classes added --- .../templates/wagtailstyleguide/base.html | 1 + wagtail/contrib/wagtailstyleguide/views.py | 1 + wagtail/wagtailadmin/edit_handlers.py | 26 +-- .../wagtailadmin/scss/components/forms.scss | 99 +++++++---- .../wagtailadmin/scss/components/header.scss | 7 - .../wagtailadmin/scss/components/icons.scss | 10 +- .../scss/fonts/wagtail-icomoon.json | 164 ++++++++++-------- .../wagtailadmin/scss/fonts/wagtail.eot | Bin 25504 -> 25648 bytes .../wagtailadmin/scss/fonts/wagtail.svg | 1 + .../wagtailadmin/scss/fonts/wagtail.ttf | Bin 25340 -> 25484 bytes .../wagtailadmin/scss/fonts/wagtail.woff | Bin 15108 -> 15192 bytes .../wagtailadmin/scss/layouts/login.scss | 18 +- .../edit_handlers/chooser_panel.html | 2 +- .../edit_handlers/field_panel_field.html | 24 +-- .../edit_handlers/multi_field_panel.html | 4 +- ...el_object.html => single_field_panel.html} | 2 +- .../templates/wagtailadmin/shared/field.html | 25 +++ .../wagtailadmin/shared/field_as_li.html | 25 +-- .../templates/wagtailadmin/shared/header.html | 2 +- .../wagtailforms/index_submissions.html | 2 +- 20 files changed, 223 insertions(+), 190 deletions(-) rename wagtail/wagtailadmin/templates/wagtailadmin/edit_handlers/{field_panel_object.html => single_field_panel.html} (57%) create mode 100644 wagtail/wagtailadmin/templates/wagtailadmin/shared/field.html diff --git a/wagtail/contrib/wagtailstyleguide/templates/wagtailstyleguide/base.html b/wagtail/contrib/wagtailstyleguide/templates/wagtailstyleguide/base.html index e0d478fe1..767f69f7f 100644 --- a/wagtail/contrib/wagtailstyleguide/templates/wagtailstyleguide/base.html +++ b/wagtail/contrib/wagtailstyleguide/templates/wagtailstyleguide/base.html @@ -388,6 +388,7 @@
  • warning
  • success
  • date
  • +
  • time
  • form
  • diff --git a/wagtail/contrib/wagtailstyleguide/views.py b/wagtail/contrib/wagtailstyleguide/views.py index 52c5433a1..9c830073c 100644 --- a/wagtail/contrib/wagtailstyleguide/views.py +++ b/wagtail/contrib/wagtailstyleguide/views.py @@ -23,6 +23,7 @@ class ExampleForm(forms.Form): url = forms.URLField(required=True) email = forms.EmailField(max_length=254) date = forms.DateField() + time = forms.TimeField() select = forms.ChoiceField(choices=CHOICES) boolean = forms.BooleanField(required=False) diff --git a/wagtail/wagtailadmin/edit_handlers.py b/wagtail/wagtailadmin/edit_handlers.py index bfdeaf812..9ded8bb29 100644 --- a/wagtail/wagtailadmin/edit_handlers.py +++ b/wagtail/wagtailadmin/edit_handlers.py @@ -195,9 +195,20 @@ class EditHandler(object): return "" def field_classnames(self): + classname = self.field_type() + "test" + + if self.bound_field.field.required: + classname += " required" + if self.bound_field.errors: + classname += " error" + + return classname + + + def input_classnames(self): """ - Additional classnames to add to the
  • when rendering this within a -
      + Additional classnames to add to the .input surrounding the input field. + Mainly used to identify certain field types boolean_field, url_field, date_field etc """ return "" @@ -367,16 +378,7 @@ class BaseFieldPanel(EditHandler): def field_type(self): return camelcase_to_underscore(self.bound_field.field.__class__.__name__) - def field_classnames(self): - classname = self.field_type() - if self.bound_field.field.required: - classname += " required" - if self.bound_field.errors: - classname += " error" - - return classname - - object_template = "wagtailadmin/edit_handlers/field_panel_object.html" + object_template = "wagtailadmin/edit_handlers/single_field_panel.html" def render_as_object(self): return mark_safe(render_to_string(self.object_template, { diff --git a/wagtail/wagtailadmin/static/wagtailadmin/scss/components/forms.scss b/wagtail/wagtailadmin/static/wagtailadmin/scss/components/forms.scss index 1d9b8b7a7..cfa028877 100644 --- a/wagtail/wagtailadmin/static/wagtailadmin/scss/components/forms.scss +++ b/wagtail/wagtailadmin/static/wagtailadmin/scss/components/forms.scss @@ -419,43 +419,85 @@ li.focused > .help{ .boolean_field .help, .radio .help{ opacity:1; } -.iconfield { - position:relative; + +/* + This is expected to go on the parent of the input/select/textarea + so in most cases .input +*/ +.iconfield, /* generic */ +.date_field, +.time_field, +.date_time_field, +.url_field{ + .input{ + position:relative; + + &:before, &:after{ + font-family:wagtail; + position:absolute; + top:0.5em; + line-height:100%; + font-size:2em; + color:$color-grey-3; + } + &:before{ + left:0.3em; + } + &:after{ + right:0.5em; + } + } input:not([type=radio]), input:not([type=checkbox]), input:not([type=submit]), input:not([type=button]){ padding-left:2.5em; } - &:before, &:after{ - font-family:wagtail; - position:absolute; - top:0.4em; - font-size:1.4em; - color:$color-grey-3; - } - &:before{ - left:0.5em; - } - &:after{ - right:0.5em; + /* smaller fields required slight repositioning of icons */ + &.field-small{ + .input{ + &:before, &:after{ + font-size:1.5em; + top:0.3em; + } + &:before{ + left:0.5em; + } + &:after{ + right:0.5em; + } + } } /* special case for search spinners */ &.icon-spinner:after{ color:$color-teal; opacity:0.8; - font-size:20px; - width:20px; - height:20px; - line-height:23px; text-align:center; top:0.3em; + } +} +.date_field, +.date_time_field{ + .input:before{ + @extend .icon-date:before; + } +} + +.time_field{ + .input:before{ + @extend .icon-time:before; + } +} + +.url_field{ + .input:before{ + @extend .icon-link:before; } } -.fields li{ +.fields > li{ @include clearfix(); padding-top:0.5em; padding-bottom:1.2em; @@ -679,18 +721,17 @@ input[type=submit], input[type=reset], input[type=button], .button, button{ @include column(2); padding-top:1.2em; padding-left:0; + } - .model_multiple_choice_field &, - .boolean_field &, - .model_choice_field &, - .image_field &, - .file_field &{ + /* these fields are formatted differently and label alignment must change */ + .model_multiple_choice_field, + .boolean_field, + .model_choice_field, + .image_field, + .file_field{ + label{ padding-top:0; } - - .boolean_field &{ - padding-bottom:0; - } } input[type=submit], input[type=reset], input[type=button], .button, button{ @@ -721,5 +762,5 @@ input[type=submit], input[type=reset], input[type=button], .button, button{ .field-content{ @include column(10); padding-right:0; - } + } } \ No newline at end of file diff --git a/wagtail/wagtailadmin/static/wagtailadmin/scss/components/header.scss b/wagtail/wagtailadmin/static/wagtailadmin/scss/components/header.scss index d93eaa270..3f079897f 100644 --- a/wagtail/wagtailadmin/static/wagtailadmin/scss/components/header.scss +++ b/wagtail/wagtailadmin/static/wagtailadmin/scss/components/header.scss @@ -97,13 +97,6 @@ header{ } } -/* mozilla specific hack */ -@-moz-document url-prefix() { - .iconfield.icon-spinner:after{ - line-height:20px; - } -} - .page-explorer header{ margin-bottom:0; padding-bottom:0em; diff --git a/wagtail/wagtailadmin/static/wagtailadmin/scss/components/icons.scss b/wagtail/wagtailadmin/static/wagtailadmin/scss/components/icons.scss index 30a089a18..e316e5e97 100644 --- a/wagtail/wagtailadmin/static/wagtailadmin/scss/components/icons.scss +++ b/wagtail/wagtailadmin/static/wagtailadmin/scss/components/icons.scss @@ -209,9 +209,9 @@ } .icon-spinner:after{ width:1em; - animation: spin 1s infinite; - -webkit-animation: spin 1s infinite; - -moz-animation: spin 1s infinite; + animation: spin 0.5s infinite linear; + -webkit-animation: spin 0.5s infinite linear; + -moz-animation: spin 0.5s infinite linear; content:"1"; } .icon-pick:before{ @@ -233,6 +233,9 @@ .icon-date:before{ content:"7"; } +.icon-time:before{ + content:"8"; +} .icon-success:before{ content:"9"; } @@ -245,6 +248,7 @@ .icon-form:before{ content:"$"; } + .icon.text-replace{ font-size:0em; line-height:0; diff --git a/wagtail/wagtailadmin/static/wagtailadmin/scss/fonts/wagtail-icomoon.json b/wagtail/wagtailadmin/static/wagtailadmin/scss/fonts/wagtail-icomoon.json index 583faf89a..57c8a26f1 100755 --- a/wagtail/wagtailadmin/static/wagtailadmin/scss/fonts/wagtail-icomoon.json +++ b/wagtail/wagtailadmin/static/wagtailadmin/scss/fonts/wagtail-icomoon.json @@ -1,6 +1,29 @@ { "IcoMoonType": "selection", "icons": [ + { + "icon": { + "paths": [ + "M632.913 707.493l-173.647-173.649v-232.782h105.469v189.094l142.759 142.757zM512 90.125c-232.995 0-421.875 188.88-421.875 421.875s188.88 421.875 421.875 421.875 421.875-188.88 421.875-421.875-188.88-421.875-421.875-421.875zM512 828.406c-174.747 0-316.406-141.659-316.406-316.406s141.659-316.406 316.406-316.406c174.747 0 316.406 141.659 316.406 316.406s-141.659 316.406-316.406 316.406z" + ], + "tags": [ + "clock", + "time", + "schedule" + ], + "grid": 16 + }, + "properties": { + "id": 72, + "order": 9, + "prevSize": 32, + "code": 56, + "name": "clock", + "ligatures": "" + }, + "setIdx": 0, + "iconIdx": 72 + }, { "icon": { "paths": [ @@ -20,7 +43,7 @@ "name": "lock39copy", "ligatures": "" }, - "setIdx": 0, + "setIdx": 1, "iconIdx": 0 }, { @@ -42,7 +65,7 @@ "name": "lock39-open", "ligatures": "" }, - "setIdx": 0, + "setIdx": 1, "iconIdx": 1 }, { @@ -63,7 +86,7 @@ "name": "form", "ligatures": "" }, - "setIdx": 0, + "setIdx": 1, "iconIdx": 2 }, { @@ -82,7 +105,7 @@ "name": "uni61", "ligatures": "" }, - "setIdx": 0, + "setIdx": 1, "iconIdx": 3 }, { @@ -101,7 +124,7 @@ "name": "uni62", "ligatures": "" }, - "setIdx": 0, + "setIdx": 1, "iconIdx": 4 }, { @@ -120,7 +143,7 @@ "name": "uni63", "ligatures": "" }, - "setIdx": 0, + "setIdx": 1, "iconIdx": 5 }, { @@ -139,7 +162,7 @@ "name": "uni64", "ligatures": "" }, - "setIdx": 0, + "setIdx": 1, "iconIdx": 6 }, { @@ -158,7 +181,7 @@ "name": "uni65", "ligatures": "" }, - "setIdx": 0, + "setIdx": 1, "iconIdx": 7 }, { @@ -177,7 +200,7 @@ "name": "uni66", "ligatures": "" }, - "setIdx": 0, + "setIdx": 1, "iconIdx": 8 }, { @@ -196,7 +219,7 @@ "name": "uni67", "ligatures": "" }, - "setIdx": 0, + "setIdx": 1, "iconIdx": 9 }, { @@ -215,7 +238,7 @@ "name": "uni69", "ligatures": "" }, - "setIdx": 0, + "setIdx": 1, "iconIdx": 10 }, { @@ -234,7 +257,7 @@ "name": "uni6A", "ligatures": "" }, - "setIdx": 0, + "setIdx": 1, "iconIdx": 11 }, { @@ -253,7 +276,7 @@ "name": "uni6B", "ligatures": "" }, - "setIdx": 0, + "setIdx": 1, "iconIdx": 12 }, { @@ -272,7 +295,7 @@ "name": "uni6C", "ligatures": "" }, - "setIdx": 0, + "setIdx": 1, "iconIdx": 13 }, { @@ -291,7 +314,7 @@ "name": "uni6E", "ligatures": "" }, - "setIdx": 0, + "setIdx": 1, "iconIdx": 14 }, { @@ -310,7 +333,7 @@ "name": "uni68", "ligatures": "" }, - "setIdx": 0, + "setIdx": 1, "iconIdx": 15 }, { @@ -329,7 +352,7 @@ "name": "uni6F", "ligatures": "" }, - "setIdx": 0, + "setIdx": 1, "iconIdx": 16 }, { @@ -348,7 +371,7 @@ "name": "uni71", "ligatures": "" }, - "setIdx": 0, + "setIdx": 1, "iconIdx": 17 }, { @@ -367,7 +390,7 @@ "name": "uni72", "ligatures": "" }, - "setIdx": 0, + "setIdx": 1, "iconIdx": 18 }, { @@ -386,7 +409,7 @@ "name": "uni73", "ligatures": "" }, - "setIdx": 0, + "setIdx": 1, "iconIdx": 19 }, { @@ -405,7 +428,7 @@ "name": "uni74", "ligatures": "" }, - "setIdx": 0, + "setIdx": 1, "iconIdx": 20 }, { @@ -424,7 +447,7 @@ "name": "uni75", "ligatures": "" }, - "setIdx": 0, + "setIdx": 1, "iconIdx": 21 }, { @@ -443,7 +466,7 @@ "name": "uni76", "ligatures": "" }, - "setIdx": 0, + "setIdx": 1, "iconIdx": 22 }, { @@ -462,7 +485,7 @@ "name": "uni77", "ligatures": "" }, - "setIdx": 0, + "setIdx": 1, "iconIdx": 23 }, { @@ -481,7 +504,7 @@ "name": "uni78", "ligatures": "" }, - "setIdx": 0, + "setIdx": 1, "iconIdx": 24 }, { @@ -500,7 +523,7 @@ "name": "uni7A", "ligatures": "" }, - "setIdx": 0, + "setIdx": 1, "iconIdx": 25 }, { @@ -519,7 +542,7 @@ "name": "uni41", "ligatures": "" }, - "setIdx": 0, + "setIdx": 1, "iconIdx": 26 }, { @@ -538,7 +561,7 @@ "name": "uni42", "ligatures": "" }, - "setIdx": 0, + "setIdx": 1, "iconIdx": 27 }, { @@ -557,7 +580,7 @@ "name": "uni44", "ligatures": "" }, - "setIdx": 0, + "setIdx": 1, "iconIdx": 28 }, { @@ -576,7 +599,7 @@ "name": "uni43", "ligatures": "" }, - "setIdx": 0, + "setIdx": 1, "iconIdx": 29 }, { @@ -595,7 +618,7 @@ "name": "uni45", "ligatures": "" }, - "setIdx": 0, + "setIdx": 1, "iconIdx": 30 }, { @@ -614,7 +637,7 @@ "name": "uni46", "ligatures": "" }, - "setIdx": 0, + "setIdx": 1, "iconIdx": 31 }, { @@ -633,7 +656,7 @@ "name": "uni47", "ligatures": "" }, - "setIdx": 0, + "setIdx": 1, "iconIdx": 32 }, { @@ -652,7 +675,7 @@ "name": "uni48", "ligatures": "" }, - "setIdx": 0, + "setIdx": 1, "iconIdx": 33 }, { @@ -671,7 +694,7 @@ "name": "uni49", "ligatures": "" }, - "setIdx": 0, + "setIdx": 1, "iconIdx": 34 }, { @@ -690,7 +713,7 @@ "name": "uni4A", "ligatures": "" }, - "setIdx": 0, + "setIdx": 1, "iconIdx": 35 }, { @@ -709,7 +732,7 @@ "name": "uni4B", "ligatures": "" }, - "setIdx": 0, + "setIdx": 1, "iconIdx": 36 }, { @@ -728,7 +751,7 @@ "name": "uni4C", "ligatures": "" }, - "setIdx": 0, + "setIdx": 1, "iconIdx": 37 }, { @@ -747,7 +770,7 @@ "name": "uni4D", "ligatures": "" }, - "setIdx": 0, + "setIdx": 1, "iconIdx": 38 }, { @@ -766,7 +789,7 @@ "name": "uni4E", "ligatures": "" }, - "setIdx": 0, + "setIdx": 1, "iconIdx": 39 }, { @@ -785,7 +808,7 @@ "name": "uni4F", "ligatures": "" }, - "setIdx": 0, + "setIdx": 1, "iconIdx": 40 }, { @@ -804,7 +827,7 @@ "name": "uni50", "ligatures": "" }, - "setIdx": 0, + "setIdx": 1, "iconIdx": 41 }, { @@ -823,7 +846,7 @@ "name": "uni51", "ligatures": "" }, - "setIdx": 0, + "setIdx": 1, "iconIdx": 42 }, { @@ -842,7 +865,7 @@ "name": "uni79", "ligatures": "" }, - "setIdx": 0, + "setIdx": 1, "iconIdx": 43 }, { @@ -861,7 +884,7 @@ "name": "uni52", "ligatures": "" }, - "setIdx": 0, + "setIdx": 1, "iconIdx": 44 }, { @@ -880,7 +903,7 @@ "name": "uni54", "ligatures": "" }, - "setIdx": 0, + "setIdx": 1, "iconIdx": 45 }, { @@ -899,7 +922,7 @@ "name": "uni57", "ligatures": "" }, - "setIdx": 0, + "setIdx": 1, "iconIdx": 46 }, { @@ -918,7 +941,7 @@ "name": "uni58", "ligatures": "" }, - "setIdx": 0, + "setIdx": 1, "iconIdx": 47 }, { @@ -937,7 +960,7 @@ "name": "uni59", "ligatures": "" }, - "setIdx": 0, + "setIdx": 1, "iconIdx": 48 }, { @@ -956,7 +979,7 @@ "name": "uni5A", "ligatures": "" }, - "setIdx": 0, + "setIdx": 1, "iconIdx": 49 }, { @@ -975,7 +998,7 @@ "name": "uni56", "ligatures": "" }, - "setIdx": 0, + "setIdx": 1, "iconIdx": 50 }, { @@ -994,7 +1017,7 @@ "name": "uni31", "ligatures": "" }, - "setIdx": 0, + "setIdx": 1, "iconIdx": 51 }, { @@ -1013,7 +1036,7 @@ "name": "uni55", "ligatures": "" }, - "setIdx": 0, + "setIdx": 1, "iconIdx": 52 }, { @@ -1032,7 +1055,7 @@ "name": "uni33", "ligatures": "" }, - "setIdx": 0, + "setIdx": 1, "iconIdx": 53 }, { @@ -1051,7 +1074,7 @@ "name": "uni32", "ligatures": "" }, - "setIdx": 0, + "setIdx": 1, "iconIdx": 54 }, { @@ -1070,7 +1093,7 @@ "name": "uni35", "ligatures": "" }, - "setIdx": 0, + "setIdx": 1, "iconIdx": 55 }, { @@ -1089,7 +1112,7 @@ "name": "uni36", "ligatures": "" }, - "setIdx": 0, + "setIdx": 1, "iconIdx": 56 }, { @@ -1108,7 +1131,7 @@ "name": "uni30", "ligatures": "" }, - "setIdx": 0, + "setIdx": 1, "iconIdx": 57 }, { @@ -1127,7 +1150,7 @@ "name": "uni3F", "ligatures": "" }, - "setIdx": 0, + "setIdx": 1, "iconIdx": 58 }, { @@ -1146,7 +1169,7 @@ "name": "uni21", "ligatures": "" }, - "setIdx": 0, + "setIdx": 1, "iconIdx": 59 }, { @@ -1165,7 +1188,7 @@ "name": "uni39", "ligatures": "" }, - "setIdx": 0, + "setIdx": 1, "iconIdx": 60 }, { @@ -1184,7 +1207,7 @@ "name": "uni53", "ligatures": "" }, - "setIdx": 0, + "setIdx": 1, "iconIdx": 61 }, { @@ -1203,7 +1226,7 @@ "name": "uni34", "ligatures": "" }, - "setIdx": 0, + "setIdx": 1, "iconIdx": 62 }, { @@ -1222,7 +1245,7 @@ "name": "uni37", "ligatures": "" }, - "setIdx": 0, + "setIdx": 1, "iconIdx": 63 } ], @@ -1245,13 +1268,14 @@ "baseline": 6.25, "whitespace": 50 }, - "showMetadata": false, - "showMetrics": true, - "useClassSelector": false, - "classSelector": ".icon", - "embed": false + "resetPoint": 58880 + }, + "imagePref": { + "color": 0, + "height": 32, + "columns": 16, + "margin": 16 }, - "imagePref": {}, "historySize": 100, "showCodes": true, "search": "", diff --git a/wagtail/wagtailadmin/static/wagtailadmin/scss/fonts/wagtail.eot b/wagtail/wagtailadmin/static/wagtailadmin/scss/fonts/wagtail.eot index 605751ad41fb7c6fa3c62568d7881f273d6a9900..343d7c831ef39f15f44a94e09b379e52092ed427 100755 GIT binary patch delta 589 zcmXw$Ur5tY6vxlGKb<;tn`^D@=dj=Jx4GH=Ib7{uvdR!6ri=<45w@Sx9AnFYd$0mW zG(tj3+fz?TM2JKT=ev)ZM1MyQ^;ALh5YbEIL;CAX@Vrs_uPAzH?gvXD&GXa zLTX7BXo#<_C6-Q?itrgX0nh>f*(rG@PpTuHCvBd}-ne$RbK^1bBEVQFJt<@OJN zq=E8*G#T7i<|B>~+tRth>>Y!ymzesXAv>LtcmEz%1IQ)HFXZIeJjB^+fN?qlgzIw7 zJn7S{yd;B~U^qWLQvi-qkjj>?;H|R>3iUJ)2MiEIut6A3!DYCq>?%rq9|+|6$U6h_ zzK6;;<%{xBd8fQpwy6%wB0E$)!Sd=N2Oen04_Jjp;DM&>b_D%Lk&`s6sf+XXMVTYu zWmZn|vYQh=#Lb|0w!#NcO}c0-_L!d0ZnQ2~i4-xCos*BJYd{QpjyWG`U04 z+-PBJ2(@B(*zFz;iwA1hdti3e(PchRfa=q>#fz48_Oo_X+y4hz+5V;8Y%M>k2X!sS zlR+FT&nHioHZp8M!4DjUoa} zK>ib;9)XY6Tj3aur;vru|MEc;LPH3;qKs7;N#$XBakAnM=(ur zhY*+WHIYrCuf(*(y2N)$#7Q2KN|V|m%_Nhdz@!kNa7ochu}jH8>45S&l{qROR3E6t zsQpm)&|uJ5qVYv@n^uz66YVVRf4UQNAL%XA`vG(T(3uP_OlKIlK(1i`I&Sl^q%1}N D9MXga diff --git a/wagtail/wagtailadmin/static/wagtailadmin/scss/fonts/wagtail.svg b/wagtail/wagtailadmin/static/wagtailadmin/scss/fonts/wagtail.svg index f095fbd63..11310f293 100755 --- a/wagtail/wagtailadmin/static/wagtailadmin/scss/fonts/wagtail.svg +++ b/wagtail/wagtailadmin/static/wagtailadmin/scss/fonts/wagtail.svg @@ -17,6 +17,7 @@ + diff --git a/wagtail/wagtailadmin/static/wagtailadmin/scss/fonts/wagtail.ttf b/wagtail/wagtailadmin/static/wagtailadmin/scss/fonts/wagtail.ttf index 84159ccc98436dfe8cd5d94f8ebcfa1169528801..ee751e5a601509283d42f5ed3356375793bb1e2b 100755 GIT binary patch delta 575 zcmex!l(FYH;{=6zhTDwC85kI47#J9G(i4jdfV2RR-vOjK(sL@)+NAf-2l6K{FvPWF zq$Z{?Y-oJJz>pvUlsC%&3NSxnG63>@fP9sV+>(lB5dnQ5A7~DvNKSro;@|J0Tnr3} zEkOC6+{B6kh5)8>3=By?I~Wx55_2U}4fs}^1PTBRVYDpBFD_wV21+p`=S1%)rFJ$e_re!eGf@&k)5>_5bhx{~%=y3P3RfpqL{=B3O*^|KtA;|6lum@&ASY zr-1qxCqGOQ-aL`9fUBOFL6?D-@iOCP237_s2B0HV)y<9N8JU&%nAqi*jg9P>)Xnsm zl$n+En9SAen2e3&n8f**l^Jcq8I2j;6Lo#86c`y5tbBB}{VbFi8I>&jw1HeLDMm&q zEh_~j3mpkYMhP7YrMmEvICn;VMN3~D9bZdD7%lRzUs4;y)s}?O3=EriCIriDVR~~V zp5Nvx12+@cPv=i8mP9zK(Sj+bQ-44lT|s&MjPh+?RN> z_=5P&1l9?%32qQ#5(*Jf5cwl!B32@HMS?|Qg%pp}2C0A2J~EpWd=wTb$|$xdUQy~& z=2HHl@=VoAO-pT=x|8}O4KIy%ns!50V!Kw1FEp8%ve(sL@)Ui-a#2;|RTV2J&Y zk(!vou;T1I28Q?!puAZIP=NUnlRl7t0LWL#$StX86cJzo@}B_p2;}4^CvtLqxx>Ja z@C7K}m77>mz~Ikxj)5Vu0mxU#OU#u_HQ-xu5-6|$sKK%zzqo{f87Re&bZ6p@YR?29 zO9bd1W(F1pMg~O&6$Wz#O9p#}D2A&4fB*jnDP&LpiW-1L9T^hAqKy9^|9|-Z+W)Kn zFZ@6M|0Ga1>B4 z<{NXbB^5xi2Mi31A|Tu>VkDrSn^*xf(DDmV5gQo01~B9$=B5J0 z{s0wOg0KPKijxKT#U((uTTFc6DO$oW3ly#t1d& zx*zl&=`S^~Hn?EeXslpdZ{lsT=Fb#92>LyR??={Q(Z06L9L(Zu0T;dtiJCs@{?4(# zyIc3N3#;b$#iGACx^;BAf#h$FS32K0fRf#>UV+H(96GOn!W`XySVc{`T`qM$>V93ut6oJ8Q$zabA#ZdMC@BjZm zIR*v=pqK$r%#k4xEXMf%@&AYaul>LH|HA)MKz-oUFj?1vUnXMHs(^TYo39MqOkkbo hPb`=TVu0w$5f0-`?(gR29}?ovz+lb7z{LOrR(lv2Cp%h;P1ND8Phwy&OWL3}eZ7CMz7Ye1 zc?3{C0fae}*bS0%6AOT1JAnKW5M~TzoRgkd3>34_0rEMZSRf-cF$E}Q0_2;4@rtwa zGJt|Wu^b>@1%#O&G3jUImQ(=6CNMBC3V?8-wOZej({K+6R{MQmW~62Op`n41a| zTLDyL3Bm?^D^3>V7Xuw(^1QKSELJY2x-5A{`_c5AIKE}9= z*@?kraz2wneIk1chbAWrR}|M`?kT+NeBb#W3LF(m651pjD7;BTSoEdXVe$VGz7l7p zq@+&C#K;=R_R7AJpReqvyj{gd<&vtg>Ql86^=TTLH2JjHwI*nLXs^_1*0t81qt~Y& zsQ=uc#!%PjjIp-ycaw-eQ}`h0_Y}S#S%*dY+Pd2|3owhaZLZ|B&|_qnyurNOGXdyg z5e5MUW(F1pMg~P-*qAd|GT1XjF;xBk`~N>smVrS5C~5!}b!12ci!%Oy{Qu$qYyYqQ zzwrP3|C2!7pa>M5Tx-EEv%o2sE1uuxD+4zZSoirO$3T(A0HP8kzbxYz0jJfITmS$7 diff --git a/wagtail/wagtailadmin/static/wagtailadmin/scss/layouts/login.scss b/wagtail/wagtailadmin/static/wagtailadmin/scss/layouts/login.scss index 13845ce53..e4bd18be2 100644 --- a/wagtail/wagtailadmin/static/wagtailadmin/scss/layouts/login.scss +++ b/wagtail/wagtailadmin/static/wagtailadmin/scss/layouts/login.scss @@ -44,6 +44,7 @@ form{ } } label{ + width:auto; color:white; } input[type=submit]{ @@ -85,22 +86,7 @@ form{ } .iconfield:before{ display:none; - } - - .full label{ - @include border-radius(2px); - text-transform:uppercase; - padding:2px 5px; - position:absolute; - top:0; - left:0; - margin-top:-1px; - font-size:0.7em; - z-index:1; - margin:0.2em 0; - line-height:1.5em; - font-weight:normal; - } + } /* Special full-width, one-off fields i.e a single text or textarea input */ .full input{ diff --git a/wagtail/wagtailadmin/templates/wagtailadmin/edit_handlers/chooser_panel.html b/wagtail/wagtailadmin/templates/wagtailadmin/edit_handlers/chooser_panel.html index a2058cab9..d6fc35c10 100644 --- a/wagtail/wagtailadmin/templates/wagtailadmin/edit_handlers/chooser_panel.html +++ b/wagtail/wagtailadmin/templates/wagtailadmin/edit_handlers/chooser_panel.html @@ -1,4 +1,4 @@ -{% extends "wagtailadmin/edit_handlers/field_panel_field.html" %} +{% extends "wagtailadmin/shared/field.html" %} {% load i18n %} {% comment %} Either the chosen or unchosen div will be shown, depending on the presence diff --git a/wagtail/wagtailadmin/templates/wagtailadmin/edit_handlers/field_panel_field.html b/wagtail/wagtailadmin/templates/wagtailadmin/edit_handlers/field_panel_field.html index 472cae0b2..f6f2fb4f5 100644 --- a/wagtail/wagtailadmin/templates/wagtailadmin/edit_handlers/field_panel_field.html +++ b/wagtail/wagtailadmin/templates/wagtailadmin/edit_handlers/field_panel_field.html @@ -1,23 +1 @@ -
      - {{ field.label_tag }} -
      -
      - {% block form_field %} - {{ field }} - {% endblock %} - -
      - {% if field.help_text %} -

      {{ field.help_text }}

      - {% endif %} - - {% if field.errors %} -

      - {% for error in field.errors %} - {{ error }} - {% endfor %} -

      - {% endif %} -
      -
      - +{% include "wagtailadmin/shared/field.html" %} diff --git a/wagtail/wagtailadmin/templates/wagtailadmin/edit_handlers/multi_field_panel.html b/wagtail/wagtailadmin/templates/wagtailadmin/edit_handlers/multi_field_panel.html index fc43a70e5..62d75c9b6 100644 --- a/wagtail/wagtailadmin/templates/wagtailadmin/edit_handlers/multi_field_panel.html +++ b/wagtail/wagtailadmin/templates/wagtailadmin/edit_handlers/multi_field_panel.html @@ -2,9 +2,7 @@ {{ self.heading }}
        {% for child in self.children %} -
      • - {{ child.render_as_field }} -
      • +
      • {{ child.render_as_field }}
      • {% endfor %}
      diff --git a/wagtail/wagtailadmin/templates/wagtailadmin/edit_handlers/field_panel_object.html b/wagtail/wagtailadmin/templates/wagtailadmin/edit_handlers/single_field_panel.html similarity index 57% rename from wagtail/wagtailadmin/templates/wagtailadmin/edit_handlers/field_panel_object.html rename to wagtail/wagtailadmin/templates/wagtailadmin/edit_handlers/single_field_panel.html index 5becef9b4..ecb35cbd6 100644 --- a/wagtail/wagtailadmin/templates/wagtailadmin/edit_handlers/field_panel_object.html +++ b/wagtail/wagtailadmin/templates/wagtailadmin/edit_handlers/single_field_panel.html @@ -1,6 +1,6 @@
      {{ self.heading }}
        -
      • {{ field_content }}
      • +
      • {{ field_content }}
      diff --git a/wagtail/wagtailadmin/templates/wagtailadmin/shared/field.html b/wagtail/wagtailadmin/templates/wagtailadmin/shared/field.html new file mode 100644 index 000000000..9113e1b6b --- /dev/null +++ b/wagtail/wagtailadmin/templates/wagtailadmin/shared/field.html @@ -0,0 +1,25 @@ +{% load wagtailadmin_tags %} +
      + {{ field.label_tag }} +
      +
      + {% block form_field %} + {{ field }} + {% endblock %} + + {# This span only used on rare occasions by certain types of input #} + +
      + {% if field.help_text %} +

      {{ field.help_text }}

      + {% endif %} + + {% if field.errors %} +

      + {% for error in field.errors %} + {{ error|escape }} + {% endfor %} +

      + {% endif %} +
      +
      \ No newline at end of file diff --git a/wagtail/wagtailadmin/templates/wagtailadmin/shared/field_as_li.html b/wagtail/wagtailadmin/templates/wagtailadmin/shared/field_as_li.html index a1f51174a..d10e9ba27 100644 --- a/wagtail/wagtailadmin/templates/wagtailadmin/shared/field_as_li.html +++ b/wagtail/wagtailadmin/templates/wagtailadmin/shared/field_as_li.html @@ -1,25 +1,4 @@ {% load wagtailadmin_tags %} -
    • -
      - {{ field.label_tag }} -
      -
      - {% block form_field %} - {{ field }} - {% endblock %} - -
      - {% if field.help_text %} -

      {{ field.help_text }}

      - {% endif %} - - {% if field.errors %} -

      - {% for error in field.errors %} - {{ error|escape }} - {% endfor %} -

      - {% endif %} -
      -
      +
    • + {% include "wagtailadmin/shared/field.html" %}
    • \ No newline at end of file diff --git a/wagtail/wagtailadmin/templates/wagtailadmin/shared/header.html b/wagtail/wagtailadmin/templates/wagtailadmin/shared/header.html index 59340d2d8..be391989d 100644 --- a/wagtail/wagtailadmin/templates/wagtailadmin/shared/header.html +++ b/wagtail/wagtailadmin/templates/wagtailadmin/shared/header.html @@ -8,7 +8,7 @@
        {% for field in search_form %} - {% include "wagtailadmin/shared/field_as_li.html" with field=field input_classes="field-small iconfield icon-search" %} + {% include "wagtailadmin/shared/field_as_li.html" with field=field field_classes="field-small iconfield" input_classes="icon-search" %} {% endfor %}
      diff --git a/wagtail/wagtailforms/templates/wagtailforms/index_submissions.html b/wagtail/wagtailforms/templates/wagtailforms/index_submissions.html index ae9572295..8e166111b 100644 --- a/wagtail/wagtailforms/templates/wagtailforms/index_submissions.html +++ b/wagtail/wagtailforms/templates/wagtailforms/index_submissions.html @@ -39,7 +39,7 @@
      From 9e94c83843c2feeec597a2a8b65b8e061f6812f8 Mon Sep 17 00:00:00 2001 From: Matt Westcott Date: Mon, 9 Jun 2014 14:08:39 +0100 Subject: [PATCH 218/298] Add 'private' indicator in page chooser for pages with view restrictions --- .../pages/_view_permission_indicator.html | 31 +++++++++---------- .../templates/wagtailadmin/pages/list.html | 10 ++++++ .../templatetags/wagtailadmin_tags.py | 22 ++++++++++++- 3 files changed, 46 insertions(+), 17 deletions(-) diff --git a/wagtail/wagtailadmin/templates/wagtailadmin/pages/_view_permission_indicator.html b/wagtail/wagtailadmin/templates/wagtailadmin/pages/_view_permission_indicator.html index ee6a7e3b4..5bb8853e0 100644 --- a/wagtail/wagtailadmin/templates/wagtailadmin/pages/_view_permission_indicator.html +++ b/wagtail/wagtailadmin/templates/wagtailadmin/pages/_view_permission_indicator.html @@ -1,24 +1,23 @@ {% load i18n wagtailadmin_tags %} +{% test_page_is_public page as is_public %} {% if not page_perms %} {% page_permissions page as page_perms %} {% endif %} -{% with page.get_view_restrictions as has_view_restrictions %} - {% if page_perms.can_set_view_restrictions %} - +{% if page_perms.can_set_view_restrictions %} + +{% else %} + {# Read-only display, for users who don't have permission to set view restrictions #} + {% if is_public %} +
      {% trans 'Public' %}
      {% else %} - {# Read-only display, for users who don't have permission to set view restrictions #} - {% if has_view_restrictions %} -
      {% trans 'Private' %}
      - {% else %} -
      {% trans 'Public' %}
      - {% endif %} +
      {% trans 'Private' %}
      {% endif %} -{% endwith %} +{% endif %} diff --git a/wagtail/wagtailadmin/templates/wagtailadmin/pages/list.html b/wagtail/wagtailadmin/templates/wagtailadmin/pages/list.html index a1d827c43..44b058e83 100644 --- a/wagtail/wagtailadmin/templates/wagtailadmin/pages/list.html +++ b/wagtail/wagtailadmin/templates/wagtailadmin/pages/list.html @@ -42,6 +42,11 @@ {% else %}

      {{ parent_page.title }}

      {% endif %} + + {% test_page_is_public parent_page as is_public %} + {% if not is_public %} + (private) + {% endif %} {% else %} {% if parent_page_perms.can_edit and 'edit' not in hide_actions|default:'' %}

      {{ parent_page.title }}

      @@ -155,6 +160,11 @@ {% else %} {{ page.title }} {% endif %} + + {% test_page_is_public page as is_public %} + {% if not is_public %} + (private) + {% endif %} {% else %} {% if page_perms.can_edit and 'edit' not in hide_actions|default:'' %} {{ page.title }} diff --git a/wagtail/wagtailadmin/templatetags/wagtailadmin_tags.py b/wagtail/wagtailadmin/templatetags/wagtailadmin_tags.py index f3c871507..a6467532d 100644 --- a/wagtail/wagtailadmin/templatetags/wagtailadmin_tags.py +++ b/wagtail/wagtailadmin/templatetags/wagtailadmin_tags.py @@ -7,7 +7,7 @@ from django.utils.translation import ugettext_lazy as _ from wagtail.wagtailadmin.menu import MenuItem from wagtail.wagtailcore import hooks -from wagtail.wagtailcore.models import get_navigation_menu_items, UserPagePermissionsProxy +from wagtail.wagtailcore.models import get_navigation_menu_items, UserPagePermissionsProxy, PageViewRestriction from wagtail.wagtailcore.utils import camelcase_to_underscore @@ -88,6 +88,26 @@ def page_permissions(context, page): return context['user_page_permissions'].for_page(page) +@register.assignment_tag(takes_context=True) +def test_page_is_public(context, page): + """ + Usage: {% test_page_is_public page as is_public %} + Sets 'is_public' to True iff there are no page view restrictions in place on + this page. + Caches the list of page view restrictions in the context, to avoid repeated + DB queries on repeated calls. + """ + if 'all_page_view_restriction_paths' not in context: + context['all_page_view_restriction_paths'] = PageViewRestriction.objects.select_related('page').values_list('page__path', flat=True) + + is_private = any([ + page.path.startswith(restricted_path) + for restricted_path in context['all_page_view_restriction_paths'] + ]) + + return not is_private + + @register.simple_tag def hook_output(hook_name): """ From ff33a00deb035063c2ff0809a4dea2eb3d48902e Mon Sep 17 00:00:00 2001 From: Dave Cranwell Date: Tue, 10 Jun 2014 14:33:08 +0100 Subject: [PATCH 219/298] 'view restrictions' renamed 'privacy' and general wagtail styling of privacy status completed --- wagtail/wagtailadmin/forms.py | 6 +- .../wagtailadmin/js/privacy-indicator.js | 18 +++++ .../js/view-permission-indicator.js | 18 ----- .../scss/components/formatters.scss | 19 +++++- .../wagtailadmin/scss/components/forms.scss | 6 ++ .../wagtailadmin/scss/components/header.scss | 10 ++- .../wagtailadmin/scss/components/listing.scss | 5 ++ .../components/view-permission-indicator.scss | 8 --- .../static/wagtailadmin/scss/normalize.css | 9 --- .../page_privacy/ancestor_privacy.html | 8 +++ .../page_privacy/set_privacy.html | 16 +++++ .../set_privacy.js} | 0 .../set_privacy_done.js} | 0 .../ancestor_restriction.html | 8 --- .../set_view_restrictions.html | 31 --------- .../wagtailadmin/pages/_editor_css.html | 1 - .../wagtailadmin/pages/_editor_js.html | 2 +- .../pages/_privacy_indicator.html | 23 +++++++ .../pages/_view_permission_indicator.html | 23 ------- .../templates/wagtailadmin/pages/edit.html | 12 ++-- .../templates/wagtailadmin/pages/index.html | 9 +-- .../templates/wagtailadmin/pages/list.html | 65 ++++++++++++------- .../templatetags/wagtailadmin_tags.py | 5 +- wagtail/wagtailadmin/urls.py | 4 +- ...e_view_restrictions.py => page_privacy.py} | 10 +-- 25 files changed, 161 insertions(+), 155 deletions(-) create mode 100644 wagtail/wagtailadmin/static/wagtailadmin/js/privacy-indicator.js delete mode 100644 wagtail/wagtailadmin/static/wagtailadmin/js/view-permission-indicator.js delete mode 100644 wagtail/wagtailadmin/static/wagtailadmin/scss/components/view-permission-indicator.scss create mode 100644 wagtail/wagtailadmin/templates/wagtailadmin/page_privacy/ancestor_privacy.html create mode 100644 wagtail/wagtailadmin/templates/wagtailadmin/page_privacy/set_privacy.html rename wagtail/wagtailadmin/templates/wagtailadmin/{page_view_restrictions/set_view_restrictions.js => page_privacy/set_privacy.js} (100%) rename wagtail/wagtailadmin/templates/wagtailadmin/{page_view_restrictions/set_view_restrictions_done.js => page_privacy/set_privacy_done.js} (100%) delete mode 100644 wagtail/wagtailadmin/templates/wagtailadmin/page_view_restrictions/ancestor_restriction.html delete mode 100644 wagtail/wagtailadmin/templates/wagtailadmin/page_view_restrictions/set_view_restrictions.html create mode 100644 wagtail/wagtailadmin/templates/wagtailadmin/pages/_privacy_indicator.html delete mode 100644 wagtail/wagtailadmin/templates/wagtailadmin/pages/_view_permission_indicator.html rename wagtail/wagtailadmin/views/{page_view_restrictions.py => page_privacy.py} (87%) diff --git a/wagtail/wagtailadmin/forms.py b/wagtail/wagtailadmin/forms.py index f3634faf0..e9b10f63c 100644 --- a/wagtail/wagtailadmin/forms.py +++ b/wagtail/wagtailadmin/forms.py @@ -78,9 +78,9 @@ class PasswordResetForm(PasswordResetForm): class PageViewRestrictionForm(forms.Form): - restriction_type = forms.ChoiceField(choices=[ - ('none', ugettext_lazy("This page is viewable by all visitors")), - ('password', ugettext_lazy("This page is only viewable to users who enter this password:")), + restriction_type = forms.ChoiceField(label="Visibility", choices=[ + ('none', ugettext_lazy("Public")), + ('password', ugettext_lazy("Private, accessible with the following password")), ], widget=forms.RadioSelect) password = forms.CharField(required=False) diff --git a/wagtail/wagtailadmin/static/wagtailadmin/js/privacy-indicator.js b/wagtail/wagtailadmin/static/wagtailadmin/js/privacy-indicator.js new file mode 100644 index 000000000..eb280f00e --- /dev/null +++ b/wagtail/wagtailadmin/static/wagtailadmin/js/privacy-indicator.js @@ -0,0 +1,18 @@ +$(function() { + /* Interface to set permissions from the explorer / editor */ + $('a.action-set-privacy').click(function() { + ModalWorkflow({ + 'url': this.href, + 'responses': { + 'setPermission': function(isPublic) { + if (isPublic) { + $('.privacy-indicator').removeClass('private').addClass('public'); + } else { + $('.privacy-indicator').removeClass('public').addClass('private'); + } + } + } + }); + return false; + }); +}); diff --git a/wagtail/wagtailadmin/static/wagtailadmin/js/view-permission-indicator.js b/wagtail/wagtailadmin/static/wagtailadmin/js/view-permission-indicator.js deleted file mode 100644 index 5e84e0840..000000000 --- a/wagtail/wagtailadmin/static/wagtailadmin/js/view-permission-indicator.js +++ /dev/null @@ -1,18 +0,0 @@ -$(function() { - /* Interface to set view permissions from the explorer / editor */ - $('a.action-set-view-permissions').click(function() { - ModalWorkflow({ - 'url': this.href, - 'responses': { - 'setPermission': function(isPublic) { - if (isPublic) { - $('.view-permission-indicator').removeClass('private').addClass('public'); - } else { - $('.view-permission-indicator').removeClass('public').addClass('private'); - } - } - } - }); - return false; - }); -}); diff --git a/wagtail/wagtailadmin/static/wagtailadmin/scss/components/formatters.scss b/wagtail/wagtailadmin/static/wagtailadmin/scss/components/formatters.scss index 97e915f36..b88c4fd60 100644 --- a/wagtail/wagtailadmin/static/wagtailadmin/scss/components/formatters.scss +++ b/wagtail/wagtailadmin/static/wagtailadmin/scss/components/formatters.scss @@ -121,8 +121,8 @@ color:lighten($color-grey-2,30%); -webkit-font-smoothing: auto; font-size:0.80em; - margin:0 0.5em; - background:white url("#{$static-root}bg-dark-diag.svg"); + margin:0 0.5em 0.5em; + background:white url( "#{$static-root}bg-dark-diag.svg"); &.primary{ color:$color-grey-2; @@ -136,6 +136,20 @@ a.status-tag.primary:hover{ color:$color-teal; } +.privacy-indicator { + &.public { + .label-private { + display: none; + } + } + &.private { + .label-public { + display: none; + } + } +} + + /* free tagging tags from taggit */ .tag{ background-color:$color-teal; @@ -174,6 +188,7 @@ a.tag:hover{ } } + /* make a block-level element inline */ .inline{ display:inline; diff --git a/wagtail/wagtailadmin/static/wagtailadmin/scss/components/forms.scss b/wagtail/wagtailadmin/static/wagtailadmin/scss/components/forms.scss index 3a8f67ba2..d71ff66da 100644 --- a/wagtail/wagtailadmin/static/wagtailadmin/scss/components/forms.scss +++ b/wagtail/wagtailadmin/static/wagtailadmin/scss/components/forms.scss @@ -69,6 +69,10 @@ input, textarea, select, .richtext, .tagit{ outline:none; background-color:$color-input-focus; } + &:disabled, &[disabled], &:disabled:hover, &[disabled]:hover{ + background-color:inherit; + cursor:not-allowed; + } } /* select boxes */ @@ -135,6 +139,7 @@ input[type=radio]:before{ display:block; content:"K"; width: 1em; + height:1em; line-height: 1.1em; padding: 4px; background-color: white; @@ -741,6 +746,7 @@ input[type=submit], input[type=reset], input[type=button], .button, button{ .choice_field &, .model_multiple_choice_field &, .boolean_field &, + .choice_field &, .model_choice_field &, .image_field &, .file_field &{ diff --git a/wagtail/wagtailadmin/static/wagtailadmin/scss/components/header.scss b/wagtail/wagtailadmin/static/wagtailadmin/scss/components/header.scss index 3f079897f..1e9905d27 100644 --- a/wagtail/wagtailadmin/static/wagtailadmin/scss/components/header.scss +++ b/wagtail/wagtailadmin/static/wagtailadmin/scss/components/header.scss @@ -5,6 +5,10 @@ header{ margin-bottom:2em; color:white; + a{ + color:white; + } + h1, h2{ margin:0; color:white; @@ -97,12 +101,6 @@ header{ } } -.page-explorer header{ - margin-bottom:0; - padding-bottom:0em; -} - - @media screen and (min-width: $breakpoint-mobile){ header{ padding-top:1.5em; diff --git a/wagtail/wagtailadmin/static/wagtailadmin/scss/components/listing.scss b/wagtail/wagtailadmin/static/wagtailadmin/scss/components/listing.scss index 9d940362a..8a5baa8d3 100644 --- a/wagtail/wagtailadmin/static/wagtailadmin/scss/components/listing.scss +++ b/wagtail/wagtailadmin/static/wagtailadmin/scss/components/listing.scss @@ -379,6 +379,11 @@ table.listing{ } } + .privacy-indicator{ + font-size:0.9em; + opacity:0.7; + } + .table-headers{ .ord{ padding-right:0; diff --git a/wagtail/wagtailadmin/static/wagtailadmin/scss/components/view-permission-indicator.scss b/wagtail/wagtailadmin/static/wagtailadmin/scss/components/view-permission-indicator.scss deleted file mode 100644 index 8e26eaf74..000000000 --- a/wagtail/wagtailadmin/static/wagtailadmin/scss/components/view-permission-indicator.scss +++ /dev/null @@ -1,8 +0,0 @@ -.view-permission-indicator { - &.public { - .label-private { display: none; } - } - &.private { - .label-public { display: none; } - } -} diff --git a/wagtail/wagtailadmin/static/wagtailadmin/scss/normalize.css b/wagtail/wagtailadmin/static/wagtailadmin/scss/normalize.css index 8d57e3c96..8d945b3ec 100755 --- a/wagtail/wagtailadmin/static/wagtailadmin/scss/normalize.css +++ b/wagtail/wagtailadmin/static/wagtailadmin/scss/normalize.css @@ -452,15 +452,6 @@ input[type="submit"] { *overflow: visible; /* 4 */ } -/** - * Re-set default cursor for disabled elements. - */ - -button[disabled], -html input[disabled] { - cursor: default; -} - /** * 1. Address box sizing set to content-box in IE 8/9. * 2. Remove excess padding in IE 8/9. diff --git a/wagtail/wagtailadmin/templates/wagtailadmin/page_privacy/ancestor_privacy.html b/wagtail/wagtailadmin/templates/wagtailadmin/page_privacy/ancestor_privacy.html new file mode 100644 index 000000000..b1471ba25 --- /dev/null +++ b/wagtail/wagtailadmin/templates/wagtailadmin/page_privacy/ancestor_privacy.html @@ -0,0 +1,8 @@ +{% load i18n %} +{% trans "Page privacy" as title_str %} +{% include "wagtailadmin/shared/header.html" with title=title_str icon="locked" %} + +
      +

      {% trans "This page has been made private by a parent page." %}

      +

      {% trans "You can edit the privacy settings on:" %} {{ page_with_restriction.title }} +

      diff --git a/wagtail/wagtailadmin/templates/wagtailadmin/page_privacy/set_privacy.html b/wagtail/wagtailadmin/templates/wagtailadmin/page_privacy/set_privacy.html new file mode 100644 index 000000000..6298d6f5f --- /dev/null +++ b/wagtail/wagtailadmin/templates/wagtailadmin/page_privacy/set_privacy.html @@ -0,0 +1,16 @@ +{% load i18n %} +{% trans "Page privacy" as title_str %} +{% include "wagtailadmin/shared/header.html" with title=title_str icon="locked" %} + +
      +

      {% trans "Note: privacy changes apply to all children of this page too." %}

      +
      + {% csrf_token %} +
        + {% include "wagtailadmin/shared/field_as_li.html" with field=form.restriction_type %} + {% include "wagtailadmin/shared/field_as_li.html" with field=form.password li_classes="password-field" %} +
      + +
      + +
      diff --git a/wagtail/wagtailadmin/templates/wagtailadmin/page_view_restrictions/set_view_restrictions.js b/wagtail/wagtailadmin/templates/wagtailadmin/page_privacy/set_privacy.js similarity index 100% rename from wagtail/wagtailadmin/templates/wagtailadmin/page_view_restrictions/set_view_restrictions.js rename to wagtail/wagtailadmin/templates/wagtailadmin/page_privacy/set_privacy.js diff --git a/wagtail/wagtailadmin/templates/wagtailadmin/page_view_restrictions/set_view_restrictions_done.js b/wagtail/wagtailadmin/templates/wagtailadmin/page_privacy/set_privacy_done.js similarity index 100% rename from wagtail/wagtailadmin/templates/wagtailadmin/page_view_restrictions/set_view_restrictions_done.js rename to wagtail/wagtailadmin/templates/wagtailadmin/page_privacy/set_privacy_done.js diff --git a/wagtail/wagtailadmin/templates/wagtailadmin/page_view_restrictions/ancestor_restriction.html b/wagtail/wagtailadmin/templates/wagtailadmin/page_view_restrictions/ancestor_restriction.html deleted file mode 100644 index d8b652f32..000000000 --- a/wagtail/wagtailadmin/templates/wagtailadmin/page_view_restrictions/ancestor_restriction.html +++ /dev/null @@ -1,8 +0,0 @@ -{% load i18n %} -{% trans "Access restricted" as title_str %} -{% include "wagtailadmin/shared/header.html" with title=title_str %} - -
      - {% trans "Access to this page is restricted because it is within the section:" %} - {{ page_with_restriction.title }} -
      diff --git a/wagtail/wagtailadmin/templates/wagtailadmin/page_view_restrictions/set_view_restrictions.html b/wagtail/wagtailadmin/templates/wagtailadmin/page_view_restrictions/set_view_restrictions.html deleted file mode 100644 index 632c34f1b..000000000 --- a/wagtail/wagtailadmin/templates/wagtailadmin/page_view_restrictions/set_view_restrictions.html +++ /dev/null @@ -1,31 +0,0 @@ -{% load i18n %} -{% trans "Set access restrictions" as title_str %} -{% include "wagtailadmin/shared/header.html" with title=title_str %} - -
      -
      - {% csrf_token %} -
        -
      • - {# 'This page is viewable by all visitors' #} - {{ form.restriction_type.0 }} -
      • -
      • - {# 'This page is only viewable to users who enter this password:' #} - {{ form.restriction_type.1 }} - - {# FIXME: need distinct CSS styling for the 'disabled' state #} - {{ form.password }} - {% if form.password.errors %} -

        - {% for error in form.password.errors %} - {{ error }} - {% endfor %} -

        - {% endif %} -
      • -
      - -
      -

      {% trans "Access restrictions will take effect on this page and all child pages." %}

      -
      diff --git a/wagtail/wagtailadmin/templates/wagtailadmin/pages/_editor_css.html b/wagtail/wagtailadmin/templates/wagtailadmin/pages/_editor_css.html index 58d7d5f17..2e140c89c 100644 --- a/wagtail/wagtailadmin/templates/wagtailadmin/pages/_editor_css.html +++ b/wagtail/wagtailadmin/templates/wagtailadmin/pages/_editor_css.html @@ -7,7 +7,6 @@ {% compress css %} - {# we'll want tag-it included, for the benefit of any modals that use it, like images. #} {# TODO: a method of injecting these sorts of things on demand when the modal is spawned #} diff --git a/wagtail/wagtailadmin/templates/wagtailadmin/pages/_editor_js.html b/wagtail/wagtailadmin/templates/wagtailadmin/pages/_editor_js.html index f494f75ec..a3185e096 100644 --- a/wagtail/wagtailadmin/templates/wagtailadmin/pages/_editor_js.html +++ b/wagtail/wagtailadmin/templates/wagtailadmin/pages/_editor_js.html @@ -21,7 +21,7 @@ - + {% hook_output 'insert_editor_js' %} {% endcompress %} diff --git a/wagtail/wagtailadmin/templates/wagtailadmin/pages/_privacy_indicator.html b/wagtail/wagtailadmin/templates/wagtailadmin/pages/_privacy_indicator.html new file mode 100644 index 000000000..53aa44800 --- /dev/null +++ b/wagtail/wagtailadmin/templates/wagtailadmin/pages/_privacy_indicator.html @@ -0,0 +1,23 @@ +{% load i18n wagtailadmin_tags %} + +{% test_page_is_public page as is_public %} +{% if not page_perms %} + {% page_permissions page as page_perms %} +{% endif %} + +
      + {% trans "Privacy" %} + {% if page_perms.can_set_view_restrictions %} + + {# labels are shown/hidden in CSS according to the 'private' / 'public' class on view-permission-indicator #} + {% trans 'Public' %} + {% trans 'Private' %} + + {% else %} + {% if is_public %} + {% trans 'Public' %} + {% else %} + {% trans 'Private' %} + {% endif %} + {% endif %} +
      diff --git a/wagtail/wagtailadmin/templates/wagtailadmin/pages/_view_permission_indicator.html b/wagtail/wagtailadmin/templates/wagtailadmin/pages/_view_permission_indicator.html deleted file mode 100644 index 5bb8853e0..000000000 --- a/wagtail/wagtailadmin/templates/wagtailadmin/pages/_view_permission_indicator.html +++ /dev/null @@ -1,23 +0,0 @@ -{% load i18n wagtailadmin_tags %} - -{% test_page_is_public page as is_public %} -{% if not page_perms %} - {% page_permissions page as page_perms %} -{% endif %} - -{% if page_perms.can_set_view_restrictions %} - -{% else %} - {# Read-only display, for users who don't have permission to set view restrictions #} - {% if is_public %} -
      {% trans 'Public' %}
      - {% else %} -
      {% trans 'Private' %}
      - {% endif %} -{% endif %} diff --git a/wagtail/wagtailadmin/templates/wagtailadmin/pages/edit.html b/wagtail/wagtailadmin/templates/wagtailadmin/pages/edit.html index a1b108933..c15efbd59 100644 --- a/wagtail/wagtailadmin/templates/wagtailadmin/pages/edit.html +++ b/wagtail/wagtailadmin/templates/wagtailadmin/pages/edit.html @@ -15,11 +15,15 @@

      {% blocktrans with title=page.title %}Editing {{ title }}{% endblocktrans %}

  • - {% trans "Status:" %} {% if page.live %}{{ page.status_string }}{% else %}{{ page.status_string }}{% endif %} + {% trans "Status" %} + {% if page.live %} + {{ page.status_string }} + {% else %} + {{ page.status_string }} + {% endif %} + + {% include "wagtailadmin/pages/_privacy_indicator.html" with page=page page_perms=page_perms only %}
    - - {% include "wagtailadmin/pages/_view_permission_indicator.html" with page=page page_perms=page_perms only %} - diff --git a/wagtail/wagtailadmin/templates/wagtailadmin/pages/index.html b/wagtail/wagtailadmin/templates/wagtailadmin/pages/index.html index e9e4847dd..3a3aa49a7 100644 --- a/wagtail/wagtailadmin/templates/wagtailadmin/pages/index.html +++ b/wagtail/wagtailadmin/templates/wagtailadmin/pages/index.html @@ -16,19 +16,12 @@ {% page_permissions parent_page as parent_page_perms %} {% include "wagtailadmin/pages/list.html" with sortable=1 allow_navigation=1 full_width=1 parent_page=parent_page orderable=parent_page_perms.can_reorder_children %} - -{% endblock %} - -{% block extra_css %} - {% compress css %} - - {% endcompress %} {% endblock %} {% block extra_js %} {% comment %} modal-workflow is required by the view restrictions interface {% endcomment %} - +