From f31a1b0c514fba3404a516c59a9be25db29a5a59 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Laur=C3=A9line=20Gu=C3=A9rin?= <zebuline@entrouvert.com>
Date: Thu, 12 Dec 2019 09:45:54 +0100
Subject: [PATCH 3/4] agendas: delete an exception source (#29209)

---
 .../chrono/manager_confirm_source_delete.html | 19 ++++++
 .../chrono/manager_import_exceptions.html     |  2 +-
 chrono/manager/urls.py                        |  2 +
 chrono/manager/views.py                       | 14 ++++-
 tests/test_manager.py                         | 58 +++++++++++++++++++
 5 files changed, 93 insertions(+), 2 deletions(-)
 create mode 100644 chrono/manager/templates/chrono/manager_confirm_source_delete.html

diff --git a/chrono/manager/templates/chrono/manager_confirm_source_delete.html b/chrono/manager/templates/chrono/manager_confirm_source_delete.html
new file mode 100644
index 0000000..8eaf1b6
--- /dev/null
+++ b/chrono/manager/templates/chrono/manager_confirm_source_delete.html
@@ -0,0 +1,19 @@
+{% extends "chrono/manager_home.html" %}
+{% load i18n %}
+
+{% block appbar %}
+<h2>{{ object }}</h2>
+{% endblock %}
+
+{% block content %}
+<form method="post">
+  {% csrf_token %}
+  <p>
+  {% blocktrans %}Are you sure you want to delete this exception source?{% endblocktrans %}
+  </p>
+  <div class="buttons">
+    <button class="delete-button">{% trans 'Delete' %}</button>
+    <a class="cancel" href="{% url 'chrono-manager-agenda-settings' object.desk.agenda_id %}">{% trans 'Cancel' %}</a>
+  </div>
+</form>
+{% endblock %}
diff --git a/chrono/manager/templates/chrono/manager_import_exceptions.html b/chrono/manager/templates/chrono/manager_import_exceptions.html
index bf778f9..a635827 100644
--- a/chrono/manager/templates/chrono/manager_import_exceptions.html
+++ b/chrono/manager/templates/chrono/manager_import_exceptions.html
@@ -35,7 +35,7 @@
             {% if object.ics_filename %}{% trans "replace" %}{% else %}{% trans "refresh" %}{% endif %}
           </a>
         </td>
-        <td><a rel="popup" href="">{% trans "remove" %}</a></td>
+        <td><a rel="popup" href="{% url 'chrono-manager-time-period-exception-source-delete' object.pk %}">{% trans "remove" %}</a></td>
       </tr>
       {% endfor %}
     </tbody>
diff --git a/chrono/manager/urls.py b/chrono/manager/urls.py
index eba7a17..831ff2f 100644
--- a/chrono/manager/urls.py
+++ b/chrono/manager/urls.py
@@ -80,6 +80,8 @@ urlpatterns = [
             name='chrono-manager-time-period-exception-extract-list'),
         url(r'^time-period-exceptions/(?P<pk>\d+)/exception-list$', views.time_period_exception_list,
             name='chrono-manager-time-period-exception-list'),
+        url(r'^time-period-exceptions-source/(?P<pk>\d+)/delete$', views.time_period_exception_source_delete,
+            name='chrono-manager-time-period-exception-source-delete'),
 
         url(r'^agendas/events.csv$', views.agenda_import_events_sample_csv,
             name='chrono-manager-sample-events-csv'),
diff --git a/chrono/manager/views.py b/chrono/manager/views.py
index 44c90ea..8cce482 100644
--- a/chrono/manager/views.py
+++ b/chrono/manager/views.py
@@ -33,7 +33,7 @@ from django.views.generic import (DetailView, CreateView, UpdateView,
         MonthArchiveView)
 
 from chrono.agendas.models import (Agenda, Event, MeetingType, TimePeriod,
-                                   Booking, Desk, TimePeriodException,
+                                   Booking, Desk, TimePeriodException, TimePeriodExceptionSource,
                                    ICSError, AgendaImportError)
 
 from .forms import (AgendaAddForm, AgendaEditForm, NewEventForm, EventForm, NewMeetingTypeForm, MeetingTypeForm,
@@ -825,9 +825,21 @@ class DeskImportTimePeriodExceptionsView(ManagedAgendaSubobjectMixin, UpdateView
             messages.info(self.request, message)
         return super(DeskImportTimePeriodExceptionsView, self).form_valid(form)
 
+
 desk_import_time_period_exceptions = DeskImportTimePeriodExceptionsView.as_view()
 
 
+class TimePeriodExceptionSourceDeleteView(ManagedDeskSubobjectMixin, DeleteView):
+    template_name = 'chrono/manager_confirm_source_delete.html'
+    model = TimePeriodExceptionSource
+
+    def get_success_url(self):
+        return reverse('chrono-manager-agenda-settings', kwargs={'pk': self.desk.agenda_id})
+
+
+time_period_exception_source_delete = TimePeriodExceptionSourceDeleteView.as_view()
+
+
 def menu_json(request):
     label = _('Agendas')
     json_str = json.dumps([{'label': force_text(label),
diff --git a/tests/test_manager.py b/tests/test_manager.py
index 71d05f2..32474a9 100644
--- a/tests/test_manager.py
+++ b/tests/test_manager.py
@@ -1412,6 +1412,64 @@ def test_agenda_import_time_period_exception_from_remote_ics_with_ssl_error(mock
     assert 'Failed to retrieve remote calendar (https://example.com/foo.ics, SSL error).' in resp.text
 
 
+def test_meetings_agenda_delete_time_period_exception_source(app, admin_user):
+    agenda = Agenda.objects.create(label='Foo bar', kind='meetings')
+    desk = Desk.objects.create(agenda=agenda, label='Desk A')
+    MeetingType(agenda=agenda, label='Blah').save()
+    TimePeriod.objects.create(
+        weekday=1, desk=desk, start_time=datetime.time(10, 0), end_time=datetime.time(12, 0)
+    )
+    login(app)
+    # import a source
+    resp = app.get('/manage/agendas/%d/' % agenda.pk).follow()
+    resp = resp.click('Settings')
+    resp = resp.click('upload')
+    ics_with_recurrent_exceptions = b"""BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//foo.bar//EN
+BEGIN:VEVENT
+DTSTART:20180101
+DTEND:20180101
+SUMMARY:New Year's Eve
+RRULE:FREQ=YEARLY
+END:VEVENT
+END:VCALENDAR"""
+    resp.form['ics_file'] = Upload('exceptions.ics', ics_with_recurrent_exceptions, 'text/calendar')
+    resp = resp.form.submit(status=302).follow()
+    assert TimePeriodException.objects.filter(desk=desk).count() == 2
+    source1 = TimePeriodExceptionSource.objects.latest('pk')
+    assert source1.timeperiodexception_set.count() == 2
+
+    # import another one
+    resp = app.get('/manage/agendas/%d/' % agenda.pk).follow()
+    resp = resp.click('Settings')
+    resp = resp.click('upload')
+    resp.form['ics_file'] = Upload('exceptions.ics', ics_with_recurrent_exceptions, 'text/calendar')
+    resp = resp.form.submit(status=302).follow()
+    assert TimePeriodException.objects.filter(desk=desk).count() == 4
+    source2 = TimePeriodExceptionSource.objects.latest('pk')
+    assert source2.timeperiodexception_set.count() == 2
+
+    # delete the second one
+    resp = app.get('/manage/agendas/%d/' % agenda.pk).follow()
+    resp = resp.click('Settings')
+    resp = resp.click('upload')
+    resp = resp.click(href='/manage/time-period-exceptions-source/%d/delete' % source2.pk)
+    resp = resp.form.submit().follow()
+    assert TimePeriodException.objects.count() == 2
+    assert source1.timeperiodexception_set.count() == 2
+    assert TimePeriodExceptionSource.objects.filter(pk=source2.pk).exists() is False
+
+    # delete the first one
+    resp = app.get('/manage/agendas/%d/' % agenda.pk).follow()
+    resp = resp.click('Settings')
+    resp = resp.click('upload')
+    resp = resp.click(href='/manage/time-period-exceptions-source/%d/delete' % source1.pk)
+    resp = resp.form.submit().follow()
+    assert TimePeriodException.objects.count() == 0
+    assert TimePeriodExceptionSource.objects.filter(pk=source1.pk).exists() is False
+
+
 def test_agenda_day_view(app, admin_user, manager_user, api_user):
     agenda = Agenda.objects.create(label='New Example', kind='meetings')
     desk = Desk.objects.create(agenda=agenda, label='New Desk')
-- 
2.20.1

