diff --git a/news/+check-id-update.bugfix.rst b/news/+check-id-update.bugfix.rst new file mode 100644 index 0000000..e12bdcb --- /dev/null +++ b/news/+check-id-update.bugfix.rst @@ -0,0 +1,7 @@ +Update id collision checks with missing checks from CMFPlone. + +Some of the checks in `utils._check_for_collision` or erroneous. These checks +were updated with the original checks from CMFPlone. The tests depend on a +fully set-up site and remain in CMFPlone. + +[thet] diff --git a/src/plone/base/tests/test_utils.py b/src/plone/base/tests/test_utils.py index 6a5e0a5..e237327 100644 --- a/src/plone/base/tests/test_utils.py +++ b/src/plone/base/tests/test_utils.py @@ -219,6 +219,11 @@ def test_is_truthy(self): self.assertFalse(is_truthy("foo")) def test_check_for_collision(self): + """Test the collision for ids in containers. + + There are more complete tests which require a fully set-up Plone site + in: `Products.CMFPlone.tests.testCheckId` + """ from plone.base.utils import _check_for_collision class Container(dict): diff --git a/src/plone/base/utils.py b/src/plone/base/utils.py index 97a4cea..855758e 100644 --- a/src/plone/base/utils.py +++ b/src/plone/base/utils.py @@ -1,5 +1,6 @@ from . import PloneMessageFactory as _ from .interfaces import ISearchSchema +from AccessControl import getSecurityManager from AccessControl import Unauthorized from Acquisition import aq_base from Acquisition import aq_get @@ -7,6 +8,7 @@ from DateTime import DateTime from plone.registry.interfaces import IRegistry from Products.CMFCore.interfaces import ITypesTool +from Products.CMFCore.permissions import AddPortalContent from Products.CMFCore.utils import getToolByName from urllib.parse import urlparse from zExceptions import NotFound @@ -514,6 +516,16 @@ def _check_for_collision(contained_by, cid, **kwargs): if base_hasattr(contained_by, cid): return _("${name} is reserved.", mapping={"name": cid}) + if base_hasattr(contained_by, "checkIdAvailable"): + # ‌`checkIdAvailable` is implemented by + # ‌`Products.CMFCore.PortalFolder.PortalFolderBase` + # Historically this used to be called from the check_id skin script, + # which would check the permission automatically, + # and the code would catch the Unauthorized exception. + if getSecurityManager().checkPermission(AddPortalContent, contained_by): + if not contained_by.checkIdAvailable(cid): + return _("${name} is reserved.", mapping={"name": cid}) + # containers may implement this hook to further restrict ids if getattr(aq_base(contained_by), "checkValidId", _marker) is not _marker: try: @@ -524,11 +536,12 @@ def _check_for_collision(contained_by, cid, **kwargs): return _("${name} is reserved.", mapping={"name": cid}) # make sure we don't collide with any parent method aliases - types_tool = getToolByName(contained_by, "types_tool", None) - if types_tool is not None: - parentFti = types_tool.getTypeInfo(contained_by) + plone_utils = getToolByName(contained_by, "plone_utils", None) + portal_types = getToolByName(contained_by, "portal_types", None) + if plone_utils is not None and portal_types is not None: + parentFti = portal_types.getTypeInfo(contained_by) if parentFti is not None: - aliases = parentFti.getMethodAliases() + aliases = plone_utils.getMethodAliases(parentFti) if aliases is not None and cid in aliases.keys(): return _("${name} is reserved.", mapping={"name": cid})