From 3ccb550888ce72355c67c209905cf768e01724fd Mon Sep 17 00:00:00 2001 From: Ryan Cross Date: Tue, 13 Jun 2017 23:33:31 +0000 Subject: [PATCH] Change views that use GET requests to delete objects to use POST. Fixes #1796. Commit ready for merge. - Legacy-Id: 13615 --- ietf/secr/drafts/tests_views.py | 13 +++++++ ietf/secr/drafts/views.py | 11 ++++-- ietf/secr/groups/tests.py | 7 ++-- ietf/secr/groups/views.py | 13 ++++--- ietf/secr/meetings/tests.py | 35 +++++++++++++++++-- ietf/secr/meetings/views.py | 46 +++++++++++++++++-------- ietf/secr/roles/tests.py | 11 +++--- ietf/secr/roles/views.py | 19 +++++----- ietf/secr/templates/meetings/times.html | 2 +- 9 files changed, 116 insertions(+), 41 deletions(-) diff --git a/ietf/secr/drafts/tests_views.py b/ietf/secr/drafts/tests_views.py index 7724b653b..417890b0d 100644 --- a/ietf/secr/drafts/tests_views.py +++ b/ietf/secr/drafts/tests_views.py @@ -154,3 +154,16 @@ class SecrDraftsTestCase(TestCase): response = self.client.get(url) self.assertEqual(response.status_code, 200) + def test_author_delete(self): + draft = make_test_data() + author = draft.documentauthor_set.first() + id = author.id + url = urlreverse('ietf.secr.drafts.views.author_delete', kwargs={'id':draft.name, 'oid':id}) + self.client.login(username="secretary", password="secretary+password") + response = self.client.get(url) + self.assertEqual(response.status_code, 200) + redirect_url = urlreverse('ietf.secr.drafts.views.authors', kwargs={'id':draft.name}) + response = self.client.post(url, {'post':'yes'}) + self.assertRedirects(response, redirect_url) + self.assertFalse(draft.documentauthor_set.filter(id=id)) + diff --git a/ietf/secr/drafts/views.py b/ietf/secr/drafts/views.py index 3fa8ced44..e99ee14d1 100644 --- a/ietf/secr/drafts/views.py +++ b/ietf/secr/drafts/views.py @@ -542,9 +542,14 @@ def author_delete(request, id, oid): ''' This view deletes the specified author from the draft ''' - DocumentAuthor.objects.get(id=oid).delete() - messages.success(request, 'The author was deleted successfully') - return redirect('ietf.secr.drafts.views.authors', id=id) + author = DocumentAuthor.objects.get(id=oid) + + if request.method == 'POST' and request.POST['post'] == 'yes': + author.delete() + messages.success(request, 'The author was deleted successfully') + return redirect('ietf.secr.drafts.views.authors', id=id) + + return render(request, 'confirm_delete.html', {'object': author}) @role_required('Secretariat') def authors(request, id): diff --git a/ietf/secr/groups/tests.py b/ietf/secr/groups/tests.py index 4da261012..8a98e8661 100644 --- a/ietf/secr/groups/tests.py +++ b/ietf/secr/groups/tests.py @@ -140,12 +140,15 @@ class GroupsTest(TestCase): make_test_data() group = Group.objects.filter(acronym='mars')[0] role = group.role_set.all()[0] + id = role.id url = reverse('ietf.secr.groups.views.delete_role', kwargs={'acronym':group.acronym,'id':role.id}) target = reverse('ietf.secr.groups.views.people', kwargs={'acronym':group.acronym}) self.client.login(username="secretary", password="secretary+password") - response = self.client.get(url,follow=True) + response = self.client.get(url) + self.assertEqual(response.status_code, 200) + response = self.client.post(url, {'post':'yes'}) self.assertRedirects(response, target) - self.assertTrue('deleted successfully' in response.content) + self.assertFalse(group.role_set.filter(id=id)) def test_people_add(self): make_test_data() diff --git a/ietf/secr/groups/views.py b/ietf/secr/groups/views.py index 1dcd3bf62..956978853 100644 --- a/ietf/secr/groups/views.py +++ b/ietf/secr/groups/views.py @@ -190,14 +190,17 @@ def delete_role(request, acronym, id): """ group = get_object_or_404(Group, acronym=acronym) role = get_object_or_404(Role, id=id) + + if request.method == 'POST' and request.POST['post'] == 'yes': + # save group + save_group_in_history(group) - # save group - save_group_in_history(group) + role.delete() + messages.success(request, 'The entry was deleted successfully') + return redirect('ietf.secr.groups.views.people', acronym=acronym) - role.delete() + return render(request, 'confirm_delete.html', {'object': role}) - messages.success(request, 'The entry was deleted successfully') - return redirect('ietf.secr.groups.views.people', acronym=acronym) @role_required('Secretariat') def edit(request, acronym): diff --git a/ietf/secr/meetings/tests.py b/ietf/secr/meetings/tests.py index 8d0e4ed2b..1f5825570 100644 --- a/ietf/secr/meetings/tests.py +++ b/ietf/secr/meetings/tests.py @@ -236,13 +236,19 @@ class SecrMeetingTestCase(TestCase): before = qs.count() expected_deletion_count = qs.filter(time=qs.first().time).count() url = reverse('ietf.secr.meetings.views.times_delete',kwargs={ - 'meeting_id':42, - 'schedule_name':'test-agenda', + 'meeting_id':meeting.number, + 'schedule_name':meeting.agenda.name, 'time':qs.first().time.strftime("%Y:%m:%d:%H:%M") }) + redirect_url = reverse('ietf.secr.meetings.views.times',kwargs={ + 'meeting_id':meeting.number, + 'schedule_name':meeting.agenda.name + }) self.client.login(username="secretary", password="secretary+password") response = self.client.get(url) - self.assertEqual(response.status_code, 302) + self.assertEqual(response.status_code, 200) + response = self.client.post(url, {'post':'yes'}) + self.assertRedirects(response, redirect_url) after = TimeSlot.objects.filter(meeting=meeting,type='session').count() self.assertEqual(after,before - expected_deletion_count) @@ -312,6 +318,18 @@ class SecrMeetingTestCase(TestCase): timeslot = session.official_timeslotassignment().timeslot self.assertEqual(timeslot.time,new_time) + def test_meetings_non_session_delete(self): + meeting = make_meeting_test_data() + slot = meeting.agenda.assignments.filter(timeslot__type='reg').first().timeslot + url = reverse('ietf.secr.meetings.views.non_session_delete', kwargs={'meeting_id':meeting.number,'schedule_name':meeting.agenda.name,'slot_id':slot.id}) + target = reverse('ietf.secr.meetings.views.non_session', kwargs={'meeting_id':meeting.number,'schedule_name':meeting.agenda.name}) + self.client.login(username="secretary", password="secretary+password") + response = self.client.get(url) + self.assertEqual(response.status_code, 200) + response = self.client.post(url, {'post':'yes'}) + self.assertRedirects(response, target) + self.assertFalse(meeting.agenda.assignments.filter(timeslot=slot)) + def test_meetings_select_group(self): make_meeting_test_data() url = reverse('ietf.secr.meetings.views.select_group',kwargs={'meeting_id':42,'schedule_name':'test-agenda'}) @@ -321,6 +339,17 @@ class SecrMeetingTestCase(TestCase): q = PyQuery(response.content) self.assertEqual(len(q("#id_scheduled_sessions")),1) + def test_meetings_schedule(self): + meeting = make_meeting_test_data() + url = reverse('ietf.secr.meetings.views.schedule',kwargs={ + 'meeting_id':meeting.number, + 'schedule_name':meeting.agenda.name, + 'acronym':'mars' + }) + self.client.login(username="secretary", password="secretary+password") + response = self.client.get(url) + self.assertEqual(response.status_code, 200) + # ---------------------- # Unit Tests # ----------------------- diff --git a/ietf/secr/meetings/views.py b/ietf/secr/meetings/views.py index bd80619b7..977150f7a 100644 --- a/ietf/secr/meetings/views.py +++ b/ietf/secr/meetings/views.py @@ -18,6 +18,7 @@ from ietf.utils.mail import send_mail from ietf.meeting.forms import duration_string from ietf.meeting.helpers import get_meeting, make_materials_directories from ietf.meeting.models import Meeting, Session, Room, TimeSlot, SchedTimeSessAssignment, Schedule +from ietf.name.models import SessionStatusName from ietf.group.models import Group, GroupEvent from ietf.person.models import Person from ietf.secr.meetings.blue_sheets import create_blue_sheets @@ -517,26 +518,30 @@ def non_session(request, meeting_id, schedule_name): @role_required('Secretariat') def non_session_delete(request, meeting_id, schedule_name, slot_id): ''' - This function deletes the non-session TimeSlot. For "other" and "plenary" timeslot - types we need to delete the corresponding Session object as well. Check for uploaded + This function deletes the non-session TimeSlot. Check for uploaded material first. SchedTimeSessAssignment objects get deleted as well. ''' - meeting = get_object_or_404(Meeting, number=meeting_id) - # schedule = get_object_or_404(Schedule, meeting=meeting, name=schedule_name) slot = get_object_or_404(TimeSlot, id=slot_id) - if slot.type_id in ('other','plenary','lead'): - assignments = slot.sessionassignments.filter(schedule__meeting=meeting) + assert slot.type_id in ('other','plenary','lead', 'reg') + + if request.method == 'POST' and request.POST['post'] == 'yes': + assignments = slot.sessionassignments.all() session_objects = [ x.session for x in assignments ] + for session in session_objects: if session.materials.exclude(states__slug='deleted'): messages.error(request, 'Materials have already been uploaded for "%s". You must delete those before deleting the timeslot.' % slot.name) return redirect('ietf.secr.meetings.views.non_session', meeting_id=meeting_id, schedule_name=schedule_name) - else: - Session.objects.filter(pk__in=[ x.pk for x in session_objects ]).delete() - slot.delete() + + # delete high order assignments, then sessions and slots + assignments.delete() + Session.objects.filter(pk__in=[ x.pk for x in session_objects ]).delete() + slot.delete() - messages.success(request, 'Non-Session timeslot deleted successfully') - return redirect('ietf.secr.meetings.views.non_session', meeting_id=meeting_id, schedule_name=schedule_name) + messages.success(request, 'The entry was deleted successfully') + return redirect('ietf.secr.meetings.views.non_session', meeting_id=meeting_id, schedule_name=schedule_name) + + return render(request, 'confirm_delete.html', {'object': slot}) @role_required('Secretariat') def non_session_edit(request, meeting_id, schedule_name, slot_id): @@ -1004,11 +1009,24 @@ def times_delete(request, meeting_id, schedule_name, time): parts = [ int(x) for x in time.split(':') ] dtime = datetime.datetime(*parts) + status = SessionStatusName.objects.get(slug='schedw') - TimeSlot.objects.filter(meeting=meeting,time=dtime).delete() + if request.method == 'POST' and request.POST['post'] == 'yes': + for slot in TimeSlot.objects.filter(meeting=meeting,time=dtime): + for assignment in slot.sessionassignments.all(): + if assignment.session: + session = assignment.session + session.status = status + session.save() + assignment.delete() + slot.delete() + messages.success(request, 'The entry was deleted successfully') + return redirect('ietf.secr.meetings.views.times', meeting_id=meeting_id,schedule_name=schedule_name) - messages.success(request, 'Timeslot deleted') - return redirect('ietf.secr.meetings.views.times', meeting_id=meeting_id,schedule_name=schedule_name) + return render(request, 'confirm_delete.html', { + 'object': '%s timeslots' % dtime.strftime("%A %H:%M"), + 'extra': 'Any sessions assigned to this timeslot will be unscheduled' + }) @role_required('Secretariat') def unschedule(request, meeting_id, schedule_name, session_id): diff --git a/ietf/secr/roles/tests.py b/ietf/secr/roles/tests.py index 5bb0d14b0..f0487bd80 100644 --- a/ietf/secr/roles/tests.py +++ b/ietf/secr/roles/tests.py @@ -28,13 +28,16 @@ class SecrRolesMainTestCase(TestCase): augment_data() group = Group.objects.filter(acronym='mars')[0] role = group.role_set.all()[0] + id = role.id url = reverse('ietf.secr.roles.views.delete_role', kwargs={'acronym':group.acronym,'id':role.id}) - target = reverse('ietf.secr.roles.views.main') + '?group=%s' % group.acronym + target = reverse('ietf.secr.roles.views.main') self.client.login(username="secretary", password="secretary+password") - response = self.client.get(url,follow=True) + response = self.client.get(url) + self.assertEqual(response.status_code, 200) + response = self.client.post(url, {'post':'yes'}) self.assertRedirects(response, target) - self.assertTrue('deleted successfully' in response.content) - + self.assertFalse(group.role_set.filter(id=id)) + def test_roles_add(self): make_test_data() augment_data() diff --git a/ietf/secr/roles/views.py b/ietf/secr/roles/views.py index 5c4842f2b..6042c6a53 100644 --- a/ietf/secr/roles/views.py +++ b/ietf/secr/roles/views.py @@ -1,7 +1,7 @@ from django.contrib import messages from django.urls import reverse from django.http import HttpResponseRedirect -from django.shortcuts import render, get_object_or_404 +from django.shortcuts import render, get_object_or_404, redirect from ietf.group.models import Group, Role from ietf.group.utils import save_group_in_history @@ -53,14 +53,15 @@ def delete_role(request, acronym, id): role = get_object_or_404(Role, id=id) group = get_object_or_404(Group, acronym=acronym) - # save group - save_group_in_history(role.group) - - role.delete() - - messages.success(request, 'The entry was deleted successfully') - url = reverse('ietf.secr.roles.views.main') + '?group=%s' % group.acronym - return HttpResponseRedirect(url) + if request.method == 'POST' and request.POST['post'] == 'yes': + # save group + save_group_in_history(group) + + role.delete() + messages.success(request, 'The entry was deleted successfully') + return redirect('ietf.secr.roles.views.main') + + return render(request, 'confirm_delete.html', {'object': role}) @role_required('Secretariat') def main(request): diff --git a/ietf/secr/templates/meetings/times.html b/ietf/secr/templates/meetings/times.html index 528930ab8..ea22ecd21 100644 --- a/ietf/secr/templates/meetings/times.html +++ b/ietf/secr/templates/meetings/times.html @@ -23,7 +23,7 @@ {{ item.time|date:"H:i" }} - {{ item.end_time|date:"H:i" }} {{ item.name }} Edit - Delete + Delete {% endfor %}