Repository: wagtail/wagtail

Issue:
Support multiple page modes on preview
This change allows pages to define multiple display modes for use when previewing; these are presented as options within the page editor interface. For example, a page with a form on it might be viewable in two modes: the standard view that displays the form, and a thankyou page shown post-submission; this allows editors to preview both modes of the page.

I tested this on wagtaildemo with this rather frivolous addition to the EventPage class:

```
def get_page_modes(self):
    return [
        ('standard', 'Standard view'),
        ('upside-down', 'Upside down'),
    ]

def show_as_mode(self, mode):
    if mode == 'upside-down':
        response = super(EventPage, self).show_as_mode(mode)
        response.render()
        response.content += """<style>body {-moz-transform: rotate(180deg); -webkit-transform: rotate(180deg); -ms-transform: rotate(180deg); -o-transform: rotate(180deg); transform: rotate(180deg);}</style>"""
        return response
    else:
        return super(EventPage, self).show_as_mode(mode)
```

This results in a 'Preview' menu as shown below; also shown for comparison is the new default view, where only one display mode is available and thus the Preview option is presented as a single button. @davecranwell, does this fit OK with our design guidelines?

Multiple modes available:
![screen shot 2014-04-08 at 16 25 39](https://cloud.githubusercontent.com/assets/85097/2645625/9a986f16-bf34-11e3-98fa-dbf716dc804f.png)

Only one mode available:
![screen shot 2014-04-08 at 16 25 29](https://cloud.githubusercontent.com/assets/85097/2645629/a8524528-bf34-11e3-984e-d0a1620a4ab6.png)



Discussion:
Hi @gasman 
Just had a quick browse over the code. Looks good to me!

All looks good to me, although I do want to make a few changes here,relatively minor things like colours and consistent border radius etc. What would be best - making those changes before or after this PR?

@davecranwell I've just pushed this as a new branch 'feature/pagemodes' on torchbox/wagtail - I suggest you make your changes there and we'll merge it all back to master when you're done.

I'm getting a Bad Request (400) error in the preview window which pops up. As far as I can tell I've modified a sample content type class appropriately. 

@davecranwell That might be something to do with the way we're creating fake HTTP requests to pass to the 'serve' method... if the template is expecting a particular HTTP header to be set which we haven't accounted for, that's likely to fail. Can you share your code and the full error message, please?

Merged into master, to unblock https://github.com/torchbox/verdant-rca/pull/12 - still awaiting design tweaks from @davecranwell, but those shouldn't be a blocker.

The 400 error mentioned previously was found to be a result of setting any other port besides "80" for the site record in the Django admin (/django-admin/wagtailcore/site/1/). My settings defined the site as using port 8111, which was a mistake, but it might stand to reason that any wagtail installation where the port is _legitimately_ non-80, would also experience this issue.


Resolving patch:
diff --git a/wagtail/wagtailadmin/views/pages.py b/wagtail/wagtailadmin/views/pages.py
--- a/wagtail/wagtailadmin/views/pages.py
+++ b/wagtail/wagtailadmin/views/pages.py
@@ -313,7 +313,13 @@ def clean():
                     approved_go_live_at = go_live_at
                 else:
                     page.live = True
-                form.save()
+
+                # We need save the page this way to workaround a bug
+                # in django-modelcluster causing m2m fields to not
+                # be committed to the database. See github issue #192
+                form.save(commit=False)
+                page.save()
+
                 # Clear approved_go_live_at for older revisions
                 page.revisions.update(
                     submitted_for_moderation=False,
@@ -328,7 +334,9 @@ def clean():
                     Page.objects.filter(id=page.id).update(has_unpublished_changes=True)
                 else:
                     page.has_unpublished_changes = True
-                    form.save()
+                    form.save(commit=False)
+                    page.save()
+
 
             page.save_revision(
                 user=request.user,


Test patch:
diff --git a/wagtail/tests/models.py b/wagtail/tests/models.py
--- a/wagtail/tests/models.py
+++ b/wagtail/tests/models.py
@@ -4,7 +4,10 @@
 from django.conf.urls import url
 from django.http import HttpResponse
 
+from taggit.models import TaggedItemBase
+
 from modelcluster.fields import ParentalKey
+from modelcluster.tags import ClusterTaggableManager
 
 from wagtail.wagtailcore.models import Page, Orderable
 from wagtail.wagtailcore.fields import RichTextField
@@ -414,3 +417,11 @@ def archive_by_author(self, request, author_slug):
 
     def main(self, request):
         return HttpResponse("MAIN VIEW")
+
+
+class TaggedPageTag(TaggedItemBase):
+    content_object = ParentalKey('tests.TaggedPage', related_name='tagged_items')
+
+
+class TaggedPage(Page):
+    tags = ClusterTaggableManager(through=TaggedPageTag, blank=True)
diff --git a/wagtail/wagtailadmin/tests/test_pages_views.py b/wagtail/wagtailadmin/tests/test_pages_views.py
--- a/wagtail/wagtailadmin/tests/test_pages_views.py
+++ b/wagtail/wagtailadmin/tests/test_pages_views.py
@@ -7,7 +7,7 @@
 from django.core.paginator import Paginator
 from django.utils import timezone
 
-from wagtail.tests.models import SimplePage, EventPage, EventPageCarouselItem, StandardIndex, BusinessIndex, BusinessChild, BusinessSubIndex
+from wagtail.tests.models import SimplePage, EventPage, EventPageCarouselItem, StandardIndex, BusinessIndex, BusinessChild, BusinessSubIndex, TaggedPage
 from wagtail.tests.utils import unittest, WagtailTestUtils
 from wagtail.wagtailcore.models import Page, PageRevision
 from wagtail.wagtailcore.signals import page_published, page_unpublished
@@ -1410,3 +1410,36 @@ def test_rejected_notification_preferences_respected(self):
 
         # No email to send
         self.assertEqual(len(mail.outbox), 0)
+
+
+class TestIssue197(TestCase, WagtailTestUtils):
+    def test_issue_197(self):
+        # Find root page
+        self.root_page = Page.objects.get(id=2)
+
+        # Create a tagged page with no tags
+        self.tagged_page = self.root_page.add_child(instance=TaggedPage(
+            title="Tagged page",
+            slug='tagged-page',
+            live=False,
+        ))
+
+        # Login
+        self.user = self.login()
+
+        # Add some tags and publish using edit view
+        post_data = {
+            'title': "Tagged page",
+            'slug':'tagged-page',
+            'tags': "hello, world",
+            'action-publish': "Publish",
+        }
+        response = self.client.post(reverse('wagtailadmin_pages_edit', args=(self.tagged_page.id, )), post_data)
+
+        # Should be redirected to explorer page
+        self.assertRedirects(response, reverse('wagtailadmin_explore', args=(self.root_page.id, )))
+
+        # Check that both tags are in the pages tag set
+        page = TaggedPage.objects.get(id=self.tagged_page.id)
+        self.assertIn('hello', page.tags.slugs())
+        self.assertIn('world', page.tags.slugs())