diff --git a/wagtail/tests/migrations/0004_auto_20141008_0122.py b/wagtail/tests/migrations/0004_auto_20141008_0122.py
new file mode 100644
index 000000000..71dcffc03
--- /dev/null
+++ b/wagtail/tests/migrations/0004_auto_20141008_0122.py
@@ -0,0 +1,35 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import models, migrations
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('wagtailcore', '0006_auto_20141008_1706'),
+ ('tests', '0003_auto_20140905_0634'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='PageChooserModel',
+ fields=[
+ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
+ ('page', models.ForeignKey(help_text=b'help text', to='wagtailcore.Page')),
+ ],
+ options={
+ },
+ bases=(models.Model,),
+ ),
+ migrations.CreateModel(
+ name='SnippetChooserModel',
+ fields=[
+ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
+ ('advert', models.ForeignKey(help_text=b'help text', to='tests.Advert')),
+ ],
+ options={
+ },
+ bases=(models.Model,),
+ ),
+ ]
diff --git a/wagtail/tests/models.py b/wagtail/tests/models.py
index 53a9b2796..3266a0fd9 100644
--- a/wagtail/tests/models.py
+++ b/wagtail/tests/models.py
@@ -17,6 +17,7 @@ from wagtail.wagtailimages.edit_handlers import ImageChooserPanel
from wagtail.wagtaildocs.edit_handlers import DocumentChooserPanel
from wagtail.wagtailforms.models import AbstractEmailForm, AbstractFormField
from wagtail.wagtailsnippets.models import register_snippet
+from wagtail.wagtailsnippets.edit_handlers import SnippetChooserPanel
from wagtail.wagtailsearch import index
from wagtail.contrib.wagtailroutablepage.models import RoutablePage
@@ -463,3 +464,15 @@ class TaggedPageTag(TaggedItemBase):
class TaggedPage(Page):
tags = ClusterTaggableManager(through=TaggedPageTag, blank=True)
+
+
+class PageChooserModel(models.Model):
+ page = models.ForeignKey('wagtailcore.Page', help_text='help text')
+
+
+class SnippetChooserModel(models.Model):
+ advert = models.ForeignKey(Advert, help_text='help text')
+
+ panels = [
+ SnippetChooserPanel('advert', Advert),
+ ]
diff --git a/wagtail/utils/widgets.py b/wagtail/utils/widgets.py
new file mode 100644
index 000000000..48070451d
--- /dev/null
+++ b/wagtail/utils/widgets.py
@@ -0,0 +1,19 @@
+from django.forms.widgets import Widget
+from django.utils.safestring import mark_safe
+
+
+class WidgetWithScript(Widget):
+ def render(self, name, value, attrs=None):
+ widget = super(WidgetWithScript, self).render(name, value, attrs)
+
+ final_attrs = self.build_attrs(attrs, name=name)
+ id_ = final_attrs.get('id', None)
+ if 'id_' is None:
+ return widget
+
+ js = self.render_js_init(id_, name, value)
+ out = '{0}'.format(widget, js)
+ return mark_safe(out)
+
+ def render_js_init(self, id_, name, value):
+ return ''
diff --git a/wagtail/wagtailadmin/edit_handlers.py b/wagtail/wagtailadmin/edit_handlers.py
index b686bd259..e2ce14c35 100644
--- a/wagtail/wagtailadmin/edit_handlers.py
+++ b/wagtail/wagtailadmin/edit_handlers.py
@@ -2,39 +2,32 @@ from __future__ import unicode_literals
import copy
-from six import string_types
from six import text_type
-from taggit.forms import TagWidget
from modelcluster.forms import ClusterForm, ClusterFormMetaclass
+
+from django.db import models
from django.template.loader import render_to_string
-from django.template.defaultfilters import addslashes
from django.utils.safestring import mark_safe
from django import forms
from django.forms.models import fields_for_model
from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ObjectDoesNotExist, ImproperlyConfigured
-from django.core.urlresolvers import reverse
from django.utils.translation import ugettext_lazy
+from taggit.managers import TaggableManager
+
+from wagtail.wagtailadmin import widgets
from wagtail.wagtailcore.models import Page
-from wagtail.wagtailcore.fields import RichTextArea
from wagtail.wagtailcore.utils import camelcase_to_underscore, resolve_model_string
-FORM_FIELD_OVERRIDES = {}
-
-WIDGET_JS = {
- forms.DateInput: (lambda id: "initDateChooser(fixPrefix('%s'));" % id),
- forms.TimeInput: (lambda id: "initTimeChooser(fixPrefix('%s'));" % id),
- forms.DateTimeInput: (lambda id: "initDateTimeChooser(fixPrefix('%s'));" % id),
- RichTextArea: (lambda id: "makeRichTextEditable(fixPrefix('%s'));" % id),
- TagWidget: (
- lambda id: "initTagField(fixPrefix('%s'), '%s');" % (
- id, addslashes(reverse('wagtailadmin_tag_autocomplete'))
- )
- ),
+FORM_FIELD_OVERRIDES = {
+ models.DateField: {'widget': widgets.AdminDateInput},
+ models.TimeField: {'widget': widgets.AdminTimeInput},
+ models.DateTimeField: {'widget': widgets.AdminDateTimeInput},
+ TaggableManager: {'widget': widgets.AdminTagWidget}
}
@@ -53,6 +46,10 @@ def formfield_for_dbfield(db_field, **kwargs):
return db_field.formfield(**kwargs)
+def widget_with_script(widget, script):
+ return mark_safe('{0}'.format(widget, script))
+
+
class WagtailAdminModelFormMetaclass(ClusterFormMetaclass):
# Override the behaviour of the regular ModelForm metaclass -
# which handles the translation of model fields to form fields -
@@ -189,7 +186,7 @@ class EditHandler(object):
def classes(self):
"""
- Additional CSS classnames to add to whatever kind of object this is at output.
+ Additional CSS classnames to add to whatever kind of object this is at output.
Subclasses of EditHandler should override this, invoking super(B, self).classes() to
append more classes specific to the situation.
"""
@@ -224,13 +221,6 @@ class EditHandler(object):
# by default, assume that the subclass provides a catch-all render() method
return self.render()
- def render_js(self):
- """
- Render a snippet of Javascript code to be executed when this object's rendered
- HTML is inserted into the DOM. (This won't necessarily happen on page load...)
- """
- return ""
-
def rendered_fields(self):
"""
return a list of the fields of the passed form which are rendered by this
@@ -303,9 +293,6 @@ class BaseCompositeEditHandler(EditHandler):
'self': self
}))
- def render_js(self):
- return mark_safe('\n'.join([handler.render_js() for handler in self.children]))
-
def rendered_fields(self):
result = []
for handler in self.children:
@@ -337,21 +324,24 @@ def ObjectList(children, heading="", classname=""):
class BaseFieldRowPanel(BaseCompositeEditHandler):
template = "wagtailadmin/edit_handlers/field_row_panel.html"
+
def FieldRowPanel(children, classname=""):
return type(str('_FieldRowPanel'), (BaseFieldRowPanel,), {
'children': children,
'classname': classname,
})
+
class BaseMultiFieldPanel(BaseCompositeEditHandler):
template = "wagtailadmin/edit_handlers/multi_field_panel.html"
def classes(self):
classes = super(BaseMultiFieldPanel, self).classes()
classes.append("multi-field")
-
+
return classes
+
def MultiFieldPanel(children, heading="", classname=""):
return type(str('_MultiFieldPanel'), (BaseMultiFieldPanel,), {
'children': children,
@@ -369,16 +359,16 @@ class BaseFieldPanel(EditHandler):
self.help_text = self.bound_field.help_text
def classes(self):
- classes = super(BaseFieldPanel, self).classes();
+ classes = super(BaseFieldPanel, self).classes()
if self.bound_field.field.required:
classes.append("required")
if self.bound_field.errors:
classes.append("error")
-
+
classes.append(self.field_type())
classes.append("single-field")
-
+
return classes
def field_type(self):
@@ -392,23 +382,16 @@ class BaseFieldPanel(EditHandler):
'field_content': self.render_as_field(show_help_text=False),
}))
- def render_js(self):
- try:
- # see if there's an entry for this widget type in WIDGET_JS
- js_func = WIDGET_JS[self.bound_field.field.widget.__class__]
- except KeyError:
- return ''
-
- return mark_safe(js_func(self.bound_field.id_for_label))
-
field_template = "wagtailadmin/edit_handlers/field_panel_field.html"
- def render_as_field(self, show_help_text=True):
- return mark_safe(render_to_string(self.field_template, {
+ def render_as_field(self, show_help_text=True, extra_context={}):
+ context = {
'field': self.bound_field,
'field_type': self.field_type(),
'show_help_text': show_help_text,
- }))
+ }
+ context.update(extra_context)
+ return mark_safe(render_to_string(self.field_template, context))
def rendered_fields(self):
return [self.field_name]
@@ -422,8 +405,7 @@ def FieldPanel(field_name, classname=""):
class BaseRichTextFieldPanel(BaseFieldPanel):
- def render_js(self):
- return mark_safe("makeRichTextEditable(fixPrefix('%s'));" % self.bound_field.id_for_label)
+ pass
def RichTextFieldPanel(field_name):
@@ -442,14 +424,7 @@ class BaseChooserPanel(BaseFieldPanel):
* field_template
* object_type_name - something like 'image' which will be used as the var name
for the object instance in the field_template
- * js_function_name - a JS function responsible for the modal workflow; this receives
- the ID of the hidden field as a parameter, and should ultimately populate that field
- with the appropriate object ID. If the function requires any other parameters, the
- subclass will need to override render_js instead.
"""
- @classmethod
- def widget_overrides(cls):
- return {cls.field_name: forms.HiddenInput}
def get_chosen_item(self):
try:
@@ -460,17 +435,16 @@ class BaseChooserPanel(BaseFieldPanel):
# like every other unpopulated field type. Yay consistency!
return None
- def render_as_field(self, show_help_text=True):
+ def render_as_field(self, show_help_text=True, extra_context={}):
instance_obj = self.get_chosen_item()
- return mark_safe(render_to_string(self.field_template, {
+ context = {
'field': self.bound_field,
self.object_type_name: instance_obj,
'is_chosen': bool(instance_obj),
'show_help_text': show_help_text,
- }))
-
- def render_js(self):
- return mark_safe("%s(fixPrefix('%s'));" % (self.js_function_name, self.bound_field.id_for_label))
+ }
+ context.update(extra_context)
+ return mark_safe(render_to_string(self.field_template, context))
class BasePageChooserPanel(BaseChooserPanel):
@@ -479,6 +453,11 @@ class BasePageChooserPanel(BaseChooserPanel):
_target_content_type = None
+ @classmethod
+ def widget_overrides(cls):
+ return {cls.field_name: widgets.AdminPageChooser(
+ content_type=cls.target_content_type())}
+
@classmethod
def target_content_type(cls):
if cls._target_content_type is None:
@@ -499,28 +478,13 @@ class BasePageChooserPanel(BaseChooserPanel):
return cls._target_content_type
- def render_as_field(self, show_help_text=True):
- instance_obj = self.get_chosen_item()
- return mark_safe(render_to_string(self.field_template, {
- 'field': self.bound_field,
- self.object_type_name: instance_obj,
- 'is_chosen': bool(instance_obj),
- 'show_help_text': show_help_text,
+ def render_as_field(self, show_help_text=True, extra_context={}):
+ context = {
'choose_another_text_str': ugettext_lazy("Choose another page"),
'choose_one_text_str': ugettext_lazy("Choose a page"),
- }))
-
- def render_js(self):
- page = self.get_chosen_item()
- parent = page.get_parent() if page else None
- content_type = self.__class__.target_content_type()
-
- return mark_safe("createPageChooser(fixPrefix('%s'), '%s.%s', %s);" % (
- self.bound_field.id_for_label,
- content_type.app_label,
- content_type.model,
- (parent.id if parent else 'null'),
- ))
+ }
+ context.update(extra_context)
+ return super(BasePageChooserPanel, self).render_as_field(show_help_text, context)
def PageChooserPanel(field_name, page_type=None):
@@ -596,14 +560,16 @@ class BaseInlinePanel(EditHandler):
template = "wagtailadmin/edit_handlers/inline_panel.html"
def render(self):
- return mark_safe(render_to_string(self.template, {
+ formset = render_to_string(self.template, {
'self': self,
'can_order': self.formset.can_order,
- }))
+ })
+ js = self.render_js_init()
+ return widget_with_script(formset, js)
js_template = "wagtailadmin/edit_handlers/inline_panel.js"
- def render_js(self):
+ def render_js_init(self):
return mark_safe(render_to_string(self.js_template, {
'self': self,
'can_order': self.formset.can_order,
@@ -622,14 +588,14 @@ def InlinePanel(base_model, relation_name, panels=None, label='', help_text=''):
# This allows users to include the publishing panel in their own per-model override
-# without having to write these fields out by hand, potentially losing 'classname'
+# without having to write these fields out by hand, potentially losing 'classname'
# and therefore the associated styling of the publishing panel
def PublishingPanel():
return MultiFieldPanel([
FieldRowPanel([
FieldPanel('go_live_at'),
FieldPanel('expire_at'),
- ], classname="label-above"),
+ ], classname="label-above"),
], ugettext_lazy('Scheduled publishing'), classname="publishing")
diff --git a/wagtail/wagtailadmin/static/wagtailadmin/js/expanding_formset.js b/wagtail/wagtailadmin/static/wagtailadmin/js/expanding_formset.js
index 9c6007dfd..df7470c6e 100644
--- a/wagtail/wagtailadmin/static/wagtailadmin/js/expanding_formset.js
+++ b/wagtail/wagtailadmin/static/wagtailadmin/js/expanding_formset.js
@@ -16,12 +16,15 @@ function buildExpandingFormset(prefix, opts) {
}
addButton.click(function() {
- var newFormHtml = emptyFormTemplate.replace(/__prefix__/g, formCount);
+ var newFormHtml = emptyFormTemplate
+ .replace(/__prefix__/g, formCount)
+ .replace(/<-(-*)\/script>/g, '<$1/script>');
formContainer.append(newFormHtml);
if (opts.onAdd) {
opts.onAdd(formCount);
}
+
formCount++;
totalFormsInput.val(formCount);
});
diff --git a/wagtail/wagtailadmin/static/wagtailadmin/js/page-editor.js b/wagtail/wagtailadmin/static/wagtailadmin/js/page-editor.js
index 0f54a4576..b3bcef55a 100644
--- a/wagtail/wagtailadmin/static/wagtailadmin/js/page-editor.js
+++ b/wagtail/wagtailadmin/static/wagtailadmin/js/page-editor.js
@@ -254,7 +254,7 @@ function InlinePanel(opts) {
}
self.updateMoveButtonDisabledStates();
- opts.onAdd(fixPrefix);
+ opts.onAdd();
}
});
diff --git a/wagtail/wagtailadmin/templates/wagtailadmin/edit_handlers/inline_panel.html b/wagtail/wagtailadmin/templates/wagtailadmin/edit_handlers/inline_panel.html
index 374018bf5..e6b2ab3c5 100644
--- a/wagtail/wagtailadmin/templates/wagtailadmin/edit_handlers/inline_panel.html
+++ b/wagtail/wagtailadmin/templates/wagtailadmin/edit_handlers/inline_panel.html
@@ -1,3 +1,5 @@
+{% load wagtailadmin_tags %}
+
{{ self.formset.management_form }}
diff --git a/wagtail/wagtailadmin/templates/wagtailadmin/edit_handlers/inline_panel.js b/wagtail/wagtailadmin/templates/wagtailadmin/edit_handlers/inline_panel.js
index 7651602f4..9e46a880f 100644
--- a/wagtail/wagtailadmin/templates/wagtailadmin/edit_handlers/inline_panel.js
+++ b/wagtail/wagtailadmin/templates/wagtailadmin/edit_handlers/inline_panel.js
@@ -1,17 +1,14 @@
(function() {
var panel = InlinePanel({
- formsetPrefix: fixPrefix("id_{{ self.formset.prefix }}"),
- emptyChildFormPrefix: fixPrefix("{{ self.empty_child.form.prefix }}"),
+ formsetPrefix: "id_{{ self.formset.prefix }}",
+ emptyChildFormPrefix: "{{ self.empty_child.form.prefix }}",
canOrder: {% if can_order %}true{% else %}false{% endif %},
- onAdd: function(fixPrefix) {
- {{ self.empty_child.render_js }}
- }
+ onAdd: function() { }
});
{% for child in self.children %}
- {{ child.render_js }}
- panel.initChildControls(fixPrefix("{{ child.form.prefix }}"));
+ panel.initChildControls("{{ child.form.prefix }}");
{% endfor %}
panel.setHasContent();
panel.updateMoveButtonDisabledStates();
diff --git a/wagtail/wagtailadmin/templates/wagtailadmin/pages/_editor_js.html b/wagtail/wagtailadmin/templates/wagtailadmin/pages/_editor_js.html
index a3185e096..b83ca3d6a 100644
--- a/wagtail/wagtailadmin/templates/wagtailadmin/pages/_editor_js.html
+++ b/wagtail/wagtailadmin/templates/wagtailadmin/pages/_editor_js.html
@@ -32,11 +32,3 @@
Additional js from widgets media. Allows for custom widgets in admin panel.
{% endcomment %}
{{ edit_handler.form.media.js }}
-
-
diff --git a/wagtail/wagtailadmin/templatetags/wagtailadmin_tags.py b/wagtail/wagtailadmin/templatetags/wagtailadmin_tags.py
index 7b5d830e6..2a31ffb50 100644
--- a/wagtail/wagtailadmin/templatetags/wagtailadmin_tags.py
+++ b/wagtail/wagtailadmin/templatetags/wagtailadmin_tags.py
@@ -1,5 +1,7 @@
from __future__ import unicode_literals
+import re
+
from django.conf import settings
from django import template
@@ -120,3 +122,25 @@ def hook_output(hook_name):
@register.assignment_tag
def usage_count_enabled():
return getattr(settings, 'WAGTAIL_USAGE_COUNT_ENABLED', False)
+
+
+class EscapeScriptNode(template.Node):
+ TAG_NAME = 'escapescript'
+ SCRIPT_RE = re.compile(r'<(-*)/script>')
+
+ def __init__(self, nodelist):
+ super(EscapeScriptNode, self).__init__()
+ self.nodelist = nodelist
+
+ def render(self, context):
+ out = self.nodelist.render(context)
+ escaped_out = self.SCRIPT_RE.sub(r'<-\1/script>', out)
+ return escaped_out
+
+ @classmethod
+ def handle(cls, parser, token):
+ nodelist = parser.parse(('end' + EscapeScriptNode.TAG_NAME,))
+ parser.delete_first_token()
+ return cls(nodelist)
+
+register.tag(EscapeScriptNode.TAG_NAME, EscapeScriptNode.handle)
diff --git a/wagtail/wagtailadmin/tests/test_edit_handlers.py b/wagtail/wagtailadmin/tests/test_edit_handlers.py
index eaedccc2c..264f2170e 100644
--- a/wagtail/wagtailadmin/tests/test_edit_handlers.py
+++ b/wagtail/wagtailadmin/tests/test_edit_handlers.py
@@ -1,25 +1,25 @@
from mock import MagicMock
-from django.test import TestCase
from django.core.exceptions import ImproperlyConfigured
-from django.forms.widgets import HiddenInput
+from django.test import TestCase
from wagtail.wagtailadmin.edit_handlers import (
get_form_for_model,
extract_panel_definitions_from_model_class,
BaseFieldPanel,
FieldPanel,
- RichTextFieldPanel,
- EditHandler,
WagtailAdminModelForm,
BaseTabbedInterface,
TabbedInterface,
BaseObjectList,
ObjectList,
PageChooserPanel,
- InlinePanel
+ InlinePanel,
)
+
+from wagtail.wagtailadmin.widgets import AdminPageChooser
from wagtail.wagtailcore.models import Page, Site
+from wagtail.tests.models import PageChooserModel
class TestGetFormForModel(TestCase):
@@ -63,9 +63,6 @@ class TestExtractPanelDefinitionsFromModelClass(TestCase):
class TestTabbedInterface(TestCase):
class FakeChild(object):
class FakeGrandchild(object):
- def render_js(self):
- return "rendered js"
-
def rendered_fields(self):
return ["rendered fields"]
@@ -101,10 +98,6 @@ class TestTabbedInterface(TestCase):
result = self.tabbed_interface.render()
self.assertIn('
', result)
- def test_render_js(self):
- result = self.tabbed_interface.render_js()
- self.assertEqual(result, 'rendered js')
-
def test_rendered_fields(self):
result = self.tabbed_interface.rendered_fields()
self.assertEqual(result, ["rendered fields"])
@@ -149,17 +142,6 @@ class TestFieldPanel(TestCase):
self.assertIn('
',
result)
- def test_render_js_unknown_widget(self):
- field = self.FakeField()
- bound_field = self.FakeField()
- widget = self.FakeField()
- field.widget = widget
- bound_field.field = field
- self.field_panel.bound_field = bound_field
- result = self.field_panel.render_js()
- self.assertEqual(result,
- '')
-
def test_render_as_field(self):
field = self.FakeField()
bound_field = self.FakeField()
@@ -194,10 +176,6 @@ class TestFieldPanel(TestCase):
result = FieldPanel('barbecue', 'snowman').get_form_class(Page)
self.assertTrue(issubclass(result, WagtailAdminModelForm))
- def test_render_js(self):
- result = self.field_panel.render_js()
- self.assertEqual(result, "")
-
def test_render_missing_fields(self):
fake_form = self.FakeForm()
fake_form["foo"] = "bar"
@@ -211,59 +189,33 @@ class TestFieldPanel(TestCase):
self.assertIn("bar", self.field_panel.render_form_content())
-class TestRichTextFieldPanel(TestCase):
- class FakeField(object):
- label = 'label'
- help_text = 'help text'
- errors = ['errors']
- id_for_label = 'id for label'
-
- def test_render_js(self):
- fake_field = self.FakeField()
- rich_text_field_panel = RichTextFieldPanel('barbecue')(
- instance=True,
- form={'barbecue': fake_field})
- result = rich_text_field_panel.render_js()
- self.assertEqual(result,
- "makeRichTextEditable(fixPrefix('id for label'));")
-
-
class TestPageChooserPanel(TestCase):
- class FakeField(object):
- label = 'label'
- help_text = 'help text'
- errors = ['errors']
- id_for_label = 'id for label'
-
- class FakeInstance(object):
- class FakePage(object):
- class FakeParent(object):
- id = 1
-
- name = 'fake page'
-
- def get_parent(self):
- return self.FakeParent()
-
- def __init__(self):
- fake_page = self.FakePage()
- self.barbecue = fake_page
def setUp(self):
- fake_field = self.FakeField()
- fake_instance = self.FakeInstance()
- self.page_chooser_panel = PageChooserPanel('barbecue')(
- instance=fake_instance,
- form={'barbecue': fake_field})
+ model = PageChooserModel
+ self.chosen_page = Page.objects.get(pk=2)
+ test_instance = model.objects.create(page=self.chosen_page)
+ self.dotted_model = model._meta.app_label + '.' + model._meta.model_name
- def test_render_js(self):
- result = self.page_chooser_panel.render_js()
- self.assertEqual(result,
- "createPageChooser(fixPrefix('id for label'), 'wagtailcore.page', 1);")
+ self.page_chooser_panel_class = PageChooserPanel('page', model)
+
+ form_class = get_form_for_model(model, widgets=self.page_chooser_panel_class.widget_overrides())
+ form = form_class(instance=test_instance)
+ form.errors['page'] = form.error_class(['errors'])
+
+ self.page_chooser_panel = self.page_chooser_panel_class(instance=test_instance,
+ form=form)
+
+ def test_render_js_init(self):
+ result = self.page_chooser_panel.render_as_field()
+ self.assertIn(
+ 'createPageChooser("{id}", "{model}", {parent});'.format(
+ id="id_page", model=self.dotted_model, parent=self.chosen_page.get_parent().id),
+ result)
def test_get_chosen_item(self):
result = self.page_chooser_panel.get_chosen_item()
- self.assertEqual(result.name, 'fake page')
+ self.assertEqual(result, self.chosen_page)
def test_render_as_field(self):
result = self.page_chooser_panel.render_as_field()
@@ -272,7 +224,7 @@ class TestPageChooserPanel(TestCase):
def test_widget_overrides(self):
result = self.page_chooser_panel.widget_overrides()
- self.assertEqual(result, {'barbecue': HiddenInput})
+ self.assertIsInstance(result['page'], AdminPageChooser)
def test_target_content_type(self):
result = PageChooserPanel(
@@ -347,9 +299,6 @@ class TestInlinePanel(TestCase):
name = 'mock panel'
class FakeChild(object):
- def render_js(self):
- return "rendered js"
-
def rendered_fields(self):
return ["rendered fields"]
@@ -407,10 +356,10 @@ class TestInlinePanel(TestCase):
form=self.fake_field)
self.assertIn('Add foo', inline_panel.render())
- def test_render_js(self):
+ def test_render_js_init(self):
inline_panel = InlinePanel(self.mock_model,
'formset')(
instance=self.fake_instance,
form=self.fake_field)
self.assertIn('var panel = InlinePanel({',
- inline_panel.render_js())
+ inline_panel.render_js_init())
diff --git a/wagtail/wagtailadmin/widgets.py b/wagtail/wagtailadmin/widgets.py
new file mode 100644
index 000000000..ca49e05cc
--- /dev/null
+++ b/wagtail/wagtailadmin/widgets.py
@@ -0,0 +1,55 @@
+from __future__ import absolute_import, unicode_literals
+
+import json
+
+from django.core.urlresolvers import reverse
+from django.forms import widgets
+
+from wagtail.utils.widgets import WidgetWithScript
+from wagtail.wagtailcore.models import Page
+
+from taggit.forms import TagWidget
+
+
+class AdminDateInput(WidgetWithScript, widgets.DateInput):
+ def render_js_init(self, id_, name, value):
+ return 'initDateChooser({});'.format(json.dumps(id_))
+
+
+class AdminTimeInput(WidgetWithScript, widgets.TimeInput):
+ def render_js_init(self, id_, name, value):
+ return 'initTimeChooser({});'.format(json.dumps(id_))
+
+
+class AdminDateTimeInput(WidgetWithScript, widgets.DateTimeInput):
+ def render_js_init(self, id_, name, value):
+ return 'initDateTimeChooser({});'.format(json.dumps(id_))
+
+
+class AdminTagWidget(WidgetWithScript, TagWidget):
+ def render_js_init(self, id_, name, value):
+ return "initTagField({0}, {1});".format(
+ json.dumps(id_),
+ json.dumps(reverse('wagtailadmin_tag_autocomplete')))
+
+
+class AdminPageChooser(WidgetWithScript, widgets.Input):
+ input_type = 'hidden'
+ target_content_type = None
+
+ def __init__(self, content_type=None, **kwargs):
+ super(AdminPageChooser, self).__init__(**kwargs)
+ if content_type is not None:
+ self.target_content_type = content_type
+
+ def render_js_init(self, id_, name, value):
+ page = Page.objects.get(pk=value) if value else None
+ parent = page.get_parent() if page else None
+ content_type = self.target_content_type
+
+ return "createPageChooser({id}, {content_type}, {parent});".format(
+ id=json.dumps(id_),
+ content_type=json.dumps('{app}.{model}'.format(
+ app=content_type.app_label,
+ model=content_type.model)),
+ parent=json.dumps(parent.id if parent else None))
diff --git a/wagtail/wagtailcore/fields.py b/wagtail/wagtailcore/fields.py
index a1362b074..a1840859e 100644
--- a/wagtail/wagtailcore/fields.py
+++ b/wagtail/wagtailcore/fields.py
@@ -1,4 +1,8 @@
+from __future__ import absolute_import, unicode_literals
+
+import json
import django
+
from django.db import models
from django.forms import Textarea
@@ -7,9 +11,10 @@ if django.VERSION < (1, 7):
add_introspection_rules([], ["^wagtail\.wagtailcore\.fields\.RichTextField"])
from wagtail.wagtailcore.rich_text import DbWhitelister, expand_db_html
+from wagtail.utils.widgets import WidgetWithScript
-class RichTextArea(Textarea):
+class RichTextArea(WidgetWithScript, Textarea):
def get_panel(self):
from wagtail.wagtailadmin.edit_handlers import RichTextFieldPanel
return RichTextFieldPanel
@@ -21,6 +26,9 @@ class RichTextArea(Textarea):
translated_value = expand_db_html(value, for_editor=True)
return super(RichTextArea, self).render(name, translated_value, attrs)
+ def render_js_init(self, id_, name, value):
+ return "makeRichTextEditable({0});".format(json.dumps(id_))
+
def value_from_datadict(self, data, files, name):
original_value = super(RichTextArea, self).value_from_datadict(data, files, name)
if original_value is None:
diff --git a/wagtail/wagtaildocs/edit_handlers.py b/wagtail/wagtaildocs/edit_handlers.py
index 66c426fac..03c48f412 100644
--- a/wagtail/wagtaildocs/edit_handlers.py
+++ b/wagtail/wagtaildocs/edit_handlers.py
@@ -1,13 +1,19 @@
+from __future__ import absolute_import, unicode_literals
+
from wagtail.wagtailadmin.edit_handlers import BaseChooserPanel
+from .widgets import AdminDocumentChooser
class BaseDocumentChooserPanel(BaseChooserPanel):
field_template = "wagtaildocs/edit_handlers/document_chooser_panel.html"
object_type_name = "document"
- js_function_name = "createDocumentChooser"
+
+ @classmethod
+ def widget_overrides(cls):
+ return {cls.field_name: AdminDocumentChooser}
def DocumentChooserPanel(field_name):
- return type('_DocumentChooserPanel', (BaseDocumentChooserPanel,), {
+ return type(str('_DocumentChooserPanel'), (BaseDocumentChooserPanel,), {
'field_name': field_name,
})
diff --git a/wagtail/wagtaildocs/widgets.py b/wagtail/wagtaildocs/widgets.py
new file mode 100644
index 000000000..fd23bec8c
--- /dev/null
+++ b/wagtail/wagtaildocs/widgets.py
@@ -0,0 +1,14 @@
+from __future__ import absolute_import, unicode_literals
+
+import json
+
+from django.forms import widgets
+
+from wagtail.utils.widgets import WidgetWithScript
+
+
+class AdminDocumentChooser(WidgetWithScript, widgets.Input):
+ input_type = 'hidden'
+
+ def render_js_init(self, id_, name, value):
+ return "createDocumentChooser({0});".format(json.dumps(id_))
diff --git a/wagtail/wagtailimages/edit_handlers.py b/wagtail/wagtailimages/edit_handlers.py
index 7165b31e5..059355c08 100644
--- a/wagtail/wagtailimages/edit_handlers.py
+++ b/wagtail/wagtailimages/edit_handlers.py
@@ -1,13 +1,19 @@
+from __future__ import absolute_import, unicode_literals
+
from wagtail.wagtailadmin.edit_handlers import BaseChooserPanel
+from .widgets import AdminImageChooser
class BaseImageChooserPanel(BaseChooserPanel):
field_template = "wagtailimages/edit_handlers/image_chooser_panel.html"
object_type_name = "image"
- js_function_name = "createImageChooser"
+
+ @classmethod
+ def widget_overrides(cls):
+ return {cls.field_name: AdminImageChooser}
def ImageChooserPanel(field_name):
- return type('_ImageChooserPanel', (BaseImageChooserPanel,), {
+ return type(str('_ImageChooserPanel'), (BaseImageChooserPanel,), {
'field_name': field_name,
})
diff --git a/wagtail/wagtailimages/widgets.py b/wagtail/wagtailimages/widgets.py
new file mode 100644
index 000000000..3580e74a1
--- /dev/null
+++ b/wagtail/wagtailimages/widgets.py
@@ -0,0 +1,14 @@
+from __future__ import absolute_import, unicode_literals
+
+import json
+
+from django.forms import widgets
+
+from wagtail.utils.widgets import WidgetWithScript
+
+
+class AdminImageChooser(WidgetWithScript, widgets.Input):
+ input_type = 'hidden'
+
+ def render_js_init(self, id_, name, value):
+ return "createImageChooser({0});".format(json.dumps(id_))
diff --git a/wagtail/wagtailsearch/forms.py b/wagtail/wagtailsearch/forms.py
index 3d1115110..aacf86036 100644
--- a/wagtail/wagtailsearch/forms.py
+++ b/wagtail/wagtailsearch/forms.py
@@ -1,7 +1,10 @@
from django import forms
+from django.contrib.contenttypes.models import ContentType
from django.forms.models import inlineformset_factory
from django.utils.translation import ugettext_lazy as _
+from wagtail.wagtailadmin.widgets import AdminPageChooser
+from wagtail.wagtailcore.models import Page
from wagtail.wagtailsearch import models
@@ -18,7 +21,7 @@ class EditorsPickForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(EditorsPickForm, self).__init__(*args, **kwargs)
- self.fields['page'].widget = forms.HiddenInput()
+ self.fields['page'].widget = AdminPageChooser(ContentType.objects.get_for_model(Page))
class Meta:
model = models.EditorsPick
@@ -35,6 +38,7 @@ EditorsPickFormSetBase = inlineformset_factory(models.Query, models.EditorsPick,
class EditorsPickFormSet(EditorsPickFormSetBase):
minimum_forms = 1
minimum_forms_message = _("Please specify at least one recommendation for this search term.")
+
def add_fields(self, form, *args, **kwargs):
super(EditorsPickFormSet, self).add_fields(form, *args, **kwargs)
diff --git a/wagtail/wagtailsearch/templates/wagtailsearch/editorspicks/add.html b/wagtail/wagtailsearch/templates/wagtailsearch/editorspicks/add.html
index 6c2a4c000..5b380b669 100644
--- a/wagtail/wagtailsearch/templates/wagtailsearch/editorspicks/add.html
+++ b/wagtail/wagtailsearch/templates/wagtailsearch/editorspicks/add.html
@@ -29,13 +29,6 @@
-{% endblock %}
-
-{% block extra_css %}
- {% include "wagtailadmin/pages/_editor_css.html" %}
-{% endblock %}
-{% block extra_js %}
- {% include "wagtailadmin/pages/_editor_js.html" %}
-{% endblock %}
\ No newline at end of file
+{% endblock %}
+
+{% block extra_css %}
+ {% include "wagtailadmin/pages/_editor_css.html" %}
+{% endblock %}
+{% block extra_js %}
+ {% include "wagtailadmin/pages/_editor_js.html" %}
+{% endblock %}
diff --git a/wagtail/wagtailsearch/templates/wagtailsearch/editorspicks/includes/editorspicks_formset.html b/wagtail/wagtailsearch/templates/wagtailsearch/editorspicks/includes/editorspicks_formset.html
index eb41892b6..0d4dec9aa 100644
--- a/wagtail/wagtailsearch/templates/wagtailsearch/editorspicks/includes/editorspicks_formset.html
+++ b/wagtail/wagtailsearch/templates/wagtailsearch/editorspicks/includes/editorspicks_formset.html
@@ -1,4 +1,4 @@
-{% load i18n %}
+{% load i18n wagtailadmin_tags %}
{{ formset.management_form }}
{% trans "Add recommended page" %}
-
\ No newline at end of file
+
diff --git a/wagtail/wagtailsearch/templates/wagtailsearch/editorspicks/includes/editorspicks_formset.js b/wagtail/wagtailsearch/templates/wagtailsearch/editorspicks/includes/editorspicks_formset.js
index 907d2eb3a..e89a8baff 100644
--- a/wagtail/wagtailsearch/templates/wagtailsearch/editorspicks/includes/editorspicks_formset.js
+++ b/wagtail/wagtailsearch/templates/wagtailsearch/editorspicks/includes/editorspicks_formset.js
@@ -1,20 +1,15 @@
(function() {
- function fixPrefix(str) {return str;}
-
var panel = InlinePanel({
- formsetPrefix: fixPrefix("id_{{ formset.prefix }}"),
- emptyChildFormPrefix: fixPrefix("{{ formset.empty_form.prefix }}"),
+ formsetPrefix: "id_{{ formset.prefix }}",
+ emptyChildFormPrefix: "{{ formset.empty_form.prefix }}",
canOrder: true,
- onAdd: function(fixPrefix) {
- createPageChooser(fixPrefix('id_{{ formset.prefix }}-__prefix__-page'), 'wagtailcore.page', null);
- }
+ onAdd: function() { }
});
{% for form in formset.forms %}
- createPageChooser(fixPrefix('id_{{ formset.prefix }}-{{ forloop.counter0 }}-page'), 'wagtailcore.page', null);
panel.initChildControls('{{ formset.prefix }}-{{ forloop.counter0 }}');
{% endfor %}
panel.updateMoveButtonDisabledStates();
-})();
\ No newline at end of file
+})();
diff --git a/wagtail/wagtailsnippets/edit_handlers.py b/wagtail/wagtailsnippets/edit_handlers.py
index 155aac93b..3aa3845b5 100644
--- a/wagtail/wagtailsnippets/edit_handlers.py
+++ b/wagtail/wagtailsnippets/edit_handlers.py
@@ -1,9 +1,12 @@
+from __future__ import absolute_import, unicode_literals
+
from django.template.loader import render_to_string
from django.contrib.contenttypes.models import ContentType
from django.utils.safestring import mark_safe
from django.utils.encoding import force_text
from wagtail.wagtailadmin.edit_handlers import BaseChooserPanel
+from .widgets import AdminSnippetChooser
class BaseSnippetChooserPanel(BaseChooserPanel):
@@ -12,6 +15,11 @@ class BaseSnippetChooserPanel(BaseChooserPanel):
_content_type = None
+ @classmethod
+ def widget_overrides(cls):
+ return {cls.field_name: AdminSnippetChooser(
+ content_type=cls.content_type())}
+
@classmethod
def content_type(cls):
if cls._content_type is None:
@@ -30,18 +38,9 @@ class BaseSnippetChooserPanel(BaseChooserPanel):
'show_help_text': show_help_text,
}))
- def render_js(self):
- content_type = self.__class__.content_type()
-
- return mark_safe("createSnippetChooser(fixPrefix('%s'), '%s/%s');" % (
- self.bound_field.id_for_label,
- content_type.app_label,
- content_type.model,
- ))
-
def SnippetChooserPanel(field_name, snippet_type):
- return type('_SnippetChooserPanel', (BaseSnippetChooserPanel,), {
+ return type(str('_SnippetChooserPanel'), (BaseSnippetChooserPanel,), {
'field_name': field_name,
'snippet_type_name': force_text(snippet_type._meta.verbose_name),
'snippet_type': snippet_type,
diff --git a/wagtail/wagtailsnippets/tests.py b/wagtail/wagtailsnippets/tests.py
index cc0a221f1..55b79f594 100644
--- a/wagtail/wagtailsnippets/tests.py
+++ b/wagtail/wagtailsnippets/tests.py
@@ -4,13 +4,12 @@ from django.db import models
from wagtail.tests.utils import WagtailTestUtils
from django.test.utils import override_settings
-from wagtail.tests.models import Advert, AlphaSnippet, ZuluSnippet
+from wagtail.tests.models import Advert, AlphaSnippet, ZuluSnippet, SnippetChooserModel
from wagtail.wagtailsnippets.models import register_snippet, SNIPPET_MODELS
from wagtail.wagtailsnippets.views.snippets import (
get_snippet_edit_handler
)
-from wagtail.wagtailsnippets.edit_handlers import SnippetChooserPanel
from wagtail.wagtailcore.models import Page
@@ -155,26 +154,30 @@ class TestSnippetChooserPanel(TestCase):
fixtures = ['wagtail/tests/fixtures/test.json']
def setUp(self):
- content_type = Advert
- test_snippet = Advert.objects.get(id=1)
+ model = SnippetChooserModel
+ self.advert_text = 'Test advert text'
+ test_snippet = model.objects.create(
+ advert=Advert.objects.create(text=self.advert_text))
- edit_handler_class = get_snippet_edit_handler(Advert)
- form_class = edit_handler_class.get_form_class(Advert)
+ edit_handler_class = get_snippet_edit_handler(model)
+ form_class = edit_handler_class.get_form_class(model)
form = form_class(instance=test_snippet)
+ edit_handler = edit_handler_class(instance=test_snippet, form=form)
- self.snippet_chooser_panel_class = SnippetChooserPanel('text', content_type)
- self.snippet_chooser_panel = self.snippet_chooser_panel_class(instance=test_snippet,
- form=form)
+ self.snippet_chooser_panel = [
+ panel for panel in edit_handler.children
+ if getattr(panel, 'field_name', None) == 'advert'][0]
def test_create_snippet_chooser_panel_class(self):
- self.assertEqual(self.snippet_chooser_panel_class.__name__, '_SnippetChooserPanel')
+ self.assertEqual(type(self.snippet_chooser_panel).__name__,
+ '_SnippetChooserPanel')
def test_render_as_field(self):
- self.assertTrue('test_advert' in self.snippet_chooser_panel.render_as_field())
+ self.assertTrue(self.advert_text in self.snippet_chooser_panel.render_as_field())
def test_render_js(self):
- self.assertTrue("createSnippetChooser(fixPrefix('id_text'), 'tests/advert');"
- in self.snippet_chooser_panel.render_js())
+ self.assertIn('createSnippetChooser("id_advert", "tests/advert");',
+ self.snippet_chooser_panel.render_as_field())
class TestSnippetRegistering(TestCase):
diff --git a/wagtail/wagtailsnippets/widgets.py b/wagtail/wagtailsnippets/widgets.py
new file mode 100644
index 000000000..00945e06f
--- /dev/null
+++ b/wagtail/wagtailsnippets/widgets.py
@@ -0,0 +1,26 @@
+from __future__ import absolute_import, unicode_literals
+
+import json
+
+from django.forms import widgets
+
+from wagtail.utils.widgets import WidgetWithScript
+
+
+class AdminSnippetChooser(WidgetWithScript, widgets.Input):
+ input_type = 'hidden'
+ target_content_type = None
+
+ def __init__(self, content_type=None, **kwargs):
+ super(AdminSnippetChooser, self).__init__(**kwargs)
+ if content_type is not None:
+ self.target_content_type = content_type
+
+ def render_js_init(self, id_, name, value):
+ content_type = self.target_content_type
+
+ return "createSnippetChooser({id}, {content_type});".format(
+ id=json.dumps(id_),
+ content_type=json.dumps('{app}/{model}'.format(
+ app=content_type.app_label,
+ model=content_type.model)))