diff --git a/compensation/forms/modals/state.py b/compensation/forms/modals/state.py index 7340c95f..3ff054d6 100644 --- a/compensation/forms/modals/state.py +++ b/compensation/forms/modals/state.py @@ -106,7 +106,7 @@ class NewCompensationStateModalForm(BaseModalForm): """ redirect_url = redirect_url if redirect_url is not None else request.META.get("HTTP_REFERER", "home") - template = self.template + template = self._TEMPLATE if request.method == "POST": if self.is_valid(): # Modal forms send one POST for checking on data validity. This can be used to return possible errors diff --git a/compensation/urls/compensation.py b/compensation/urls/compensation.py index b410ac78..d97565f2 100644 --- a/compensation/urls/compensation.py +++ b/compensation/urls/compensation.py @@ -18,8 +18,8 @@ from compensation.views.compensation.action import NewCompensationActionView, Ed from compensation.views.compensation.state import NewCompensationStateView, EditCompensationStateView, \ RemoveCompensationStateView from compensation.views.compensation.compensation import \ - remove_view, CompensationIndexView, CompensationIdentifierGeneratorView, CompensationDetailView, \ - NewCompensationFormView, EditCompensationFormView + CompensationIndexView, CompensationIdentifierGeneratorView, CompensationDetailView, \ + NewCompensationFormView, EditCompensationFormView, RemoveCompensationView from compensation.views.compensation.log import CompensationLogView urlpatterns = [ @@ -31,7 +31,7 @@ urlpatterns = [ path('', CompensationDetailView.as_view(), name='detail'), path('/log', CompensationLogView.as_view(), name='log'), path('/edit', EditCompensationFormView.as_view(), name='edit'), - path('/remove', remove_view, name='remove'), + path('/remove', RemoveCompensationView.as_view(), name='remove'), path('/state/new', NewCompensationStateView.as_view(), name='new-state'), path('/state//edit', EditCompensationStateView.as_view(), name='state-edit'), diff --git a/compensation/urls/eco_account.py b/compensation/urls/eco_account.py index fc3c6116..d3d143f1 100644 --- a/compensation/urls/eco_account.py +++ b/compensation/urls/eco_account.py @@ -8,9 +8,8 @@ Created on: 24.08.21 from django.urls import path from compensation.autocomplete.eco_account import EcoAccountAutocomplete -from compensation.views.eco_account.eco_account import remove_view, \ - EcoAccountIndexView, EcoAccountIdentifierGeneratorView, EcoAccountDetailView, NewEcoAccountFormView, \ - EditEcoAccountFormView +from compensation.views.eco_account.eco_account import EcoAccountIndexView, EcoAccountIdentifierGeneratorView, \ + EcoAccountDetailView, NewEcoAccountFormView, EditEcoAccountFormView, RemoveEcoAccountView from compensation.views.eco_account.log import EcoAccountLogView from compensation.views.eco_account.record import EcoAccountRecordView from compensation.views.eco_account.report import EcoAccountReportView @@ -37,7 +36,7 @@ urlpatterns = [ path('/record', EcoAccountRecordView.as_view(), name='record'), path('/report', EcoAccountReportView.as_view(), name='report'), path('/edit', EditEcoAccountFormView.as_view(), name='edit'), - path('/remove', remove_view, name='remove'), + path('/remove', RemoveEcoAccountView.as_view(), name='remove'), path('/resub', EcoAccountResubmissionView.as_view(), name='resubmission-create'), path('/state/new', NewEcoAccountStateView.as_view(), name='new-state'), diff --git a/compensation/views/compensation/compensation.py b/compensation/views/compensation/compensation.py index 35ff6510..098849d8 100644 --- a/compensation/views/compensation/compensation.py +++ b/compensation/views/compensation/compensation.py @@ -25,6 +25,7 @@ from konova.utils.message_templates import COMPENSATION_REMOVED_TEMPLATE, DATA_C from konova.views.base import BaseIndexView, BaseIdentifierGeneratorView, BaseNewSpatialLocatedObjectFormView, \ BaseEditSpatialLocatedObjectFormView from konova.views.detail import BaseDetailView +from konova.views.remove import BaseRemoveModalFormView class CompensationIndexView(LoginRequiredMixin, BaseIndexView): @@ -164,25 +165,10 @@ class CompensationDetailView(BaseDetailView): return context -@login_required_modal -@login_required -@default_group_required -@shared_access_required(Compensation, "id") -def remove_view(request: HttpRequest, id: str): - """ Renders a modal view for removing the compensation - - Args: - request (HttpRequest): The incoming request - id (str): The compensation's id - - Returns: - - """ - comp = get_object_or_404(Compensation, id=id) - form = RemoveModalForm(request.POST or None, instance=comp, request=request) - return form.process_request( - request=request, - msg_success=COMPENSATION_REMOVED_TEMPLATE.format(comp.identifier), - redirect_url=reverse("compensation:index"), - ) +class RemoveCompensationView(LoginRequiredMixin, BaseRemoveModalFormView): + _MODEL_CLS = Compensation + _FORM_CLS = RemoveModalForm + _REDIRECT_URL = "compensation:index" + def _user_has_permission(self, user): + return user.is_default_user() diff --git a/compensation/views/eco_account/deduction.py b/compensation/views/eco_account/deduction.py index 01e84f3f..0dddc1eb 100644 --- a/compensation/views/eco_account/deduction.py +++ b/compensation/views/eco_account/deduction.py @@ -22,6 +22,10 @@ class NewEcoAccountDeductionView(LoginRequiredMixin, AbstractNewDeductionView): if not obj.recorded: raise Http404() + def _check_for_recorded_instance(self, obj): + # Deductions can be created on recorded as well as on non-recorded entries + return None + class EditEcoAccountDeductionView(LoginRequiredMixin, AbstractEditDeductionView): _MODEL_CLS = EcoAccount diff --git a/compensation/views/eco_account/eco_account.py b/compensation/views/eco_account/eco_account.py index 95dd8005..f9c50ab8 100644 --- a/compensation/views/eco_account/eco_account.py +++ b/compensation/views/eco_account/eco_account.py @@ -26,6 +26,7 @@ from konova.utils.message_templates import CANCEL_ACC_RECORDED_OR_DEDUCTED, RECO from konova.views.base import BaseIndexView, BaseIdentifierGeneratorView, BaseNewSpatialLocatedObjectFormView, \ BaseEditSpatialLocatedObjectFormView from konova.views.detail import BaseDetailView +from konova.views.remove import BaseRemoveModalFormView class EcoAccountIndexView(LoginRequiredMixin, BaseIndexView): @@ -254,34 +255,10 @@ class EcoAccountDetailView(BaseDetailView): return context -@login_required_modal -@login_required -@default_group_required -@shared_access_required(EcoAccount, "id") -def remove_view(request: HttpRequest, id: str): - """ Renders a modal view for removing the eco account - - Args: - request (HttpRequest): The incoming request - id (str): The account's id - - Returns: - - """ - acc = get_object_or_404(EcoAccount, id=id) - - # If the eco account has already been recorded OR there are already deductions, it can not be deleted by a regular - # default group user - if acc.recorded is not None or acc.deductions.exists(): - user = request.user - if not user.in_group(ETS_GROUP): - messages.info(request, CANCEL_ACC_RECORDED_OR_DEDUCTED) - return redirect("compensation:acc:detail", id=id) - - form = RemoveEcoAccountModalForm(request.POST or None, instance=acc, request=request) - return form.process_request( - request=request, - msg_success=_("Eco-account removed"), - redirect_url=reverse("compensation:acc:index"), - ) +class RemoveEcoAccountView(LoginRequiredMixin, BaseRemoveModalFormView): + _MODEL_CLS = EcoAccount + _FORM_CLS = RemoveEcoAccountModalForm + _REDIRECT_URL = "compensation:acc:index" + def _user_has_permission(self, user): + return user.is_default_user() diff --git a/ema/urls.py b/ema/urls.py index bfc6c0f4..835530bb 100644 --- a/ema/urls.py +++ b/ema/urls.py @@ -10,8 +10,8 @@ from django.urls import path from ema.views.action import NewEmaActionView, EditEmaActionView, RemoveEmaActionView from ema.views.deadline import NewEmaDeadlineView, EditEmaDeadlineView, RemoveEmaDeadlineView from ema.views.document import NewEmaDocumentView, EditEmaDocumentView, RemoveEmaDocumentView, GetEmaDocumentView -from ema.views.ema import remove_view, EmaIndexView, \ - EmaIdentifierGeneratorView, EmaDetailView, EditEmaFormView, NewEmaFormView +from ema.views.ema import EmaIndexView, EmaIdentifierGeneratorView, EmaDetailView, EditEmaFormView, NewEmaFormView, \ + RemoveEmaView from ema.views.log import EmaLogView from ema.views.record import EmaRecordView from ema.views.report import EmaReportView @@ -27,7 +27,7 @@ urlpatterns = [ path("", EmaDetailView.as_view(), name="detail"), path('/log', EmaLogView.as_view(), name='log'), path('/edit', EditEmaFormView.as_view(), name='edit'), - path('/remove', remove_view, name='remove'), + path('/remove', RemoveEmaView.as_view(), name='remove'), path('/record', EmaRecordView.as_view(), name='record'), path('/report', EmaReportView.as_view(), name='report'), path('/resub', EmaResubmissionView.as_view(), name='resubmission-create'), diff --git a/ema/views/ema.py b/ema/views/ema.py index d454ffba..6057a95c 100644 --- a/ema/views/ema.py +++ b/ema/views/ema.py @@ -5,21 +5,17 @@ Contact: ksp-servicestelle@sgdnord.rlp.de Created on: 19.08.22 """ -from django.contrib.auth.decorators import login_required from django.contrib.auth.mixins import LoginRequiredMixin -from django.http import HttpRequest from django.shortcuts import get_object_or_404 -from django.urls import reverse from django.utils.translation import gettext_lazy as _ from ema.forms import NewEmaForm, EditEmaForm from ema.models import Ema from ema.tables import EmaTable -from konova.decorators import shared_access_required, conservation_office_group_required, login_required_modal -from konova.forms.modals import RemoveModalForm from konova.views.base import BaseIndexView, BaseIdentifierGeneratorView, BaseNewSpatialLocatedObjectFormView, \ BaseEditSpatialLocatedObjectFormView from konova.views.detail import BaseDetailView +from konova.views.remove import BaseRemoveModalFormView class EmaIndexView(LoginRequiredMixin, BaseIndexView): @@ -112,26 +108,9 @@ class EmaDetailView(BaseDetailView): } return context +class RemoveEmaView(LoginRequiredMixin, BaseRemoveModalFormView): + _MODEL_CLS = Ema + _REDIRECT_URL = "ema:index" -@login_required_modal -@login_required -@conservation_office_group_required -@shared_access_required(Ema, "id") -def remove_view(request: HttpRequest, id: str): - """ Renders a modal view for removing the EMA - - Args: - request (HttpRequest): The incoming request - id (str): The EMA's id - - Returns: - - """ - ema = get_object_or_404(Ema, id=id) - form = RemoveModalForm(request.POST or None, instance=ema, request=request) - return form.process_request( - request=request, - msg_success=_("EMA removed"), - redirect_url=reverse("ema:index"), - ) - + def _user_has_permission(self, user): + return user.is_ets_user() diff --git a/intervention/urls.py b/intervention/urls.py index e179d000..39024f79 100644 --- a/intervention/urls.py +++ b/intervention/urls.py @@ -14,9 +14,8 @@ from intervention.views.deduction import NewInterventionDeductionView, EditInter RemoveInterventionDeductionView from intervention.views.document import NewInterventionDocumentView, GetInterventionDocumentView, \ RemoveInterventionDocumentView, EditInterventionDocumentView -from intervention.views.intervention import remove_view, \ - InterventionIndexView, InterventionIdentifierGeneratorView, InterventionDetailView, NewInterventionFormView, \ - EditInterventionFormView +from intervention.views.intervention import InterventionIndexView, InterventionIdentifierGeneratorView, \ + InterventionDetailView, NewInterventionFormView, EditInterventionFormView, RemoveInterventionView from intervention.views.log import InterventionLogView from intervention.views.record import InterventionRecordView from intervention.views.report import InterventionReportView @@ -33,7 +32,7 @@ urlpatterns = [ path('', InterventionDetailView.as_view(), name='detail'), path('/log', InterventionLogView.as_view(), name='log'), path('/edit', EditInterventionFormView.as_view(), name='edit'), - path('/remove', remove_view, name='remove'), + path('/remove', RemoveInterventionView.as_view(), name='remove'), path('/share/', InterventionShareByTokenView.as_view(), name='share-token'), path('/share', InterventionShareFormView.as_view(), name='share-form'), path('/check', InterventionCheckView.as_view(), name='check'), diff --git a/intervention/views/intervention.py b/intervention/views/intervention.py index cd0e9308..1350340a 100644 --- a/intervention/views/intervention.py +++ b/intervention/views/intervention.py @@ -10,22 +10,21 @@ from django.contrib.auth.decorators import login_required from django.contrib.auth.mixins import LoginRequiredMixin from django.http import HttpRequest from django.shortcuts import get_object_or_404, render, redirect -from django.urls import reverse from django.utils.translation import gettext_lazy as _ from intervention.forms.intervention import EditInterventionForm, NewInterventionForm from intervention.models import Intervention from intervention.tables import InterventionTable from konova.contexts import BaseContext -from konova.decorators import default_group_required, shared_access_required, login_required_modal +from konova.decorators import default_group_required, shared_access_required from konova.forms import SimpleGeomForm -from konova.forms.modals import RemoveModalForm from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER from konova.utils.message_templates import DATA_CHECKED_PREVIOUSLY_TEMPLATE, RECORDED_BLOCKS_EDIT, \ CHECK_STATE_RESET, FORM_INVALID, GEOMETRY_SIMPLIFIED, GEOMETRIES_IGNORED_TEMPLATE from konova.views.base import BaseIndexView, BaseIdentifierGeneratorView, BaseNewSpatialLocatedObjectFormView, \ BaseEditSpatialLocatedObjectFormView from konova.views.detail import BaseDetailView +from konova.views.remove import BaseRemoveModalFormView class InterventionIndexView(LoginRequiredMixin, BaseIndexView): @@ -181,26 +180,6 @@ def edit_view(request: HttpRequest, id: str): context = BaseContext(request, context).context return render(request, template, context) - -@login_required_modal -@login_required -@default_group_required -@shared_access_required(Intervention, "id") -def remove_view(request: HttpRequest, id: str): - """ Renders a remove view for this intervention - - Args: - request (HttpRequest): The incoming request - id (str): The uuid id as string - - Returns: - - """ - obj = Intervention.objects.get(id=id) - identifier = obj.identifier - form = RemoveModalForm(request.POST or None, instance=obj, request=request) - return form.process_request( - request, - _("{} removed").format(identifier), - redirect_url=reverse("intervention:index") - ) +class RemoveInterventionView(LoginRequiredMixin, BaseRemoveModalFormView): + _MODEL_CLS = Intervention + _REDIRECT_URL = "intervention:index" diff --git a/konova/forms/base_form.py b/konova/forms/base_form.py index e648f796..2e1cc960 100644 --- a/konova/forms/base_form.py +++ b/konova/forms/base_form.py @@ -10,14 +10,11 @@ from abc import abstractmethod from django import forms from django.utils.translation import gettext_lazy as _ -from konova.models import BaseObject - class BaseForm(forms.Form): """ Basic form for that holds attributes needed in all other forms """ - template = None action_url = None action_btn_label = _("Save") form_title = None @@ -43,7 +40,6 @@ class BaseForm(forms.Form): self.has_required_fields = True break - self.check_for_recorded_instance() self.__check_valid_label_input_ratio() @abstractmethod @@ -137,34 +133,3 @@ class BaseForm(forms.Form): set_class = self.fields[field].widget.attrs.get("class", "") set_class = set_class.replace(cls, "") self.fields[field].widget.attrs["class"] = set_class - - def check_for_recorded_instance(self): - """ Checks if the instance is recorded and runs some special logic if yes - - If the instance is recorded, the form shall not display any possibility to - edit any data. Instead, the users should get some information about why they can not edit anything. - - There are situations where the form should be rendered regularly, - e.g deduction forms for (recorded) eco accounts. - - Returns: - - """ - is_none = self.instance is None - is_other_data_type = not isinstance(self.instance, BaseObject) - - if is_none or is_other_data_type: - # Do nothing - return - - if self.instance.is_recorded: - self.block_form() - - def block_form(self): - """ - Overwrites template, providing no actions - - Returns: - - """ - self.template = "form/recorded_no_edit.html" \ No newline at end of file diff --git a/konova/forms/modals/base_form.py b/konova/forms/modals/base_form.py index 96539cf9..93127b0e 100644 --- a/konova/forms/modals/base_form.py +++ b/konova/forms/modals/base_form.py @@ -23,7 +23,7 @@ class BaseModalForm(BaseForm, BSModalForm): """ is_modal_form = True render_submit = True - template = "modal/modal_form.html" + _TEMPLATE = "modal/modal_form.html" def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) @@ -43,7 +43,7 @@ class BaseModalForm(BaseForm, BSModalForm): """ redirect_url = redirect_url if redirect_url is not None else request.META.get("HTTP_REFERER", "home") - template = self.template + template = self._TEMPLATE if request.method == "POST": if self.is_valid(): if not is_ajax(request.META): diff --git a/konova/utils/message_templates.py b/konova/utils/message_templates.py index 860dc9eb..a7e05c53 100644 --- a/konova/utils/message_templates.py +++ b/konova/utils/message_templates.py @@ -20,6 +20,9 @@ ENTRY_REMOVE_MISSING_PERMISSION = _("Only conservation or registration office us MISSING_GROUP_PERMISSION = _("You need to be part of another user group.") CHECK_STATE_RESET = _("Status of Checked reset") +# REMOVED +GENERIC_REMOVED_TEMPLATE = _("{} removed") + # RECORDING RECORDED_BLOCKS_EDIT = _("Entry is recorded. To edit data, the entry first needs to be unrecorded.") ENTRY_RECORDED = _("{} recorded") diff --git a/konova/views/base.py b/konova/views/base.py index a1de7e29..5f9db037 100644 --- a/konova/views/base.py +++ b/konova/views/base.py @@ -16,8 +16,9 @@ from django.utils.translation import gettext_lazy as _ from konova.contexts import BaseContext from konova.forms import BaseForm, SimpleGeomForm +from konova.models import BaseObject from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER -from konova.utils.general import check_user_is_in_any_group, check_id_is_valid_uuid +from konova.utils.general import check_user_is_in_any_group from konova.utils.message_templates import MISSING_GROUP_PERMISSION, DATA_UNSHARED, IDENTIFIER_REPLACED, \ GEOMETRY_SIMPLIFIED, GEOMETRIES_IGNORED_TEMPLATE, RECORDED_BLOCKS_EDIT, FORM_INVALID @@ -89,6 +90,7 @@ class BaseModalFormView(BaseView): def get(self, request: HttpRequest, id: str, *args, **kwargs): obj = self._MODEL_CLS.objects.get(id=id) + self._check_for_recorded_instance(obj) form = self._FORM_CLS( request.POST or None, request.FILES or None, @@ -104,6 +106,7 @@ class BaseModalFormView(BaseView): def post(self, request: HttpRequest, id: str, *args, **kwargs): obj = self._MODEL_CLS.objects.get(id=id) + self._check_for_recorded_instance(obj) form = self._FORM_CLS( request.POST or None, request.FILES or None, @@ -140,6 +143,36 @@ class BaseModalFormView(BaseView): def _get_msg_success(self, *args, **kwargs): 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 yes + + If the instance is recorded, the view should provide some information about why the user can not edit anything. + + There are situations where the form should be rendered regularly, + e.g deduction forms for (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: + self._block_form() + + def _block_form(self): + """ + Overwrites template, providing no actions + + Returns: + + """ + self._TEMPLATE = "form/recorded_no_edit.html" + class BaseIndexView(BaseView): """ Base class for index views diff --git a/konova/views/record.py b/konova/views/record.py index 7f3c41ce..cf780acb 100644 --- a/konova/views/record.py +++ b/konova/views/record.py @@ -26,3 +26,7 @@ class AbstractRecordView(BaseModalFormView): return ENTRY_RECORDED.format(obj.identifier) else: return ENTRY_UNRECORDED.format(obj.identifier) + + def _check_for_recorded_instance(self, obj): + # Do not block record view if instance might be recorded + return None \ No newline at end of file diff --git a/konova/views/remove.py b/konova/views/remove.py new file mode 100644 index 00000000..82b1e57a --- /dev/null +++ b/konova/views/remove.py @@ -0,0 +1,28 @@ +""" +Author: Michel Peltriaux +Created on: 21.10.25 + +""" +from django.urls import reverse + +from konova.forms.modals import RemoveModalForm +from konova.utils.message_templates import GENERIC_REMOVED_TEMPLATE +from konova.views.base import BaseModalFormView + + +class BaseRemoveModalFormView(BaseModalFormView): + _MODEL_CLS = None + _FORM_CLS = RemoveModalForm + _MSG_SUCCESS = GENERIC_REMOVED_TEMPLATE + _REDIRECT_URL = None + + def _user_has_permission(self, user): + return user.is_default_user() + + def _get_redirect_url(self, *args, **kwargs): + return reverse(self._REDIRECT_URL) + + def _get_msg_success(self, *args, **kwargs): + obj = kwargs.get("obj", None) + assert obj is not None + return self._MSG_SUCCESS.format(obj.identifier) diff --git a/konova/views/resubmission.py b/konova/views/resubmission.py index cbe21af0..940d09f7 100644 --- a/konova/views/resubmission.py +++ b/konova/views/resubmission.py @@ -22,3 +22,7 @@ class AbstractResubmissionView(LoginRequiredMixin, BaseModalFormView): def _user_has_permission(self, user): return user.is_default_user() + + def _check_for_recorded_instance(self, obj): + # Resubmissions are allowed despite an entry being recorded + return None \ No newline at end of file