From 585defc2a9d6d0e97e593028e3878f003da0e08f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Laur=C3=A9line=20Gu=C3=A9rin?= <zebuline@entrouvert.com>
Date: Fri, 15 May 2020 16:52:58 +0200
Subject: [PATCH 3/8] agendas: manager views for resources (#38942)

---
 chrono/manager/forms.py                       | 13 +++++
 .../templates/chrono/manager_home.html        |  3 +-
 .../chrono/manager_resource_detail.html       | 19 +++++++
 .../chrono/manager_resource_form.html         | 22 ++++++++
 .../chrono/manager_resource_list.html         | 36 +++++++++++++
 chrono/manager/urls.py                        |  5 ++
 chrono/manager/views.py                       | 54 +++++++++++++++++++
 tests/test_manager.py                         | 41 +++++++++++++-
 8 files changed, 191 insertions(+), 2 deletions(-)
 create mode 100644 chrono/manager/templates/chrono/manager_resource_detail.html
 create mode 100644 chrono/manager/templates/chrono/manager_resource_form.html
 create mode 100644 chrono/manager/templates/chrono/manager_resource_list.html

diff --git a/chrono/manager/forms.py b/chrono/manager/forms.py
index fb52dfb..5ac66f4 100644
--- a/chrono/manager/forms.py
+++ b/chrono/manager/forms.py
@@ -35,6 +35,7 @@ from chrono.agendas.models import (
     TimePeriodException,
     TimePeriodExceptionSource,
     VirtualMember,
+    Resource,
     WEEKDAYS_LIST,
 )
 
@@ -67,6 +68,18 @@ class AgendaEditForm(AgendaAddForm):
             self.fields['maximal_booking_delay'].required = True
 
 
+class ResourceAddForm(forms.ModelForm):
+    class Meta:
+        model = Resource
+        fields = ['label', 'description']
+
+
+class ResourceEditForm(forms.ModelForm):
+    class Meta:
+        model = Resource
+        fields = ['label', 'slug', 'description']
+
+
 class NewEventForm(forms.ModelForm):
     class Meta:
         model = Event
diff --git a/chrono/manager/templates/chrono/manager_home.html b/chrono/manager/templates/chrono/manager_home.html
index b8fc057..b382c0d 100644
--- a/chrono/manager/templates/chrono/manager_home.html
+++ b/chrono/manager/templates/chrono/manager_home.html
@@ -5,6 +5,7 @@
 <h2>{% trans 'Agendas' %}</h2>
 {% if user.is_staff %}
 <span class="actions">
+<a href="{% url 'chrono-manager-resource-list' %}">{% trans 'Resources' %}</a>
 <a rel="popup" href="{% url 'chrono-manager-agendas-import' %}">{% trans 'Import' %}</a>
 <a rel="popup" href="{% url 'chrono-manager-agenda-add' %}">{% trans 'New' %}</a>
 </span>
@@ -17,7 +18,7 @@
 <div>
   <ul class="objects-list single-links">
     {% for object in object_list %}
-    <li><a href="{% url 'chrono-manager-agenda-view' pk=object.id %}">{{ object.label }}</li>
+    <li><a href="{% url 'chrono-manager-agenda-view' pk=object.id %}">{{ object.label }}</a></li>
     {% endfor %}
   </ul>
 </div>
diff --git a/chrono/manager/templates/chrono/manager_resource_detail.html b/chrono/manager/templates/chrono/manager_resource_detail.html
new file mode 100644
index 0000000..4287718
--- /dev/null
+++ b/chrono/manager/templates/chrono/manager_resource_detail.html
@@ -0,0 +1,19 @@
+{% extends "chrono/manager_resource_list.html" %}
+{% load i18n %}
+
+{% block page-title-extra-label %}
+- {{ object.label }}
+{% endblock %}
+
+{% block breadcrumb %}
+{{ block.super }}
+<a href="{% url 'chrono-manager-resource-view' pk=object.pk %}">{{ object }}</a>
+{% endblock %}
+
+{% block appbar %}
+<h2>{{ object }}</h2>
+<span class="actions">
+<a rel="popup" href="{% url 'chrono-manager-resource-edit' pk=object.pk %}">{% trans 'Edit' %}</a>
+<a rel="popup" href="{% url 'chrono-manager-resource-delete' pk=object.pk %}">{% trans 'Delete' %}</a>
+</span>
+{% endblock %}
diff --git a/chrono/manager/templates/chrono/manager_resource_form.html b/chrono/manager/templates/chrono/manager_resource_form.html
new file mode 100644
index 0000000..39b7194
--- /dev/null
+++ b/chrono/manager/templates/chrono/manager_resource_form.html
@@ -0,0 +1,22 @@
+{% extends "chrono/manager_home.html" %}
+{% load i18n %}
+
+{% block appbar %}
+{% if object.id %}
+<h2>{% trans "Edit Resource" %}</h2>
+{% else %}
+<h2>{% trans "New Resource" %}</h2>
+{% endif %}
+{% endblock %}
+
+{% block content %}
+
+<form method="post" enctype="multipart/form-data">
+  {% csrf_token %}
+  {{ form.as_p }}
+  <div class="buttons">
+    <button class="submit-button">{% trans "Save" %}</button>
+    <a class="cancel" href="{% url 'chrono-manager-resource-list' %}">{% trans 'Cancel' %}</a>
+  </div>
+</form>
+{% endblock %}
diff --git a/chrono/manager/templates/chrono/manager_resource_list.html b/chrono/manager/templates/chrono/manager_resource_list.html
new file mode 100644
index 0000000..2ff6dd8
--- /dev/null
+++ b/chrono/manager/templates/chrono/manager_resource_list.html
@@ -0,0 +1,36 @@
+{% extends "chrono/manager_base.html" %}
+{% load i18n %}
+
+{% block breadcrumb %}
+{{ block.super }}
+<a href="{% url 'chrono-manager-resource-list' %}">{% trans "Resources" %}</a>
+{% endblock %}
+
+{% block appbar %}
+<h2>{% trans 'Resources' %}</h2>
+<span class="actions">
+<a rel="popup" href="{% url 'chrono-manager-resource-add' %}">{% trans 'New' %}</a>
+</span>
+{% endblock %}
+
+
+{% block content %}
+{% if object_list %}
+<div>
+  <ul class="objects-list single-links">
+    {% for object in object_list %}
+    <li>
+        <a href="{% url 'chrono-manager-resource-view' pk=object.pk %}">{{ object.label }} ({{ object.slug }})</a>
+    </li>
+    {% endfor %}
+  </ul>
+</div>
+{% else %}
+<div class="big-msg-info">
+  {% blocktrans %}
+  This site doesn't have any resource yet. Click on the "New" button in the top
+  right of the page to add a first one.
+  {% endblocktrans %}
+</div>
+{% endif %}
+{% endblock %}
diff --git a/chrono/manager/urls.py b/chrono/manager/urls.py
index 56b2b9f..55d5112 100644
--- a/chrono/manager/urls.py
+++ b/chrono/manager/urls.py
@@ -20,6 +20,11 @@ from . import views
 
 urlpatterns = [
     url(r'^$', views.homepage, name='chrono-manager-homepage'),
+    url(r'^resources/$', views.resource_list, name='chrono-manager-resource-list'),
+    url(r'^resource/add/$', views.resource_add, name='chrono-manager-resource-add'),
+    url(r'^resource/(?P<pk>\d+)/$', views.resource_view, name='chrono-manager-resource-view'),
+    url(r'^resource/(?P<pk>\d+)/edit/$', views.resource_edit, name='chrono-manager-resource-edit'),
+    url(r'^resource/(?P<pk>\d+)/delete/$', views.resource_delete, name='chrono-manager-resource-delete'),
     url(r'^agendas/add/$', views.agenda_add, name='chrono-manager-agenda-add'),
     url(r'^agendas/import/$', views.agendas_import, name='chrono-manager-agendas-import'),
     url(r'^agendas/(?P<pk>\d+)/$', views.agenda_view, name='chrono-manager-agenda-view'),
diff --git a/chrono/manager/views.py b/chrono/manager/views.py
index 1f538a4..1e71457 100644
--- a/chrono/manager/views.py
+++ b/chrono/manager/views.py
@@ -57,6 +57,7 @@ from chrono.agendas.models import (
     AgendaImportError,
     TimePeriodExceptionSource,
     VirtualMember,
+    Resource,
 )
 
 from .forms import (
@@ -76,6 +77,8 @@ from .forms import (
     TimePeriodAddForm,
     TimePeriodExceptionSourceReplaceForm,
     VirtualMemberForm,
+    ResourceAddForm,
+    ResourceEditForm,
 )
 from .utils import import_site
 
@@ -97,6 +100,57 @@ class HomepageView(ListView):
 homepage = HomepageView.as_view()
 
 
+class ResourceListView(ListView):
+    template_name = 'chrono/manager_resource_list.html'
+    model = Resource
+
+
+resource_list = ResourceListView.as_view()
+
+
+class ResourceDetailView(DetailView):
+    template_name = 'chrono/manager_resource_detail.html'
+    model = Resource
+
+
+resource_view = ResourceDetailView.as_view()
+
+
+class ResourceAddView(CreateView):
+    template_name = 'chrono/manager_resource_form.html'
+    model = Resource
+    form_class = ResourceAddForm
+
+    def get_success_url(self):
+        return reverse('chrono-manager-resource-view', kwargs={'pk': self.object.id})
+
+
+resource_add = ResourceAddView.as_view()
+
+
+class ResourceEditView(UpdateView):
+    template_name = 'chrono/manager_resource_form.html'
+    model = Resource
+    form_class = ResourceEditForm
+
+    def get_success_url(self):
+        return reverse('chrono-manager-resource-view', kwargs={'pk': self.object.id})
+
+
+resource_edit = ResourceEditView.as_view()
+
+
+class ResourceDeleteView(DeleteView):
+    template_name = 'chrono/manager_confirm_delete.html'
+    model = Resource
+
+    def get_success_url(self):
+        return reverse('chrono-manager-resource-list')
+
+
+resource_delete = ResourceDeleteView.as_view()
+
+
 class AgendaAddView(CreateView):
     template_name = 'chrono/manager_agenda_form.html'
     model = Agenda
diff --git a/tests/test_manager.py b/tests/test_manager.py
index 1fb5faf..2ff70bd 100644
--- a/tests/test_manager.py
+++ b/tests/test_manager.py
@@ -6,7 +6,6 @@ import datetime
 import json
 import mock
 import os
-import re
 
 from django.contrib.auth.models import User, Group
 from django.db import connection
@@ -25,6 +24,7 @@ from chrono.agendas.models import (
     Desk,
     Event,
     MeetingType,
+    Resource,
     TimePeriod,
     TimePeriodException,
     TimePeriodExceptionSource,
@@ -180,6 +180,45 @@ def test_view_agendas_as_manager(app, manager_user):
     resp = app.get('/manage/agendas/%s/settings' % '9999', status=404)
 
 
+def test_add_resource(app, admin_user):
+    app = login(app)
+    resp = app.get('/manage/', status=200)
+    resp = resp.click('Resources')
+    resp = resp.click('New')
+    resp.form['label'] = 'Foo bar'
+    resp = resp.form.submit()
+    resource = Resource.objects.latest('pk')
+    assert resp.location.endswith('/manage/resource/%s/' % resource.pk)
+    assert resource.label == 'Foo bar'
+    assert resource.slug == 'foo-bar'
+
+
+def test_edit_resource(app, admin_user):
+    resource = Resource.objects.create(label='Foo bar')
+
+    app = login(app)
+    resp = app.get('/manage/resource/%s/' % resource.pk, status=200)
+    resp = resp.click('Edit')
+    resp.form['label'] = 'Foo bar baz'
+    resp.form['slug'] = 'baz'
+    resp = resp.form.submit()
+    assert resp.location.endswith('/manage/resource/%s/' % resource.pk)
+    resource.refresh_from_db()
+    assert resource.label == 'Foo bar baz'
+    assert resource.slug == 'baz'
+
+
+def test_delete_resource(app, admin_user):
+    resource = Resource.objects.create(label='Foo bar')
+
+    app = login(app)
+    resp = app.get('/manage/resource/%s/' % resource.pk, status=200)
+    resp = resp.click('Delete')
+    resp = resp.form.submit()
+    assert resp.location.endswith('/manage/resources/')
+    assert Resource.objects.exists() is False
+
+
 def test_add_agenda(app, admin_user):
     app = login(app)
     resp = app.get('/manage/', status=200)
-- 
2.20.1

