diff --git a/wagtail/wagtailcore/blocks/__init__.py b/wagtail/wagtailcore/blocks/__init__.py
index 302d8583d..4b4cb288d 100644
--- a/wagtail/wagtailcore/blocks/__init__.py
+++ b/wagtail/wagtailcore/blocks/__init__.py
@@ -1,5 +1,5 @@
from __future__ import absolute_import, unicode_literals
-# this ensures that any render / __str__ methods returning HTML via calls to mark_safe / format_html
+# unicode_literals ensures that any render / __str__ methods returning HTML via calls to mark_safe / format_html
# return a SafeText, not SafeBytes; necessary so that it doesn't get re-encoded when the template engine
# calls force_text, which would cause it to lose its 'safe' flag
@@ -19,7 +19,6 @@ from django.forms.utils import ErrorList
import six
from wagtail.wagtailcore.utils import escape_script
-from wagtail.wagtailcore.rich_text import expand_db_html
from .utils import indent, js_dict
@@ -321,149 +320,6 @@ class BoundBlock(object):
return self.block.render(self.value)
-# ===========
-# Field block
-# ===========
-
-class FieldBlock(Block):
- class Meta:
- default = None
-
- def render_form(self, value, prefix='', errors=None):
- widget = self.field.widget
-
- if self.label:
- label_html = format_html(
- """ """,
- label_id=widget.id_for_label(prefix), label=self.label
- )
- else:
- label_html = ''
-
- widget_attrs = {'id': prefix, 'placeholder': self.label}
-
- if hasattr(widget, 'render_with_errors'):
- widget_html = widget.render_with_errors(prefix, value, attrs=widget_attrs, errors=errors)
- widget_has_rendered_errors = True
- else:
- widget_html = widget.render(prefix, value, attrs=widget_attrs)
- widget_has_rendered_errors = False
-
- return render_to_string('wagtailadmin/block_forms/field.html', {
- 'name': self.name,
- 'label': self.label,
- 'classes': self.meta.classname,
- 'widget': widget_html,
- 'label_tag': label_html,
- 'field': self.field,
- 'errors': errors if (not widget_has_rendered_errors) else None
- })
-
- def value_from_datadict(self, data, files, prefix):
- return self.to_python(self.field.widget.value_from_datadict(data, files, prefix))
-
- def clean(self, value):
- return self.field.clean(value)
-
-
-class CharBlock(FieldBlock):
- def __init__(self, required=True, help_text=None, max_length=None, min_length=None, **kwargs):
- # CharField's 'label' and 'initial' parameters are not exposed, as Block handles that functionality natively (via 'label' and 'default')
- self.field = forms.CharField(required=required, help_text=help_text, max_length=max_length, min_length=min_length)
- super(CharBlock, self).__init__(**kwargs)
-
- def get_searchable_content(self, value):
- return [force_text(value)]
-
-
-class URLBlock(FieldBlock):
- def __init__(self, required=True, help_text=None, max_length=None, min_length=None, **kwargs):
- self.field = forms.URLField(required=required, help_text=help_text, max_length=max_length, min_length=min_length)
- super(URLBlock, self).__init__(**kwargs)
-
-
-class RichTextBlock(FieldBlock):
- @cached_property
- def field(self):
- from wagtail.wagtailcore.fields import RichTextArea
- return forms.CharField(widget=RichTextArea)
-
- def render_basic(self, value):
- return mark_safe('
' + expand_db_html(value) + '
')
-
- def get_searchable_content(self, value):
- return [force_text(value)]
-
-
-class RawHTMLBlock(FieldBlock):
- def __init__(self, required=True, help_text=None, max_length=None, min_length=None, **kwargs):
- self.field = forms.CharField(
- required=required, help_text=help_text, max_length=max_length, min_length=min_length,
- widget = forms.Textarea)
- super(RawHTMLBlock, self).__init__(**kwargs)
-
- def render_basic(self, value):
- return mark_safe(value) # if it isn't safe, that's the site admin's problem for allowing raw HTML blocks in the first place...
-
- class Meta:
- icon = 'code'
-
-
-class ChooserBlock(FieldBlock):
- def __init__(self, required=True, **kwargs):
- self.required=required
- super(ChooserBlock, self).__init__(**kwargs)
-
- """Abstract superclass for fields that implement a chooser interface (page, image, snippet etc)"""
- @cached_property
- def field(self):
- return forms.ModelChoiceField(queryset=self.target_model.objects.all(), widget=self.widget, required=self.required)
-
- def to_python(self, value):
- if value is None or isinstance(value, self.target_model):
- return value
- else:
- try:
- return self.target_model.objects.get(pk=value)
- except self.target_model.DoesNotExist:
- return None
-
- def get_prep_value(self, value):
- if isinstance(value, self.target_model):
- return value.id
- else:
- return value
-
- def clean(self, value):
- # ChooserBlock works natively with model instances as its 'value' type (because that's what you
- # want to work with when doing front-end templating), but ModelChoiceField.clean expects an ID
- # as the input value (and returns a model instance as the result). We don't want to bypass
- # ModelChoiceField.clean entirely (it might be doing relevant validation, such as checking page
- # type) so we convert our instance back to an ID here. It means we have a wasted round-trip to
- # the database when ModelChoiceField.clean promptly does its own lookup, but there's no easy way
- # around that...
- if isinstance(value, self.target_model):
- value = value.pk
- return super(ChooserBlock, self).clean(value)
-
-class PageChooserBlock(ChooserBlock):
- @cached_property
- def target_model(self):
- from wagtail.wagtailcore.models import Page # TODO: allow limiting to specific page types
- return Page
-
- @cached_property
- def widget(self):
- from wagtail.wagtailadmin.widgets import AdminPageChooser
- return AdminPageChooser
-
- def render_basic(self, value):
- if value:
- return format_html('{1}', value.url, value.title)
- else:
- return ''
-
-
# ===========
# StructBlock
# ===========
@@ -1094,3 +950,7 @@ class BlockField(forms.Field):
def clean(self, value):
return self.block.clean(value)
+
+
+# Import block types defined in wagtail.wagtailcore.blocks.field into the wagtail.wagtailcore.blocks namespace
+from .field import * # NOQA
diff --git a/wagtail/wagtailcore/blocks/field.py b/wagtail/wagtailcore/blocks/field.py
new file mode 100644
index 000000000..2cf01eea0
--- /dev/null
+++ b/wagtail/wagtailcore/blocks/field.py
@@ -0,0 +1,159 @@
+from __future__ import absolute_import, unicode_literals
+
+from django import forms
+from django.template.loader import render_to_string
+from django.utils.encoding import force_text
+from django.utils.functional import cached_property
+from django.utils.html import format_html
+from django.utils.safestring import mark_safe
+
+from wagtail.wagtailcore.blocks import Block
+from wagtail.wagtailcore.rich_text import expand_db_html
+
+class FieldBlock(Block):
+ class Meta:
+ default = None
+
+ def render_form(self, value, prefix='', errors=None):
+ widget = self.field.widget
+
+ if self.label:
+ label_html = format_html(
+ """ """,
+ label_id=widget.id_for_label(prefix), label=self.label
+ )
+ else:
+ label_html = ''
+
+ widget_attrs = {'id': prefix, 'placeholder': self.label}
+
+ if hasattr(widget, 'render_with_errors'):
+ widget_html = widget.render_with_errors(prefix, value, attrs=widget_attrs, errors=errors)
+ widget_has_rendered_errors = True
+ else:
+ widget_html = widget.render(prefix, value, attrs=widget_attrs)
+ widget_has_rendered_errors = False
+
+ return render_to_string('wagtailadmin/block_forms/field.html', {
+ 'name': self.name,
+ 'label': self.label,
+ 'classes': self.meta.classname,
+ 'widget': widget_html,
+ 'label_tag': label_html,
+ 'field': self.field,
+ 'errors': errors if (not widget_has_rendered_errors) else None
+ })
+
+ def value_from_datadict(self, data, files, prefix):
+ return self.to_python(self.field.widget.value_from_datadict(data, files, prefix))
+
+ def clean(self, value):
+ return self.field.clean(value)
+
+
+class CharBlock(FieldBlock):
+ def __init__(self, required=True, help_text=None, max_length=None, min_length=None, **kwargs):
+ # CharField's 'label' and 'initial' parameters are not exposed, as Block handles that functionality natively (via 'label' and 'default')
+ self.field = forms.CharField(required=required, help_text=help_text, max_length=max_length, min_length=min_length)
+ super(CharBlock, self).__init__(**kwargs)
+
+ def get_searchable_content(self, value):
+ return [force_text(value)]
+
+
+class URLBlock(FieldBlock):
+ def __init__(self, required=True, help_text=None, max_length=None, min_length=None, **kwargs):
+ self.field = forms.URLField(required=required, help_text=help_text, max_length=max_length, min_length=min_length)
+ super(URLBlock, self).__init__(**kwargs)
+
+
+class RichTextBlock(FieldBlock):
+ @cached_property
+ def field(self):
+ from wagtail.wagtailcore.fields import RichTextArea
+ return forms.CharField(widget=RichTextArea)
+
+ def render_basic(self, value):
+ return mark_safe('
' + expand_db_html(value) + '
')
+
+ def get_searchable_content(self, value):
+ return [force_text(value)]
+
+
+class RawHTMLBlock(FieldBlock):
+ def __init__(self, required=True, help_text=None, max_length=None, min_length=None, **kwargs):
+ self.field = forms.CharField(
+ required=required, help_text=help_text, max_length=max_length, min_length=min_length,
+ widget = forms.Textarea)
+ super(RawHTMLBlock, self).__init__(**kwargs)
+
+ def render_basic(self, value):
+ return mark_safe(value) # if it isn't safe, that's the site admin's problem for allowing raw HTML blocks in the first place...
+
+ class Meta:
+ icon = 'code'
+
+
+class ChooserBlock(FieldBlock):
+ def __init__(self, required=True, **kwargs):
+ self.required=required
+ super(ChooserBlock, self).__init__(**kwargs)
+
+ """Abstract superclass for fields that implement a chooser interface (page, image, snippet etc)"""
+ @cached_property
+ def field(self):
+ return forms.ModelChoiceField(queryset=self.target_model.objects.all(), widget=self.widget, required=self.required)
+
+ def to_python(self, value):
+ if value is None or isinstance(value, self.target_model):
+ return value
+ else:
+ try:
+ return self.target_model.objects.get(pk=value)
+ except self.target_model.DoesNotExist:
+ return None
+
+ def get_prep_value(self, value):
+ if isinstance(value, self.target_model):
+ return value.id
+ else:
+ return value
+
+ def clean(self, value):
+ # ChooserBlock works natively with model instances as its 'value' type (because that's what you
+ # want to work with when doing front-end templating), but ModelChoiceField.clean expects an ID
+ # as the input value (and returns a model instance as the result). We don't want to bypass
+ # ModelChoiceField.clean entirely (it might be doing relevant validation, such as checking page
+ # type) so we convert our instance back to an ID here. It means we have a wasted round-trip to
+ # the database when ModelChoiceField.clean promptly does its own lookup, but there's no easy way
+ # around that...
+ if isinstance(value, self.target_model):
+ value = value.pk
+ return super(ChooserBlock, self).clean(value)
+
+class PageChooserBlock(ChooserBlock):
+ @cached_property
+ def target_model(self):
+ from wagtail.wagtailcore.models import Page # TODO: allow limiting to specific page types
+ return Page
+
+ @cached_property
+ def widget(self):
+ from wagtail.wagtailadmin.widgets import AdminPageChooser
+ return AdminPageChooser
+
+ def render_basic(self, value):
+ if value:
+ return format_html('{1}', value.url, value.title)
+ else:
+ return ''
+
+
+# Ensure that the blocks defined here get deconstructed as wagtailcore.blocks.FooBlock
+# rather than wagtailcore.blocks.field.FooBlock
+block_classes = [FieldBlock, CharBlock, URLBlock, RichTextBlock, RawHTMLBlock, ChooserBlock, PageChooserBlock]
+DECONSTRUCT_ALIASES = {
+ cls: 'wagtail.wagtailcore.blocks.%s' % cls.__name__
+ for cls in block_classes
+}
+__all__ = [cls.__name__ for cls in block_classes]