# Refactoring team views
* refactors team views * split views.py into users.py and teams.py in users app * refactors method headers for _user_has_permission() * adds method and class comments and documentation to base view classes
This commit is contained in:
parent
bc2e901ca9
commit
f122778232
@ -59,7 +59,7 @@ class NewCompensationFormView(BaseNewSpatialLocatedObjectFormView):
|
||||
intervention = get_object_or_404(Intervention, id=intervention_id)
|
||||
return intervention.is_shared_with(user)
|
||||
|
||||
def _user_has_permission(self, user):
|
||||
def _user_has_permission(self, user, **kwargs):
|
||||
# User has to be an ets user
|
||||
return user.is_default_user()
|
||||
|
||||
@ -88,7 +88,7 @@ class EditCompensationFormView(BaseEditSpatialLocatedObjectFormView):
|
||||
_TEMPLATE = "compensation/form/view.html"
|
||||
_REDIRECT_URL = "compensation:detail"
|
||||
|
||||
def _user_has_permission(self, user):
|
||||
def _user_has_permission(self, user, **kwargs):
|
||||
# User has to be a default user
|
||||
return user.is_default_user()
|
||||
|
||||
@ -170,5 +170,5 @@ class RemoveCompensationView(LoginRequiredMixin, BaseRemoveModalFormView):
|
||||
_FORM_CLS = RemoveModalForm
|
||||
_REDIRECT_URL = "compensation:index"
|
||||
|
||||
def _user_has_permission(self, user):
|
||||
def _user_has_permission(self, user, **kwargs):
|
||||
return user.is_default_user()
|
||||
|
||||
@ -49,7 +49,7 @@ class NewEcoAccountFormView(BaseNewSpatialLocatedObjectFormView):
|
||||
_TAB_TITLE = _("New Eco-Account")
|
||||
_REDIRECT_URL = "compensation:acc:detail"
|
||||
|
||||
def _user_has_permission(self, user):
|
||||
def _user_has_permission(self, user, **kwargs):
|
||||
# User has to be a default user
|
||||
return user.is_default_user()
|
||||
|
||||
@ -60,7 +60,7 @@ class EditEcoAccountFormView(BaseEditSpatialLocatedObjectFormView):
|
||||
_TEMPLATE = "compensation/form/view.html"
|
||||
_REDIRECT_URL = "compensation:acc:detail"
|
||||
|
||||
def _user_has_permission(self, user):
|
||||
def _user_has_permission(self, user, **kwargs):
|
||||
# User has to be a default user
|
||||
return user.is_default_user()
|
||||
|
||||
@ -260,5 +260,5 @@ class RemoveEcoAccountView(LoginRequiredMixin, BaseRemoveModalFormView):
|
||||
_FORM_CLS = RemoveEcoAccountModalForm
|
||||
_REDIRECT_URL = "compensation:acc:index"
|
||||
|
||||
def _user_has_permission(self, user):
|
||||
def _user_has_permission(self, user, **kwargs):
|
||||
return user.is_default_user()
|
||||
|
||||
@ -24,7 +24,7 @@ class BasePaymentView(LoginRequiredMixin, BaseModalFormView):
|
||||
url = super()._get_redirect_url(*args, **kwargs)
|
||||
return f"{url}#related_data"
|
||||
|
||||
def _user_has_permission(self, user):
|
||||
def _user_has_permission(self, user, **kwargs):
|
||||
return user.is_default_user()
|
||||
|
||||
|
||||
|
||||
@ -16,14 +16,14 @@ class NewEmaActionView(AbstractNewCompensationActionView):
|
||||
_MODEL_CLS = Ema
|
||||
_REDIRECT_URL = _EMA_ACCOUNT_DETAIL_URL_NAME
|
||||
|
||||
def _user_has_permission(self, user):
|
||||
def _user_has_permission(self, user, **kwargs):
|
||||
return user.is_ets_user()
|
||||
|
||||
class EditEmaActionView(AbstractEditCompensationActionView):
|
||||
_MODEL_CLS = Ema
|
||||
_REDIRECT_URL = _EMA_ACCOUNT_DETAIL_URL_NAME
|
||||
|
||||
def _user_has_permission(self, user):
|
||||
def _user_has_permission(self, user, **kwargs):
|
||||
return user.is_ets_user()
|
||||
|
||||
|
||||
@ -31,5 +31,5 @@ class RemoveEmaActionView(AbstractRemoveCompensationActionView):
|
||||
_MODEL_CLS = Ema
|
||||
_REDIRECT_URL = _EMA_ACCOUNT_DETAIL_URL_NAME
|
||||
|
||||
def _user_has_permission(self, user):
|
||||
def _user_has_permission(self, user, **kwargs):
|
||||
return user.is_ets_user()
|
||||
|
||||
@ -14,7 +14,7 @@ class NewEmaDeadlineView(AbstractNewDeadlineView):
|
||||
_MODEL_CLS = Ema
|
||||
_REDIRECT_URL = _EMA_DETAIL_URL_NAME
|
||||
|
||||
def _user_has_permission(self, user):
|
||||
def _user_has_permission(self, user, **kwargs):
|
||||
return user.is_ets_user()
|
||||
|
||||
|
||||
@ -22,7 +22,7 @@ class EditEmaDeadlineView(AbstractEditDeadlineView):
|
||||
_MODEL_CLS = Ema
|
||||
_REDIRECT_URL = _EMA_DETAIL_URL_NAME
|
||||
|
||||
def _user_has_permission(self, user):
|
||||
def _user_has_permission(self, user, **kwargs):
|
||||
return user.is_ets_user()
|
||||
|
||||
|
||||
@ -30,5 +30,5 @@ class RemoveEmaDeadlineView(AbstractRemoveDeadlineView):
|
||||
_MODEL_CLS = Ema
|
||||
_REDIRECT_URL = _EMA_DETAIL_URL_NAME
|
||||
|
||||
def _user_has_permission(self, user):
|
||||
def _user_has_permission(self, user, **kwargs):
|
||||
return user.is_ets_user()
|
||||
|
||||
@ -16,14 +16,14 @@ class NewEmaDocumentView(AbstractNewDocumentView):
|
||||
_FORM_CLS = NewEmaDocumentModalForm
|
||||
_REDIRECT_URL = "ema:detail"
|
||||
|
||||
def _user_has_permission(self, user):
|
||||
def _user_has_permission(self, user, **kwargs):
|
||||
return user.is_ets_user()
|
||||
|
||||
class GetEmaDocumentView(AbstractGetDocumentView):
|
||||
_MODEL_CLS = Ema
|
||||
_DOCUMENT_CLS = EmaDocument
|
||||
|
||||
def _user_has_permission(self, user):
|
||||
def _user_has_permission(self, user, **kwargs):
|
||||
return user.is_ets_user()
|
||||
|
||||
class RemoveEmaDocumentView(AbstractRemoveDocumentView):
|
||||
@ -32,7 +32,7 @@ class RemoveEmaDocumentView(AbstractRemoveDocumentView):
|
||||
_FORM_CLS = RemoveEmaDocumentModalForm
|
||||
_REDIRECT_URL = "ema:detail"
|
||||
|
||||
def _user_has_permission(self, user):
|
||||
def _user_has_permission(self, user, **kwargs):
|
||||
return user.is_ets_user()
|
||||
|
||||
class EditEmaDocumentView(AbstractEditDocumentView):
|
||||
@ -41,5 +41,5 @@ class EditEmaDocumentView(AbstractEditDocumentView):
|
||||
_DOCUMENT_CLS = EmaDocument
|
||||
_REDIRECT_URL = "ema:detail"
|
||||
|
||||
def _user_has_permission(self, user):
|
||||
def _user_has_permission(self, user, **kwargs):
|
||||
return user.is_ets_user()
|
||||
|
||||
@ -38,7 +38,7 @@ class NewEmaFormView(BaseNewSpatialLocatedObjectFormView):
|
||||
_TAB_TITLE = _("New EMA")
|
||||
_REDIRECT_URL = "ema:detail"
|
||||
|
||||
def _user_has_permission(self, user):
|
||||
def _user_has_permission(self, user, **kwargs):
|
||||
# User has to be an ets user
|
||||
return user.is_ets_user()
|
||||
|
||||
@ -50,7 +50,7 @@ class EditEmaFormView(BaseEditSpatialLocatedObjectFormView):
|
||||
_REDIRECT_URL = "ema:detail"
|
||||
_TAB_TITLE = _("Edit {}")
|
||||
|
||||
def _user_has_permission(self, user):
|
||||
def _user_has_permission(self, user, **kwargs):
|
||||
# User has to be an ets user
|
||||
return user.is_ets_user()
|
||||
|
||||
@ -59,7 +59,7 @@ class EmaIdentifierGeneratorView(LoginRequiredMixin, BaseIdentifierGeneratorView
|
||||
_MODEL_CLS = Ema
|
||||
_REDIRECT_URL = "ema:index"
|
||||
|
||||
def _user_has_permission(self, user):
|
||||
def _user_has_permission(self, user, **kwargs):
|
||||
return user.is_ets_user()
|
||||
|
||||
|
||||
@ -112,5 +112,5 @@ class RemoveEmaView(LoginRequiredMixin, BaseRemoveModalFormView):
|
||||
_MODEL_CLS = Ema
|
||||
_REDIRECT_URL = "ema:index"
|
||||
|
||||
def _user_has_permission(self, user):
|
||||
def _user_has_permission(self, user, **kwargs):
|
||||
return user.is_ets_user()
|
||||
|
||||
@ -14,5 +14,5 @@ from konova.views.log import AbstractLogView
|
||||
class EmaLogView(LoginRequiredMixin, AbstractLogView):
|
||||
_MODEL_CLS = Ema
|
||||
|
||||
def _user_has_permission(self, user):
|
||||
def _user_has_permission(self, user, **kwargs):
|
||||
return user.is_ets_user()
|
||||
|
||||
@ -16,5 +16,5 @@ class EmaResubmissionView(AbstractResubmissionView):
|
||||
_REDIRECT_URL = "ema:detail"
|
||||
action_url = "ema:resubmission-create"
|
||||
|
||||
def _user_has_permission(self, user):
|
||||
def _user_has_permission(self, user, **kwargs):
|
||||
return user.is_ets_user()
|
||||
|
||||
@ -17,5 +17,5 @@ class EmaShareFormView(AbstractShareFormView):
|
||||
_MODEL_CLS = Ema
|
||||
_REDIRECT_URL = "ema:detail"
|
||||
|
||||
def _user_has_permission(self, user):
|
||||
def _user_has_permission(self, user, **kwargs):
|
||||
return user.is_ets_user()
|
||||
@ -14,7 +14,7 @@ class NewEmaStateView(AbstractNewCompensationStateView):
|
||||
_MODEL_CLS = Ema
|
||||
_REDIRECT_URL = "ema:detail"
|
||||
|
||||
def _user_has_permission(self, user):
|
||||
def _user_has_permission(self, user, **kwargs):
|
||||
return user.is_ets_user()
|
||||
|
||||
|
||||
@ -22,7 +22,7 @@ class EditEmaStateView(AbstractEditCompensationStateView):
|
||||
_MODEL_CLS = Ema
|
||||
_REDIRECT_URL = "ema:detail"
|
||||
|
||||
def _user_has_permission(self, user):
|
||||
def _user_has_permission(self, user, **kwargs):
|
||||
return user.is_ets_user()
|
||||
|
||||
|
||||
@ -30,5 +30,5 @@ class RemoveEmaStateView(AbstractRemoveCompensationStateView):
|
||||
_MODEL_CLS = Ema
|
||||
_REDIRECT_URL = "ema:detail"
|
||||
|
||||
def _user_has_permission(self, user):
|
||||
def _user_has_permission(self, user, **kwargs):
|
||||
return user.is_ets_user()
|
||||
|
||||
@ -19,7 +19,7 @@ class InterventionCheckView(LoginRequiredMixin, BaseModalFormView):
|
||||
_MSG_SUCCESS = _("Check performed")
|
||||
_REDIRECT_URL = "intervention:detail"
|
||||
|
||||
def _user_has_permission(self, user):
|
||||
def _user_has_permission(self, user, **kwargs):
|
||||
return user.is_zb_user()
|
||||
|
||||
def _get_redirect_url(self, *args, **kwargs):
|
||||
|
||||
@ -25,7 +25,7 @@ class BaseRevocationView(LoginRequiredMixin, BaseModalFormView):
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
||||
def _user_has_permission(self, user):
|
||||
def _user_has_permission(self, user, **kwargs):
|
||||
return user.is_default_user()
|
||||
|
||||
def _get_redirect_url(self, *args, **kwargs):
|
||||
@ -63,7 +63,7 @@ class GetRevocationDocumentView(LoginRequiredMixin, BaseView):
|
||||
return redirect("intervention:detail", id=doc.instance.id)
|
||||
return get_document(doc)
|
||||
|
||||
def _user_has_permission(self, user):
|
||||
def _user_has_permission(self, user, **kwargs):
|
||||
return user.is_default_user()
|
||||
|
||||
def _user_has_shared_access(self, user, **kwargs):
|
||||
|
||||
@ -20,6 +20,12 @@ 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")
|
||||
|
||||
# USER | TEAM
|
||||
TEAM_ADDED = _("New team added")
|
||||
TEAM_EDITED = _("Team edited")
|
||||
TEAM_REMOVED = _("Team removed")
|
||||
TEAM_LEFT = _("Left Team")
|
||||
|
||||
# REMOVED
|
||||
GENERIC_REMOVED_TEMPLATE = _("{} removed")
|
||||
|
||||
|
||||
@ -21,7 +21,7 @@ class AbstractCompensationActionView(LoginRequiredMixin, BaseModalFormView):
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
||||
def _user_has_permission(self, user):
|
||||
def _user_has_permission(self, user, **kwargs):
|
||||
return user.is_default_user()
|
||||
|
||||
def _get_redirect_url(self, *args, **kwargs):
|
||||
|
||||
@ -8,6 +8,7 @@ from abc import abstractmethod
|
||||
from bootstrap_modal_forms.mixins import is_ajax
|
||||
from django.contrib import messages
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
from django.http import HttpRequest, JsonResponse, HttpResponseRedirect
|
||||
from django.shortcuts import render, redirect, get_object_or_404
|
||||
from django.urls import reverse
|
||||
@ -24,18 +25,37 @@ from konova.utils.message_templates import MISSING_GROUP_PERMISSION, DATA_UNSHAR
|
||||
|
||||
|
||||
class BaseView(View):
|
||||
_TEMPLATE: str = "CHANGE_ME"
|
||||
_TAB_TITLE: str = "CHANGE_ME"
|
||||
_REDIRECT_URL: str = "CHANGE_ME"
|
||||
_REDIRECT_URL_ERROR: str = "home"
|
||||
""" An abstract base view
|
||||
|
||||
This class represents the root of all views on this project. It defines private variables which have to be used
|
||||
by inheriting classes for proper generic inheriting.
|
||||
|
||||
"""
|
||||
_TEMPLATE: str = "CHANGE_ME" # Path to template file
|
||||
_TAB_TITLE: str = "CHANGE_ME" # Title displayed on browser tab
|
||||
_REDIRECT_URL: str = "CHANGE_ME" # Default URL to redirect after processing (notation as django url "namespace:endpoint")
|
||||
_REDIRECT_URL_ERROR: str = "home" # Default URL to redirect in case of an error (same notation)
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
""" Dispatching requests before forwarding them into GET or POST endpoints.
|
||||
|
||||
Defines basic checks which need to be done before a user can get access to any view inheriting from
|
||||
this class.
|
||||
|
||||
Args:
|
||||
request (HttpRequest): The incoming request
|
||||
*args ():
|
||||
**kwargs ():
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
request = check_user_is_in_any_group(request)
|
||||
|
||||
if not self._user_has_permission(request.user):
|
||||
if not self._user_has_permission(request.user, **kwargs):
|
||||
messages.info(request, MISSING_GROUP_PERMISSION)
|
||||
return redirect(reverse(self._REDIRECT_URL_ERROR))
|
||||
|
||||
@ -46,37 +66,68 @@ class BaseView(View):
|
||||
return super().dispatch(request, *args, **kwargs)
|
||||
|
||||
@abstractmethod
|
||||
def _user_has_permission(self, user):
|
||||
""" Has to be implemented properly by inheriting classes
|
||||
def _user_has_permission(self, user, **kwargs):
|
||||
""" Checks whether the user has permission to get this view rendered.
|
||||
|
||||
If no specific check is needed, this method can be overwritten with a simple True returning.
|
||||
|
||||
Args:
|
||||
user ():
|
||||
user (User): The performing user
|
||||
**kwargs ():
|
||||
|
||||
Returns:
|
||||
|
||||
has_permission (bool): Whether the user has permission to see this view
|
||||
"""
|
||||
raise NotImplementedError("User permission not checked!")
|
||||
|
||||
@abstractmethod
|
||||
def _user_has_shared_access(self, user, **kwargs):
|
||||
""" Has to be implemented properly by inheriting classes
|
||||
""" Checks whether the user has shared access to this object.
|
||||
|
||||
If no shared-access-check is needed, this method can be overwritten with a simple True returning.
|
||||
|
||||
Args:
|
||||
user ():
|
||||
user (User): The performing user
|
||||
**kwargs ():
|
||||
|
||||
Returns:
|
||||
|
||||
has_shared_access (bool): Whether the user has shared access
|
||||
"""
|
||||
raise NotImplementedError("Shared access not checked!")
|
||||
|
||||
def _get_redirect_url(self, *args, **kwargs):
|
||||
""" Getter to construct a more specific, data dependant redirect URL
|
||||
|
||||
By default the method simply returns the pre-defined redirect URL.
|
||||
|
||||
Args:
|
||||
*args ():
|
||||
**kwargs ():
|
||||
|
||||
Returns:
|
||||
url (str): Reversed redirect url
|
||||
"""
|
||||
return self._REDIRECT_URL
|
||||
|
||||
def _get_redirect_url_error(self, *args, **kwargs):
|
||||
""" Getter to construct a more specific, data dependant redirect URL in error cases
|
||||
|
||||
By default the method simply returns the pre-defined redirect URL for errors.
|
||||
|
||||
Args:
|
||||
*args ():
|
||||
**kwargs ():
|
||||
|
||||
Returns:
|
||||
url (str): Reversed redirect url
|
||||
"""
|
||||
return self._REDIRECT_URL_ERROR
|
||||
|
||||
class BaseModalFormView(BaseView):
|
||||
_TEMPLATE = "modal/modal_form.html"
|
||||
""" 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
|
||||
@ -85,12 +136,45 @@ class BaseModalFormView(BaseView):
|
||||
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, id: str, *args, **kwargs):
|
||||
obj = self._MODEL_CLS.objects.get(id=id)
|
||||
self._check_for_recorded_instance(obj)
|
||||
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 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,
|
||||
@ -104,9 +188,27 @@ class BaseModalFormView(BaseView):
|
||||
context = BaseContext(request, context).context
|
||||
return render(request, self._TEMPLATE, context)
|
||||
|
||||
def post(self, request: HttpRequest, id: str, *args, **kwargs):
|
||||
obj = self._MODEL_CLS.objects.get(id=id)
|
||||
self._check_for_recorded_instance(obj)
|
||||
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,
|
||||
@ -114,13 +216,15 @@ class BaseModalFormView(BaseView):
|
||||
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):
|
||||
# Modal forms send one POST for checking on data validity. This can be used to return possible errors
|
||||
# on the form. A second POST (if no errors occurs) is sent afterward and needs to process the
|
||||
# saving/commiting of the data to the database. is_ajax() performs this check. The first request is
|
||||
# an ajax call, the second is a regular form POST.
|
||||
# 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(
|
||||
@ -136,20 +240,41 @@ class BaseModalFormView(BaseView):
|
||||
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)
|
||||
assert obj is not None
|
||||
return reverse(self._REDIRECT_URL, args=(obj.id,))
|
||||
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 yes
|
||||
""" 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,
|
||||
e.g deduction forms for (recorded) eco accounts.
|
||||
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:
|
||||
|
||||
@ -162,29 +287,32 @@ class BaseModalFormView(BaseView):
|
||||
return
|
||||
|
||||
if obj.is_recorded:
|
||||
self._block_form()
|
||||
# Replace default template with a blocking one
|
||||
self._TEMPLATE = "form/recorded_no_edit.html"
|
||||
|
||||
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
|
||||
""" Abstract base class for index views
|
||||
|
||||
"""
|
||||
_TEMPLATE = "generic_index.html"
|
||||
_TEMPLATE: str = "generic_index.html"
|
||||
_INDEX_TABLE_CLS = None
|
||||
_REDIRECT_URL = "home"
|
||||
_REDIRECT_URL: str = "home"
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
||||
def get(self, request: HttpRequest):
|
||||
def get(self, request: HttpRequest, *args, **kwargs):
|
||||
""" GET endpoint for rendering index views
|
||||
|
||||
Args:
|
||||
request (HttpRequest): The incoming request
|
||||
*args ():
|
||||
**kwargs ():
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
qs = self._get_queryset()
|
||||
table = self._INDEX_TABLE_CLS(
|
||||
request=request,
|
||||
@ -199,9 +327,14 @@ class BaseIndexView(BaseView):
|
||||
|
||||
@abstractmethod
|
||||
def _get_queryset(self):
|
||||
""" Generic getter for the queryset of objects which shall be processed on this view
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def _user_has_permission(self, user):
|
||||
def _user_has_permission(self, user, **kwargs):
|
||||
# No specific permissions needed for opening base index view
|
||||
return True
|
||||
|
||||
@ -228,7 +361,7 @@ class BaseIdentifierGeneratorView(BaseView):
|
||||
}
|
||||
)
|
||||
|
||||
def _user_has_permission(self, user):
|
||||
def _user_has_permission(self, user, **kwargs):
|
||||
""" Should be overwritten in inheriting classes!
|
||||
|
||||
Args:
|
||||
@ -245,6 +378,9 @@ class BaseIdentifierGeneratorView(BaseView):
|
||||
|
||||
|
||||
class BaseFormView(BaseView):
|
||||
""" Abstract base class for rendering form views
|
||||
|
||||
"""
|
||||
_MODEL_CLS = None
|
||||
_FORM_CLS = None
|
||||
|
||||
@ -252,18 +388,21 @@ class BaseFormView(BaseView):
|
||||
abstract = True
|
||||
|
||||
def _get_additional_context(self, **kwargs):
|
||||
"""
|
||||
""" Getter for additional data, which is needed to properly render the current view
|
||||
|
||||
Args:
|
||||
**kwargs ():
|
||||
|
||||
Returns:
|
||||
|
||||
context (dict): Additional context data for rendering
|
||||
"""
|
||||
return {}
|
||||
|
||||
|
||||
class BaseSpatialLocatedObjectFormView(LoginRequiredMixin, BaseFormView):
|
||||
""" Abstract base view for processing objects with spatial data
|
||||
|
||||
"""
|
||||
_GEOMETRY_FORM_CLS = SimpleGeomForm
|
||||
|
||||
class Meta:
|
||||
@ -271,8 +410,11 @@ class BaseSpatialLocatedObjectFormView(LoginRequiredMixin, BaseFormView):
|
||||
|
||||
|
||||
class BaseNewSpatialLocatedObjectFormView(BaseSpatialLocatedObjectFormView):
|
||||
""" Base view for creating new spatial data related to objects
|
||||
|
||||
def _user_has_permission(self, user):
|
||||
"""
|
||||
|
||||
def _user_has_permission(self, user, **kwargs):
|
||||
# User has to have default privilege to call this endpoint
|
||||
return user.is_default_user()
|
||||
|
||||
@ -280,10 +422,21 @@ class BaseNewSpatialLocatedObjectFormView(BaseSpatialLocatedObjectFormView):
|
||||
# There is no shared access control since nothing exists yet
|
||||
return True
|
||||
|
||||
def get(self, request: HttpRequest, **kwargs):
|
||||
def get(self, request: HttpRequest, *args, **kwargs):
|
||||
""" GET endpoint for rendering a form view where object data and spatial data are processed
|
||||
|
||||
Args:
|
||||
request (HttpRequest): The incoming request
|
||||
**kwargs ():
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
# First initialize the regular object form and the geometry form based on request-bound data
|
||||
form: BaseForm = self._FORM_CLS(None, **kwargs, user=request.user)
|
||||
geom_form: SimpleGeomForm = self._GEOMETRY_FORM_CLS(None, user=request.user, read_only=False)
|
||||
|
||||
# Get some additional context and put everything into the rendering pipeline
|
||||
context = self._get_additional_context()
|
||||
context = BaseContext(request, additional_context=context).context
|
||||
context.update(
|
||||
@ -295,16 +448,30 @@ class BaseNewSpatialLocatedObjectFormView(BaseSpatialLocatedObjectFormView):
|
||||
)
|
||||
return render(request, self._TEMPLATE, context)
|
||||
|
||||
def post(self, request: HttpRequest, **kwargs):
|
||||
def post(self, request: HttpRequest, *args, **kwargs):
|
||||
""" POST endpoint for processing object and spatial data provided by forms
|
||||
|
||||
Args:
|
||||
request (HttpRequest): The incoming request
|
||||
**kwargs ():
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
# First initialize the regular object form and the geometry form based on request-bound data
|
||||
form: BaseForm = self._FORM_CLS(request.POST or None, **kwargs, user=request.user)
|
||||
geom_form: SimpleGeomForm = self._GEOMETRY_FORM_CLS(request.POST or None, user=request.user, read_only=False)
|
||||
|
||||
# Only continue if both forms are without errors
|
||||
if form.is_valid() and geom_form.is_valid():
|
||||
obj = form.save(request.user, geom_form)
|
||||
obj_redirect_url = reverse(self._REDIRECT_URL, args=(obj.id,))
|
||||
|
||||
generated_identifier = form.cleaned_data.get("identifier", None)
|
||||
|
||||
# There is a rare chance that an identifier has been taken already between sending the form and processing
|
||||
# the data. If the identifier can not be used anymore, we have to inform the user that another identifier
|
||||
# had to be generated
|
||||
if generated_identifier != obj.identifier:
|
||||
messages.info(
|
||||
request,
|
||||
@ -314,12 +481,18 @@ class BaseNewSpatialLocatedObjectFormView(BaseSpatialLocatedObjectFormView):
|
||||
)
|
||||
)
|
||||
messages.success(request, _("{} added").format(obj.identifier))
|
||||
# Very complex geometries have to be simplified automatically while processing the spatial data. If this
|
||||
# is the case, the user has to be informed. (They might want to check whether the stored geometry still
|
||||
# fits their needs)
|
||||
if geom_form.has_geometry_simplified():
|
||||
messages.info(
|
||||
request,
|
||||
GEOMETRY_SIMPLIFIED
|
||||
)
|
||||
|
||||
# If certain parts of the geometry do not pass the quality check (e.g. way too small and therefore more like
|
||||
# cutting errors) we need to inform the user that some parts have been removed/ignored while storing the
|
||||
# geometry
|
||||
num_ignored_geometries = geom_form.get_num_geometries_ignored()
|
||||
if num_ignored_geometries > 0:
|
||||
messages.info(
|
||||
@ -329,6 +502,7 @@ class BaseNewSpatialLocatedObjectFormView(BaseSpatialLocatedObjectFormView):
|
||||
|
||||
return redirect(obj_redirect_url)
|
||||
else:
|
||||
# Something was not properly entered on the forms, so we have to inform the user
|
||||
context = self._get_additional_context()
|
||||
messages.error(request, FORM_INVALID, extra_tags="danger",)
|
||||
|
||||
@ -344,15 +518,30 @@ class BaseNewSpatialLocatedObjectFormView(BaseSpatialLocatedObjectFormView):
|
||||
|
||||
|
||||
class BaseEditSpatialLocatedObjectFormView(BaseSpatialLocatedObjectFormView):
|
||||
""" Base view for editing new spatial data related to objects
|
||||
|
||||
"""
|
||||
_TAB_TITLE = _("Edit {}")
|
||||
|
||||
def get(self, request: HttpRequest, id: str):
|
||||
def get(self, request: HttpRequest, id: str, *args, **kwargs):
|
||||
""" GET endpoint for rendering a form view where object data and spatial data are processed
|
||||
|
||||
Args:
|
||||
request (HttpRequest): The incoming request
|
||||
id (str): The id of the object (not the geometry)
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
# First fetch the object identified by the id
|
||||
obj = get_object_or_404(
|
||||
self._MODEL_CLS,
|
||||
id=id
|
||||
)
|
||||
obj_redirect_url = reverse(self._REDIRECT_URL, args=(obj.id,))
|
||||
|
||||
# Check whether the object is recorded. If so - we can redirect the user and inform about the un-editability
|
||||
# of this entry
|
||||
if obj.is_recorded:
|
||||
messages.info(
|
||||
request,
|
||||
@ -360,9 +549,11 @@ class BaseEditSpatialLocatedObjectFormView(BaseSpatialLocatedObjectFormView):
|
||||
)
|
||||
return redirect(obj_redirect_url)
|
||||
|
||||
# Seems like the object is not recorded. Good - initialize the forms based on the obj and request-bound data
|
||||
form: BaseForm = self._FORM_CLS(None, instance=obj, user=request.user)
|
||||
geom_form: SimpleGeomForm = self._GEOMETRY_FORM_CLS(None, instance=obj, read_only=False)
|
||||
|
||||
# Get additional context for rendering and put everything in the rendering pipeline
|
||||
context = self._get_additional_context()
|
||||
context = BaseContext(request, additional_context=context).context
|
||||
context.update(
|
||||
@ -374,13 +565,33 @@ class BaseEditSpatialLocatedObjectFormView(BaseSpatialLocatedObjectFormView):
|
||||
)
|
||||
return render(request, self._TEMPLATE, context)
|
||||
|
||||
def post(self, request: HttpRequest, id: str):
|
||||
def post(self, request: HttpRequest, id: str, *args, **kwargs):
|
||||
""" POST endpoint for processing object and spatial data provided by forms
|
||||
|
||||
Args:
|
||||
request (HttpRequest): The incoming request
|
||||
id (str): The object's id
|
||||
*args ():
|
||||
**kwargs ():
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
obj = get_object_or_404(
|
||||
self._MODEL_CLS,
|
||||
id=id
|
||||
)
|
||||
obj_redirect_url = reverse(self._REDIRECT_URL, args=(obj.id,))
|
||||
|
||||
# If the object is recorded, we abort the processing directly and inform the user
|
||||
if obj.is_recorded:
|
||||
messages.info(
|
||||
request,
|
||||
RECORDED_BLOCKS_EDIT
|
||||
)
|
||||
return redirect(obj_redirect_url)
|
||||
|
||||
# Initialize forms with obj and request-bound data
|
||||
form: BaseForm = self._FORM_CLS(request.POST or None, instance=obj, user=request.user)
|
||||
geom_form: SimpleGeomForm = self._GEOMETRY_FORM_CLS(request.POST or None, instance=obj, read_only=False)
|
||||
|
||||
@ -388,12 +599,18 @@ class BaseEditSpatialLocatedObjectFormView(BaseSpatialLocatedObjectFormView):
|
||||
obj = form.save(request.user, geom_form)
|
||||
messages.success(request, _("{} edited").format(obj.identifier))
|
||||
|
||||
# Very complex geometries have to be simplified automatically while processing the spatial data. If this
|
||||
# is the case, the user has to be informed. (They might want to check whether the stored geometry still
|
||||
# fits their needs)
|
||||
if geom_form.has_geometry_simplified():
|
||||
messages.info(
|
||||
request,
|
||||
GEOMETRY_SIMPLIFIED
|
||||
)
|
||||
|
||||
# If certain parts of the geometry do not pass the quality check (e.g. way too small and therefore more like
|
||||
# cutting errors) we need to inform the user that some parts have been removed/ignored while storing the
|
||||
# geometry
|
||||
num_ignored_geometries = geom_form.get_num_geometries_ignored()
|
||||
if num_ignored_geometries > 0:
|
||||
messages.info(
|
||||
@ -420,5 +637,5 @@ class BaseEditSpatialLocatedObjectFormView(BaseSpatialLocatedObjectFormView):
|
||||
obj = get_object_or_404(self._MODEL_CLS, id=kwargs.get('id', None))
|
||||
return obj.is_shared_with(user)
|
||||
|
||||
def _user_has_permission(self, user):
|
||||
def _user_has_permission(self, user, **kwargs):
|
||||
return user.is_default_user()
|
||||
@ -25,7 +25,7 @@ class AbstractNewDeadlineView(LoginRequiredMixin, BaseModalFormView):
|
||||
def _get_redirect_url(self, *args, **kwargs):
|
||||
return super()._get_redirect_url(*args, **kwargs) + "#related_data"
|
||||
|
||||
def _user_has_permission(self, user):
|
||||
def _user_has_permission(self, user, **kwargs):
|
||||
return user.is_default_user()
|
||||
|
||||
|
||||
@ -41,7 +41,7 @@ class AbstractEditDeadlineView(LoginRequiredMixin, BaseModalFormView):
|
||||
def _get_redirect_url(self, *args, **kwargs):
|
||||
return super()._get_redirect_url(*args, **kwargs) + "#related_data"
|
||||
|
||||
def _user_has_permission(self, user):
|
||||
def _user_has_permission(self, user, **kwargs):
|
||||
return user.is_default_user()
|
||||
|
||||
|
||||
@ -57,5 +57,5 @@ class AbstractRemoveDeadlineView(LoginRequiredMixin, BaseModalFormView):
|
||||
def _get_redirect_url(self, *args, **kwargs):
|
||||
return super()._get_redirect_url(*args, **kwargs) + "#related_data"
|
||||
|
||||
def _user_has_permission(self, user):
|
||||
def _user_has_permission(self, user, **kwargs):
|
||||
return user.is_default_user()
|
||||
|
||||
@ -28,7 +28,7 @@ class AbstractDeductionView(BaseModalFormView):
|
||||
"""
|
||||
pass
|
||||
|
||||
def _user_has_permission(self, user) -> bool:
|
||||
def _user_has_permission(self, user, **kwargs) -> bool:
|
||||
"""
|
||||
|
||||
Args:
|
||||
|
||||
@ -42,7 +42,7 @@ class BaseDetailView(LoginRequiredMixin, BaseView):
|
||||
# Access to an entry's detail view is not restricted by the state of being-shared or not
|
||||
return True
|
||||
|
||||
def _user_has_permission(self, user):
|
||||
def _user_has_permission(self, user, **kwargs):
|
||||
# Detail views have no restrictions
|
||||
return True
|
||||
|
||||
|
||||
@ -27,7 +27,7 @@ class AbstractNewDocumentView(LoginRequiredMixin, BaseModalFormView):
|
||||
def _get_redirect_url(self, *args, **kwargs):
|
||||
return super()._get_redirect_url(*args, **kwargs) + "#related_data"
|
||||
|
||||
def _user_has_permission(self, user):
|
||||
def _user_has_permission(self, user, **kwargs):
|
||||
return user.is_default_user()
|
||||
|
||||
|
||||
@ -58,7 +58,7 @@ class AbstractGetDocumentView(LoginRequiredMixin, BaseView):
|
||||
def post(self, request, id: str, doc_id: str):
|
||||
return self.get(request, id, doc_id)
|
||||
|
||||
def _user_has_permission(self, user):
|
||||
def _user_has_permission(self, user, **kwargs):
|
||||
return user.is_default_user()
|
||||
|
||||
def _user_has_shared_access(self, user, **kwargs):
|
||||
@ -80,7 +80,7 @@ class AbstractRemoveDocumentView(LoginRequiredMixin, BaseModalFormView):
|
||||
def _get_redirect_url(self, *args, **kwargs):
|
||||
return super()._get_redirect_url(*args, **kwargs) + "#related_data"
|
||||
|
||||
def _user_has_permission(self, user):
|
||||
def _user_has_permission(self, user, **kwargs):
|
||||
return user.is_default_user()
|
||||
|
||||
def _get_msg_success(self, *args, **kwargs):
|
||||
@ -100,7 +100,7 @@ class AbstractEditDocumentView(LoginRequiredMixin, BaseModalFormView):
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
||||
def _user_has_permission(self, user):
|
||||
def _user_has_permission(self, user, **kwargs):
|
||||
return user.is_default_user()
|
||||
|
||||
def _get_redirect_url(self, *args, **kwargs):
|
||||
|
||||
@ -110,7 +110,7 @@ class GeomParcelsView(BaseView):
|
||||
def _user_has_shared_access(self, user, **kwargs):
|
||||
return True
|
||||
|
||||
def _user_has_permission(self, user):
|
||||
def _user_has_permission(self, user, **kwargs):
|
||||
return True
|
||||
|
||||
|
||||
@ -160,5 +160,5 @@ class GeomParcelsContentView(BaseView):
|
||||
def _user_has_shared_access(self, user, **kwargs):
|
||||
return True
|
||||
|
||||
def _user_has_permission(self, user):
|
||||
def _user_has_permission(self, user, **kwargs):
|
||||
return True
|
||||
|
||||
@ -74,7 +74,7 @@ class HomeView(LoginRequiredMixin, BaseView):
|
||||
context = BaseContext(request, additional_context).context
|
||||
return render(request, self._TEMPLATE, context)
|
||||
|
||||
def _user_has_permission(self, user):
|
||||
def _user_has_permission(self, user, **kwargs):
|
||||
# No specific permission needed for home view
|
||||
return True
|
||||
|
||||
|
||||
@ -46,5 +46,5 @@ class AbstractLogView(BaseView):
|
||||
obj = get_object_or_404(self._MODEL_CLS, id=obj_id)
|
||||
return obj.is_shared_with(user)
|
||||
|
||||
def _user_has_permission(self, user):
|
||||
def _user_has_permission(self, user, **kwargs):
|
||||
return user.is_default_user()
|
||||
|
||||
@ -15,7 +15,7 @@ class AbstractRecordView(BaseModalFormView):
|
||||
_FORM_CLS = RecordModalForm
|
||||
_MSG_SUCCESS = None
|
||||
|
||||
def _user_has_permission(self, user):
|
||||
def _user_has_permission(self, user, **kwargs):
|
||||
return user.is_ets_user()
|
||||
|
||||
def _get_msg_success(self, *args, **kwargs):
|
||||
|
||||
@ -16,7 +16,7 @@ class BaseRemoveModalFormView(BaseModalFormView):
|
||||
_MSG_SUCCESS = GENERIC_REMOVED_TEMPLATE
|
||||
_REDIRECT_URL = None
|
||||
|
||||
def _user_has_permission(self, user):
|
||||
def _user_has_permission(self, user, **kwargs):
|
||||
return user.is_default_user()
|
||||
|
||||
def _get_redirect_url(self, *args, **kwargs):
|
||||
|
||||
@ -97,7 +97,7 @@ class BaseReportView(BaseView):
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def _user_has_permission(self, user):
|
||||
def _user_has_permission(self, user, **kwargs):
|
||||
# Reports do not need specific permissions to be callable
|
||||
return True
|
||||
|
||||
|
||||
@ -20,7 +20,7 @@ class AbstractResubmissionView(LoginRequiredMixin, BaseModalFormView):
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
||||
def _user_has_permission(self, user):
|
||||
def _user_has_permission(self, user, **kwargs):
|
||||
return user.is_default_user()
|
||||
|
||||
def _check_for_recorded_instance(self, obj):
|
||||
|
||||
@ -60,7 +60,7 @@ class AbstractShareByTokenView(LoginRequiredMixin, BaseView):
|
||||
)
|
||||
return redirect("home")
|
||||
|
||||
def _user_has_permission(self, user):
|
||||
def _user_has_permission(self, user, **kwargs):
|
||||
# No permissions are needed to get shared access via token
|
||||
return True
|
||||
|
||||
@ -77,5 +77,5 @@ class AbstractShareFormView(LoginRequiredMixin, BaseModalFormView):
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
||||
def _user_has_permission(self, user):
|
||||
def _user_has_permission(self, user, **kwargs):
|
||||
return user.is_default_user()
|
||||
|
||||
@ -23,7 +23,7 @@ class AbstractCompensationStateView(LoginRequiredMixin, BaseModalFormView):
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
||||
def _user_has_permission(self, user):
|
||||
def _user_has_permission(self, user, **kwargs):
|
||||
return user.is_default_user()
|
||||
|
||||
def _get_redirect_url(self, *args, **kwargs):
|
||||
|
||||
12
user/urls.py
12
user/urls.py
@ -11,7 +11,9 @@ from user.autocomplete.share import ShareUserAutocomplete, ShareTeamAutocomplete
|
||||
from user.autocomplete.team import TeamAdminAutocomplete
|
||||
from user.views.api_token import APITokenView, new_api_token_view
|
||||
from user.views.propagate import PropagateUserView
|
||||
from user.views.views import *
|
||||
from user.views.teams import TeamIndexView, NewTeamView, TeamDetailModalView, EditTeamView, RemoveTeamView, \
|
||||
LeaveTeamView
|
||||
from user.views.users import UserDetailView, NotificationsView, ContactView
|
||||
|
||||
app_name = "user"
|
||||
urlpatterns = [
|
||||
@ -22,11 +24,11 @@ urlpatterns = [
|
||||
path("token/api/new", new_api_token_view, name="api-token-new"),
|
||||
path("contact/<id>", ContactView.as_view(), name="contact"),
|
||||
path("team/", TeamIndexView.as_view(), name="team-index"),
|
||||
path("team/new", new_team_view, name="team-new"),
|
||||
path("team/new", NewTeamView.as_view(), name="team-new"),
|
||||
path("team/<id>", TeamDetailModalView.as_view(), name="team-data"),
|
||||
path("team/<id>/edit", edit_team_view, name="team-edit"),
|
||||
path("team/<id>/remove", remove_team_view, name="team-remove"),
|
||||
path("team/<id>/leave", leave_team_view, name="team-leave"),
|
||||
path("team/<id>/edit", EditTeamView.as_view(), name="team-edit"),
|
||||
path("team/<id>/remove", RemoveTeamView.as_view(), name="team-remove"),
|
||||
path("team/<id>/leave", LeaveTeamView.as_view(), name="team-leave"),
|
||||
|
||||
# Autocomplete urls
|
||||
path("atcmplt/share/u", ShareUserAutocomplete.as_view(), name="share-user-autocomplete"),
|
||||
|
||||
105
user/views/teams.py
Normal file
105
user/views/teams.py
Normal file
@ -0,0 +1,105 @@
|
||||
"""
|
||||
Author: Michel Peltriaux
|
||||
Created on: 05.11.25
|
||||
|
||||
"""
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from django.http import Http404, HttpRequest
|
||||
from django.shortcuts import get_object_or_404, render
|
||||
from django.urls import reverse
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from konova.contexts import BaseContext
|
||||
from konova.utils.message_templates import TEAM_LEFT, TEAM_REMOVED, TEAM_EDITED, TEAM_ADDED
|
||||
from konova.views.base import BaseModalFormView
|
||||
from user.forms.modals.team import LeaveTeamModalForm, RemoveTeamModalForm, EditTeamModalForm, NewTeamModalForm
|
||||
from user.forms.team import TeamDataForm
|
||||
from user.models import Team
|
||||
from user.views.users import UserBaseView
|
||||
|
||||
|
||||
class TeamDetailModalView(LoginRequiredMixin, BaseModalFormView):
|
||||
_FORM_CLS = TeamDataForm
|
||||
_MODEL_CLS = Team
|
||||
|
||||
def _user_has_shared_access(self, user, **kwargs):
|
||||
# No specific constraints
|
||||
return True
|
||||
|
||||
def _user_has_permission(self, user, **kwargs):
|
||||
# No specific constraints
|
||||
return True
|
||||
|
||||
|
||||
class TeamIndexView(LoginRequiredMixin, UserBaseView):
|
||||
_TEMPLATE = "user/team/index.html"
|
||||
_TAB_TITLE = _("Teams")
|
||||
|
||||
def get(self, request: HttpRequest):
|
||||
user = request.user
|
||||
context = {
|
||||
"teams": user.shared_teams,
|
||||
"tab_title": self._TAB_TITLE,
|
||||
}
|
||||
context = BaseContext(request, context).context
|
||||
return render(request, self._TEMPLATE, context)
|
||||
|
||||
|
||||
class BaseTeamView(LoginRequiredMixin, BaseModalFormView):
|
||||
_REDIRECT_URL = "user:team-index"
|
||||
_MODEL_CLS = Team
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
||||
def _user_has_permission(self, user, **kwargs):
|
||||
# Nothing to check here - just pass the test
|
||||
return True
|
||||
|
||||
def _user_has_shared_access(self, user, **kwargs):
|
||||
# Nothing to check here - just pass the test
|
||||
return True
|
||||
|
||||
def _get_redirect_url(self, *args, **kwargs):
|
||||
return reverse(self._REDIRECT_URL)
|
||||
|
||||
class NewTeamView(BaseTeamView):
|
||||
_FORM_CLS = NewTeamModalForm
|
||||
_MSG_SUCCESS = TEAM_ADDED
|
||||
|
||||
class EditTeamView(BaseTeamView):
|
||||
_FORM_CLS = EditTeamModalForm
|
||||
_MSG_SUCCESS = TEAM_EDITED
|
||||
|
||||
def _user_has_permission(self, user, **kwargs):
|
||||
team = get_object_or_404(Team, id=kwargs.get("id"))
|
||||
user_is_admin = team.is_user_admin(user)
|
||||
if not user_is_admin:
|
||||
# If user is not an admin, we act as if there is no such team on the database
|
||||
raise Http404()
|
||||
return user_is_admin
|
||||
|
||||
|
||||
class RemoveTeamView(BaseTeamView):
|
||||
_FORM_CLS = RemoveTeamModalForm
|
||||
_MSG_SUCCESS = TEAM_REMOVED
|
||||
|
||||
def _user_has_permission(self, user, **kwargs):
|
||||
team_id = kwargs.get("id")
|
||||
team = get_object_or_404(Team, id=team_id)
|
||||
user_is_admin = team.is_user_admin(user)
|
||||
if not user_is_admin:
|
||||
raise Http404()
|
||||
return True
|
||||
|
||||
class LeaveTeamView(BaseTeamView):
|
||||
_FORM_CLS = LeaveTeamModalForm
|
||||
_MSG_SUCCESS = TEAM_LEFT
|
||||
|
||||
def _user_has_shared_access(self, user, **kwargs):
|
||||
team_id = kwargs.get("id")
|
||||
team = get_object_or_404(self._MODEL_CLS, id=team_id)
|
||||
is_user_team_member = team.users.filter(id=user.id).exists()
|
||||
if not is_user_team_member:
|
||||
raise Http404()
|
||||
return True
|
||||
81
user/views/users.py
Normal file
81
user/views/users.py
Normal file
@ -0,0 +1,81 @@
|
||||
from django.contrib import messages
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
|
||||
from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER
|
||||
from konova.views.base import BaseView, BaseModalFormView
|
||||
from user.forms.modals.user import UserContactForm
|
||||
from user.forms.user import UserNotificationForm
|
||||
from user.models import User
|
||||
from django.http import HttpRequest
|
||||
from django.shortcuts import render, redirect
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from konova.contexts import BaseContext
|
||||
|
||||
|
||||
class UserBaseView(BaseView):
|
||||
def _user_has_shared_access(self, user, **kwargs):
|
||||
return True
|
||||
|
||||
def _user_has_permission(self, user, **kwargs):
|
||||
return True
|
||||
|
||||
|
||||
class UserDetailView(LoginRequiredMixin, UserBaseView):
|
||||
_TEMPLATE = "user/index.html"
|
||||
_TAB_TITLE = _("User settings")
|
||||
|
||||
def get(self, request: HttpRequest):
|
||||
context = {
|
||||
"user": request.user,
|
||||
TAB_TITLE_IDENTIFIER: self._TAB_TITLE,
|
||||
}
|
||||
context = BaseContext(request, context).context
|
||||
return render(request, self._TEMPLATE, context)
|
||||
|
||||
|
||||
class NotificationsView(LoginRequiredMixin, UserBaseView):
|
||||
_TEMPLATE = "user/notifications.html"
|
||||
_TAB_TITLE = _("User notifications")
|
||||
|
||||
def get(self, request: HttpRequest):
|
||||
user = request.user
|
||||
form = UserNotificationForm(user=user, data=None)
|
||||
context = {
|
||||
"user": user,
|
||||
"form": form,
|
||||
TAB_TITLE_IDENTIFIER: self._TAB_TITLE,
|
||||
}
|
||||
context = BaseContext(request, context).context
|
||||
return render(request, self._TEMPLATE, context)
|
||||
|
||||
def post(self, request: HttpRequest):
|
||||
user = request.user
|
||||
form = UserNotificationForm(user=user, data=request.POST)
|
||||
if form.is_valid():
|
||||
form.save()
|
||||
messages.success(
|
||||
request,
|
||||
_("Notifications edited")
|
||||
)
|
||||
return redirect("user:detail")
|
||||
context = {
|
||||
"user": user,
|
||||
"form": form,
|
||||
TAB_TITLE_IDENTIFIER: self._TAB_TITLE,
|
||||
}
|
||||
context = BaseContext(request, context).context
|
||||
return render(request, self._TEMPLATE, context)
|
||||
|
||||
|
||||
class ContactView(LoginRequiredMixin, BaseModalFormView):
|
||||
_FORM_CLS = UserContactForm
|
||||
_MODEL_CLS = User
|
||||
|
||||
def _user_has_shared_access(self, user, **kwargs):
|
||||
# No specific constraints
|
||||
return True
|
||||
|
||||
def _user_has_permission(self, user, **kwargs):
|
||||
# No specific constraints
|
||||
return True
|
||||
@ -1,206 +0,0 @@
|
||||
from django.contrib import messages
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from django.urls import reverse
|
||||
|
||||
from konova.sub_settings.context_settings import TAB_TITLE_IDENTIFIER
|
||||
from konova.views.base import BaseView, BaseModalFormView
|
||||
from user.forms.modals.team import NewTeamModalForm, EditTeamModalForm, RemoveTeamModalForm, LeaveTeamModalForm
|
||||
from user.forms.modals.user import UserContactForm
|
||||
from user.forms.team import TeamDataForm
|
||||
from user.forms.user import UserNotificationForm
|
||||
from user.models import User, Team
|
||||
from django.http import HttpRequest, Http404
|
||||
from django.shortcuts import render, redirect, get_object_or_404
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from konova.contexts import BaseContext
|
||||
from konova.decorators import login_required_modal
|
||||
|
||||
|
||||
class UserBaseView(BaseView):
|
||||
def _user_has_shared_access(self, user, **kwargs):
|
||||
return True
|
||||
|
||||
def _user_has_permission(self, user):
|
||||
return True
|
||||
|
||||
|
||||
class UserDetailView(LoginRequiredMixin, UserBaseView):
|
||||
_TEMPLATE = "user/index.html"
|
||||
_TAB_TITLE = _("User settings")
|
||||
|
||||
def get(self, request: HttpRequest):
|
||||
context = {
|
||||
"user": request.user,
|
||||
TAB_TITLE_IDENTIFIER: self._TAB_TITLE,
|
||||
}
|
||||
context = BaseContext(request, context).context
|
||||
return render(request, self._TEMPLATE, context)
|
||||
|
||||
|
||||
class NotificationsView(LoginRequiredMixin, UserBaseView):
|
||||
_TEMPLATE = "user/notifications.html"
|
||||
_TAB_TITLE = _("User notifications")
|
||||
|
||||
def get(self, request: HttpRequest):
|
||||
user = request.user
|
||||
form = UserNotificationForm(user=user, data=None)
|
||||
context = {
|
||||
"user": user,
|
||||
"form": form,
|
||||
TAB_TITLE_IDENTIFIER: self._TAB_TITLE,
|
||||
}
|
||||
context = BaseContext(request, context).context
|
||||
return render(request, self._TEMPLATE, context)
|
||||
|
||||
def post(self, request: HttpRequest):
|
||||
user = request.user
|
||||
form = UserNotificationForm(user=user, data=request.POST)
|
||||
if form.is_valid():
|
||||
form.save()
|
||||
messages.success(
|
||||
request,
|
||||
_("Notifications edited")
|
||||
)
|
||||
return redirect("user:detail")
|
||||
context = {
|
||||
"user": user,
|
||||
"form": form,
|
||||
TAB_TITLE_IDENTIFIER: self._TAB_TITLE,
|
||||
}
|
||||
context = BaseContext(request, context).context
|
||||
return render(request, self._TEMPLATE, context)
|
||||
|
||||
|
||||
class ContactView(LoginRequiredMixin, BaseModalFormView):
|
||||
def get(self, request: HttpRequest, id: str):
|
||||
""" Renders contact modal view of a users contact data
|
||||
|
||||
Args:
|
||||
request (HttpRequest): The incoming request
|
||||
id (str): The user's id
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
user = get_object_or_404(User, id=id)
|
||||
form = UserContactForm(request.POST or None, instance=user, request=request)
|
||||
context = {
|
||||
"form": form,
|
||||
}
|
||||
context = BaseContext(request, context).context
|
||||
return render(request, self._TEMPLATE, context)
|
||||
|
||||
def _user_has_shared_access(self, user, **kwargs):
|
||||
# No specific constraints
|
||||
return True
|
||||
|
||||
def _user_has_permission(self, user):
|
||||
# No specific constraints
|
||||
return True
|
||||
|
||||
|
||||
class TeamDetailModalView(LoginRequiredMixin, BaseModalFormView):
|
||||
def get(self, request: HttpRequest, id: str):
|
||||
""" Renders team data
|
||||
|
||||
Args:
|
||||
request (HttpRequest): The incoming request
|
||||
id (str): The team's id
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
team = get_object_or_404(Team, id=id)
|
||||
form = TeamDataForm(request.POST or None, instance=team, request=request)
|
||||
context = {
|
||||
"form": form,
|
||||
}
|
||||
context = BaseContext(request, context).context
|
||||
return render(request, self._TEMPLATE, context)
|
||||
|
||||
def _user_has_shared_access(self, user, **kwargs):
|
||||
# No specific constraints
|
||||
return True
|
||||
|
||||
def _user_has_permission(self, user):
|
||||
# No specific constraints
|
||||
return True
|
||||
|
||||
|
||||
class TeamIndexView(LoginRequiredMixin, UserBaseView):
|
||||
_TEMPLATE = "user/team/index.html"
|
||||
_TAB_TITLE = _("Teams")
|
||||
|
||||
def get(self, request: HttpRequest):
|
||||
user = request.user
|
||||
context = {
|
||||
"teams": user.shared_teams,
|
||||
"tab_title": self._TAB_TITLE,
|
||||
}
|
||||
context = BaseContext(request, context).context
|
||||
return render(request, self._TEMPLATE, context)
|
||||
|
||||
|
||||
@login_required_modal
|
||||
@login_required
|
||||
def new_team_view(request: HttpRequest):
|
||||
form = NewTeamModalForm(request.POST or None, request=request)
|
||||
return form.process_request(
|
||||
request,
|
||||
_("New team added"),
|
||||
redirect_url=reverse("user:team-index")
|
||||
)
|
||||
|
||||
|
||||
@login_required_modal
|
||||
@login_required
|
||||
def edit_team_view(request: HttpRequest, id: str):
|
||||
team = get_object_or_404(Team, id=id)
|
||||
user_is_admin = team.is_user_admin(request.user)
|
||||
if not user_is_admin:
|
||||
raise Http404()
|
||||
form = EditTeamModalForm(request.POST or None, instance=team, request=request)
|
||||
return form.process_request(
|
||||
request,
|
||||
_("Team edited"),
|
||||
redirect_url=reverse("user:team-index")
|
||||
)
|
||||
|
||||
|
||||
@login_required_modal
|
||||
@login_required
|
||||
def remove_team_view(request: HttpRequest, id: str):
|
||||
team = get_object_or_404(Team, id=id)
|
||||
user_is_admin = team.is_user_admin(request.user)
|
||||
if not user_is_admin:
|
||||
raise Http404()
|
||||
form = RemoveTeamModalForm(request.POST or None, instance=team, request=request)
|
||||
return form.process_request(
|
||||
request,
|
||||
_("Team removed"),
|
||||
redirect_url=reverse("user:team-index")
|
||||
)
|
||||
|
||||
|
||||
@login_required_modal
|
||||
@login_required
|
||||
def leave_team_view(request: HttpRequest, id: str):
|
||||
team = get_object_or_404(Team, id=id)
|
||||
user = request.user
|
||||
|
||||
is_user_team_member = team.users.filter(id=user.id).exists()
|
||||
if not is_user_team_member:
|
||||
messages.info(
|
||||
request,
|
||||
_("You are not a member of this team")
|
||||
)
|
||||
return redirect("user:team-index")
|
||||
|
||||
form = LeaveTeamModalForm(request.POST or None, instance=team, request=request)
|
||||
return form.process_request(
|
||||
request,
|
||||
_("Left Team"),
|
||||
redirect_url=reverse("user:team-index")
|
||||
)
|
||||
Loading…
x
Reference in New Issue
Block a user