From ec279682d26dcac8ac201b8621845fd2c4ea23c8 Mon Sep 17 00:00:00 2001
From: Ryan Cross <rcross@amsl.com>
Date: Sat, 14 Jul 2018 16:14:35 +0000
Subject: [PATCH] Give secretariat the ability to cancel non-working group
 sessions. Fixes #2537.  Commit ready for merge.  - Legacy-Id: 15328

---
 ietf/secr/meetings/tests.py                   | 13 ++++++++++
 ietf/secr/meetings/urls.py                    |  1 +
 ietf/secr/meetings/views.py                   | 19 ++++++++++++++
 ietf/secr/static/secr/css/custom.css          |  5 ++++
 ietf/secr/templates/confirm_cancel.html       | 26 +++++++++++++++++++
 ietf/secr/templates/meetings/non_session.html |  6 +++--
 6 files changed, 68 insertions(+), 2 deletions(-)
 create mode 100644 ietf/secr/templates/confirm_cancel.html

diff --git a/ietf/secr/meetings/tests.py b/ietf/secr/meetings/tests.py
index 9e1a23d4f..f09aa0304 100644
--- a/ietf/secr/meetings/tests.py
+++ b/ietf/secr/meetings/tests.py
@@ -335,6 +335,19 @@ class SecrMeetingTestCase(TestCase):
         self.assertRedirects(response, target)
         self.assertFalse(meeting.agenda.assignments.filter(timeslot=slot))
 
+    def test_meetings_non_session_cancel(self):
+        meeting = make_meeting_test_data()
+        slot = meeting.agenda.assignments.filter(timeslot__type='reg').first().timeslot
+        url = reverse('ietf.secr.meetings.views.non_session_cancel', kwargs={'meeting_id':meeting.number,'schedule_name':meeting.agenda.name,'slot_id':slot.id})
+        redirect_url = 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, redirect_url)
+        session = slot.sessionassignments.filter(schedule=meeting.agenda).first().session
+        self.assertEqual(session.status_id, 'canceled')
+
     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'})
diff --git a/ietf/secr/meetings/urls.py b/ietf/secr/meetings/urls.py
index 8bd355f10..0b5993b2d 100644
--- a/ietf/secr/meetings/urls.py
+++ b/ietf/secr/meetings/urls.py
@@ -14,6 +14,7 @@ urlpatterns = [
     url(r'^(?P<meeting_id>\d{1,6})/notifications/$', views.notifications),
     url(r'^(?P<meeting_id>\d{1,6})/(?P<schedule_name>[A-Za-z0-9_\-]+)/$', views.select),
     url(r'^(?P<meeting_id>\d{1,6})/(?P<schedule_name>[A-Za-z0-9_\-]+)/non_session/$', views.non_session),
+    url(r'^(?P<meeting_id>\d{1,6})/(?P<schedule_name>[A-Za-z0-9_\-]+)/non_session/cancel/(?P<slot_id>\d{1,6})/$', views.non_session_cancel),
     url(r'^(?P<meeting_id>\d{1,6})/(?P<schedule_name>[A-Za-z0-9_\-]+)/non_session/edit/(?P<slot_id>\d{1,6})/$', views.non_session_edit),
     url(r'^(?P<meeting_id>\d{1,6})/(?P<schedule_name>[A-Za-z0-9_\-]+)/non_session/delete/(?P<slot_id>\d{1,6})/$', views.non_session_delete),
     url(r'^(?P<meeting_id>\d{1,6})/(?P<schedule_name>[A-Za-z0-9_\-]+)/rooms/$', views.rooms),
diff --git a/ietf/secr/meetings/views.py b/ietf/secr/meetings/views.py
index e5b78f0e7..f342fd097 100644
--- a/ietf/secr/meetings/views.py
+++ b/ietf/secr/meetings/views.py
@@ -491,6 +491,25 @@ def non_session(request, meeting_id, schedule_name):
         'schedule': schedule},
     )
 
+@role_required('Secretariat')
+def non_session_cancel(request, meeting_id, schedule_name, slot_id):
+    '''
+    This function cancels the non-session TimeSlot.  Check for uploaded
+    material first.  SchedTimeSessAssignment objects get canceled as well.
+    '''
+    slot = get_object_or_404(TimeSlot, id=slot_id)
+    meeting = get_object_or_404(Meeting, number=meeting_id)
+    schedule = get_object_or_404(Schedule, meeting=meeting, name=schedule_name)
+
+    if request.method == 'POST' and request.POST['post'] == 'yes':
+        assignments = slot.sessionassignments.filter(schedule=schedule)
+        Session.objects.filter(pk__in=[x.session.pk for x in assignments]).update(status_id='canceled')
+
+        messages.success(request, 'The session was canceled successfully')
+        return redirect('ietf.secr.meetings.views.non_session', meeting_id=meeting_id, schedule_name=schedule_name)
+
+    return render(request, 'confirm_cancel.html', {'object': slot})
+
 @role_required('Secretariat')
 def non_session_delete(request, meeting_id, schedule_name, slot_id):
     '''
diff --git a/ietf/secr/static/secr/css/custom.css b/ietf/secr/static/secr/css/custom.css
index 5d609f24b..3fe48ccc5 100644
--- a/ietf/secr/static/secr/css/custom.css
+++ b/ietf/secr/static/secr/css/custom.css
@@ -490,6 +490,11 @@ tr.break td {
     border-top: 2px solid black;
 }
 
+tr.canceled {
+    background-color: #e12817;
+    color: white;
+}
+
 #id_schedule_selector {
     display: inline;
 }
diff --git a/ietf/secr/templates/confirm_cancel.html b/ietf/secr/templates/confirm_cancel.html
new file mode 100644
index 000000000..c4452a67a
--- /dev/null
+++ b/ietf/secr/templates/confirm_cancel.html
@@ -0,0 +1,26 @@
+{% extends "base_site.html" %}
+{% load staticfiles %}
+
+{% block title %}Confirm Cancel{% endblock %}
+
+
+{% block content %}
+
+<div class="module draft-container">
+    <h2>Confirm Cancel</h2>
+    <h3>Are you sure?</h3>
+    <p>You are about to cancel: {{ object }}</p>
+    {% if extra %}<p>{{ extra }}</p>{% endif %}
+    
+    <form action="" method="post">{% csrf_token %}
+      <div>
+        <p>
+          <input type="hidden" name="post" value="yes">
+          <input type="submit" value="Yes, I'm sure">
+          <a href="#" onclick="history.go(-1);return false;" class="button cancel-link">Take me back</a>
+        </p>
+      </div>
+    </form>
+</div> <!-- module -->
+
+{% endblock %}
diff --git a/ietf/secr/templates/meetings/non_session.html b/ietf/secr/templates/meetings/non_session.html
index 69b499226..5d1acebe7 100644
--- a/ietf/secr/templates/meetings/non_session.html
+++ b/ietf/secr/templates/meetings/non_session.html
@@ -6,7 +6,7 @@
     <h2>TimeSlots</h2>
 
     {% if slots %}
-    <table class="full-width">
+    <table id="nonsessions" class="full-width">
       <thead>
         <tr>
           <th>Day</th>
@@ -19,11 +19,12 @@
           <th>Type</th>
           <th></th>
           <th></th>
+          <th></th>
         </tr>
       </thead>
       <tbody>
         {% for item in slots %}
-          <tr class="{% cycle row1 row2 %}{% ifchanged item.type %} break{% endifchanged %}">
+          <tr class="{% cycle row1 row2 %}{% ifchanged item.type %} break{% endifchanged %}{% if item.session.status.slug == "canceled" %} canceled{% endif %}">
             <td>{{ item.time|date:"D" }}</td>
             <td>{{ item.time|date:"H:i" }}-{{ item.end_time|date:"H:i" }}</td>
             <td>{{ item.name }}</td>
@@ -33,6 +34,7 @@
             <td>{{ item.show_location }}</td>
             <td>{{ item.type }}</td>
             <td><a href="{% url "ietf.secr.meetings.views.non_session_edit" meeting_id=meeting.number schedule_name=schedule.name slot_id=item.id %}">Edit</a></td>
+            <td><a href="{% url "ietf.secr.meetings.views.non_session_cancel" meeting_id=meeting.number schedule_name=schedule.name slot_id=item.id %}">Cancel</a></td>
             <td><a href="{% url "ietf.secr.meetings.views.non_session_delete" meeting_id=meeting.number schedule_name=schedule.name slot_id=item.id %}">Delete</a></td>
           </tr>
         {% endfor %}