diff --git a/wagtail/wagtailadmin/views/pages.py b/wagtail/wagtailadmin/views/pages.py index 3fb905715..4c50eaafe 100644 --- a/wagtail/wagtailadmin/views/pages.py +++ b/wagtail/wagtailadmin/views/pages.py @@ -340,12 +340,14 @@ def preview_on_edit(request, page_id): if form.is_valid(): form.save(commit=False) - # FIXME: passing the original request to page.serve is dodgy (particularly if page.serve has - # special treatment of POSTs). Ought to construct one that more or less matches what would be sent - # as a front-end GET request + # This view will generally be invoked as an AJAX request; as such, in the case of + # an error Django will return a plaintext response. This isn't what we want, since + # we will be writing the response back to an HTML page regardless of success or + # failure - as such, we strip out the X-Requested-With header to get Django to return + # an HTML error response + request.META.pop('HTTP_X_REQUESTED_WITH', None) - request.META.pop('HTTP_X_REQUESTED_WITH', None) # Make this request appear to the page's serve method as a non-ajax one, as they will often implement custom behaviour for XHR - response = page.serve(request) + response = page.serve(page.dummy_request()) response['X-Wagtail-Preview'] = 'ok' return response @@ -380,10 +382,14 @@ def preview_on_create(request, content_type_app_name, content_type_model_name, p if form.is_valid(): form.save(commit=False) - # FIXME: passing the original request to page.serve is dodgy (particularly if page.serve has - # special treatment of POSTs). Ought to construct one that more or less matches what would be sent - # as a front-end GET request - response = page.serve(request) + # This view will generally be invoked as an AJAX request; as such, in the case of + # an error Django will return a plaintext response. This isn't what we want, since + # we will be writing the response back to an HTML page regardless of success or + # failure - as such, we strip out the X-Requested-With header to get Django to return + # an HTML error response + request.META.pop('HTTP_X_REQUESTED_WITH', None) + + response = page.serve(page.dummy_request()) response['X-Wagtail-Preview'] = 'ok' return response diff --git a/wagtail/wagtailcore/models.py b/wagtail/wagtailcore/models.py index b06137b9d..5c3c50f9b 100644 --- a/wagtail/wagtailcore/models.py +++ b/wagtail/wagtailcore/models.py @@ -1,5 +1,7 @@ import sys import os +from StringIO import StringIO +from urlparse import urlparse from modelcluster.models import ClusterableModel @@ -7,6 +9,8 @@ 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.handlers.wsgi import WSGIRequest +from django.core.handlers.base import BaseHandler from django.contrib.contenttypes.models import ContentType from django.contrib.auth.models import Group from django.conf import settings @@ -501,6 +505,41 @@ class Page(MP_Node, ClusterableModel, Indexed): user_perms = UserPagePermissionsProxy(user) return user_perms.for_page(self) + def dummy_request(self): + """ + Construct a HttpRequest object that is, as far as possible, representative of ones that would + receive this page as a response. Used for previewing / moderation and any other place where we + want to display a view of this page in the admin interface without going through the regular + page routing logic. + """ + url = self.full_url + if url: + url_info = urlparse(url) + hostname = url_info.netloc + path = url_info.path + port = url_info.port or 80 + else: + hostname = 'example.com' + path = '/' + port = 80 + + request = WSGIRequest({ + 'REQUEST_METHOD': 'GET', + 'PATH_INFO': path, + 'SERVER_NAME': hostname, + 'SERVER_PORT': port, + 'wsgi.input': StringIO(), + }) + + # Apply middleware to the request - see http://www.mellowmorning.com/2011/04/18/mock-django-request-for-testing/ + handler = BaseHandler() + handler.load_middleware() + for middleware_method in handler._request_middleware: + if middleware_method(request): + raise Exception("Couldn't create request mock object - " + "request middleware returned a response") + return request + def get_navigation_menu_items(): # Get all pages that appear in the navigation menu: ones which have children,