prepare 0.2.1; do not wrap media files in <a/> tags when saving data of db_store plugin; make sure custom form handler actions are properly listed in the simple theme as well; make it possible to fail silently on missing form element- and form handler- plugins;

This commit is contained in:
Artur Barseghyan 2014-11-07 03:09:01 +01:00
parent 309fc06b68
commit d5d505b962
20 changed files with 279 additions and 95 deletions

View file

@ -1,5 +1,19 @@
Release history and notes
=====================================
0.2.1
-------------------------------------
2014-11-06
- Minor improvements of the `db_store` plugin.
- Minor improvements of the `simple` theme. Make sure that custom
form handler actions are properly shown in the form handlers list.
- Make it possible to fail silently on missing form element or form
handler plugins by setting the respected values to False:
* `FAIL_ON_MISSING_FORM_ELEMENT_PLUGINS`,
* `FAIL_ON_MISSING_FORM_HANDLER_PLUGINS`. Raising an appropriate exception
otherwise.
0.2
-------------------------------------
2014-11-05
@ -12,8 +26,8 @@ Note, that this release contains minor backwards incompatible changes.
you have written your own or have changed existing form handler plugins
with use of one of the above mentioned methods, append those arguments to
the method declarations when upgrading to this version. If you haven't
written your own form or changed existing form handler plugins, you may
just upgrade to this version.
written your own or changed existing form handler plugins, you may just
upgrade to this version.
- Added data export features to the ``db_store`` plugin.
- Minor fixes in ``db_store`` plugin.
- Added missing documentation for the ``feincms_integration`` app.

View file

@ -1244,6 +1244,28 @@ somehow doesn't appear in the list of available plugins, do run the
plugins into the database, but also is a great way of checking for possible
errors.
If you have forms refering to form element- of form handler- plugins
that are currently missing (not registered, removed, failed to load - thus
there would be a risk that your form would't be rendered properly/fully and
the necessary data handling wouldn't happen either) you will get an
appropriate exception. Although it's fine to get an instant error message about
such failures in development, in production is wouldn't look appropriate.
Thus, there are two settings related to the non-existing (not-found) form
element- and form handler- plugins.
- FOBI_FAIL_ON_MISSING_FORM_ELEMENT_PLUGINS: If you want no error to be
shown in case of missing form element plugins, set this to False in
your settings module. Default value is True.
- FOBI_FAIL_ON_MISSING_FORM_HANDLER_PLUGINS: If you want no error to be
shown in case of missing form element handlers, set this to False in
your settings module. Default value is True.
Troubleshooting
===============================================
If you get a ``FormElementPluginDoesNotExist`` or a
``FormHandlerPluginDoesNotExist`` exception, make sure you have listed your
plugin in the `settings` module of your project.
License
===============================================
GPL 2.0/LGPL 2.1

View file

@ -208,6 +208,12 @@ Must haves
- Add data export features for the ``db_store`` plugin into the "simpe"
theme as well (same way as already done fore "bootstrap 3" and
"foundation 5" themes.
- Add a management command to remove broken form elements.
- Think of making putting several actions (repair) into the management
interface (UI).
- Add `PluginThemeAddOn`, which would be a stand-alone plugin for having the
specific theme HTML/JS/CSS added to the appropriate form element or a form
handler plugin.
Should haves
===============================================

View file

@ -1244,6 +1244,28 @@ somehow doesn't appear in the list of available plugins, do run the
plugins into the database, but also is a great way of checking for possible
errors.
If you have forms refering to form element- of form handler- plugins
that are currently missing (not registered, removed, failed to load - thus
there would be a risk that your form would't be rendered properly/fully and
the necessary data handling wouldn't happen either) you will get an
appropriate exception. Although it's fine to get an instant error message about
such failures in development, in production is wouldn't look appropriate.
Thus, there are two settings related to the non-existing (not-found) form
element- and form handler- plugins.
- FOBI_FAIL_ON_MISSING_FORM_ELEMENT_PLUGINS: If you want no error to be
shown in case of missing form element plugins, set this to False in
your settings module. Default value is True.
- FOBI_FAIL_ON_MISSING_FORM_HANDLER_PLUGINS: If you want no error to be
shown in case of missing form element handlers, set this to False in
your settings module. Default value is True.
Troubleshooting
===============================================
If you get a ``FormElementPluginDoesNotExist`` or a
``FormHandlerPluginDoesNotExist`` exception, make sure you have listed your
plugin in the `settings` module of your project.
License
===============================================
GPL 2.0/LGPL 2.1

View file

@ -56,7 +56,7 @@ for static_dir in static_dirs:
for locale_dir in locale_dirs:
locale_files += [os.path.join(locale_dir, f) for f in os.listdir(locale_dir)]
version = '0.2'
version = '0.2.1'
install_requires = [
'Pillow>=2.0.0',

View file

@ -1,6 +1,6 @@
__title__ = 'django-fobi'
__version__ = '0.2'
__build__ = 0x000008
__version__ = '0.2.1'
__build__ = 0x000009
__author__ = 'Artur Barseghyan <artur.barseghyan@gmail.com>'
__copyright__ = 'Copyright (c) 2014 Artur Barseghyan'
__license__ = 'GPL 2.0/LGPL 2.1'

View file

@ -54,9 +54,13 @@ from fobi.discover import autodiscover
from fobi.constants import CALLBACK_STAGES
from fobi.settings import (
DEFAULT_THEME, FORM_HANDLER_PLUGINS_EXECUTION_ORDER,
CUSTOM_THEME_DATA, THEME_FOOTER_TEXT, DEBUG
CUSTOM_THEME_DATA, THEME_FOOTER_TEXT, FAIL_ON_MISSING_FORM_ELEMENT_PLUGINS,
FAIL_ON_MISSING_FORM_HANDLER_PLUGINS, DEBUG
)
from fobi.exceptions import (
InvalidRegistryItemType, DoesNotExist, ThemeDoesNotExist,
FormElementPluginDoesNotExist, FormHandlerPluginDoesNotExist
)
from fobi.exceptions import InvalidRegistryItemType, ThemeDoesNotExist
from fobi.helpers import (
uniquify_sequence, map_field_name_to_label, clean_dict,
map_field_name_to_label, get_ignorable_form_values
@ -308,8 +312,12 @@ class BaseTheme(object):
<span class="{delete_option_class}"></span> {delete_text}</a>
</li>
</ul>
<input type="hidden" value="{form_element_position}" name="form-{counter}-position" id="id_form-{counter}-position" class="form-element-position">
<input type="hidden" value="{form_element_pk}" name="form-{counter}-id" id="id_form-{counter}-id">
<input type="hidden" value="{form_element_position}"
name="form-{counter}-position"
id="id_form-{counter}-position"
class="form-element-position">
<input type="hidden" value="{form_element_pk}"
name="form-{counter}-id" id="id_form-{counter}-id">
""".format(
container_class = cls.form_list_container_class,
edit_option_html = "{edit_option_html}",
@ -460,7 +468,9 @@ class BasePluginForm(object):
:param django.http.HttpRequest request:
"""
if self.plugin_data_fields:
return self._get_plugin_data(self.plugin_data_fields, request=request, json_format=json_format)
return self._get_plugin_data(self.plugin_data_fields,
request=request,
json_format=json_format)
def save_plugin_data(self, request=None):
"""
@ -1147,8 +1157,7 @@ class FormElementPlugin(BasePlugin):
extra={}):
"""
If ``kwargs_update_func`` is given, is callable and returns results
without failures,
return the result. Otherwise - return None.
without failures, return the result. Otherwise - return None.
"""
# Check hooks
if kwargs_update_func and callable(kwargs_update_func):
@ -1163,7 +1172,6 @@ class FormElementPlugin(BasePlugin):
return kwargs_update
except Exception as e:
logger.debug(str(e))
#import ipdb; ipdb.set_trace()
return {}
def _submit_plugin_form_data(self, form_entry, request, form):
@ -1371,8 +1379,21 @@ class BaseRegistry(object):
"""
Registry of dash plugins. It's essential, that class registered has the
``uid`` property.
If ``fail_on_missing_plugin`` is set to True, an appropriate exception
(``plugin_not_found_exception_cls``) is raised in cases if plugin cound't
be found in the registry.
:property mixed type:
:property bool fail_on_missing_plugin:
:property fobi.exceptions.DoesNotExist plugin_not_found_exception_cls:
:property str plugin_not_found_error_message:
"""
type = None
fail_on_missing_plugin = False
plugin_not_found_exception_cls = DoesNotExist
plugin_not_found_error_message = "Can't find plugin with uid `{0}` in " \
"`{1}` registry."
def __init__(self):
assert self.type
@ -1432,11 +1453,17 @@ class BaseRegistry(object):
:return mixed.
"""
item = self._registry.get(uid, default)
if not item:
logger.debug(
"Can't find plugin with uid `{0}` in `{1}` "
"registry".format(uid, self.__class__)
err_msg = self.plugin_not_found_error_message.format(
uid, self.__class__
)
if self.fail_on_missing_plugin:
logger.error(err_msg)
raise self.plugin_not_found_exception_cls(err_msg)
else:
logger.debug(err_msg)
return item
@ -1445,6 +1472,8 @@ class FormElementPluginRegistry(BaseRegistry):
Form element plugins registry.
"""
type = (FormElementPlugin, FormFieldPlugin)
fail_on_missing_plugin = FAIL_ON_MISSING_FORM_ELEMENT_PLUGINS
plugin_not_found_exception_cls = FormElementPluginDoesNotExist
class FormHandlerPluginRegistry(BaseRegistry):
@ -1452,6 +1481,8 @@ class FormHandlerPluginRegistry(BaseRegistry):
Form handler plugins registry.
"""
type = FormHandlerPlugin
fail_on_missing_plugin = FAIL_ON_MISSING_FORM_HANDLER_PLUGINS
plugin_not_found_exception_cls = FormHandlerPluginDoesNotExist
class ThemeRegistry(BaseRegistry):
@ -1480,7 +1511,7 @@ class FormCallbackRegistry(object):
:return string:
"""
name = "{0}.{1}".format(cls.__module__, cls.__name__)
return "{0}.{1}".format(cls.__module__, cls.__name__)
def register(self, cls):
"""
@ -1494,7 +1525,7 @@ class FormCallbackRegistry(object):
"`{1}`".format(cls, self.__class__)
)
uid = self.uidfy(cls)
#uid = self.uidfy(cls)
# If item has not been forced yet, add/replace its' value in the
# registry.
@ -1659,7 +1690,9 @@ def assemble_form_field_widget_class(base_class, plugin):
Wrapped class.
"""
def __new__(cls, name, bases, attrs):
new_class = super(DeclarativeMetaclass, cls).__new__(cls, name, bases, attrs)
new_class = super(DeclarativeMetaclass, cls).__new__(
cls, name, bases, attrs
)
return new_class
def render(self, name, value, attrs=None):
@ -1672,7 +1705,9 @@ def assemble_form_field_widget_class(base_class, plugin):
return widget.render(name, value, attrs=attrs)
else:
#print 'rendered using standard'
super(DeclarativeMetaclass, self).render(name, value, attrs=attrs)
super(DeclarativeMetaclass, self).render(
name, value, attrs=attrs
)
class WrappedWidget(with_metaclass(DeclarativeMetaclass, base_class)):
"""
@ -1681,9 +1716,9 @@ def assemble_form_field_widget_class(base_class, plugin):
return WrappedWidget
# ********************************************************************************
# *********************************** Generic ************************************
# ********************************************************************************
# *****************************************************************************
# *********************************** Generic *********************************
# *****************************************************************************
def get_registered_plugins(registry):
"""

View file

@ -57,7 +57,7 @@ class SelectMultipleModelObjectsInputPlugin(FormFieldPlugin):
"""
# Get the object
obj = form.cleaned_data.get(self.data.name, None)
#import ipdb; ipdb.set_trace()
if obj:
# Handle the upload
admin_url = admin_change_url(

View file

@ -7,9 +7,6 @@ __all__ = ('DBStoreHandlerPlugin',)
import json
import datetime
from six import string_types
from django.conf import settings
from django.utils.translation import ugettext_lazy as _
from django.core.urlresolvers import reverse
@ -32,13 +29,10 @@ class DBStoreHandlerPlugin(FormHandlerPlugin):
:param django.http.HttpRequest request:
:param django.forms.Form form:
"""
#import ipdb; ipdb.set_trace()
# Clean up the values, leave our content fields and empty values.
field_name_to_label_map, cleaned_data = get_processed_form_data(form)
for key, value in cleaned_data.items():
if isinstance(value, string_types) and value.startswith(settings.MEDIA_URL):
cleaned_data[key] = '<a href="{value}">{value}</a>'.format(value=value)
if isinstance(value, (datetime.datetime, datetime.date)):
cleaned_data[key] = value.isoformat() if hasattr(value, 'isoformat') else value
@ -58,12 +52,14 @@ class DBStoreHandlerPlugin(FormHandlerPlugin):
"""
return (
(
reverse('fobi.contrib.plugins.form_handlers.db_store.view_saved_form_data_entries', args=[form_entry.pk]),
reverse('fobi.contrib.plugins.form_handlers.db_store.view_saved_form_data_entries',
args=[form_entry.pk]),
_("View entries"),
'glyphicon glyphicon-list'
),
(
reverse('fobi.contrib.plugins.form_handlers.db_store.export_saved_form_data_entries', args=[form_entry.pk]),
reverse('fobi.contrib.plugins.form_handlers.db_store.export_saved_form_data_entries',
args=[form_entry.pk]),
_("Export entries"),
'glyphicon glyphicon-export'
),

View file

@ -6,9 +6,11 @@ __all__ = ('SavedFormDataEntry',)
import json
from six import string_types
from django.db import models
from django.utils.translation import ugettext_lazy as _
#from django.contrib.auth.models import User
from django.conf import settings
from django.db import models
@ -38,7 +40,8 @@ except ImportError:
raise ImproperlyConfigured("Your custom user model ({0}.{1}) doesn't "
"have ``username`` property, while "
"``django-fobi`` relies on its' presence"
".".format(user._meta.app_label, user._meta.object_name))
".".format(user._meta.app_label, \
user._meta.object_name))
# ****************************************************************************
# ****************************************************************************
@ -50,9 +53,12 @@ class SavedFormDataEntry(models.Model):
"""
Saved form data.
"""
form_entry = models.ForeignKey('fobi.FormEntry', verbose_name=_("Form"), null=True, blank=True)
user = models.ForeignKey(User, verbose_name=_("User"), null=True, blank=True)
form_data_headers = models.TextField(_("Form data headers"), null=True, blank=True)
form_entry = models.ForeignKey('fobi.FormEntry', verbose_name=_("Form"),
null=True, blank=True)
user = models.ForeignKey(User, verbose_name=_("User"), null=True,
blank=True)
form_data_headers = models.TextField(_("Form data headers"), null=True,
blank=True)
saved_data = models.TextField(_("Plugin data"), null=True, blank=True)
created = models.DateTimeField(_("Date created"), auto_now_add=True)
@ -72,6 +78,13 @@ class SavedFormDataEntry(models.Model):
"""
headers = json.loads(self.form_data_headers)
data = json.loads(self.saved_data)
for key, value in data.items():
if isinstance(value, string_types) and \
(value.startswith(settings.MEDIA_URL) or \
value.startswith('http://') or value.startswith('https://')):
data[key] = '<a href="{value}">{value}</a>'.format(value=value)
return two_dicts_to_string(headers, data)
formatted_saved_data.allow_tags = True
formatted_saved_data.short_description = _("Saved data")

View file

@ -56,10 +56,7 @@ class HTTPRepostHandlerPlugin(FormHandlerPlugin):
file_path = settings.PROJECT_DIR('../{0}'.format(file_path))
files[field_name] = (imf.name, open(file_path, 'rb'))
#logger.debug("ORIGINAL REQUEST FILES: ")
#logger.debug(request.FILES)
for field_name, imf in request.FILES.items():
#import ipdb; ipdb.set_trace()
try:
file_path = form.cleaned_data.get(field_name, '')
process_path(file_path, imf)
@ -67,9 +64,6 @@ class HTTPRepostHandlerPlugin(FormHandlerPlugin):
file_path = extract_file_path(imf.name)
process_path(file_path, imf)
#logger.debug("FILES: ")
#logger.debug(files)
#import ipdb; ipdb.set_trace()
response = requests.post(self.data.endpoint_url, \
data=request.POST, files=files)

View file

@ -99,6 +99,7 @@
<tbody>
{% for form_handler in form_handlers %}
{% with form_handler.get_plugin as plugin %}
{% if plugin %}
<tr>
<td>{{ form_handler.plugin_name }}
{% if form_handler.plugin_data %}
@ -129,6 +130,7 @@
</ul>
</td>
</tr>
{% endif %}
{% endwith %}
{% endfor %}
</tbody>

View file

@ -1,4 +1,4 @@
{% load i18n %}
{% load i18n fobi_tags %}
<h1>{% trans "Edit form" %}</h1>
@ -107,15 +107,15 @@
</thead>
<tbody>
{% for form_handler in form_handlers %}
{% with form_handler.get_plugin as plugin %}
{% if plugin %}
<tr class="{% if forloop.counter|divisibleby:2 %}row2{% else %}row1{% endif %}">
<td>{{ form_handler.plugin_name }}
{% if form_handler.plugin_data %}
{% with form_handler.get_plugin as plugin %}
{% if form_handler.plugin_data %}
<a class="popover-link" href="javascript:;" data-toggle="popover" data-content="{% spaceless %}{{ plugin.plugin_data_repr|safe }}{% endspaceless %}" role="button">
<span class="badge" title="{% trans 'Info' %}">?</span>
</a>
{% endwith %}
{% endif %}
{% endif %}
</td>
<td>
<ul class="list-inline">
@ -123,9 +123,15 @@
<li><a href="{% url 'fobi.edit_form_handler_entry' form_handler.pk %}"><span class="glyphicon glyphicon-edit"></span> {% trans "Edit" %}</a></li>
{% endif %}
<li><a href="{% url 'fobi.delete_form_handler_entry' form_handler.pk %}"><span class="glyphicon glyphicon-remove"></span> {% trans "Delete" %}</a></li>
{% get_fobi_form_handler_plugin_custom_actions plugin form_entry as form_handler_plugin_custom_actions %}
{% for action in form_handler_plugin_custom_actions %}
<li><a href="{{ action.0 }}"><span class="{{ action.2 }}"></span> {{ action.1 }}</a></li>
{% endfor %}
</ul>
</td>
</tr>
{% endif %}
{% endwith %}
{% endfor %}
</tbody>
</table>

View file

@ -4,11 +4,17 @@ __copyright__ = 'Copyright (c) 2014 Artur Barseghyan'
__license__ = 'GPL 2.0/LGPL 2.1'
__all__ = (
'RESTRICT_PLUGIN_ACCESS', 'FORM_ELEMENT_PLUGINS_MODULE_NAME',
'FORM_HANDLER_PLUGINS_MODULE_NAME', 'FORM_CALLBACKS_MODULE_NAME',
'THEMES_MODULE_NAME', 'DEFAULT_THEME', 'DISPLAY_AUTH_LINK',
'WAIT_BETWEEN_TEST_STEPS', 'WAIT_AT_TEST_END', 'THEME_FOOTER_TEXT',
'FORM_IMPORTER_PLUGINS_MODULE_NAME', 'CUSTOM_THEME_DATA',
'DEBUG',
'FORM_HANDLER_PLUGINS_MODULE_NAME', 'FORM_IMPORTER_PLUGINS_MODULE_NAME',
'FORM_CALLBACKS_MODULE_NAME', 'THEMES_MODULE_NAME', 'DEFAULT_THEME',
'DISPLAY_AUTH_LINK', 'DEBUG',
'CUSTOM_THEME_DATA', 'THEME_FOOTER_TEXT',
'DEFAULT_MAX_LENGTH', 'FORM_HANDLER_PLUGINS_EXECUTION_ORDER',
'FAIL_ON_MISSING_FORM_ELEMENT_PLUGINS',
'FAIL_ON_MISSING_FORM_HANDLER_PLUGINS'
'WAIT_BETWEEN_TEST_STEPS', 'WAIT_AT_TEST_END',
)
from django.utils.translation import ugettext
@ -45,9 +51,6 @@ DEFAULT_THEME = 'bootstrap3'
DISPLAY_AUTH_LINK = True
WAIT_BETWEEN_TEST_STEPS = 2
WAIT_AT_TEST_END = 4
DEBUG = False
# **************************************************************
@ -72,3 +75,15 @@ FORM_HANDLER_PLUGINS_EXECUTION_ORDER = (
# The 'db_store' is left out intentionally, since it should
# be the last plugin to be executed.
)
FAIL_ON_MISSING_FORM_ELEMENT_PLUGINS = True
FAIL_ON_MISSING_FORM_HANDLER_PLUGINS = True
# **************************************************************
# **************************************************************
# ************************ Tests related ***********************
# **************************************************************
# **************************************************************
WAIT_BETWEEN_TEST_STEPS = 2
WAIT_AT_TEST_END = 4

View file

@ -4,7 +4,7 @@ __copyright__ = 'Copyright (c) 2014 Artur Barseghyan'
__license__ = 'GPL 2.0/LGPL 2.1'
__all__ = ('assemble_form_class',)
import logging
#import logging
from six import with_metaclass
@ -12,17 +12,17 @@ from django.utils.datastructures import SortedDict
from django.forms.forms import BaseForm#, get_declared_fields
from django.forms.widgets import media_property
logger = logging.getLogger(__file__)
#logger = logging.getLogger(__file__)
# ******************************************************************************
# ******************************************************************************
# **************************** Form generator **********************************
# ******************************************************************************
# ******************************************************************************
# ****************************************************************************
# ****************************************************************************
# **************************** Form generator ********************************
# ****************************************************************************
# ****************************************************************************
def assemble_form_class(form_entry, base_class=BaseForm, request=None, \
origin=None, origin_kwargs_update_func=None, origin_return_func=None,
form_element_entries=None):
origin=None, origin_kwargs_update_func=None, \
origin_return_func=None, form_element_entries=None):
"""
Assembles a form class by given entry.
@ -32,8 +32,8 @@ def assemble_form_class(form_entry, base_class=BaseForm, request=None, \
:param string origin:
:param callable origin_kwargs_update_func:
:param callable origin_return_func:
:param iterable form_element_entries: If given, used instead of ``form_entry.formelemententry_set.all`` (no
additional database hit).
:param iterable form_element_entries: If given, used instead of
``form_entry.formelemententry_set.all`` (no additional database hit).
"""
if form_element_entries is None:
form_element_entries = form_entry.formelemententry_set.all()
@ -42,26 +42,37 @@ def assemble_form_class(form_entry, base_class=BaseForm, request=None, \
"""
Copied from ``django.forms.forms.DeclarativeFieldsMetaclass``.
Metaclass that converts Field attributes to a dictionary called 'base_fields', taking into
account parent class 'base_fields' as well.
Metaclass that converts Field attributes to a dictionary called
`base_fields`, taking into account parent class 'base_fields' as well.
"""
def __new__(cls, name, bases, attrs):
base_fields = []
for creation_counter, form_element_entry in enumerate(form_element_entries):
plugin = form_element_entry.get_plugin(request=request)
plugin_form_field_instances = plugin._get_form_field_instances(
form_element_entry = form_element_entry,
origin = origin,
kwargs_update_func = origin_kwargs_update_func,
return_func = origin_return_func,
extra = {'counter': creation_counter}
)
for form_field_name, form_field_instance in plugin_form_field_instances:
base_fields.append((form_field_name, form_field_instance))
# We simply make sure the plugin exists. We don't handle
# exceptions relate to the non-existent plugins here. They
# are istead handled in registry.
if plugin:
plugin_form_field_instances = plugin._get_form_field_instances(
form_element_entry = form_element_entry,
origin = origin,
kwargs_update_func = origin_kwargs_update_func,
return_func = origin_return_func,
extra = {'counter': creation_counter}
)
for form_field_name, form_field_instance in plugin_form_field_instances:
base_fields.append((form_field_name, form_field_instance))
attrs['base_fields'] = SortedDict(base_fields)
new_class = super(DeclarativeFieldsMetaclass, cls).__new__(cls, name, bases, attrs)
new_class = super(DeclarativeFieldsMetaclass, cls).__new__(
cls, name, bases, attrs
)
if 'media' not in attrs:
new_class.media = media_property(new_class)
return new_class
class DynamicForm(with_metaclass(DeclarativeFieldsMetaclass, base_class)):

View file

@ -4,7 +4,9 @@ __copyright__ = 'Copyright (c) 2014 Artur Barseghyan'
__license__ = 'GPL 2.0/LGPL 2.1'
__all__ = (
'BaseException', 'ImproperlyConfigured', 'InvalidRegistryItemType',
'DoesNotExist', 'ThemeDoesNotExist', 'NoDefaultThemeSet',
'DoesNotExist', 'ThemeDoesNotExist', 'PluginDoesNotExist',
'FormElementPluginDoesNotExist', 'FormHandlerPluginDoesNotExist',
'NoDefaultThemeSet',
)
class BaseException(Exception):
@ -37,6 +39,23 @@ class ThemeDoesNotExist(DoesNotExist):
"""
class PluginDoesNotExist(DoesNotExist):
"""
Raised when no plugin with given uid can be found.
"""
class FormElementPluginDoesNotExist(PluginDoesNotExist):
"""
Raised when no form element plugin with given uid can be found.
"""
class FormHandlerPluginDoesNotExist(PluginDoesNotExist):
"""
Raised when no form handler plugin with given uid can be found.
"""
class NoDefaultThemeSet(ImproperlyConfigured):
"""
Raised when no active theme is chosen.

View file

@ -395,6 +395,11 @@ class AbstractPluginEntry(models.Model):
if not cls:
# No need to log here, since already logged in registry.
if registry.fail_on_missing_plugin:
err_msg = registry.plugin_not_found_error_message.format(
self.plugin_uid, registry.__class__
)
raise registry.plugin_not_found_exception_cls(err_msg)
return None
# Creating plugin instance.

View file

@ -15,12 +15,18 @@ __author__ = 'Artur Barseghyan <artur.barseghyan@gmail.com>'
__copyright__ = 'Copyright (c) 2014 Artur Barseghyan'
__license__ = 'GPL 2.0/LGPL 2.1'
__all__ = (
'RESTRICT_PLUGIN_ACCESS', 'FORM_ELEMENT_PLUGINS_MODULE_NAME',
'FORM_HANDLER_PLUGINS_MODULE_NAME', 'FORM_CALLBACKS_MODULE_NAME',
'THEMES_MODULE_NAME', 'DEFAULT_THEME', 'DISPLAY_AUTH_LINK',
'WAIT_BETWEEN_TEST_STEPS', 'WAIT_AT_TEST_END', 'THEME_FOOTER_TEXT',
'FORM_HANDLER_PLUGINS_EXECUTION_ORDER', 'CUSTOM_THEME_DATA',
'FORM_IMPORTER_PLUGINS_MODULE_NAME', 'DEBUG',
'RESTRICT_PLUGIN_ACCESS', 'FORM_ELEMENT_PLUGINS_MODULE_NAME',
'FORM_HANDLER_PLUGINS_MODULE_NAME', 'FORM_IMPORTER_PLUGINS_MODULE_NAME',
'FORM_CALLBACKS_MODULE_NAME', 'THEMES_MODULE_NAME', 'DEFAULT_THEME',
'DISPLAY_AUTH_LINK', 'DEBUG',
'CUSTOM_THEME_DATA', 'THEME_FOOTER_TEXT',
'DEFAULT_MAX_LENGTH', 'FORM_HANDLER_PLUGINS_EXECUTION_ORDER',
'FAIL_ON_MISSING_FORM_ELEMENT_PLUGINS',
'FAIL_ON_MISSING_FORM_HANDLER_PLUGINS'
'WAIT_BETWEEN_TEST_STEPS', 'WAIT_AT_TEST_END',
)
from fobi.conf import get_setting
@ -31,14 +37,16 @@ from fobi.exceptions import NoDefaultThemeSet
# *************************** Core *****************************
# **************************************************************
# **************************************************************
RESTRICT_PLUGIN_ACCESS = get_setting('RESTRICT_PLUGIN_ACCESS')
FORM_ELEMENT_PLUGINS_MODULE_NAME = get_setting('FORM_ELEMENT_PLUGINS_MODULE_NAME')
FORM_ELEMENT_PLUGINS_MODULE_NAME = \
get_setting('FORM_ELEMENT_PLUGINS_MODULE_NAME')
FORM_HANDLER_PLUGINS_MODULE_NAME = get_setting('FORM_HANDLER_PLUGINS_MODULE_NAME')
FORM_HANDLER_PLUGINS_MODULE_NAME = \
get_setting('FORM_HANDLER_PLUGINS_MODULE_NAME')
FORM_IMPORTER_PLUGINS_MODULE_NAME = get_setting('FORM_IMPORTER_PLUGINS_MODULE_NAME')
FORM_IMPORTER_PLUGINS_MODULE_NAME = \
get_setting('FORM_IMPORTER_PLUGINS_MODULE_NAME')
FORM_CALLBACKS_MODULE_NAME = get_setting('FORM_CALLBACKS_MODULE_NAME')
@ -51,9 +59,6 @@ DISPLAY_AUTH_LINK = get_setting('DISPLAY_AUTH_LINK')
if not DEFAULT_THEME:
raise NoDefaultThemeSet("No default theme set!")
WAIT_BETWEEN_TEST_STEPS = get_setting('WAIT_BETWEEN_TEST_STEPS')
WAIT_AT_TEST_END = get_setting('WAIT_AT_TEST_END')
DEBUG = get_setting('DEBUG')
# **************************************************************
@ -69,7 +74,21 @@ THEME_FOOTER_TEXT = get_setting('THEME_FOOTER_TEXT')
# *********************** Plugin related ***********************
# **************************************************************
# **************************************************************
DEFAULT_MAX_LENGTH = get_setting('DEFAULT_MAX_LENGTH')
FORM_HANDLER_PLUGINS_EXECUTION_ORDER = get_setting('FORM_HANDLER_PLUGINS_EXECUTION_ORDER')
FORM_HANDLER_PLUGINS_EXECUTION_ORDER = \
get_setting('FORM_HANDLER_PLUGINS_EXECUTION_ORDER')
FAIL_ON_MISSING_FORM_ELEMENT_PLUGINS = \
get_setting('FAIL_ON_MISSING_FORM_ELEMENT_PLUGINS')
FAIL_ON_MISSING_FORM_HANDLER_PLUGINS = \
get_setting('FAIL_ON_MISSING_FORM_HANDLER_PLUGINS')
# **************************************************************
# **************************************************************
# ************************ Tests related ***********************
# **************************************************************
# **************************************************************
WAIT_BETWEEN_TEST_STEPS = get_setting('WAIT_BETWEEN_TEST_STEPS')
WAIT_AT_TEST_END = get_setting('WAIT_AT_TEST_END')

View file

@ -103,6 +103,7 @@
<tbody>
{% for form_handler in form_handlers %}
{% with form_handler.get_plugin as plugin %}
{% if plugin %}
<tr>
<td>{{ form_handler.plugin_name }}
{% if form_handler.plugin_data or plugin.plugin_data_repr %}
@ -125,6 +126,7 @@
</ul>
</td>
</tr>
{% endif %}
{% endwith %}
{% endfor %}
</tbody>

View file

@ -10,6 +10,8 @@ __all__ = (
'dashboard', 'view_form_entry', 'form_entry_submitted',
)
#import logging
from django.template import RequestContext
from django.shortcuts import render_to_response, redirect
from django.core.exceptions import ObjectDoesNotExist
@ -43,6 +45,7 @@ from fobi.utils import (
append_edit_and_delete_links_to_field
)
#logger = logging.getLogger(__name__)
# *****************************************************************************
# *****************************************************************************