Convert task_delete and task_done views from GET to POST

This commit is contained in:
Scot Hacker 2019-02-10 11:06:36 -08:00
parent 891148e496
commit 01cab7a82f
8 changed files with 109 additions and 52 deletions

View file

@ -168,6 +168,8 @@ The previous `tox` system was removed with the v2 release, since we no longer ai
# Version History # Version History
**2.2.1** Convert task delete and toggle_done views to POST only
**2.2.0** Re-instate enforcement of TODO_STAFF_ONLY setting **2.2.0** Re-instate enforcement of TODO_STAFF_ONLY setting
**2.1.1** Correct Python version requirement in documentation to Python 3.6 **2.1.1** Correct Python version requirement in documentation to Python 3.6

View file

@ -1,7 +1,7 @@
""" """
A multi-user, multi-group task management and assignment system for Django. A multi-user, multi-group task management and assignment system for Django.
""" """
__version__ = '2.2.0' __version__ = '2.2.1'
__author__ = 'Scot Hacker' __author__ = 'Scot Hacker'
__email__ = 'shacker@birdhouse.org' __email__ = 'shacker@birdhouse.org'

View file

@ -52,13 +52,16 @@
{% if task.assigned_to %}{{ task.assigned_to }}{% else %}Anyone{% endif %} {% if task.assigned_to %}{{ task.assigned_to }}{% else %}Anyone{% endif %}
</td> </td>
<td> <td>
<a href="{% url "todo:task_toggle_done" task.id %}" class="btn btn-info btn-sm"> <form method="post" action="{% url "todo:task_toggle_done" task.id %}" role="form">
{% csrf_token %}
<button class="btn btn-info btn-sm" type="submit" name="toggle_done">
{% if view_completed %} {% if view_completed %}
Not Done Not Done
{% else %} {% else %}
Done Done
{% endif %} {% endif %}
</a> </button>
</form>
</td> </td>
</tr> </tr>
{% endfor %} {% endfor %}

View file

@ -12,21 +12,40 @@
</div> </div>
<div class="col-sm-4"> <div class="col-sm-4">
<ul class="list-group"> <div class="mb-2">
<li class="list-group-item">
<form action="" method="post"> <button
class="btn btn-sm btn-primary"
id="EditTaskButton"
type="button"
data-toggle="collapse"
data-target="#AddEditTask"
>
Edit Task
</button>
<form method="post" action="{% url "todo:task_toggle_done" task.id %}" role="form" style="display:inline;">
{% csrf_token %} {% csrf_token %}
<div style="display:inline;">
<button class="btn btn-sm btn-primary" id="EditTaskButton" type="button" <button class="btn btn-info btn-sm" type="submit" name="toggle_done">
data-toggle="collapse" data-target="#AddEditTask">Edit Task</button>
<a href="{% url "todo:task_toggle_done" task.id %}" class="btn btn-info btn-sm">
{% if task.completed %} Mark Not Done {% else %} Mark Done {% endif %} {% if task.completed %} Mark Not Done {% else %} Mark Done {% endif %}
</a> </button>
</div>
<a href="{% url "todo:delete_task" task.id %}" class="btn btn-danger btn-sm">Delete</a>
</form> </form>
</li>
<form method="post" action="{% url "todo:delete_task" task.id %}" role="form" style="display:inline;">
{% csrf_token %}
<div style="display:inline;">
<button class="btn btn-danger btn-sm" type="submit" name="submit_delete">
Delete
</button>
</div>
</form>
</div>
<ul class="list-group">
<li class="list-group-item"> <li class="list-group-item">
<strong>Assigned to:</strong> <strong>Assigned to:</strong>
{% if task.assigned_to %} {{ task.assigned_to.get_full_name }} {% else %} Anyone {% endif %} {% if task.assigned_to %} {{ task.assigned_to.get_full_name }} {% else %} Anyone {% endif %}

View file

@ -1,5 +1,4 @@
import bleach import bleach
import json
import pytest import pytest
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
@ -14,8 +13,6 @@ Next permissions tests - some views should respond for staffers only.
After that, view contents and behaviors. After that, view contents and behaviors.
""" """
# ### SMOKETESTS ###
@pytest.mark.django_db @pytest.mark.django_db
def test_todo_setup(todo_setup): def test_todo_setup(todo_setup):
@ -85,6 +82,31 @@ def test_view_task_detail(todo_setup, admin_client):
assert response.status_code == 200 assert response.status_code == 200
def test_del_task(todo_setup, admin_user, client):
task = Task.objects.first()
url = reverse("todo:delete_task", kwargs={"task_id": task.id})
# View accepts POST, not GET
client.login(username="admin", password="password")
response = client.get(url)
assert response.status_code == 403
response = client.post(url)
assert not Task.objects.filter(id=task.id).exists()
def test_task_toggle_done(todo_setup, admin_user, client):
task = Task.objects.first()
assert not task.completed
url = reverse("todo:task_toggle_done", kwargs={"task_id": task.id})
# View accepts POST, not GET
client.login(username="admin", password="password")
response = client.get(url)
assert response.status_code == 403
client.post(url)
task.refresh_from_db()
assert task.completed
def test_view_search(todo_setup, admin_client): def test_view_search(todo_setup, admin_client):
url = reverse("todo:search") url = reverse("todo:search")
response = admin_client.get(url) response = admin_client.get(url)

View file

@ -16,20 +16,27 @@ def delete_task(request, task_id: int) -> HttpResponse:
Redirect to the list from which the task came. Redirect to the list from which the task came.
""" """
if request.method == "POST":
task = get_object_or_404(Task, pk=task_id) task = get_object_or_404(Task, pk=task_id)
redir_url = reverse(
"todo:list_detail",
kwargs={"list_id": task.task_list.id, "list_slug": task.task_list.slug},
)
# Permissions # Permissions
if not ( if not (
(task.created_by == request.user) (task.created_by == request.user)
or (request.user.is_superuser)
or (task.assigned_to == request.user) or (task.assigned_to == request.user)
or (task.task_list.group in request.user.groups.all()) or (task.task_list.group in request.user.groups.all())
): ):
raise PermissionDenied raise PermissionDenied
tlist = task.task_list
task.delete() task.delete()
messages.success(request, "Task '{}' has been deleted".format(task.title)) messages.success(request, "Task '{}' has been deleted".format(task.title))
return redirect( return redirect(redir_url)
reverse("todo:list_detail", kwargs={"list_id": tlist.id, "list_slug": tlist.slug})
) else:
raise PermissionDenied

View file

@ -20,7 +20,7 @@ def list_lists(request) -> HttpResponse:
searchform = SearchForm(auto_id=False) searchform = SearchForm(auto_id=False)
# Make sure user belongs to at least one group. # Make sure user belongs to at least one group.
if request.user.groups.all().count() == 0: if not request.user.groups.all().exists():
messages.warning( messages.warning(
request, request,
"You do not yet belong to any groups. Ask your administrator to add you to one.", "You do not yet belong to any groups. Ask your administrator to add you to one.",

View file

@ -17,12 +17,18 @@ def toggle_done(request, task_id: int) -> HttpResponse:
Redirect to the list from which the task came. Redirect to the list from which the task came.
""" """
if request.method == "POST":
task = get_object_or_404(Task, pk=task_id) task = get_object_or_404(Task, pk=task_id)
redir_url = reverse(
"todo:list_detail",
kwargs={"list_id": task.task_list.id, "list_slug": task.task_list.slug},
)
# Permissions # Permissions
if not ( if not (
(request.user.is_superuser) (task.created_by == request.user)
or (task.created_by == request.user) or (request.user.is_superuser)
or (task.assigned_to == request.user) or (task.assigned_to == request.user)
or (task.task_list.group in request.user.groups.all()) or (task.task_list.group in request.user.groups.all())
): ):
@ -31,9 +37,7 @@ def toggle_done(request, task_id: int) -> HttpResponse:
toggle_task_completed(task.id) toggle_task_completed(task.id)
messages.success(request, "Task status changed for '{}'".format(task.title)) messages.success(request, "Task status changed for '{}'".format(task.title))
return redirect( return redirect(redir_url)
reverse(
"todo:list_detail", else:
kwargs={"list_id": task.task_list.id, "list_slug": task.task_list.slug}, raise PermissionDenied
)
)