Repository: collective/collective.blog (language: python)

Issue:
Create a control panel to control collective.blog registry configurations
- [ ] No need for a ClassicUI control panel


Discussion / hints:

Fix patch:
diff --git a/news/36.feature b/news/36.feature
new file mode 100644
index 0000000..75ca37d
--- /dev/null
+++ b/news/36.feature
@@ -0,0 +1,1 @@
+Add control panel to configure Blog Settings [@ericof]
diff --git a/src/collective/blog/configure.zcml b/src/collective/blog/configure.zcml
index 0d449fe..a38f475 100644
--- a/src/collective/blog/configure.zcml
+++ b/src/collective/blog/configure.zcml
@@ -15,6 +15,7 @@
   <include file="permissions.zcml" />
   <include file="profiles.zcml" />
 
+  <!-- Internal packages -->
   <include package=".behaviors" />
   <include package=".controlpanel" />
   <include package=".indexers" />
diff --git a/src/collective/blog/controlpanel/configure.zcml b/src/collective/blog/controlpanel/configure.zcml
index 158a060..ee299b3 100644
--- a/src/collective/blog/controlpanel/configure.zcml
+++ b/src/collective/blog/controlpanel/configure.zcml
@@ -4,5 +4,10 @@
     xmlns:plone="http://namespaces.plone.org/plone"
     i18n_domain="collective.blog"
     >
+  <!-- Control Panel adapter-->
+  <adapter
+      factory=".controlpanel.BlogConfigletPanel"
+      name="blog-controlpanel"
+      />
 
 </configure>
diff --git a/src/collective/blog/controlpanel/controlpanel.py b/src/collective/blog/controlpanel/controlpanel.py
new file mode 100644
index 0000000..80bcbdf
--- /dev/null
+++ b/src/collective/blog/controlpanel/controlpanel.py
@@ -0,0 +1,17 @@
+from collective.blog import _
+from collective.blog.interfaces import IBlogSettings
+from plone.restapi.controlpanels import RegistryConfigletPanel
+from zope.component import adapter
+from zope.interface import Interface
+
+
+@adapter(Interface, Interface)
+class BlogConfigletPanel(RegistryConfigletPanel):
+    """Blog Settings Control Panel"""
+
+    schema = IBlogSettings
+    schema_prefix = "collective.blog.settings"
+    configlet_id = "blog-controlpanel"
+    configlet_category_id = "Products"
+    title = _("Blog Settings")
+    group = "Products"
diff --git a/src/collective/blog/profiles/default/controlpanel.xml b/src/collective/blog/profiles/default/controlpanel.xml
index a75d00c..88ad8f0 100644
--- a/src/collective/blog/profiles/default/controlpanel.xml
+++ b/src/collective/blog/profiles/default/controlpanel.xml
@@ -1,4 +1,15 @@
 <?xml version="1.0" encoding="utf-8"?>
-<object name="portal_controlpanel">
+<object xmlns:i18n="http://xml.zope.org/namespaces/i18n"
+        name="portal_controlpanel"
+        i18n:domain="collective.blog"
+>
+  <configlet action_id="blog-controlpanel"
+             appId="blog-controlpanel"
+             category="Products"
+             title="Blog Settings"
+             visible="True"
+  >
+    <permission>Manage portal</permission>
+  </configlet>
 
 </object>


Test patch:
diff --git a/tests/controlpanel/conftest.py b/tests/controlpanel/conftest.py
new file mode 100644
index 0000000..48714b8
--- /dev/null
+++ b/tests/controlpanel/conftest.py
@@ -0,0 +1,45 @@
+from plone.app.testing import SITE_OWNER_NAME
+from plone.app.testing import SITE_OWNER_PASSWORD
+from plone.restapi.testing import RelativeSession
+
+import pytest
+
+
+@pytest.fixture()
+def app(functional):
+    return functional["app"]
+
+
+@pytest.fixture()
+def portal(functional):
+    portal = functional["portal"]
+    return portal
+
+
+@pytest.fixture()
+def http_request(functional):
+    return functional["request"]
+
+
+@pytest.fixture()
+def request_factory(portal):
+    def factory():
+        url = portal.absolute_url()
+        api_session = RelativeSession(url)
+        api_session.headers.update({"Accept": "application/json"})
+        return api_session
+
+    return factory
+
+
+@pytest.fixture()
+def anon_request(request_factory):
+    return request_factory()
+
+
+@pytest.fixture()
+def manager_request(request_factory):
+    request = request_factory()
+    request.auth = (SITE_OWNER_NAME, SITE_OWNER_PASSWORD)
+    yield request
+    request.auth = ()
diff --git a/tests/controlpanel/test_controlpanel.py b/tests/controlpanel/test_controlpanel.py
new file mode 100644
index 0000000..30eee3b
--- /dev/null
+++ b/tests/controlpanel/test_controlpanel.py
@@ -0,0 +1,71 @@
+import pytest
+
+
+class TestControlPanel:
+    base_endpoint: str = "/@controlpanels"
+    configlet_id: str = "blog-controlpanel"
+
+    def test_controlpanel_is_available_anon(self, anon_request):
+        response = anon_request.get(self.base_endpoint)
+        assert response.status_code == 401
+        data = response.json()
+        assert isinstance(data, dict)
+        assert data["type"] == "Unauthorized"
+
+    def test_controlpanel_is_available_manager(self, manager_request):
+        response = manager_request.get(self.base_endpoint)
+        assert response.status_code == 200
+        data = response.json()
+        assert isinstance(data, list)
+        filtered = [item for item in data if item["@id"].endswith(self.configlet_id)]
+        assert len(filtered) == 1
+        controlpanel = filtered[0]
+        assert controlpanel["title"] == "Blog Settings"
+
+    def test_controlpanel_get(self, manager_request):
+        url = f"{self.base_endpoint}/{self.configlet_id}"
+        response = manager_request.get(url)
+        assert response.status_code == 200
+        data = response.json()
+        assert isinstance(data, dict)
+        assert "data" in data
+        assert "schema" in data
+
+    @pytest.mark.parametrize(
+        "attr,type_,expected",
+        [
+            ("enable_authors_folder", bool, True),
+        ],
+    )
+    def test_controlpanel_default_values(self, manager_request, attr, type_, expected):
+        url = f"{self.base_endpoint}/{self.configlet_id}"
+        response = manager_request.get(url)
+        data = response.json()
+        value = data["data"][attr]
+        assert isinstance(value, type_)
+        if type_ in (bool, None):
+            assert value is expected
+        else:
+            assert value == expected
+
+    @pytest.mark.parametrize(
+        "attr,type_,value",
+        [
+            ("enable_authors_folder", bool, False),
+        ],
+    )
+    def test_controlpanel_update(self, manager_request, attr, type_, value):
+        url = f"{self.base_endpoint}/{self.configlet_id}"
+        # Update values
+        new_values = {attr: value}
+        response = manager_request.patch(url, json=new_values)
+        assert response.status_code == 204
+        # Check values are persisted
+        response = manager_request.get(url)
+        data = response.json()
+        new_value = data["data"][attr]
+        assert isinstance(new_value, type_)
+        if type_ in (bool, None):
+            assert new_value is value
+        else:
+            assert new_value == value