diff --git a/.travis.yml b/.travis.yml
index 3e0a4bb98..8764f2859 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -12,7 +12,7 @@ services:
# Package installation
install:
- python setup.py install
- - pip install psycopg2 pyelasticsearch elasticutils==0.8.2 wand embedly
+ - pip install psycopg2 elasticsearch wand embedly
- pip install coveralls
# Pre-test configuration
before_script:
diff --git a/CHANGELOG.txt b/CHANGELOG.txt
index 039935b43..ca58949b4 100644
--- a/CHANGELOG.txt
+++ b/CHANGELOG.txt
@@ -3,15 +3,19 @@ Changelog
0.4 (xx.xx.20xx)
~~~~~~~~~~~~~~~~
+ * ElasticUtils/pyelasticsearch swapped for elasticsearch-py
* 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
- * Added styleguide
+ * 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
+ * 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
* Fix: Animated GIFs are now coalesced before resizing
* Fix: Wand backend clones images before modifying them
* Fix: Admin breadcrumb now positioned correctly on mobile
diff --git a/CONTRIBUTORS.rst b/CONTRIBUTORS.rst
index a3f9c7221..fcba8d2d7 100644
--- a/CONTRIBUTORS.rst
+++ b/CONTRIBUTORS.rst
@@ -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
diff --git a/README.rst b/README.rst
index 62b3c5cd5..0e68b8c6b 100644
--- a/README.rst
+++ b/README.rst
@@ -1,7 +1,7 @@
.. 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&zxcv
+.. 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?zxcv
diff --git a/docs/building_your_site/frontenddevelopers.rst b/docs/building_your_site/frontenddevelopers.rst
index 0ef918582..651b15818 100644
--- a/docs/building_your_site/frontenddevelopers.rst
+++ b/docs/building_your_site/frontenddevelopers.rst
@@ -189,17 +189,41 @@ The available resizing methods are:
More control over the ``img`` tag
---------------------------------
-In some cases greater control over the ``img`` tag is required, for example to add a custom ``class``. Rather than generating the ``img`` element for you, Wagtail can assign the relevant data to another object using Django's ``as`` syntax:
+Wagtail provides two shorcuts to give greater control over the ``img`` element:
+
+.. versionadded:: 0.4
+**Adding attributes to the {% image %} tag**
+
+Extra attributes can be specified with the syntax ``attribute="value"``:
.. code-block:: django
-
- {% load wagtailimages_tags %}
- ...
+
+ {% image self.photo width-400 class="foo" id="bar" %}
+
+No validation is performed on attributes add in this way by the developer. It's possible to add `src`, `width`, `height` and `alt` of your own that might conflict with those generated by the tag itself.
+
+
+**Generating the image "as"**
+
+Wagtail can assign the image data to another object using Django's ``as`` syntax:
+
+.. code-block:: django
+
{% image self.photo width-400 as tmp_photo %}
+.. versionadded:: 0.4
+The ``attrs`` shortcut
+-----------------------
+
+You can also use the ``attrs`` property as a shorthand to output the ``src``, ``width``, ``height`` and ``alt`` attributes in one go:
+
+.. code-block:: django
+
+
+
.. _rich-text-filter:
diff --git a/docs/editing_api.rst b/docs/editing_api.rst
index 88fc9458f..9f7602b11 100644
--- a/docs/editing_api.rst
+++ b/docs/editing_api.rst
@@ -546,6 +546,28 @@ Where ``'hook'`` is one of the following hook strings and ``function`` is a func
+ 'demo/css/vendor/font-awesome/css/font-awesome.min.css">')
hooks.register('insert_editor_css', editor_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
`` element to the whitelist, and allow the ``target`` attribute on ```` elements: + + .. code-block:: python + + from wagtail.wagtailadmin import hooks + from wagtail.wagtailcore.whitelist import attribute_rule, check_url, allow_without_attributes + + def whitelister_element_rules(): + return { + 'blockquote': allow_without_attributes, + 'a': attribute_rule({'href': check_url, 'target': True}), + } + hooks.register('construct_whitelister_element_rules', whitelister_element_rules) + Image Formats in the Rich Text Editor ------------------------------------- diff --git a/docs/wagtail_search.rst b/docs/wagtail_search.rst index 3d8ea26b2..1fa2c6525 100644 --- a/docs/wagtail_search.rst +++ b/docs/wagtail_search.rst @@ -220,17 +220,14 @@ The default DB search backend uses Django's ``__icontains`` filter. Elasticsearch Backend ````````````````````` -Prerequisites are the Elasticsearch service itself and, via pip, the `elasticutils`_ and `pyelasticsearch`_ packages: +Prerequisites are the Elasticsearch service itself and, via pip, the `elasticsearch-py`_ package: .. code-block:: guess - pip install elasticutils==0.8.2 pyelasticsearch + pip install elasticsearch .. note:: - ElasticUtils 0.9+ is not supported. - -.. note:: - The dependency on elasticutils and pyelasticsearch is scheduled to be replaced by a dependency on `elasticsearch-py`_. + 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: @@ -246,7 +243,7 @@ The backend is configured in settings: } } -Other than ``BACKEND`` the keys are optional and default to the values shown. ``FORCE_NEW`` is used by elasticutils. In addition, any other keys are passed directly to the Elasticsearch constructor as case-sensitive keyword arguments (e.g. ``'max_retries': 1``). +Other than ``BACKEND`` the keys are optional and default to the values shown. ``FORCE_NEW`` is used by elasticsearch-py. 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: @@ -256,8 +253,6 @@ If you prefer not to run an Elasticsearch server in development or production, t - Configure ``URLS`` and ``INDEX`` in the Elasticsearch entry in ``WAGTAILSEARCH_BACKENDS`` - Run ``./manage.py update_index`` -.. _elasticutils: http://elasticutils.readthedocs.org -.. _pyelasticsearch: http://pyelasticsearch.readthedocs.org .. _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 diff --git a/runtests.py b/runtests.py index 96653b40d..efa66910f 100755 --- a/runtests.py +++ b/runtests.py @@ -13,7 +13,7 @@ MEDIA_ROOT = os.path.join(WAGTAIL_ROOT, 'test-media') if not settings.configured: try: - import elasticutils + import elasticsearch has_elasticsearch = True except ImportError: has_elasticsearch = False diff --git a/wagtail/tests/models.py b/wagtail/tests/models.py index f798129dc..ca6d411da 100644 --- a/wagtail/tests/models.py +++ b/wagtail/tests/models.py @@ -303,6 +303,9 @@ class StandardChild(Page): pass class BusinessIndex(Page): + subpage_types = ['tests.BusinessChild', 'tests.BusinessSubIndex'] + +class BusinessSubIndex(Page): subpage_types = ['tests.BusinessChild'] class BusinessChild(Page): diff --git a/wagtail/tests/utils.py b/wagtail/tests/utils.py index 0b13e5932..02327799b 100644 --- a/wagtail/tests/utils.py +++ b/wagtail/tests/utils.py @@ -24,21 +24,3 @@ class WagtailTestUtils(object): self.client.login(username='test', password='password') return user - - # From: https://github.com/django/django/blob/255449c1ee61c14778658caae8c430fa4d76afd6/django/contrib/auth/tests/test_views.py#L70-L85 - def assertURLEqual(self, url, expected, parse_qs=False): - """ - Given two URLs, make sure all their components (the ones given by - urlparse) are equal, only comparing components that are present in both - URLs. - If `parse_qs` is True, then the querystrings are parsed with QueryDict. - This is useful if you don't want the order of parameters to matter. - Otherwise, the query strings are compared as-is. - """ - fields = ParseResult._fields - - for attr, x, y in zip(fields, urlparse(url), urlparse(expected)): - if parse_qs and attr == 'query': - x, y = QueryDict(x), QueryDict(y) - if x and y and x != y: - self.fail("%r != %r (%s doesn't match)" % (url, expected, attr)) diff --git a/wagtail/tests/wagtail_hooks.py b/wagtail/tests/wagtail_hooks.py index 91c76c9f4..5dccca666 100644 --- a/wagtail/tests/wagtail_hooks.py +++ b/wagtail/tests/wagtail_hooks.py @@ -1,4 +1,5 @@ from wagtail.wagtailadmin import hooks +from wagtail.wagtailcore.whitelist import attribute_rule, check_url, allow_without_attributes def editor_css(): return """""" @@ -8,3 +9,11 @@ hooks.register('insert_editor_css', editor_css) def editor_js(): return """""" hooks.register('insert_editor_js', editor_js) + + +def whitelister_element_rules(): + return { + 'blockquote': allow_without_attributes, + 'a': attribute_rule({'href': check_url, 'target': True}), + } +hooks.register('construct_whitelister_element_rules', whitelister_element_rules) diff --git a/wagtail/wagtailadmin/edit_handlers.py b/wagtail/wagtailadmin/edit_handlers.py index 2b4006610..a15440016 100644 --- a/wagtail/wagtailadmin/edit_handlers.py +++ b/wagtail/wagtailadmin/edit_handlers.py @@ -19,7 +19,7 @@ from django.utils.translation import ugettext as _ from django.utils.translation import ugettext_lazy from wagtail.wagtailcore.models import Page -from wagtail.wagtailcore.util import camelcase_to_underscore +from wagtail.wagtailcore.utils import camelcase_to_underscore from wagtail.wagtailcore.fields import RichTextArea diff --git a/wagtail/wagtailadmin/static/wagtailadmin/js/page-editor.js b/wagtail/wagtailadmin/static/wagtailadmin/js/page-editor.js index eacb8f86d..03adb7aca 100644 --- a/wagtail/wagtailadmin/static/wagtailadmin/js/page-editor.js +++ b/wagtail/wagtailadmin/static/wagtailadmin/js/page-editor.js @@ -329,9 +329,9 @@ $(function() { }); /* Set up behaviour of preview button */ - $('.action-preview').click(function(e) { + $('.action-preview').click(function(e) { e.preventDefault(); - + var previewWindow = window.open($(this).data('placeholder'), $(this).data('windowname')); $.ajax({ @@ -340,18 +340,9 @@ $(function() { data: $('#page-edit-form').serialize(), success: function(data, textStatus, request) { if (request.getResponseHeader('X-Wagtail-Preview') == 'ok') { - var pdoc = previewWindow.document; - var frame = pdoc.getElementById('preview-frame'); - - frame = frame.contentWindow || frame.contentDocument.document || frame.contentDocument; - frame.document.open(); - frame.document.write(data); - frame.document.close(); - - var hideTimeout = setTimeout(function(){ - pdoc.getElementById('loading-spinner-wrapper').className += 'remove'; - clearTimeout(hideTimeout); - }, 50) // just enough to give effect without adding discernible slowness + previewWindow.document.open(); + previewWindow.document.write(data); + previewWindow.document.close(); } else { previewWindow.close(); document.open(); diff --git a/wagtail/wagtailadmin/templates/wagtailadmin/pages/preview.html b/wagtail/wagtailadmin/templates/wagtailadmin/pages/preview.html index d5c4b7a51..46a4d2867 100644 --- a/wagtail/wagtailadmin/templates/wagtailadmin/pages/preview.html +++ b/wagtail/wagtailadmin/templates/wagtailadmin/pages/preview.html @@ -11,6 +11,5 @@-