From ea4c1ae19951d90590a54802c682c692e6787468 Mon Sep 17 00:00:00 2001 From: Mikel Larreategi Date: Mon, 15 Sep 2025 12:39:30 +0200 Subject: [PATCH 1/4] fix: create the dynamic page folder only if the user is editing the site, otherwise we will have an error --- src/cs_dynamicpages/views/dynamic_view.py | 25 ++++++++++++----------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/src/cs_dynamicpages/views/dynamic_view.py b/src/cs_dynamicpages/views/dynamic_view.py index 96b2f93..cef7187 100644 --- a/src/cs_dynamicpages/views/dynamic_view.py +++ b/src/cs_dynamicpages/views/dynamic_view.py @@ -36,18 +36,19 @@ def dynamic_page_folder_element(self): if page_folders: return page_folders else: - alsoProvides(self.request, IDisableCSRFProtection) - api.content.create( - container=self.context, - type="DynamicPageFolder", - title="Rows", - ) - return api.content.find( - portal_type="DynamicPageFolder", - context=self.context, - depth=1, - sort_on="getObjPositionInParent", - ) + if self.can_edit(): + alsoProvides(self.request, IDisableCSRFProtection) + api.content.create( + container=self.context, + type="DynamicPageFolder", + title="Rows", + ) + return api.content.find( + portal_type="DynamicPageFolder", + context=self.context, + depth=1, + sort_on="getObjPositionInParent", + ) def dynamic_page_folder_element_url(self): dynamic_page_folder = self.dynamic_page_folder_element() From 553dd681fede6f21a2fe11995c21180d10101633 Mon Sep 17 00:00:00 2001 From: Mikel Larreategi Date: Mon, 15 Sep 2025 12:40:00 +0200 Subject: [PATCH 2/4] fix: change the registry record name --- src/cs_dynamicpages/content/dynamic_page_row.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cs_dynamicpages/content/dynamic_page_row.py b/src/cs_dynamicpages/content/dynamic_page_row.py index e7f71da..e118f1e 100644 --- a/src/cs_dynamicpages/content/dynamic_page_row.py +++ b/src/cs_dynamicpages/content/dynamic_page_row.py @@ -53,7 +53,7 @@ def featured_list(self): def show_featured_add_button(self): row_type = self.row_type row_type_fields = api.portal.get_registry_record( - "cs_dynamicpages.dynamica_pages_control_panel.row_type_fields" + "cs_dynamicpages.dynamic_pages_control_panel.row_type_fields" ) for row_type_field in row_type_fields: if row_type_field["row_type"] == row_type: From 3b96881511d7bf9c195274ff06c3120b9a09f36d Mon Sep 17 00:00:00 2001 From: Mikel Larreategi Date: Mon, 15 Sep 2025 12:40:26 +0200 Subject: [PATCH 3/4] feat: register a text indexer extender --- src/cs_dynamicpages/configure.zcml | 9 +-- src/cs_dynamicpages/indexers/configure.zcml | 4 ++ .../indexers/content_index_extender.py | 72 +++++++++++++++++++ src/cs_dynamicpages/subscribers/__init__.py | 0 .../subscribers/configure.zcml | 29 ++++++++ .../subscribers/index_contents_in_parents.py | 15 ++++ ...st_subscriber_index_contents_in_parents.py | 25 +++++++ 7 files changed, 147 insertions(+), 7 deletions(-) create mode 100644 src/cs_dynamicpages/indexers/content_index_extender.py create mode 100644 src/cs_dynamicpages/subscribers/__init__.py create mode 100644 src/cs_dynamicpages/subscribers/configure.zcml create mode 100644 src/cs_dynamicpages/subscribers/index_contents_in_parents.py create mode 100644 src/cs_dynamicpages/tests/test_subscriber_index_contents_in_parents.py diff --git a/src/cs_dynamicpages/configure.zcml b/src/cs_dynamicpages/configure.zcml index 31e5d92..6e811bd 100644 --- a/src/cs_dynamicpages/configure.zcml +++ b/src/cs_dynamicpages/configure.zcml @@ -21,15 +21,11 @@ - - - - + - + - @@ -40,7 +36,6 @@ - diff --git a/src/cs_dynamicpages/indexers/configure.zcml b/src/cs_dynamicpages/indexers/configure.zcml index ee4ad7c..3a7c52b 100644 --- a/src/cs_dynamicpages/indexers/configure.zcml +++ b/src/cs_dynamicpages/indexers/configure.zcml @@ -3,5 +3,9 @@ + diff --git a/src/cs_dynamicpages/indexers/content_index_extender.py b/src/cs_dynamicpages/indexers/content_index_extender.py new file mode 100644 index 0000000..da68d29 --- /dev/null +++ b/src/cs_dynamicpages/indexers/content_index_extender.py @@ -0,0 +1,72 @@ +from plone import api +from plone.app.dexterity import textindexer +from plone.dexterity.interfaces import IDexterityContainer +from zope.component import adapter +from zope.interface import implementer + + +def extract_text_value_to_index(content): + """convert the text field of a content item to plain text""" + text = content.text and content.text.output or "" + pt = api.portal.get_tool("portal_transforms") + data = pt.convertTo("text/plain", text, mimetype="text/html") + return data.getData() + + +FIELDS_TO_INDEX = { + # Full field name: function to get the indexed value + "IBasic.title": lambda content: content.Title(), + "IBasic.description": lambda content: content.Description(), + "IRichTextBehavior-text": extract_text_value_to_index, +} + + +@implementer(textindexer.IDynamicTextIndexExtender) +@adapter(IDexterityContainer) +class FolderishItemTextExtender: + def __init__(self, context): + self.context = context + + def __call__(self): + layout = self.context.getLayout() + if layout == "dynamic-view": + return get_available_text_from_dynamic_pages(self.context) + return "" + + +def get_enabled_fields(row_type): + """return the fields that are enabled to be edited in the given + row type + """ + row_type_fields = api.portal.get_registry_record( + "cs_dynamicpages.dynamic_pages_control_panel.row_type_fields" + ) + + for item in row_type_fields: + if item["row_type"] == row_type: + return item["each_row_type_fields"] + + return [] + + +def get_available_text_from_dynamic_pages(context): + """this should return the indexable texts + for a given dynamic page + + it should extract the texts from the row container in the context + """ + value = [] + brains = api.content.find( + portal_type="DynamicPageRow", + review_state="published", + context=context, + ) + for brain in brains: + drr = brain.getObject() + enabled_fields = get_enabled_fields(drr.row_type) + for enabled_field in enabled_fields: + extract_content_function = FIELDS_TO_INDEX.get(enabled_field) + if extract_content_function is not None: + value.append(extract_content_function(drr)) + + return " ".join(value) diff --git a/src/cs_dynamicpages/subscribers/__init__.py b/src/cs_dynamicpages/subscribers/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/cs_dynamicpages/subscribers/configure.zcml b/src/cs_dynamicpages/subscribers/configure.zcml new file mode 100644 index 0000000..3a1e11d --- /dev/null +++ b/src/cs_dynamicpages/subscribers/configure.zcml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + diff --git a/src/cs_dynamicpages/subscribers/index_contents_in_parents.py b/src/cs_dynamicpages/subscribers/index_contents_in_parents.py new file mode 100644 index 0000000..8889038 --- /dev/null +++ b/src/cs_dynamicpages/subscribers/index_contents_in_parents.py @@ -0,0 +1,15 @@ +from Acquisition import aq_parent +from cs_dynamicpages import logger + + +def handler(obj, event): + """When modifying a DynamicPageRow, we need to index the contents of the + item where this row is shown. + To do so we go up in the tree until we reach the content and force + the reindex of it. + """ + dynamic_page_folder = aq_parent(obj) + if dynamic_page_folder.portal_type == "DynamicPageFolder": + content = aq_parent(dynamic_page_folder) + content.reindexObject() + logger.info("Reindex item: %s", "/".join(content.getPhysicalPath())) diff --git a/src/cs_dynamicpages/tests/test_subscriber_index_contents_in_parents.py b/src/cs_dynamicpages/tests/test_subscriber_index_contents_in_parents.py new file mode 100644 index 0000000..850fb93 --- /dev/null +++ b/src/cs_dynamicpages/tests/test_subscriber_index_contents_in_parents.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +from cs_dynamicpages.testing import CS_DYNAMICPAGES_FUNCTIONAL_TESTING +from cs_dynamicpages.testing import CS_DYNAMICPAGES_INTEGRATION_TESTING +from plone.app.testing import setRoles +from plone.app.testing import TEST_USER_ID + +import unittest + + +class SubscriberIntegrationTest(unittest.TestCase): + + layer = CS_DYNAMICPAGES_INTEGRATION_TESTING + + def setUp(self): + self.portal = self.layer['portal'] + setRoles(self.portal, TEST_USER_ID, ['Manager']) + + +class SubscriberFunctionalTest(unittest.TestCase): + + layer = CS_DYNAMICPAGES_FUNCTIONAL_TESTING + + def setUp(self): + self.portal = self.layer['portal'] + setRoles(self.portal, TEST_USER_ID, ['Manager']) From 76d971db2ca7d87fda03c4410bfe597a0f2e2b39 Mon Sep 17 00:00:00 2001 From: Mikel Larreategi Date: Mon, 15 Sep 2025 15:10:39 +0200 Subject: [PATCH 4/4] make lint --- .../indexers/content_index_extender.py | 2 +- .../test_subscriber_index_contents_in_parents.py | 11 ++++------- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/src/cs_dynamicpages/indexers/content_index_extender.py b/src/cs_dynamicpages/indexers/content_index_extender.py index da68d29..d967294 100644 --- a/src/cs_dynamicpages/indexers/content_index_extender.py +++ b/src/cs_dynamicpages/indexers/content_index_extender.py @@ -7,7 +7,7 @@ def extract_text_value_to_index(content): """convert the text field of a content item to plain text""" - text = content.text and content.text.output or "" + text = (content.text and content.text.output) or "" pt = api.portal.get_tool("portal_transforms") data = pt.convertTo("text/plain", text, mimetype="text/html") return data.getData() diff --git a/src/cs_dynamicpages/tests/test_subscriber_index_contents_in_parents.py b/src/cs_dynamicpages/tests/test_subscriber_index_contents_in_parents.py index 850fb93..a2772c8 100644 --- a/src/cs_dynamicpages/tests/test_subscriber_index_contents_in_parents.py +++ b/src/cs_dynamicpages/tests/test_subscriber_index_contents_in_parents.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- from cs_dynamicpages.testing import CS_DYNAMICPAGES_FUNCTIONAL_TESTING from cs_dynamicpages.testing import CS_DYNAMICPAGES_INTEGRATION_TESTING from plone.app.testing import setRoles @@ -8,18 +7,16 @@ class SubscriberIntegrationTest(unittest.TestCase): - layer = CS_DYNAMICPAGES_INTEGRATION_TESTING def setUp(self): - self.portal = self.layer['portal'] - setRoles(self.portal, TEST_USER_ID, ['Manager']) + self.portal = self.layer["portal"] + setRoles(self.portal, TEST_USER_ID, ["Manager"]) class SubscriberFunctionalTest(unittest.TestCase): - layer = CS_DYNAMICPAGES_FUNCTIONAL_TESTING def setUp(self): - self.portal = self.layer['portal'] - setRoles(self.portal, TEST_USER_ID, ['Manager']) + self.portal = self.layer["portal"] + setRoles(self.portal, TEST_USER_ID, ["Manager"])