add markdown plugin

This commit is contained in:
Artur Barseghyan 2017-12-21 14:09:47 +01:00
parent 4a66c1ee42
commit a3e83e54ce
36 changed files with 617 additions and 19 deletions

1
.gitignore vendored
View file

@ -73,3 +73,4 @@ README_PARTS.rst
/examples/wagtaildemo/static
/examples/wagtaildemo/media
/examples/wagtaildemo/Vagrantfile.local
/src/fobi/contrib/themes/bootstrap3/widgets/form_elements/content_markdown_bootstrap3_widget

View file

@ -15,6 +15,12 @@ are used for versioning (schema follows below):
0.3.4 to 0.4).
- All backwards incompatible changes are mentioned in this document.
0.12.9
------
2017-12-21
- Added content markdown plugin.
0.12.8
------
2017-12-19

View file

@ -0,0 +1,18 @@
-r common.txt
-r test.txt
-r style_checkers.txt
-r djangorestframework.txt
Django>=2.0,<2.1
django-admin-tools>=0.8.0
django-ckeditor>=5.4.0
django-debug-toolbar>=1.6
django-formtools>=2.0
django-registration-redux>=1.4
django-simple-captcha>=0.5.5
easy-thumbnails>=2.4.1
sqlparse>=0.2.2
https://github.com/neithere/django-autoslug/archive/master.zip
# django-formtools, compatible with Django 1.11
#https://github.com/django/django-formtools/archive/master.tar.gz

View file

@ -266,8 +266,8 @@ INSTALLED_APPS = [
'easy_thumbnails', # Thumbnailer
'registration', # Auth views and registration app
'captcha',
# 'django_summernote', # WYSIWYG
'ckeditor',
'fobi.reusable.markdown_widget',
# 'ckeditor_uploader',
# ***********************************************************************
@ -334,6 +334,7 @@ INSTALLED_APPS = [
# ***********************************************************************
'fobi.contrib.plugins.form_elements.content.content_image',
'fobi.contrib.plugins.form_elements.content.content_image_url',
'fobi.contrib.plugins.form_elements.content.content_markdown',
'fobi.contrib.plugins.form_elements.content.content_text',
'fobi.contrib.plugins.form_elements.content.content_richtext',
'fobi.contrib.plugins.form_elements.content.content_video',
@ -379,6 +380,10 @@ INSTALLED_APPS = [
'fobi.contrib.themes.bootstrap3.widgets.form_elements.'
'content_richtext_bootstrap3_widget',
# # Markdown
# 'fobi.contrib.themes.bootstrap3.widgets.form_elements.'
# 'content_markdown_bootstrap3_widget',
# ***********************************************************************
# ************************ Foundation 5 theme ***************************
# ***********************************************************************
@ -450,6 +455,7 @@ INSTALLED_APPS = [
# Presentational elements
'fobi.contrib.apps.drf_integration.form_elements.content.content_image',
'fobi.contrib.apps.drf_integration.form_elements.content.content_image_url',
'fobi.contrib.apps.drf_integration.form_elements.content.content_markdown',
'fobi.contrib.apps.drf_integration.form_elements.content.content_richtext',
'fobi.contrib.apps.drf_integration.form_elements.content.content_text',
'fobi.contrib.apps.drf_integration.form_elements.content.content_video',

View file

@ -90,15 +90,12 @@ if 'feincms' in settings.INSTALLED_APPS:
else:
urlpatterns += i18n_patterns(*url_patterns_args)
# Conditionally include django-summernote
if 'django_summernote' in settings.INSTALLED_APPS:
url_patterns_args = [
url(r'^summernote/', include('django_summernote.urls')),
]
if versions.DJANGO_LTE_1_7:
urlpatterns += i18n_patterns('', *url_patterns_args)
else:
urlpatterns += i18n_patterns(*url_patterns_args)
# # Conditionally include django-markdownx
# if 'markdownx' in settings.INSTALLED_APPS:
# url_patterns_args = [
# url(r'^markdownx/', include('markdownx.urls')),
# ]
# urlpatterns += list(url_patterns_args)
if 'ckeditor_uploader' in settings.INSTALLED_APPS:
url_patterns_args = [

View file

@ -0,0 +1,11 @@
django-autoslug==1.9.3
django-formtools>=2.0
django-nine>=0.1.13
django-nonefield>=0.1
easy-thumbnails>=2.4.1
Pillow>=2.0.0
requests>=1.0.0
six>=1.9
Unidecode>=0.04.1
vishap>=0.1.5,<2.0
#https://github.com/django/django-formtools/archive/master.tar.gz

View file

@ -4,7 +4,7 @@ import sys
from distutils.version import LooseVersion
from setuptools import setup, find_packages
version = '0.12.8'
version = '0.12.9'
# ***************************************************************************
# ************************** Python version *********************************

View file

@ -1,6 +1,6 @@
__title__ = 'django-fobi'
__version__ = '0.12.8'
__build__ = 0x000091
__version__ = '0.12.9'
__build__ = 0x000092
__author__ = 'Artur Barseghyan <artur.barseghyan@gmail.com>'
__copyright__ = '2014-2017 Artur Barseghyan'
__license__ = 'GPL 2.0/LGPL 2.1'

View file

@ -19,8 +19,9 @@ __copyright__ = '2016-2017 Artur Barseghyan'
__license__ = 'GPL 2.0/LGPL 2.1'
__all__ = (
'ContentImageField',
'ContentMarkdownField',
'ContentRichTextField',
'ContentTextField',
'ContentRichTextField'
'ContentVideoField',
'ModelChoiceField',
'ModelMultipleChoiceField',
@ -145,6 +146,10 @@ class ContentRichTextField(NoneField):
"""Content rich text field."""
class ContentMarkdownField(NoneField):
"""Content markdown field."""
class ContentImageField(NoneField):
"""Content image field."""

View file

@ -0,0 +1,63 @@
fobi.contrib.apps.drf_integration.form_elements.content.content_markdown
########################################################################
A ``django-fobi`` ContentMarkdown plugin for integration with
``Django REST framework``. Makes use of the
``fobi.contrib.apps.drf_integration.fields.ContentMarkdown``.
Installation
^^^^^^^^^^^^
(1) Add ``fobi.contrib.apps.drf_integration.form_elements.content.content_markdown``
to the ``INSTALLED_APPS`` in your ``settings.py``.
.. code-block:: python
INSTALLED_APPS = (
# ...
'fobi.contrib.apps.drf_integration.form_elements.content.content_markdown',
# ...
)
(2) In the terminal type:
.. code-block:: sh
./manage.py fobi_sync_plugins
(3) Assign appropriate permissions to the target users/groups to be using
the plugin if ``FOBI_RESTRICT_PLUGIN_ACCESS`` is set to True.
Usage
^^^^^
Unlike standard fields, ``ContentMarkdown`` field is purely presentational.
You're not supposed to make write actions on it (it won't work). Neither
will it be displayed in the browsable API (list/retrieve actions). However,
it will be listed in the options action call.
**Sample JSON response fragment**
.. code-block:: javascript
"actions": {
"PUT": {
// ...
"content_markdown_89c8c319-195b-487a-a44d-f59ef14a5d44": {
"type": "content",
"required": false,
"read_only": true,
"contenttype": "text",
"content": "\n<p>\n\nLorem ipsum dolor sit amet.\n\n\n</p>\n",
"raw": {
"text": "Lorem ipsum dolor sit amet."
}
},
// ...
}
}
**JSON response fragment explained**
- ``type`` (str): Set to "content" for all presentational form elements.
- ``contenttype`` (str): Set to "text" for ``ContentText`` field.
- ``content`` (str): Representation of the content. Rendered partial HTML.
- ``raw`` (json dict): Raw attributes of the ``ContentText`` plugin. Contains
"text" attribute.

View file

@ -0,0 +1,11 @@
__title__ = 'fobi.contrib.apps.drf_integration.form_elements.content.' \
'content_markdown'
__author__ = 'Artur Barseghyan <artur.barseghyan@gmail.com>'
__copyright__ = '2014-2017 Artur Barseghyan'
__license__ = 'GPL 2.0/LGPL 2.1'
__all__ = ('default_app_config', 'UID',)
default_app_config = 'fobi.contrib.apps.drf_integration.form_elements.' \
'content.content_markdown.apps.Config'
UID = 'content_markdown'

View file

@ -0,0 +1,19 @@
__title__ = 'fobi.contrib.apps.drf_integration.form_elements.content.' \
'content_markdown.apps'
__author__ = 'Artur Barseghyan <artur.barseghyan@gmail.com>'
__copyright__ = '2014-2017 Artur Barseghyan'
__license__ = 'GPL 2.0/LGPL 2.1'
try:
__all__ = ('Config',)
from django.apps import AppConfig
class Config(AppConfig):
name = 'fobi.contrib.apps.drf_integration.form_elements.content.' \
'content_markdown'
label = 'fobi_contrib_apps_drf_integration_form_elements_content_' \
'content_markdown'
except ImportError:
pass

View file

@ -0,0 +1,66 @@
import logging
from django.utils.translation import ugettext_lazy as _
from .......base import IntegrationFormElementPlugin
from .... import UID as INTEGRATE_WITH_UID
from ....base import (
DRFIntegrationFormElementPluginProcessor,
DRFSubmitPluginFormDataMixin,
)
from ....fields import ContentMarkdownField
from . import UID
__title__ = 'fobi.contrib.apps.drf_integration.form_elements.content.' \
'content_markdown.base'
__author__ = 'Artur Barseghyan <artur.barseghyan@gmail.com>'
__copyright__ = '2014-2017 Artur Barseghyan'
__license__ = 'GPL 2.0/LGPL 2.1'
__all__ = ('ContentMarkdownPlugin',)
LOGGER = logging.getLogger(__name__)
class ContentMarkdownPlugin(IntegrationFormElementPlugin,
DRFSubmitPluginFormDataMixin):
"""Content markdown (CharField) plugin."""
uid = UID
integrate_with = INTEGRATE_WITH_UID
name = _("Content markdown")
group = _("Content")
def get_custom_field_instances(self,
form_element_plugin,
request=None,
form_entry=None,
form_element_entries=None,
has_value=None,
**kwargs):
"""Get form field instances."""
rendered_text = form_element_plugin.get_rendered_text()
raw_data = form_element_plugin.get_raw_data()
field_kwargs = {
'initial': rendered_text,
'default': rendered_text,
'required': False,
'label': '',
'read_only': True,
'raw_data': raw_data,
}
field_metadata = {
'type': 'content',
'contenttype': 'text',
'content': rendered_text,
'raw_data': raw_data
}
return [
DRFIntegrationFormElementPluginProcessor(
field_class=ContentMarkdownField,
field_kwargs=field_kwargs,
field_metadata=field_metadata
)
]

View file

@ -0,0 +1,12 @@
from .......base import integration_form_element_plugin_registry
from .base import ContentMarkdownPlugin
__title__ = 'fobi.contrib.apps.drf_integration.form_elements.content.' \
'content_markdown.fobi_integration_form_elements'
__author__ = 'Artur Barseghyan <artur.barseghyan@gmail.com>'
__copyright__ = '2014-2017 Artur Barseghyan'
__license__ = 'GPL 2.0/LGPL 2.1'
__all__ = ('ContentMarkdownPlugin',)
integration_form_element_plugin_registry.register(ContentMarkdownPlugin)

View file

@ -0,0 +1,46 @@
fobi.contrib.plugins.form_elements.content.content_markdown
-----------------------------------------------------------
A ``Fobi`` Markdown form element plugin based on
`remarkable <https://github.com/jonschlinkert/remarkable/>`_ and
`markdown <https://github.com/Python-Markdown/markdown>`_.
Installation
~~~~~~~~~~~~
(1) Install ``markdown``.
.. code-block:: sh
pip install markdown
(2) Add ``fobi.reusable.markdown_widget`` to ``INSTALLED_APPS`` in
``settings.py``.
.. code-block:: python
INSTALLED_APPS = (
...
'fobi.reusable.markdown_widget',
...
)
(3) Add ``fobi.contrib.plugins.form_elements.content.content_markdown`` to
``INSTALLED_APPS`` in ``settings.py``.
.. code-block:: python
INSTALLED_APPS = (
...
'fobi.contrib.plugins.form_elements.content.content_markdown',
...
)
(4) In the terminal type:
.. code-block:: sh
./manage.py fobi_sync_plugins
(5) Assign appropriate permissions to the target users/groups to be using
the plugin if ``FOBI_RESTRICT_PLUGIN_ACCESS`` is set to ``True``.

View file

@ -0,0 +1,10 @@
__title__ = 'fobi.contrib.plugins.form_elements.content.content_markdown'
__author__ = 'Artur Barseghyan <artur.barseghyan@gmail.com>'
__copyright__ = '2014-2017 Artur Barseghyan'
__license__ = 'GPL 2.0/LGPL 2.1'
__all__ = ('default_app_config', 'UID')
default_app_config = 'fobi.contrib.plugins.form_elements.content.' \
'content_markdown.apps.Config'
UID = 'content_markdown'

View file

@ -0,0 +1,18 @@
__title__ = 'fobi.contrib.plugins.form_elements.content.content_markdown.apps'
__author__ = 'Artur Barseghyan <artur.barseghyan@gmail.com>'
__copyright__ = '2014-2017 Artur Barseghyan'
__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_markdown'
label = 'fobi_contrib_plugins_form_elements_content_content_markdown'
except ImportError:
pass

View file

@ -0,0 +1,60 @@
from __future__ import absolute_import
from collections import OrderedDict
from uuid import uuid4
from django.utils.translation import ugettext_lazy as _
from nonefield.fields import NoneField
from fobi.base import FormElementPlugin
from fobi.reusable.markdown_widget.helpers import convert_to_markdown
from . import UID
from .forms import ContentMarkdownForm
__title__ = 'fobi.contrib.plugins.form_elements.content.content_richtext.base'
__author__ = 'Artur Barseghyan <artur.barseghyan@gmail.com>'
__copyright__ = '2014-2017 Artur Barseghyan'
__license__ = 'GPL 2.0/LGPL 2.1'
__all__ = ('ContentMarkdownPlugin',)
class ContentMarkdownPlugin(FormElementPlugin):
"""Content markdown plugin."""
uid = UID
name = _('Content markdown')
group = _('Content')
form = ContentMarkdownForm
html_classes = ['content-markdown']
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_rendered_text(self):
"""Get rendered text."""
rendered_text = "<div>{0}</div>".format(
convert_to_markdown(self.data.text)
)
return rendered_text
def get_form_field_instances(self,
request=None,
form_entry=None,
form_element_entries=None,
**kwargs):
field_kwargs = {
'initial': convert_to_markdown(self.data.text),
'required': False,
'label': '',
}
return [(self.data.name, NoneField, field_kwargs)]

View file

@ -0,0 +1,15 @@
from __future__ import absolute_import
from fobi.base import form_element_plugin_registry
from .base import ContentMarkdownPlugin
__title__ = 'fobi.contrib.plugins.form_elements.content.content_markdown.' \
'fobi_form_elements'
__author__ = 'Artur Barseghyan <artur.barseghyan@gmail.com>'
__copyright__ = '2014-2017 Artur Barseghyan'
__license__ = 'GPL 2.0/LGPL 2.1'
__all__ = ('ContentMarkdownPlugin',)
form_element_plugin_registry.register(ContentMarkdownPlugin)

View file

@ -0,0 +1,34 @@
from django import forms
from django.utils.translation import ugettext_lazy as _
from fobi.base import BasePluginForm, get_theme
from fobi.reusable.markdown_widget.widgets import MarkdownWidget
__title__ = 'fobi.contrib.plugins.form_elements.content.content_richtext.forms'
__author__ = 'Artur Barseghyan <artur.barseghyan@gmail.com>'
__copyright__ = '2014-2017 Artur Barseghyan'
__license__ = 'GPL 2.0/LGPL 2.1'
__all__ = ('ContentMarkdownForm',)
theme = get_theme(request=None, as_instance=True)
class ContentMarkdownForm(forms.Form, BasePluginForm):
"""ContentMarkDownForm."""
plugin_data_fields = [
('text', '')
]
text = forms.CharField(
label=_('Text'),
required=True,
widget=MarkdownWidget(
attrs={
'class': '{} content-markdown'.format(
theme.form_element_html_class
)
}
),
)

View file

@ -0,0 +1,24 @@
/*
Document : fobi.plugin.content_markdown.js
Created on : Dec 21, 2017, 03:30:35 AM
Author : Artur Barseghyan (artur.barseghyan@gmail.com)
Description:
Markdown plugin init scripts.
*/
;
$(document).ready(function() {
// var markdownConverter = new showdown.Converter();
var markdownConverter = new Remarkable();
var markdownTextarea = $('div.markdown-widget-wrapper textarea');
var markdownPreview = $('div.markdown-widget-wrapper .markdown-preview');
function generateMarkdownPreview() {
var markdownText = markdownTextarea.val();
// var markdownHtml = markdownConverter.makeHtml(markdownText);
var markdownHtml = markdownConverter.render(markdownText);
markdownPreview.html(markdownHtml);
}
markdownTextarea.bind('keyup', generateMarkdownPreview);
generateMarkdownPreview();
});

View file

@ -0,0 +1,21 @@
from __future__ import absolute_import
from fobi.base import FormElementPluginWidget
from . import UID
__title__ = 'fobi.contrib.plugins.form_elements.content.' \
'content_markdown.widgets'
__author__ = 'Artur Barseghyan <artur.barseghyan@gmail.com>'
__copyright__ = '2014-2017 Artur Barseghyan'
__license__ = 'GPL 2.0/LGPL 2.1'
__all__ = (
'BaseContentMarkdownPluginWidget',
)
class BaseContentMarkdownPluginWidget(FormElementPluginWidget):
"""Base content markdown form element plugin widget."""
plugin_uid = UID
html_classes = ['content-markdown']

View file

@ -2,7 +2,6 @@ from django import forms
from django.conf import settings
from django.utils.translation import ugettext_lazy as _
# from django_summernote.widgets import SummernoteInplaceWidget
from ckeditor.widgets import CKEditorWidget
from fobi.base import BasePluginForm, get_theme

View file

@ -0,0 +1,25 @@
=======================================================================================
fobi.contrib.themes.bootstrap3.widgets.form_elements.content_markdown_bootstrap3_widget
=======================================================================================
A rich-text widget to the ``content_richtext`` plugin (for Bootstrap 3 theme).
Installation
============
1. Add ``fobi.contrib.themes.bootstrap3.widgets.form_elements.content_markdown_bootstrap3_widget``
to the ``INSTALLED_APPS`` in your ``settings.py``.
.. code-block:: python
INSTALLED_APPS = (
# ...
'fobi.contrib.themes.bootstrap3',
'fobi.contrib.themes.bootstrap3.widgets.form_elements.content_markdown_bootstrap3_widget',
'fobi.contrib.plugins.form_elements.content.content_markdown',
# ...
)
2. Specify ``bootstrap3`` as a default theme in your ``settings.py``:
.. code-block:: python
FOBI_DEFAULT_THEME = 'bootstrap3'

View file

@ -0,0 +1,9 @@
__title__ = 'fobi.contrib.themes.bootstrap3.widgets.form_elements.' \
'content_markdown_bootstrap3_widget'
__author__ = 'Artur Barseghyan <artur.barseghyan@gmail.com>'
__copyright__ = '2014-2017 Artur Barseghyan'
__license__ = 'GPL 2.0/LGPL 2.1'
__all__ = ('default_app_config',)
default_app_config = 'fobi.contrib.themes.bootstrap3.widgets.form_elements.' \
'content_markdown_bootstrap3_widget.apps.Config'

View file

@ -0,0 +1,20 @@
__title__ = 'fobi.contrib.themes.bootstrap3.widgets.form_elements.' \
'content_markdown_bootstrap3_widget.apps'
__author__ = 'Artur Barseghyan <artur.barseghyan@gmail.com>'
__copyright__ = '2014-2017 Artur Barseghyan'
__license__ = 'GPL 2.0/LGPL 2.1'
__all__ = ('Config',)
try:
from django.apps import AppConfig
class Config(AppConfig):
"""Config."""
name = 'fobi.contrib.themes.bootstrap3.widgets.form_elements.' \
'content_markdown_bootstrap3_widget'
label = 'fobi_contrib_themes_bootstrap3_widgets_form_elements_' \
'content_markdown_bootstrap3_widget'
except ImportError:
pass

View file

@ -0,0 +1,23 @@
from fobi.base import form_element_plugin_widget_registry
from fobi.contrib.plugins.form_elements.content.content_markdown.widgets \
import (
BaseContentMarkdownPluginWidget
)
from fobi.contrib.themes.bootstrap3 import UID
__title__ = 'fobi.contrib.themes.bootstrap3.widgets.form_elements.' \
'content_richtext_bootstrap3_widget.fobi_form_elements'
__author__ = 'Artur Barseghyan <artur.barseghyan@gmail.com>'
__copyright__ = '2014-2017 Artur Barseghyan'
__license__ = 'GPL 2.0/LGPL 2.1'
__all__ = ('ContentMarkdownPluginWidget',)
class ContentMarkdownPluginWidget(BaseContentMarkdownPluginWidget):
"""ContentMarkdownText plugin widget for Bootstrap 3."""
theme_uid = UID
# Registering the widget
form_element_plugin_widget_registry.register(ContentMarkdownPluginWidget)

View file

@ -0,0 +1,4 @@
Reusable
--------
Home for reusable apps that live in fobi codebase, but might be migrated
elsewhere some day.

View file

View file

@ -0,0 +1,4 @@
Markdown widget
~~~~~~~~~~~~~~~
Markdown widget for Django based on `remarkable
<https://github.com/jonschlinkert/remarkable>`_.

View file

@ -0,0 +1,22 @@
from markdown import markdown
__title__ = 'fobi.reusable.markdown_widget.helpers'
__author__ = 'Artur Barseghyan <artur.barseghyan@gmail.com>'
__copyright__ = '2014-2017 Artur Barseghyan'
__license__ = 'GPL 2.0/LGPL 2.1'
__all__ = ('convert_to_markdown',)
def convert_to_markdown(content):
"""
Trans-compiles Markdown text to HTML.
:param content: Markdown text.
:type content: str
:return: HTML encoded text.
:rtype: str
"""
md = markdown(
text=content
)
return md

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,37 @@
from django.forms.utils import flatatt
from django.forms.widgets import Textarea
from django.utils.html import format_html
from fobi.helpers import safe_text
__title__ = 'fobi.reusable.markdown_widget.widgets'
__author__ = 'Artur Barseghyan <artur.barseghyan@gmail.com>'
__copyright__ = '2014-2017 Artur Barseghyan'
__license__ = 'GPL 2.0/LGPL 2.1'
__all__ = ('MarkdownWidget',)
class MarkdownWidget(Textarea):
"""Markdown widget based on showdownjs."""
def render(self, name, value, attrs=None):
if value is None:
value = ''
final_attrs = self.build_attrs(attrs, name=name)
return format_html(
'<div class="markdown-widget-wrapper">'
'<textarea{}>\r\n{}</textarea>'
'<div class="markdown-preview">Preview</div>'
'</div>',
flatatt(final_attrs),
safe_text(value)
)
class Media(object):
"""Media options."""
js = [
# 'markdown_widget/showdown.min.js',
'markdown_widget/remarkable.min.js',
'content_markdown/fobi.plugin.content_markdown.js',
]

View file

@ -6,7 +6,7 @@
{% block extrahead %}
{% if form %}
{{ form.media }}
{{ form.media.css }}
{% endif %}
{{ block.super }}
{% endblock extrahead %}
@ -24,4 +24,11 @@
{% endblock content %}
{% block sidebar-content %}
{% endblock sidebar-content %}
{% endblock sidebar-content %}
{% block theme-javascripts %}
{{ block.super }}
{% if form %}
{{ form.media.js }}
{% endif %}
{% endblock theme-javascripts %}

View file

@ -23,8 +23,8 @@ __author__ = 'Artur Barseghyan <artur.barseghyan@gmail.com>'
__copyright__ = '2014-2017 Artur Barseghyan'
__license__ = 'GPL 2.0/LGPL 2.1'
__all__ = (
'NumberInput',
'BooleanRadioSelect',
'NumberInput',
'RichSelect',
'RichSelectInverseQuotes',
)
@ -110,7 +110,8 @@ class RichSelectInverseQuotes(RichSelect):
"""
if versions.DJANGO_GTE_1_11:
template_name = 'fobi/django/forms/widgets/rich_select_inverse.html'
option_template_name = 'fobi/django/forms/widgets/rich_select_inverse_option.html'
option_template_name = 'fobi/django/forms/widgets/' \
'rich_select_inverse_option.html'
elif versions.DJANGO_GTE_1_10:
def render(self, name, value, attrs=None):