Repository: openfun/richie

Issue:
Add list of categories page
## Feature Request

**Is your feature request related to a problem or unsupported use case? Please describe.**
Categories are a taxonomy that needs to be displayed on the page `/en/categories/`.

**Describe the solution you'd like**
- [x] Add a template that displays the whole nested structure of categories. The first level is a meta category (=filter on the search page), the immediate children are top level categories.
- [x] Add basic styling




Discussion:

Resolving patch:
diff --git a/sandbox/settings.py b/sandbox/settings.py
--- a/sandbox/settings.py
+++ b/sandbox/settings.py
@@ -240,6 +240,7 @@ class Base(DRFMixin, Configuration):
         ("courses/cms/course_run_detail.html", _("Course run page")),
         ("courses/cms/organization_list.html", _("Organization list")),
         ("courses/cms/organization_detail.html", _("Organization page")),
+        ("courses/cms/category_list.html", _("Category list")),
         ("courses/cms/category_detail.html", _("Category page")),
         ("courses/cms/blogpost_list.html", _("Blog post list")),
         ("courses/cms/blogpost_detail.html", _("Blog post page")),
diff --git a/src/richie/apps/courses/models/category.py b/src/richie/apps/courses/models/category.py
--- a/src/richie/apps/courses/models/category.py
+++ b/src/richie/apps/courses/models/category.py
@@ -143,6 +143,17 @@ def get_persons(self, language=None):
             .distinct()
         )
 
+    def get_children_categories(self):
+        """
+        Return a query to get only the children pages that are Category
+        objects related to this category. A convenient method since
+        ``page.get_child_pages`` return every direct child page, no matter
+        it's a Category or not.
+        """
+        return Category.objects.filter(
+            extended_object__in=self.extended_object.get_child_pages()
+        ).select_related("extended_object")
+
 
 def get_category_limit_choices_to():
     """Return a query limiting the categories proposed when creating a CategoryPlugin."""
diff --git a/src/richie/apps/demo/defaults.py b/src/richie/apps/demo/defaults.py
--- a/src/richie/apps/demo/defaults.py
+++ b/src/richie/apps/demo/defaults.py
@@ -48,7 +48,7 @@
         "in_navigation": True,
         "kwargs": {
             "reverse_id": models.Category.ROOT_REVERSE_ID,
-            "template": "richie/child_pages_list.html",
+            "template": "courses/cms/category_list.html",
         },
     },
     "organizations": {


Test patch:
diff --git a/tests/apps/courses/test_cms_plugins_category.py b/tests/apps/courses/test_cms_plugins_category.py
--- a/tests/apps/courses/test_cms_plugins_category.py
+++ b/tests/apps/courses/test_cms_plugins_category.py
@@ -163,15 +163,16 @@ def test_cms_plugins_category_render_on_public_page(self):
         # And CMS page title should be in title attribute of the link
         self.assertContains(
             response,
-            '<a class="category-plugin__body" href="/en/public-title/" title="{title:s}"'.format(
-                title=category.public_extension.extended_object.get_title()
-            ),
+            (
+                '<a class="category-glimpse category-glimpse--link" '
+                'href="/en/public-title/" title="{title:s}"'
+            ).format(title=category.public_extension.extended_object.get_title()),
             status_code=200,
         )
         # The category's title should be wrapped in a div
         self.assertContains(
             response,
-            '<div class="category-plugin__title">{:s}</div>'.format(
+            '<h2 class="category-glimpse__title">{:s}</h2>'.format(
                 category.public_extension.extended_object.get_title()
             ),
             html=True,
@@ -187,9 +188,10 @@ def test_cms_plugins_category_render_on_public_page(self):
         response = self.client.get(url)
         self.assertContains(
             response,
-            '<a class="category-plugin__body" href="/fr/titre-publique/" title="{title:s}"'.format(
-                title=category.public_extension.extended_object.get_title()
-            ),
+            (
+                '<a class="category-glimpse category-glimpse--link" '
+                'href="/fr/titre-publique/" title="{title:s}"'
+            ).format(title=category.public_extension.extended_object.get_title()),
             status_code=200,
         )
         # pylint: disable=no-member
diff --git a/tests/apps/courses/test_models_category.py b/tests/apps/courses/test_models_category.py
--- a/tests/apps/courses/test_models_category.py
+++ b/tests/apps/courses/test_models_category.py
@@ -253,3 +253,23 @@ def test_models_category_get_persons_several_languages(self):
         )
         self.assertEqual(Person.objects.count(), 2)
         self.assertEqual(category.get_persons().count(), 1)
+
+    def test_models_category_get_children_categories(self):
+        """
+        It should be possible to retrieve the list of direct children page which
+        are Category extensions and not any other type.
+        """
+        empty_category = CategoryFactory(should_publish=True)
+
+        parent_category = CategoryFactory(should_publish=True)
+        child_categories = CategoryFactory.create_batch(
+            2, page_parent=parent_category.extended_object, should_publish=True
+        )
+
+        with self.assertNumQueries(2):
+            self.assertFalse(empty_category.get_children_categories().exists())
+
+        with self.assertNumQueries(2):
+            self.assertEqual(
+                set(parent_category.get_children_categories()), set(child_categories)
+            )