diff --git a/scripts/prepare_docs.sh b/scripts/prepare_docs.sh index dfac6904..5b65689b 100755 --- a/scripts/prepare_docs.sh +++ b/scripts/prepare_docs.sh @@ -145,6 +145,9 @@ cat README.rst \ src/fobi/contrib/plugins/form_elements/content/content_text/README.rst \ docs/empty.rst.distrib \ \ + src/fobi/contrib/plugins/form_elements/content/content_richtext/README.rst \ + docs/empty.rst.distrib \ + \ src/fobi/contrib/plugins/form_elements/content/content_video/README.rst \ docs/empty.rst.distrib \ \ diff --git a/src/fobi/contrib/plugins/form_elements/content/content_richtext/README.rst b/src/fobi/contrib/plugins/form_elements/content/content_richtext/README.rst new file mode 100644 index 00000000..f55dd010 --- /dev/null +++ b/src/fobi/contrib/plugins/form_elements/content/content_richtext/README.rst @@ -0,0 +1,93 @@ +fobi.contrib.plugins.form_elements.content.content_richtext +----------------------------------------------------------- + +A ``Fobi`` Rich text form element plugin based on +`Summernote `_. + +Installation +~~~~~~~~~~~~ + +(1) Install ``django-summernote``. + + .. code-block:: sh + + pip install django-summernote + +(2) Add ``django_summernote`` to ``INSTALLED_APPS`` in ``settings.py``. + + .. code-block:: python + + INSTALLED_APPS = ( + ... + 'django_summernote', + ... + ) + +(3) Add ``django_summernote.urls`` to ``urls.py``. + + .. code-block:: python + + urlpatterns = [ + ... + url(r'^summernote/', include('django_summernote.urls')), + ... + ] + +(4) Add ``fobi.contrib.plugins.form_elements.content.content_richtext`` to + ``INSTALLED_APPS`` in ``settings.py``. + + .. code-block:: python + + INSTALLED_APPS = ( + ... + 'fobi.contrib.plugins.form_elements.content.content_richtext', + ... + ) + +(5) In the terminal type: + + .. code-block:: sh + + ./manage.py fobi_sync_plugins + +(6) Assign appropriate permissions to the target users/groups to be using + the plugin if ``FOBI_RESTRICT_PLUGIN_ACCESS`` is set to ``True``. + +Controlling HTML tags and attributes +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +(1) Install ``bleach``. + + .. code-block:: sh + + pip install bleach + +(2) Specify ``FOBI_PLUGIN_CONTENT_RICHTEXT_ALLOWED_TAGS`` and + ``FOBI_PLUGIN_CONTENT_RICHTEXT_ALLOWED_ATTRIBUTES`` in + ``settings.py``. The default values are: + + .. code-block:: python + + FOBI_PLUGIN_CONTENT_RICHTEXT_ALLOWED_TAGS = [ + 'a', + 'abbr', + 'acronym', + 'b', + 'blockquote', + 'code', + 'em', + 'i', + 'li', + 'ol', + 'strong', + 'ul', + ] + + FOBI_PLUGIN_CONTENT_RICHTEXT_ALLOWED_ATTRIBUTES = { + 'a': ['href', 'title'], + 'abbr': ['title'], + 'acronym': ['title'], + } + +For frontend-only control one could alternatively use +a ``summernote`` plugin like ``summernote-cleaner``. diff --git a/src/fobi/contrib/plugins/form_elements/content/content_richtext/__init__.py b/src/fobi/contrib/plugins/form_elements/content/content_richtext/__init__.py new file mode 100644 index 00000000..c2cf0bb6 --- /dev/null +++ b/src/fobi/contrib/plugins/form_elements/content/content_richtext/__init__.py @@ -0,0 +1,10 @@ +__title__ = 'fobi.contrib.plugins.form_elements.content.content_richtext' +__author__ = 'Frantisek Holop ' +__copyright__ = 'RIPE NCC' +__license__ = 'GPL 2.0/LGPL 2.1' +__all__ = ('default_app_config', 'UID') + +default_app_config = 'fobi.contrib.plugins.form_elements.content.' \ + 'content_richtext.apps.Config' + +UID = 'content_richtext' diff --git a/src/fobi/contrib/plugins/form_elements/content/content_richtext/apps.py b/src/fobi/contrib/plugins/form_elements/content/content_richtext/apps.py new file mode 100644 index 00000000..01939507 --- /dev/null +++ b/src/fobi/contrib/plugins/form_elements/content/content_richtext/apps.py @@ -0,0 +1,18 @@ +__title__ = 'fobi.contrib.plugins.form_elements.content.content_richtext.apps' +__author__ = 'Frantisek Holop ' +__copyright__ = 'RIPE NCC' +__license__ = 'GPL 2.0/LGPL 2.1' + +try: + __all__ = ('Config',) + + from django.apps import AppConfig + + class Config(AppConfig): + """Config.""" + + name = 'fobi.contrib.plugins.form_elements.content.content_richtext' + label = 'fobi_contrib_plugins_form_elements_content_content_richtext' + +except ImportError: + pass diff --git a/src/fobi/contrib/plugins/form_elements/content/content_richtext/base.py b/src/fobi/contrib/plugins/form_elements/content/content_richtext/base.py new file mode 100644 index 00000000..e860f2b0 --- /dev/null +++ b/src/fobi/contrib/plugins/form_elements/content/content_richtext/base.py @@ -0,0 +1,47 @@ +from __future__ import absolute_import + +from collections import OrderedDict +from uuid import uuid4 + +from django.utils.encoding import smart_str +from django.utils.translation import ugettext_lazy as _ + +from nonefield.fields import NoneField + +from fobi.base import FormElementPlugin + +from . import UID +from .forms import ContentRichTextForm + +__title__ = 'fobi.contrib.plugins.form_elements.content.content_richtext.base' +__author__ = 'Frantisek Holop ' +__copyright__ = 'RIPE NCC' +__license__ = 'GPL 2.0/LGPL 2.1' +__all__ = ('ContentRichTextPlugin',) + + +class ContentRichTextPlugin(FormElementPlugin): + uid = UID + name = _('Content rich text') + group = _('Content') + form = ContentRichTextForm + + def post_processor(self): + self.data.name = '{0}_{1}'.format(self.uid, uuid4()) + + def get_raw_data(self): + return OrderedDict( + ( + ('text', self.data.text), + ) + ) + + def get_form_field_instances(self, request=None, form_entry=None, + form_element_entries=None, **kwargs): + field_kwargs = { + 'initial': smart_str(self.data.text), + 'required': False, + 'label': '', + } + + return [(self.data.name, NoneField, field_kwargs)] diff --git a/src/fobi/contrib/plugins/form_elements/content/content_richtext/fobi_form_elements.py b/src/fobi/contrib/plugins/form_elements/content/content_richtext/fobi_form_elements.py new file mode 100644 index 00000000..ab1ce5cc --- /dev/null +++ b/src/fobi/contrib/plugins/form_elements/content/content_richtext/fobi_form_elements.py @@ -0,0 +1,15 @@ +from __future__ import absolute_import + +from fobi.base import form_element_plugin_registry + +from .base import ContentRichTextPlugin + +__title__ = 'fobi.contrib.plugins.form_elements.content.content_richtext.' \ + 'fobi_form_elements' +__author__ = 'Frantisek Holop ' +__copyright__ = 'RIPE NCC' +__license__ = 'GPL 2.0/LGPL 2.1' +__all__ = ('ContentRichTextPlugin',) + + +form_element_plugin_registry.register(ContentRichTextPlugin) diff --git a/src/fobi/contrib/plugins/form_elements/content/content_richtext/forms.py b/src/fobi/contrib/plugins/form_elements/content/content_richtext/forms.py new file mode 100644 index 00000000..2f5643b7 --- /dev/null +++ b/src/fobi/contrib/plugins/form_elements/content/content_richtext/forms.py @@ -0,0 +1,68 @@ +from django import forms +from django.conf import settings +from django.utils.translation import ugettext_lazy as _ + +from django_summernote.widgets import SummernoteInplaceWidget + +from fobi.base import BasePluginForm, get_theme + +try: + import bleach + BLEACH_INSTALLED = True +except ImportError as err: + BLEACH_INSTALLED = False + +__title__ = 'fobi.contrib.plugins.form_elements.content.content_richtext.forms' +__author__ = 'Frantisek Holop ' +__copyright__ = 'RIPE NCC' +__license__ = 'GPL 2.0/LGPL 2.1' +__all__ = ('ContentRichTextForm',) + + +theme = get_theme(request=None, as_instance=True) + + +class ContentRichTextForm(forms.Form, BasePluginForm): + plugin_data_fields = [ + ('text', '') + ] + + text = forms.CharField( + label=_('Text'), + required=True, + widget=SummernoteInplaceWidget(), + ) + + def clean_text(self): + if not BLEACH_INSTALLED: + return self.cleaned_data['text'] + + ALLOWED_TAGS = [ + 'a', 'abbr', 'acronym', 'b', 'blockquote', + 'code', 'em', 'i', 'li', 'ol', 'strong', 'ul', + ] + + ALLOWED_ATTRIBUTES = { + 'a': ['href', 'title'], + 'abbr': ['title'], + 'acronym': ['title'], + } + + allowed_tags = getattr( + settings, + 'FOBI_PLUGIN_CONTENT_RICHTEXT_ALLOWED_TAGS', + ALLOWED_TAGS + ) + allowed_attrs = getattr( + settings, + 'FOBI_PLUGIN_CONTENT_RICHTEXT_ALLOWED_ATTRIBUTES', + ALLOWED_ATTRIBUTES + ) + + return bleach.clean( + text=self.cleaned_data['text'], + tags=allowed_tags, + attributes=allowed_attrs, + strip=True, + strip_comments=True, + )