Skip to content
Permalink
Browse files

Support building a custom hallo plugins list from a 'features' kwarg …

…on RichTextField
  • Loading branch information
gasman committed Aug 10, 2017
1 parent 31cf4b6 commit 3532f8662aef9fcc169da44edd934ebecf232077
@@ -0,0 +1,29 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.1 on 2017-07-13 22:20
from __future__ import unicode_literals

from django.db import migrations, models
import django.db.models.deletion
import wagtail.wagtailcore.fields


class Migration(migrations.Migration):

dependencies = [
('wagtailcore', '0040_page_draft_title'),
('tests', '0018_multiselect_form_field'),
]

operations = [
migrations.CreateModel(
name='RichTextFieldWithFeaturesPage',
fields=[
('page_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='wagtailcore.Page')),
('body', wagtail.wagtailcore.fields.RichTextField()),
],
options={
'abstract': False,
},
bases=('wagtailcore.page',),
),
]
@@ -911,6 +911,15 @@ class CustomRichBlockFieldPage(Page):
]


class RichTextFieldWithFeaturesPage(Page):
body = RichTextField(features=['blockquote', 'embed', 'made-up-feature'])

content_panels = [
FieldPanel('title', classname="full title"),
FieldPanel('body'),
]


# a page that only contains RichTextField within an InlinePanel,
# to test that the inline child's form media gets pulled through
class SectionedRichTextPageSection(Orderable):
@@ -5,6 +5,7 @@
from django.http import HttpResponse

from wagtail.wagtailadmin.menu import MenuItem
from wagtail.wagtailadmin.rich_text import HalloPlugin
from wagtail.wagtailadmin.search import SearchArea
from wagtail.wagtailcore import hooks
from wagtail.wagtailcore.whitelist import allow_without_attributes, attribute_rule, check_url
@@ -96,5 +97,5 @@ def polite_pages_only(parent_page, pages, request):
@hooks.register('register_rich_text_features')
def register_blockquote_feature(features):
features.register_editor_plugin(
'hallo', 'blockquote', {'plugin_name': 'halloblockquote'}
'hallo', 'blockquote', HalloPlugin(name='halloblockquote')
)
@@ -9,15 +9,30 @@

from wagtail.utils.widgets import WidgetWithScript
from wagtail.wagtailadmin.edit_handlers import RichTextFieldPanel
from wagtail.wagtailcore.rich_text import DbWhitelister, expand_db_html
from wagtail.wagtailcore.rich_text import DbWhitelister, expand_db_html, features


class HalloRichTextArea(WidgetWithScript, widgets.Textarea):
# this class's constructor accepts a 'features' kwarg
accepts_features = True

def get_panel(self):
return RichTextFieldPanel

def __init__(self, *args, **kwargs):
self.options = kwargs.pop('options', None)

self.features = kwargs.pop('features', None)
if self.features is None:
self.plugins = None
else:
# construct a list of plugin objects, by querying the feature registry
# and keeping the non-null responses from get_editor_plugin
self.plugins = filter(None, [
features.get_editor_plugin('hallo', feature_name)
for feature_name in self.features
])

super(HalloRichTextArea, self).__init__(*args, **kwargs)

def render(self, name, value, attrs=None):
@@ -28,15 +43,19 @@ def render(self, name, value, attrs=None):
return super(HalloRichTextArea, self).render(name, translated_value, attrs)

def render_js_init(self, id_, name, value):
try:
plugins = self.options['plugins']
except (TypeError, KeyError):
if self.options is not None and 'plugins' in self.options:
plugin_data = self.options['plugins']
elif self.plugins is not None:
plugin_data = {}
for plugin in self.plugins:
plugin.construct_plugins_list(plugin_data)
else:
# no plugin list specified, so initialise without a plugins arg
# (so that it'll pick up the globally-defined halloPlugins list instead)
return "makeHalloRichTextEditable({0});".format(json.dumps(id_))

return "makeHalloRichTextEditable({0}, {1});".format(
json.dumps(id_), json.dumps(plugins)
json.dumps(id_), json.dumps(plugin_data)
)

def value_from_datadict(self, data, files, name):
@@ -56,20 +75,36 @@ def media(self):
])


class HalloPlugin(object):
def __init__(self, **kwargs):
self.name = kwargs.pop('name', None)
self.options = kwargs.pop('options', {})

def construct_plugins_list(self, plugins):
if self.name is not None:
plugins[self.name] = self.options


DEFAULT_RICH_TEXT_EDITORS = {
'default': {
'WIDGET': 'wagtail.wagtailadmin.rich_text.HalloRichTextArea'
}
}


def get_rich_text_editor_widget(name='default'):
def get_rich_text_editor_widget(name='default', features=None):
editor_settings = getattr(settings, 'WAGTAILADMIN_RICH_TEXT_EDITORS', DEFAULT_RICH_TEXT_EDITORS)

editor = editor_settings[name]
options = editor.get('OPTIONS', None)
cls = import_string(editor['WIDGET'])

kwargs = {}

if options is not None:
kwargs['options'] = options

if options is None:
return import_string(editor['WIDGET'])()
if getattr(cls, 'accepts_features', False):
kwargs['features'] = features

return import_string(editor['WIDGET'])(options=options)
return cls(**kwargs)
@@ -265,3 +265,39 @@ def test_custom_editor_in_rich_text_block(self):

# Check that the custom plugin options are being passed in the hallo initialiser
self.assertIn('makeHalloRichTextEditable("body", {"halloheadings": {"formatBlocks": ["p"]}});', form_html)


class TestHalloJsWithFeaturesKwarg(BaseRichTextEditHandlerTestCase, WagtailTestUtils):

def setUp(self):
super(TestHalloJsWithFeaturesKwarg, self).setUp()

# Find root page
self.root_page = Page.objects.get(id=2)

self.login()

def test_features_list_on_rich_text_field(self):
response = self.client.get(reverse(
'wagtailadmin_pages:add', args=('tests', 'richtextfieldwithfeaturespage', self.root_page.id)
))

# Check status code
self.assertEqual(response.status_code, 200)

# Check that the custom plugin options are being passed in the hallo initialiser
self.assertContains(response, '"halloblockquote":')
self.assertContains(response, '"hallowagtailembeds":')
self.assertNotContains(response, '"halloheadings":')
self.assertNotContains(response, '"hallowagtailimage":')

def test_features_list_on_rich_text_block(self):
block = RichTextBlock(features=['blockquote', 'embed', 'made-up-feature'])

form_html = block.render_form(block.to_python("<p>hello</p>"), 'body')

# Check that the custom plugin options are being passed in the hallo initialiser
self.assertIn('"halloblockquote":', form_html)
self.assertIn('"hallowagtailembeds":', form_html)
self.assertNotIn('"halloheadings":', form_html)
self.assertNotIn('"hallowagtailimage":', form_html)
@@ -450,9 +450,10 @@ class Meta:

class RichTextBlock(FieldBlock):

def __init__(self, required=True, help_text=None, editor='default', **kwargs):
def __init__(self, required=True, help_text=None, editor='default', features=None, **kwargs):
self.field_options = {'required': required, 'help_text': help_text}
self.editor = editor
self.features = features
super(RichTextBlock, self).__init__(**kwargs)

def get_default(self):
@@ -474,7 +475,10 @@ def get_prep_value(self, value):
@cached_property
def field(self):
from wagtail.wagtailadmin.rich_text import get_rich_text_editor_widget
return forms.CharField(widget=get_rich_text_editor_widget(self.editor), **self.field_options)
return forms.CharField(
widget=get_rich_text_editor_widget(self.editor, features=self.features),
**self.field_options
)

def value_for_form(self, value):
# Rich text editors take the source-HTML string as input (and takes care
@@ -12,11 +12,13 @@
class RichTextField(models.TextField):
def __init__(self, *args, **kwargs):
self.editor = kwargs.pop('editor', 'default')
self.features = kwargs.pop('features', None)
# TODO: preserve 'editor' and 'features' when deconstructing for migrations
super(RichTextField, self).__init__(*args, **kwargs)

def formfield(self, **kwargs):
from wagtail.wagtailadmin.rich_text import get_rich_text_editor_widget
defaults = {'widget': get_rich_text_editor_widget(self.editor)}
defaults = {'widget': get_rich_text_editor_widget(self.editor, features=self.features)}
defaults.update(kwargs)
return super(RichTextField, self).formfield(**defaults)

@@ -152,7 +152,7 @@ def test_register_rich_text_features_hook(self):
# plugin, via the register_rich_text_features hook; test that we can retrieve it here
features = FeatureRegistry()
blockquote = features.get_editor_plugin('hallo', 'blockquote')
self.assertEqual(blockquote['plugin_name'], 'halloblockquote')
self.assertEqual(blockquote.name, 'halloblockquote')

def test_missing_editor_plugin_returns_none(self):
features = FeatureRegistry()
@@ -10,6 +10,7 @@
from django.utils.translation import ungettext

from wagtail.wagtailadmin.menu import MenuItem
from wagtail.wagtailadmin.rich_text import HalloPlugin
from wagtail.wagtailadmin.search import SearchArea
from wagtail.wagtailadmin.site_summary import SummaryItem
from wagtail.wagtailcore import hooks
@@ -74,6 +75,14 @@ def editor_js():
)


@hooks.register('register_rich_text_features')
def register_embed_feature(features):
features.register_editor_plugin(
'hallo', 'document-link',
HalloPlugin(name='hallowagtaildoclink')
)


@hooks.register('register_rich_text_link_handler')
def register_document_link_handler():
return ('document', DocumentLinkHandler)
@@ -5,6 +5,7 @@
from django.core import urlresolvers
from django.utils.html import format_html

from wagtail.wagtailadmin.rich_text import HalloPlugin
from wagtail.wagtailcore import hooks
from wagtail.wagtailembeds import urls
from wagtail.wagtailembeds.rich_text import MediaEmbedHandler
@@ -32,6 +33,14 @@ def editor_js():
)


@hooks.register('register_rich_text_features')
def register_embed_feature(features):
features.register_editor_plugin(
'hallo', 'embed',
HalloPlugin(name='hallowagtailembeds')
)


@hooks.register('register_rich_text_embed_handler')
def register_media_embed_handler():
return ('media', MediaEmbedHandler)
@@ -8,6 +8,7 @@
from django.utils.translation import ungettext

from wagtail.wagtailadmin.menu import MenuItem
from wagtail.wagtailadmin.rich_text import HalloPlugin
from wagtail.wagtailadmin.search import SearchArea
from wagtail.wagtailadmin.site_summary import SummaryItem
from wagtail.wagtailcore import hooks
@@ -66,6 +67,14 @@ def editor_js():
)


@hooks.register('register_rich_text_features')
def register_image_feature(features):
features.register_editor_plugin(
'hallo', 'image',
HalloPlugin(name='hallowagtailimage')
)


@hooks.register('register_image_operations')
def register_image_operations():
return [

0 comments on commit 3532f86

Please sign in to comment.