Merge pull request #182 from gasman/feature/editor_css_hook

Add hook for inserting site-specific CSS/JS into the editor interface
This commit is contained in:
Matt Westcott 2014-04-07 10:25:41 +01:00
commit b994fcdf3b
26 changed files with 121 additions and 92 deletions

View file

@ -85,6 +85,7 @@ if not settings.configured:
PASSWORD_HASHERS=(
'django.contrib.auth.hashers.MD5PasswordHasher', # don't use the intentionally slow default password hasher
),
COMPRESS_ENABLED=False, # disable compression so that we can run tests on the content of the compress tag
WAGTAILSEARCH_BACKENDS=WAGTAILSEARCH_BACKENDS,
WAGTAIL_SITE_NAME='Test Site'
)

View file

@ -0,0 +1,10 @@
from wagtail.wagtailadmin import hooks
def editor_css():
return """<link rel="stylesheet" href="/path/to/my/custom.css">"""
hooks.register('insert_editor_css', editor_css)
def editor_js():
return """<script src="/path/to/my/custom.js"></script>"""
hooks.register('insert_editor_js', editor_js)

View file

@ -79,8 +79,9 @@
}
}
/* === CSS BELOW THIS POINT IS CUSTOM TO RCA === */
/* TODO: find extensible way for developers to style rich text to suit their implementation, without making changes to anything within wagtailadmin */
/* Set some reasonable default heading styles. These can be overridden in site-specific custom CSS
to make them better reflect their appearance on the front-end (however, it's arguably better for editors
NOT to be thinking about a specific visual appearance when they choose heading levels...) */
h1,h2,h3,h4,h5,h6{
font-family:inherit;
@ -90,9 +91,7 @@
h2 {
font-size: 2em;
line-height: 1.2em;
padding-top:0.5em;
border-top:1px solid #CCC;
clear:both
clear:both;
}
h3 {
font-size: 1.7em;
@ -111,19 +110,20 @@
hr {
border-bottom: 1px solid #ccc;
border-top: none;
border-left: none;
border-left: none;
}
/*
These styles correspond to the image formats defined in wagtailimages/formats.py,
so that images displayed in the rich text field receive more or less the same
styling that they would receive on the site front-end.
TODO: when we implement a mechanism to configure the image format list on a
per-installation / per-site basis, we'll need a way to insert the corresponding
CSS here.
Wagtail installations that define their own image formats (in a myapp.image_formats module)
should ideally use the insert_editor_css hook to pass in their own custom CSS to have those
images render within the rich text area in the same styles that would appear on the front-end.
*/
.bodytext-image {
.richtext-image {
margin-top: 3px; /* close as possible to match line-height space above p */
img {
width:100%;

View file

@ -1,5 +1,5 @@
{% extends "wagtailadmin/admin_base.html" %}
{% load wagtailadmin_nav %}
{% load wagtailadmin_tags %}
{% load i18n %}
{% block furniture %}
<div class="nav-wrapper">

View file

@ -1,4 +1,4 @@
{% load compress %}
{% load compress wagtailadmin_tags %}
{% comment %}
CSS declarations to be included on the 'create page' and 'edit page' views
@ -16,6 +16,8 @@
{# we'll want tag-it included, for the benefit of any modals that use it, like images. #}
{# TODO: a method of injecting these sorts of things on demand when the modal is spawned #}
<link rel="stylesheet" href="{{ STATIC_URL }}wagtailadmin/scss/vendor/jquery.tagit.css">
{% hook_output 'insert_editor_css' %}
{% endcompress %}
{{ edit_handler.form.media.css }}

View file

@ -1,5 +1,4 @@
{% load compress %}
{% load localize %}
{% load wagtailadmin_tags compress localize %}
{% comment %}
Javascript declarations to be included on the 'create page' and 'edit page' views
@ -22,15 +21,17 @@
<script src="{{ STATIC_URL }}wagtailadmin/js/page-chooser.js"></script>
{% comment %}
TODO: have a mechanism to specify image-chooser.js (and hallo-wagtailimage.js)
within the wagtailimages app -
ideally wagtailadmin shouldn't have to know anything at all about wagtailimages
TODO: use the insert_editor_js hook to inject things like image-chooser.js and hallo-wagtailimage.js
from their respective apps such as wagtailimages -
ideally wagtailadmin shouldn't have to know anything at all about wagtailimages.
TODO: a method of injecting these sorts of things on demand when the modal is spawned.
{% endcomment %}
<script src="{{ STATIC_URL }}wagtailimages/js/image-chooser.js"></script>
<script src="{{ STATIC_URL }}wagtaildocs/js/document-chooser.js"></script>
<script src="{{ STATIC_URL }}wagtailsnippets/js/snippet-chooser.js"></script>
<script src="{{ STATIC_URL }}admin/js/urlify.js"></script>
{% hook_output 'insert_editor_js' %}
{% endcompress %}
{% comment %}

View file

@ -1,6 +1,6 @@
{% extends "wagtailadmin/base.html" %}
{% load i18n %}
{% load meta_description %}
{% load wagtailadmin_tags %}
{% block bodyclass %}menu-explorer{% endblock %}
{% block titletag %}{% blocktrans with title=parent_page.title %}Create a page in {{ title }}{% endblocktrans %}{% endblock %}

View file

@ -1,5 +1,5 @@
{% extends "wagtailadmin/base.html" %}
{% load page_permissions %}
{% load wagtailadmin_tags %}
{% load i18n %}
{% block titletag %}{% blocktrans with page_type=content_type.model_class.get_verbose_name %}New {{ page_type }}{% endblocktrans %}{% endblock %}

View file

@ -1,5 +1,5 @@
{% extends "wagtailadmin/base.html" %}
{% load page_permissions %}
{% load wagtailadmin_tags %}
{% load gravatar %}
{% load i18n %}
{% block titletag %}{% blocktrans with title=page.title %}Editing {{ title }}{% endblocktrans %}{% endblock %}

View file

@ -1,6 +1,6 @@
{% extends "wagtailadmin/base.html" %}
{% load i18n %}
{% load page_permissions %}
{% load wagtailadmin_tags %}
{% block titletag %}{% blocktrans with title=parent_page.title %}Exploring {{ title }}{% endblocktrans %}{% endblock %}
{% block bodyclass %}menu-explorer page-explorer {% if ordering == 'ord' %}reordering{% endif %}{% endblock %}

View file

@ -1,5 +1,5 @@
{% load i18n %}
{% load page_permissions %}
{% load wagtailadmin_tags %}
<table class="listing{% if full_width %} full-width{% endif %}{% if moving or choosing %} chooser{% endif %}">
{% if orderable %}
<col width="50px" />

View file

@ -1,4 +1,4 @@
{% load wagtailadmin_nav %}
{% load wagtailadmin_tags %}
{% for page, children in nodes %}
<li {% if children %}class="has-children"{% endif %}>

View file

@ -1,4 +1,4 @@
{% load fieldtype %}
{% load wagtailadmin_tags %}
<li class="{{ field.css_classes }} {{ field|fieldtype }} {% if field.errors %}error{% endif %}">
<div class="field">
{% if field|fieldtype != "boolean_field" %}{{ field.label_tag }}{% endif %}

View file

@ -1,4 +1,4 @@
{% load gravatar wagtailadmin_nav %}
{% load gravatar wagtailadmin_tags %}
{% load i18n %}
<nav class="nav-main">
<ul>

View file

@ -1,13 +0,0 @@
from django import template
register = template.Library()
@register.filter("ellipsistrim")
def ellipsistrim(value, max_length):
if len(value) > max_length:
truncd_val = value[:max_length]
if not len(value) == max_length+1 and value[max_length+1] != " ":
truncd_val = truncd_val[:truncd_val.rfind(" ")]
return truncd_val + "..."
return value

View file

@ -1,13 +0,0 @@
from django import template
from wagtail.wagtailcore.util import camelcase_to_underscore
register = template.Library()
@register.filter
def fieldtype(bound_field):
try:
return camelcase_to_underscore(bound_field.field.__class__.__name__)
except AttributeError:
return ""

View file

@ -1,11 +0,0 @@
from django import template
register = template.Library()
@register.filter
def meta_description(model):
try:
return model.model_class()._meta.description
except:
return ""

View file

@ -1,21 +0,0 @@
from django import template
from wagtail.wagtailcore.models import UserPagePermissionsProxy
register = template.Library()
@register.assignment_tag(takes_context=True)
def page_permissions(context, page):
"""
Usage: {% page_permissions page as page_perms %}
Sets the variable 'page_perms' to a PagePermissionTester object that can be queried to find out
what actions the current logged-in user can perform on the given page.
"""
# Create a UserPagePermissionsProxy object to represent the user's global permissions, and
# cache it in the context for the duration of the page request, if one does not exist already
if 'user_page_permissions' not in context:
context['user_page_permissions'] = UserPagePermissionsProxy(context['request'].user)
# Now retrieve a PagePermissionTester from it, specific to the given page
return context['user_page_permissions'].for_page(page)

View file

@ -5,7 +5,8 @@ from django.utils.translation import ugettext_lazy as _
from wagtail.wagtailadmin import hooks
from wagtail.wagtailadmin.menu import MenuItem
from wagtail.wagtailcore.models import get_navigation_menu_items
from wagtail.wagtailcore.models import get_navigation_menu_items, UserPagePermissionsProxy
from wagtail.wagtailcore.util import camelcase_to_underscore
from wagtail.wagtailsnippets.permissions import user_can_edit_snippets # TODO: reorganise into pluggable architecture so that wagtailsnippets registers its own menu item
@ -73,3 +74,57 @@ def main_nav(context):
'menu_items': sorted(menu_items, key=lambda i: i.order),
'request': request,
}
@register.filter("ellipsistrim")
def ellipsistrim(value, max_length):
if len(value) > max_length:
truncd_val = value[:max_length]
if not len(value) == max_length+1 and value[max_length+1] != " ":
truncd_val = truncd_val[:truncd_val.rfind(" ")]
return truncd_val + "..."
return value
@register.filter
def fieldtype(bound_field):
try:
return camelcase_to_underscore(bound_field.field.__class__.__name__)
except AttributeError:
return ""
@register.filter
def meta_description(model):
try:
return model.model_class()._meta.description
except:
return ""
@register.assignment_tag(takes_context=True)
def page_permissions(context, page):
"""
Usage: {% page_permissions page as page_perms %}
Sets the variable 'page_perms' to a PagePermissionTester object that can be queried to find out
what actions the current logged-in user can perform on the given page.
"""
# Create a UserPagePermissionsProxy object to represent the user's global permissions, and
# cache it in the context for the duration of the page request, if one does not exist already
if 'user_page_permissions' not in context:
context['user_page_permissions'] = UserPagePermissionsProxy(context['request'].user)
# Now retrieve a PagePermissionTester from it, specific to the given page
return context['user_page_permissions'].for_page(page)
@register.simple_tag
def hook_output(hook_name):
"""
Example: {% hook_output 'insert_editor_css' %}
Whenever we have a hook whose functions take no parameters and return a string, this tag can be used
to output the concatenation of all of those return values onto the page.
Note that the output is not escaped - it is the hook function's responsibility to escape unsafe content.
"""
snippets = [fn() for fn in hooks.get_hooks(hook_name)]
return u''.join(snippets)

View file

@ -308,3 +308,21 @@ class TestPageMove(TestCase):
def test_page_set_page_position(self):
response = self.client.get(reverse('wagtailadmin_pages_set_page_position', args=(self.test_page.id, )))
self.assertEqual(response.status_code, 200)
class TestEditorHooks(TestCase):
def setUp(self):
self.homepage = Page.objects.get(id=2)
login(self.client)
def test_editor_css_and_js_hooks_on_add(self):
response = self.client.get(reverse('wagtailadmin_pages_create', args=('tests', 'simplepage', self.homepage.id)))
self.assertEqual(response.status_code, 200)
self.assertContains(response, '<link rel="stylesheet" href="/path/to/my/custom.css">')
self.assertContains(response, '<script src="/path/to/my/custom.js"></script>')
def test_editor_css_and_js_hooks_on_edit(self):
response = self.client.get(reverse('wagtailadmin_pages_edit', args=(self.homepage.id, )))
self.assertEqual(response.status_code, 200)
self.assertContains(response, '<link rel="stylesheet" href="/path/to/my/custom.css">')
self.assertContains(response, '<script src="/path/to/my/custom.js"></script>')

View file

@ -1,4 +1,4 @@
{% load image_tags ellipsistrim%}
{% load image_tags %}
{% load i18n %}
{% trans "Insert embed" as ins_emb_str %}
{% include "wagtailadmin/shared/header.html" with title=ins_emb_str merged=1 %}

View file

@ -90,6 +90,6 @@ def search_for_image_formats():
# Define default image formats
register_image_format(Format('fullwidth', 'Full width', 'full-width', 'width-800'))
register_image_format(Format('left', 'Left-aligned', 'left', 'width-500'))
register_image_format(Format('right', 'Right-aligned', 'right', 'width-500'))
register_image_format(Format('fullwidth', 'Full width', 'richtext-image full-width', 'width-800'))
register_image_format(Format('left', 'Left-aligned', 'richtext-image left', 'width-500'))
register_image_format(Format('right', 'Right-aligned', 'richtext-image right', 'width-500'))

View file

@ -1,4 +1,4 @@
{% load image_tags ellipsistrim%}
{% load image_tags %}
{% load i18n %}
{% trans "Choose an image" as choose_str %}
{% include "wagtailadmin/shared/header.html" with title=choose_str merged=1 tabbed=1 icon="image" %}

View file

@ -1,4 +1,4 @@
{% load image_tags ellipsistrim %}
{% load image_tags wagtailadmin_tags %}
{% load i18n %}
{% if images %}
{% if is_searching %}

View file

@ -1,5 +1,5 @@
{% extends "wagtailadmin/base.html" %}
{% load image_tags ellipsistrim %}
{% load image_tags %}
{% load i18n %}
{% block titletag %}{% trans "Images" %}{% endblock %}

View file

@ -1,4 +1,4 @@
{% load image_tags ellipsistrim %}
{% load image_tags wagtailadmin_tags %}
{% load i18n %}
{% if images %}
{% if is_searching %}