wagtail/wagtail/wagtailcore/fields.py

80 lines
2.8 KiB
Python
Raw Normal View History

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
from django import forms
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
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
class RichTextArea(WidgetWithScript, forms.Textarea):
2014-01-24 11:42:11 +00:00
def get_panel(self):
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)
class StreamField(with_metaclass(models.SubfieldBase, models.Field)):
def __init__(self, block_types, **kwargs):
self.block_types = block_types
self.stream_block = StreamBlock(block_types)
super(StreamField, self).__init__(**kwargs)
def get_internal_type(self):
return 'TextField'
def deconstruct(self):
name, path, args, kwargs = super(StreamField, self).deconstruct()
kwargs['block_types'] = self.block_types
return name, path, args, kwargs
def to_python(self, value):
if value is None or value == '':
return StreamValue(self.stream_block, [])
elif isinstance(value, StreamValue):
return value
else: # assume string
return self.stream_block.to_python(json.loads(value))
def get_prep_value(self, value):
return json.dumps(self.stream_block.get_prep_value(value))
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)