django-fobi/src/fobi/views/class_based.py
2022-07-10 01:32:58 +02:00

1387 lines
46 KiB
Python

import datetime
import json
import logging
from django.contrib import messages
from django.core.exceptions import PermissionDenied, ObjectDoesNotExist
from django.db import IntegrityError, models
from django.shortcuts import redirect, get_object_or_404
from django.http import Http404
from django.urls import reverse_lazy
from django.utils.datastructures import MultiValueDictKeyError
from django.utils.translation import gettext_lazy as _
from django.views import View
from django.views.generic import CreateView, UpdateView, DeleteView
from django.views.generic.edit import DeletionMixin
from ..base import (
fire_form_callbacks,
run_form_handlers,
run_form_wizard_handlers,
form_element_plugin_registry,
form_handler_plugin_registry,
form_wizard_handler_plugin_registry,
submit_plugin_form_data,
get_theme,
# get_registered_form_handler_plugins
)
from ..dynamic import assemble_form_class
from ..forms import (
FormElementEntryFormSet,
FormEntryForm,
FormWizardEntryForm,
FormWizardFormEntryFormSet,
ImportFormEntryForm,
ImportFormWizardEntryForm,
)
from ..models import (
FormElementEntry,
FormEntry,
FormHandlerEntry,
FormWizardEntry,
FormWizardFormEntry,
FormWizardHandlerEntry,
)
from ..permissions.default import (
CreateFormEntryPermission,
EditFormEntryPermission,
DeleteFormEntryPermission,
AddFormElementEntryPermission,
EditFormElementEntryPermission,
DeleteFormElementEntryPermission,
AddFormHandlerEntryPermission,
EditFormHandlerEntryPermission,
DeleteFormHandlerEntryPermission,
)
from ..settings import DEBUG, GET_PARAM_INITIAL_DATA, SORT_PLUGINS_BY_VALUE
from ..utils import (
append_edit_and_delete_links_to_field,
get_user_form_element_plugins_grouped,
get_user_form_field_plugin_uids,
get_user_form_handler_plugin_uids,
get_user_form_handler_plugins,
get_user_form_wizard_handler_plugin_uids,
get_user_form_wizard_handler_plugins,
get_wizard_files_upload_dir,
perform_form_entry_import,
prepare_form_entry_export_data,
)
__all__ = (
"PermissionMixin",
"AbstractDeletePluginEntryView",
"CreateFormEntryView",
"EditFormEntryView",
"DeleteFormEntryView",
"AddFormElementEntryView",
"EditFormElementEntryView",
"DeleteFormElementEntryView",
"AddFormHandlerEntryView",
"EditFormHandlerEntryView",
"DeleteFormHandlerEntryView",
)
logger = logging.getLogger(__name__)
# *****************************************************************************
# *****************************************************************************
# *********************************** Generic *********************************
# *****************************************************************************
# *****************************************************************************
class PermissionMixin(View):
"""Mixin for permission-based views."""
permission_classes: tuple = ()
def permission_denied(self, request, message=None, code=None):
"""If request is not permitted, raise."""
raise PermissionDenied()
def get_permissions(self):
"""Return initialized list of permissions required by this view."""
return [permission() for permission in self.permission_classes]
def dispatch(self, request, *args, **kwargs):
"""Dispatch the request."""
self.check_permissions(request)
return super(PermissionMixin, self).dispatch(request, *args, **kwargs)
def check_permissions(self, request):
"""Check if the request should be permitted.
Raises an appropriate exception if the request is not permitted.
"""
for permission in self.get_permissions():
if not permission.has_permission(request, self):
self.permission_denied(
request,
message=getattr(permission, 'message', None),
code=getattr(permission, 'code', None)
)
class AbstractDeletePluginEntryView(PermissionMixin, DeleteView):
"""Abstract delete view for plugin entries."""
pk_url_kwarg: str
get_user_plugin_uids_func: callable
message: str
html_anchor: str
def _get_queryset(self, request):
"""Get queryset."""
return self.model._default_manager \
.select_related('form_entry') \
.filter(form_entry__user__pk=request.user.pk)
def get_object(self, queryset=None):
"""Get object."""
# TODO: There's a tiny deviation from `_delete_plugin_entry`
# implementation. The message in the latter is fully custom, while
# in this case we're stuck to Django's own implementation.
# Comment added on 2022-07-10.
return get_object_or_404(
self._get_queryset(self.request),
pk=self.kwargs.get(self.pk_url_kwarg),
)
# Add support for browsers which only accept GET and POST for now.
def get(self, request, *args, **kwargs):
return self.delete(request, *args, **kwargs)
def delete(self, request, *args, **kwargs):
"""Delete."""
self.object = self.get_object()
self._run_before_plugin_entry_delete(request, self.object)
form_entry = self.object.form_entry
plugin = self.object.get_plugin(request=request)
plugin.request = request
plugin._delete_plugin_data()
self.object.delete()
self._run_after_plugin_entry_delete(
request, self.kwargs.get("form_entry_id")
)
messages.info(request, self.message.format(plugin.name))
redirect_url = reverse_lazy(
'fobi.edit_form_entry', kwargs={'form_entry_id': form_entry.pk}
)
return redirect("{0}{1}".format(redirect_url, self.html_anchor))
def _run_before_plugin_entry_delete(self, request, form_entry):
"""Run just before plugin entry has been deleted."""
try:
self.run_before_plugin_entry_delete(request, form_entry)
return True
except:
return False
def run_before_plugin_entry_delete(self, request, form_entry):
"""Run just before plugin entry has been deleted."""
def _run_after_plugin_entry_delete(self, request, form_entry_id):
"""Run after plugin entry has been deleted."""
try:
self.run_after_plugin_entry_delete(request, form_entry_id)
return True
except:
return False
def run_after_plugin_entry_delete(self, request, form_entry_id):
"""Run after plugin entry has been deleted."""
# *****************************************************************************
# *****************************************************************************
# ********************************** Builder **********************************
# *****************************************************************************
# *****************************************************************************
# *****************************************************************************
# **************************** Create form entry ******************************
# *****************************************************************************
class CreateFormEntryView(PermissionMixin, CreateView):
"""Create form entry."""
template_name = None
form_class = FormEntryForm
theme = None
permission_classes = (CreateFormEntryPermission,)
def get_context_data(self, **kwargs):
"""Get context data."""
context = super(CreateFormEntryView, self).get_context_data(**kwargs)
context["form"] = self.get_form()
if not self.theme:
theme = get_theme(request=self.request, as_instance=True)
else:
theme = self.theme
if theme:
context.update({"fobi_theme": theme})
return context
def get_template_names(self):
"""Get template names."""
template_name = self.template_name
if not template_name:
if not self.theme:
theme = get_theme(request=self.request, as_instance=True)
else:
theme = self.theme
template_name = theme.create_form_entry_template
return [template_name]
def get_form_kwargs(self):
kwargs = super(CreateFormEntryView, self).get_form_kwargs()
kwargs["request"] = self.request
return kwargs
def get(self, request, *args, **kwargs):
"""Handle GET requests: instantiate a blank version of the form."""
self.object = None
return self.render_to_response(self.get_context_data())
def post(self, request, *args, **kwargs):
"""
Handle POST requests: instantiate a form instance with the passed
POST variables and then check if it's valid.
"""
self.object = None
form = self.get_form()
if form.is_valid():
form_entry = form.save(commit=False)
form_entry.user = request.user
self._run_before_form_create(request, form_entry)
try:
form_entry.save()
self._run_after_form_create(request, form_entry)
messages.info(
request,
_("Form {0} was created successfully.").format(
form_entry.name
),
)
return redirect(
"fobi.edit_form_entry", form_entry_id=form_entry.pk
)
except IntegrityError as err:
messages.info(
request,
_("Errors occurred while saving the form: {0}.").format(
str(err)
),
)
return self.render_to_response(self.get_context_data())
def _run_before_form_create(self, request, form_entry):
"""Run just before form_entry has been created/saved."""
try:
self.run_before_form_create(request, form_entry)
return True
except:
return False
def run_before_form_create(self, request, form_entry):
"""Run just before form_entry has been created/saved."""
def _run_after_form_create(self, request, form_entry):
"""Run after form_entry has been created/saved."""
try:
self.run_after_form_create(request, form_entry)
return True
except:
return False
def run_after_form_create(self, request, form_entry):
"""Run after the form_entry has been created/saved."""
# **************************************************************************
# ******************************* Edit form entry **************************
# **************************************************************************
class EditFormEntryView(PermissionMixin, UpdateView):
"""Edit form entry."""
template_name = None
form_class = FormEntryForm
theme = None
pk_url_kwarg = "form_entry_id"
permission_classes = (EditFormEntryPermission,)
def get_context_data(self, **kwargs):
"""Get context data."""
context = super(EditFormEntryView, self).get_context_data(**kwargs)
# In case of success, we don't need this (since redirect would happen).
# Thus, fetch only if needed.
form_elements = self.object.formelemententry_set.all()
form_handlers = self.object.formhandlerentry_set.all()[:]
used_form_handler_uids = [
form_handler.plugin_uid for form_handler in form_handlers
]
# The code below (two lines below) is not really used at the moment,
# thus - comment out, but do not remove, as we might need it later on.
# all_form_entries = FormEntry._default_manager \
# .only('id', 'name', 'slug') \
# .filter(user__pk=request.user.pk)
# List of form element plugins allowed to user
user_form_element_plugins = get_user_form_element_plugins_grouped(
self.request.user, sort_by_value=SORT_PLUGINS_BY_VALUE
)
# List of form handler plugins allowed to user
user_form_handler_plugins = get_user_form_handler_plugins(
self.request.user,
exclude_used_singles=True,
used_form_handler_plugin_uids=used_form_handler_uids,
)
# Assembling the form for preview
form_cls = assemble_form_class(
self.object,
origin="edit_form_entry",
origin_kwargs_update_func=append_edit_and_delete_links_to_field,
request=self.request,
)
assembled_form = form_cls()
# In debug mode, try to identify possible problems.
if DEBUG:
assembled_form.as_p()
else:
try:
assembled_form.as_p()
except Exception as err:
logger.error(err)
# If no theme provided, pick a default one.
if not self.theme:
theme = get_theme(request=self.request, as_instance=True)
else:
theme = self.theme
if theme:
context.update({"fobi_theme": theme})
theme.collect_plugin_media(form_elements)
context.update(
{
"form": self.get_form(),
"form_entry": self.object,
"form_elements": form_elements,
"form_handlers": form_handlers,
"user_form_element_plugins": user_form_element_plugins,
"user_form_handler_plugins": user_form_handler_plugins,
"assembled_form": assembled_form,
"fobi_theme": theme,
}
)
return context
def get_template_names(self):
"""Get template names."""
template_name = self.template_name
if not template_name:
if not self.theme:
theme = get_theme(request=self.request, as_instance=True)
else:
theme = self.theme
template_name = theme.edit_form_entry_template
return [template_name]
def get_form_kwargs(self):
kwargs = super(EditFormEntryView, self).get_form_kwargs()
kwargs["request"] = self.request
return kwargs
def _get_queryset(self, request):
"""Get queryset."""
return FormEntry._default_manager \
.select_related('user') \
.prefetch_related('formelemententry_set') \
.filter(user__pk=request.user.pk)
def get(self, request, *args, **kwargs):
self.object = self.get_object(queryset=self._get_queryset(request))
"""Handle GET requests: instantiate a blank version of the form."""
form_element_entry_formset = FormElementEntryFormSet(
queryset=self.object.formelemententry_set.all(),
# prefix='form_element'
)
return self.render_to_response(
self.get_context_data(
form_element_entry_formset=form_element_entry_formset,
)
)
def post(self, request, *args, **kwargs):
"""
Handle POST requests: instantiate a form instance with the passed
POST variables and then check if it's valid.
"""
self.object = self.get_object(queryset=self._get_queryset(request))
form = self.get_form()
# This is where we save ordering if it has been changed.
# The `FormElementEntryFormSet` contain ids and positions only.
if "ordering" in request.POST:
form_element_entry_formset = FormElementEntryFormSet(
request.POST,
request.FILES,
queryset=self.object.formelemententry_set.all(),
# prefix = 'form_element'
)
# If form elements aren't properly made (developers's fault)
# there might be problems with saving the ordering - likely
# in case of hidden elements only. Thus, we want to avoid
# errors here.
try:
if form_element_entry_formset.is_valid():
form_element_entry_formset.save()
messages.info(
request, _("Elements ordering edited successfully.")
)
return redirect(
reverse_lazy(
"fobi.edit_form_entry",
kwargs={"form_entry_id": self.object.pk},
)
)
except MultiValueDictKeyError as err:
messages.error(
request,
_(
"Errors occurred while trying to change the "
"elements ordering!"
),
)
return redirect(
reverse_lazy(
"fobi.edit_form_entry",
kwargs={"form_entry_id": self.object.pk},
)
)
else:
form_element_entry_formset = FormElementEntryFormSet(
queryset=self.object.formelemententry_set.all(),
# prefix='form_element'
)
if form.is_valid():
obj = form.save(commit=False)
obj.user = request.user
try:
obj.save()
messages.info(
request,
_("Form {0} was edited successfully.").format(obj.name),
)
return redirect(
reverse_lazy(
"fobi.edit_form_entry", kwargs={"form_entry_id": obj.pk}
)
)
except IntegrityError as err:
messages.info(
request,
_("Errors occurred while saving the form: {0}.").format(
str(err)
),
)
return self.render_to_response(
self.get_context_data(
form_element_entry_formset=form_element_entry_formset,
)
)
# *****************************************************************************
# ********************************* Delete form entry *************************
# *****************************************************************************
class DeleteFormEntryView(PermissionMixin, DeletionMixin):
"""Delete form entry."""
model = FormEntry
success_url = reverse_lazy("fobi.dashboard")
permission_classes = (DeleteFormEntryPermission,)
def get_object(self, queryset=None):
"""Get object."""
return get_object_or_404(
FormEntry._default_manager.all(),
pk=self.kwargs.get("form_entry_id"),
user__pk=self.request.user.pk
)
# Add support for browsers which only accept GET and POST for now.
def get(self, request, *args, **kwargs):
return self.delete(request, *args, **kwargs)
def delete(self, request, *args, **kwargs):
"""Delete."""
self.object = self.get_object()
success_url = self.get_success_url()
self._run_before_form_delete(request, self.object)
self.object.delete()
self._run_after_form_delete(request, self.kwargs.get("form_entry_id"))
messages.info(
request,
_("Form {0} was deleted successfully.").format(self.object.name),
)
return redirect(success_url)
def _run_before_form_delete(self, request, form_entry):
"""Run just before form_entry has been deleted."""
try:
self.run_before_form_delete(request, form_entry)
return True
except:
return False
def run_before_form_delete(self, request, form_entry):
"""Run just before form_entry has been deleted."""
def _run_after_form_delete(self, request, form_entry_id):
"""Run after form_entry has been deleted."""
try:
self.run_after_form_delete(request, form_entry_id)
return True
except:
return False
def run_after_form_delete(self, request, form_entry_id):
"""Run after form_entry has been deleted."""
# *****************************************************************************
# **************************** Add form element entry *************************
# *****************************************************************************
class AddFormElementEntryView(PermissionMixin, CreateView):
"""Add form element entry."""
template_name = None
form_class = None
theme = None
permission_classes = (AddFormElementEntryPermission,)
def get_essential_objects(
self,
form_entry_id,
form_element_plugin_uid,
request,
):
"""Get essential objects."""
try:
form_entry = FormEntry._default_manager \
.prefetch_related('formelemententry_set') \
.get(pk=form_entry_id)
except ObjectDoesNotExist as err:
raise Http404(_("Form entry not found."))
form_elements = form_entry.formelemententry_set.all()
user_form_element_plugin_uids = get_user_form_field_plugin_uids(
request.user
)
if form_element_plugin_uid not in user_form_element_plugin_uids:
raise Http404(
_("Plugin does not exist or you are not allowed "
"to use this plugin!"))
form_element_plugin_cls = form_element_plugin_registry.get(
form_element_plugin_uid
)
form_element_plugin = form_element_plugin_cls(user=request.user)
form_element_plugin.request = request
form_element_plugin_form_cls = form_element_plugin.get_form()
# form = None
obj = FormElementEntry()
obj.form_entry = form_entry
obj.plugin_uid = form_element_plugin_uid
obj.user = request.user
return (
form_entry,
form_elements,
form_element_plugin_cls,
form_element_plugin,
form_element_plugin_form_cls,
user_form_element_plugin_uids,
obj,
)
def do_save_object(
self,
form_entry_id,
form_entry,
obj,
form_element_plugin,
request
):
"""Do save object."""
# Handling the position
position = 1
records = FormElementEntry.objects.filter(form_entry=form_entry) \
.aggregate(models.Max('position'))
if records:
try:
position = records['{0}__max'.format('position')] + 1
except TypeError as err:
pass
obj.position = position
# Save the object.
obj.save()
messages.info(
request,
_('The form element plugin "{0}" was added '
'successfully.').format(form_element_plugin.name)
)
return redirect(
"{0}?active_tab=tab-form-elements".format(
reverse_lazy(
'fobi.edit_form_entry',
kwargs={'form_entry_id': form_entry_id}
)
)
)
def get_context_data(self, **kwargs):
"""Get context data."""
context = super(AddFormElementEntryView, self).get_context_data(**kwargs)
if not self.theme:
theme = get_theme(request=self.request, as_instance=True)
else:
theme = self.theme
if theme:
context.update({"fobi_theme": theme})
return context
def get_template_names(self):
"""Get template names."""
template_name = self.template_name
if not template_name:
if not self.theme:
theme = get_theme(request=self.request, as_instance=True)
else:
theme = self.theme
template_name = theme.add_form_element_entry_template
return [template_name]
def get(self, request, *args, **kwargs):
"""Handle GET requests: instantiate a blank version of the form."""
self.object = None
(
form_entry,
form_elements,
form_element_plugin_cls,
form_element_plugin,
form_element_plugin_form_cls,
user_form_element_plugin_uids,
obj,
) = self.get_essential_objects(
self.kwargs.get("form_entry_id"),
self.kwargs.get("form_element_plugin_uid"),
self.request,
)
save_object = False
if not form_element_plugin_form_cls:
save_object = True
if not save_object:
form = form_element_plugin.get_initialised_create_form_or_404()
if save_object:
return self.do_save_object(
self.kwargs.get("form_entry_id"),
form_entry,
obj,
form_element_plugin,
request
)
return self.render_to_response(
self.get_context_data(
form=form,
form_entry=form_entry,
form_element_plugin=form_element_plugin,
)
)
def post(self, request, *args, **kwargs):
"""
Handle POST requests: instantiate a form instance with the passed
POST variables and then check if it's valid.
"""
self.object = None
(
form_entry,
form_elements,
form_element_plugin_cls,
form_element_plugin,
form_element_plugin_form_cls,
user_form_element_plugin_uids,
obj,
) = self.get_essential_objects(
self.kwargs.get("form_entry_id"),
self.kwargs.get("form_element_plugin_uid"),
self.request,
)
save_object = False
if not form_element_plugin_form_cls:
save_object = True
if not save_object:
form = form_element_plugin.get_initialised_create_form_or_404(
data=request.POST,
files=request.FILES
)
form.validate_plugin_data(form_elements, request=request)
if form.is_valid():
# Saving the plugin form data.
form.save_plugin_data(request=request)
# Getting the plugin data.
obj.plugin_data = form.get_plugin_data(request=request)
save_object = True
if save_object:
return self.do_save_object(
self.kwargs.get("form_entry_id"),
form_entry,
obj,
form_element_plugin,
request
)
return self.render_to_response(
self.get_context_data(
form=form,
form_entry=form_entry,
form_element_plugin=form_element_plugin,
)
)
# *****************************************************************************
# **************************** Edit form element entry ************************
# *****************************************************************************
class EditFormElementEntryView(PermissionMixin, UpdateView):
"""Edit form element entry view."""
template_name = None
form_class = None
theme = None
pk_url_kwarg = "form_element_entry_id"
permission_classes = (EditFormElementEntryPermission,)
def _get_queryset(self, request):
"""Get queryset."""
return FormElementEntry._default_manager \
.select_related('form_entry', 'form_entry__user') \
.filter(form_entry__user__pk=request.user.pk)
def get_essential_objects(
self,
form_element_entry_id,
request,
):
"""Get essential objects."""
try:
obj = self.get_object(queryset=self._get_queryset(request))
except ObjectDoesNotExist as err:
raise Http404(_("Form element entry not found."))
form_entry = obj.form_entry
form_element_plugin = obj.get_plugin(request=request)
form_element_plugin.request = request
form_element_plugin_form_cls = form_element_plugin.get_form()
return (
form_entry,
form_element_entry_id,
form_element_plugin,
form_element_plugin_form_cls,
obj,
)
def get_context_data(self, **kwargs):
"""Get context data."""
context = super(EditFormElementEntryView, self).get_context_data(**kwargs)
if not self.theme:
theme = get_theme(request=self.request, as_instance=True)
else:
theme = self.theme
if theme:
context.update({"fobi_theme": theme})
return context
def get_template_names(self):
"""Get template names."""
template_name = self.template_name
if not template_name:
if not self.theme:
theme = get_theme(request=self.request, as_instance=True)
else:
theme = self.theme
template_name = theme.edit_form_element_entry_template
return [template_name]
def get(self, request, *args, **kwargs):
"""Handle GET requests: instantiate a blank version of the form."""
(
form_entry,
form_element_entry_id,
form_element_plugin,
form_element_plugin_form_cls,
obj,
) = self.get_essential_objects(
self.kwargs.get("form_element_entry_id"),
self.request,
)
self.object = obj
form = None
if not form_element_plugin_form_cls:
messages.info(
request,
_('The form element plugin "{0}" '
'is not configurable!').format(form_element_plugin.name)
)
return redirect('fobi.edit_form_entry', form_entry_id=form_entry.pk)
else:
form = form_element_plugin.get_initialised_edit_form_or_404()
return self.render_to_response(
self.get_context_data(
form=form,
form_entry=form_entry,
form_element_plugin=form_element_plugin,
)
)
def post(self, request, *args, **kwargs):
"""
Handle POST requests: instantiate a form instance with the passed
POST variables and then check if it's valid.
"""
self.object = None
(
form_entry,
form_element_entry_id,
form_element_plugin,
form_element_plugin_form_cls,
obj,
) = self.get_essential_objects(
self.kwargs.get("form_element_entry_id"),
self.request,
)
self.object = obj
if not form_element_plugin_form_cls:
messages.info(
request,
_('The form element plugin "{0}" '
'is not configurable!').format(form_element_plugin.name)
)
return redirect('fobi.edit_form_entry', form_entry_id=form_entry.pk)
form = form_element_plugin.get_initialised_edit_form_or_404(
data=request.POST,
files=request.FILES
)
form_elements = FormElementEntry._default_manager \
.select_related('form_entry',
'form_entry__user') \
.exclude(pk=form_element_entry_id) \
.filter(form_entry=form_entry)
form.validate_plugin_data(form_elements, request=request)
if form.is_valid():
# Saving the plugin form data.
form.save_plugin_data(request=request)
# Getting the plugin data.
obj.plugin_data = form.get_plugin_data(request=request)
# Save the object.
obj.save()
messages.info(
request,
_('The form element plugin "{0}" was edited '
'successfully.').format(form_element_plugin.name)
)
return redirect('fobi.edit_form_entry',
form_entry_id=form_entry.pk)
return self.render_to_response(
self.get_context_data(
form=form,
form_entry=form_entry,
form_element_plugin=form_element_plugin,
)
)
# *****************************************************************************
# **************************** Delete form element entry **********************
# *****************************************************************************
class DeleteFormElementEntryView(AbstractDeletePluginEntryView):
"""Delete form element entry."""
model = FormElementEntry
permission_classes = (DeleteFormElementEntryPermission,)
pk_url_kwarg = "form_element_entry_id"
get_user_plugin_uids_func = get_user_form_field_plugin_uids
message = _('The form element plugin "{0}" was deleted successfully.')
html_anchor = '?active_tab=tab-form-elements'
# *****************************************************************************
# **************************** Add form handler entry *************************
# *****************************************************************************
class AddFormHandlerEntryView(PermissionMixin, CreateView):
"""Add form handler entry."""
template_name = None
form_class = None
theme = None
permission_classes = (AddFormHandlerEntryPermission,)
def get_essential_objects(
self,
form_entry_id,
form_handler_plugin_uid,
request,
):
"""Get essential objects."""
try:
form_entry = FormEntry._default_manager \
.get(pk=form_entry_id)
except ObjectDoesNotExist as err:
raise Http404(_("Form entry not found."))
# TODO: Form handlers don't have this, while form elements do.
# Find out whether including this improves performance.
# Comment added on 2022-07-10.
# form_elements = form_entry.formelemententry_set.all()
user_form_handler_plugin_uids = get_user_form_handler_plugin_uids(
request.user
)
if form_handler_plugin_uid not in user_form_handler_plugin_uids:
raise Http404(
_("Plugin does not exist or you are not allowed "
"to use this plugin!")
)
form_handler_plugin_cls = form_handler_plugin_registry.get(
form_handler_plugin_uid
)
# Check if we deal with form handler plugin that is only allowed to be
# used once. In that case, check if it has been used already in the
# current form entry.
if not form_handler_plugin_cls.allow_multiple:
times_used = FormHandlerEntry._default_manager \
.filter(form_entry__id=form_entry_id,
plugin_uid=form_handler_plugin_cls.uid) \
.count()
if times_used > 0:
raise Http404(
_("The {0} plugin can be used only once in a "
"form.").format(form_handler_plugin_cls.name)
)
form_handler_plugin = form_handler_plugin_cls(user=request.user)
form_handler_plugin.request = request
form_handler_plugin_form_cls = form_handler_plugin.get_form()
obj = FormHandlerEntry()
obj.form_entry = form_entry
obj.plugin_uid = form_handler_plugin_uid
obj.user = request.user
return (
form_entry,
# form_elements,
form_handler_plugin_cls,
form_handler_plugin,
form_handler_plugin_form_cls,
user_form_handler_plugin_uids,
obj,
)
def do_save_object(
self,
form_entry_id,
form_entry,
obj,
form_handler_plugin,
request
):
"""Do save object."""
# Save the object.
obj.save()
messages.info(
request,
_('The form handler plugin "{0}" was added '
'successfully.').format(form_handler_plugin.name)
)
return redirect(
"{0}?active_tab=tab-form-handlers".format(
reverse_lazy(
'fobi.edit_form_entry',
kwargs={'form_entry_id': form_entry_id}
)
)
)
def get_context_data(self, **kwargs):
"""Get context data."""
context = super(AddFormHandlerEntryView, self).get_context_data(**kwargs)
if not self.theme:
theme = get_theme(request=self.request, as_instance=True)
else:
theme = self.theme
if theme:
context.update({"fobi_theme": theme})
return context
def get_template_names(self):
"""Get template names."""
template_name = self.template_name
if not template_name:
if not self.theme:
theme = get_theme(request=self.request, as_instance=True)
else:
theme = self.theme
template_name = theme.add_form_handler_entry_template
return [template_name]
def get(self, request, *args, **kwargs):
"""Handle GET requests: instantiate a blank version of the form."""
self.object = None
(
form_entry,
# form_elements,
form_handler_plugin_cls,
form_handler_plugin,
form_handler_plugin_form_cls,
user_form_handler_plugin_uids,
obj,
) = self.get_essential_objects(
self.kwargs.get("form_entry_id"),
self.kwargs.get("form_handler_plugin_uid"),
self.request,
)
save_object = False
if not form_handler_plugin_form_cls:
save_object = True
if not save_object:
form = form_handler_plugin.get_initialised_create_form_or_404()
if save_object:
return self.do_save_object(
self.kwargs.get("form_entry_id"),
form_entry,
obj,
form_handler_plugin,
request
)
return self.render_to_response(
self.get_context_data(
form=form,
form_entry=form_entry,
form_handler_plugin=form_handler_plugin,
)
)
def post(self, request, *args, **kwargs):
"""
Handle POST requests: instantiate a form instance with the passed
POST variables and then check if it's valid.
"""
self.object = None
(
form_entry,
# form_elements,
form_handler_plugin_cls,
form_handler_plugin,
form_handler_plugin_form_cls,
user_form_handler_plugin_uids,
obj,
) = self.get_essential_objects(
self.kwargs.get("form_entry_id"),
self.kwargs.get("form_handler_plugin_uid"),
self.request,
)
save_object = False
if not form_handler_plugin_form_cls:
save_object = True
if not save_object:
form = form_handler_plugin.get_initialised_create_form_or_404(
data=request.POST,
files=request.FILES
)
# TODO: Form handlers don't have this, while form elements do.
# Find out whether this is something that could be correct
# for form handlers.
# form.validate_plugin_data(form_elements, request=request)
if form.is_valid():
# Saving the plugin form data.
form.save_plugin_data(request=request)
# Getting the plugin data.
obj.plugin_data = form.get_plugin_data(request=request)
save_object = True
if save_object:
return self.do_save_object(
self.kwargs.get("form_entry_id"),
form_entry,
obj,
form_handler_plugin,
request
)
return self.render_to_response(
self.get_context_data(
form=form,
form_entry=form_entry,
form_handler_plugin=form_handler_plugin,
)
)
# *****************************************************************************
# **************************** Edit form handler entry ************************
# *****************************************************************************
class EditFormHandlerEntryView(PermissionMixin, UpdateView):
"""Edit form handler entry view."""
template_name = None
form_class = None
theme = None
pk_url_kwarg = "form_handler_entry_id"
permission_classes = (EditFormHandlerEntryPermission,)
def _get_queryset(self, request):
"""Get queryset."""
# TODO: The form element entry has also `form_entry__user` in
# `seleect_related`. Find out if that something that could
# be also be applied here to improve the performance.
return FormHandlerEntry._default_manager \
.select_related('form_entry') \
.filter(form_entry__user__pk=request.user.pk)
def get_essential_objects(
self,
form_handler_entry_id,
request,
):
"""Get essential objects."""
try:
obj = self.get_object(queryset=self._get_queryset(request))
except ObjectDoesNotExist as err:
raise Http404(_("Form element entry not found."))
form_entry = obj.form_entry
form_handler_plugin = obj.get_plugin(request=request)
form_handler_plugin.request = request
form_handler_plugin_form_cls = form_handler_plugin.get_form()
return (
form_entry,
form_handler_entry_id,
form_handler_plugin,
form_handler_plugin_form_cls,
obj,
)
def get_context_data(self, **kwargs):
"""Get context data."""
context = super(EditFormHandlerEntryView, self).get_context_data(**kwargs)
if not self.theme:
theme = get_theme(request=self.request, as_instance=True)
else:
theme = self.theme
if theme:
context.update({"fobi_theme": theme})
return context
def get_template_names(self):
"""Get template names."""
template_name = self.template_name
if not template_name:
if not self.theme:
theme = get_theme(request=self.request, as_instance=True)
else:
theme = self.theme
template_name = theme.edit_form_handler_entry_template
return [template_name]
def get(self, request, *args, **kwargs):
"""Handle GET requests: instantiate a blank version of the form."""
(
form_entry,
form_handler_entry_id,
form_handler_plugin,
form_handler_plugin_form_cls,
obj,
) = self.get_essential_objects(
self.kwargs.get("form_handler_entry_id"),
self.request,
)
self.object = obj
form = None
if not form_handler_plugin_form_cls:
messages.info(
request,
_('The form handler plugin "{0}" is not '
'configurable!').format(form_handler_plugin.name)
)
return redirect('fobi.edit_form_entry', form_entry_id=form_entry.pk)
else:
form = form_handler_plugin.get_initialised_edit_form_or_404()
return self.render_to_response(
self.get_context_data(
form=form,
form_entry=form_entry,
form_handler_plugin=form_handler_plugin,
)
)
def post(self, request, *args, **kwargs):
"""
Handle POST requests: instantiate a form instance with the passed
POST variables and then check if it's valid.
"""
self.object = None
(
form_entry,
form_handler_entry_id,
form_handler_plugin,
form_handler_plugin_form_cls,
obj,
) = self.get_essential_objects(
self.kwargs.get("form_handler_entry_id"),
self.request,
)
self.object = obj
if not form_handler_plugin_form_cls:
messages.info(
request,
_('The form handler plugin "{0}" is not '
'configurable!').format(form_handler_plugin.name)
)
return redirect('fobi.edit_form_entry', form_entry_id=form_entry.pk)
form = form_handler_plugin.get_initialised_edit_form_or_404(
data=request.POST,
files=request.FILES
)
if form.is_valid():
# Saving the plugin form data.
form.save_plugin_data(request=request)
# Getting the plugin data.
obj.plugin_data = form.get_plugin_data(request=request)
# Save the object.
obj.save()
messages.info(
request,
_('The form handler plugin "{0}" was edited '
'successfully.').format(form_handler_plugin.name)
)
return redirect(
"{0}?active_tab=tab-form-handlers".format(
reverse_lazy(
'fobi.edit_form_entry',
kwargs={'form_entry_id': form_entry.pk}
)
)
)
return self.render_to_response(
self.get_context_data(
form=form,
form_entry=form_entry,
form_handler_plugin=form_handler_plugin,
)
)
# *****************************************************************************
# **************************** Delete form handler entry **********************
# *****************************************************************************
class DeleteFormHandlerEntryView(AbstractDeletePluginEntryView):
"""Delete form handler entry."""
model = FormHandlerEntry
permission_classes = (DeleteFormHandlerEntryPermission,)
pk_url_kwarg = "form_handler_entry_id"
get_user_plugin_uids_func = get_user_form_handler_plugin_uids
message = _('The form handler plugin "{0}" was deleted successfully.')
html_anchor = '?active_tab=tab-form-handlers'