Refactor edit handler JavaScript integration
Edit handler panels which required JavaScript provided the JavaScript in
a `render_js` function, the output of which was collected together and
inserted after the `<script>` tags in the footer. Now that the
`<script>` tags are at the top of the `<body>`, these collected
initialisations were failing when they could not find the elements.
A new set of widgets have been created: `AdminPageChooser`,
`AdminImageChooser`, `AdminSnippetChooser`, `AdminDocumentChooser`.
These widgets output the initialisation JavaScript in a `<script>` tag
immediately following the widget itself, assuming that the required
external libraries and scripts are now included further up in the page.
This is how the GeoDjango fields from Django work, as well as many other
third party widgets. It makes writing integrations for arbitary widgets
easy, as they do not have to know anything about Wagtail edit handlers.
This also removes the need for the `fixPrefix()` JavaScript function
wrapping all field initialisers. As the fields are initialised as they
are inserted in to the page, any scripts included with them are inserted
at the same time. Additionally, the `__prefix__` placeholder in empty
form fields is also present in the initialisation scripts, and is fixed
at the same time. A template tag `escapescript` has been added to help
with this, to prevent `<script>` initialisation blocks from prematurely
closing the `<script type='text/django-form-template'>` tags.
2014-09-09 05:28:41 +00:00
|
|
|
from __future__ import absolute_import, unicode_literals
|
|
|
|
|
|
|
|
|
|
import json
|
|
|
|
|
|
2014-01-24 11:42:11 +00:00
|
|
|
from django.db import models
|
2015-01-16 14:01:17 +00:00
|
|
|
from django import forms
|
2015-01-14 20:55:25 +00:00
|
|
|
from django.utils.six import with_metaclass
|
2014-09-06 22:03:22 +00:00
|
|
|
|
2014-01-24 11:42:11 +00:00
|
|
|
from wagtail.wagtailcore.rich_text import DbWhitelister, expand_db_html
|
Refactor edit handler JavaScript integration
Edit handler panels which required JavaScript provided the JavaScript in
a `render_js` function, the output of which was collected together and
inserted after the `<script>` tags in the footer. Now that the
`<script>` tags are at the top of the `<body>`, these collected
initialisations were failing when they could not find the elements.
A new set of widgets have been created: `AdminPageChooser`,
`AdminImageChooser`, `AdminSnippetChooser`, `AdminDocumentChooser`.
These widgets output the initialisation JavaScript in a `<script>` tag
immediately following the widget itself, assuming that the required
external libraries and scripts are now included further up in the page.
This is how the GeoDjango fields from Django work, as well as many other
third party widgets. It makes writing integrations for arbitary widgets
easy, as they do not have to know anything about Wagtail edit handlers.
This also removes the need for the `fixPrefix()` JavaScript function
wrapping all field initialisers. As the fields are initialised as they
are inserted in to the page, any scripts included with them are inserted
at the same time. Additionally, the `__prefix__` placeholder in empty
form fields is also present in the initialisation scripts, and is fixed
at the same time. A template tag `escapescript` has been added to help
with this, to prevent `<script>` initialisation blocks from prematurely
closing the `<script type='text/django-form-template'>` tags.
2014-09-09 05:28:41 +00:00
|
|
|
from wagtail.utils.widgets import WidgetWithScript
|
2015-01-16 14:01:17 +00:00
|
|
|
from wagtail.wagtailadmin.blocks import StreamBlock, StreamValue # FIXME: wagtailcore shouldn't be depending on wagtailadmin
|
2014-01-24 11:42:11 +00:00
|
|
|
|
2014-02-06 11:33:32 +00:00
|
|
|
|
2015-01-16 14:01:17 +00:00
|
|
|
class RichTextArea(WidgetWithScript, forms.Textarea):
|
2014-01-24 11:42:11 +00:00
|
|
|
def get_panel(self):
|
2014-01-24 17:34:26 +00:00
|
|
|
from wagtail.wagtailadmin.edit_handlers import RichTextFieldPanel
|
2014-01-24 11:42:11 +00:00
|
|
|
return RichTextFieldPanel
|
|
|
|
|
|
|
|
|
|
def render(self, name, value, attrs=None):
|
|
|
|
|
if value is None:
|
|
|
|
|
translated_value = None
|
|
|
|
|
else:
|
|
|
|
|
translated_value = expand_db_html(value, for_editor=True)
|
|
|
|
|
return super(RichTextArea, self).render(name, translated_value, attrs)
|
|
|
|
|
|
Refactor edit handler JavaScript integration
Edit handler panels which required JavaScript provided the JavaScript in
a `render_js` function, the output of which was collected together and
inserted after the `<script>` tags in the footer. Now that the
`<script>` tags are at the top of the `<body>`, these collected
initialisations were failing when they could not find the elements.
A new set of widgets have been created: `AdminPageChooser`,
`AdminImageChooser`, `AdminSnippetChooser`, `AdminDocumentChooser`.
These widgets output the initialisation JavaScript in a `<script>` tag
immediately following the widget itself, assuming that the required
external libraries and scripts are now included further up in the page.
This is how the GeoDjango fields from Django work, as well as many other
third party widgets. It makes writing integrations for arbitary widgets
easy, as they do not have to know anything about Wagtail edit handlers.
This also removes the need for the `fixPrefix()` JavaScript function
wrapping all field initialisers. As the fields are initialised as they
are inserted in to the page, any scripts included with them are inserted
at the same time. Additionally, the `__prefix__` placeholder in empty
form fields is also present in the initialisation scripts, and is fixed
at the same time. A template tag `escapescript` has been added to help
with this, to prevent `<script>` initialisation blocks from prematurely
closing the `<script type='text/django-form-template'>` tags.
2014-09-09 05:28:41 +00:00
|
|
|
def render_js_init(self, id_, name, value):
|
|
|
|
|
return "makeRichTextEditable({0});".format(json.dumps(id_))
|
|
|
|
|
|
2014-01-24 11:42:11 +00:00
|
|
|
def value_from_datadict(self, data, files, name):
|
|
|
|
|
original_value = super(RichTextArea, self).value_from_datadict(data, files, name)
|
|
|
|
|
if original_value is None:
|
|
|
|
|
return None
|
|
|
|
|
return DbWhitelister.clean(original_value)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class RichTextField(models.TextField):
|
|
|
|
|
def formfield(self, **kwargs):
|
|
|
|
|
defaults = {'widget': RichTextArea}
|
|
|
|
|
defaults.update(kwargs)
|
|
|
|
|
return super(RichTextField, self).formfield(**defaults)
|
2015-01-14 15:41:05 +00:00
|
|
|
|
|
|
|
|
|
2015-01-16 14:01:17 +00:00
|
|
|
class StreamField(with_metaclass(models.SubfieldBase, models.Field)):
|
2015-01-14 15:41:05 +00:00
|
|
|
def __init__(self, block_types, **kwargs):
|
|
|
|
|
self.block_types = block_types
|
2015-01-14 20:55:25 +00:00
|
|
|
self.stream_block = StreamBlock(block_types)
|
2015-01-14 15:41:05 +00:00
|
|
|
super(StreamField, self).__init__(**kwargs)
|
|
|
|
|
|
2015-02-04 19:03:15 +00:00
|
|
|
def get_internal_type(self):
|
|
|
|
|
return 'TextField'
|
|
|
|
|
|
2015-01-14 15:41:05 +00:00
|
|
|
def deconstruct(self):
|
|
|
|
|
name, path, args, kwargs = super(StreamField, self).deconstruct()
|
|
|
|
|
kwargs['block_types'] = self.block_types
|
|
|
|
|
return name, path, args, kwargs
|
2015-01-14 20:55:25 +00:00
|
|
|
|
|
|
|
|
def to_python(self, value):
|
2015-02-11 13:30:26 +00:00
|
|
|
if value is None or value == '':
|
2015-02-05 11:12:21 +00:00
|
|
|
return StreamValue(self.stream_block, [])
|
2015-01-16 14:01:17 +00:00
|
|
|
elif isinstance(value, StreamValue):
|
2015-01-14 20:55:25 +00:00
|
|
|
return value
|
|
|
|
|
else: # assume string
|
2015-01-16 14:01:17 +00:00
|
|
|
return self.stream_block.to_python(json.loads(value))
|
2015-01-14 20:55:25 +00:00
|
|
|
|
|
|
|
|
def get_prep_value(self, value):
|
2015-01-16 14:01:17 +00:00
|
|
|
return json.dumps(self.stream_block.get_prep_value(value))
|
2015-01-14 20:55:25 +00:00
|
|
|
|
2015-01-16 14:01:17 +00:00
|
|
|
def formfield(self, **kwargs):
|
|
|
|
|
"""
|
|
|
|
|
Override formfield to use a plain forms.Field so that we do no transformation on the value
|
|
|
|
|
(as distinct from the usual fallback of forms.CharField, which transforms it into a string).
|
|
|
|
|
"""
|
|
|
|
|
defaults = {'form_class': forms.Field}
|
|
|
|
|
defaults.update(kwargs)
|
|
|
|
|
return super(StreamField, self).formfield(**defaults)
|
|
|
|
|
|
|
|
|
|
def value_to_string(self, obj):
|
|
|
|
|
value = self._get_val_from_obj(obj)
|
|
|
|
|
return self.get_prep_value(value)
|