* renames certain classes to match their content * splits larger files into smaller ones
184 lines
6.3 KiB
Python
184 lines
6.3 KiB
Python
"""
|
|
Author: Michel Peltriaux
|
|
Created on: 12.12.25
|
|
|
|
"""
|
|
from bootstrap_modal_forms.mixins import is_ajax
|
|
from django.contrib import messages
|
|
from django.core.exceptions import ObjectDoesNotExist
|
|
from django.http import HttpRequest, HttpResponseRedirect
|
|
from django.shortcuts import get_object_or_404, render
|
|
from django.urls import reverse
|
|
|
|
from konova.contexts import BaseContext
|
|
from konova.models import BaseObject
|
|
from konova.views.base import AbstractBaseView
|
|
|
|
|
|
class AbstractModalFormView(AbstractBaseView):
|
|
""" Abstract base view providing logic to perform most modal form based view renderings
|
|
|
|
"""
|
|
_TEMPLATE: str = "modal/modal_form.html"
|
|
_MODEL_CLS = None
|
|
_FORM_CLS = None
|
|
_MSG_SUCCESS = None
|
|
|
|
class Meta:
|
|
abstract = True
|
|
|
|
def _user_has_shared_access(self, user, **kwargs):
|
|
""" Checks whether the user has shared access to this object.
|
|
|
|
For objects inheriting from BaseObject class the method 'is_shared_with()' is a handy
|
|
wrapper for checking shared access. For any other circumstances this method should be overwritten
|
|
to provide custom shared-access-checking logic.
|
|
|
|
If no shared-access-check is needed, this method can be overwritten with a simple True returning.
|
|
|
|
Args:
|
|
user (User): The performing user
|
|
**kwargs ():
|
|
|
|
Returns:
|
|
has_shared_access (bool): Whether the user has shared access
|
|
"""
|
|
obj = get_object_or_404(self._MODEL_CLS, id=kwargs.get("id"))
|
|
return obj.is_shared_with(user)
|
|
|
|
def get(self, request: HttpRequest, *args, **kwargs):
|
|
""" GET endpoint for rendering a view holding a modal form
|
|
|
|
Args:
|
|
request (HttpRequest): The incoming request
|
|
*args ():
|
|
**kwargs ():
|
|
|
|
Returns:
|
|
|
|
"""
|
|
# If there is an id provided as mapped parameter from the URL -> take it ...
|
|
_id = kwargs.pop("id", None)
|
|
try:
|
|
# ... and try to resolve it into an object
|
|
obj = self._MODEL_CLS.objects.get(id=_id)
|
|
self._check_for_recorded_instance(obj)
|
|
except ObjectDoesNotExist:
|
|
# ... If there is none, maybe we are currently processing
|
|
# the creation of a new object (therefore no id yet), so let's continue
|
|
obj = None
|
|
form = self._FORM_CLS(
|
|
request.POST or None,
|
|
request.FILES or None,
|
|
instance=obj,
|
|
request=request,
|
|
**kwargs
|
|
)
|
|
context = {
|
|
"form": form,
|
|
}
|
|
context = BaseContext(request, context).context
|
|
return render(request, self._TEMPLATE, context)
|
|
|
|
def post(self, request: HttpRequest, *args, **kwargs):
|
|
""" POST endpoint for processing form contents of a view
|
|
|
|
Args:
|
|
request (HttpRequest): The incoming request
|
|
*args ():
|
|
**kwargs ():
|
|
|
|
Returns:
|
|
|
|
"""
|
|
# If there is an id provided as mapped parameter from the URL take it ...
|
|
_id = kwargs.pop("id", None)
|
|
try:
|
|
# ... and try to resolve it into a record
|
|
obj = self._MODEL_CLS.objects.get(id=_id)
|
|
self._check_for_recorded_instance(obj)
|
|
except ObjectDoesNotExist:
|
|
# ... If there is none, maybe we are currently processing
|
|
# the creation of a new object (therefore no id yet), so let's continue
|
|
obj = None
|
|
form = self._FORM_CLS(
|
|
request.POST or None,
|
|
request.FILES or None,
|
|
instance=obj,
|
|
request=request,
|
|
**kwargs
|
|
)
|
|
# Get now the redirect url and take specifics of the obj into account for that.
|
|
# We do not do this after saving the form to avoid side effects due to possibly changed data
|
|
redirect_url = self._get_redirect_url(obj=obj)
|
|
if form.is_valid():
|
|
# Modal forms send one POST for checking on data validity. This is used to evaluate possible errors
|
|
# on the form. The second POST (if no errors have been found) is the 'proper' one,
|
|
# which we want to process by saving/commiting of the data to the database.
|
|
if not is_ajax(request.META):
|
|
# Get now the success message and take specifics of the obj into account for that
|
|
msg_success = self._get_msg_success(obj=obj, *args, **kwargs)
|
|
form.save()
|
|
messages.success(
|
|
request,
|
|
msg_success
|
|
)
|
|
return HttpResponseRedirect(redirect_url)
|
|
else:
|
|
context = {
|
|
"form": form,
|
|
}
|
|
context = BaseContext(request, context).context
|
|
return render(request, self._TEMPLATE, context)
|
|
|
|
def _get_redirect_url(self, *args, **kwargs):
|
|
""" Getter to construct a more specific, data dependant redirect URL (if needed)
|
|
|
|
Args:
|
|
*args ():
|
|
**kwargs ():
|
|
|
|
Returns:
|
|
url (str): Reversed redirect url
|
|
"""
|
|
obj = kwargs.get("obj", None)
|
|
if obj:
|
|
return reverse(self._REDIRECT_URL, args=(obj.id,))
|
|
else:
|
|
return reverse(self._REDIRECT_URL)
|
|
|
|
def _get_msg_success(self, *args, **kwargs):
|
|
""" Getter to construct a more specific, data dependant success message
|
|
|
|
Args:
|
|
*args ():
|
|
**kwargs ():
|
|
|
|
Returns:
|
|
|
|
"""
|
|
return self._MSG_SUCCESS
|
|
|
|
def _check_for_recorded_instance(self, obj):
|
|
""" Checks if the object on this view is recorded and runs some special logic if so
|
|
|
|
If the instance is recorded, the view should provide some information about why the user can not edit anything.
|
|
This behaviour is only intended to mask any form for instances based on the BaseObject class.
|
|
|
|
There are situations where the form should be rendered regularly, despite the instance being recorded,
|
|
e.g. for rendering deduction form contents on (recorded) eco accounts.
|
|
|
|
Returns:
|
|
|
|
"""
|
|
is_none = obj is None
|
|
is_other_data_type = not isinstance(obj, BaseObject)
|
|
|
|
if is_none or is_other_data_type:
|
|
# Do nothing
|
|
return
|
|
|
|
if obj.is_recorded:
|
|
# Replace default template with a blocking one
|
|
self._TEMPLATE = "form/recorded_no_edit.html"
|