Compare commits

...

3 Commits

Author SHA1 Message Date
mipel
f58650effa Default group required
* adds access checks depending on the current group confgiguration of a user
* removes buttons for adding/editing or removing data if default group is not set for a user
* removes buttons for adding/removing related data in unshared interventions
* removes shared-user setting from share menu of an intervention if user is not zb or ets
* renames has_access() from intervention Model into is_shared_with() for more clarity
* fixes bug in group check in_group() from utils.py
2021-08-02 16:23:29 +02:00
mipel
63b2d3ef66 Compensation enhancements
* compensations won't be listed in index table if related intervention has been deleted
* adds functionality for intervention remove button
* adds outcommented code
* adds configurable redirect_url for RemoveModalForm's process_request method
* adds translation
2021-08-02 15:39:33 +02:00
mipel
d203046666 Intervention detail view
* adds overview of shared with users
2021-08-02 14:56:08 +02:00
14 changed files with 87 additions and 53 deletions

View File

@ -26,9 +26,9 @@ def index_view(request: HttpRequest):
A rendered view A rendered view
""" """
template = "generic_index.html" template = "generic_index.html"
user = request.user
compensations = Compensation.objects.filter( compensations = Compensation.objects.filter(
deleted=None, deleted=None, # only show those which are not deleted individually
intervention__deleted=None, # and don't show the ones whose intervention has been deleted
) )
table = CompensationTable( table = CompensationTable(
request=request, request=request,
@ -42,12 +42,14 @@ def index_view(request: HttpRequest):
@login_required @login_required
@default_group_required
def new_view(request: HttpRequest): def new_view(request: HttpRequest):
# ToDo # ToDo
pass pass
@login_required @login_required
@default_group_required
def edit_view(request: HttpRequest, id: str): def edit_view(request: HttpRequest, id: str):
# ToDo # ToDo
pass pass
@ -106,12 +108,14 @@ def account_index_view(request: HttpRequest):
@login_required @login_required
@default_group_required
def account_new_view(request: HttpRequest): def account_new_view(request: HttpRequest):
# ToDo # ToDo
pass pass
@login_required @login_required
@default_group_required
def account_edit_view(request: HttpRequest, id: str): def account_edit_view(request: HttpRequest, id: str):
# ToDo # ToDo
pass pass
@ -130,6 +134,7 @@ def account_remove_view(request: HttpRequest, id: str):
@login_required @login_required
@default_group_required
def new_payment_view(request: HttpRequest, intervention_id: str): def new_payment_view(request: HttpRequest, intervention_id: str):
""" Renders a modal view for adding new payments """ Renders a modal view for adding new payments
@ -168,6 +173,7 @@ def new_payment_view(request: HttpRequest, intervention_id: str):
@login_required @login_required
@default_group_required
def payment_remove_view(request: HttpRequest, id: str): def payment_remove_view(request: HttpRequest, id: str):
""" Renders a modal view for removing payments """ Renders a modal view for removing payments
@ -187,6 +193,7 @@ def payment_remove_view(request: HttpRequest, id: str):
@login_required @login_required
@default_group_required
def withdraw_remove_view(request: HttpRequest, id: str, withdraw_id: str): def withdraw_remove_view(request: HttpRequest, id: str, withdraw_id: str):
""" Renders a modal view for removing withdraws """ Renders a modal view for removing withdraws

View File

@ -18,7 +18,8 @@ from intervention.models import Intervention
from konova.enums import UserActionLogEntryEnum from konova.enums import UserActionLogEntryEnum
from konova.forms import BaseForm, BaseModalForm from konova.forms import BaseForm, BaseModalForm
from konova.models import Document from konova.models import Document
from konova.settings import DEFAULT_LAT, DEFAULT_LON, DEFAULT_ZOOM from konova.settings import DEFAULT_LAT, DEFAULT_LON, DEFAULT_ZOOM, ZB_GROUP, ETS_GROUP
from konova.utils.user_checks import in_group
from organisation.models import Organisation from organisation.models import Organisation
from user.models import UserActionLogEntry from user.models import UserActionLogEntry
@ -293,18 +294,22 @@ class ShareInterventionForm(BaseModalForm):
) )
# Initialize users field # Initialize users field
users = self.instance.users.all() # Remove field if user is not in registration or conservation group
choices = [] if not in_group(self.request.user, ZB_GROUP) and not in_group(self.request.user, ETS_GROUP):
for n in users: del self.fields["users"]
choices.append( else:
(n.id, n.username) users = self.instance.users.all()
choices = []
for n in users:
choices.append(
(n.id, n.username)
)
self.fields["users"].choices = choices
u_ids = list(users.values_list("id", flat=True))
self.initialize_form_field(
"users",
u_ids
) )
self.fields["users"].choices = choices
u_ids = list(users.values_list("id", flat=True))
self.initialize_form_field(
"users",
u_ids
)
def save(self): def save(self):
accessing_users = User.objects.filter( accessing_users = User.objects.filter(

View File

@ -143,13 +143,9 @@ class Intervention(BaseObject):
) )
for com in coms: for com in coms:
com.deleted = action com.deleted = action
#com.deleted_on = _now
#com.deleted_by = _user
com.save() com.save()
self.deleted = action self.deleted = action
#self.deleted_on = _now
#self.deleted_by = _user
self.save() self.save()
@staticmethod @staticmethod
@ -211,7 +207,7 @@ class Intervention(BaseObject):
self.identifier = new_id self.identifier = new_id
super().save(*args, **kwargs) super().save(*args, **kwargs)
def has_access(self, user: User): def is_shared_with(self, user: User):
""" Access check """ Access check
Checks whether a given user has access to this intervention Checks whether a given user has access to this intervention

View File

@ -10,12 +10,14 @@
</div> </div>
<div class="col-sm-6"> <div class="col-sm-6">
<div class="d-flex justify-content-end"> <div class="d-flex justify-content-end">
{% if is_default_member and has_access %}
<a href="{% url 'compensation:new' %}" title="{% trans 'Add new compensation' %}"> <a href="{% url 'compensation:new' %}" title="{% trans 'Add new compensation' %}">
<button class="btn btn-outline-default"> <button class="btn btn-outline-default">
{% fa5_icon 'plus' %} {% fa5_icon 'plus' %}
{% fa5_icon 'leaf' %} {% fa5_icon 'leaf' %}
</button> </button>
</a> </a>
{% endif %}
</div> </div>
</div> </div>
</div> </div>
@ -45,9 +47,11 @@
</td> </td>
<td class="align-middle">{{ comp.title }}</td> <td class="align-middle">{{ comp.title }}</td>
<td> <td>
{% if is_default_member and has_access %}
<button data-form-url="{% url 'compensation:remove' comp.id %}" class="btn btn-default btn-modal" title="{% trans 'Remove compensation' %}"> <button data-form-url="{% url 'compensation:remove' comp.id %}" class="btn btn-default btn-modal" title="{% trans 'Remove compensation' %}">
{% fa5_icon 'trash' %} {% fa5_icon 'trash' %}
</button> </button>
{% endif %}
</td> </td>
</tr> </tr>
{% endfor %} {% endfor %}

View File

@ -10,10 +10,12 @@
</div> </div>
<div class="col-sm-6"> <div class="col-sm-6">
<div class="d-flex justify-content-end"> <div class="d-flex justify-content-end">
{% if is_default_member and has_access %}
<button class="btn btn-outline-default btn-modal" data-form-url="{% url 'intervention:new-doc' intervention.id %}" title="{% trans 'Add new document' %}"> <button class="btn btn-outline-default btn-modal" data-form-url="{% url 'intervention:new-doc' intervention.id %}" title="{% trans 'Add new document' %}">
{% fa5_icon 'plus' %} {% fa5_icon 'plus' %}
{% fa5_icon 'file' %} {% fa5_icon 'file' %}
</button> </button>
{% endif %}
</div> </div>
</div> </div>
</div> </div>
@ -43,9 +45,11 @@
</td> </td>
<td class="align-middle">{{ doc.comment }}</td> <td class="align-middle">{{ doc.comment }}</td>
<td> <td>
{% if is_default_member and has_access %}
<button data-form-url="{% url 'doc-remove' doc.id %}" class="btn btn-default btn-modal" title="{% trans 'Remove document' %}"> <button data-form-url="{% url 'doc-remove' doc.id %}" class="btn btn-default btn-modal" title="{% trans 'Remove document' %}">
{% fa5_icon 'trash' %} {% fa5_icon 'trash' %}
</button> </button>
{% endif %}
</td> </td>
</tr> </tr>
{% endfor %} {% endfor %}

View File

@ -10,12 +10,14 @@
</div> </div>
<div class="col-sm-6"> <div class="col-sm-6">
<div class="d-flex justify-content-end"> <div class="d-flex justify-content-end">
{% if is_default_member and has_access %}
<a href="{% url 'compensation:new' %}" title="{% trans 'Add new withdraw' %}"> <a href="{% url 'compensation:new' %}" title="{% trans 'Add new withdraw' %}">
<button class="btn btn-outline-default"> <button class="btn btn-outline-default">
{% fa5_icon 'plus' %} {% fa5_icon 'plus' %}
{% fa5_icon 'tree' %} {% fa5_icon 'tree' %}
</button> </button>
</a> </a>
{% endif %}
</div> </div>
</div> </div>
</div> </div>
@ -45,9 +47,11 @@
</td> </td>
<td class="align-middle">{{ withdraw.amount|floatformat:2 }} %</td> <td class="align-middle">{{ withdraw.amount|floatformat:2 }} %</td>
<td> <td>
{% if is_default_member and has_access %}
<button data-form-url="{% url 'compensation:withdraw-remove' withdraw.account.id withdraw.id %}" class="btn btn-default btn-modal" title="{% trans 'Remove Withdraw' %}"> <button data-form-url="{% url 'compensation:withdraw-remove' withdraw.account.id withdraw.id %}" class="btn btn-default btn-modal" title="{% trans 'Remove Withdraw' %}">
{% fa5_icon 'trash' %} {% fa5_icon 'trash' %}
</button> </button>
{% endif %}
</td> </td>
</tr> </tr>
{% endfor %} {% endfor %}

View File

@ -10,10 +10,12 @@
</div> </div>
<div class="col-sm-6"> <div class="col-sm-6">
<div class="d-flex justify-content-end"> <div class="d-flex justify-content-end">
{% if is_default_member and has_access %}
<button class="btn btn-outline-default btn-modal" data-form-url="{% url 'compensation:pay-new' intervention.id %}" title="{% trans 'Add new payment' %}"> <button class="btn btn-outline-default btn-modal" data-form-url="{% url 'compensation:pay-new' intervention.id %}" title="{% trans 'Add new payment' %}">
{% fa5_icon 'plus' %} {% fa5_icon 'plus' %}
{% fa5_icon 'money-bill-wave' %} {% fa5_icon 'money-bill-wave' %}
</button> </button>
{% endif %}
</div> </div>
</div> </div>
</div> </div>
@ -47,9 +49,11 @@
<td class="align-middle">{{ pay.due_on }}</td> <td class="align-middle">{{ pay.due_on }}</td>
<td class="align-middle">{{ pay.comment }}</td> <td class="align-middle">{{ pay.comment }}</td>
<td> <td>
{% if is_default_member and has_access %}
<button data-form-url="{% url 'compensation:pay-remove' pay.id %}" class="btn btn-default btn-modal" title="{% trans 'Remove payment' %}"> <button data-form-url="{% url 'compensation:pay-remove' pay.id %}" class="btn btn-default btn-modal" title="{% trans 'Remove payment' %}">
{% fa5_icon 'trash' %} {% fa5_icon 'trash' %}
</button> </button>
{% endif %}
</td> </td>
</tr> </tr>
{% endfor %} {% endfor %}

View File

@ -27,26 +27,30 @@
<button class="btn btn-default btn-modal mr-2" title="{% trans 'Share' %}" data-form-url="{% url 'intervention:share-create' intervention.id %}"> <button class="btn btn-default btn-modal mr-2" title="{% trans 'Share' %}" data-form-url="{% url 'intervention:share-create' intervention.id %}">
{% fa5_icon 'share-alt' %} {% fa5_icon 'share-alt' %}
</button> </button>
{% if is_zb_member %}
<a href="{% url 'home' %}" class="mr-2"> <a href="{% url 'home' %}" class="mr-2">
<button class="btn btn-default" title="{% trans 'Run check' %}"> <button class="btn btn-default" title="{% trans 'Run check' %}">
{% fa5_icon 'star' %} {% fa5_icon 'star' %}
</button> </button>
</a> </a>
{% endif %}
{% if is_ets_member %}
<a href="{% url 'home' %}" class="mr-2"> <a href="{% url 'home' %}" class="mr-2">
<button class="btn btn-default" title="{% trans 'Record' %}"> <button class="btn btn-default" title="{% trans 'Record' %}">
{% fa5_icon 'bookmark' %} {% fa5_icon 'bookmark' %}
</button> </button>
</a> </a>
{% endif %}
{% if is_default_member %}
<a href="{% url 'home' %}" class="mr-2"> <a href="{% url 'home' %}" class="mr-2">
<button class="btn btn-default" title="{% trans 'Edit' %}"> <button class="btn btn-default" title="{% trans 'Edit' %}">
{% fa5_icon 'edit' %} {% fa5_icon 'edit' %}
</button> </button>
</a> </a>
<a href="{% url 'home' %}" class="mr-2"> <button class="btn btn-default btn-modal" data-form-url="{% url 'intervention:remove' intervention.id %}" title="{% trans 'Delete' %}">
<button class="btn btn-default" title="{% trans 'Delete' %}"> {% fa5_icon 'trash' %}
{% fa5_icon 'trash' %} </button>
</button> {% endif %}
</a>
{% endif %} {% endif %}
</div> </div>
</div> </div>
@ -134,6 +138,14 @@
{% endwith %} {% endwith %}
</td> </td>
</tr> </tr>
<tr>
<th scope="row">{% trans 'Shared with' %}</th>
<td class="align-middle">
{% for user in intervention.users.all %}
{% include 'user/includes/contact_modal_button.html' %}
{% endfor %}
</td>
</tr>
</table> </table>
</div> </div>
</div> </div>

View File

@ -9,8 +9,9 @@ from intervention.models import Intervention
from intervention.tables import InterventionTable from intervention.tables import InterventionTable
from konova.contexts import BaseContext from konova.contexts import BaseContext
from konova.decorators import * from konova.decorators import *
from konova.forms import RemoveForm, SimpleGeomForm, NewDocumentForm from konova.forms import SimpleGeomForm, NewDocumentForm, RemoveModalForm
from konova.utils.message_templates import FORM_INVALID from konova.utils.message_templates import FORM_INVALID
from konova.utils.user_checks import in_group
@login_required @login_required
@ -45,6 +46,7 @@ def index_view(request: HttpRequest):
@login_required @login_required
@default_group_required
def new_view(request: HttpRequest): def new_view(request: HttpRequest):
""" """
Renders a view for a new intervention creation Renders a view for a new intervention creation
@ -130,7 +132,8 @@ def open_view(request: HttpRequest, id: str):
compensations = intervention.compensations.filter( compensations = intervention.compensations.filter(
deleted=None, deleted=None,
) )
has_access = intervention.has_access(user=request.user) _user = request.user
is_data_shared = intervention.is_shared_with(user=_user)
geom_form = SimpleGeomForm( geom_form = SimpleGeomForm(
instance=intervention instance=intervention
@ -139,11 +142,14 @@ def open_view(request: HttpRequest, id: str):
context = { context = {
"intervention": intervention, "intervention": intervention,
"compensations": compensations, "compensations": compensations,
"has_access": has_access, "has_access": is_data_shared,
"geom_form": geom_form, "geom_form": geom_form,
"is_default_member": in_group(_user, _(DEFAULT_GROUP)),
"is_zb_member": in_group(_user, _(ZB_GROUP)),
"is_ets_member": in_group(_user, _(ETS_GROUP)),
} }
if not has_access: if not is_data_shared:
messages.info(request, _("Remember: This data has not been shared with you, yet. This means you can only read but can not edit or perform any actions like running a check or recording.")) messages.info(request, _("Remember: This data has not been shared with you, yet. This means you can only read but can not edit or perform any actions like running a check or recording."))
context = BaseContext(request, context).context context = BaseContext(request, context).context
@ -180,6 +186,7 @@ def edit_view(request: HttpRequest, id: str):
@login_required @login_required
@default_group_required
def remove_view(request: HttpRequest, id: str): def remove_view(request: HttpRequest, id: str):
""" Renders a remove view for this intervention """ Renders a remove view for this intervention
@ -190,21 +197,14 @@ def remove_view(request: HttpRequest, id: str):
Returns: Returns:
""" """
template = "konova/form.html"
obj = Intervention.objects.get(id=id) obj = Intervention.objects.get(id=id)
identifier = obj.identifier
# ToDo form = RemoveModalForm(request.POST or None, instance=obj, user=request.user)
return form.process_request(
form = RemoveForm( request,
object_to_remove=obj, _("{} removed").format(identifier),
remove_post_url=reverse("intervention:remove", args=(id,)), redirect_url=reverse("intervention:index")
cancel_url=reverse("intervention:index"),
) )
context = {
"form": form,
}
context = BaseContext(request, context).context
return render(request, template, context)
@login_required @login_required
@ -226,7 +226,7 @@ def share_view(request: HttpRequest, id: str, token: str):
# Check tokens # Check tokens
if intervention.access_token == token: if intervention.access_token == token:
# Send different messages in case user has already been added to list of sharing users # Send different messages in case user has already been added to list of sharing users
if intervention.has_access(user): if intervention.is_shared_with(user):
messages.info( messages.info(
request, request,
_("{} has already been shared with you").format(intervention.identifier) _("{} has already been shared with you").format(intervention.identifier)
@ -258,7 +258,6 @@ def create_share_view(request: HttpRequest, id: str):
Returns: Returns:
""" """
user = request.user
intervention = get_object_or_404(Intervention, id=id) intervention = get_object_or_404(Intervention, id=id)
form = ShareInterventionForm(request.POST or None, instance=intervention, request=request) form = ShareInterventionForm(request.POST or None, instance=intervention, request=request)
if request.method == "POST": if request.method == "POST":

View File

@ -200,7 +200,7 @@ class RemoveModalForm(BaseModalForm):
# If the class does not provide restorable delete functionality, we must delete the entry finally # If the class does not provide restorable delete functionality, we must delete the entry finally
self.instance.delete() self.instance.delete()
def process_request(self, request: HttpRequest, msg_success: str = _("Object removed"), msg_error: str = FORM_INVALID): def process_request(self, request: HttpRequest, msg_success: str = _("Object removed"), msg_error: str = FORM_INVALID, redirect_url: str = None):
""" Generic processing of request """ Generic processing of request
Wraps the request processing logic, so we don't need the same code everywhere a RemoveModalForm is being used Wraps the request processing logic, so we don't need the same code everywhere a RemoveModalForm is being used
@ -213,6 +213,7 @@ class RemoveModalForm(BaseModalForm):
Returns: Returns:
""" """
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 request.method == "POST":
if self.is_valid(): if self.is_valid():
@ -221,13 +222,13 @@ class RemoveModalForm(BaseModalForm):
request, request,
msg_success msg_success
) )
return redirect(request.META.get("HTTP_REFERER", "home")) return redirect(redirect_url)
else: else:
messages.info( messages.info(
request, request,
msg_error msg_error
) )
return redirect(request.META.get("HTTP_REFERER", "home")) return redirect(redirect_url)
elif request.method == "GET": elif request.method == "GET":
context = { context = {
"form": self, "form": self,

View File

@ -8,7 +8,6 @@ Created on: 17.11.20
import os import os
import uuid import uuid
from django.contrib.auth.models import User
from django.contrib.gis.db.models import MultiPolygonField from django.contrib.gis.db.models import MultiPolygonField
from django.db import models from django.db import models
@ -35,8 +34,6 @@ class BaseResource(UuidModel):
A basic resource model, which defines attributes for every derived model A basic resource model, which defines attributes for every derived model
""" """
created = models.ForeignKey(UserActionLogEntry, on_delete=models.SET_NULL, null=True, blank=True, related_name='+') created = models.ForeignKey(UserActionLogEntry, on_delete=models.SET_NULL, null=True, blank=True, related_name='+')
#created_on = models.DateTimeField(auto_now_add=True, null=True)
#created_by = models.ForeignKey(User, null=True, on_delete=models.SET_NULL, related_name="+")
class Meta: class Meta:
abstract = True abstract = True
@ -51,8 +48,6 @@ class BaseObject(BaseResource):
identifier = models.CharField(max_length=1000, null=True, blank=True) identifier = models.CharField(max_length=1000, null=True, blank=True)
title = models.CharField(max_length=1000, null=True, blank=True) title = models.CharField(max_length=1000, null=True, blank=True)
deleted = models.ForeignKey(UserActionLogEntry, on_delete=models.SET_NULL, null=True, blank=True, related_name='+') deleted = models.ForeignKey(UserActionLogEntry, on_delete=models.SET_NULL, null=True, blank=True, related_name='+')
#deleted_on = models.DateTimeField(null=True, blank=True)
#deleted_by = models.ForeignKey(User, null=True, blank=True, on_delete=models.SET_NULL, related_name="+")
comment = models.TextField(null=True, blank=True) comment = models.TextField(null=True, blank=True)
class Meta: class Meta:

View File

@ -5,6 +5,7 @@ Contact: michel.peltriaux@sgdnord.rlp.de
Created on: 02.07.21 Created on: 02.07.21
""" """
from django.utils.translation import gettext_lazy as _
from django.contrib.auth.models import User from django.contrib.auth.models import User
@ -18,4 +19,6 @@ def in_group(user: User, group: str) -> bool:
Returns: Returns:
bool bool
""" """
return group in user.groups.values("name") return user.groups.filter(
name=_(group)
)

Binary file not shown.

View File

@ -324,7 +324,7 @@ msgstr "Ökokonto Abbuchungen"
#: intervention/templates/intervention/detail/includes/eco-account-withdraws.html:13 #: intervention/templates/intervention/detail/includes/eco-account-withdraws.html:13
msgid "Add new withdraw" msgid "Add new withdraw"
msgstr "" msgstr "Neue Abbuchung hinzufügen"
#: intervention/templates/intervention/detail/includes/eco-account-withdraws.html:28 #: intervention/templates/intervention/detail/includes/eco-account-withdraws.html:28
msgid "Account Identifier" msgid "Account Identifier"