2010-03-31 16:46:17 +02:00
|
|
|
from django import forms
|
|
|
|
from django.conf import settings
|
2015-07-07 20:35:43 +02:00
|
|
|
from django.core.exceptions import ImproperlyConfigured
|
2010-03-31 17:05:15 +02:00
|
|
|
from django.core.urlresolvers import reverse
|
2015-07-07 20:35:43 +02:00
|
|
|
from django.core.serializers.json import DjangoJSONEncoder
|
2011-09-28 13:26:30 +02:00
|
|
|
from django.template.loader import render_to_string
|
2013-11-12 02:28:54 +01:00
|
|
|
from django.utils.encoding import force_text
|
2015-07-07 20:35:43 +02:00
|
|
|
from django.utils.functional import Promise
|
|
|
|
from django.utils.html import conditional_escape
|
|
|
|
from django.utils.safestring import mark_safe
|
2014-02-24 09:04:21 +01:00
|
|
|
from django.utils.translation import get_language
|
2015-07-07 20:35:43 +02:00
|
|
|
|
2015-04-02 14:33:40 +02:00
|
|
|
try:
|
|
|
|
# Django >=1.7
|
|
|
|
from django.forms.utils import flatatt
|
|
|
|
except ImportError:
|
|
|
|
# Django <1.7
|
|
|
|
from django.forms.util import flatatt
|
2014-10-22 13:49:17 +02:00
|
|
|
|
|
|
|
|
|
|
|
class LazyEncoder(DjangoJSONEncoder):
|
|
|
|
def default(self, obj):
|
|
|
|
if isinstance(obj, Promise):
|
|
|
|
return force_text(obj)
|
|
|
|
return super(LazyEncoder, self).default(obj)
|
2013-03-17 06:54:34 +01:00
|
|
|
|
2010-03-31 16:46:17 +02:00
|
|
|
|
2014-10-22 13:49:17 +02:00
|
|
|
json_encode = LazyEncoder().encode
|
2010-06-28 00:04:05 +02:00
|
|
|
|
2010-12-09 12:43:29 +01:00
|
|
|
DEFAULT_CONFIG = {
|
2013-03-17 06:53:54 +01:00
|
|
|
'skin': 'moono',
|
2013-04-04 09:03:17 +02:00
|
|
|
'toolbar_Basic': [
|
|
|
|
['Source', '-', 'Bold', 'Italic']
|
|
|
|
],
|
|
|
|
'toolbar_Full': [
|
2013-03-17 06:53:54 +01:00
|
|
|
['Styles', 'Format', 'Bold', 'Italic', 'Underline', 'Strike', 'SpellChecker', 'Undo', 'Redo'],
|
2015-01-27 11:23:01 +01:00
|
|
|
['Link', 'Unlink', 'Anchor'],
|
2013-03-17 06:53:54 +01:00
|
|
|
['Image', 'Flash', 'Table', 'HorizontalRule'],
|
|
|
|
['TextColor', 'BGColor'],
|
|
|
|
['Smiley', 'SpecialChar'], ['Source'],
|
|
|
|
],
|
2013-04-04 09:03:17 +02:00
|
|
|
'toolbar': 'Full',
|
2010-12-09 12:43:29 +01:00
|
|
|
'height': 291,
|
2012-04-20 16:53:00 +02:00
|
|
|
'width': 835,
|
2010-12-09 12:43:29 +01:00
|
|
|
'filebrowserWindowWidth': 940,
|
2012-04-20 17:27:54 +02:00
|
|
|
'filebrowserWindowHeight': 725,
|
2010-12-09 12:43:29 +01:00
|
|
|
}
|
|
|
|
|
2011-08-30 12:31:49 +02:00
|
|
|
|
2010-03-31 16:46:17 +02:00
|
|
|
class CKEditorWidget(forms.Textarea):
|
2010-03-31 17:05:15 +02:00
|
|
|
"""
|
|
|
|
Widget providing CKEditor for Rich Text Editing.
|
|
|
|
Supports direct image uploads and embed.
|
|
|
|
"""
|
2010-03-31 16:46:17 +02:00
|
|
|
class Media:
|
2014-08-24 21:03:03 +02:00
|
|
|
js = ()
|
|
|
|
jquery_url = getattr(settings, 'CKEDITOR_JQUERY_URL', None)
|
|
|
|
if jquery_url:
|
|
|
|
js += (jquery_url, )
|
2010-06-22 13:05:56 +02:00
|
|
|
try:
|
2014-08-24 21:03:03 +02:00
|
|
|
js += (
|
2015-06-07 19:25:06 +02:00
|
|
|
settings.STATIC_URL + 'ckeditor/ckeditor/ckeditor.js',
|
|
|
|
settings.STATIC_URL + 'ckeditor/ckeditor-init.js',
|
2010-06-22 13:05:56 +02:00
|
|
|
)
|
|
|
|
except AttributeError:
|
2011-08-30 12:31:49 +02:00
|
|
|
raise ImproperlyConfigured("django-ckeditor requires \
|
|
|
|
CKEDITOR_MEDIA_PREFIX setting. This setting specifies a \
|
|
|
|
URL prefix to the ckeditor JS and CSS media (not \
|
|
|
|
uploaded media). Make sure to use a trailing slash: \
|
|
|
|
CKEDITOR_MEDIA_PREFIX = '/media/ckeditor/'")
|
2010-06-28 00:04:05 +02:00
|
|
|
|
Add support for external plugin resource declarations.
This commit adds keyword arguments to fields.RichTextField that allow the
overriding of extraPlugins on a per-field basis, and that allow the declaration
of external plugin resources (using CKEDITOR.plugins.addExternal()).
This improves the implementation of CKEditor plugins in django apps,
because they can be hosted and deployed separately from the CKEditor sources.
As an example, here's how to define a custom RichTextField by just currying
the existing one's constructor with the new arguments:
from django.utils.functional import curry
extra_plugins = ['custom_plugin']
external_plugin_resources=[('custom_plugin', '/custom_plugin_base/', 'plugin.js')]
CustomRichTextField = curry(RichTextField, extra_plugins=extra_plugins, external_plugin_resources=external_plugin_resources)
The external_plugin_resources kwarg is a list of tuples that contain the same
information as the parameters to CKEDITOR.plugins.addExternal().
You may want to write "reverse('some_view')" for the second parameter.
If that fails due to circular imports, wrap the reverse call in a
SimpleLazyObject, like this:
from django.utils.functional import SimpleLazyObject
external_plugin_resources=[('custom_plugin', SimpleLazyObject(lambda: reverse('custom_plugin_view', kwargs=...) + 'some_relative_path/'), 'plugin.js')]
2014-02-28 08:44:32 +01:00
|
|
|
def __init__(self, config_name='default', extra_plugins=None, external_plugin_resources=None, *args, **kwargs):
|
2010-06-28 00:04:05 +02:00
|
|
|
super(CKEditorWidget, self).__init__(*args, **kwargs)
|
2010-12-09 12:43:29 +01:00
|
|
|
# Setup config from defaults.
|
2011-03-10 08:14:29 +01:00
|
|
|
self.config = DEFAULT_CONFIG.copy()
|
2010-12-09 12:43:29 +01:00
|
|
|
|
|
|
|
# Try to get valid config from settings.
|
|
|
|
configs = getattr(settings, 'CKEDITOR_CONFIGS', None)
|
2013-03-17 06:55:02 +01:00
|
|
|
if configs:
|
2010-12-09 13:03:48 +01:00
|
|
|
if isinstance(configs, dict):
|
|
|
|
# Make sure the config_name exists.
|
2012-04-20 17:34:39 +02:00
|
|
|
if config_name in configs:
|
2010-12-09 13:03:48 +01:00
|
|
|
config = configs[config_name]
|
|
|
|
# Make sure the configuration is a dictionary.
|
|
|
|
if not isinstance(config, dict):
|
2011-08-30 12:31:49 +02:00
|
|
|
raise ImproperlyConfigured('CKEDITOR_CONFIGS["%s"] \
|
|
|
|
setting must be a dictionary type.' % \
|
|
|
|
config_name)
|
2010-12-09 13:03:48 +01:00
|
|
|
# Override defaults with settings config.
|
|
|
|
self.config.update(config)
|
|
|
|
else:
|
2011-08-30 12:31:49 +02:00
|
|
|
raise ImproperlyConfigured("No configuration named '%s' \
|
|
|
|
found in your CKEDITOR_CONFIGS setting." % \
|
|
|
|
config_name)
|
2010-12-09 12:43:29 +01:00
|
|
|
else:
|
2011-08-30 12:31:49 +02:00
|
|
|
raise ImproperlyConfigured('CKEDITOR_CONFIGS setting must be a\
|
|
|
|
dictionary type.')
|
2012-12-13 12:19:26 +01:00
|
|
|
|
Add support for external plugin resource declarations.
This commit adds keyword arguments to fields.RichTextField that allow the
overriding of extraPlugins on a per-field basis, and that allow the declaration
of external plugin resources (using CKEDITOR.plugins.addExternal()).
This improves the implementation of CKEditor plugins in django apps,
because they can be hosted and deployed separately from the CKEditor sources.
As an example, here's how to define a custom RichTextField by just currying
the existing one's constructor with the new arguments:
from django.utils.functional import curry
extra_plugins = ['custom_plugin']
external_plugin_resources=[('custom_plugin', '/custom_plugin_base/', 'plugin.js')]
CustomRichTextField = curry(RichTextField, extra_plugins=extra_plugins, external_plugin_resources=external_plugin_resources)
The external_plugin_resources kwarg is a list of tuples that contain the same
information as the parameters to CKEDITOR.plugins.addExternal().
You may want to write "reverse('some_view')" for the second parameter.
If that fails due to circular imports, wrap the reverse call in a
SimpleLazyObject, like this:
from django.utils.functional import SimpleLazyObject
external_plugin_resources=[('custom_plugin', SimpleLazyObject(lambda: reverse('custom_plugin_view', kwargs=...) + 'some_relative_path/'), 'plugin.js')]
2014-02-28 08:44:32 +01:00
|
|
|
extra_plugins = extra_plugins or []
|
2012-12-13 12:19:26 +01:00
|
|
|
|
Add support for external plugin resource declarations.
This commit adds keyword arguments to fields.RichTextField that allow the
overriding of extraPlugins on a per-field basis, and that allow the declaration
of external plugin resources (using CKEDITOR.plugins.addExternal()).
This improves the implementation of CKEditor plugins in django apps,
because they can be hosted and deployed separately from the CKEditor sources.
As an example, here's how to define a custom RichTextField by just currying
the existing one's constructor with the new arguments:
from django.utils.functional import curry
extra_plugins = ['custom_plugin']
external_plugin_resources=[('custom_plugin', '/custom_plugin_base/', 'plugin.js')]
CustomRichTextField = curry(RichTextField, extra_plugins=extra_plugins, external_plugin_resources=external_plugin_resources)
The external_plugin_resources kwarg is a list of tuples that contain the same
information as the parameters to CKEDITOR.plugins.addExternal().
You may want to write "reverse('some_view')" for the second parameter.
If that fails due to circular imports, wrap the reverse call in a
SimpleLazyObject, like this:
from django.utils.functional import SimpleLazyObject
external_plugin_resources=[('custom_plugin', SimpleLazyObject(lambda: reverse('custom_plugin_view', kwargs=...) + 'some_relative_path/'), 'plugin.js')]
2014-02-28 08:44:32 +01:00
|
|
|
if extra_plugins:
|
|
|
|
self.config['extraPlugins'] = ','.join(extra_plugins)
|
|
|
|
|
|
|
|
self.external_plugin_resources = external_plugin_resources or []
|
2011-08-30 12:31:49 +02:00
|
|
|
|
2010-03-31 16:46:17 +02:00
|
|
|
def render(self, name, value, attrs={}):
|
2011-08-30 12:31:49 +02:00
|
|
|
if value is None:
|
|
|
|
value = ''
|
2010-03-31 16:46:17 +02:00
|
|
|
final_attrs = self.build_attrs(attrs, name=name)
|
2015-01-25 13:59:06 +01:00
|
|
|
if 'filebrowserUploadUrl' not in self.config:
|
|
|
|
self.config.setdefault('filebrowserUploadUrl', reverse('ckeditor_upload'))
|
|
|
|
if 'filebrowserBrowseUrl' not in self.config:
|
|
|
|
self.config.setdefault('filebrowserBrowseUrl', reverse('ckeditor_browse'))
|
2014-02-24 09:04:21 +01:00
|
|
|
if not self.config.get('language'):
|
|
|
|
self.config['language'] = get_language()
|
|
|
|
|
2014-12-12 11:18:27 +01:00
|
|
|
# Force to text to evaluate possible lazy objects
|
|
|
|
external_plugin_resources = [[force_text(a), force_text(b), force_text(c)] for a, b, c in self.external_plugin_resources]
|
|
|
|
|
2011-09-28 13:26:30 +02:00
|
|
|
return mark_safe(render_to_string('ckeditor/widget.html', {
|
|
|
|
'final_attrs': flatatt(final_attrs),
|
2013-11-12 02:28:54 +01:00
|
|
|
'value': conditional_escape(force_text(value)),
|
2011-09-28 13:26:30 +02:00
|
|
|
'id': final_attrs['id'],
|
Add support for external plugin resource declarations.
This commit adds keyword arguments to fields.RichTextField that allow the
overriding of extraPlugins on a per-field basis, and that allow the declaration
of external plugin resources (using CKEDITOR.plugins.addExternal()).
This improves the implementation of CKEditor plugins in django apps,
because they can be hosted and deployed separately from the CKEditor sources.
As an example, here's how to define a custom RichTextField by just currying
the existing one's constructor with the new arguments:
from django.utils.functional import curry
extra_plugins = ['custom_plugin']
external_plugin_resources=[('custom_plugin', '/custom_plugin_base/', 'plugin.js')]
CustomRichTextField = curry(RichTextField, extra_plugins=extra_plugins, external_plugin_resources=external_plugin_resources)
The external_plugin_resources kwarg is a list of tuples that contain the same
information as the parameters to CKEDITOR.plugins.addExternal().
You may want to write "reverse('some_view')" for the second parameter.
If that fails due to circular imports, wrap the reverse call in a
SimpleLazyObject, like this:
from django.utils.functional import SimpleLazyObject
external_plugin_resources=[('custom_plugin', SimpleLazyObject(lambda: reverse('custom_plugin_view', kwargs=...) + 'some_relative_path/'), 'plugin.js')]
2014-02-28 08:44:32 +01:00
|
|
|
'config': json_encode(self.config),
|
2014-12-12 11:18:27 +01:00
|
|
|
'external_plugin_resources' : json_encode(external_plugin_resources)
|
2013-03-17 06:55:02 +01:00
|
|
|
}))
|