mirror of
https://github.com/Hopiu/django-fobi.git
synced 2026-05-22 11:21:53 +00:00
Merge pull request #100 from minusf/richtext
fobi rich text content plugin based on summernote
This commit is contained in:
commit
18e579b9f8
7 changed files with 254 additions and 0 deletions
|
|
@ -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 \
|
||||
\
|
||||
|
|
|
|||
|
|
@ -0,0 +1,93 @@
|
|||
fobi.contrib.plugins.form_elements.content.content_richtext
|
||||
-----------------------------------------------------------
|
||||
|
||||
A ``Fobi`` Rich text form element plugin based on
|
||||
`Summernote <https://summernote.org/>`_.
|
||||
|
||||
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``.
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
__title__ = 'fobi.contrib.plugins.form_elements.content.content_richtext'
|
||||
__author__ = 'Frantisek Holop <fholop@ripe.net>'
|
||||
__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'
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
__title__ = 'fobi.contrib.plugins.form_elements.content.content_richtext.apps'
|
||||
__author__ = 'Frantisek Holop <fholop@ripe.net>'
|
||||
__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
|
||||
|
|
@ -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 <fholop@ripe.net>'
|
||||
__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)]
|
||||
|
|
@ -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 <fholop@ripe.net>'
|
||||
__copyright__ = 'RIPE NCC'
|
||||
__license__ = 'GPL 2.0/LGPL 2.1'
|
||||
__all__ = ('ContentRichTextPlugin',)
|
||||
|
||||
|
||||
form_element_plugin_registry.register(ContentRichTextPlugin)
|
||||
|
|
@ -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 <fholop@ripe.net>'
|
||||
__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,
|
||||
)
|
||||
Loading…
Reference in a new issue