Skip to content

Commit

Permalink
Override copymove for aliases (#103)
Browse files Browse the repository at this point in the history
* Customize also  and  endpoints to raise BadRequest if that action will override some aliases.

* add test

* add test

* zpretty

* fix tests

* Update src/redturtle/volto/restapi/services/copymove/copymove.py

* Update src/redturtle/volto/restapi/services/copymove/copymove.py

* self.context is the destintaion != parent that is the parent of the source

---------

Co-authored-by: Mauro Amico <[email protected]>
  • Loading branch information
cekk and mamico authored May 6, 2024
1 parent 8011256 commit e5665a5
Show file tree
Hide file tree
Showing 11 changed files with 160 additions and 32 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
__pycache__
.DS_Store
pyvenv.cfg
.coverage
Expand Down
2 changes: 2 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ Changelog
[mamico]
- Customize INameChooser adapter to check also alias ids and disallow to create contents that could override aliases.
[cekk]
- Customize also `copy` and `move` endpoints to raise BadRequest if that action will override some aliases.
[cekk]
- Add flag in controlpanel to enable/disable INameChooser customization.
[cekk]
- Exclude bg_color from transformed fields in deserializer.
Expand Down
50 changes: 28 additions & 22 deletions src/redturtle/volto/adapters/namechooser.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,32 +10,38 @@
from zope.component import getUtility


def check_alias(context, id):
context = aq_inner(context)
try:
if not api.portal.get_registry_record(
"check_aliases_in_namechooser",
interface=IRedTurtleVoltoSettings,
default=False,
):
return
except KeyError:
return
storage = getUtility(IRedirectionStorage)
path = "/".join(context.getPhysicalPath()) + "/" + id
if storage.get(path):
portal_path = "/".join(api.portal.get().getPhysicalPath())
fixed_path = path.replace(portal_path, "")
msg = _(
"name_chooser_alias_error",
default='The id "${id}" is invalid because there is already an alias for that path. '
'Change its id or ask site administrators to remove "${fixed_path}" in aliases management.',
mapping={"id": id, "fixed_path": fixed_path},
)
raise BadRequest(api.portal.translate(msg))


class NormalizingNameChooser(BaseNormalizingNameChooser):
def chooseName(self, name, obj):
"""
Additional check: the id should not be in redirection tool.
"""
id = super().chooseName(name=name, obj=obj)
try:
if not api.portal.get_registry_record(
"check_aliases_in_namechooser",
interface=IRedTurtleVoltoSettings,
default=False,
):
return id
except KeyError:
return id
parent = aq_inner(self.context)
storage = getUtility(IRedirectionStorage)
path = "/".join(parent.getPhysicalPath()) + "/" + id
if storage.get(path):
portal_path = "/".join(api.portal.get().getPhysicalPath())
fixed_path = path.replace(portal_path, "")
msg = _(
"name_chooser_alias_error",
default='The id "${id}" is invalid because there is already an alias for that path. '
'Search "${fixed_path}" in aliases management to manage it.',
mapping={"id": id, "fixed_path": fixed_path},
)
raise BadRequest(api.portal.translate(msg))

# this raise BadRequest if there is an override with aliases
check_alias(context=self.context, id=id)
return id
8 changes: 4 additions & 4 deletions src/redturtle/volto/locales/it/LC_MESSAGES/redturtle.volto.po
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"POT-Creation-Date: 2024-03-21 13:53+0000\n"
"POT-Creation-Date: 2024-03-28 10:20+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI +ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <[email protected]>\n"
Expand Down Expand Up @@ -67,10 +67,10 @@ msgstr "Se abilitato, verrà utilizzato un ranking custom per la ricerca testual
msgid "enable_advanced_query_ranking_label"
msgstr "Abilita ranking custom con AdvancedQuery"

#. Default: "The id \"${id}\" is invalid because there is already an alias for that path. Search \"${fixed_path}\" in aliases management to manage it."
#: redturtle/volto/adapters/namechooser.py:35
#. Default: "The id \"${id}\" is invalid because there is already an alias for that path. Change its id or ask site administrators to remove \"${fixed_path}\" in aliases management."
#: redturtle/volto/adapters/namechooser.py:29
msgid "name_chooser_alias_error"
msgstr "L'id \"${id}\" non è valido perché già utilizzato per un alias. Puoi verificarlo ed eventualmente cancellarlo, cercando \"${fixed_path}\" nella Gestione URL."
msgstr "L'id \"${id}\" non è valido perché già utilizzato per un alias. Modifca l'id oppure rivolgiti agli amministratori per cancellare l'alias esistente: \"${fixed_path}\"."

#. Default: "Insert an external link directly into the field,or select an internal link clicking on the icon."
#: redturtle/volto/types/adapters.py:25
Expand Down
6 changes: 3 additions & 3 deletions src/redturtle/volto/locales/redturtle.volto.pot
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"POT-Creation-Date: 2024-03-21 13:53+0000\n"
"POT-Creation-Date: 2024-03-28 12:42+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI +ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <[email protected]>\n"
Expand Down Expand Up @@ -70,8 +70,8 @@ msgstr ""
msgid "enable_advanced_query_ranking_label"
msgstr ""

#. Default: "The id \"${id}\" is invalid because there is already an alias for that path. Search \"${fixed_path}\" in aliases management to manage it."
#: redturtle/volto/adapters/namechooser.py:35
#. Default: "The id \"${id}\" is invalid because there is already an alias for that path. Change its id or ask site administrators to remove \"${fixed_path}\" in aliases management."
#: redturtle/volto/adapters/namechooser.py:29
msgid "name_chooser_alias_error"
msgstr ""

Expand Down
1 change: 1 addition & 0 deletions src/redturtle/volto/restapi/services/configure.zcml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
xmlns:zcml="http://namespaces.zope.org/zcml"
>

<include package=".copymove" />
<include package=".navigation" />
<include package=".querystringsearch" />
<include package=".search" />
Expand Down
Empty file.
25 changes: 25 additions & 0 deletions src/redturtle/volto/restapi/services/copymove/configure.zcml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<configure
xmlns="http://namespaces.zope.org/zope"
xmlns:plone="http://namespaces.plone.org/plone"
xmlns:zcml="http://namespaces.zope.org/zcml"
>

<plone:service
method="POST"
factory=".copymove.Copy"
for="Products.CMFCore.interfaces.IFolderish"
permission="zope2.View"
layer="redturtle.volto.interfaces.IRedturtleVoltoLayer"
name="@copy"
/>

<plone:service
method="POST"
factory=".copymove.Move"
for="Products.CMFCore.interfaces.IFolderish"
permission="zope2.View"
layer="redturtle.volto.interfaces.IRedturtleVoltoLayer"
name="@move"
/>

</configure>
21 changes: 21 additions & 0 deletions src/redturtle/volto/restapi/services/copymove/copymove.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
from plone.restapi.services.copymove.copymove import Copy as BaseCopy
from plone.restapi.services.copymove.copymove import Move as BaseMove
from redturtle.volto.adapters.namechooser import check_alias


class Copy(BaseCopy):
"""Copies existing content objects."""

def clipboard(self, parent, ids):
for id in ids:
check_alias(context=self.context, id=id)
return super().clipboard(parent, ids)


class Move(BaseMove):
"""Moves existing content objects."""

def clipboard(self, parent, ids):
for id in ids:
check_alias(context=self.context, id=id)
return super().clipboard(parent, ids)
72 changes: 72 additions & 0 deletions src/redturtle/volto/tests/test_copymove_customization.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# -*- coding: utf-8 -*-
from plone import api
from plone.app.testing import setRoles
from plone.app.testing import SITE_OWNER_NAME
from plone.app.testing import SITE_OWNER_PASSWORD
from plone.app.testing import TEST_USER_ID
from plone.restapi.testing import RelativeSession
from redturtle.volto.interfaces import IRedTurtleVoltoSettings
from redturtle.volto.testing import REDTURTLE_VOLTO_API_FUNCTIONAL_TESTING

import transaction
import unittest


class TestCopyMoveCustomization(unittest.TestCase):
layer = REDTURTLE_VOLTO_API_FUNCTIONAL_TESTING

def setUp(self):
self.app = self.layer["app"]
self.portal = self.layer["portal"]
self.portal_url = self.portal.absolute_url()
setRoles(self.portal, TEST_USER_ID, ["Manager"])

self.api_session = RelativeSession(self.portal_url)
self.api_session.headers.update({"Accept": "application/json"})
self.api_session.auth = (SITE_OWNER_NAME, SITE_OWNER_PASSWORD)

foo = api.content.create(
container=self.portal,
type="Document",
title="Foo",
)
api.content.rename(obj=foo, new_id="xxx")

self.bar = api.content.create(
container=self.portal,
type="Document",
title="Bar",
)
api.content.create(
container=self.bar,
type="Document",
title="foo",
)

# enable it
api.portal.set_registry_record(
"check_aliases_in_namechooser", True, interface=IRedTurtleVoltoSettings
)

transaction.commit()

def tearDown(self):
self.api_session.close()

def test_move_raise_error_if_id_is_a_valid_alias(self):
response = self.api_session.post("/@move", json={"source": ["/bar/foo"]})

self.assertEqual(response.status_code, 400)
self.assertEqual(
response.json()["message"],
'The id "foo" is invalid because there is already an alias for that path. Change its id or ask site administrators to remove "/foo" in aliases management.',
)

def test_copy_raise_error_if_id_is_a_valid_alias(self):
response = self.api_session.post("/@copy", json={"source": ["/bar/foo"]})

self.assertEqual(response.status_code, 400)
self.assertEqual(
response.json()["message"],
'The id "foo" is invalid because there is already an alias for that path. Change its id or ask site administrators to remove "/foo" in aliases management.',
)
6 changes: 3 additions & 3 deletions src/redturtle/volto/tests/test_namechooser_customization.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ def test_name_chooser_raise_badrequest_on_site_root(self):
chooser.chooseName("foo", fake_obj)

self.assertEqual(
'The id "foo" is invalid because there is already an alias for that path. Search "/foo" in aliases management to manage it.',
'The id "foo" is invalid because there is already an alias for that path. Change its id or ask site administrators to remove "/foo" in aliases management.',
str(cm.exception),
)

Expand Down Expand Up @@ -180,7 +180,7 @@ def test_if_enabled_name_chooser_raise_badrequest_on_folderish_container(self):
chooser.chooseName("aaa", fake_obj)

self.assertEqual(
'The id "aaa" is invalid because there is already an alias for that path. Search "/container/aaa" in aliases management to manage it.',
'The id "aaa" is invalid because there is already an alias for that path. Change its id or ask site administrators to remove "/container/aaa" in aliases management.',
str(cm.exception),
)

Expand All @@ -196,7 +196,7 @@ def test_api_rename_raise_exception_if_name_is_alias(self):
api.content.rename(obj=item, new_id="foo", safe_id=True)

self.assertEqual(
'The id "foo" is invalid because there is already an alias for that path. Search "/foo" in aliases management to manage it.',
'The id "foo" is invalid because there is already an alias for that path. Change its id or ask site administrators to remove "/foo" in aliases management.',
str(cm.exception),
)

Expand Down

0 comments on commit e5665a5

Please sign in to comment.