Source code for fobi.models

from __future__ import absolute_import

import logging

from autoslug import AutoSlugField
from django.conf import settings
from django.contrib.auth.models import Group
from django.db import models
from django.urls import reverse
from django.utils import timezone
from django.utils.translation import gettext_lazy as _
from six import python_2_unicode_compatible

from .base import (
    form_element_plugin_registry,
    form_handler_plugin_registry,
    form_wizard_handler_plugin_registry,
    get_registered_form_element_plugins,
    get_registered_form_handler_plugins,
    get_registered_form_wizard_handler_plugins,
)
from .constants import DEFAULT_WIZARD_TYPE, WIZARD_TYPES

__title__ = "fobi.models"
__author__ = "Artur Barseghyan <artur.barseghyan@gmail.com>"
__copyright__ = "2014-2019 Artur Barseghyan"
__license__ = "GPL 2.0/LGPL 2.1"
__all__ = (
    # Plugins
    "AbstractPluginModel",
    "FormElement",
    "FormHandler",
    "FormWizardHandler",
    # Entries
    "AbstractFormWizardPluginEntry",
    "AbstractPluginEntry",
    "BaseAbstractPluginEntry",
    "FormElementEntry",
    "FormEntry",
    "FormFieldsetEntry",
    "FormHandlerEntry",
    "FormWizardEntry",
    "FormWizardFormEntry",
    "FormWizardHandlerEntry",
)


logger = logging.getLogger(__name__)

# ****************************************************************************
# **************** Safe User import for Django > 1.5, < 1.8 ******************
# ****************************************************************************
AUTH_USER_MODEL = settings.AUTH_USER_MODEL

# ****************************************************************************
# ****************************************************************************
# ****************************************************************************

# ****************************************************************************
# ****************************************************************************
# ******************************* Plugin models ******************************
# ****************************************************************************
# ****************************************************************************


[docs]@python_2_unicode_compatible 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( AUTH_USER_MODEL, verbose_name=_("User"), blank=True ) groups = models.ManyToManyField(Group, verbose_name=_("Group"), blank=True)
[docs] class Meta(object): """Meta class.""" 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()
[docs] def get_registered_plugins(self): """Get registered plugins.""" raise NotImplementedError( "You should implement ``get_registered_plugins`` method!" )
def __str__(self): return "{0} ({1})".format( dict(self.get_registered_plugins()).get(self.plugin_uid, ""), self.plugin_uid, )
[docs] def plugin_uid_code(self): """Plugin uid code. Mainly used in admin. """ return self.plugin_uid
plugin_uid_code.allow_tags = True plugin_uid_code.short_description = _("UID")
[docs] def plugin_uid_admin(self): """Plugin uid admin. Mainly used in admin. """ return self.__str__()
plugin_uid_admin.allow_tags = True plugin_uid_admin.short_description = _("Plugin")
[docs] def groups_list(self): """Groups list. 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")
[docs] def users_list(self): """Users list. Flat list (comma separated string) of users allowed to use the plugin. Used in Django admin. :return string: """ return ", ".join([u.get_username() for u in self.users.all()])
users_list.allow_tags = True users_list.short_description = _("Users")
[docs]class FormElement(AbstractPluginModel): """Form element. 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(object): """Meta class.""" abstract = False verbose_name = _("Form element plugin") verbose_name_plural = _("Form element plugins")
[docs] def get_registered_plugins(self): """Add choices.""" return get_registered_form_element_plugins()
[docs]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(object): """Class meta.""" abstract = False verbose_name = _("Form handler plugin") verbose_name_plural = _("Form handler plugins")
[docs] def get_registered_plugins(self): """Add choices.""" return get_registered_form_handler_plugins()
[docs]class FormWizardHandler(AbstractPluginModel): """ Form wizard 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(object): """Class meta.""" abstract = False verbose_name = _("Form wizard handler plugin") verbose_name_plural = _("Form wizard handler plugins")
[docs] def get_registered_plugins(self): """Add choices.""" return get_registered_form_wizard_handler_plugins()
# ***************************************************************************** # ***************************************************************************** # ******************************** Entry models ******************************* # ***************************************************************************** # *****************************************************************************
[docs]@python_2_unicode_compatible class FormWizardEntry(models.Model): """Form wizard entry.""" user = models.ForeignKey( AUTH_USER_MODEL, verbose_name=_("User"), on_delete=models.CASCADE ) name = models.CharField(_("Name"), max_length=255) title = models.CharField( _("Title"), max_length=255, null=True, blank=True, help_text=_("Shown in templates if available."), ) 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."), ) 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" ), ) show_all_navigation_buttons = models.BooleanField( _("Show all navigation buttons?"), default=False, help_text=_("Show all navigation buttons."), ) # action = models.CharField( # _("Action"), max_length=255, null=True, blank=True, # help_text=_("Custom form action; don't fill this field, unless " # "really necessary.") # ) wizard_type = models.CharField( _("Type"), max_length=255, null=False, blank=False, choices=WIZARD_TYPES, default=DEFAULT_WIZARD_TYPE, help_text=_("Type of the form wizard."), ) created = models.DateTimeField( _("Created"), null=True, blank=True, auto_now_add=True ) updated = models.DateTimeField( _("Updated"), null=True, blank=True, auto_now=True ) class Meta(object): """Meta class.""" verbose_name = _("Form wizard entry") verbose_name_plural = _("Form wizard entries") unique_together = ( ("user", "slug"), ("user", "name"), ) def __str__(self): return self.name
[docs] def get_absolute_url(self): """Get absolute URL. Absolute URL, which goes to the form-wizard view view. :return string: """ return reverse( "fobi.view_form_wizard_entry", kwargs={"form_wizard_entry_slug": self.slug}, )
[docs]@python_2_unicode_compatible class FormEntry(models.Model): """Form entry.""" user = models.ForeignKey( AUTH_USER_MODEL, verbose_name=_("User"), on_delete=models.CASCADE ) name = models.CharField(_("Name"), max_length=255) title = models.CharField( _("Title"), max_length=255, null=True, blank=True, help_text=_("Shown in templates if available."), ) 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."), ) active_date_from = models.DateTimeField( _("Active from"), null=True, blank=True, help_text=_( "Date and time when the form becomes active " "in the format: 'YYYY-MM-DD HH:MM'. " "Leave it blank to activate immediately." ), ) active_date_to = models.DateTimeField( _("Active until"), null=True, blank=True, help_text=_( "Date and time when the form becomes inactive " "in the format: 'YYYY-MM-DD HH:MM'. " "Leave it blank to keep active forever." ), ) inactive_page_title = models.CharField( _("Inactive form page title"), max_length=255, null=True, blank=True, help_text=_("Custom message title to display if form is inactive."), ) inactive_page_message = models.TextField( _("Inactive form page body"), null=True, blank=True, help_text=_("Custom message text to display if form is inactive."), ) 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; don't fill this field, unless really " "necessary." ), ) created = models.DateTimeField( _("Created"), null=True, blank=True, auto_now_add=True ) updated = models.DateTimeField( _("Updated"), null=True, blank=True, auto_now=True ) class Meta(object): """Meta class.""" verbose_name = _("Form entry") verbose_name_plural = _("Form entries") unique_together = ( ("user", "slug"), ("user", "name"), ) def __str__(self): return self.name @property def is_active(self): active_from_ok = True active_to_ok = True now = timezone.now() if self.active_date_from and now < self.active_date_from: active_from_ok = False if self.active_date_to and now > self.active_date_to: active_to_ok = False if active_from_ok and active_to_ok: return True else: return False
[docs] def get_absolute_url(self): """Get absolute URL. Absolute URL, which goes to the form-entry view view page. :return string: """ return reverse( "fobi.view_form_entry", kwargs={"form_entry_slug": self.slug} )
[docs]@python_2_unicode_compatible class FormWizardFormEntry(models.Model): """Form wizard form entry. A coupling point between `FormWizardEntry` and `FormEntry`.""" form_wizard_entry = models.ForeignKey( FormWizardEntry, verbose_name=_("Form wizard entry"), null=False, blank=False, on_delete=models.CASCADE, ) form_entry = models.ForeignKey( FormEntry, verbose_name=_("Form entry"), null=False, blank=False, on_delete=models.CASCADE, ) position = models.PositiveIntegerField(_("Position"), null=True, blank=True) class Meta(object): """Meta class.""" abstract = False verbose_name = _("Form wizard form entry") verbose_name_plural = _("Form wizard form entries") ordering = ["position"] unique_together = (("form_wizard_entry", "form_entry"),) def __str__(self): return "{0} - {1}".format(self.form_wizard_entry, self.form_entry)
[docs]@python_2_unicode_compatible class FormFieldsetEntry(models.Model): """Form fieldset entry.""" form_entry = models.ForeignKey( FormEntry, verbose_name=_("Form"), null=True, blank=True, on_delete=models.CASCADE, ) name = models.CharField(_("Name"), max_length=255) is_repeatable = models.BooleanField( _("Is repeatable?"), default=False, help_text=_("Makes your form fieldset repeatable."), ) class Meta(object): """Meta class.""" verbose_name = _("Form fieldset entry") verbose_name_plural = _("Form fieldset entries") unique_together = (("form_entry", "name"),) def __str__(self): return self.name
[docs]@python_2_unicode_compatible class BaseAbstractPluginEntry(models.Model): """Base for AbstractPluginEntry. :Properties: - `plugin_data` (str): JSON formatted string with plugin data. """ plugin_data = models.TextField( verbose_name=_("Plugin data"), null=True, blank=True )
[docs] class Meta(object): """Meta class.""" abstract = True
def __str__(self): return "{0} plugin for user {1}".format( self.plugin_uid, self.entry_user ) @property def entry_user(self): """Get user from the parent container.""" raise NotImplementedError( "You should implement ``entry_user`` property!" )
[docs] def get_registered_plugins(self): """Get registered plugins.""" raise NotImplementedError( "You should implement ``get_registered_plugins`` method!" )
[docs] def get_registry(self): """Get registry.""" raise NotImplementedError( "You should implement ``get_registry`` method!" )
[docs] def plugin_uid_code(self): """Plugin uid code. Mainly used in admin. """ return self.plugin_uid
plugin_uid_code.allow_tags = True plugin_uid_code.short_description = _("UID")
[docs] def plugin_name(self): """Plugin name.""" return dict(self.get_registered_plugins()).get(self.plugin_uid, "")
[docs] def get_plugin(self, fetch_related_data=False, request=None): """Get plugin. 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.entry_user) # So that plugin has the request object plugin.request = request return plugin.process( self.plugin_data, fetch_related_data=fetch_related_data )
[docs]class AbstractPluginEntry(BaseAbstractPluginEntry): """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"), on_delete=models.CASCADE )
[docs] class Meta(object): """Meta class.""" 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() @property def entry_user(self): """Get user.""" return self.form_entry.user
[docs]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, on_delete=models.CASCADE, ) position = models.PositiveIntegerField(_("Position"), null=True, blank=True) class Meta(object): """Meta class.""" abstract = False verbose_name = _("Form element entry") verbose_name_plural = _("Form element entries") ordering = ["position"]
[docs] def get_registered_plugins(self): """Gets registered plugins.""" return get_registered_form_element_plugins()
[docs] def get_registry(self): """Get registry.""" return form_element_plugin_registry
[docs]class FormHandlerEntry(AbstractPluginEntry): """Form handler entry. :Properties: - `form_entry` (fobi.models.FormEntry): Form to which the handler 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(object): """Meta class.""" abstract = False verbose_name = _("Form handler entry") verbose_name_plural = _("Form handler entries")
[docs] def get_registered_plugins(self): """Gets registered plugins.""" return get_registered_form_handler_plugins()
[docs] def get_registry(self): """Get registry.""" return form_handler_plugin_registry
[docs]class AbstractFormWizardPluginEntry(BaseAbstractPluginEntry): """Abstract form wizard plugin entry. :Properties: - `form_entry` (fobi.models.FormWizardEntry): FormWizard to which the plugin belongs to. - `plugin_uid` (str): Plugin UID. - `plugin_data` (str): JSON formatted string with plugin data. """ form_wizard_entry = models.ForeignKey( FormWizardEntry, verbose_name=_("Form wizard"), on_delete=models.CASCADE )
[docs] class Meta(object): """Meta class.""" abstract = True
@property def entry_user(self): """Get user.""" return self.form_wizard_entry.user
[docs]class FormWizardHandlerEntry(AbstractFormWizardPluginEntry): """Form wizard handler entry. :Properties: - `form_wizard_entry` (fobi.models.FormWizardEntry): FormWizard to which the handler 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(object): """Meta class.""" abstract = False verbose_name = _("Form wizard handler entry") verbose_name_plural = _("Form wizard handler entries")
[docs] def get_registered_plugins(self): """Gets registered plugins.""" return get_registered_form_wizard_handler_plugins()
[docs] def get_registry(self): """Get registry.""" return form_wizard_handler_plugin_registry