From 6092791b864c736a4c443b47780414ee5951fcf2 Mon Sep 17 00:00:00 2001 From: Karl Hobley Date: Fri, 16 May 2014 12:17:07 +0100 Subject: [PATCH 01/15] Added get_medusa_paths method to Page This is used to find a list of URLs for the entire site to feed into django-medusa If your page requires extra views, you can override this like so: def get_medusa_paths(self): yield '/myextraview/' super(MyPageType, self).get_medusa_paths() --- wagtail/wagtailcore/models.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/wagtail/wagtailcore/models.py b/wagtail/wagtailcore/models.py index 441c74a28..ac22ebc92 100644 --- a/wagtail/wagtailcore/models.py +++ b/wagtail/wagtailcore/models.py @@ -622,6 +622,19 @@ class Page(MP_Node, ClusterableModel, Indexed): """ return self.serve(self.dummy_request()) + def get_medusa_paths(self): + """ + This is a generator of URL paths to feed into django-medusa + Override this if you would like to create static versions of subpages + """ + # Yield paths for this page + yield '/' + + # Yield paths for child pages + for child in self.get_children().live(): + for path in child.get_medusa_paths(): + yield '/' + child.slug + path + def get_ancestors(self, inclusive=False): return Page.objects.ancestor_of(self, inclusive) From d180ad8f089911b3791048ea5f5586880ead5fd3 Mon Sep 17 00:00:00 2001 From: Karl Hobley Date: Fri, 16 May 2014 12:27:05 +0100 Subject: [PATCH 02/15] Added medusa renderer for pages --- wagtail/wagtailcore/renderers.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 wagtail/wagtailcore/renderers.py diff --git a/wagtail/wagtailcore/renderers.py b/wagtail/wagtailcore/renderers.py new file mode 100644 index 000000000..33339581a --- /dev/null +++ b/wagtail/wagtailcore/renderers.py @@ -0,0 +1,17 @@ +from django_medusa.renderers import StaticSiteRenderer +from .models import Site + + +class PageRenderer(StaticSiteRenderer): + def get_paths(self): + # Get site + # TODO: Find way to get this to work with other sites + site = Site.objects.filter(is_default_site=True).first() + if site is None: + return [] + + # Return list of paths + return site.root_page.get_medusa_paths() + + +renderers = [PageRenderer] From d364f95413c4f73265525c42a0b3e7d4a668ccb4 Mon Sep 17 00:00:00 2001 From: Karl Hobley Date: Mon, 19 May 2014 14:03:11 +0100 Subject: [PATCH 03/15] Fixed typo --- wagtail/wagtailcore/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wagtail/wagtailcore/models.py b/wagtail/wagtailcore/models.py index ac22ebc92..6de87c44c 100644 --- a/wagtail/wagtailcore/models.py +++ b/wagtail/wagtailcore/models.py @@ -632,7 +632,7 @@ class Page(MP_Node, ClusterableModel, Indexed): # Yield paths for child pages for child in self.get_children().live(): - for path in child.get_medusa_paths(): + for path in child.specific.get_medusa_paths(): yield '/' + child.slug + path def get_ancestors(self, inclusive=False): From e2930ab6bc9e2b522b6e127943efe7e30e645a42 Mon Sep 17 00:00:00 2001 From: Karl Hobley Date: Mon, 19 May 2014 16:45:56 +0100 Subject: [PATCH 04/15] Added contrib folder --- wagtail/contrib/__init__.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 wagtail/contrib/__init__.py diff --git a/wagtail/contrib/__init__.py b/wagtail/contrib/__init__.py new file mode 100644 index 000000000..e69de29bb From 4207a0b3b824b3b03d8d708a016b6d5eacbe11f2 Mon Sep 17 00:00:00 2001 From: Karl Hobley Date: Mon, 19 May 2014 16:51:08 +0100 Subject: [PATCH 05/15] Moved medusa renders into their own app in contrib --- wagtail/contrib/wagtailmedusa/__init__.py | 0 wagtail/contrib/wagtailmedusa/models.py | 0 wagtail/{wagtailcore => contrib/wagtailmedusa}/renderers.py | 2 +- 3 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 wagtail/contrib/wagtailmedusa/__init__.py create mode 100644 wagtail/contrib/wagtailmedusa/models.py rename wagtail/{wagtailcore => contrib/wagtailmedusa}/renderers.py (90%) diff --git a/wagtail/contrib/wagtailmedusa/__init__.py b/wagtail/contrib/wagtailmedusa/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/wagtail/contrib/wagtailmedusa/models.py b/wagtail/contrib/wagtailmedusa/models.py new file mode 100644 index 000000000..e69de29bb diff --git a/wagtail/wagtailcore/renderers.py b/wagtail/contrib/wagtailmedusa/renderers.py similarity index 90% rename from wagtail/wagtailcore/renderers.py rename to wagtail/contrib/wagtailmedusa/renderers.py index 33339581a..a11566b5e 100644 --- a/wagtail/wagtailcore/renderers.py +++ b/wagtail/contrib/wagtailmedusa/renderers.py @@ -1,5 +1,5 @@ from django_medusa.renderers import StaticSiteRenderer -from .models import Site +from wagtail.wagtailcore.models import Site class PageRenderer(StaticSiteRenderer): From f4040a648d0554c9e1c6da00ecbc661a29fdebe3 Mon Sep 17 00:00:00 2001 From: Karl Hobley Date: Mon, 19 May 2014 16:55:48 +0100 Subject: [PATCH 06/15] Added medusa renderer for documents --- wagtail/contrib/wagtailmedusa/renderers.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/wagtail/contrib/wagtailmedusa/renderers.py b/wagtail/contrib/wagtailmedusa/renderers.py index a11566b5e..19c4eb89f 100644 --- a/wagtail/contrib/wagtailmedusa/renderers.py +++ b/wagtail/contrib/wagtailmedusa/renderers.py @@ -1,5 +1,6 @@ from django_medusa.renderers import StaticSiteRenderer from wagtail.wagtailcore.models import Site +from wagtail.wagtaildocs.models import Document class PageRenderer(StaticSiteRenderer): @@ -14,4 +15,10 @@ class PageRenderer(StaticSiteRenderer): return site.root_page.get_medusa_paths() -renderers = [PageRenderer] +class DocumentRenderer(StaticSiteRenderer): + def get_paths(self): + # Return list of paths to documents + return (doc.url for doc in Document.objects.all()) + + +renderers = [PageRenderer, DocumentRenderer] From b5d4c0e78a0e25b8ef49ebdf9bdfa94240ac02e9 Mon Sep 17 00:00:00 2001 From: Karl Hobley Date: Mon, 19 May 2014 17:10:19 +0100 Subject: [PATCH 07/15] Renamed get_medusa_paths to get_staticsite_paths --- wagtail/contrib/wagtailmedusa/renderers.py | 2 +- wagtail/wagtailcore/models.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/wagtail/contrib/wagtailmedusa/renderers.py b/wagtail/contrib/wagtailmedusa/renderers.py index 19c4eb89f..ee0ccf4f8 100644 --- a/wagtail/contrib/wagtailmedusa/renderers.py +++ b/wagtail/contrib/wagtailmedusa/renderers.py @@ -12,7 +12,7 @@ class PageRenderer(StaticSiteRenderer): return [] # Return list of paths - return site.root_page.get_medusa_paths() + return site.root_page.get_staticsite_paths() class DocumentRenderer(StaticSiteRenderer): diff --git a/wagtail/wagtailcore/models.py b/wagtail/wagtailcore/models.py index 6de87c44c..d453684bd 100644 --- a/wagtail/wagtailcore/models.py +++ b/wagtail/wagtailcore/models.py @@ -622,9 +622,9 @@ class Page(MP_Node, ClusterableModel, Indexed): """ return self.serve(self.dummy_request()) - def get_medusa_paths(self): + def get_staticsite_paths(self): """ - This is a generator of URL paths to feed into django-medusa + This is a generator of URL paths to feed into a static site generator Override this if you would like to create static versions of subpages """ # Yield paths for this page @@ -632,7 +632,7 @@ class Page(MP_Node, ClusterableModel, Indexed): # Yield paths for child pages for child in self.get_children().live(): - for path in child.specific.get_medusa_paths(): + for path in child.specific.get_staticsite_paths(): yield '/' + child.slug + path def get_ancestors(self, inclusive=False): From 26183e608bb1c4c8df8838879a74c1b43d91a4da Mon Sep 17 00:00:00 2001 From: Matt Westcott Date: Tue, 20 May 2014 11:05:19 +0100 Subject: [PATCH 08/15] mention some more optional/required dependencies in 'getting started' docs --- docs/gettingstarted.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/gettingstarted.rst b/docs/gettingstarted.rst index e4d2d6484..626aa42c0 100644 --- a/docs/gettingstarted.rst +++ b/docs/gettingstarted.rst @@ -102,12 +102,17 @@ Required dependencies ===================== - `pip `_ +- `libjpeg `_ +- `libxml2 `_ +- `libxslt `_ +- `zlib `_ Optional dependencies ===================== - `PostgreSQL`_ - `Elasticsearch`_ +- `Redis`_ Installation ============ @@ -137,6 +142,7 @@ with a regular Django project. .. _the Wagtail codebase: https://github.com/torchbox/wagtail .. _PostgreSQL: http://www.postgresql.org .. _Elasticsearch: http://www.elasticsearch.org +.. _Redis: http://redis.io/ _`Remove the demo app` ~~~~~~~~~~~~~~~~~~~~~~ From 9564cbf39f1366dbb0f99f904982c0e50356fc31 Mon Sep 17 00:00:00 2001 From: Karl Hobley Date: Tue, 20 May 2014 11:17:49 +0100 Subject: [PATCH 09/15] Added document describing how to generate a static website --- docs/static_site_generation.rst | 83 +++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 docs/static_site_generation.rst diff --git a/docs/static_site_generation.rst b/docs/static_site_generation.rst new file mode 100644 index 000000000..dbdf40eb2 --- /dev/null +++ b/docs/static_site_generation.rst @@ -0,0 +1,83 @@ +Generating a static site +======================== + +This document describes how to render your Wagtail site into static HTML files using `django medusa`_ and the 'wagtail.contrib.wagtailmedusa' module. + + +Installing django-medusa +~~~~~~~~~~~~~~~~~~~~~~~~ + +Firstly, install django medusa from pip: + +.. code:: + + pip install django-medusa + + +Then add 'django_medusa' and 'wagtail.contrib.wagtailmedusa' to INSTALLED_APPS: + +.. code:: python + + INSTALLED_APPS = [ + ... + 'django_medusa', + 'wagtail.contrib.wagtailmedusa', + ] + + +Replacing GET parameters with custom routing +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Pages which require GET parameters (eg, pagination) don't generate suitable filenames for generated HTML files so they need to be changed to use custom routing instead. + +For example, lets say we have a Blog Index which uses pagination. We can override the 'route' method to make it respond on urls like '/page/1' and pass the page number through to the serve method: + +.. code:: python + + class BlogIndex(Page): + ... + + def serve(self, request, page=1): + ... + + def route(self, request, path_components): + if self.live and len(path_components) == 2 and path_components[0] == 'page': + try: + return self.serve(request, page=int(path_components[1])) + except (TypeError, ValueError): + pass + + return super(BlogIndex, self).route(request, path_components) + + +Rendering pages which use custom routing +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +For page types that override the route method, we need to let django medusa know which URLs it responds on. This is done by overriding the 'get_staticsite_paths' method to make it yield one string per URL path. + +For example, the BlogIndex above would need to yield one URL for each page of results: + +.. code:: python + + def get_staticsite_paths(self): + # Get page count + page_count = ... + + # Yield a path for each page + for page in range(page_count): + yield '/%d/' % (page + 1) + + # Yield from superclass + for path in super(BlogIndex, self).get_staticsite_paths(): + yield path + + +Rendering +~~~~~~~~~ + +To render a site, just run ``./manage.py staticsitegen``. This will render the entire website and place the HTML in a folder called 'medusa_output'. The static and media folders need to be copied into this folder manually after the rendering is complete. + +To test, open the 'medusa_output' folder in a terminal and run ``python -m SimpleHTTPServer``. + + +.. _django medusa: https://github.com/mtigas/django-medusa From c194a55304a5e9b45cf53f133502cfdcb820ec42 Mon Sep 17 00:00:00 2001 From: Karl Hobley Date: Tue, 20 May 2014 12:39:23 +0100 Subject: [PATCH 10/15] Renamed get_staticsite_paths to get_static_site_paths --- docs/static_site_generation.rst | 6 +++--- wagtail/contrib/wagtailmedusa/renderers.py | 2 +- wagtail/wagtailcore/models.py | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/static_site_generation.rst b/docs/static_site_generation.rst index dbdf40eb2..66bd23ff5 100644 --- a/docs/static_site_generation.rst +++ b/docs/static_site_generation.rst @@ -53,13 +53,13 @@ For example, lets say we have a Blog Index which uses pagination. We can overrid Rendering pages which use custom routing ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -For page types that override the route method, we need to let django medusa know which URLs it responds on. This is done by overriding the 'get_staticsite_paths' method to make it yield one string per URL path. +For page types that override the route method, we need to let django medusa know which URLs it responds on. This is done by overriding the 'get_static_site_paths' method to make it yield one string per URL path. For example, the BlogIndex above would need to yield one URL for each page of results: .. code:: python - def get_staticsite_paths(self): + def get_static_site_paths(self): # Get page count page_count = ... @@ -68,7 +68,7 @@ For example, the BlogIndex above would need to yield one URL for each page of re yield '/%d/' % (page + 1) # Yield from superclass - for path in super(BlogIndex, self).get_staticsite_paths(): + for path in super(BlogIndex, self).get_static_site_paths(): yield path diff --git a/wagtail/contrib/wagtailmedusa/renderers.py b/wagtail/contrib/wagtailmedusa/renderers.py index ee0ccf4f8..950e13a14 100644 --- a/wagtail/contrib/wagtailmedusa/renderers.py +++ b/wagtail/contrib/wagtailmedusa/renderers.py @@ -12,7 +12,7 @@ class PageRenderer(StaticSiteRenderer): return [] # Return list of paths - return site.root_page.get_staticsite_paths() + return site.root_page.get_static_site_paths() class DocumentRenderer(StaticSiteRenderer): diff --git a/wagtail/wagtailcore/models.py b/wagtail/wagtailcore/models.py index d453684bd..17b88198e 100644 --- a/wagtail/wagtailcore/models.py +++ b/wagtail/wagtailcore/models.py @@ -622,7 +622,7 @@ class Page(MP_Node, ClusterableModel, Indexed): """ return self.serve(self.dummy_request()) - def get_staticsite_paths(self): + def get_static_site_paths(self): """ This is a generator of URL paths to feed into a static site generator Override this if you would like to create static versions of subpages @@ -632,7 +632,7 @@ class Page(MP_Node, ClusterableModel, Indexed): # Yield paths for child pages for child in self.get_children().live(): - for path in child.specific.get_staticsite_paths(): + for path in child.specific.get_static_site_paths(): yield '/' + child.slug + path def get_ancestors(self, inclusive=False): From 61779be5c96f62c0c2e99c99e91b8a6cf9caf4c1 Mon Sep 17 00:00:00 2001 From: Karl Hobley Date: Tue, 20 May 2014 13:04:47 +0100 Subject: [PATCH 11/15] Added unit tests for get_static_site_paths --- wagtail/tests/models.py | 42 ++++++++++++++++++++++++++++++++++-- wagtail/wagtailcore/tests.py | 41 +++++++++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+), 2 deletions(-) diff --git a/wagtail/tests/models.py b/wagtail/tests/models.py index 9124cb253..97858df4d 100644 --- a/wagtail/tests/models.py +++ b/wagtail/tests/models.py @@ -1,4 +1,5 @@ from django.db import models +from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger from modelcluster.fields import ParentalKey from wagtail.wagtailcore.models import Page, Orderable from wagtail.wagtailcore.fields import RichTextField @@ -187,11 +188,48 @@ class EventIndex(Page): intro = RichTextField(blank=True) ajax_template = 'tests/includes/event_listing.html' - def get_context(self, request): + def get_events(self): + return self.get_children().live().type(EventPage) + + def get_paginator(self): + return Paginator(self.get_events(), 4) + + def get_context(self, request, page=1): + # Pagination + paginator = self.get_paginator() + try: + events = paginator.page(page) + except PageNotAnInteger: + events = paginator.page(1) + except EmptyPage: + events = paginator.page(paginator.num_pages) + + # Update context context = super(EventIndex, self).get_context(request) - context['events'] = EventPage.objects.filter(live=True) + context['events'] = events return context + def route(self, request, path_components): + if self.live and len(path_components) == 1: + try: + return self.serve(request, page=int(path_components[0])) + except (TypeError, ValueError): + pass + + return super(EventIndex, self).route(request, path_components) + + def get_static_site_paths(self): + # Get page count + page_count = self.get_paginator().num_pages + + # Yield a path for each page + for page in range(page_count): + yield '/%d/' % (page + 1) + + # Yield from superclass + for path in super(EventIndex, self).get_static_site_paths(): + yield path + EventIndex.content_panels = [ FieldPanel('title', classname="full title"), FieldPanel('intro', classname="full"), diff --git a/wagtail/wagtailcore/tests.py b/wagtail/wagtailcore/tests.py index 757221a29..db498650a 100644 --- a/wagtail/wagtailcore/tests.py +++ b/wagtail/wagtailcore/tests.py @@ -162,6 +162,47 @@ class TestServeView(TestCase): self.assertContains(response, 'Christmas') +class TestStaticSitePaths(TestCase): + def setUp(self): + self.root_page = Page.objects.get(id=1) + + # For simple tests + self.home_page = self.root_page.add_child(instance=SimplePage(title="Homepage", slug="home")) + self.about_page = self.home_page.add_child(instance=SimplePage(title="About us", slug="about")) + self.contact_page = self.home_page.add_child(instance=SimplePage(title="Contact", slug="contact")) + + # For custom tests + self.event_index = self.root_page.add_child(instance=EventIndex(title="Events", slug="events")) + for i in range(20): + self.event_index.add_child(instance=EventPage(title="Event " + str(i), slug="event" + str(i))) + + def test_local_static_site_paths(self): + paths = list(self.about_page.get_static_site_paths()) + + self.assertEqual(paths, ['/']) + + def test_child_static_site_paths(self): + paths = list(self.home_page.get_static_site_paths()) + + self.assertEqual(paths, ['/', '/about/', '/contact/']) + + def test_custom_static_site_paths(self): + paths = list(self.event_index.get_static_site_paths()) + + # Event index path + expected_paths = ['/'] + + # One path for each page of results + expected_paths.extend(['/' + str(i + 1) + '/' for i in range(5)]) + + # One path for each event page + expected_paths.extend(['/event' + str(i) + '/' for i in range(20)]) + + paths.sort() + expected_paths.sort() + self.assertEqual(paths, expected_paths) + + class TestPageUrlTags(TestCase): fixtures = ['test.json'] From 9d65cf8f12c6374d417cadbb8a3c0fef82340ef4 Mon Sep 17 00:00:00 2001 From: Karl Hobley Date: Tue, 20 May 2014 13:48:02 +0100 Subject: [PATCH 12/15] Added changelog entry --- CHANGELOG.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.txt b/CHANGELOG.txt index a0024b6a0..7e5cb1be1 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -5,6 +5,7 @@ Changelog ~~~~~~~~~~~~~~~~ * Added toolbar to allow logged-in users to add and edit pages from the site front-end * Support for alternative image processing backends such as Wand, via the WAGTAILIMAGES_BACKENDS setting + * Added support for generating static sites using django-medusa * Added custom Query set for Pages with some handy methods for querying pages * Editor's guide documentation * Editor interface now outputs form media CSS / JS, to support custom widgets with assets From c349f06d6adcd363d254e9def094ce8043aad18d Mon Sep 17 00:00:00 2001 From: Tom Dyson Date: Tue, 20 May 2014 17:22:19 +0100 Subject: [PATCH 13/15] Missing underline for new static generation docs --- docs/static_site_generation.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/static_site_generation.rst b/docs/static_site_generation.rst index 66bd23ff5..fe4b504ea 100644 --- a/docs/static_site_generation.rst +++ b/docs/static_site_generation.rst @@ -26,7 +26,7 @@ Then add 'django_medusa' and 'wagtail.contrib.wagtailmedusa' to INSTALLED_APPS: Replacing GET parameters with custom routing -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Pages which require GET parameters (eg, pagination) don't generate suitable filenames for generated HTML files so they need to be changed to use custom routing instead. From 431931b6a9551d02b642b5abb54f04fd315b6e0e Mon Sep 17 00:00:00 2001 From: Tom Dyson Date: Tue, 20 May 2014 17:24:16 +0100 Subject: [PATCH 14/15] Include static site generation docs in doc index --- docs/index.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/index.rst b/docs/index.rst index dbfe91f33..5b2b62bc4 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -13,6 +13,7 @@ It supports Django 1.6.2+ on Python 2.6 and 2.7. Django 1.7 and Python 3 support wagtail_search deploying performance + static_site_generation contributing support roadmap From 1f8e8a062bb1ea86c4ec73f0ac6e931cd4ab23ef Mon Sep 17 00:00:00 2001 From: Karl Hobley Date: Wed, 21 May 2014 13:44:21 +0100 Subject: [PATCH 15/15] Removed stray print statement --- wagtail/wagtailadmin/views/pages.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/wagtail/wagtailadmin/views/pages.py b/wagtail/wagtailadmin/views/pages.py index a827b6cab..308f6fdd4 100644 --- a/wagtail/wagtailadmin/views/pages.py +++ b/wagtail/wagtailadmin/views/pages.py @@ -82,8 +82,6 @@ def content_type_use(request, content_type_app_name, content_type_model_name): except EmptyPage: pages = paginator.page(paginator.num_pages) - print page_class - return render(request, 'wagtailadmin/pages/content_type_use.html', { 'pages': pages, 'app_name': content_type_app_name,