diff --git a/news/+move-moved.internal.md b/news/+move-moved.internal.md new file mode 100644 index 0000000000..6fb6d6897e --- /dev/null +++ b/news/+move-moved.internal.md @@ -0,0 +1,10 @@ +Remove utility functions which were already moved to plone.base. + +The following utility functions were already moved to plone.base.utils and are +removed from CMFPlone: + +- `transaction_note` +- `check_id` +- `_check_for_collision` + +[thet] diff --git a/src/Products/CMFPlone/tests/testCheckId.py b/src/Products/CMFPlone/tests/testCheckId.py index df213e1ff4..21a6c9fb0d 100644 --- a/src/Products/CMFPlone/tests/testCheckId.py +++ b/src/Products/CMFPlone/tests/testCheckId.py @@ -1,8 +1,8 @@ from AccessControl import Unauthorized from plone.app.testing.bbb import PloneTestCase +from plone.base.utils import check_id from Products.CMFCore.utils import getToolByName from Products.CMFPlone.tests import dummy -from Products.CMFPlone.utils import check_id from ZODB.POSException import ConflictError diff --git a/src/Products/CMFPlone/utils.py b/src/Products/CMFPlone/utils.py index 9acdd9222b..4600913928 100644 --- a/src/Products/CMFPlone/utils.py +++ b/src/Products/CMFPlone/utils.py @@ -16,12 +16,10 @@ from os.path import abspath from os.path import join from os.path import split -from plone.base import PloneMessageFactory as _ from plone.base import utils as base_utils from plone.base.interfaces.siteroot import IPloneSiteRoot from plone.i18n.normalizer.interfaces import IIDNormalizer from plone.registry.interfaces import IRegistry -from Products.CMFCore.permissions import AddPortalContent from Products.CMFCore.permissions import ManageUsers from Products.CMFCore.utils import getToolByName from Products.CMFCore.utils import ToolInit as CMFCoreToolInit @@ -29,7 +27,6 @@ from Products.CMFPlone.log import log # noqa: F401 - for python scripts from Products.CMFPlone.log import log_deprecated # noqa: F401 - for python scripts from Products.CMFPlone.log import log_exc # noqa: F401 - for python scripts -from ZODB.POSException import ConflictError from zope.component import getMultiAdapter from zope.component import getUtility from zope.component import providedBy @@ -43,7 +40,6 @@ import OFS import re import sys -import transaction import zope.interface @@ -70,6 +66,9 @@ get_top_site_from_url="plone.base.utils:get_top_site_from_url", pretty_title_or_id="plone.base.utils:pretty_title_or_id", _createObjectByType="plone.base.utils:unrestricted_construct_instance", + transaction_note="plone.base.utils:transaction_note", + check_id="plone.base.utils:check_id", + _check_for_collision="plone.base.utils:_check_for_collision", ) deprecated_import( @@ -107,7 +106,6 @@ def safe_nativestring(value, encoding="utf-8"): security.declarePrivate("package_home") security.declarePrivate("ImageFile") security.declarePrivate("CMFCoreToolInit") -security.declarePrivate("transaction") security.declarePrivate("zope") # Canonical way to get at CMFPlone directory @@ -316,15 +314,6 @@ def getFSVersionTuple(): return versionTupleFromString(version) -def transaction_note(note): - """Write human legible note""" - T = transaction.get() - if (len(T.description) + len(note)) >= 65533: - log("Transaction note too large omitting %s" % str(note)) - else: - T.note(base_utils.safe_text(note)) - - def tuplize(value): if isinstance(value, tuple): return value @@ -526,219 +515,3 @@ def _safe_format(inst, method): as we do in CMFPlone/__init__.py. """ return SafeFormatter(inst).safe_format - - -def check_id( - context, id=None, required=0, alternative_id=None, contained_by=None, **kwargs -): - """Test an id to make sure it is valid. - - This used to be in Products/CMFPlone/skins/plone_scripts/check_id.py. - - Returns an error message if the id is bad or None if the id is good. - Parameters are as follows: - - id - the id to check - - required - if False, id can be the empty string - - alternative_id - an alternative value to use for the id - if the id is empty or autogenerated - - Accept keyword arguments for compatibility with the fallback - in Products.validation. - - Note: The reason the id is included is to handle id error messages for - such objects as files and images that supply an alternative id when an - id is auto-generated. - If you say "There is already an item with this name in this folder" - for an image that has the Name field populated with an autogenerated id, - it can cause some confusion; since the real problem is the name of - the image file name, not in the name of the autogenerated id. - """ - - def xlate(message): - ts = getToolByName(context, "translation_service", None) - if ts is None: - return message - return ts.translate(message, context=context.REQUEST) - - # if an alternative id has been supplied, see if we need to use it - if alternative_id and not id: - id = alternative_id - - # make sure we have an id if one is required - if not id: - if required: - return xlate(_("Please enter a name.")) - - # Id is not required and no alternative was specified, so assume the - # object's id will be context.getId(). We still should check to make - # sure context.getId() is OK to handle the case of pre-created objects - # constructed via portal_factory. The main potential problem is an id - # collision, e.g. if portal_factory autogenerates an id that already - # exists. - - id = context.getId() - - # - # do basic id validation - # - - # check for reserved names - if id in ( - "login", - "layout", - "plone", - "zip", - "properties", - ): - return xlate(_("${name} is reserved.", mapping={"name": id})) - - # check for bad characters - plone_utils = getToolByName(context, "plone_utils", None) - if plone_utils is not None: - bad_chars = plone_utils.bad_chars(id) - if len(bad_chars) > 0: - bad_chars = "".join(bad_chars).decode("utf-8") - decoded_id = id.decode("utf-8") - return xlate( - _( - "${name} is not a legal name. The following characters are " - "invalid: ${characters}", - mapping={"name": decoded_id, "characters": bad_chars}, - ) - ) - - # check for a catalog index - portal_catalog = getToolByName(context, "portal_catalog", None) - if portal_catalog is not None: - if id in list(portal_catalog.indexes()) + list(portal_catalog.schema()): - return xlate(_("${name} is reserved.", mapping={"name": id})) - - # id is good; decide if we should check for id collisions - if contained_by is not None: - # Always check for collisions if a container was passed - # via the contained_by parameter. - checkForCollision = True - else: - # if we have an existing object, only check for collisions - # if we are changing the id - checkForCollision = context.getId() != id - - # check for id collisions - if not checkForCollision: - # We are done. - return - # handles two use cases: - # 1. object has not yet been created and we don't know where it will be - # 2. object has been created and checking validity of id within - # container - if contained_by is None: - try: - contained_by = context.getParentNode() - except Unauthorized: - return # nothing we can do - try: - result = _check_for_collision(contained_by, id, **kwargs) - except Unauthorized: - # There is a permission problem. Safe to assume we can't use this id. - return xlate(_("${name} is reserved.", mapping={"name": id})) - if result is not None: - result = xlate( - result, - ) - return result - - -def _check_for_collision(contained_by, id, **kwargs): - """Check for collisions of an object id in a container. - - Accept keyword arguments for compatibility with the fallback - in Products.validation. - - When this was still a Python skin script, some security checks - would have been done automatically and caught by some - 'except Unauthorized' lines. Now, in unrestricted Python - code, we explicitly check. But not all checks make sense. If you don't - have the 'Access contents information' permission, theoretically - you should not be able to check for an existing conflicting id, - but it seems silly to then pretend that there is no conflict. - - For safety, we let the check_id - function do a try/except Unauthorized when calling us. - """ - secman = getSecurityManager() - # if not secman.checkPermission( - # 'Access contents information', contained_by): - # # We cannot check. Do not complain. - # return - - # Check for an existing object. - if id in contained_by: - existing_obj = getattr(contained_by, id, None) - if base_utils.base_hasattr(existing_obj, "portal_type"): - return _( - "There is already an item named ${name} in this folder.", - mapping={"name": id}, - ) - - if base_utils.base_hasattr(contained_by, "checkIdAvailable"): - # 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 secman.checkPermission(AddPortalContent, contained_by): - if not contained_by.checkIdAvailable(id): - return _("${name} is reserved.", mapping={"name": id}) - - # containers may implement this hook to further restrict ids - if base_utils.base_hasattr(contained_by, "checkValidId"): - try: - contained_by.checkValidId(id) - except ConflictError: - raise - except: # noqa: E722 - return _("${name} is reserved.", mapping={"name": id}) - - # make sure we don't collide with any parent method aliases - 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 = plone_utils.getMethodAliases(parentFti) - if aliases is not None: - if id in aliases.keys(): - return _("${name} is reserved.", mapping={"name": id}) - - # Lastly, we want to disallow the id of any of the tools in the portal - # root, as well as any object that can be acquired via portal_skins. - # However, we do want to allow overriding of *content* in the object's - # parent path, including the portal root. - - if id == "index_html": - # always allow index_html - return - portal_url = getToolByName(contained_by, "portal_url", None) - if portal_url is None: - # Probably a test. - # All other code needs the portal, so there is nothing left to check. - return - portal = portal_url.getPortalObject() - if id in portal.contentIds(): - # Fine to use the same id as a *content* item from the root. - return - # It is allowed to give an object the same id as another - # container in it's acquisition path as long as the - # object is outside the portal. - outsideportal = getattr(aq_parent(portal), id, None) - insideportal = getattr(portal, id, None) - if ( - insideportal is not None - and outsideportal is not None - and aq_base(outsideportal) == aq_base(insideportal) - ): - return - # but not other things - if getattr(portal, id, None) is not None: - return _("${name} is reserved.", mapping={"name": id})