mirror of
https://github.com/Hopiu/wagtail.git
synced 2026-05-11 08:43:10 +00:00
commit
54ebf42b51
30 changed files with 273 additions and 164 deletions
|
|
@ -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
|
||||
|
|
@ -24,6 +25,7 @@ Changelog
|
|||
* Fix: Page slugs are now validated on page edit
|
||||
* Fix: Filter objects are cached to avoid a database hit every time an {% image %} tag is compiled
|
||||
* Fix: Moving or changing a site root page no longer causes URLs for subpages to change to 'None'
|
||||
* Fix: Eliminated raw SQL queries from wagtailcore / wagtailadmin, to ensure cross-database compatibility
|
||||
|
||||
0.2 (11.03.2014)
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ Contributors
|
|||
* Miguel Vieira
|
||||
* Ben Emery
|
||||
* David Smith
|
||||
* Ben Margolis
|
||||
|
||||
Translators
|
||||
===========
|
||||
|
|
|
|||
|
|
@ -102,12 +102,17 @@ Required dependencies
|
|||
=====================
|
||||
|
||||
- `pip <https://github.com/pypa/pip>`_
|
||||
- `libjpeg <http://ijg.org/>`_
|
||||
- `libxml2 <http://xmlsoft.org/>`_
|
||||
- `libxslt <http://xmlsoft.org/XSLT/>`_
|
||||
- `zlib <http://www.zlib.net/>`_
|
||||
|
||||
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`
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
83
docs/static_site_generation.rst
Normal file
83
docs/static_site_generation.rst
Normal file
|
|
@ -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_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_static_site_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_static_site_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
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
# Production-configured Wagtail installation
|
||||
# (secure services/account for full production use).
|
||||
# Production-configured Wagtail installation.
|
||||
# BUT, SECURE SERVICES/ACCOUNT FOR FULL PRODUCTION USE!
|
||||
# Tested on Debian 7.0.
|
||||
# Tom Dyson and Neal Todd
|
||||
|
||||
|
|
@ -42,6 +42,7 @@ aptitude -y install openjdk-7-jre-headless
|
|||
curl -O https://download.elasticsearch.org/elasticsearch/elasticsearch/elasticsearch-1.0.0.deb
|
||||
dpkg -i elasticsearch-1.0.0.deb
|
||||
rm elasticsearch-1.0.0.deb
|
||||
perl -pi -e"s/# network.host: 192.168.0.1/network.host: 127.0.0.1/" /etc/elasticsearch/elasticsearch.yml
|
||||
update-rc.d elasticsearch defaults 95 10
|
||||
service elasticsearch start
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
# Production-configured Wagtail installation
|
||||
# (secure services/account for full production use).
|
||||
# Production-configured Wagtail installation.
|
||||
# BUT, SECURE SERVICES/ACCOUNT FOR FULL PRODUCTION USE!
|
||||
# Tested on Ubuntu 13.04 and 13.10.
|
||||
# Tom Dyson and Neal Todd
|
||||
|
||||
|
|
@ -40,6 +40,7 @@ aptitude -y install openjdk-7-jre-headless
|
|||
curl -O https://download.elasticsearch.org/elasticsearch/elasticsearch/elasticsearch-1.0.0.deb
|
||||
dpkg -i elasticsearch-1.0.0.deb
|
||||
rm elasticsearch-1.0.0.deb
|
||||
perl -pi -e"s/# network.host: 192.168.0.1/network.host: 127.0.0.1/" /etc/elasticsearch/elasticsearch.yml
|
||||
update-rc.d elasticsearch defaults 95 10
|
||||
service elasticsearch start
|
||||
|
||||
|
|
|
|||
1
tox.ini
1
tox.ini
|
|
@ -3,7 +3,6 @@ dj16=
|
|||
Django>=1.6,<1.7
|
||||
pyelasticsearch==0.6.1
|
||||
elasticutils==0.8.2
|
||||
unittest2
|
||||
|
||||
[tox]
|
||||
envlist =
|
||||
|
|
|
|||
0
wagtail/contrib/__init__.py
Normal file
0
wagtail/contrib/__init__.py
Normal file
0
wagtail/contrib/wagtailmedusa/__init__.py
Normal file
0
wagtail/contrib/wagtailmedusa/__init__.py
Normal file
0
wagtail/contrib/wagtailmedusa/models.py
Normal file
0
wagtail/contrib/wagtailmedusa/models.py
Normal file
24
wagtail/contrib/wagtailmedusa/renderers.py
Normal file
24
wagtail/contrib/wagtailmedusa/renderers.py
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
from django_medusa.renderers import StaticSiteRenderer
|
||||
from wagtail.wagtailcore.models import Site
|
||||
from wagtail.wagtaildocs.models import Document
|
||||
|
||||
|
||||
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_static_site_paths()
|
||||
|
||||
|
||||
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]
|
||||
|
|
@ -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"),
|
||||
|
|
|
|||
|
|
@ -1,9 +1,20 @@
|
|||
from django.contrib.auth.models import User
|
||||
|
||||
# We need to make sure that we're using the same unittest library that Django uses internally
|
||||
# Otherwise, we get issues with the "SkipTest" and "ExpectedFailure" exceptions being recognised as errors
|
||||
try:
|
||||
# Firstly, try to import unittest from Django
|
||||
from django.utils import unittest
|
||||
except ImportError:
|
||||
# Django doesn't include unittest
|
||||
# We must be running on Django 1.7+ which doesn't support Python 2.6 so
|
||||
# the standard unittest library should be unittest2
|
||||
import unittest
|
||||
|
||||
|
||||
def login(client):
|
||||
# Create a user
|
||||
User.objects.create_superuser(username='test', email='test@email.com', password='password')
|
||||
|
||||
# Login
|
||||
client.login(username='test', password='password')
|
||||
client.login(username='test', password='password')
|
||||
|
|
|
|||
|
|
@ -115,7 +115,7 @@ img{
|
|||
}
|
||||
|
||||
.nav-wrapper{
|
||||
@include box-shadow(inset -2px 0px 10px 0px rgba(0, 0, 0, 0.5));
|
||||
@include box-shadow(inset -5px 0px 5px -3px rgba(0, 0, 0, 0.3));
|
||||
position:relative;
|
||||
background: $color-grey-1;
|
||||
margin-left: -100%;
|
||||
|
|
@ -404,7 +404,7 @@ header{
|
|||
width:1em;
|
||||
display:none;
|
||||
margin-right:0.4em;
|
||||
font-size:1.5em;
|
||||
font-size:1.3em;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -904,13 +904,8 @@ footer, .logo{
|
|||
.wrapper{
|
||||
max-width:$breakpoint-desktop-larger;
|
||||
}
|
||||
.nav-wrapper{
|
||||
@include box-shadow(inset -6px 0px 4px 0px rgba(0, 0, 0, 0.2));
|
||||
|
||||
.inner{
|
||||
background:$color-grey-1;
|
||||
@include box-shadow(inset -6px 0px 4px 0px rgba(0, 0, 0, 0.2));
|
||||
}
|
||||
.nav-wrapper .inner{
|
||||
background:$color-grey-1;
|
||||
}
|
||||
|
||||
footer{
|
||||
|
|
|
|||
|
|
@ -179,6 +179,7 @@ form{
|
|||
left: $desktop-nice-padding;
|
||||
margin-top: -1em;
|
||||
top: 50%;
|
||||
font-size:1.5em;
|
||||
}
|
||||
|
||||
.full{
|
||||
|
|
|
|||
|
|
@ -1,13 +0,0 @@
|
|||
{% extends "wagtailadmin/base.html" %}
|
||||
{% load i18n %}
|
||||
{% block titletag %}{% blocktrans with page_type=content_type.model_class.get_verbose_name %}Where do you want to create a {{ page_type }}{% endblocktrans %}{% endblock %}
|
||||
{% block content %}
|
||||
{% trans "Where do you want to create this" as where_str %}
|
||||
{% include "wagtailadmin/shared/header.html" with title=where_str subtitle=content_type.model_class.get_verbose_name icon="doc-empty-inverse" %}
|
||||
|
||||
<ul>
|
||||
{% for page in parent_pages %}
|
||||
<li><a href="{% url 'wagtailadmin_pages_create' content_type.app_label content_type.model page.id %}">{{ page.title }}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endblock %}
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
{% extends "wagtailadmin/base.html" %}
|
||||
{% load i18n %}
|
||||
{% block titletag %}{% trans "Create a new page" %}{% endblock %}
|
||||
{% block bodyclass %}menu-explorer{% endblock %}
|
||||
{% block content %}
|
||||
{% trans "Create a new page" as create_str %}
|
||||
{% include "wagtailadmin/shared/header.html" with title=create_str icon="doc-empty-inverse" %}
|
||||
|
||||
<div class="nice-padding">
|
||||
<p>{% trans "Your new page will be saved in the <em>top level</em> of your website. You can move it after saving." %}</p>
|
||||
|
||||
{% if all_page_types %}
|
||||
<ul class="listing">
|
||||
{% for content_type in all_page_types %}
|
||||
<li><a href="{% url 'wagtailadmin_pages_select_location' content_type.app_label content_type.model %}" class="icon icon-plus-inverse">{{ content_type.model_class.get_verbose_name }}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
|
@ -6,10 +6,6 @@
|
|||
{{ menu_item.render_html }}
|
||||
{% endfor %}
|
||||
|
||||
{% comment %}
|
||||
{# TODO: make this work #}
|
||||
<li><a href="{% url 'wagtailadmin_pages_select_type' %}" class="icon teal icon-plus-inverse">{% trans 'New page' %}</a></li>
|
||||
{% endcomment %}
|
||||
<li class="footer">
|
||||
<div class="avatar icon icon-user"><a href="{% url 'wagtailadmin_account' %}" title="{% trans 'Account settings' %}">{% if request.user.email %}<img src="{% gravatar_url request.user.email %}" />{% endif %}</a></div>
|
||||
<a href="{% url 'wagtailadmin_logout' %}">{% trans "Log out" %}</a>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
from django.test import TestCase
|
||||
import unittest2 as unittest
|
||||
from wagtail.tests.models import SimplePage, EventPage
|
||||
from wagtail.tests.utils import login
|
||||
from wagtail.tests.utils import login, unittest
|
||||
from wagtail.wagtailcore.models import Page
|
||||
from django.core.urlresolvers import reverse
|
||||
|
||||
|
|
@ -37,32 +36,6 @@ class TestPageExplorer(TestCase):
|
|||
self.assertTrue(response.context['pages'].filter(id=self.child_page.id).exists())
|
||||
|
||||
|
||||
class TestPageSelectTypeLocation(TestCase):
|
||||
def setUp(self):
|
||||
# Find root page
|
||||
self.root_page = Page.objects.get(id=2)
|
||||
|
||||
# Login
|
||||
login(self.client)
|
||||
|
||||
def test_select_type(self):
|
||||
response = self.client.get(reverse('wagtailadmin_pages_select_type'))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
@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)
|
||||
|
||||
def test_select_location_nonexistanttype(self):
|
||||
response = self.client.get(reverse('wagtailadmin_pages_select_location', args=('notanapp', 'notamodel')))
|
||||
self.assertEqual(response.status_code, 404)
|
||||
|
||||
def test_select_location_nonpagetype(self):
|
||||
response = self.client.get(reverse('wagtailadmin_pages_select_location', args=('wagtailimages', 'image')))
|
||||
self.assertEqual(response.status_code, 404)
|
||||
|
||||
|
||||
class TestPageCreation(TestCase):
|
||||
def setUp(self):
|
||||
# Find root page
|
||||
|
|
|
|||
|
|
@ -49,8 +49,6 @@ urlpatterns += [
|
|||
url(r'^pages/$', pages.index, name='wagtailadmin_explore_root'),
|
||||
url(r'^pages/(\d+)/$', pages.index, name='wagtailadmin_explore'),
|
||||
|
||||
url(r'^pages/new/$', pages.select_type, name='wagtailadmin_pages_select_type'),
|
||||
url(r'^pages/new/(\w+)/(\w+)/$', pages.select_location, name='wagtailadmin_pages_select_location'),
|
||||
url(r'^pages/new/(\w+)/(\w+)/(\d+)/$', pages.create, name='wagtailadmin_pages_create'),
|
||||
url(r'^pages/new/(\w+)/(\w+)/(\d+)/preview/$', pages.preview_on_create, name='wagtailadmin_pages_preview_on_create'),
|
||||
url(r'^pages/usage/(\w+)/(\w+)/$', pages.content_type_use, name='wagtailadmin_pages_type_use'),
|
||||
|
|
|
|||
|
|
@ -40,28 +40,6 @@ def index(request, parent_page_id=None):
|
|||
})
|
||||
|
||||
|
||||
@permission_required('wagtailadmin.access_admin')
|
||||
def select_type(request):
|
||||
# Get the list of page types that can be created within the pages that currently exist
|
||||
existing_page_types = ContentType.objects.raw("""
|
||||
SELECT DISTINCT content_type_id AS id FROM wagtailcore_page
|
||||
""")
|
||||
|
||||
all_page_types = sorted(get_page_types(), key=lambda pagetype: pagetype.name.lower())
|
||||
page_types = set()
|
||||
for content_type in existing_page_types:
|
||||
allowed_subpage_types = content_type.model_class().clean_subpage_types()
|
||||
for subpage_type in allowed_subpage_types:
|
||||
subpage_content_type = ContentType.objects.get_for_model(subpage_type)
|
||||
|
||||
page_types.add(subpage_content_type)
|
||||
|
||||
return render(request, 'wagtailadmin/pages/select_type.html', {
|
||||
'page_types': page_types,
|
||||
'all_page_types': all_page_types
|
||||
})
|
||||
|
||||
|
||||
@permission_required('wagtailadmin.access_admin')
|
||||
def add_subpage(request, parent_page_id):
|
||||
parent_page = get_object_or_404(Page, id=parent_page_id).specific
|
||||
|
|
@ -78,38 +56,6 @@ def add_subpage(request, parent_page_id):
|
|||
})
|
||||
|
||||
|
||||
@permission_required('wagtailadmin.access_admin')
|
||||
def select_location(request, content_type_app_name, content_type_model_name):
|
||||
try:
|
||||
content_type = ContentType.objects.get_by_natural_key(content_type_app_name, content_type_model_name)
|
||||
except ContentType.DoesNotExist:
|
||||
raise Http404
|
||||
|
||||
page_class = content_type.model_class()
|
||||
# page_class must be a Page type and not some other random model
|
||||
if not issubclass(page_class, Page):
|
||||
raise Http404
|
||||
|
||||
# find all the valid locations (parent pages) where a page of the chosen type can be added
|
||||
parent_pages = page_class.allowed_parent_pages()
|
||||
|
||||
if len(parent_pages) == 0:
|
||||
# user cannot create a page of this type anywhere - fail with an error
|
||||
messages.error(request, _("Sorry, you do not have access to create a page of type <em>'{0}'</em>.").format(content_type.name))
|
||||
return redirect('wagtailadmin_pages_select_type')
|
||||
elif len(parent_pages) == 1:
|
||||
# only one possible location - redirect them straight there
|
||||
messages.warning(request, _("Pages of this type can only be created as children of <em>'{0}'</em>. This new page will be saved there.").format(parent_pages[0].title))
|
||||
return redirect('wagtailadmin_pages_create', content_type_app_name, content_type_model_name, parent_pages[0].id)
|
||||
else:
|
||||
# prompt them to select a location
|
||||
return render(request, 'wagtailadmin/pages/select_location.html', {
|
||||
'content_type': content_type,
|
||||
'page_class': page_class,
|
||||
'parent_pages': parent_pages,
|
||||
})
|
||||
|
||||
|
||||
@permission_required('wagtailadmin.access_admin')
|
||||
def content_type_use(request, content_type_app_name, content_type_model_name):
|
||||
try:
|
||||
|
|
@ -136,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,
|
||||
|
|
|
|||
|
|
@ -622,6 +622,19 @@ class Page(MP_Node, ClusterableModel, Indexed):
|
|||
"""
|
||||
return self.serve(self.dummy_request())
|
||||
|
||||
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
|
||||
"""
|
||||
# Yield paths for this page
|
||||
yield '/'
|
||||
|
||||
# Yield paths for child pages
|
||||
for child in self.get_children().live():
|
||||
for path in child.specific.get_static_site_paths():
|
||||
yield '/' + child.slug + path
|
||||
|
||||
def get_ancestors(self, inclusive=False):
|
||||
return Page.objects.ancestor_of(self, inclusive)
|
||||
|
||||
|
|
@ -638,17 +651,9 @@ def get_navigation_menu_items():
|
|||
# or are at the top-level (this rule required so that an empty site out-of-the-box has a working menu)
|
||||
navigable_content_type_ids = get_navigable_page_content_type_ids()
|
||||
if navigable_content_type_ids:
|
||||
pages = Page.objects.raw("""
|
||||
SELECT * FROM wagtailcore_page
|
||||
WHERE numchild > 0 OR content_type_id IN %s OR depth = 2
|
||||
ORDER BY path
|
||||
""", [tuple(navigable_content_type_ids)])
|
||||
pages = Page.objects.filter(Q(content_type__in=navigable_content_type_ids)|Q(depth=2)|Q(numchild__gt=0)).order_by('path')
|
||||
else:
|
||||
pages = Page.objects.raw("""
|
||||
SELECT * FROM wagtailcore_page
|
||||
WHERE numchild > 0 OR depth = 2
|
||||
ORDER BY path
|
||||
""")
|
||||
pages = Page.objects.filter(Q(depth=2)|Q(numchild__gt=0)).order_by('path')
|
||||
|
||||
# Turn this into a tree structure:
|
||||
# tree_node = (page, children)
|
||||
|
|
|
|||
|
|
@ -162,6 +162,47 @@ class TestServeView(TestCase):
|
|||
self.assertContains(response, '<a href="/events/christmas/">Christmas</a>')
|
||||
|
||||
|
||||
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']
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import os.path
|
|||
from taggit.managers import TaggableManager
|
||||
|
||||
from django.core.files import File
|
||||
from django.core.exceptions import ImproperlyConfigured, ObjectDoesNotExist, ValidationError
|
||||
from django.core.exceptions import ImproperlyConfigured, ObjectDoesNotExist
|
||||
from django.db import models
|
||||
from django.db.models.signals import pre_delete
|
||||
from django.dispatch.dispatcher import receiver
|
||||
|
|
@ -17,6 +17,8 @@ from unidecode import unidecode
|
|||
|
||||
from wagtail.wagtailadmin.taggable import TagSearchable
|
||||
from wagtail.wagtailimages.backends import get_image_backend
|
||||
from .utils import validate_image_format
|
||||
|
||||
|
||||
class AbstractImage(models.Model, TagSearchable):
|
||||
title = models.CharField(max_length=255, verbose_name=_('Title') )
|
||||
|
|
@ -34,12 +36,7 @@ class AbstractImage(models.Model, TagSearchable):
|
|||
filename = prefix[:-1] + dot + extension
|
||||
return os.path.join(folder_name, filename)
|
||||
|
||||
def file_extension_validator(ffile):
|
||||
extension = ffile.name.split(".")[-1].lower()
|
||||
if extension not in ["gif", "jpg", "jpeg", "png"]:
|
||||
raise ValidationError(_("Not a valid image format. Please use a gif, jpeg or png file instead."))
|
||||
|
||||
file = models.ImageField(verbose_name=_('File'), upload_to=get_upload_to, width_field='width', height_field='height', validators=[file_extension_validator])
|
||||
file = models.ImageField(verbose_name=_('File'), upload_to=get_upload_to, width_field='width', height_field='height', validators=[validate_image_format])
|
||||
width = models.IntegerField(editable=False)
|
||||
height = models.IntegerField(editable=False)
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
|
|
|
|||
|
|
@ -4,9 +4,7 @@ from django.contrib.auth.models import User, Group, Permission
|
|||
from django.core.urlresolvers import reverse
|
||||
from django.core.files.uploadedfile import SimpleUploadedFile
|
||||
|
||||
import unittest2 as unittest
|
||||
|
||||
from wagtail.tests.utils import login
|
||||
from wagtail.tests.utils import login, unittest
|
||||
from wagtail.wagtailimages.models import get_image_model
|
||||
from wagtail.wagtailimages.templatetags import image_tags
|
||||
|
||||
|
|
|
|||
28
wagtail/wagtailimages/utils.py
Normal file
28
wagtail/wagtailimages/utils.py
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
import os
|
||||
|
||||
from PIL import Image
|
||||
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
|
||||
def validate_image_format(f):
|
||||
# Check file extension
|
||||
extension = os.path.splitext(f.name)[1].lower()[1:]
|
||||
|
||||
if extension == 'jpg':
|
||||
extension = 'jpeg'
|
||||
|
||||
if extension not in ['gif', 'jpeg', 'png']:
|
||||
raise ValidationError(_("Not a valid image. Please use a gif, jpeg or png file with the correct file extension."))
|
||||
|
||||
if not f.closed:
|
||||
# Open image file
|
||||
file_position = f.tell()
|
||||
f.seek(0)
|
||||
image = Image.open(f)
|
||||
f.seek(file_position)
|
||||
|
||||
# Check that the internal format matches the extension
|
||||
if image.format.upper() != extension.upper():
|
||||
raise ValidationError(_("Not a valid %s image. Please use a gif, jpeg or png file with the correct file extension.") % (extension.upper()))
|
||||
|
|
@ -2,7 +2,7 @@ from django.test import TestCase
|
|||
from django.test.utils import override_settings
|
||||
from django.conf import settings
|
||||
from django.core import management
|
||||
import unittest2 as unittest
|
||||
from wagtail.tests.utils import unittest
|
||||
from wagtail.wagtailsearch import models, get_search_backend
|
||||
from wagtail.wagtailsearch.backends.db import DBSearch
|
||||
from wagtail.wagtailsearch.backends import InvalidSearchBackendError
|
||||
|
|
|
|||
|
|
@ -1,9 +1,8 @@
|
|||
from django.test import TestCase
|
||||
from django.core import management
|
||||
from wagtail.wagtailsearch import models
|
||||
from wagtail.tests.utils import login
|
||||
from wagtail.tests.utils import login, unittest
|
||||
from StringIO import StringIO
|
||||
import unittest2 as unittest
|
||||
|
||||
|
||||
class TestHitCounter(TestCase):
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ when you run "manage.py test".
|
|||
Replace this with more appropriate tests for your application.
|
||||
"""
|
||||
|
||||
import unittest2 as unittest
|
||||
from wagtail.tests.utils import unittest
|
||||
|
||||
from django.test import TestCase
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue