From 167e9d4b41d4eeb05d00ad9e5b2bcec319f0f99e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20P=C3=A9ters?= <fpeters@entrouvert.com>
Date: Mon, 13 Dec 2021 23:00:24 +0100
Subject: [PATCH] backoffice: style formdefs and cards index pages (#59655)

---
 tests/admin_pages/test_card.py          |  3 +
 wcs/admin/forms.py                      | 99 +++++++++----------------
 wcs/backoffice/cards.py                 | 22 ++----
 wcs/templates/wcs/backoffice/cards.html | 20 +++++
 wcs/templates/wcs/backoffice/forms.html | 48 ++++++++++++
 5 files changed, 112 insertions(+), 80 deletions(-)
 create mode 100644 wcs/templates/wcs/backoffice/cards.html
 create mode 100644 wcs/templates/wcs/backoffice/forms.html

diff --git a/tests/admin_pages/test_card.py b/tests/admin_pages/test_card.py
index 9b6824ab1..7b3bbdaee 100644
--- a/tests/admin_pages/test_card.py
+++ b/tests/admin_pages/test_card.py
@@ -43,6 +43,9 @@ def teardown_module(module):
 def test_cards_list(pub):
     create_superuser(pub)
 
+    role = pub.role_class(name='foobar')
+    role.store()
+
     CardDef.wipe()
     carddef = CardDef()
     carddef.name = 'card title'
diff --git a/wcs/admin/forms.py b/wcs/admin/forms.py
index f2551f8b2..a3fd091b2 100644
--- a/wcs/admin/forms.py
+++ b/wcs/admin/forms.py
@@ -1682,6 +1682,7 @@ class FormsDirectory(AccessControlled, Directory):
 
     section = 'forms'
     top_title = _('Forms')
+    index_template_name = 'wcs/backoffice/forms.html'
     import_title = _('Import Form')
     import_submit_label = _('Import Form')
     import_paragraph = _('You can install a new form by uploading a file ' 'or by pointing to the form URL.')
@@ -1717,83 +1718,51 @@ class FormsDirectory(AccessControlled, Directory):
 
     def _q_index(self):
         self.html_top(title=self.top_title)
-        r = TemplateIO(html=True)
         get_response().add_javascript(['jquery.js', 'widget_list.js'])
-        r += self.form_actions()
 
         global_access = is_global_accessible(self.section)
 
-        cats = self.category_class.select()
-        self.category_class.sort_by_position(cats)
-        one = False
+        categories = self.category_class.select()
+        self.category_class.sort_by_position(categories)
+        categories.append(self.category_class(_('Misc')))
+
         formdefs = self.formdef_class.select(order_by='name', ignore_errors=True, lightweight=True)
-        for c in cats:
+        has_form_with_category_set = False
+        for category in categories:
             if not global_access:
                 user_roles = set(get_request().user.get_roles())
-                management_roles = {x.id for x in getattr(c, 'management_roles') or []}
+                management_roles = {x.id for x in getattr(category, 'management_roles') or []}
                 if not user_roles.intersection(management_roles):
                     continue
-            l2 = [x for x in formdefs if str(x.category_id) == str(c.id)]
+            l2 = [x for x in formdefs if str(x.category_id) == str(category.id)]
             l2 = [x for x in l2 if not x.disabled or (x.disabled and x.disabled_redirection)] + [
                 x for x in l2 if x.disabled and not x.disabled_redirection
             ]
-            if l2:
-                r += self.form_list(l2, title=c.name)
-                one = True
-
-        if global_access:
-            l2 = [x for x in formdefs if not x.category]
-            if l2:
-                if one:
-                    title = _('Misc')
-                else:
-                    title = None
-                l2 = [x for x in l2 if not x.disabled or (x.disabled and x.disabled_redirection)] + [
-                    x for x in l2 if x.disabled and not x.disabled_redirection
-                ]
-                r += self.form_list(l2, title=title)
-        return r.getvalue()
-
-    def form_actions(self):
-        r = TemplateIO(html=True)
-        has_roles = bool(get_publisher().role_class.count())
-        r += htmltext('<div id="appbar">')
-        r += htmltext('<h2>%s</h2>') % _('Forms')
-        if has_roles:
-            r += htmltext('<span class="actions">')
-            if is_global_accessible('forms'):
-                r += htmltext('<a href="data-sources/">%s</a>') % _('Data sources')
-                r += htmltext('<a href="blocks/">%s</a>') % _('Fields blocks')
-                if get_publisher().get_backoffice_root().is_accessible('categories'):
-                    r += htmltext('<a href="categories/">%s</a>') % _('Categories')
-            r += htmltext('<a href="import" rel="popup">%s</a>') % _('Import')
-            r += htmltext('<a class="new-item" href="new" rel="popup">%s</a>') % _('New Form')
-            r += htmltext('</span>')
-        r += htmltext('</div>')
-
-        if not has_roles:
-            r += htmltext('<p>%s</p>') % _('You first have to define roles.')
-        return r.getvalue()
-
-    def form_list(self, formdefs, title):
-        r = TemplateIO(html=True)
-        if title:
-            r += htmltext('<h2>%s</h2>') % title
-
-        r += htmltext('<ul class="biglist">')
-        for formdef in formdefs:
-            if formdef.disabled:
-                r += htmltext('<li class="disabled">')
-            else:
-                r += htmltext('<li>')
-            r += htmltext('<strong class="label"><a href="%s/">%s</a></strong>') % (formdef.id, formdef.name)
-            if formdef.disabled and formdef.disabled_redirection:
-                r += htmltext('<a class="redirection" href="%s">(') % formdef.disabled_redirection
-                r += _('redirection')
-                r += htmltext(')</a>')
-            r += htmltext('</li>')
-        r += htmltext('</ul>')
-        return r.getvalue()
+            category.objects = l2
+            if category.objects and category.id:
+                has_form_with_category_set = True
+
+        if not has_form_with_category_set:
+            # no form with a category set, do not display "Misc" title
+            categories[-1].name = None
+
+        context = {
+            'view': self,
+            'objects': formdefs,
+            'categories': categories,
+            'has_roles': bool(get_publisher().role_class.count()),
+        }
+        context.update(self.get_extra_index_context_data())
+
+        return template.QommonTemplateResponse(templates=[self.index_template_name], context=context)
+
+    def get_extra_index_context_data(self):
+        return {
+            'is_global_accessible_forms': is_global_accessible('forms'),
+            'is_global_accessible_categories': get_publisher()
+            .get_backoffice_root()
+            .is_accessible('categories'),
+        }
 
     def new(self):
         get_response().breadcrumb.append(('new', _('New')))
diff --git a/wcs/backoffice/cards.py b/wcs/backoffice/cards.py
index 8d7126f9d..32cf36dcb 100644
--- a/wcs/backoffice/cards.py
+++ b/wcs/backoffice/cards.py
@@ -229,6 +229,7 @@ class CardsDirectory(FormsDirectory):
 
     section = 'cards'
     top_title = _('Card Models')
+    index_template_name = 'wcs/backoffice/cards.html'
     import_title = _('Import Card Model')
     import_submit_label = _('Import Card Model')
     import_paragraph = _(
@@ -241,21 +242,12 @@ class CardsDirectory(FormsDirectory):
         'you should nevertheless check everything is ok. '
     )
 
-    def form_actions(self):
-        r = TemplateIO(html=True)
-        r += htmltext('<div id="appbar">')
-        r += htmltext('<h2>%s</h2>') % _('Card Models')
-        r += htmltext('<span class="actions">')
-        if get_publisher().get_backoffice_root().is_global_accessible('forms'):
-            r += htmltext('<a href="../forms/data-sources/">%s</a>') % _('Data sources')
-            r += htmltext('<a href="../forms/blocks/">%s</a>') % _('Fields blocks')
-        if get_publisher().get_backoffice_root().is_global_accessible('cards'):
-            r += htmltext('<a href="categories/">%s</a>') % _('Categories')
-        r += htmltext('<a href="import" rel="popup">%s</a>') % _('Import')
-        r += htmltext('<a class="new-item" href="new" rel="popup">%s</a>') % _('New Card Model')
-        r += htmltext('</span>')
-        r += htmltext('</div>')
-        return r.getvalue()
+    def get_extra_index_context_data(self):
+        context = super().get_extra_index_context_data()
+        context['is_global_accessible_cards'] = (
+            get_publisher().get_backoffice_root().is_global_accessible('cards')
+        )
+        return context
 
     def new(self):
         get_response().breadcrumb.append(('new', _('New')))
diff --git a/wcs/templates/wcs/backoffice/cards.html b/wcs/templates/wcs/backoffice/cards.html
new file mode 100644
index 000000000..284ff5c51
--- /dev/null
+++ b/wcs/templates/wcs/backoffice/cards.html
@@ -0,0 +1,20 @@
+{% extends "wcs/backoffice/forms.html" %}
+{% load i18n %}
+
+{% block appbar-title %}{% trans "Card Models" %}{% endblock %}
+
+{% block appbar-actions %}
+{% if is_global_accessible_forms %}
+  <a href="../forms/data-sources/">{% trans "Data sources" %}</a>
+  <a href="../forms/blocks/">{% trans "Fields blocks" %}</a>
+{% endif %}
+{% if is_global_accessible_cards %}
+  <a href="categories/">{% trans "Categories" %}</a>
+{% endif %}
+<a rel="popup" href="import">{% trans "Import" %}</a>
+<a rel="popup" href="new">{% trans "New Card Model" %}</a>
+{% endblock %}
+
+{% block no-objects %}
+{% trans "There are no card models defined." %}
+{% endblock %}
diff --git a/wcs/templates/wcs/backoffice/forms.html b/wcs/templates/wcs/backoffice/forms.html
new file mode 100644
index 000000000..eaff02cc0
--- /dev/null
+++ b/wcs/templates/wcs/backoffice/forms.html
@@ -0,0 +1,48 @@
+{% extends "wcs/backoffice/base.html" %}
+{% load i18n %}
+
+{% block appbar-title %}{% trans "Forms" %}{% endblock %}
+
+{% block appbar-actions %}
+{% if has_roles %}
+  {% if is_global_accessible_forms %}
+  <a href="data-sources/">{% trans "Data sources" %}</a>
+  <a href="blocks/">{% trans "Fields blocks" %}</a>
+    {% if is_global_accessible_categories %}
+    <a href="categories/">{% trans "Categories" %}</a>
+    {% endif %}
+  {% endif %}
+  <a rel="popup" href="import">{% trans "Import" %}</a>
+  <a rel="popup" href="new">{% trans "New Form" %}</a>
+{% endif %}
+{% endblock %}
+
+{% block content %}
+{% if not has_roles %}
+<p>{% trans "You first have to define roles." %}</p>
+{% elif objects %}
+{% for category in categories %}
+  {% if category.objects %}
+  <div class="section">
+    {% if category.name %}<h2>{{ category.name }}</h2>{% endif %}
+    <ul class="objects-list single-links">
+      {% for item in category.objects %}
+      <li {% if item.disabled %}class="disabled"{% endif %}><a href="{{ item.id }}/">{{ item.name }}
+        {% if item.disabled and item.disabled_redirection %}
+        <span class="extra-info">- {% trans "redirection" %}</span>
+        {% endif %}
+        </a>
+      </li>
+      {% endfor %}
+    </ul>
+  </div>
+  {% endif %}
+{% endfor %}
+{% else %}
+<div class="infonotice">
+{% block no-objects %}
+{% trans "There are no forms defined." %}
+{% endblock %}
+</div>
+{% endif %}
+{% endblock %}
-- 
2.34.1

