pull/534/head
Chris Rogers 2014-08-11 17:57:04 +01:00
commit bf1175523d
843 zmienionych plików z 80112 dodań i 27263 usunięć

1
.gitignore vendored
Wyświetl plik

@ -5,3 +5,4 @@
/MANIFEST
/wagtail.egg-info/
/docs/_build/
/.tox/

Wyświetl plik

@ -1,10 +1,12 @@
# Python releases to test
language: python
# Test matrix
python:
- 2.7
# Django releases
- 2.7
- 3.2
- 3.4
env:
- DJANGO_VERSION=Django==1.6.2
- DJANGO_VERSION=Django==1.6.5
#- DJANGO_VERSION=Django==1.7.0
# Services
services:
- redis-server
@ -12,7 +14,7 @@ services:
# Package installation
install:
- python setup.py install
- pip install psycopg2 pyelasticsearch elasticutils wand
- pip install psycopg2 elasticsearch wand embedly mock python-dateutil
- pip install coveralls
# Pre-test configuration
before_script:

Wyświetl plik

@ -1,6 +1,109 @@
Changelog
=========
0.5 (01.08.2014)
~~~~~~~~~~~~~~~~
* Added multiple image uploader
* Added support for face and feature detection on images using the OpenCV library
* Added RoutablePage model to allow embedding Django-style URL routing within a page
* Added image/document/snippet usage stats
* Explorer nav now rendered separately and fetched with AJAX when needed
* Added decorator syntax for hooks
* Replaced lxml dependency with html5lib, to simplify installation
* Added page_unpublished signal
* Added mechanism to obtain external URLs to images, at any size
* Added Copy Page action to the explorer
* Fix: Updates to tag fields are now properly committed to the database when publishing directly from the page edit interface
0.4.1 (14.07.2014)
~~~~~~~~~~~~~~~~~~
* ElasticSearch backend now respects the backward-compatible URLS configuration setting, in addition to HOSTS
* Documentation fixes
0.4 (10.07.2014)
~~~~~~~~~~~~~~~~
* ElasticUtils/pyelasticsearch swapped for elasticsearch-py
* Python 3.2, 3.3 and 3.4 support
* Added scheduled publishing
* Added support for private (password-protected) pages
* Added frontend cache invalidator
* Added sitemap generator
* Added notification preferences
* Added a new way to configure searchable/filterable fields on models
* Added 'original' as a resizing rule supported by the 'image' tag
* Hallo.js updated to version 1.0.4
* Snippets are now ordered alphabetically
* Removed the "More" section from the admin menu
* Added pagination to page listings in admin
* Support for setting a subpage_types property on page models, to define which page types are allowed as subpages
* Added a new datetime picker widget
* Added styleguide (mainly for wagtail developers)
* Aesthetic improvements to preview experience
* 'image' tag now accepts extra keyword arguments to be output as attributes on the img tag
* Login screen redirects to dashboard if user is already logged in
* Renamed some template tag libraries
* Any extra arguments given to serve are now passed through to get_context and get_template
* Added an 'attrs' property to image rendition objects to output src, width, height and alt attributes all in one go
* Added 'construct_whitelister_element_rules' hook for customising the HTML whitelist used when saving rich text fields
* Added 'in_menu' and 'not_in_menu' methods to PageQuerySet
* Added 'get_next_siblings' and 'get_prev_siblings' to Page
* Added init_new_page signal
* Added page_published signal
* Added copy method to Page to allow copying of pages
* Added ``search`` method to ``PageQuerySet``
* Added ``get_indexed_objects`` allowing developers to customise which objects get added to the search index
* Major refactor of Elasticsearch backend
* Use ``match`` instead of ``query_string`` queries
* Fields are now indexed in Elasticsearch with their correct type
* Filter fields are no longer included in '_all' (in Elasticsearch)
* Fields with partial matching are now indexed together into '_partials'
* Fix: Animated GIFs are now coalesced before resizing
* Fix: Wand backend clones images before modifying them
* Fix: Admin breadcrumb now positioned correctly on mobile
* Fix: Page chooser breadcrumb now updates the chooser modal instead of linking to Explorer
* Fix: Embeds - Fixed crash when no HTML field is sent back from the embed provider
* Fix: Multiple sites with same hostname but different ports are now allowed
* Fix: No longer possible to create multiple sites with is_default_site = True
0.3.1 (03.06.2014)
~~~~~~~~~~~~~~~~~~
* Fix: When constructing dummy requests for pages with no routable URL, fall back on a hostname from ALLOWED_HOSTS and finally 'localhost', to avoid 'Invalid HTTP_HOST header' errors on preview when DEBUG=False.
* Fix: Ensure that url_path is populated when previewing a newly created page, to avoid unnecessarily taking the above fallback.
* Fix: Deleting an item from an InlinePanel, then generating a validation error on saving, no longer causes the deleted item to confusingly reappear with an error of its own.
0.3 (28.05.2014)
~~~~~~~~~~~~~~~~
* 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
* Added 'wagtailforms' module for creating form pages on a site, and handling form submissions
* Editor's guide documentation
* Expanded developer documentation
* Editor interface now outputs form media CSS / JS, to support custom widgets with assets
* Migrations and user management now correctly handle custom AUTH_USER_MODEL settings
* Added 'slugurl' template tag to output the URL of a page with a given slug
* MultiFieldPanel definitions now accept a 'classname' attribute, including a special classname of 'collapsible' to allow showing / hiding them on click
* Added 'insert_editor_css' and 'insert_editor_js' hooks for passing in custom CSS / JS to the editor interface
* Made JPEG compression level configurable through the IMAGE_COMPRESSION_QUALITY setting, and increased default to 85
* Added document_served signal which gets fired when a document is downloaded
* Added translations for Portuguese Brazil and Traditional Chinese (Taiwan).
* Made compatible with Python 2.6
* 'richtext' template filter now wraps output in <div class="rich-text"></div>, to assist in styling
* Embeds now save author_name and provider_name if set by oEmbed provider
* Fix: non-ASCII characters in image filenames are now converted into ASCII equivalents rather than becoming all underscores
* Fix: paths to fonts and images within CSS are no longer hard-coded to /static/
* Fix: Localization files for the JQuery UI datepicker are stored locally and only imported when a localization is known to be available
* 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
* Fix: Snippets menu item is hidden for administrators if no snippet types are defined
* Fix: 'Upload' tab in image chooser now retains focus if submit action returns a form error.
* Fix: Search input now appears on image chooser after form validation error.
0.2 (11.03.2014)
~~~~~~~~~~~~~~~~
* SQLite support added

Wyświetl plik

@ -1,8 +1,8 @@
Original Authors
================
* Matthew Westcott matthew.westcott@torchbox.com
* David Cranwell david.cranwell@torchbox.com
* Matthew Westcott matthew.westcott@torchbox.com twitter: @gasmanic
* David Cranwell david.cranwell@torchbox.com twitter: @davecranwell
* Karl Hobley karl.hobley@torchbox.com
* Helen Chapman helen.chapman@torchbox.com
@ -21,6 +21,15 @@ Contributors
* Lewis Cowper
* Stephen Newey
* Ryan Foster
* v1kku
* Miguel Vieira
* Ben Emery
* David Smith
* Ben Margolis
* Tom Talbot
* Jeffrey Hearn
* Robert Clark
* Tim Heap
Translators
===========
@ -28,12 +37,14 @@ Translators
* Basque: Unai Zalakain
* Bulgarian: Lyuboslav Petrov
* Catalan: David Llop
* Chinese: Lihan Li
* Chinese: Lihan Li, tulpar008, wwj718
* French: Sylvain Fankhauser
* Galician: fooflare
* German: Karl Sander, Johannes Spielmann
* Greek: Serafeim Papastefanos
* Mongolian: Delgermurun Purevkhuu
* Polish: Łukasz Bołdys
* Portuguese Brazil: Gilson Filho
* Romanian: Dan Braghis
* Spanish: Unai Zalakain, fooflare
* Traditional Chinese (Taiwan): wdv4758h

Wyświetl plik

@ -1,10 +1,10 @@
.. image:: https://travis-ci.org/torchbox/wagtail.png?branch=master
:target: https://travis-ci.org/torchbox/wagtail
.. image:: https://coveralls.io/repos/torchbox/wagtail/badge.png?branch=master
:target: https://coveralls.io/r/torchbox/wagtail?branch=master
.. image:: https://coveralls.io/repos/torchbox/wagtail/badge.png?branch=master&zxcv1
:target: https://coveralls.io/r/torchbox/wagtail?branch=master
.. image:: https://pypip.in/v/wagtail/badge.png?asdf
.. image:: https://pypip.in/v/wagtail/badge.png?zxcv
:target: https://crate.io/packages/wagtail/
Wagtail CMS
@ -24,9 +24,11 @@ Wagtail is a Django content management system built originally for the `Royal Co
* Support for tree-based content organisation
* Optional preview->submit->approve workflow
* Fast out of the box. `Varnish <https://www.varnish-cache.org/>`_-friendly if you need it
* Tests! But not enough; we're working hard to improve this
* A simple `form builder <http://docs.wagtail.io/en/latest/form_builder.html>`_
* Optional `static site generation <http://docs.wagtail.io/en/latest/static_site_generation.html>`_
* Excellent `test coverage <https://coveralls.io/r/torchbox/wagtail?branch=master>`_
Find out more at `wagtail.io <http://wagtail.io/>`_. Documentation is at `wagtail.readthedocs.org <http://wagtail.readthedocs.org/>`_.
Find out more at `wagtail.io <http://wagtail.io/>`_.
Got a question? Ask it on our `Google Group <https://groups.google.com/forum/#!forum/wagtail>`_.
@ -35,7 +37,26 @@ Getting started
* To get you up and running quickly, we've provided a demonstration site with all the configuration in place, at `github.com/torchbox/wagtaildemo <https://github.com/torchbox/wagtaildemo/>`_; see the `README <https://github.com/torchbox/wagtaildemo/blob/master/README.md>`_ for installation instructions.
* See the `Getting Started <http://wagtail.readthedocs.org/en/latest/gettingstarted.html#getting-started>`_ docs for installation (with the demo app) on a fresh Debian/Ubuntu box with production-ready dependencies, on OS X and on a Vagrant box.
* `Serafeim Papastefanos <https://github.com/spapas>`_ has written a `tutorial <http://spapas.github.io/2014/02/13/wagtail-tutorial/>`_ with all the steps to build a simple Wagtail site from scratch.
* We've also provided a skeletal django-template to get started on a blank site: https://github.com/torchbox/wagtail-template
Documentation
~~~~~~~~~~~~~
Available at `wagtail.readthedocs.org <http://wagtail.readthedocs.org/>`_ and always being updated.
Compatibility
~~~~~~~~~~~~~
Wagtail supports Django 1.6.2+ on Python 2.6, 2.7, 3.2, 3.3 and 3.4.
Django 1.7 support is in progress pending further release candidate testing.
Wagtail's dependencies are summarised at `requirements.io <https://requires.io/github/torchbox/wagtail/requirements>`_.
Contributing
~~~~~~~~~~~~
If you're a Python or Django developer, fork the repo and get stuck in! Send us a useful pull request and we'll post you a `t-shirt <https://twitter.com/WagtailCMS/status/432166799464210432/photo/1>`_. Our immediate priorities are better docs, more tests, internationalisation and localisation.
If you're a Python or Django developer, fork the repo and get stuck in!
We suggest you start by checking the `Help develop me! <https://github.com/torchbox/wagtail/issues?labels=Help+develop+me%21>`_ label and the `coding guidelines <http://wagtail.readthedocs.org/en/latest/contributing.html#coding-guidelines>`_.
Send us a useful pull request and we'll post you a `t-shirt <https://twitter.com/WagtailCMS/status/432166799464210432/photo/1>`_.
We also welcome `translations <http://wagtail.readthedocs.org/en/latest/contributing.html#translations>`_ for Wagtail's interface.

Wyświetl plik

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 3.5 KiB

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 3.5 KiB

Wyświetl plik

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 3.7 KiB

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 3.7 KiB

Wyświetl plik

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 29 KiB

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 29 KiB

Wyświetl plik

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 67 KiB

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 67 KiB

Wyświetl plik

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 41 KiB

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 41 KiB

Wyświetl plik

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 44 KiB

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 44 KiB

Wyświetl plik

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 32 KiB

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 32 KiB

Wyświetl plik

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 6.9 KiB

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 6.9 KiB

Wyświetl plik

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 9.5 KiB

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 9.5 KiB

Wyświetl plik

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 11 KiB

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 11 KiB

Wyświetl plik

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 6.8 KiB

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 6.8 KiB

Wyświetl plik

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 37 KiB

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 37 KiB

Wyświetl plik

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 36 KiB

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 36 KiB

Wyświetl plik

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 3.9 KiB

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 3.9 KiB

Wyświetl plik

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 4.9 KiB

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 4.9 KiB

Wyświetl plik

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 3.8 KiB

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 3.8 KiB

Wyświetl plik

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 3.5 KiB

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 3.5 KiB

Wyświetl plik

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 4.0 KiB

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 4.0 KiB

Wyświetl plik

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 4.0 KiB

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 4.0 KiB

Wyświetl plik

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 3.9 KiB

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 3.9 KiB

Wyświetl plik

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 74 KiB

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 74 KiB

Wyświetl plik

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 68 KiB

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 68 KiB

Wyświetl plik

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 29 KiB

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 29 KiB

Wyświetl plik

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 6.3 KiB

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 6.3 KiB

Wyświetl plik

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 43 KiB

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 43 KiB

Wyświetl plik

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 111 KiB

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 111 KiB

Wyświetl plik

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 40 KiB

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 40 KiB

Wyświetl plik

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 216 KiB

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 216 KiB

Wyświetl plik

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 30 KiB

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 30 KiB

Wyświetl plik

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 19 KiB

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 19 KiB

Wyświetl plik

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 117 KiB

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 117 KiB

Wyświetl plik

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 10 KiB

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 10 KiB

Wyświetl plik

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 22 KiB

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 22 KiB

Wyświetl plik

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 6.2 KiB

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 6.2 KiB

Wyświetl plik

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 37 KiB

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 37 KiB

Wyświetl plik

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 10 KiB

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 10 KiB

Wyświetl plik

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 3.6 KiB

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 3.6 KiB

Wyświetl plik

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 25 KiB

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 25 KiB

Wyświetl plik

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 22 KiB

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 22 KiB

Wyświetl plik

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 35 KiB

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 35 KiB

Wyświetl plik

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 272 KiB

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 272 KiB

Wyświetl plik

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 194 KiB

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 194 KiB

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 19 KiB

Wyświetl plik

@ -1,6 +0,0 @@
Building your site
==================
Serafeim Papastefanos has written a comprehensive tutorial on creating a site from scratch in Wagtail; for the time being, this is our recommended resource:
`spapas.github.io/2014/02/13/wagtail-tutorial/ <http://spapas.github.io/2014/02/13/wagtail-tutorial/>`_

Wyświetl plik

@ -15,6 +15,7 @@
import sys
import os
# on_rtd is whether we are on readthedocs.org, this line of code grabbed from docs.readthedocs.org
on_rtd = os.environ.get('READTHEDOCS', None) == 'True'
@ -26,7 +27,15 @@ if not on_rtd: # only import and set the theme if we're building docs locally
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#sys.path.insert(0, os.path.abspath('.'))
sys.path.insert(0, os.path.abspath('..'))
# Autodoc may need to import some models modules which require django settings
# be configured
os.environ['DJANGO_SETTINGS_MODULE'] = 'wagtail.tests.settings'
# Use SQLite3 database engine so it doesn't attempt to use psycopg2 on RTD
os.environ['DATABASE_ENGINE'] = 'django.db.backends.sqlite3'
# -- General configuration ------------------------------------------------
@ -36,7 +45,9 @@ if not on_rtd: # only import and set the theme if we're building docs locally
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = []
extensions = [
'sphinx.ext.autodoc',
]
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
@ -51,7 +62,7 @@ source_suffix = '.rst'
master_doc = 'index'
# General information about the project.
project = u'Wagtail Documentation'
project = u'Wagtail'
copyright = u'2014, Torchbox'
# The version info for the project you're documenting, acts as replacement for
@ -59,9 +70,9 @@ copyright = u'2014, Torchbox'
# built documents.
#
# The short X.Y version.
version = '0.1'
version = '0.5'
# The full version, including alpha/beta/rc tags.
release = '0.1'
release = '0.5'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.

Wyświetl plik

@ -0,0 +1,121 @@
.. _frontend_cache_purging:
Frontend cache purging
======================
.. versionadded:: 0.4
Many websites use a frontend cache such as Varnish, Squid or Cloudflare to gain extra performance. The downside of using a frontend cache though is that they don't respond well to updating content and will often keep an old version of a page cached after it has been updated.
This document describes how to configure Wagtail to purge old versions of pages from a frontend cache whenever a page gets updated.
Setting it up
~~~~~~~~~~~~~
Firstly, add ``"wagtail.contrib.wagtailfrontendcache"`` to your INSTALLED_APPS:
.. code-block:: python
INSTALLED_APPS = [
...
"wagtail.contrib.wagtailfrontendcache"
]
The ``wagtailfrontendcache`` module provides a set of signal handlers which will automatically purge the cache whenever a page is published or deleted. You should register these somewhere at the top of your ``urls.py`` file:
.. code-block:: python
# urls.py
from wagtail.contrib.wagtailfrontendcache.signal_handlers import register_signal_handlers
register_signal_handlers()
You then need to set the ``WAGTAILFRONTENDCACHE_LOCATION`` setting to the URL of your Varnish/Squid cache server. This must be a direct connection to the server and cannot go through another proxy. By default, this is set to ``http://127.0.0.1:8000`` which is very likely incorrect.
Finally, make sure you have configured your frontend cache to accept PURGE requests:
- `Varnish <https://www.varnish-cache.org/docs/3.0/tutorial/purging.html>`_
- `Squid <http://wiki.squid-cache.org/SquidFaq/OperatingSquid#How_can_I_purge_an_object_from_my_cache.3F>`_
Advanced usage
~~~~~~~~~~~~~~
Purging more than one URL per page
----------------------------------
By default, Wagtail will only purge one URL per page. If your page has more than one URL to be purged, you will need to override the ``get_cached_paths`` method on your page type.
.. code-block:: python
class BlogIndexPage(Page):
def get_blog_items(self):
# This returns a Django paginator of blog items in this section
return Paginator(self.get_children().live().type(BlogPage), 10)
def get_cached_paths(self):
# Yield the main URL
yield '/'
# Yield one URL per page in the paginator to make sure all pages are purged
for page_number in range(1, self.get_blog_items().num_pages):
yield '/?page=' + str(page_number)
Purging index pages
-------------------
Another problem is pages that list other pages (such as a blog index) will not be purged when a blog entry gets added, changed or deleted. You may want to purge the blog index page so the updates are added into the listing quickly.
This can be solved by using the ``purge_page_from_cache`` utility function which can be found in the ``wagtail.contrib.wagtailfrontendcache.utils`` module.
Let's take the the above BlogIndexPage as an example. We need to register a signal handler to run when one of the BlogPages get updated/deleted. This signal handler should call the ``purge_page_from_cache`` function on all BlogIndexPages that contain the BlogPage being updated/deleted.
.. code-block:: python
# models.py
from django.db.models.signals import pre_delete
from wagtail.wagtailcore.signals import page_published
from wagtail.contrib.wagtailfrontendcache.utils import purge_page_from_cache
...
def blog_page_changed(blog_page):
# Find all the live BlogIndexPages that contain this blog_page
for blog_index in BlogIndexPage.objects.live():
if blog_page in blog_index.get_blog_items().object_list:
# Purge this blog index
purge_page_from_cache(blog_index)
@register(page_published, sender=BlogPage):
def blog_published_handler(instance):
blog_page_changed(instance)
@register(pre_delete, sender=BlogPage)
def blog_deleted_handler(instance):
blog_page_changed(instance)
Purging individual URLs
-----------------------
``wagtail.contrib.wagtailfrontendcache.utils`` provides another utils function called ``purge_url_from_cache``. As the name suggests, this purges an individual URL from the cache.
For example, this could be useful for purging a single page of blogs:
.. code-block:: python
from wagtail.contrib.wagtailfrontendcache.utils import purge_url_from_cache
# Purge the first page of the blog index
purge_url_from_cache(blog_index.url + '?page=1')

Wyświetl plik

@ -0,0 +1,11 @@
Contrib components
==================
.. toctree::
:maxdepth: 2
static_site_generation
sitemap_generation
frontend_cache_purging

Wyświetl plik

@ -0,0 +1,62 @@
.. _sitemap_generation:
Sitemap generation
==================
.. versionadded:: 0.4
This document describes how to create XML sitemaps for your Wagtail website using the ``wagtail.contrib.wagtailsitemaps`` module.
Basic configuration
~~~~~~~~~~~~~~~~~~~
You firstly need to add ``"wagtail.contrib.wagtailsitemaps"`` to INSTALLED_APPS in your Django settings file:
.. code-block:: python
INSTALLED_APPS = [
...
"wagtail.contrib.wagtailsitemaps",
]
Then, in urls.py, you need to add a link to the ``wagtail.contrib.wagtailsitemaps.views.sitemap`` view which generates the sitemap:
.. code-block:: python
from wagtail.contrib.wagtailsitemaps.views import sitemap
urlpatterns = patterns('',
...
url('^sitemap\.xml$', sitemap),
)
You should now be able to browse to "/sitemap.xml" and see the sitemap working. By default, all published pages in your website will be added to the site map.
Customising
~~~~~~~~~~~
URLs
----
The Page class defines a ``get_sitemap_urls`` method which you can override to customise sitemaps per page instance. This method must return a list of dictionaries, one dictionary per URL entry in the sitemap. You can exclude pages from the sitemap by returning an empty list.
Each dictionary can contain the following:
- **location** (required) - This is the full URL path to add into the sitemap.
- **lastmod** - A python date or datetime set to when the page was last modified.
- **changefreq**
- **priority**
You can add more but you will need to override the ``wagtailsitemaps/sitemap.xml`` template in order for them to be displayed in the sitemap.
Cache
-----
By default, sitemaps are cached for 100 minutes. You can change this by setting ``WAGTAILSITEMAPS_CACHE_TIMEOUT`` in your Django settings to the number of seconds you would like the cache to last for.

Wyświetl plik

@ -0,0 +1,88 @@
Generating a static site
========================
This document describes how to render your Wagtail site into static HTML files on your local filesystem, Amazon S3 or Google App Engine, using `django medusa`_ and the ``wagtail.contrib.wagtailmedusa`` module.
Installing django-medusa
~~~~~~~~~~~~~~~~~~~~~~~~
First, 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',
]
Rendering
~~~~~~~~~
To render a site, 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. This feature inherits django-medusa's ability to render your static site to Amazon S3 or Google App Engine; see the `medusa docs <https://github.com/mtigas/django-medusa/blob/master/README.markdown>`_ for configuration details.
To test, open the 'medusa_output' folder in a terminal and run ``python -m SimpleHTTPServer``.
Advanced topics
~~~~~~~~~~~~~~~
Replacing GET parameters with custom routing
--------------------------------------------
Pages which require GET parameters (e.g. for pagination) don't generate suitable filenames for generated HTML files so they need to be changed to use custom routing instead.
For example, let's 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
from wagtail.wagtailcore.url_routing import RouteResult
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 RouteResult(self, kwargs={'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
.. _django medusa: https://github.com/mtigas/django-medusa

Wyświetl plik

@ -0,0 +1,72 @@
.. _form_builder:
Form builder
============
The `wagtailforms` module allows you to set up single-page forms, such as a 'Contact us' form, as pages of a Wagtail site. It provides a set of base models that site implementors can extend to create their own 'Form' page type with their own site-specific templates. Once a page type has been set up in this way, editors can build forms within the usual page editor, consisting of any number of fields. Form submissions are stored for later retrieval through a new 'Forms' section within the Wagtail admin interface; in addition, they can be optionally e-mailed to an address specified by the editor.
Usage
~~~~~
Add 'wagtail.wagtailforms' to your INSTALLED_APPS:
.. code:: python
INSTALLED_APPS = [
...
'wagtail.wagtailforms',
]
Within the models.py of one of your apps, create a model that extends wagtailforms.models.AbstractEmailForm:
.. code:: python
from wagtail.wagtailforms.models import AbstractEmailForm, AbstractFormField
class FormField(AbstractFormField):
page = ParentalKey('FormPage', related_name='form_fields')
class FormPage(AbstractEmailForm):
intro = RichTextField(blank=True)
thank_you_text = RichTextField(blank=True)
FormPage.content_panels = [
FieldPanel('title', classname="full title"),
FieldPanel('intro', classname="full"),
InlinePanel(FormPage, 'form_fields', label="Form fields"),
FieldPanel('thank_you_text', classname="full"),
MultiFieldPanel([
FieldPanel('to_address', classname="full"),
FieldPanel('from_address', classname="full"),
FieldPanel('subject', classname="full"),
], "Email")
]
AbstractEmailForm defines the fields 'to_address', 'from_address' and 'subject', and expects form_fields to be defined. Any additional fields are treated as ordinary page content - note that FormPage is responsible for serving both the form page itself and the landing page after submission, so the model definition should include all necessary content fields for both of those views.
If you do not want your form page type to offer form-to-email functionality, you can inherit from AbstractForm instead of AbstractEmailForm, and omit the 'to_address', 'from_address' and 'subject' fields from the content_panels definition.
You now need to create two templates named form_page.html and form_page_landing.html (where 'form_page' is the underscore-formatted version of the class name). form_page.html differs from a standard Wagtail template in that it is passed a variable 'form', containing a Django form object, in addition to the usual 'self' variable. A very basic template for the form would thus be:
.. code:: html
{% load pageurl rich_text %}
<html>
<head>
<title>{{ self.title }}</title>
</head>
<body>
<h1>{{ self.title }}</h1>
{{ self.intro|richtext }}
<form action="{% pageurl self %}" method="POST">
{% csrf_token %}
{{ form.as_p }}
<input type="submit">
</form>
</body>
</html>
form_page_landing.html is a regular Wagtail template, displayed after the user makes a successful form submission.

Wyświetl plik

@ -0,0 +1,88 @@
.. _image_feature_detection:
=================
Feature Detection
=================
Wagtail has the ability to automatically detect faces and features inside your images and crop the images to those features.
Feature detection uses OpenCV to detect faces/features in an image when the image is uploaded. The detected features stored internally as a focal point in the ``focal_point_{x, y, width, height}`` fields on the ``Image`` model. These fields are used by the ``fill`` image filter when an image is rendered in a template to crop the image.
Setup
=====
Feature detection requires OpenCV which can be a bit tricky to install as it's not currently pip-installable.
Installing OpenCV on Debian/Ubuntu
----------------------------------
Debian and ubuntu provide an apt-get package called ``python-opencv``:
.. code-block:: bash
sudo apt-get install python-opencv python-numpy
This will install PyOpenCV into your site packages. If you are using a virtual environment, you need to make sure site packages are enabled or Wagtail will not be able to import PyOpenCV.
Enabling site packages in the virtual environment
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
If you are not using a virtual envionment, you can skip this step.
Enabling site packages is different depending on whether you are using pyvenv (Python 3.3+ only) or virtualenv to manage your virtual environment.
pyvenv
``````
Go into your pyvenv directory and open the ``pyvenv.cfg`` file then set ``include-system-site-packages`` to ``true``.
virtualenv
``````````
Go into your virtualenv directory and delete a file called ``lib/python-x.x/no-global-site-packages.txt``.
Testing the OpenCV installation
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
You can test that OpenCV can be seen by Wagtail by opening up a python shell (with your virtual environment active) and typing:
.. code-block:: python
import cv
If you don't see an ``ImportError``, it worked. (If you see the error ``libdc1394 error: Failed to initialize libdc1394``, this is harmless and can be ignored.)
Switching on feature detection in Wagtail
-----------------------------------------
Once OpenCV is installed, you need to set the ``WAGTAILIMAGES_FEATURE_DETECTION_ENABLED`` setting to ``True``:
.. code-block:: python
# settings.py
WAGTAILIMAGES_FEATURE_DETECTION_ENABLED = True
Manually running feature detection
----------------------------------
Feature detection runs when new images are uploaded in to Wagtail. If you already have images in your Wagtail site and would like to run feature detection on them, you will have to run it manually.
You can manually run feature detection on all images by running the following code in the python shell:
.. code-block:: python
from wagtail.wagtailimages.models import Image
for image in Image.objects.all():
if image.focal_point is None:
image.focal_point = image.get_suggested_focal_point()
image.save()

Wyświetl plik

@ -0,0 +1,9 @@
Images
======
.. toctree::
:maxdepth: 2
using_images_outside_wagtail
feature_detection

Wyświetl plik

@ -0,0 +1,82 @@
.. _using_images_outside_wagtail:
============================
Using images outside Wagtail
============================
Wagtail provides a way for you to generate external URLs for images in your image library which you can use to display your images on external sites.
Setup
=====
Add an entry in your URLs configuration for ``wagtail.wagtailimages.urls``:
.. code-block:: python
from wagtail.wagtailimages import urls as wagtailimages_urls
urlpatterns = patterns('',
...
url(r'^images/', include(wagtailimages_urls)),
...
)
Generating URLs for images
==========================
Once the above setup is done, a button should appear under the image preview on the image edit page. Clicking this button will take you to an interface where you can specify the size you want the image to be, and it will generate a URL and a preview of what the image is going to look like.
The filter box lets you choose how you would like the image to be resized:
.. glossary::
``Original``
Leaves the image at its original size - no resizing is performed.
``Resize to max``
Fit **within** the given dimensions.
The longest edge will be reduced to the equivalent dimension size defined. e.g A portrait image of width 1000, height 2000, treated with the ``max`` dimensions ``1000x500`` (landscape) would result in the image shrunk so the *height* was 500 pixels and the width 250.
``Resize to min``
**Cover** the given dimensions.
This may result in an image slightly **larger** than the dimensions you specify. e.g A square image of width 2000, height 2000, treated with the ``min`` dimensions ``500x200`` (landscape) would have it's height and width changed to 500, i.e matching the width required, but greater than the height.
``Resize to width``
Reduces the width of the image to the dimension specified.
``Resize to height``
Resize the height of the image to the dimension specified..
``Resize to fill``
Resize and **crop** to fill the **exact** dimensions.
This can be particularly useful for websites requiring square thumbnails of arbitrary images. For example, a landscape image of width 2000, height 1000, treated with ``fill`` dimensions ``200x200`` would have its height reduced to 200, then its width (ordinarily 400) cropped to 200.
Using the URLs on your website or blog
======================================
Once you have generated a URL, you can put it straight into the ``src`` attribute of an ``<img>`` tag:
..code-block:: html
<img src="(image url here)">
Performance
===========
Currently, Wagtail will regenerate the image every time it is requested. For high volume sites, it is recommended to use a frontend cache to reduce load on the backend servers.

Wyświetl plik

@ -0,0 +1,12 @@
Core components
===============
.. toctree::
:maxdepth: 2
pages/index
images/index
snippets
search/index
form_builder

Wyświetl plik

@ -0,0 +1,68 @@
.. _private_pages:
Private pages
=============
.. versionadded:: 0.4
Users with publish permission on a page can set it to be private by clicking the 'Privacy' control in the top right corner of the page explorer or editing interface, and setting a password. Users visiting this page, or any of its subpages, will be prompted to enter a password before they can view the page.
Private pages work on Wagtail out of the box - the site implementer does not need to do anything to set them up. However, the default "password required" form is only a bare-bones HTML page, and site implementers may wish to replace this with a page customised to their site design.
Setting up a global "password required" page
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
By setting ``PASSWORD_REQUIRED_TEMPLATE`` in your Django settings file, you can specify the path of a template which will be used for all "password required" forms on the site (except for page types that specifically override it - see below):
.. code-block:: python
PASSWORD_REQUIRED_TEMPLATE = 'myapp/password_required.html'
This template will receive the same set of context variables that the blocked page would pass to its own template via ``get_context()`` - including ``self`` to refer to the page object itself - plus the following additional variables (which override any of the page's own context variables of the same name):
- **form** - A Django form object for the password prompt; this will contain a field named ``password`` as its only visible field. A number of hidden fields may also be present, so the page must loop over ``form.hidden_fields`` if not using one of Django's rendering helpers such as ``form.as_p``.
- **action_url** - The URL that the password form should be submitted to, as a POST request.
A basic template suitable for use as ``PASSWORD_REQUIRED_TEMPLATE`` might look like this:
.. code-block:: django
<!DOCTYPE HTML>
<html>
<head>
<title>Password required</title>
</head>
<body>
<h1>Password required</h1>
<p>You need a password to access this page.</p>
<form action="{{ action_url }}" method="POST">
{% csrf_token %}
{{ form.non_field_errors }}
<div>
{{ form.password.errors }}
{{ form.password.label_tag }}
{{ form.password }}
</div>
{% for field in form.hidden_fields %}
{{ field }}
{% endfor %}
<input type="submit" value="Continue" />
</form>
</body>
</html>
Setting a "password required" page for a specific page type
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The attribute ``password_required_template`` can be defined on a page model to use a custom template for the "password required" view, for that page type only. For example, if a site had a page type for displaying embedded videos along with a description, it might choose to use a custom "password required" template that displays the video description as usual, but shows the password form in place of the video embed.
.. code-block:: python
class VideoPage(Page):
...
password_required_template = 'video/password_required.html'

Wyświetl plik

@ -0,0 +1,180 @@
=====================
Page Queryset Methods
=====================
All models that inherit from ``Page`` are given some extra Queryset methods accessible from their ``.objects`` attribute.
Examples
========
- Selecting only live pages
.. code-block:: python
live_pages = Page.objects.live()
- Selecting published EventPages that are descendants of events_index
.. code-block:: python
events = EventPage.objects.live().descendant_of(events_index)
- Getting a list of menu items
.. code-block:: python
# This gets a queryset of live children of the homepage with ``show_in_menus`` set
menu_items = homepage.get_children().live().in_menu()
Reference
=========
.. automodule:: wagtail.wagtailcore.query
.. autoclass:: PageQuerySet
.. automethod:: live
Example:
.. code-block:: python
published_pages = Page.objects.live()
.. automethod:: not_live
Example:
.. code-block:: python
unpublished_pages = Page.objects.not_live()
.. automethod:: in_menu
Example:
.. code-block:: python
# Build a menu from live pages that are children of the homepage
menu_items = homepage.get_children().live().in_menu()
.. note::
To put your page in menus, set the show_in_menus flag to true:
.. code-block:: python
# Add 'my_page' to the menu
my_page.show_in_menus = True
.. automethod:: page
Example:
.. code-block:: python
# Append an extra page to a queryset
new_queryset = old_queryset | Page.objects.page(page_to_add)
.. automethod:: not_page
Example:
.. code-block:: python
# Remove a page from a queryset
new_queryset = old_queryset & Page.objects.not_page(page_to_remove)
.. automethod:: descendant_of
Example:
.. code-block:: python
# Get EventPages that are under the special_events Page
special_events = EventPage.objects.descendant_of(special_events_index)
# Alternative way
special_events = special_events_index.get_descendants()
.. automethod:: not_descendant_of
Example:
.. code-block:: python
# Get EventPages that are not under the archived_events Page
non_archived_events = EventPage.objects.not_descendant_of(archived_events_index)
.. automethod:: child_of
Example:
.. code-block:: python
# Get a list of sections
sections = Page.objects.child_of(homepage)
# Alternative way
sections = homepage.get_children()
.. automethod:: ancestor_of
Example:
.. code-block:: python
# Get the current section
current_section = Page.objects.ancestor_of(current_page).child_of(homepage).first()
# Alternative way
current_section = current_page.get_ancestors().child_of(homepage).first()
.. automethod:: not_ancestor_of
Example:
.. code-block:: python
# Get the other sections
other_sections = Page.objects.not_ancestor_of(current_page).child_of(homepage)
.. automethod:: sibling_of
Example:
.. code-block:: python
# Get list of siblings
siblings = Page.objects.sibling_of(current_page)
# Alternative way
siblings = current_page.get_siblings()
.. automethod:: public
See: :ref:`private_pages`
.. note::
This doesn't filter out unpublished pages. If you want to only have published public pages, use ``.live().public()``
Example:
.. code-block:: python
# Find all the pages that are viewable by the public
all_pages = Page.objects.live().public()
.. automethod:: search
See: :ref:`wagtailsearch_for_python_developers`
Example:
.. code-block:: python
# Search future events
results = EventPage.objects.live().filter(date__gt=timezone.now()).search("Hello")

Wyświetl plik

@ -0,0 +1,88 @@
.. _routable_page:
====================================
Embedding URL configuration in Pages
====================================
.. versionadded:: 0.5
The ``RoutablePage`` class provides a convenient way for a page to respond on multiple sub-URLs with different views. For example, a blog section on a site might provide several different types of index page at URLs like ``/blog/2013/06/``, ``/blog/authors/bob/``, ``/blog/tagged/python/``, all served by the same ``BlogIndex`` page.
A ``RoutablePage`` exists within the page tree like any other page, but URL paths underneath it are checked against a list of patterns, using Django's urlconf scheme. If none of the patterns match, control is passed to subpages as usual (or failing that, a 404 error is thrown).
The basics
==========
To use ``RoutablePage``, you need to make your class inherit from :class:`wagtail.contrib.wagtailroutablepage.models.RoutablePage` and configure the ``subpage_urls`` attribute with your URL configuration.
Here's an example of an ``EventPage`` with three views:
.. code-block:: python
from django.conf.urls import url
from wagtail.contrib.wagtailroutablepage.models import RoutablePage
class EventPage(RoutablePage):
subpage_urls = (
url(r'^$', 'current_events', name='current_events'),
url(r'^past/$', 'past_events', name='past_events'),
url(r'^year/(\d+)/$', 'events_for_year', name='events_for_year'),
)
def current_events(self, request):
"""
View function for the current events page
"""
...
def past_events(self, request):
"""
View function for the current events page
"""
...
def events_for_year(self, request):
"""
View function for the events for year page
"""
...
The ``RoutablePage`` class
==========================
.. automodule:: wagtail.contrib.wagtailroutablepage.models
.. autoclass:: RoutablePage
.. autoattribute:: subpage_urls
Example:
.. code-block:: python
from django.conf.urls import url
subpage_urls = (
url(r'^$', 'serve', name='main'),
url(r'^archive/$', 'archive', name='archive'),
)
.. automethod:: resolve_subpage
Example:
.. code-block:: python
view, args, kwargs = page.resolve_subpage('/past/')
response = view(request, *args, **kwargs)
.. automethod:: reverse_subpage
Example:
.. code-block:: python
url = page.url + page.reverse_subpage('events_for_year', args=('2014', ))

Wyświetl plik

@ -0,0 +1,134 @@
====================
Creating page models
====================
The Page Class
~~~~~~~~~~~~~~
``Page`` uses Django's model interface, so you can include any field type and field options that Django allows. Wagtail provides some fields and editing handlers that simplify data entry in the Wagtail admin interface, so you may want to keep those in mind when deciding what properties to add to your models in addition to those already provided by ``Page``.
Built-in Properties of the Page Class
-------------------------------------
Wagtail provides some properties in the ``Page`` class which are common to most webpages. Since you'll be subclassing ``Page``, you don't have to worry about implementing them.
Public Properties
`````````````````
``title`` (string, required)
Human-readable title for the content
``slug`` (string, required)
Machine-readable URL component for this piece of content. The name of the page as it will appear in URLs e.g ``http://domain.com/blog/[my-slug]/``
``seo_title`` (string)
Alternate SEO-crafted title which overrides the normal title for use in the ``<head>`` of a page
``search_description`` (string)
A SEO-crafted description of the content, used in both internal search indexing and for the meta description read by search engines
The ``Page`` class actually has alot more to it, but these are probably the only built-in properties you'll need to worry about when creating templates for your models.
Anatomy of a Wagtail Model
~~~~~~~~~~~~~~~~~~~~~~~~~~
So what does a Wagtail model definition look like? Here's a model representing a typical blog post:
.. code-block:: python
from django.db import models
from wagtail.wagtailcore.models import Page
from wagtail.wagtailcore.fields import RichTextField
from wagtail.wagtailadmin.edit_handlers import FieldPanel
from wagtail.wagtailimages.edit_handlers import ImageChooserPanel
from wagtail.wagtailimages.models import Image
class BlogPage(Page):
body = RichTextField()
date = models.DateField("Post date")
feed_image = models.ForeignKey(
'wagtailimages.Image',
null=True,
blank=True,
on_delete=models.SET_NULL,
related_name='+'
)
BlogPage.content_panels = [
FieldPanel('title', classname="full title"),
FieldPanel('date'),
FieldPanel('body', classname="full"),
]
BlogPage.promote_panels = [
FieldPanel('slug'),
FieldPanel('seo_title'),
FieldPanel('show_in_menus'),
FieldPanel('search_description'),
ImageChooserPanel('feed_image'),
]
To keep track of your ``Page``-derived models, it might be helpful to include "Page" as the last part of your class name. ``BlogPage`` defines three properties: ``body``, ``date``, and ``feed_image``. These are a mix of basic Django models (``DateField``), Wagtail fields (``RichTextField``), and a pointer to a Wagtail model (``Image``).
Next, the ``content_panels`` and ``promote_panels`` lists define the capabilities and layout of the Wagtail admin page edit interface. The lists are filled with "panels" and "choosers", which will provide a fine-grain interface for inputting the model's content. The ``ImageChooserPanel``, for instance, lets one browse the image library, upload new images, and input image metadata. The ``RichTextField`` is the basic field for creating web-ready website rich text, including text formatting and embedded media like images and video. The Wagtail admin offers other choices for fields, Panels, and Choosers, with the option of creating your own to precisely fit your content without workarounds or other compromises.
Your models may be even more complex, with methods overriding the built-in functionality of the ``Page`` to achieve webdev magic. Or, you can keep your models simple and let Wagtail's built-in functionality do the work.
Now that we have a basic idea of how our content is defined, lets look at relationships between pieces of content.
Page Properties and Methods Reference
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In addition to the model fields provided, ``Page`` has many properties and methods that you may wish to reference, use, or override in creating your own models. Those listed here are relatively straightforward to use, but consult the Wagtail source code for a full view of what's possible.
.. automodule:: wagtail.wagtailcore.models
.. autoclass:: Page
.. autoattribute:: specific
.. autoattribute:: specific_class
.. autoattribute:: url
.. autoattribute:: full_url
.. automethod:: relative_url
.. automethod:: is_navigable
.. automethod:: route
.. automethod:: serve
.. automethod:: get_context
.. automethod:: get_template
.. autoattribute:: preview_modes
.. automethod:: serve_preview
.. automethod:: get_ancestors
.. automethod:: get_descendants
.. automethod:: get_siblings
.. automethod:: search
.. _wagtail_site_admin:
Site
~~~~
Django's built-in admin interface provides the way to map a "site" (hostname or domain) to any node in the wagtail tree, using that node as the site's root.
Access this by going to ``/django-admin/`` and then "Home Wagtailcore Sites." To try out a development site, add a single site with the hostname ``localhost`` at port ``8000`` and map it to one of the pieces of content you have created.
Wagtail's developers plan to move the site settings into the Wagtail admin interface.

Wyświetl plik

@ -0,0 +1,663 @@
Defining models with the Editing API
====================================
.. note::
This documentation is currently being written.
Wagtail provides a highly-customizable editing interface consisting of several components:
* **Fields** — built-in content types to augment the basic types provided by Django
* **Panels** — the basic editing blocks for fields, groups of fields, and related object clusters
* **Choosers** — interfaces for finding related objects in a ForeignKey relationship
Configuring your models to use these components will shape the Wagtail editor to your needs. Wagtail also provides an API for injecting custom CSS and JavaScript for further customization, including extending the hallo.js rich text editor.
There is also an Edit Handler API for creating your own Wagtail editor components.
Defining Panels
~~~~~~~~~~~~~~~
A "panel" is the basic editing block in Wagtail. Wagtail will automatically pick the appropriate editing widget for most Django field types; implementors just need to add a panel for each field they want to show in the Wagtail page editor, in the order they want them to appear.
There are four basic types of panels:
``FieldPanel( field_name, classname=None )``
This is the panel used for basic Django field types. ``field_name`` is the name of the class property used in your model definition. ``classname`` is a string of optional CSS classes given to the panel which are used in formatting and scripted interactivity. By default, panels are formatted as inset fields. The CSS class ``full`` can be used to format the panel so it covers the full width of the Wagtail page editor. The CSS class ``title`` can be used to mark a field as the source for auto-generated slug strings.
``MultiFieldPanel( children, heading="", classname=None )``
This panel condenses several ``FieldPanel`` s or choosers, from a list or tuple, under a single ``heading`` string.
``InlinePanel( base_model, relation_name, panels=None, classname=None, label='', help_text='' )``
This panel allows for the creation of a "cluster" of related objects over a join to a separate model, such as a list of related links or slides to an image carousel. This is a very powerful, but tricky feature which will take some space to cover, so we'll skip over it for now. For a full explanation on the usage of ``InlinePanel``, see :ref:`inline_panels`.
``FieldRowPanel( children, classname=None)``
This panel is purely aesthetic. It creates a columnar layout in the editing interface, where each of the child Panels appears alongside each other rather than below. Use of FieldRowPanel particularly helps reduce the "snow-blindness" effect of seeing so many fields on the page, for complex models. It also improves the perceived association between fields of a similar nature. For example if you created a model representing an "Event" which had a starting date and ending date, it may be intuitive to find the start and end date on the same "row".
FieldRowPanel should be used in combination with ``col*`` classnames added to each of the child Panels of the FieldRowPanel. The Wagtail editing interface is layed out using a grid system, in which the maximum width of the editor is 12 columns wide. Classes ``col1``-``col12`` can be applied to each child of a FieldRowPanel. The class ``col3`` will ensure that field appears 3 columns wide or a quarter the width. ``col4`` would cause the field to be 4 columns wide, or a third the width.
**(In addition to these four, there are also Chooser Panels, detailed below.)**
Wagtail provides a tabbed interface to help organize panels. Three such tabs are provided:
* ``content_panels`` is the main tab, used for the bulk of your model's fields.
* ``promote_panels`` is suggested for organizing fields regarding the promotion of the page around the site and the Internet. For example, a field to dictate whether the page should show in site-wide menus, descriptive text that should appear in site search results, SEO-friendly titles, OpenGraph meta tag content and other machine-readable information.
* ``settings_panels`` is essentially for non-copy fields. By default it contains the page's scheduled publishing fields. Other suggested fields could include a field to switch between one layout/style and another.
Let's look at an example of a panel definition:
.. code-block:: python
COMMON_PANELS = (
FieldPanel('slug'),
FieldPanel('seo_title'),
FieldPanel('show_in_menus'),
FieldPanel('search_description'),
)
...
class ExamplePage( Page ):
# field definitions omitted
...
ExamplePage.content_panels = [
FieldPanel('title', classname="full title"),
FieldPanel('body', classname="full"),
FieldRowPanel([
FieldPanel('start_date', classname="col3"),
FieldPanel('end_date', classname="col3"),
]),
ImageChooserPanel('splash_image'),
DocumentChooserPanel('free_download'),
PageChooserPanel('related_page'),
]
ExamplePage.promote_panels = [
MultiFieldPanel(COMMON_PANELS, "Common page configuration"),
]
After the ``Page``-derived class definition, just add lists of panel definitions to order and organize the Wagtail page editing interface for your model.
Built-in Fields and Choosers
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Django's field types are automatically recognized and provided with an appropriate widget for input. Just define that field the normal Django way and pass the field name into ``FieldPanel()`` when defining your panels. Wagtail will take care of the rest.
Here are some Wagtail-specific types that you might include as fields in your models.
Rich Text (HTML)
----------------
Wagtail provides a general-purpose WYSIWYG editor for creating rich text content (HTML) and embedding media such as images, video, and documents. To include this in your models, use the ``RichTextField()`` function when defining a model field:
.. code-block:: python
from wagtail.wagtailcore.fields import RichTextField
from wagtail.wagtailadmin.edit_handlers import FieldPanel
# ...
class BookPage(Page):
book_text = RichTextField()
BookPage.content_panels = [
FieldPanel('body', classname="full"),
# ...
]
``RichTextField`` inherits from Django's basic ``TextField`` field, so you can pass any field parameters into ``RichTextField`` as if using a normal Django field. This field does not need a special panel and can be defined with ``FieldPanel``.
However, template output from ``RichTextField`` is special and need to be filtered to preserve embedded content. See :ref:`rich-text-filter`.
If you're interested in extending the capabilities of the Wagtail WYSIWYG editor (hallo.js), See :ref:`extending_wysiwyg`.
Images
------
One of the features of Wagtail is a unified image library, which you can access in your models through the ``Image`` model and the ``ImageChooserPanel`` chooser. Here's how:
.. code-block:: python
from wagtail.wagtailimages.models import Image
from wagtail.wagtailimages.edit_handlers import ImageChooserPanel
# ...
class BookPage(Page):
cover = models.ForeignKey(
'wagtailimages.Image',
null=True,
blank=True,
on_delete=models.SET_NULL,
related_name='+'
)
BookPage.content_panels = [
ImageChooserPanel('cover'),
# ...
]
Django's default behavior is to "cascade" deletions through a ForeignKey relationship, which is probably not what you want happening. This is why the ``null``, ``blank``, and ``on_delete`` parameters should be set to allow for an empty field. (See `Django model field reference (on_delete)`_ ). ``ImageChooserPanel`` takes only one argument: the name of the field.
.. _Django model field reference (on_delete): https://docs.djangoproject.com/en/dev/ref/models/fields/#django.db.models.ForeignKey.on_delete
Displaying ``Image`` objects in a template requires the use of a template tag. See :ref:`image_tag`.
Documents
---------
For files in other formats, Wagtail provides a generic file store through the ``Document`` model:
.. code-block:: python
from wagtail.wagtaildocs.models import Document
from wagtail.wagtaildocs.edit_handlers import DocumentChooserPanel
# ...
class BookPage(Page):
book_file = models.ForeignKey(
'wagtaildocs.Document',
null=True,
blank=True,
on_delete=models.SET_NULL,
related_name='+'
)
BookPage.content_panels = [
DocumentChooserPanel('book_file'),
# ...
]
As with images, Wagtail documents should also have the appropriate extra parameters to prevent cascade deletions across a ForeignKey relationship. ``DocumentChooserPanel`` takes only one argument: the name of the field.
Documents can be used directly in templates without tags or filters. Its properties are:
.. glossary::
``title``
The title of the document.
``url``
URL to the file.
``created_at``
The date and time the document was created (DateTime).
``filename``
The filename of the file.
``file_extension``
The extension of the file.
``tags``
A ``TaggableManager`` which keeps track of tags associated with the document (uses the ``django-taggit`` module).
Pages and Page-derived Models
-----------------------------
You can explicitly link ``Page``-derived models together using the ``Page`` model and ``PageChooserPanel``.
.. code-block:: python
from wagtail.wagtailcore.models import Page
from wagtail.wagtailadmin.edit_handlers import PageChooserPanel
# ...
class BookPage(Page):
publisher = models.ForeignKey(
'wagtailcore.Page',
null=True,
blank=True,
on_delete=models.SET_NULL,
related_name='+',
)
BookPage.content_panels = [
PageChooserPanel('related_page', 'demo.PublisherPage'),
# ...
]
``PageChooserPanel`` takes two arguments: a field name and an optional page type. Specifying a page type (in the form of an ``"appname.modelname"`` string) will filter the chooser to display only pages of that type.
Snippets
--------
Snippets are vanilla Django models you create yourself without a Wagtail-provided base class. So using them as a field in a page requires specifying your own ``appname.modelname``. A chooser, ``SnippetChooserPanel``, is provided which takes the field name and snippet class.
.. code-block:: python
from wagtail.wagtailsnippets.edit_handlers import SnippetChooserPanel
# ...
class BookPage(Page):
advert = models.ForeignKey(
'demo.Advert',
null=True,
blank=True,
on_delete=models.SET_NULL,
related_name='+'
)
BookPage.content_panels = [
SnippetChooserPanel('advert', Advert),
# ...
]
See :ref:`snippets` for more information.
Field Customization
~~~~~~~~~~~~~~~~~~~
By adding CSS classnames to your panel definitions or adding extra parameters to your field definitions, you can control much of how your fields will display in the Wagtail page editing interface. Wagtail's page editing interface takes much of its behavior from Django's admin, so you may find many options for customization covered there. (See `Django model field reference`_ ).
.. _Django model field reference: https://docs.djangoproject.com/en/dev/ref/models/fields/
Full-Width Input
----------------
Use ``classname="full"`` to make a field (input element) stretch the full width of the Wagtail page editor. This will not work if the field is encapsulated in a ``MultiFieldPanel``, which places its child fields into a formset.
Titles
------
Use ``classname="title"`` to make Page's built-in title field stand out with more vertical padding.
Col*
------
Fields within a ``FieldRowPanel`` can have their width dictated in terms of the number of columns it should span. The ``FieldRowPanel`` is always considered to be 12 columns wide regardless of browser size or the nesting of ``FieldRowPanel`` in any other type of panel. Specify a number of columns thus: ``col3``, ``col4``, ``col6`` etc (up to 12). The resulting width with be *relative* to the full width of the ``FieldRowPanel``.
Required Fields
---------------
To make input or chooser selection manditory for a field, add ``blank=False`` to its model definition. (See `Django model field reference (blank)`_ ).
.. _Django model field reference (blank): https://docs.djangoproject.com/en/dev/ref/models/fields/#django.db.models.Field.blank
Hiding Fields
-------------
Without a panel definition, a default form field (without label) will be used to represent your fields. If you intend to hide a field on the Wagtail page editor, define the field with ``editable=False`` (See `Django model field reference (editable)`_ ).
.. _Django model field reference (editable): https://docs.djangoproject.com/en/dev/ref/models/fields/#editable
MultiFieldPanel
~~~~~~~~~~~~~~~
The ``MultiFieldPanel`` groups a list of child fields into a fieldset, which can also be collapsed into a heading bar to save space.
.. code-block:: python
BOOK_FIELD_COLLECTION = [
ImageChooserPanel('cover'),
DocumentChooserPanel('book_file'),
PageChooserPanel('publisher'),
]
BookPage.content_panels = [
MultiFieldPanel(
BOOK_FIELD_COLLECTION,
heading="Collection of Book Fields",
classname="collapsible collapsed"
),
# ...
]
By default, ``MultiFieldPanel`` s are expanded and not collapsible. Adding the classname ``collapsible`` will enable the collapse control. Adding both ``collapsible`` and ``collapsed`` to the classname parameter will load the editor page with the ``MultiFieldPanel`` collapsed under its heading.
.. _inline_panels:
Inline Panels and Model Clusters
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The ``django-modelcluster`` module allows for streamlined relation of extra models to a Wagtail page. For instance, you can create objects related through a ``ForeignKey`` relationship on the fly and save them to a draft revision of a ``Page`` object. Normally, your related objects "cluster" would need to be created beforehand (or asynchronously) before linking them to a Page.
Let's look at the example of adding related links to a ``Page``-derived model. We want to be able to add as many as we like, assign an order, and do all of this without leaving the page editing screen.
.. code-block:: python
from wagtail.wagtailcore.models import Orderable, Page
from modelcluster.fields import ParentalKey
# The abstract model for related links, complete with panels
class RelatedLink(models.Model):
title = models.CharField(max_length=255)
link_external = models.URLField("External link", blank=True)
panels = [
FieldPanel('title'),
FieldPanel('link_external'),
]
class Meta:
abstract = True
# The real model which combines the abstract model, an
# Orderable helper class, and what amounts to a ForeignKey link
# to the model we want to add related links to (BookPage)
class BookPageRelatedLinks(Orderable, RelatedLink):
page = ParentalKey('demo.BookPage', related_name='related_links')
class BookPage( Page ):
# ...
BookPage.content_panels = [
# ...
InlinePanel( BookPage, 'related_links', label="Related Links" ),
]
The ``RelatedLink`` class is a vanilla Django abstract model. The ``BookPageRelatedLinks`` model extends it with capability for being ordered in the Wagtail interface via the ``Orderable`` class as well as adding a ``page`` property which links the model to the ``BookPage`` model we're adding the related links objects to. Finally, in the panel definitions for ``BookPage``, we'll add an ``InlinePanel`` to provide an interface for it all. Let's look again at the parameters that ``InlinePanel`` accepts:
.. code-block:: python
InlinePanel( base_model, relation_name, panels=None, label='', help_text='' )
``base_model`` is the model you're extending with the cluster. The ``relation_name`` is the ``related_name`` label given to the cluster's ``ParentalKey`` relation. You can add the ``panels`` manually or make them part of the cluster model. Finally, ``label`` and ``help_text`` provide a heading and caption, respectively, for the Wagtail editor.
For another example of using model clusters, see :ref:`tagging`
For more on ``django-modelcluster``, visit `the django-modelcluster github project page`_.
.. _the django-modelcluster github project page: https://github.com/torchbox/django-modelcluster
.. _extending_wysiwyg:
Extending the WYSIWYG Editor (hallo.js)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
To inject javascript into the Wagtail page editor, see the :ref:`insert_editor_js <insert_editor_js>` hook. Once you have the hook in place and your hallo.js plugin loads into the Wagtail page editor, use the following Javascript to register the plugin with hallo.js.
.. code-block:: javascript
registerHalloPlugin(name, opts);
hallo.js plugin names are prefixed with the ``"IKS."`` namespace, but the ``name`` you pass into ``registerHalloPlugin()`` should be without the prefix. ``opts`` is an object passed into the plugin.
For information on developing custom hallo.js plugins, see the project's page: https://github.com/bergie/hallo
Edit Handler API
~~~~~~~~~~~~~~~~
Admin Hooks
-----------
On loading, Wagtail will search for any app with the file ``wagtail_hooks.py`` and execute the contents. This provides a way to register your own functions to execute at certain points in Wagtail's execution, such as when a ``Page`` object is saved or when the main menu is constructed.
.. versionadded:: 0.5
Decorator syntax was added in 0.5; earlier versions only supported ``hooks.register`` as an ordinary function call.
Registering functions with a Wagtail hook is done through the ``@hooks.register`` decorator:
.. code-block:: python
from wagtail.wagtailcore import hooks
@hooks.register('name_of_hook')
def my_hook_function(arg1, arg2...)
# your code here
Alternatively, ``hooks.register`` can be called as an ordinary function, passing in the name of the hook and a handler function defined elsewhere:
.. code-block:: python
hooks.register('name_of_hook', my_hook_function)
The available hooks are:
.. _before_serve_page:
``before_serve_page``
.. versionadded:: 0.4
Called when Wagtail is about to serve a page. The callable passed into the hook will receive the page object, the request object, and the args and kwargs that will be passed to the page's ``serve()`` method. If the callable returns an ``HttpResponse``, that response will be returned immediately to the user, and Wagtail will not proceed to call ``serve()`` on the page.
.. code-block:: python
from wagtail.wagtailcore import hooks
@hooks.register('before_serve_page')
def block_googlebot(page, request, serve_args, serve_kwargs):
if request.META.get('HTTP_USER_AGENT') == 'GoogleBot':
return HttpResponse("<h1>bad googlebot no cookie</h1>")
.. _construct_wagtail_edit_bird:
``construct_wagtail_edit_bird``
Add or remove items from the wagtail userbar. Add, edit, and moderation tools are provided by default. The callable passed into the hook must take the ``request`` object and a list of menu objects, ``items``. The menu item objects must have a ``render`` method which can take a ``request`` object and return the HTML string representing the menu item. See the userbar templates and menu item classes for more information.
.. code-block:: python
from wagtail.wagtailcore import hooks
class UserbarPuppyLinkItem(object):
def render(self, request):
return '<li><a href="http://cuteoverload.com/tag/puppehs/" ' \
+ 'target="_parent" class="action icon icon-wagtail">Puppies!</a></li>'
@hooks.register('construct_wagtail_edit_bird')
def add_puppy_link_item(request, items):
return items.append( UserbarPuppyLinkItem() )
.. _construct_homepage_panels:
``construct_homepage_panels``
Add or remove panels from the Wagtail admin homepage. The callable passed into this hook should take a ``request`` object and a list of ``panels``, objects which have a ``render()`` method returning a string. The objects also have an ``order`` property, an integer used for ordering the panels. The default panels use integers between ``100`` and ``300``.
.. code-block:: python
from django.utils.safestring import mark_safe
from wagtail.wagtailcore import hooks
class WelcomePanel(object):
order = 50
def render(self):
return mark_safe("""
<section class="panel summary nice-padding">
<h3>No, but seriously -- welcome to the admin homepage.</h3>
</section>
""")
@hooks.register('construct_homepage_panels')
def add_another_welcome_panel(request, panels):
return panels.append( WelcomePanel() )
.. _after_create_page:
``after_create_page``
Do something with a ``Page`` object after it has been saved to the database (as a published page or a revision). The callable passed to this hook should take a ``request`` object and a ``page`` object. The function does not have to return anything, but if an object with a ``status_code`` property is returned, Wagtail will use it as a response object. By default, Wagtail will instead redirect to the Explorer page for the new page's parent.
.. code-block:: python
from django.http import HttpResponse
from wagtail.wagtailcore import hooks
@hooks.register('after_create_page')
def do_after_page_create(request, page):
return HttpResponse("Congrats on making content!", content_type="text/plain")
.. _after_edit_page:
``after_edit_page``
Do something with a ``Page`` object after it has been updated. Uses the same behavior as ``after_create_page``.
.. _after_delete_page:
``after_delete_page``
Do something after a ``Page`` object is deleted. Uses the same behavior as ``after_create_page``.
.. _register_admin_urls:
``register_admin_urls``
Register additional admin page URLs. The callable fed into this hook should return a list of Django URL patterns which define the structure of the pages and endpoints of your extension to the Wagtail admin. For more about vanilla Django URLconfs and views, see `url dispatcher`_.
.. _url dispatcher: https://docs.djangoproject.com/en/dev/topics/http/urls/
.. code-block:: python
from django.http import HttpResponse
from django.conf.urls import url
from wagtail.wagtailcore import hooks
def admin_view( request ):
return HttpResponse( \
"I have approximate knowledge of many things!", \
content_type="text/plain")
@hooks.register('register_admin_urls')
def urlconf_time():
return [
url(r'^how_did_you_almost_know_my_name/$', admin_view, name='frank' ),
]
.. _construct_main_menu:
``construct_main_menu``
Add, remove, or alter ``MenuItem`` objects from the Wagtail admin menu. The callable passed to this hook must take a ``request`` object and a list of ``menu_items``; it must return a list of menu items. New items can be constructed from the ``MenuItem`` class by passing in: a ``label`` which will be the text in the menu item, the URL of the admin page you want the menu item to link to (usually by calling ``reverse()`` on the admin view you've set up), CSS class ``name`` applied to the wrapping ``<li>`` of the menu item as ``"menu-{name}"``, CSS ``classnames`` which are used to give the link an icon, and an ``order`` integer which determine's the item's place in the menu.
.. code-block:: python
from django.core.urlresolvers import reverse
from wagtail.wagtailcore import hooks
from wagtail.wagtailadmin.menu import MenuItem
@hooks.register('construct_main_menu')
def construct_main_menu(request, menu_items):
menu_items.append(
MenuItem( 'Frank', reverse('frank'), classnames='icon icon-folder-inverse', order=10000)
)
.. _insert_editor_js:
``insert_editor_js``
Add additional Javascript files or code snippets to the page editor. Output must be compatible with ``compress``, as local static includes or string.
.. code-block:: python
from django.utils.html import format_html, format_html_join
from django.conf import settings
from wagtail.wagtailcore import hooks
@hooks.register('insert_editor_js')
def editor_js():
js_files = [
'demo/js/hallo-plugins/hallo-demo-plugin.js',
]
js_includes = format_html_join('\n', '<script src="{0}{1}"></script>',
((settings.STATIC_URL, filename) for filename in js_files)
)
return js_includes + format_html(
"""
<script>
registerHalloPlugin('demoeditor');
</script>
"""
)
.. _insert_editor_css:
``insert_editor_css``
Add additional CSS or SCSS files or snippets to the page editor. Output must be compatible with ``compress``, as local static includes or string.
.. code-block:: python
from django.utils.html import format_html
from django.conf import settings
from wagtail.wagtailcore import hooks
@hooks.register('insert_editor_css')
def editor_css():
return format_html('<link rel="stylesheet" href="' \
+ settings.STATIC_URL \
+ 'demo/css/vendor/font-awesome/css/font-awesome.min.css">')
.. _construct_whitelister_element_rules:
``construct_whitelister_element_rules``
.. versionadded:: 0.4
Customise the rules that define which HTML elements are allowed in rich text areas. By default only a limited set of HTML elements and attributes are whitelisted - all others are stripped out. The callables passed into this hook must return a dict, which maps element names to handler functions that will perform some kind of manipulation of the element. These handler functions receive the element as a `BeautifulSoup <http://www.crummy.com/software/BeautifulSoup/bs4/doc/>`_ Tag object.
The ``wagtail.wagtailcore.whitelist`` module provides a few helper functions to assist in defining these handlers: ``allow_without_attributes``, a handler which preserves the element but strips out all of its attributes, and ``attribute_rule`` which accepts a dict specifying how to handle each attribute, and returns a handler function. This dict will map attribute names to either True (indicating that the attribute should be kept), False (indicating that it should be dropped), or a callable (which takes the initial attribute value and returns either a final value for the attribute, or None to drop the attribute).
For example, the following hook function will add the ``<blockquote>`` element to the whitelist, and allow the ``target`` attribute on ``<a>`` elements:
.. code-block:: python
from wagtail.wagtailcore import hooks
from wagtail.wagtailcore.whitelist import attribute_rule, check_url, allow_without_attributes
@hooks.register('construct_whitelister_element_rules')
def whitelister_element_rules():
return {
'blockquote': allow_without_attributes,
'a': attribute_rule({'href': check_url, 'target': True}),
}
Image Formats in the Rich Text Editor
-------------------------------------
On loading, Wagtail will search for any app with the file ``image_formats.py`` and execute the contents. This provides a way to customize the formatting options shown to the editor when inserting images in the ``RichTextField`` editor.
As an example, add a "thumbnail" format:
.. code-block:: python
# image_formats.py
from wagtail.wagtailimages.formats import Format, register_image_format
register_image_format(Format('thumbnail', 'Thumbnail', 'richtext-image thumbnail', 'max-120x120'))
To begin, import the the ``Format`` class, ``register_image_format`` function, and optionally ``unregister_image_format`` function. To register a new ``Format``, call the ``register_image_format`` with the ``Format`` object as the argument. The ``Format`` takes the following init arguments:
``name``
The unique key used to identify the format. To unregister this format, call ``unregister_image_format`` with this string as the only argument.
``label``
The label used in the chooser form when inserting the image into the ``RichTextField``.
``classnames``
The string to assign to the ``class`` attribute of the generated ``<img>`` tag.
``filter_spec``
The string specification to create the image rendition. For more, see the :ref:`image_tag`.
To unregister, call ``unregister_image_format`` with the string of the ``name`` of the ``Format`` as the only argument.
Content Index Pages (CRUD)
--------------------------
Custom Choosers
---------------
Tests
-----

Wyświetl plik

@ -0,0 +1,24 @@
Pages
=====
.. note::
This documentation is currently being written.
Wagtail requires a little careful setup to define the types of content that you want to present through your website. The basic unit of content in Wagtail is the ``Page``, and all of your page-level content will inherit basic webpage-related properties from it. But for the most part, you will be defining content yourself, through the construction of Django models using Wagtail's ``Page`` as a base.
Wagtail organizes content created from your models in a tree, which can have any structure and combination of model objects in it. Wagtail doesn't prescribe ways to organize and interrelate your content, but here we've sketched out some strategies for organizing your models.
The presentation of your content, the actual webpages, includes the normal use of the Django template system. We'll cover additional functionality that Wagtail provides at the template level later on.
.. toctree::
:maxdepth: 2
theory
creating_pages
writing_templates
model_recipes
editing_api
advanced_topics/queryset_methods
advanced_topics/private_pages
advanced_topics/routable_page

Wyświetl plik

@ -0,0 +1,218 @@
.. _model_recipes:
Recipes
=======
Overriding the serve() Method
-----------------------------
Wagtail defaults to serving ``Page``-derived models by passing ``self`` to a Django HTML template matching the model's name, but suppose you wanted to serve something other than HTML? You can override the ``serve()`` method provided by the ``Page`` class and handle the Django request and response more directly.
Consider this example from the Wagtail demo site's ``models.py``, which serves an ``EventPage`` object as an iCal file if the ``format`` variable is set in the request:
.. code-block:: python
class EventPage(Page):
...
def serve(self, request):
if "format" in request.GET:
if request.GET['format'] == 'ical':
# Export to ical format
response = HttpResponse(
export_event(self, 'ical'),
content_type='text/calendar',
)
response['Content-Disposition'] = 'attachment; filename=' + self.slug + '.ics'
return response
else:
# Unrecognised format error
message = 'Could not export event\n\nUnrecognised format: ' + request.GET['format']
return HttpResponse(message, content_type='text/plain')
else:
# Display event page as usual
return super(EventPage, self).serve(request)
``serve()`` takes a Django request object and returns a Django response object. Wagtail returns a ``TemplateResponse`` object with the template and context which it generates, which allows middleware to function as intended, so keep in mind that a simpler response object like a ``HttpResponse`` will not receive these benefits.
With this strategy, you could use Django or Python utilities to render your model in JSON or XML or any other format you'd like.
.. _overriding_route_method:
Adding Endpoints with Custom route() Methods
--------------------------------------------
Wagtail routes requests by iterating over the path components (separated with a forward slash ``/``), finding matching objects based on their slug, and delegating further routing to that object's model class. The Wagtail source is very instructive in figuring out what's happening. This is the default ``route()`` method of the ``Page`` class:
.. code-block:: python
class Page(...):
...
def route(self, request, path_components):
if path_components:
# request is for a child of this page
child_slug = path_components[0]
remaining_components = path_components[1:]
# find a matching child or 404
try:
subpage = self.get_children().get(slug=child_slug)
except Page.DoesNotExist:
raise Http404
# delegate further routing
return subpage.specific.route(request, remaining_components)
else:
# request is for this very page
if self.live:
# Return a RouteResult that will tell Wagtail to call
# this page's serve() method
return RouteResult(self)
else:
# the page matches the request, but isn't published, so 404
raise Http404
``route()`` takes the current object (``self``), the ``request`` object, and a list of the remaining ``path_components`` from the request URL. It either continues delegating routing by calling ``route()`` again on one of its children in the Wagtail tree, or ends the routing process by returning a ``RouteResult`` object or raising a 404 error.
The ``RouteResult`` object (defined in wagtail.wagtailcore.url_routing) encapsulates all the information Wagtail needs to call a page's ``serve()`` method and return a final response: this information consists of the page object, and any additional args / kwargs to be passed to ``serve()``.
By overriding the ``route()`` method, we could create custom endpoints for each object in the Wagtail tree. One use case might be using an alternate template when encountering the ``print/`` endpoint in the path. Another might be a REST API which interacts with the current object. Just to see what's involved, lets make a simple model which prints out all of its child path components.
First, ``models.py``:
.. code-block:: python
from django.shortcuts import render
from wagtail.wagtailcore.url_routing import RouteResult
...
class Echoer(Page):
def route(self, request, path_components):
if path_components:
# tell Wagtail to call self.serve() with an additional 'path_components' kwarg
return RouteResult(self, kwargs={'path_components': path_components})
else:
if self.live:
# tell Wagtail to call self.serve() with no further args
return RouteResult(self)
else:
raise Http404
def serve(self, path_components=[]):
render(request, self.template, {
'self': self,
'echo': ' '.join(path_components),
})
Echoer.content_panels = [
FieldPanel('title', classname="full title"),
]
Echoer.promote_panels = [
MultiFieldPanel(COMMON_PANELS, "Common page configuration"),
]
This model, ``Echoer``, doesn't define any properties, but does subclass ``Page`` so objects will be able to have a custom title and slug. The template just has to display our ``{{ echo }}`` property.
Now, once creating a new ``Echoer`` page in the Wagtail admin titled "Echo Base," requests such as::
http://127.0.0.1:8000/echo-base/tauntaun/kennel/bed/and/breakfast/
Will return::
tauntaun kennel bed and breakfast
Be careful if you're introducing new required arguments to the ``serve()`` method - Wagtail still needs to be able to display a default view of the page for previewing and moderation, and by default will attempt to do this by calling ``serve()`` with a request object and no further arguments. If your ``serve()`` method does not accept that as a method signature, you will need to override the page's ``serve_preview()`` method to call ``serve()`` with suitable arguments:
.. code-block:: python
def serve_preview(self, request, mode_name):
return self.serve(request, color='purple')
.. _tagging:
Tagging
-------
Wagtail provides tagging capability through the combination of two django modules, ``taggit`` and ``modelcluster``. ``taggit`` provides a model for tags which is extended by ``modelcluster``, which in turn provides some magical database abstraction which makes drafts and revisions possible in Wagtail. It's a tricky recipe, but the net effect is a many-to-many relationship between your model and a tag class reserved for your model.
Using an example from the Wagtail demo site, here's what the tag model and the relationship field looks like in ``models.py``:
.. code-block:: python
from modelcluster.fields import ParentalKey
from modelcluster.tags import ClusterTaggableManager
from taggit.models import Tag, TaggedItemBase
...
class BlogPageTag(TaggedItemBase):
content_object = ParentalKey('demo.BlogPage', related_name='tagged_items')
...
class BlogPage(Page):
...
tags = ClusterTaggableManager(through=BlogPageTag, blank=True)
BlogPage.promote_panels = [
...
FieldPanel('tags'),
]
Wagtail's admin provides a nice interface for inputting tags into your content, with typeahead tag completion and friendly tag icons.
Now that we have the many-to-many tag relationship in place, we can fit in a way to render both sides of the relation. Here's more of the Wagtail demo site ``models.py``, where the index model for ``BlogPage`` is extended with logic for filtering the index by tag:
.. code-block:: python
class BlogIndexPage(Page):
...
def serve(self, request):
# Get blogs
blogs = self.blogs
# Filter by tag
tag = request.GET.get('tag')
if tag:
blogs = blogs.filter(tags__name=tag)
return render(request, self.template, {
'self': self,
'blogs': blogs,
})
Here, ``blogs.filter(tags__name=tag)`` invokes a reverse Django queryset filter on the ``BlogPageTag`` model to optionally limit the ``BlogPage`` objects sent to the template for rendering. Now, lets render both sides of the relation by showing the tags associated with an object and a way of showing all of the objects associated with each tag. This could be added to the ``blog_page.html`` template:
.. code-block:: django
{% for tag in self.tags.all %}
<a href="{% pageurl self.blog_index %}?tag={{ tag }}">{{ tag }}</a>
{% endfor %}
Iterating through ``self.tags.all`` will display each tag associated with ``self``, while the link(s) back to the index make use of the filter option added to the ``BlogIndexPage`` model. A Django query could also use the ``tagged_items`` related name field to get ``BlogPage`` objects associated with a tag.
This is just one possible way of creating a taxonomy for Wagtail objects. With all of the components for a taxonomy available through Wagtail, you should be able to fulfill even the most exotic taxonomic schemes.
Custom Page Contexts by Overriding get_context()
------------------------------------------------
Load Alternate Templates by Overriding get_template()
-----------------------------------------------------
Preview Modes
-------------
preview_modes
serve_preview

Wyświetl plik

@ -0,0 +1,95 @@
======
Theory
======
Introduction to Trees
~~~~~~~~~~~~~~~~~~~~~
If you're unfamiliar with trees as an abstract data type, you might want to `review the concepts involved. <http://en.wikipedia.org/wiki/Tree_(data_structure)>`_
As a web developer, though, you probably already have a good understanding of trees as filesystem directories or paths. Wagtail pages can create the same structure, as each page in the tree has its own URL path, like so::
/
people/
nien-nunb/
laura-roslin/
events/
captain-picard-day/
winter-wrap-up/
The Wagtail admin interface uses the tree to organize content for editing, letting you navigate up and down levels in the tree through its Explorer menu. This method of organization is a good place to start in thinking about your own Wagtail models.
Nodes and Leaves
----------------
It might be handy to think of the ``Page``-derived models you want to create as being one of two node types: parents and leaves. Wagtail isn't prescriptive in this approach, but it's a good place to start if you're not experienced in structuring your own content types.
Nodes
`````
Parent nodes on the Wagtail tree probably want to organize and display a browse-able index of their descendants. A blog, for instance, needs a way to show a list of individual posts.
A Parent node could provide its own function returning its descendant objects.
.. code-block:: python
class EventPageIndex(Page):
# ...
def events(self):
# Get list of live event pages that are descendants of this page
events = EventPage.objects.live().descendant_of(self)
# Filter events list to get ones that are either
# running now or start in the future
events = events.filter(date_from__gte=date.today())
# Order by date
events = events.order_by('date_from')
return events
This example makes sure to limit the returned objects to pieces of content which make sense, specifically ones which have been published through Wagtail's admin interface (``live()``) and are children of this node (``descendant_of(self)``). By setting a ``subpage_types`` class property in your model, you can specify which models are allowed to be set as children, but Wagtail will allow any ``Page``-derived model by default. Regardless, it's smart for a parent model to provide an index filtered to make sense.
Leaves
``````
Leaves are the pieces of content itself, a page which is consumable, and might just consist of a bunch of properties. A blog page leaf might have some body text and an image. A person page leaf might have a photo, a name, and an address.
It might be helpful for a leaf to provide a way to back up along the tree to a parent, such as in the case of breadcrumbs navigation. The tree might also be deep enough that a leaf's parent won't be included in general site navigation.
The model for the leaf could provide a function that traverses the tree in the opposite direction and returns an appropriate ancestor:
.. code-block:: python
class EventPage(Page):
# ...
def event_index(self):
# Find closest ancestor which is an event index
return self.get_ancestors().type(EventIndexPage).last()
If defined, ``subpage_types`` will also limit the parent models allowed to contain a leaf. If not, Wagtail will allow any combination of parents and leafs to be associated in the Wagtail tree. Like with index pages, it's a good idea to make sure that the index is actually of the expected model to contain the leaf.
Other Relationships
```````````````````
Your ``Page``-derived models might have other interrelationships which extend the basic Wagtail tree or depart from it entirely. You could provide functions to navigate between siblings, such as a "Next Post" link on a blog page (``post->post->post``). It might make sense for subtrees to interrelate, such as in a discussion forum (``forum->post->replies``) Skipping across the hierarchy might make sense, too, as all objects of a certain model class might interrelate regardless of their ancestors (``events = EventPage.objects.all``). It's largely up to the models to define their interrelations, the possibilities are really endless.
.. _anatomy_of_a_wagtail_request:
Anatomy of a Wagtail Request
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
For going beyond the basics of model definition and interrelation, it might help to know how Wagtail handles requests and constructs responses. In short, it goes something like:
#. Django gets a request and routes through Wagtail's URL dispatcher definitions
#. Wagtail checks the hostname of the request to determine which ``Site`` record will handle this request.
#. Starting from the root page of that site, Wagtail traverses the page tree, calling the ``route()`` method and letting each page model decide whether it will handle the request itself or pass it on to a child page.
#. The page responsible for handling the request returns a ``RouteResult`` object from ``route()``, which identifies the page along with any additional args/kwargs to be passed to ``serve()``.
#. Wagtail calls ``serve()``, which constructs a context using ``get_context()``
#. ``serve()`` finds a template to pass it to using ``get_template()``
#. A response object is returned by ``serve()`` and Django responds to the requester.
You can apply custom behavior to this process by overriding ``Page`` class methods such as ``route()`` and ``serve()`` in your own models. For examples, see :ref:`model_recipes`.

Wyświetl plik

@ -0,0 +1,349 @@
For Front End developers
========================
.. contents:: Contents
:local:
========================
Overview
========================
Wagtail uses Django's templating language. For developers new to Django, start with Django's own template documentation:
https://docs.djangoproject.com/en/dev/topics/templates/
Python programmers new to Django/Wagtail may prefer more technical documentation:
https://docs.djangoproject.com/en/dev/ref/templates/api/
You should be familiar with Django templating basics before continuing with this documentation.
==========================
Templates
==========================
Every type of page or "content type" in Wagtail is defined as a "model" in a file called ``models.py``. If your site has a blog, you might have a ``BlogPage`` model and another called ``BlogPageListing``. The names of the models are up to the Django developer.
For each page model in ``models.py``, Wagtail assumes an HTML template file exists of (almost) the same name. The Front End developer may need to create these templates themselves by refering to ``models.py`` to infer template names from the models defined therein.
To find a suitable template, Wagtail converts CamelCase names to underscore_case. So for a ``BlogPage``, a template ``blog_page.html`` will be expected. The name of the template file can be overridden per model if necessary.
Template files are assumed to exist here::
name_of_project/
name_of_app/
templates/
name_of_app/
blog_page.html
models.py
For more information, see the Django documentation for the `application directories template loader`_.
.. _application directories template loader: https://docs.djangoproject.com/en/dev/ref/templates/api/
Page content
~~~~~~~~~~~~
The data/content entered into each page is accessed/output through Django's ``{{ double-brace }}`` notation. Each field from the model must be accessed by prefixing ``self.``. e.g the page title ``{{ self.title }}`` or another field ``{{ self.author }}``.
Additionally ``request.`` is available and contains Django's request object.
==============
Static assets
==============
Static files e.g CSS, JS and images are typically stored here::
name_of_project/
name_of_app/
static/
name_of_app/
css/
js/
images/
models.py
(The names "css", "js" etc aren't important, only their position within the tree.)
Any file within the static folder should be inserted into your HTML using the ``{% static %}`` tag. More about it: :ref:`static_tag`.
User images
~~~~~~~~~~~
Images uploaded to Wagtail by its users (as opposed to a developer's static files, above) go into the image library and from there are added to pages via the :doc:`page editor interface </editor_manual/new_pages/inserting_images>`.
Unlike other CMS, adding images to a page does not involve choosing a "version" of the image to use. Wagtail has no predefined image "formats" or "sizes". Instead the template developer defines image manipulation to occur *on the fly* when the image is requested, via a special syntax within the template.
Images from the library must be requested using this syntax, but a developer's static images can be added via conventional means e.g ``img`` tags. Only images from the library can be manipulated on the fly.
Read more about the image manipulation syntax here :ref:`image_tag`.
========================
Template tags & filters
========================
In addition to Django's standard tags and filters, Wagtail provides some of its own, which can be ``load``-ed `as you would any other <https://docs.djangoproject.com/en/dev/topics/templates/#custom-tag-and-filter-libraries>`_
.. _image_tag:
Images (tag)
~~~~~~~~~~~~
.. versionchanged:: 0.4
The 'image_tags' tags library was renamed to 'wagtailimages_tags'
The ``image`` tag inserts an XHTML-compatible ``img`` element into the page, setting its ``src``, ``width``, ``height`` and ``alt``. See also :ref:`image_tag_alt`.
The syntax for the tag is thus::
{% image [image] [resize-rule] %}
For example:
.. code-block:: django
{% load wagtailimages_tags %}
...
{% image self.photo width-400 %}
<!-- or a square thumbnail: -->
{% image self.photo fill-80x80 %}
In the above syntax ``[image]`` is the Django object refering to the image. If your page model defined a field called "photo" then ``[image]`` would probably be ``self.photo``. The ``[resize-rule]`` defines how the image is to be resized when inserted into the page; various resizing methods are supported, to cater for different usage cases (e.g. lead images that span the whole width of the page, or thumbnails to be cropped to a fixed size).
Note that a space separates ``[image]`` and ``[resize-rule]``, but the resize rule must not contain spaces.
The available resizing methods are:
.. glossary::
``max``
(takes two dimensions)
.. code-block:: django
{% image self.photo max-1000x500 %}
Fit **within** the given dimensions.
The longest edge will be reduced to the equivalent dimension size defined. e.g A portrait image of width 1000, height 2000, treated with the ``max`` dimensions ``1000x500`` (landscape) would result in the image shrunk so the *height* was 500 pixels and the width 250.
``min``
(takes two dimensions)
.. code-block:: django
{% image self.photo min-500x200 %}
**Cover** the given dimensions.
This may result in an image slightly **larger** than the dimensions you specify. e.g A square image of width 2000, height 2000, treated with the ``min`` dimensions ``500x200`` (landscape) would have it's height and width changed to 500, i.e matching the width required, but greater than the height.
``width``
(takes one dimension)
.. code-block:: django
{% image self.photo width-640 %}
Reduces the width of the image to the dimension specified.
``height``
(takes one dimension)
.. code-block:: django
{% image self.photo height-480 %}
Resize the height of the image to the dimension specified..
``fill``
(takes two dimensions)
.. code-block:: django
{% image self.photo fill-200x200 %}
Resize and **crop** to fill the **exact** dimensions.
This can be particularly useful for websites requiring square thumbnails of arbitrary images. For example, a landscape image of width 2000, height 1000, treated with ``fill`` dimensions ``200x200`` would have its height reduced to 200, then its width (ordinarily 400) cropped to 200.
**The crop always aligns on the centre of the image.**
``original``
(takes no dimensions)
.. code-block:: django
{% image self.photo original %}
Leaves the image at its original size - no resizing is performed.
.. Note::
Wagtail does not allow deforming or stretching images. Image dimension ratios will always be kept. Wagtail also *does not support upscaling*. Small images forced to appear at larger sizes will "max out" at their their native dimensions.
.. _image_tag_alt:
More control over the ``img`` tag
---------------------------------
Wagtail provides two shorcuts to give greater control over the ``img`` element:
**1. Adding attributes to the {% image %} tag**
.. versionadded:: 0.4
Extra attributes can be specified with the syntax ``attribute="value"``:
.. code-block:: django
{% image self.photo width-400 class="foo" id="bar" %}
No validation is performed on attributes added in this way so it's possible to add `src`, `width`, `height` and `alt` of your own that might conflict with those generated by the tag itself.
**2. Generating the image "as foo" to access individual properties**
Wagtail can assign the image data to another variable using Django's ``as`` syntax:
.. code-block:: django
{% image self.photo width-400 as tmp_photo %}
<img src="{{ tmp_photo.url }}" width="{{ tmp_photo.width }}"
height="{{ tmp_photo.height }}" alt="{{ tmp_photo.alt }}" class="my-custom-class" />
.. Note::
The image property used for the ``src`` attribute is actually ``image.url``, not ``image.src``.
The ``attrs`` shortcut
-----------------------
.. versionadded:: 0.4
You can also use the ``attrs`` property as a shorthand to output the attributes ``src``, ``width``, ``height`` and ``alt`` in one go:
.. code-block:: django
<img {{ tmp_photo.attrs }} class="my-custom-class" />
.. _rich-text-filter:
Rich text (filter)
~~~~~~~~~~~~~~~~~~
.. versionchanged:: 0.4
The 'rich_text' tags library was renamed to 'wagtailcore_tags'
This filter takes a chunk of HTML content and renders it as safe HTML in the page. Importantly it also expands internal shorthand references to embedded images and links made in the Wagtail editor into fully-baked HTML ready for display.
Only fields using ``RichTextField`` need this applied in the template.
.. code-block:: django
{% load wagtailcore_tags %}
...
{{ self.body|richtext }}
.. Note::
Note that the template tag loaded differs from the name of the filter.
Responsive Embeds
-----------------
Wagtail embeds and images are included at their full width, which may overflow the bounds of the content container you've defined in your templates. To make images and embeds responsive -- meaning they'll resize to fit their container -- include the following CSS.
.. code-block:: css
.rich-text img {
max-width: 100%;
height: auto;
}
.responsive-object {
position: relative;
}
.responsive-object iframe,
.responsive-object object,
.responsive-object embed {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
Internal links (tag)
~~~~~~~~~~~~~~~~~~~~
.. versionchanged:: 0.4
The 'pageurl' tags library was renamed to 'wagtailcore_tags'
pageurl
--------
Takes a Page object and returns a relative URL (``/foo/bar/``) if within the same site as the current page, or absolute (``http://example.com/foo/bar/``) if not.
.. code-block:: django
{% load wagtailcore_tags %}
...
<a href="{% pageurl self.blog_page %}">
slugurl
--------
Takes any ``slug`` as defined in a page's "Promote" tab and returns the URL for the matching Page. Like ``pageurl``, will try to provide a relative link if possible, but will default to an absolute link if on a different site. This is most useful when creating shared page furniture e.g top level navigation or site-wide links.
.. code-block:: django
{% load wagtailcore_tags %}
...
<a href="{% slugurl self.your_slug %}">
.. _static_tag:
Static files (tag)
~~~~~~~~~~~~~~~~~~
Used to load anything from your static files directory. Use of this tag avoids rewriting all static paths if hosting arrangements change, as they might between local and a live environments.
.. code-block:: django
{% load static %}
...
<img src="{% static "name_of_app/myimage.jpg" %}" alt="My image"/>
Notice that the full path name is not required and the path snippet you enter only need begin with the parent app's directory name.
========================
Wagtail User Bar
========================
This tag provides a contextual flyout menu on the top-right of a page for logged-in users. The menu gives editors the ability to edit the current page or add another at the same level. Moderators are also given the ability to accept or reject a page previewed as part of content moderation.
.. code-block:: django
{% load wagtailuserbar %}
...
{% wagtailuserbar %}
By default the User Bar appears in the top right of the browser window, flush with the edge. If this conflicts with your design it can be moved with a css rule in your own CSS files e.g to move it down from the top:
.. code-block:: css
#wagtail-userbar{
top:200px
}

Wyświetl plik

@ -0,0 +1,65 @@
.. _wagtailsearch_backends:
========
Backends
========
Wagtail can degrade to a database-backed text search, but we strongly recommend `Elasticsearch`_.
.. _Elasticsearch: http://www.elasticsearch.org/
.. _wagtailsearch_backends_database:
Database Backend
================
The default DB search backend uses Django's ``__icontains`` filter.
Elasticsearch Backend
=====================
Prerequisites are the Elasticsearch service itself and, via pip, the `elasticsearch-py`_ package:
.. code-block:: guess
pip install elasticsearch
.. note::
If you are using Elasticsearch < 1.0, install elasticsearch-py version 0.4.5: ```pip install elasticsearch==0.4.5```
The backend is configured in settings:
.. code-block:: python
WAGTAILSEARCH_BACKENDS = {
'default': {
'BACKEND': 'wagtail.wagtailsearch.backends.elasticsearch.ElasticSearch',
'URLS': ['http://localhost:9200'],
'INDEX': 'wagtail',
'TIMEOUT': 5,
}
}
Other than ``BACKEND`` the keys are optional and default to the values shown. In addition, any other keys are passed directly to the Elasticsearch constructor as case-sensitive keyword arguments (e.g. ``'max_retries': 1``).
If you prefer not to run an Elasticsearch server in development or production, there are many hosted services available, including `Searchly`_, who offer a free account suitable for testing and development. To use Searchly:
- Sign up for an account at `dashboard.searchly.com/users/sign\_up`_
- Use your Searchly dashboard to create a new index, e.g. 'wagtaildemo'
- Note the connection URL from your Searchly dashboard
- Configure ``URLS`` and ``INDEX`` in the Elasticsearch entry in ``WAGTAILSEARCH_BACKENDS``
- Run ``./manage.py update_index``
.. _elasticsearch-py: http://elasticsearch-py.readthedocs.org
.. _Searchly: http://www.searchly.com/
.. _dashboard.searchly.com/users/sign\_up: https://dashboard.searchly.com/users/sign_up
Rolling Your Own
================
Wagtail search backends implement the interface defined in ``wagtail/wagtail/wagtailsearch/backends/base.py``. At a minimum, the backend's ``search()`` method must return a collection of objects or ``model.objects.none()``. For a fully-featured search backend, examine the Elasticsearch backend code in ``elasticsearch.py``.

Wyświetl plik

@ -0,0 +1,41 @@
.. _editors-picks:
Editor's picks
==============
Editor's picks are a way of explicitly linking relevant content to search terms, so results pages can contain curated content in addition to results from the search algorithm. In a template using the search results view, editor's picks can be accessed through the variable ``query.editors_picks``. To include editor's picks in your search results template, use the following properties.
``query.editors_picks.all``
This gathers all of the editor's picks objects relating to the current query, in order according to their sort order in the Wagtail admin. You can then iterate through them using a ``{% for ... %}`` loop. Each editor's pick object provides these properties:
``editors_pick.page``
The page object associated with the pick. Use ``{% pageurl editors_pick.page %}`` to generate a URL or provide other properties of the page object.
``editors_pick.description``
The description entered when choosing the pick, perhaps explaining why the page is relevant to the search terms.
Putting this all together, a block of your search results template displaying editor's picks might look like this:
.. code-block:: django
{% with query.editors_picks.all as editors_picks %}
{% if editors_picks %}
<div class="well">
<h3>Editors picks</h3>
<ul>
{% for editors_pick in editors_picks %}
<li>
<h4>
<a href="{% pageurl editors_pick.page %}">
{{ editors_pick.page.title }}
</a>
</h4>
<p>{{ editors_pick.description|safe }}</p>
</li>
{% endfor %}
</ul>
</div>
{% endif %}
{% endwith %}

Wyświetl plik

@ -0,0 +1,162 @@
.. _wagtailsearch_for_python_developers:
=====================
For Python developers
=====================
Basic usage
===========
All searches are performed on Django QuerySets. Wagtail provides a ``search`` method on the queryset for all page models:
.. code-block:: python
# Search future EventPages
>>> from wagtail.wagtailcore.models import EventPage
>>> EventPage.objects.filter(date__gt=timezone.now()).search("Hello world!")
All methods of ``PageQuerySet`` are supported by wagtailsearch:
.. code-block:: python
# Search all live EventPages that are under the events index
>>> EventPage.objects.live().descendant_of(events_index).search("Event")
[<EventPage: Event 1>, <EventPage: Event 2>]
Indexing extra fields
=====================
.. versionchanged:: 0.4
The ``indexed_fields`` configuration format was replaced with ``search_fields``
.. note::
Searching on extra fields with the database backend is not currently supported.
Fields need to be explicitly added to the search configuration in order for you to be able to search/filter on them.
You can add new fields to the search index by overriding the ``search_fields`` property and appending a list of extra ``SearchField``/``FilterField`` objects to it.
The default value of ``search_fields`` (as set in ``Page``) indexes the ``title`` field as a ``SearchField`` and some other generally useful fields as ``FilterField`` rules.
Quick example
-------------
This creates an ``EventPage`` model with two fields ``description`` and ``date``. ``description`` is indexed as a ``SearchField`` and ``date`` is indexed as a ``FilterField``
.. code-block:: python
from wagtail.wagtailsearch import indexed
class EventPage(Page):
description = models.TextField()
date = models.DateField()
search_fields = Page.search_fields + ( # Inherit search_fields from Page
indexed.SearchField('description'),
indexed.FilterField('date'),
)
# Get future events which contain the string "Christmas" in the title or description
>>> EventPage.objects.filter(date__gt=timezone.now()).search("Christmas")
``indexed.SearchField``
-----------------------
These are added to the search index and are used for performing full-text searches on your models. These would usually be text fields.
Options
```````
- **partial_match** (boolean) - Setting this to true allows results to be matched on parts of words. For example, this is set on the title field by default so a page titled "Hello World!" will be found if the user only types "Hel" into the search box.
- **boost** (number) - This allows you to set fields as being more important than others. Setting this to a high number on a field will make pages with matches in that field to be ranked higher. By default, this is set to 100 on the title field and 1 on all other fields.
- **es_extra** (dict) - This field is to allow the developer to set or override any setting on the field in the ElasticSearch mapping. Use this if you want to make use of any ElasticSearch features that are not yet supported in Wagtail.
``indexed.FilterField``
-----------------------
These are added to the search index but are not used for full-text searches. Instead, they allow you to run filters on your search results.
Indexing callables and other attributes
---------------------------------------
.. note::
This is not supported in the :ref:`wagtailsearch_backends_database`
Search/filter fields do not need to be Django fields, they could be any method or attribute on your class.
One use for this is indexing ``get_*_display`` methods Django creates automatically for fields with choices.
.. code-block:: python
from wagtail.wagtailsearch import indexed
class EventPage(Page):
IS_PRIVATE_CHOICES = (
(False, "Public"),
(True, "Private"),
)
is_private = models.BooleanField(choices=IS_PRIVATE_CHOICES)
search_fields = Page.search_fields + (
# Index the human-readable string for searching
indexed.SearchField('get_is_private_display'),
# Index the boolean value for filtering
indexed.FilterField('is_private'),
)
Indexing non-page models
========================
Any Django model can be indexed and searched.
To do this, inherit from ``indexed.Indexed`` and add some ``search_fields`` to the model.
.. code-block:: python
from wagtail.wagtailsearch import indexed
class Book(models.Model, indexed.Indexed):
title = models.CharField(max_length=255)
genre = models.CharField(max_length=255, choices=GENRE_CHOICES)
author = models.ForeignKey(Author)
published_date = models.DateTimeField()
search_fields = (
indexed.SearchField('title', partial_match=True, boost=10),
indexed.SearchField('get_genre_display'),
indexed.FilterField('genre'),
indexed.FilterField('author'),
indexed.FilterField('published_date'),
)
# As this model doesn't have a search method in its QuerySet, we have to call search directly on the backend
>>> from wagtail.wagtailsearch.backends import get_search_backend
>>> s = get_search_backend()
# Run a search for a book by Roald Dahl
>>> roald_dahl = Author.objects.get(name="Roald Dahl")
>>> s.search("chocolate factory", Book.objects.filter(author=roald_dahl))
[<Book: Charlie and the chocolate factory>]

Wyświetl plik

@ -0,0 +1,169 @@
.. _wagtailsearch_frontend_views:
Frontend views
==============
Default Page Search
-------------------
Wagtail provides a default frontend search interface which indexes the ``title`` field common to all ``Page``-derived models. Let's take a look at all the components of the search interface.
The most basic search functionality just needs a search box which submits a request. Since this will be reused throughout the site, let's put it in ``mysite/includes/search_box.html`` and then use ``{% include ... %}`` to weave it into templates:
.. code-block:: django
<form action="{% url 'wagtailsearch_search' %}" method="get">
<input type="text" name="q"{% if query_string %} value="{{ query_string }}"{% endif %}>
<input type="submit" value="Search">
</form>
The form is submitted to the url of the ``wagtailsearch_search`` view, with the search terms variable ``q``. The view will use its own basic search results template.
Let's use our own template for the results, though. First, in your project's ``settings.py``, define a path to your template:
.. code-block:: python
WAGTAILSEARCH_RESULTS_TEMPLATE = 'mysite/search_results.html'
Next, let's look at the template itself:
.. code-block:: django
{% extends "mysite/base.html" %}
{% load pageurl %}
{% block title %}Search{% if search_results %} Results{% endif %}{% endblock %}
{% block search_box %}
{% include "mysite/includes/search_box.html" with query_string=query_string only %}
{% endblock %}
{% block content %}
<h2>Search Results{% if request.GET.q %} for {{ request.GET.q }}{% endif %}</h2>
<ul>
{% for result in search_results %}
<li>
<h4><a href="{% pageurl result.specific %}">{{ result.specific }}</a></h4>
{% if result.specific.search_description %}
{{ result.specific.search_description|safe }}
{% endif %}
</li>
{% empty %}
<li>No results found</li>
{% endfor %}
</ul>
{% endblock %}
The search view provides a context with a few useful variables.
``query_string``
The terms (string) used to make the search.
``search_results``
A collection of Page objects matching the query. The ``specific`` property of ``Page`` will give the most-specific subclassed model object for the Wagtail page. For instance, if an ``Event`` model derived from the basic Wagtail ``Page`` were included in the search results, you could use ``specific`` to access the custom properties of the ``Event`` model (``result.specific.date_of_event``).
``is_ajax``
Boolean. This returns Django's ``request.is_ajax()``.
``query``
A Wagtail ``Query`` object matching the terms. The ``Query`` model provides several class methods for viewing the statistics of all queries, but exposes only one property for single objects, ``query.hits``, which tracks the number of time the search string has been used over the lifetime of the site. ``Query`` also joins to the Editor's Picks functionality though ``query.editors_picks``. See :ref:`editors-picks`.
Asynchronous Search with JSON and AJAX
--------------------------------------
Wagtail provides JSON search results when queries are made to the ``wagtailsearch_suggest`` view. To take advantage of it, we need a way to make that URL available to a static script. Instead of hard-coding it, let's set a global variable in our ``base.html``:
.. code-block:: django
<script>
var wagtailJSONSearchURL = "{% url 'wagtailsearch_suggest' %}";
</script>
Now add a simple interface for the search with a ``<input>`` element to gather search terms and a ``<div>`` to display the results:
.. code-block:: html
<div>
<h3>Search</h3>
<input id="json-search" type="text">
<div id="json-results"></div>
</div>
Finally, we'll use JQuery to make the asynchronous requests and handle the interactivity:
.. code-block:: guess
$(function() {
// cache the elements
var searchBox = $('#json-search'),
resultsBox = $('#json-results');
// when there's something in the input box, make the query
searchBox.on('input', function() {
if( searchBox.val() == ''){
resultsBox.html('');
return;
}
// make the request to the Wagtail JSON search view
$.ajax({
url: wagtailJSONSearchURL + "?q=" + searchBox.val(),
dataType: "json"
})
.done(function(data) {
console.log(data);
if( data == undefined ){
resultsBox.html('');
return;
}
// we're in business! let's format the results
var htmlOutput = '';
data.forEach(function(element, index, array){
htmlOutput += '<p><a href="' + element.url + '">' + element.title + '</a></p>';
});
// and display them
resultsBox.html(htmlOutput);
})
.error(function(data){
console.log(data);
});
});
});
Results are returned as a JSON object with this structure:
.. code-block:: guess
{
[
{
title: "Lumpy Space Princess",
url: "/oh-my-glob/"
},
{
title: "Lumpy Space",
url: "/no-smooth-posers/"
},
...
]
}
What if you wanted access to the rest of the results context or didn't feel like using JSON? Wagtail also provides a generalized AJAX interface where you can use your own template to serve results asynchronously.
The AJAX interface uses the same view as the normal HTML search, ``wagtailsearch_search``, but will serve different results if Django classifies the request as AJAX (``request.is_ajax()``). Another entry in your project settings will let you override the template used to serve this response:
.. code-block:: python
WAGTAILSEARCH_RESULTS_TEMPLATE_AJAX = 'myapp/includes/search_listing.html'
In this template, you'll have access to the same context variables provided to the HTML template. You could provide a template in JSON format with extra properties, such as ``query.hits`` and editor's picks, or render an HTML snippet that can go directly into your results ``<div>``. If you need more flexibility, such as multiple formats/templates based on differing requests, you can set up a custom search view.
Custom Search Views
-------------------
This functionality is still under active development to provide a streamlined interface, but take a look at ``wagtail/wagtail/wagtailsearch/views/frontend.py`` if you are interested in coding custom search views.

Wyświetl plik

@ -0,0 +1,17 @@
.. _wagtailsearch:
Search
======
Wagtail provides a comprehensive and extensible search interface. In addition, it provides ways to promote search results through "Editor's Picks." Wagtail also collects simple statistics on queries made through the search interface.
.. toctree::
:maxdepth: 2
for_python_developers
frontend_views
editors_picks
backends

Wyświetl plik

@ -0,0 +1,168 @@
.. _snippets:
Snippets
========
Snippets are pieces of content which do not necessitate a full webpage to render. They could be used for making secondary content, such as headers, footers, and sidebars, editable in the Wagtail admin. Snippets are models which do not inherit the ``Page`` class and are thus not organized into the Wagtail tree, but can still be made editable by assigning panels and identifying the model as a snippet with ``register_snippet()``.
Snippets are not search-able or order-able in the Wagtail admin, so decide carefully if the content type you would want to build into a snippet might be more suited to a page.
Snippet Models
--------------
Here's an example snippet from the Wagtail demo website:
.. code-block:: python
from django.db import models
from wagtail.wagtailadmin.edit_handlers import FieldPanel
from wagtail.wagtailsnippets.models import register_snippet
...
class Advert(models.Model):
url = models.URLField(null=True, blank=True)
text = models.CharField(max_length=255)
panels = [
FieldPanel('url'),
FieldPanel('text'),
]
def __unicode__(self):
return self.text
register_snippet(Advert)
The ``Advert`` model uses the basic Django model class and defines two properties: text and url. The editing interface is very close to that provided for ``Page``-derived models, with fields assigned in the panels property. Snippets do not use multiple tabs of fields, nor do they provide the "save as draft" or "submit for moderation" features.
``register_snippet(Advert)`` tells Wagtail to treat the model as a snippet. The ``panels`` list defines the fields to show on the snippet editing page. It's also important to provide a string representation of the class through ``def __unicode__(self):`` so that the snippet objects make sense when listed in the Wagtail admin.
Including Snippets in Template Tags
-----------------------------------
The simplest way to make your snippets available to templates is with a template tag. This is mostly done with vanilla Django, so perhaps reviewing Django's documentation for `django custom template tags`_ will be more helpful. We'll go over the basics, though, and make note of any considerations to make for Wagtail.
First, add a new python file to a ``templatetags`` folder within your app. The demo website, for instance uses the path ``wagtaildemo/demo/templatetags/demo_tags.py``. We'll need to load some Django modules and our app's models and ready the ``register`` decorator:
.. _django custom template tags: https://docs.djangoproject.com/en/dev/howto/custom-template-tags/
.. code-block:: python
from django import template
from demo.models import *
register = template.Library()
...
# Advert snippets
@register.inclusion_tag('demo/tags/adverts.html', takes_context=True)
def adverts(context):
return {
'adverts': Advert.objects.all(),
'request': context['request'],
}
``@register.inclusion_tag()`` takes two variables: a template and a boolean on whether that template should be passed a request context. It's a good idea to include request contexts in your custom template tags, since some Wagtail-specific template tags like ``pageurl`` need the context to work properly. The template tag function could take arguments and filter the adverts to return a specific model, but for brevity we'll just use ``Advert.objects.all()``.
Here's what's in the template used by the template tag:
.. code-block:: django
{% for advert in adverts %}
<p>
<a href="{{ advert.url }}">
{{ advert.text }}
</a>
</p>
{% endfor %}
Then in your own page templates, you can include your snippet template tag with:
.. code-block:: django
{% block content %}
...
{% adverts %}
{% endblock %}
Binding Pages to Snippets
-------------------------
In the above example, the list of adverts is a fixed list, displayed as part of the template independently of the page content. This might be what you want for a common panel in a sidebar, say - but in other scenarios you may wish to refer to a snippet within page content. This can be done by defining a foreign key to the snippet model within your page model, and adding a ``SnippetChooserPanel`` to the page's ``content_panels`` definitions. For example, if you wanted to be able to specify an advert to appear on ``BookPage``:
.. code-block:: python
from wagtail.wagtailsnippets.edit_handlers import SnippetChooserPanel
# ...
class BookPage(Page):
advert = models.ForeignKey(
'demo.Advert',
null=True,
blank=True,
on_delete=models.SET_NULL,
related_name='+'
)
BookPage.content_panels = [
SnippetChooserPanel('advert', Advert),
# ...
]
The snippet could then be accessed within your template as ``self.advert``.
To attach multiple adverts to a page, the ``SnippetChooserPanel`` can be placed on an inline child object of ``BookPage``, rather than on ``BookPage`` itself. Here this child model is named ``BookPageAdvertPlacement`` (so called because there is one such object for each time that an advert is placed on a BookPage):
.. code-block:: python
from django.db import models
from wagtail.wagtailcore.models import Page
from wagtail.wagtailsnippets.edit_handlers import SnippetChooserPanel
from modelcluster.fields import ParentalKey
...
class BookPageAdvertPlacement(Orderable, models.Model):
page = ParentalKey('demo.BookPage', related_name='advert_placements')
advert = models.ForeignKey('demo.Advert', related_name='+')
class Meta:
verbose_name = "Advert Placement"
verbose_name_plural = "Advert Placements"
panels = [
SnippetChooserPanel('advert', Advert),
]
def __unicode__(self):
return self.page.title + " -> " + self.advert.text
class BookPage(Page):
...
BookPage.content_panels = [
InlinePanel(BookPage, 'advert_placements', label="Adverts"),
# ...
]
These child objects are now accessible through the page's ``advert_placements`` property, and from there we can access the linked Advert snippet as ``advert``. In the template for ``BookPage``, we could include the following:
.. code-block:: django
{% for advert_placement in self.advert_placements.all %}
<p><a href="{{ advert_placement.advert.url }}">{{ advert_placement.advert.text }}</a></p>
{% endfor %}

Wyświetl plik

@ -3,14 +3,14 @@ Documents
Documents such as PDFs can be managed from the Documents interface, available in the left-hand menu. This interface allows you to add documents to and remove documents from the CMS.
.. image:: ../../images/screen29_documents_page.png
.. image:: ../../_static/images/screen29_documents_page.png
* Add documents by clicking the *Add document* button in the top-right.
* Search for documents in the CMS by entering your search term in the search bar. The results will be automatically updated as you type.
* You can also filter the results by *Popular tags*. Click on a tag to update the search results listing.
* Edit the details of a document by clicking the document title.
.. image:: ../../images/screen30_documents_edit_page.png
.. image:: ../../_static/images/screen30_documents_edit_page.png
* When editing a document you can replace the file associated with that document record. This means you can update documents without having to update the pages on which they are placed. Changing the file will change it on all pages that use the document.
* Add or remove tags using the Tags field.

Wyświetl plik

@ -3,11 +3,11 @@ Images
If you want to edit, add or remove images from the CMS outside of the individual pages you can do so from the Images interface. This is accessed from the left-hand menu.
.. image:: ../../images/screen31_images_page.png
.. image:: ../../_static/images/screen31_images_page.png
* Clicking an image will allow you to edit the data associated with it. This includes the Alt text, the photographers credit, the medium of the subject matter and much more. **NOTE:** changing the alt text here will alter it for all occurrences of the image in carousels, but not in inline images, where the alt text can be set separately.
.. image:: ../../images/screen32_image_edit_page.png
.. image:: ../../_static/images/screen32_image_edit_page.png
* When editing an image you can replace the file associated with that image record. This means you can update images without having to update the pages on which they are placed. Changing the file will change it on all pages that use the image.

Wyświetl plik

@ -10,3 +10,4 @@ Wagtail allows you to manage all of your documents and images through their own
documents
images
snippets

Wyświetl plik

@ -1,6 +1,9 @@
Snippets
~~~~~~~~
.. note::
Documentation currently incomplete and in draft status
.. UNSURE HOW TO WRITE THIS AS THE ADVERT EXAMPLE IN WAGTAIL DEMO IS NOT A PARTICULARLY HELPFUL USE CASE.
When creating a page on a website, it is a common occurance to want to add in a piece of content that already exists on another page. An example of this would be a person's contact details, or an advert that you want to simply show on every page of your site, without having to manually apply it.

Wyświetl plik

@ -6,7 +6,7 @@ There are two ways that you can access the edit screen of an existing page:
* Clicking the title of the page in an `Explorer page <the_explorer_page.html>`_ or in `search results <using_search.html>`_.
* Clicking the *Edit* link below the title in either of the situations above.
.. image:: ../images/screen12_edit_screen_overview.png
.. image:: ../_static/images/screen12_edit_screen_overview.png
* When editing an existing page the title of the page being edited is displayed at the top of the page.
* The current status of the page is displayed in the top-right.

Wyświetl plik

@ -11,7 +11,7 @@ The Dashboard provides information on:
You can return to the Dashboard at any time by clicking the Wagtail log in the top-left of the screen.
.. image:: ../../images/screen02_dashboard_editor.png
.. image:: ../../_static/images/screen02_dashboard_editor.png
- Clicking the logo returns you to your Dashboard.
- Thes stats at the top of the page describe the total amount of content on the CMS (just for fun!).

Wyświetl plik

@ -1,7 +1,7 @@
The Explorer menu
~~~~~~~~~~~~~~~~~
.. image:: ../../images/screen03_explorer_menu.png
.. image:: ../../_static/images/screen03_explorer_menu.png
* Click the Explorer button in the sidebar to open the site explorer. This allows you to navigate through the tree-structure of the site.
* Clicking the name of a page will take you to the Explorer page for that section (see below). NOTE: The site explorer only displays pages which themselves have child pages. To see and edit the child pages you should click the name of the parent page in the site explorer.

Wyświetl plik

@ -4,20 +4,20 @@ The Explorer page
The Explorer page allows you to view the a page's children and perform actions on them. From here you can publish/unpublish pages, move pages to other sections, drill down further into the content tree, or reorder pages under the parent for the purposes of display in menus.
.. image:: ../../images/screen05_explorer_page.png
.. image:: ../../_static/images/screen05_explorer_page.png
* The name of the section you are looking at is displayed below the breadcrumb (the row of page names beginning with the home icon). Each section is also itself a page (in this case the homepage). Clicking the title of the section takes you to the Edit screen for the section page.
* As the heading suggests, below are the child pages of the section. Clicking the titles of each child page will take you to its Edit screen.
.. image:: ../../images/screen06_explorer_page_arrow.png
.. image:: ../../_static/images/screen06_explorer_page_arrow.png
* Clicking the arrows will display a further level of child pages.
.. image:: ../../images/screen07_explorer_page_breadcrumb.png
.. image:: ../../_static/images/screen07_explorer_page_breadcrumb.png
* As you drill down through the site the breadcrumb (the row of pages beginning with the home icon) will display the path you have taken. Clicking on the page titles in the breadcrumb will take you to the Explorer screen for that page.
.. image:: ../../images/screen08_add_page_button.png
.. image:: ../../_static/images/screen08_add_page_button.png
* To add further child pages press the Add child page button below the parent page title. You can view the parent page on the live site by pressing the View live button. The Move button will take you to the Move page screen where you can reposition the page and all its child pages in the site structure.
* Similar buttons are available for each child page. These are made visible on hover.
@ -25,7 +25,7 @@ The Explorer page allows you to view the a page's children and perform actions o
Reordering pages
________________
.. image:: ../../images/screen08.5_reorder_page_handles.png
.. image:: ../../_static/images/screen08.5_reorder_page_handles.png
* Clicking the icon to the far left of the child pages table will enable the reordering handles. This allows you to reorder the way that content displays in the main menu of your website.
* Reorder by dragging the pages by the handles on the far left (the icon made up of 8 dots).

Wyświetl plik

@ -1,7 +1,7 @@
Using search
~~~~~~~~~~~~
.. image:: ../../images/screen04_search_screen.png
.. image:: ../../_static/images/screen04_search_screen.png
* A very easy way to find the page that you want is to use the main search feature, accessible from the left-hand menu.
* Simply type in part or all of the name of the page you are looking for, and the results below will automatically update as you type.

Wyświetl plik

@ -15,4 +15,4 @@ __________
* Access this by adding **/admin** onto the end of your root URL (e.g. www.example.com/admin).
* Enter your username and password and click **Sign in**.
.. image:: ../images/screen01_login.png
.. image:: ../_static/images/screen01_login.png

Wyświetl plik

@ -1,9 +1,10 @@
Using Wagtail: an Editor's guide
================================
This section of the documentation is written for the users of a Wagtail-powered site. That is, the content editors, moderators and administrators who will be running things on a day-to-day basis.
.. note::
Documentation currently incomplete and in draft status
**NOTE:** This section of the documentation is currently in draft status.
This section of the documentation is written for the users of a Wagtail-powered site. That is, the content editors, moderators and administrators who will be running things on a day-to-day basis.
.. toctree::
:maxdepth: 3

Wyświetl plik

@ -3,15 +3,15 @@ Adding multiple items
A common feature of Wagtail is the ability to add more than one of a particular type of field or item. For example, you can add as many carousel items or related links as you wish.
.. image:: ../../images/screen24_multiple_items_closed.png
.. image:: ../../_static/images/screen24_multiple_items_closed.png
* Whenever you see the white cross in the green circle illustrated here it means you can add multiple objects or items to a page. Clicking the icon will display the fields required for that piece of content. The image below demonstrates this with a *Related link* item.
.. image:: ../../images/screen25_multiple_items_open.png
.. image:: ../../_static/images/screen25_multiple_items_open.png
* You can delete an individual item by pressing the trash can in the top-right.
* You can add more items by clicking the link with the white cross again.
.. image:: ../../images/screen26_reordering_multiple_items.png
.. image:: ../../_static/images/screen26_reordering_multiple_items.png
4. You can reorder your multiple items using the up and down arrows. Doing this will affect the order in which they are display on the live page.

Wyświetl plik

@ -7,34 +7,34 @@ There are two types of text entry fields you will see when creating a page. Some
So, when you click into certain fields, for example the *Body* field, you will be presented with a set of tools which allow you to format and style your text. These tools also allow you to insert links, images, videos clips and links to documents.
.. image:: ../../images/screen11_rich_text_field.png
.. image:: ../../_static/images/screen11_rich_text_field.png
Below is a summary of what the different buttons represent:
.. image:: ../../images/screen11.1_bold_italic.png
.. image:: ../../_static/images/screen11.1_bold_italic.png
**Bold / Italic:** Either click then type for bold or italic, or highlight and select to convert existing text to bold or italic.
.. image:: ../../images/screen11.2_formatting_options.png
.. image:: ../../_static/images/screen11.2_formatting_options.png
**Paragraph / heading levels:** Clicking into a paragraph and selecting one of these options will change the level of the text. H1 is not included as this is reserved for the page title.
.. image:: ../../images/screen11.3_lists.png
.. image:: ../../_static/images/screen11.3_lists.png
**Bulleted and numbered lists**
.. image:: ../../images/screen11.4_horizontal_rule.png
.. image:: ../../_static/images/screen11.4_horizontal_rule.png
**Horizontal rule:** Creates a horizontal line at the position of the cursor. If inserted inside a paragraph it will split the paragraph into two seperate paragraphs.
.. image:: ../../images/screen11.5_undo_redo.png
.. image:: ../../_static/images/screen11.5_undo_redo.png
**Undo / redo:** As expected will undo or redo the latest actions. Never use the your browser's back button when attempting to undo changes as this could lead to errors. Either use this undo button, or the usual keyboard shortcut, CTRL+Z.
.. image:: ../../images/screen11.6_images_videos.png
.. image:: ../../_static/images/screen11.6_images_videos.png
**Insert image / video:** Allows you to insert an image or video into the rich text field. See Inserting images and videos section for more details. .. insert links for images and videos here>>
.. image:: ../../images/screen11.7_links_docs.png
.. image:: ../../_static/images/screen11.7_links_docs.png
**Insert link / document:** Allows you to insert a link or a document into the rich text field. See Inserting links and Inserting documents for more details. .. insert links to Links and documents sections>>

Wyświetl plik

@ -1,7 +1,7 @@
Creating new pages
~~~~~~~~~~~~~~~~~~
.. image:: ../../images/screen08_add_page_button.png
.. image:: ../../_static/images/screen08_add_page_button.png
Create new pages by clicking the Add child page. This creates a child page of the section you are currently in. In this case a child page of the Homepage.

Wyświetl plik

@ -1,10 +1,10 @@
Inserting links to documents into body text
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. image:: ../../images/screen27_docs_icon.png
.. image:: ../../_static/images/screen27_docs_icon.png
It is possible to insert links to documents held in the CMS into the body text of a web page by clicking the button above in the Body field.
The process for doing this is the same as when inserting an image. You are given the choice of either choosing a document from the CMS, or uploading a new document.
.. image:: ../../images/screen28_docs_form.png
.. image:: ../../_static/images/screen28_docs_form.png

Wyświetl plik

@ -11,11 +11,11 @@ __________________________________
The carousel is where the main, featured images and videos associated with a page should be displayed.
.. image:: ../../images/screen14_add_carousel_button.png
.. image:: ../../_static/images/screen14_add_carousel_button.png
* To insert a carousel item click the Add carousel content link in the Carousel content section.
.. image:: ../../images/screen15_carousel_form.png
.. image:: ../../_static/images/screen15_carousel_form.png
* You can then insert an image by clicking the *Choose an image* button.
* It is also possible to add videos to a carousel. Simply copy and paste the web address for the video (either YouTube or Vimeo) into the *Embed URL* field and click Insert. A poster image for the video can also be uploaded or selected from the CMS. This is the image displayed before a user has clicked play on the video.
@ -38,7 +38,7 @@ When you click the *Choose an image* button you will be presented with a pop-up
The image below demonstrates finding and inserting an image that is already present in the CMS image library.
.. image:: ../../images/screen16_selecting_image_from_library.png
.. image:: ../../_static/images/screen16_selecting_image_from_library.png
#. Typing into the search box will automatically display the results below.
#. Clicking one of the Popular tags will filter the search results by that tag.
@ -46,7 +46,7 @@ The image below demonstrates finding and inserting an image that is already pre
**Uploading a new image to the CMS**
.. image:: ../../images/screen17_upload_image.png
.. image:: ../../_static/images/screen17_upload_image.png
#. You must include an image title for your uploaded image
#. Click the *Choose file* button to choose an image from your computer.
@ -60,7 +60,7 @@ Images can also be inserted into the body text of a page. When editing the Body
In addition, the Wagtail Demo site allows you to chose an aligmnent for you image.
.. image:: ../../images/screen18_image_alignment.png
.. image:: ../../_static/images/screen18_image_alignment.png
#. You can select how the image is displayed by selecting one of the format options.
#. In the Wagtail Demo site, images inserted into the body text do not have embeded captions (these should be added as regular body text). However you must still provide specific alt text for your image.

Wyświetl plik

@ -5,7 +5,7 @@ Similar to images, there are a variety of points at which you will want to add l
Whichever way you insert a link, you will be presented with the form displayed below.
.. image:: ../../images/screen19_link_form.png
.. image:: ../../_static/images/screen19_link_form.png
* Search for an existing page to link to using the search bar at the top of the pop-up.
* Below the search bar you can select the type of link you want to insert. The following types are available:

Wyświetl plik

@ -1,12 +1,15 @@
.. _inserting_videos:
Inserting videos into body content
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
As well as inserting videos into a carousel, Wagtail's rich text fields allow you to add videos into the body of a page by clicking the *Add video* button in the toolbar.
.. image:: ../../images/screen20_insert_video_form.png
.. image:: ../../_static/images/screen20_insert_video_form.png
* Copy and paste the web address for the video (either YouTube or Vimeo) into the URL field and click Insert.
.. image:: ../../images/screen21_video_in_editor.png
.. image:: ../../_static/images/screen21_video_in_editor.png
* A placeholder with the name of the video and a screenshot will be inserted into the text area. Clicking the X in the top corner will remove the video.
* A placeholder with the name of the video and a screenshot will be inserted into the text area. Clicking the X in the top corner will remove the video.

Wyświetl plik

@ -9,4 +9,4 @@ The Save/Preview/Submit for moderation menu is always present at the bottom of t
* **Publish/Unpublish:** Clicking either the *Publish* or *Unpublish* buttons will take you to a confirmation screen asking you to confirm that you wish to publish or unpublish this page. If a page is published it will be accessible from its specific URL and will also be displayed in site search results. (moderators and administrators only)
* **Delete:** Clicking this button will take you to a confirmation screen asking you to confirm that you wish to delete the current page. Be sure that this is actually what you want to do, as deleted pages are not recoverable. In many situations simply unpublishing the page will be enough. (moderators and administrators only)
.. image:: ../../images/screen13_publish_menu.png
.. image:: ../../_static/images/screen13_publish_menu.png

Wyświetl plik

@ -3,9 +3,9 @@ Required fields
* Fields marked with an asterisk are required. You will not be able to save a draft or submit the page for moderation without these fields being completed.
.. image:: ../../images/screen22_required_field.png
.. image:: ../../_static/images/screen22_required_field.png
* If you try to save/submit the page with some required fields not filled out, you will see the error displayed here.
* The number of validation errors for each of the *Promote* and *Content* tabs will appear in a red circle, and the text, 'This field is required', will appear below each field that must be completed.
.. image:: ../../images/screen23_validation_error.png
.. image:: ../../_static/images/screen23_validation_error.png

Wyświetl plik

@ -1,12 +1,12 @@
Selecting a page type
~~~~~~~~~~~~~~~~~~~~~
.. image:: ../../images/screen09_page_type_selection.png
.. image:: ../../_static/images/screen09_page_type_selection.png
* On the left of the page chooser screen are listed all the types of pages that you can create. Clicking the page type name will take you to the Create new page screen for that page type (see below).
* Clicking the *View all … pages* links on the right will display all the pages that exist on the website of this type. This is to help you judge what type of page you will need to complete your task.
.. image:: ../../images/screen10_blank_page_edit_screen.png
.. image:: ../../_static/images/screen10_blank_page_edit_screen.png
* Once youve selected a page type you will be presented with a blank New page screen.
* Click into the areas below each field's heading to start entering content.

Wyświetl plik

@ -4,7 +4,7 @@ Getting Started
On Ubuntu
~~~~~~~~~
If you have a fresh instance of Ubuntu 13.04 or 13.10, you can install Wagtail,
If you have a fresh instance of Ubuntu 13.04 or later, you can install Wagtail,
along with a demonstration site containing a set of standard templates and page
types, in one step. As the root user::
@ -40,7 +40,7 @@ Once you've experimented with the demo app and are ready to build your pages via
On OS X
~~~~~~~
Install `pip <http://pip.readthedocs.org/en/latest/installing.html>`_ and `virtualenvwrapper <http://virtualenvwrapper.readthedocs.org/en/latest/>`_ if you don't have them already. Then, in your terminal::
Install `pip <http://pip.readthedocs.org/en/latest/installing.html>`__ and `virtualenvwrapper <http://virtualenvwrapper.readthedocs.org/en/latest/>`_ if you don't have them already. Then, in your terminal::
mkvirtualenv wagtaildemo
git clone https://github.com/torchbox/wagtaildemo.git
@ -65,7 +65,7 @@ Wagtail instance available as the basis for your new site:
- Install `Vagrant <http://www.vagrantup.com/>`_ 1.1+
- Clone the demonstration site, create the Vagrant box and initialise Wagtail::
git clone git@github.com:torchbox/wagtaildemo.git
git clone https://github.com/torchbox/wagtaildemo.git
cd wagtaildemo
vagrant up
vagrant ssh
@ -75,10 +75,10 @@ Wagtail instance available as the basis for your new site:
./manage.py runserver 0.0.0.0:8000
- This will make the app accessible on the host machine as
`localhost:8111 <http://localhost:8111>`_ - you can access the Wagtail admin
interface at `localhost:8111/admin <http://localhost:8111/admin>`_. The codebase
is located on the host machine, exported to the VM as a shared folder; code
editing and Git operations will generally be done on the host.
`localhost:8111 <http://localhost:8111>`_ - you can access the Wagtail admin
interface at `localhost:8111/admin <http://localhost:8111/admin>`_. The codebase
is located on the host machine, exported to the VM as a shared folder; code
editing and Git operations will generally be done on the host.
Using Docker
~~~~~~~~~~~~
@ -101,13 +101,18 @@ use the following steps:
Required dependencies
=====================
- `pip <https://github.com/pypa/pip>`_
- `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`
~~~~~~~~~~~~~~~~~~~~~~
@ -160,4 +166,4 @@ Once you've experimented with the demo app and are ready to build your pages via
COMMIT;
EOF
rm -r demo media/images/* media/original_images/*
perl -pi -e"s/('demo',|WAGTAILSEARCH_RESULTS_TEMPLATE)/#\1/" $PROJECT/settingsbase.py
perl -pi -e"s/('demo',|WAGTAILSEARCH_RESULTS_TEMPLATE)/#\1/" $PROJECT/settings/base.py

Wyświetl plik

@ -15,8 +15,29 @@ Coding guidelines
~~~~~~~~~~~~~~~~~
* PEP8. We ask that all Python contributions adhere to the `PEP8 <http://www.python.org/dev/peps/pep-0008/>`_ style guide, apart from the restriction on line length (E501). The `pep8 tool <http://pep8.readthedocs.org/en/latest/>`_ makes it easy to check your code, e.g. ``pep8 --ignore=E501 your_file.py``.
* Python 2 and 3 compatibility. All contributions should support Python 2 and 3 and we recommend using the `six <https://pythonhosted.org/six/>`_ compatibility library (use the pip version installed as a dependency, not the version bundled with Django).
* Tests. Wagtail has a suite of tests, which we are committed to improving and expanding. We run continuous integration at `travis-ci.org/torchbox/wagtail <https://travis-ci.org/torchbox/wagtail>`_ to ensure that no commits or pull requests introduce test failures. If your contributions add functionality to Wagtail, please include the additional tests to cover it; if your contributions alter existing functionality, please update the relevant tests accordingly.
Styleguide
~~~~~~~~~~
Developers working on the Wagtail UI or creating new UI components may wish to test their work against our Styleguide, which is provided as the contrib module "wagtailstyleguide".
To install the styleguide module on your site, add it to the list of ``INSTALLED_APPS`` in your settings:
.. code-block:: python
INSTALLED_APPS = (
...
'wagtail.contrib.wagtailstyleguide',
...
)
At present the styleguide is static: new UI components must be added to it manually, and there are no hooks into it for other modules to use. We hope to support hooks in the future.
The styleguide doesn't currently provide examples of all the core interface components; notably the Page, Document, Image and Snippet chooser interfaces are not currently represented.
Translations
~~~~~~~~~~~~

Wyświetl plik

@ -4,7 +4,7 @@ Deploying Wagtail
On your server
~~~~~~~~~~~~~~
Wagtail is straightforward to deploy on modern Linux-based distributions, but see the section on :doc:`performance </performance>` for the non-Python services we recommend. If you are running Debian or Ubuntu, this installation script for our Vagrant box may be useful:
Wagtail is straightforward to deploy on modern Linux-based distributions, but see the section on :doc:`performance </howto/performance>` for the non-Python services we recommend. If you are running Debian or Ubuntu, this installation script for our Vagrant box may be useful:
`github.com/torchbox/wagtaildemo/blob/master/etc/install/install.sh <https://github.com/torchbox/wagtaildemo/blob/master/etc/install/install.sh>`_
@ -18,4 +18,4 @@ On Gondor
On other PAASs and IAASs
~~~~~~~~~~~~~~~~~~~~~~~~
We know of Wagtail sites running on `Heroku <http://spapas.github.io/2014/02/13/wagtail-tutorial/>`_, Digital Ocean and elsewhere. If you have successfully installed Wagtail on your platform or infrastructure, please :doc:`contribute </contributing>` your notes to this documentation!
We know of Wagtail sites running on `Heroku <http://spapas.github.io/2014/02/13/wagtail-tutorial/>`_, Digital Ocean and elsewhere. If you have successfully installed Wagtail on your platform or infrastructure, please :doc:`contribute </howto/contributing>` your notes to this documentation!

Wyświetl plik

@ -0,0 +1,11 @@
How to
======
.. toctree::
:maxdepth: 2
settings
deploying
performance
contributing

Wyświetl plik

@ -1,13 +1,15 @@
Performance
===========
Wagtail is designed for speed, both in the editor interface and on the front-end, but if you want even better performance or you need to handle very high volumes of traffic, here are some tips on eeking out the most from your installation.
Wagtail is designed for speed, both in the editor interface and on the front-end, but if you want even better performance or you need to handle very high volumes of traffic, here are some tips on eking out the most from your installation.
Editor interface
~~~~~~~~~~~~~~~~
We have tried to minimise external dependencies for a working installation of Wagtail, in order to make it as simple as possible to get going. However, a number of default settings can be configured for better performance:
Cache
-----
@ -25,20 +27,27 @@ We recommend `Redis <http://redis.io/>`_ as a fast, persistent cache. Install Re
Without a persistent cache, Wagtail will recreate all compressable assets at each server start, e.g. when any files change under ```./manage.py runserver```.
Search
------
Wagtail has strong support for `Elasticsearch <http://www.elasticsearch.org/>`_ - both in the editor interface and for users of your site - but can fall back to a database search if Elasticsearch isn't present. Elasticsearch is faster and more powerful than the Django ORM for text search, so we recommend installing it or using a hosted service like `Searchly <http://www.searchly.com/>`_.
Database
--------
Wagtail is tested on SQLite, and should work on other Django-supported database backends, but we recommend PostgreSQL for production use.
Public users
~~~~~~~~~~~~
Caching proxy
-------------
To support high volumes of traffic with excellent response times, we recommend a caching proxy. Both `Varnish <http://www.varnish-cache.org/>`_ and `Squid <http://www.squid-cache.org/>`_ have been tested in production. Hosted proxies like `Cloudflare <https://www.cloudflare.com/>`_ should also work well.
To support high volumes of traffic with excellent response times, we recommend a caching proxy. Both `Varnish <http://www.varnish-cache.org/>`_ and `Squid <http://www.squid-cache.org/>`_ have been tested in production. Hosted proxies like `Cloudflare <https://www.cloudflare.com/>`_ should also work well.
.. versionadded:: 0.4
Wagtail supports automatic cache invalidation for Varnish/Squid. See :ref:`frontend_cache_purging` for more information.

Some files were not shown because too many files have changed in this diff Show More