django-fobi/src/fobi/models.py

489 lines
16 KiB
Python

from __future__ import absolute_import
__title__ = 'fobi.models'
__author__ = 'Artur Barseghyan <artur.barseghyan@gmail.com>'
__copyright__ = 'Copyright (c) 2014 Artur Barseghyan'
__license__ = 'GPL 2.0/LGPL 2.1'
__all__ = (
# Plugins
'AbstractPluginModel', 'FormElement', 'FormHandler',
# Entries
'AbstractPluginEntry', 'FormWizardEntry', 'FormEntry', 'FormElementEntry',
'FormFieldsetEntry', 'FormHandlerEntry',
)
import logging
logger = logging.getLogger(__name__)
from django.core.urlresolvers import reverse
from django.db import models
from django.utils.translation import ugettext_lazy as _
#from django.contrib.auth.models import User, Group
from django.contrib.auth.models import Group
from autoslug import AutoSlugField
from fobi.base import (
get_registered_form_element_plugins, get_registered_form_handler_plugins,
form_element_plugin_registry, form_handler_plugin_registry
)
# ****************************************************************************
# **************** Safe User import for Django > 1.5, < 1.8 ******************
# ****************************************************************************
try:
# Django 1.7 check
from django.apps import AppConfig
from django.conf import settings
User = settings.AUTH_USER_MODEL
except ImportError:
# Django 1.6 check
try:
from django.contrib.auth import get_user_model
# Fall back to Django 1.5
except ImportError:
from django.contrib.auth.models import User
else:
User = get_user_model()
# Sanity checks
user = User()
if not hasattr(user, 'username'):
from fobi.exceptions import ImproperlyConfigured
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))
# ****************************************************************************
# ****************************************************************************
# ****************************************************************************
# ****************************************************************************
# ****************************************************************************
# ******************************* Plugin models ******************************
# ****************************************************************************
# ****************************************************************************
class AbstractPluginModel(models.Model):
"""
Abstract plugin model.
Used when ``fobi.settings.RESTRICT_PLUGIN_ACCESS`` is set to True.
:Properties:
- `plugin_uid` (str): Plugin UID.
- `users` (django.contrib.auth.models.User): White list of the users
allowed to use the plugin.
- `groups` (django.contrib.auth.models.Group): White list of the
user groups allowed to use the plugin.
"""
#plugin_uid = models.CharField(_("Plugin UID"), max_length=255, unique=True, editable=False)
users = models.ManyToManyField(User, verbose_name=_("User"), null=True, \
blank=True)
groups = models.ManyToManyField(Group, verbose_name=_("Group"), null=True, \
blank=True)
class Meta:
abstract = True
#def __init__(self, *args, **kwargs):
# """
# Add choices.
# """
# super(AbstractPluginModel, self).__init__(*args, **kwargs)
# plugin_uid = self._meta.get_field('plugin_uid')
# plugin_uid._choices = self.get_registered_plugins()
def get_registered_plugins(self):
"""
"""
raise NotImplemented(
"You should implement ``get_registered_plugins`` method!"
)
def __unicode__(self):
return "{0} ({1})".format(
dict(self.get_registered_plugins()).get(self.plugin_uid, ''),
self.plugin_uid
)
def plugin_uid_code(self):
"""
Mainly used in admin.
"""
return self.plugin_uid
plugin_uid_code.allow_tags = True
plugin_uid_code.short_description = _('UID')
def plugin_uid_admin(self):
"""
Mainly used in admin.
"""
return self.__unicode__()
plugin_uid_admin.allow_tags = True
plugin_uid_admin.short_description = _('Plugin')
def groups_list(self):
"""
Flat list (comma separated string) of groups allowed to use the
plugin. Used in Django admin.
:return string:
"""
return ', '.join([g.name for g in self.groups.all()])
groups_list.allow_tags = True
groups_list.short_description = _('Groups')
def users_list(self):
"""
Flat list (comma separated string) of users allowed to use the
plugin. Used in Django admin.
:return string:
"""
return ', '.join([u.username for u in self.users.all()])
users_list.allow_tags = True
users_list.short_description = _('Users')
class FormElement(AbstractPluginModel):
"""
Form field plugin. Used when ``fobi.settings.RESTRICT_PLUGIN_ACCESS``
is set to True.
:Properties:
- `plugin_uid` (str): Plugin UID.
- `users` (django.contrib.auth.models.User): White list of the users
allowed to use the form element plugin.
- `groups` (django.contrib.auth.models.Group): White list of the user
groups allowed to use the form element plugin.
"""
plugin_uid = models.CharField(
_("Plugin UID"), max_length=255, unique=True, editable=False,
choices=get_registered_form_element_plugins()
)
#objects = FormFieldPluginModelManager()
class Meta:
abstract = False
verbose_name = _("Form element plugin")
verbose_name_plural = _("Form element plugins")
def get_registered_plugins(self):
"""
Add choices.
"""
return get_registered_form_element_plugins()
class FormHandler(AbstractPluginModel):
"""
Form handler plugin. Used when ``fobi.settings.RESTRICT_PLUGIN_ACCESS``
is set to True.
:Properties:
- `plugin_uid` (str): Plugin UID.
- `users` (django.contrib.auth.models.User): White list of the users
allowed to use the form handler plugin.
- `groups` (django.contrib.auth.models.Group): White list of the
user groups allowed to use the form handler plugin.
"""
plugin_uid = models.CharField(
_("Plugin UID"), max_length=255, unique=True, editable=False,
choices=get_registered_form_handler_plugins()
)
#objects = FormHandlerPluginModelManager()
class Meta:
abstract = False
verbose_name = _("Form handler plugin")
verbose_name_plural = _("Form handler plugins")
def get_registered_plugins(self):
"""
Add choices.
"""
return get_registered_form_handler_plugins()
# *****************************************************************************
# *****************************************************************************
# ******************************** Entry models *******************************
# *****************************************************************************
# *****************************************************************************
class FormWizardEntry(models.Model):
"""
"""
user = models.ForeignKey(User, verbose_name=_("User"))
name = models.CharField(_("Name"), max_length=255)
slug = AutoSlugField(populate_from='name', verbose_name=_("Slug"), \
unique=True)
is_public = models.BooleanField(
_("Is public?"), default=False,
help_text=_("Makes your form wizard visible to the public.")
)
is_cloneable = models.BooleanField(
_("Is cloneable?"), default=False,
help_text=_("Makes your form wizard cloneable by other users.")
)
class Meta:
verbose_name = _("Form wizard entry")
verbose_name_plural = _("Form wizard entries")
unique_together = (('user', 'slug'), ('user', 'name'),)
def __unicode__(self):
return self.name
def get_absolute_url(self):
"""
Absolute URL, which goes to the dashboard workspace page.
:return string:
"""
return reverse('fobi.form_wizard', kwargs={'slug': self.slug})
class FormEntry(models.Model):
"""
Form entry.
:Properties:
- `user` (django.contrib.auth.models.User: User owning the plugin.
- `wizard` (str): Form wizard to which the form entry belongs to.
- `name` (str): Form name.
- `slug` (str): Form slug.
- `description` (str): Form description.
- `is_public` (bool): If set to True, is visible to public.
- `is_clonable` (bool): If set to True, is clonable.
- `position` (int): Ordering position in the wizard.
"""
form_wizard_entry = models.ForeignKey(
FormWizardEntry, verbose_name=_("Form wizard"), null=True, blank=True
)
user = models.ForeignKey(User, verbose_name=_("User"))
name = models.CharField(_("Name"), max_length=255)
slug = AutoSlugField(
populate_from='name', verbose_name=_("Slug"), unique=True
)
is_public = models.BooleanField(
_("Public?"), default=False,
help_text=_("Makes your form visible to the public.")
)
is_cloneable = models.BooleanField(
_("Cloneable?"), default=False,
help_text=_("Makes your form cloneable by other users.")
)
position = models.PositiveIntegerField(
_("Position"), null=True, blank=True
)
success_page_title = models.CharField(
_("Success page title"), max_length=255, null=True, blank=True,
help_text=_("Custom message title to display after valid form is "
"submitted")
)
success_page_message = models.TextField(
_("Success page body"), null=True, blank=True,
help_text=_("Custom message text to display after valid form is "
"submitted")
)
action = models.CharField(
_("Action"), max_length=255, null=True, blank=True,
help_text=_("Custom form action")
)
class Meta:
verbose_name = _("Form entry")
verbose_name_plural = _("Form entries")
unique_together = (('user', 'slug'), ('user', 'name'),)
def __unicode__(self):
return self.name
def get_absolute_url(self):
"""
Absolute URL, which goes to the dashboard workspace page.
:return string:
"""
return reverse('fobi.form_entry', kwargs={'slug': self.slug})
class FormFieldsetEntry(models.Model):
"""
Form fieldset entry.
"""
form_entry = models.ForeignKey(FormEntry, verbose_name=_("Form"), \
null=True, blank=True)
name = models.CharField(_("Name"), max_length=255)
is_repeatable = models.BooleanField(
_("Is repeatable?"), default=False,
help_text=_("Makes your form fieldset repeatable.")
)
class Meta:
verbose_name = _("Form fieldset entry")
verbose_name_plural = _("Form fieldset entries")
unique_together = (('form_entry', 'name'),)
def __unicode__(self):
return self.name
class AbstractPluginEntry(models.Model):
"""
Abstract plugin entry.
:Properties:
- `form_entry` (fobi.models.FormEntry): Form to which the field plugin
belongs to.
- `plugin_uid` (str): Plugin UID.
- `plugin_data` (str): JSON formatted string with plugin data.
"""
form_entry = models.ForeignKey(FormEntry, verbose_name=_("Form"))
plugin_data = models.TextField(verbose_name=_("Plugin data"), null=True, \
blank=True)
class Meta:
abstract = True
#def __init__(self, *args, **kwargs):
# """
# Add choices.
# """
# super(AbstractPluginEntry, self).__init__(*args, **kwargs)
# plugin_uid = self._meta.get_field('plugin_uid')
# plugin_uid._choices = self.get_registered_plugins()
def __unicode__(self):
return "{0} plugin for user {1}".format(
self.plugin_uid, self.form_entry.user
)
def get_registered_plugins(self):
"""
"""
raise NotImplemented("You should implement ``get_registered_plugins``"
" method!")
def get_registry(self):
"""
"""
raise NotImplemented("You should implement ``get_registry`` method!")
def get_plugin(self, fetch_related_data=False, request=None):
"""
Gets the plugin class (by ``plugin_uid`` property), makes an instance
of it, serves the data stored in ``plugin_data`` field (if available).
Once all is done, plugin is ready to be rendered.
:param bool fetch_related_data: When set to True, plugin is told to
re-fetch all related data (stored in models or other sources).
:return fobi.base.BasePlugin: Subclass of ``fobi.base.BasePlugin``.
"""
# Getting form element plugin from registry.
registry = self.get_registry()
cls = registry.get(self.plugin_uid)
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.
plugin = cls(user=self.form_entry.user)
# So that plugin has the request object
plugin.request = request
return plugin.process(
self.plugin_data, fetch_related_data=fetch_related_data
)
def plugin_uid_code(self):
"""
Mainly used in admin.
"""
return self.plugin_uid
plugin_uid_code.allow_tags = True
plugin_uid_code.short_description = _('UID')
def plugin_name(self):
return dict(self.get_registered_plugins()).get(self.plugin_uid, '')
class FormElementEntry(AbstractPluginEntry):
"""
Form field entry.
:Properties:
- `form` (fobi.models.FormEntry): Form to which the field plugin
belongs to.
- `plugin_uid` (str): Plugin UID.
- `plugin_data` (str): JSON formatted string with plugin data.
- `form_fieldset_entry`: Fieldset.
- `position` (int): Entry position.
"""
plugin_uid = models.CharField(_("Plugin name"), max_length=255, \
choices=get_registered_form_element_plugins())
form_fieldset_entry = models.ForeignKey(FormFieldsetEntry, \
verbose_name=_("Form fieldset"), \
null=True, blank=True)
position = models.PositiveIntegerField(_("Position"), null=True, blank=True)
class Meta:
abstract = False
verbose_name = _("Form element entry")
verbose_name_plural = _("Form element entries")
ordering = ['position',]
def get_registered_plugins(self):
"""
Gets registered plugins.
"""
return get_registered_form_element_plugins()
def get_registry(self):
return form_element_plugin_registry
class FormHandlerEntry(AbstractPluginEntry):
"""
Form handler entry.
:Properties:
- `form_entry` (fobi.models.FormEntry): Form to which the field plugin
belongs to.
- `plugin_uid` (str): Plugin UID.
- `plugin_data` (str): JSON formatted string with plugin data.
"""
plugin_uid = models.CharField(_("Plugin name"), max_length=255, \
choices=get_registered_form_handler_plugins())
class Meta:
abstract = False
verbose_name = _("Form handler entry")
verbose_name_plural = _("Form handler entries")
def get_registered_plugins(self):
"""
Gets registered plugins.
"""
return get_registered_form_handler_plugins()
def get_registry(self):
return form_handler_plugin_registry