Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Override copymove for aliases #103

Merged
merged 8 commits into from
May 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -6,6 +6,8 @@ Changelog

- 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]

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 <LL@li.org>\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 <LL@li.org>\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)
mamico marked this conversation as resolved.
Show resolved Hide resolved
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)
mamico marked this conversation as resolved.
Show resolved Hide resolved
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
Loading