From a58d8ed270c9240a024c3143dc10df414d5da7b9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20P=C3=A9ters?= <fpeters@entrouvert.com>
Date: Sat, 20 Jul 2019 20:07:26 +0200
Subject: [PATCH] backoffice: move categories to be under forms (#7457)

---
 tests/test_admin_pages.py | 52 +++++++++++++++++++++++----------------
 wcs/admin/categories.py   |  4 +++
 wcs/admin/forms.py        |  6 ++++-
 wcs/backoffice/root.py    |  5 ++--
 4 files changed, 43 insertions(+), 24 deletions(-)

diff --git a/tests/test_admin_pages.py b/tests/test_admin_pages.py
index a21f241f1..7d030f33d 100644
--- a/tests/test_admin_pages.py
+++ b/tests/test_admin_pages.py
@@ -3751,7 +3751,17 @@ def test_roles_delete(pub):
 def test_categories(pub):
     create_superuser(pub)
     app = login(get_app(pub))
-    app.get('/backoffice/categories/')
+    resp = app.get('/backoffice/forms/categories/')
+
+def test_categories_legacy_urls(pub):
+    create_superuser(pub)
+    app = login(get_app(pub))
+    resp = app.get('/backoffice/categories/')
+    assert resp.location.endswith('/backoffice/forms/categories/')
+    resp = app.get('/backoffice/categories/1')
+    assert resp.location.endswith('/backoffice/forms/categories/1')
+    resp = app.get('/backoffice/categories/1/')
+    assert resp.location.endswith('/backoffice/forms/categories/1/')
 
 def test_categories_new(pub):
     create_superuser(pub)
@@ -3759,18 +3769,18 @@ def test_categories_new(pub):
     app = login(get_app(pub))
 
     # go to the page and cancel
-    resp = app.get('/backoffice/categories/')
+    resp = app.get('/backoffice/forms/categories/')
     resp = resp.click('New Category')
     resp = resp.forms[0].submit('cancel')
-    assert resp.location == 'http://example.net/backoffice/categories/'
+    assert resp.location == 'http://example.net/backoffice/forms/categories/'
 
     # go to the page and add a category
-    resp = app.get('/backoffice/categories/')
+    resp = app.get('/backoffice/forms/categories/')
     resp = resp.click('New Category')
     resp.forms[0]['name'] = 'a new category'
     resp.forms[0]['description'] = 'description of the category'
     resp = resp.forms[0].submit('submit')
-    assert resp.location == 'http://example.net/backoffice/categories/'
+    assert resp.location == 'http://example.net/backoffice/forms/categories/'
     resp = resp.follow()
     assert 'a new category' in resp.body
     resp = resp.click('a new category')
@@ -3786,14 +3796,14 @@ def test_categories_edit(pub):
     category.store()
 
     app = login(get_app(pub))
-    resp = app.get('/backoffice/categories/1/')
+    resp = app.get('/backoffice/forms/categories/1/')
     assert 'no form associated to this category' in resp.body
 
     resp = resp.click(href='edit')
     assert resp.forms[0]['name'].value == 'foobar'
     resp.forms[0]['description'] = 'category description'
     resp = resp.forms[0].submit('submit')
-    assert resp.location == 'http://example.net/backoffice/categories/'
+    assert resp.location == 'http://example.net/backoffice/forms/categories/'
     resp = resp.follow()
     resp = resp.click('foobar')
     assert '<h2>foobar' in resp.body
@@ -3808,7 +3818,7 @@ def test_categories_edit_duplicate_name(pub):
     category.store()
 
     app = login(get_app(pub))
-    resp = app.get('/backoffice/categories/1/')
+    resp = app.get('/backoffice/forms/categories/1/')
 
     resp = resp.click(href='edit')
     assert resp.forms[0]['name'].value == 'foobar'
@@ -3817,7 +3827,7 @@ def test_categories_edit_duplicate_name(pub):
     assert 'This name is already used' in resp.body
 
     resp = resp.forms[0].submit('cancel')
-    assert resp.location == 'http://example.net/backoffice/categories/'
+    assert resp.location == 'http://example.net/backoffice/forms/categories/'
 
 def test_categories_with_formdefs(pub):
     Category.wipe()
@@ -3826,7 +3836,7 @@ def test_categories_with_formdefs(pub):
 
     FormDef.wipe()
     app = login(get_app(pub))
-    resp = app.get('/backoffice/categories/1/')
+    resp = app.get('/backoffice/forms/categories/1/')
     assert 'form bar' not in resp.body
 
     formdef = FormDef()
@@ -3835,7 +3845,7 @@ def test_categories_with_formdefs(pub):
     formdef.category_id = category.id
     formdef.store()
 
-    resp = app.get('/backoffice/categories/1/')
+    resp = app.get('/backoffice/forms/categories/1/')
     assert 'form bar' in resp.body
     assert 'no form associated to this category' not in resp.body
 
@@ -3846,17 +3856,17 @@ def test_categories_delete(pub):
 
     FormDef.wipe()
     app = login(get_app(pub))
-    resp = app.get('/backoffice/categories/1/')
+    resp = app.get('/backoffice/forms/categories/1/')
 
     resp = resp.click(href='delete')
     resp = resp.forms[0].submit('cancel')
-    assert resp.location == 'http://example.net/backoffice/categories/1/'
+    assert resp.location == 'http://example.net/backoffice/forms/categories/1/'
     assert Category.count() == 1
 
-    resp = app.get('/backoffice/categories/1/')
+    resp = app.get('/backoffice/forms/categories/1/')
     resp = resp.click(href='delete')
     resp = resp.forms[0].submit()
-    assert resp.location == 'http://example.net/backoffice/categories/'
+    assert resp.location == 'http://example.net/backoffice/forms/categories/'
     resp = resp.follow()
     assert Category.count() == 0
 
@@ -3870,18 +3880,18 @@ def test_categories_edit_description(pub):
     app = login(get_app(pub))
     # this URL is used for editing from the frontoffice, there's no link
     # pointing to it in the admin.
-    resp = app.get('/backoffice/categories/1/description')
+    resp = app.get('/backoffice/forms/categories/1/description')
     assert resp.forms[0]['description'].value == 'category description'
     resp.forms[0]['description'] = 'updated description'
 
     # check cancel doesn't save the change
     resp2 = resp.forms[0].submit('cancel')
-    assert resp2.location == 'http://example.net/backoffice/categories/1/'
+    assert resp2.location == 'http://example.net/backoffice/forms/categories/1/'
     assert Category.get(1).description == 'category description'
 
     # check submit does it properly
     resp2 = resp.forms[0].submit('submit')
-    assert resp2.location == 'http://example.net/backoffice/categories/1/'
+    assert resp2.location == 'http://example.net/backoffice/forms/categories/1/'
     resp2 = resp2.follow()
     assert Category.get(1).description == 'updated description'
 
@@ -3891,7 +3901,7 @@ def test_categories_new_duplicate_name(pub):
     category.store()
 
     app = login(get_app(pub))
-    resp = app.get('/backoffice/categories/')
+    resp = app.get('/backoffice/forms/categories/')
     resp = resp.click('New Category')
     resp.forms[0]['name'] = 'foobar'
     resp = resp.forms[0].submit('submit')
@@ -3907,12 +3917,12 @@ def test_categories_reorder(pub):
     category.store()
 
     app = login(get_app(pub))
-    resp = app.get('/backoffice/categories/update_order?order=1;2;3;')
+    resp = app.get('/backoffice/forms/categories/update_order?order=1;2;3;')
     categories = Category.select()
     Category.sort_by_position(categories)
     assert [x.id for x in categories] == ['1', '2', '3']
 
-    resp = app.get('/backoffice/categories/update_order?order=3;1;2;')
+    resp = app.get('/backoffice/forms/categories/update_order?order=3;1;2;')
     categories = Category.select()
     Category.sort_by_position(categories)
     assert [x.id for x in categories] == ['3', '1', '2']
diff --git a/wcs/admin/categories.py b/wcs/admin/categories.py
index 25d4e5fb1..30afbf8af 100644
--- a/wcs/admin/categories.py
+++ b/wcs/admin/categories.py
@@ -230,3 +230,7 @@ class CategoriesDirectory(Directory):
     def _q_lookup(self, component):
         get_response().breadcrumb.append( ('categories/', _('Categories')) )
         return CategoryPage(component)
+
+    def _q_traverse(self, path):
+        get_response().breadcrumb.append(('forms/', _('Forms')))
+        return super(CategoriesDirectory, self)._q_traverse(path)
diff --git a/wcs/admin/forms.py b/wcs/admin/forms.py
index 35a07c4eb..01204e156 100644
--- a/wcs/admin/forms.py
+++ b/wcs/admin/forms.py
@@ -45,6 +45,7 @@ from wcs.workflows import Workflow
 from wcs.forms.root import qrcode
 
 from fields import FieldDefPage, FieldsDirectory
+from .categories import CategoriesDirectory
 from .data_sources import NamedDataSourcesDirectory
 from .logged_errors import LoggedErrorsDirectory
 
@@ -1452,8 +1453,9 @@ class NamedDataSourcesDirectoryInForms(NamedDataSourcesDirectory):
 
 class FormsDirectory(AccessControlled, Directory):
     _q_exports = ['', 'new', ('import', 'p_import'),
-            ('data-sources', 'data_sources')]
+            'categories', ('data-sources', 'data_sources')]
 
+    categories = CategoriesDirectory()
     data_sources = NamedDataSourcesDirectoryInForms()
     formdef_page_class = FormDefPage
 
@@ -1472,6 +1474,8 @@ class FormsDirectory(AccessControlled, Directory):
         if has_roles:
             r += htmltext('<span class="actions">')
             r += htmltext('<a href="data-sources/">%s</a>') % _('Data sources')
+            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>')
diff --git a/wcs/backoffice/root.py b/wcs/backoffice/root.py
index f13b877cf..57d60b778 100644
--- a/wcs/backoffice/root.py
+++ b/wcs/backoffice/root.py
@@ -43,7 +43,6 @@ class RootDirectory(BackofficeRootDirectory):
     _q_exports = ['', 'pending', 'statistics', ('menu.json', 'menu_json')]
 
     bounces = wcs.admin.bounces.BouncesDirectory()
-    categories = wcs.admin.categories.CategoriesDirectory()
     forms = wcs.admin.forms.FormsDirectory()
     roles = wcs.admin.roles.RolesDirectory()
     settings = wcs.admin.settings.SettingsDirectory()
@@ -59,7 +58,6 @@ class RootDirectory(BackofficeRootDirectory):
         ('workflows/', N_('Workflows Workshop')),
         ('users/', N_('Users'), {'check_display_function': roles.is_visible}),
         ('roles/', N_('Roles'), {'check_display_function': roles.is_visible}),
-        ('categories/', N_('Categories')),
         ('bounces/', N_('Bounces'), {'check_display_function': bounces.is_visible}),
         ('settings/', N_('Settings')),
     ]
@@ -76,6 +74,9 @@ class RootDirectory(BackofficeRootDirectory):
             except KeyError:
                 pass
         get_response().add_javascript(['jquery.js', 'qommon.js', 'gadjo.js'])
+        if path and path[0] == 'categories':
+            # legacy /backoffice/categories/<...>, redirect.
+            return redirect('/backoffice/forms/' + '/'.join(path))
         return super(RootDirectory, self)._q_traverse(path)
 
     @classmethod
-- 
2.22.0

