From ba5ba5e2e95d1c16fd12af2d362569066ed88899 Mon Sep 17 00:00:00 2001 From: Johannes Raggam Date: Fri, 20 Feb 2026 14:52:16 +0100 Subject: [PATCH] Avoid a plone.protect warning on `index_html` documents in portal root. `index_html` are often used to create default pages in containers. On the portal root this was prevented by a problem in a name collision checker in plone.base, fixed in https://github.com/plone/plone.base/pull/107 and a plone.protect warning issued by a write-on-read by setting the `__replaceable__` attribute on the `index_html` object. Fixes: https://github.com/plone/Products.CMFPlone/issues/4279 --- news/4279.bugfix.md | 10 +++++++ src/Products/CMFPlone/Portal.py | 3 +-- .../CMFPlone/tests/testPortalCreation.py | 26 +++++++++++++++++++ 3 files changed, 37 insertions(+), 2 deletions(-) create mode 100644 news/4279.bugfix.md diff --git a/news/4279.bugfix.md b/news/4279.bugfix.md new file mode 100644 index 0000000000..6a382cba22 --- /dev/null +++ b/news/4279.bugfix.md @@ -0,0 +1,10 @@ +Avoid a plone.protect warning on `index_html` documents in portal root. + +`index_html` are often used to create default pages in containers. On the +portal root this was prevented by a problem in a name collision checker in +plone.base, fixed in https://github.com/plone/plone.base/pull/107 and a +plone.protect warning issued by a write-on-read by setting the +`__replaceable__` attribute on the `index_html` object. + +Fixes: https://github.com/plone/Products.CMFPlone/issues/4279 +[thet] diff --git a/src/Products/CMFPlone/Portal.py b/src/Products/CMFPlone/Portal.py index 878872e666..2b91c2952f 100644 --- a/src/Products/CMFPlone/Portal.py +++ b/src/Products/CMFPlone/Portal.py @@ -172,10 +172,9 @@ def index_html(self): return result elif method not in ("GET", "HEAD", "POST"): raise AttributeError("index_html") - # Acquire from skin. + # Acquire content from here. _target = self.__getattr__("index_html") result = aq_base(_target).__of__(self) - setattr(result, "__replaceable__", REPLACEABLE) return result index_html = ComputedAttribute(index_html, 1) diff --git a/src/Products/CMFPlone/tests/testPortalCreation.py b/src/Products/CMFPlone/tests/testPortalCreation.py index afdb358d44..9ed22a3ca6 100644 --- a/src/Products/CMFPlone/tests/testPortalCreation.py +++ b/src/Products/CMFPlone/tests/testPortalCreation.py @@ -481,6 +481,32 @@ def testSyndicationTabDisabled(self): if action.getId() == "syndication" and action.visible: self.fail("Actions tool still has visible 'syndication' action") + def testIndexHtmlNotInvokingPloneProtect(self): + """A `index_html` in the portal root should not invoke a plone.protect + exception. + """ + from plone.app.testing import SITE_OWNER_NAME + from plone.app.testing import SITE_OWNER_PASSWORD + from plone.testing.zope import Browser + + browser = Browser(self.layer["app"]) + browser.open(self.portal.absolute_url() + "/login_form") + + browser.getControl("Login Name").value = SITE_OWNER_NAME + browser.getControl("Password").value = SITE_OWNER_PASSWORD + browser.getControl("Log in").click() + + browser.open(self.portal.absolute_url() + "/++add++Document") + browser.getControl("Title").value = "index_html" + browser.getControl("Save").click() + browser.open(self.portal.absolute_url() + "/index_html") + + contents = browser.contents + + # Check, if plone.protect confirm view isn't shown. + self.assertNotIn("exploit", contents) + self.assertNotIn("Confirm action", contents) + def testObjectButtonActionsInvisibleOnPortalDefaultDocument(self): # only a manager would have proper permissions self.setRoles(["Manager", "Member"])