Add functionality to delete all leave requests for a worker with permission checks
This commit is contained in:
@@ -14,7 +14,14 @@
|
|||||||
<p style="margin-top:0.5rem"><a class="button is-light" hx-get="{% url 'rota:worker_settings_token' token %}" hx-target="#modal" hx-swap="innerHTML">Update your settings</a></p>
|
<p style="margin-top:0.5rem"><a class="button is-light" hx-get="{% url 'rota:worker_settings_token' token %}" hx-target="#modal" hx-swap="innerHTML">Update your settings</a></p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
{# Navigation back to the first rota this worker belongs to (if available) #}
|
||||||
|
{% if worker %}
|
||||||
|
{% with back_rota=worker.rotas.all.0 %}
|
||||||
|
{% if back_rota %}
|
||||||
|
<p style="margin-top:0.5rem"><a class="button" href="{% url 'rota:rota_detail' back_rota.id %}">← Back to rota: {{ back_rota.name }}</a></p>
|
||||||
|
{% endif %}
|
||||||
|
{% endwith %}
|
||||||
|
{% endif %}
|
||||||
<form method="post">
|
<form method="post">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
{% if token %}
|
{% if token %}
|
||||||
|
|||||||
@@ -7,11 +7,14 @@
|
|||||||
<!-- Rely on the server to return an out-of-band (OOB) swap that updates
|
<!-- Rely on the server to return an out-of-band (OOB) swap that updates
|
||||||
the #leave-list. Prevent htmx from attempting the main swap by
|
the #leave-list. Prevent htmx from attempting the main swap by
|
||||||
disabling the default swap for this request. -->
|
disabling the default swap for this request. -->
|
||||||
<form method="post" hx-post="{% url 'rota:leave_delete' l.id %}" hx-swap="none" style="display:inline;margin-left:0.5rem">
|
{% comment %}Only show delete control when the current request is allowed to delete:{% endcomment %}
|
||||||
|
{% if request.user.is_staff or request.user.is_authenticated and request.user.email and request.user.email == l.worker.email or token %}
|
||||||
|
<form method="post" action="{% url 'rota:leave_delete' l.id %}" hx-post="{% url 'rota:leave_delete' l.id %}" hx-swap="none" style="display:inline;margin-left:0.5rem">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
{% if token %}<input type="hidden" name="token" value="{{ token }}">{% endif %}
|
{% if token %}<input type="hidden" name="token" value="{{ token }}">{% endif %}
|
||||||
<button class="button is-small is-danger" type="submit" onclick="return confirm('Delete this leave request?');">Delete</button>
|
<button class="button is-small is-danger" type="submit" onclick="return confirm('Delete this leave request?');">Delete</button>
|
||||||
</form>
|
</form>
|
||||||
|
{% endif %}
|
||||||
</li>
|
</li>
|
||||||
{% empty %}
|
{% empty %}
|
||||||
<li>No leave recorded</li>
|
<li>No leave recorded</li>
|
||||||
|
|||||||
@@ -18,6 +18,13 @@
|
|||||||
<h1 class="title">{{ worker.name }}</h1>
|
<h1 class="title">{{ worker.name }}</h1>
|
||||||
<p class="subtitle">{{ worker.email }} - {{ worker.site }}</p>
|
<p class="subtitle">{{ worker.email }} - {{ worker.site }}</p>
|
||||||
|
|
||||||
|
{# Navigation: back to an assigned rota (show first assigned rota if any) #}
|
||||||
|
{% with back_rota=worker.rotas.all.0 %}
|
||||||
|
{% if back_rota %}
|
||||||
|
<p style="margin-top:0.5rem"><a class="button is-light" href="{% url 'rota:rota_detail' back_rota.id %}">← Back to rota: {{ back_rota.name }}</a></p>
|
||||||
|
{% endif %}
|
||||||
|
{% endwith %}
|
||||||
|
|
||||||
<div class="box">
|
<div class="box">
|
||||||
<h2 class="subtitle">Request leave</h2>
|
<h2 class="subtitle">Request leave</h2>
|
||||||
<form method="post">
|
<form method="post">
|
||||||
@@ -33,6 +40,13 @@
|
|||||||
|
|
||||||
<h2 class="subtitle">Existing leave</h2>
|
<h2 class="subtitle">Existing leave</h2>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
|
{# Delete-all control: only show to staff or the worker themselves #}
|
||||||
|
{% if request.user.is_staff or request.user.is_authenticated and request.user.email and request.user.email == worker.email %}
|
||||||
|
<form method="post" action="{% url 'rota:leave_delete_all' worker.id %}" hx-post="{% url 'rota:leave_delete_all' worker.id %}" hx-swap="none" style="display:inline;margin-bottom:0.5rem">
|
||||||
|
{% csrf_token %}
|
||||||
|
<button class="button is-small is-danger" type="submit" onclick="return confirm('Delete ALL leave requests for this worker?');">Delete all leave</button>
|
||||||
|
</form>
|
||||||
|
{% endif %}
|
||||||
{% include 'rota/partials/leave_list.html' with leaves=worker.leaves.all %}
|
{% include 'rota/partials/leave_list.html' with leaves=worker.leaves.all %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ urlpatterns = [
|
|||||||
path("leave/request/token/<uuid:token>/", views.request_leave, name="request_leave_token"),
|
path("leave/request/token/<uuid:token>/", views.request_leave, name="request_leave_token"),
|
||||||
path("worker/settings/token/<uuid:token>/", views.worker_settings_token, name="worker_settings_token"),
|
path("worker/settings/token/<uuid:token>/", views.worker_settings_token, name="worker_settings_token"),
|
||||||
path("leave/<int:leave_id>/delete/", views.leave_delete, name="leave_delete"),
|
path("leave/<int:leave_id>/delete/", views.leave_delete, name="leave_delete"),
|
||||||
|
path("worker/<int:worker_id>/leaves/delete_all/", views.leave_delete_all, name="leave_delete_all"),
|
||||||
path("worker/<int:worker_id>/regenerate_token/", views.regenerate_worker_token, name="regenerate_worker_token"),
|
path("worker/<int:worker_id>/regenerate_token/", views.regenerate_worker_token, name="regenerate_worker_token"),
|
||||||
path("run/<int:run_id>/", views.rota_run_detail, name="rota_run_detail"),
|
path("run/<int:run_id>/", views.rota_run_detail, name="rota_run_detail"),
|
||||||
path("run/<int:run_id>/export/html/", views.rota_run_export_html, name="rota_run_export_html"),
|
path("run/<int:run_id>/export/html/", views.rota_run_export_html, name="rota_run_export_html"),
|
||||||
|
|||||||
@@ -1062,6 +1062,52 @@ def leave_delete(request, leave_id):
|
|||||||
return response
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
@require_POST
|
||||||
|
def leave_delete_all(request, worker_id):
|
||||||
|
"""Delete all Leave records for a worker. Permissions same as `leave_delete`.
|
||||||
|
|
||||||
|
Returns an OOB swap updating `#leave-list` and triggers `leaveUpdated`.
|
||||||
|
"""
|
||||||
|
worker = get_object_or_404(Worker, pk=worker_id)
|
||||||
|
|
||||||
|
# permission checks (same as leave_delete)
|
||||||
|
allowed = False
|
||||||
|
token = request.POST.get('token') or request.GET.get('token')
|
||||||
|
try:
|
||||||
|
if request.user and request.user.is_authenticated and request.user.is_staff:
|
||||||
|
allowed = True
|
||||||
|
elif request.user and request.user.is_authenticated and getattr(request.user, 'email', None) and request.user.email == (worker.email or ''):
|
||||||
|
allowed = True
|
||||||
|
elif token:
|
||||||
|
try:
|
||||||
|
import uuid
|
||||||
|
|
||||||
|
token_uuid = uuid.UUID(token)
|
||||||
|
if worker.request_token == token_uuid:
|
||||||
|
allowed = True
|
||||||
|
except Exception:
|
||||||
|
allowed = False
|
||||||
|
except Exception:
|
||||||
|
allowed = False
|
||||||
|
|
||||||
|
if not allowed:
|
||||||
|
return HttpResponseBadRequest('Permission denied')
|
||||||
|
|
||||||
|
# delete all leaves for this worker
|
||||||
|
worker.leaves.all().delete()
|
||||||
|
|
||||||
|
# render updated leave list for the worker
|
||||||
|
leave_list_html = render_to_string(
|
||||||
|
"rota/partials/leave_list.html",
|
||||||
|
{"leaves": worker.leaves.all(), "token": token},
|
||||||
|
request=request,
|
||||||
|
)
|
||||||
|
resp = f'<div id="leave-list" hx-swap-oob="true">{leave_list_html}</div>'
|
||||||
|
response = HttpResponse(resp)
|
||||||
|
response["HX-Trigger"] = "leaveUpdated"
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
@require_POST
|
@require_POST
|
||||||
def rota_run_export_regenerate(request, run_id):
|
def rota_run_export_regenerate(request, run_id):
|
||||||
"""Regenerate the HTML export for a completed run and persist it on the RotaRun.
|
"""Regenerate the HTML export for a completed run and persist it on the RotaRun.
|
||||||
|
|||||||
Reference in New Issue
Block a user