Skip to content
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 news/+dependencies.internal
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Declared ``plone.autoform``, ``plone.behavior``, ``plone.supermodel`` and ``Products.GenericSetup`` as dependencies. @ericof
1 change: 1 addition & 0 deletions news/+pytest-plone.tests
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fixed test isolation with pytest-plone 1.1.0 by keeping importers and fixtures from committing transactions during tests. @ericof
1 change: 1 addition & 0 deletions news/109.bugfix
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fixed export and import of grid blocks whose images have no ``image_scales`` information. @ericof
1 change: 1 addition & 0 deletions news/110.bugfix
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fixed ``plone-exporter`` silently skipping fields protected by a read permission not granted to the ``Manager`` role, such as behavior fields on the Plone Site root. Both command line tools now run with the site hook set and without overriding the roles of the system user. @ericof
5 changes: 4 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
"Framework :: Plone :: 6.2",
"Framework :: Plone :: Core",
"Framework :: Zope :: 5",
"License :: OSI Approved :: GNU General Public License v2 (GPLv2)",
"Operating System :: OS Independent",
"Programming Language :: Python",
"Programming Language :: Python :: 3.10",
Expand Down Expand Up @@ -50,14 +49,18 @@
"plone.app.redirector",
"plone.app.textfield",
"plone.app.users",
"plone.autoform",
"plone.base",
"plone.behavior",
"plone.dexterity",
"plone.namedfile",
"plone.restapi",
"plone.supermodel",
"plone.uuid",
"Products.CMFEditions",
"Products.CMFPlone",
"Products.DCWorkflow",
"Products.GenericSetup",
"Products.PlonePAS",
"Products.PortalTransforms",
"z3c.relationfield",
Expand Down
5 changes: 2 additions & 3 deletions src/plone/exportimport/cli/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
from plone import api
from plone.exportimport import logger as package_logger
from plone.exportimport.exporters import get_exporter
from plone.exportimport.importers import get_importer
Expand Down Expand Up @@ -54,7 +53,7 @@ def exporter_cli(args=sys.argv):
logger.error(f"{namespace.path} does not exist, please create it first.")
sys.exit(1)
site = cli_helpers.get_site(app, namespace.site, logger)
with api.env.adopt_roles(["Manager"]):
with hooks.site(site):
results = get_exporter(site).export_site(path, options=namespace)
logger.info(f" Using path {path} to export content from Plone site at /{site.id}")
for item in results[1:]:
Expand All @@ -75,7 +74,7 @@ def importer_cli(args=sys.argv):
if not namespace.quiet:
cli_helpers.setup_logger_console(package_logger)
site = cli_helpers.get_site(app, namespace.site, logger)
with hooks.site(site), api.env.adopt_roles(["Manager"]):
with hooks.site(site):
logger.info(f" Using path {path} to import content to Plone site at /{site.id}")
results = get_importer(site).import_site(path)
for item in results:
Expand Down
3 changes: 3 additions & 0 deletions src/plone/exportimport/testing/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,17 @@ class ExportImportLayer(PloneSandboxLayer):

def setUpZope(self, app, configurationContext):
import plone.exportimport
import plone.exportimport.testing
import plone.volto

self.loadZCML(package=plone.volto)
self.loadZCML(package=plone.exportimport)
self.loadZCML(package=plone.exportimport.testing)

def setUpPloneSite(self, portal):
st = portal.portal_setup
st.runAllImportStepsFromProfile("plone.volto:default")
st.runAllImportStepsFromProfile("plone.exportimport.testing:testing")

# Enable plone.constraintypes behavior,
# which is not enabled by default in plone.volto
Expand Down
43 changes: 43 additions & 0 deletions src/plone/exportimport/testing/configure.zcml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<configure
xmlns="http://namespaces.zope.org/zope"
xmlns:genericsetup="http://namespaces.zope.org/genericsetup"
xmlns:plone="http://namespaces.plone.org/plone"
>

<include
package="plone.behavior"
file="meta.zcml"
/>

<genericsetup:registerProfile
name="testing"
title="Testing"
description="Testing profile for plone.exportimport"
provides="Products.GenericSetup.interfaces.EXTENSION"
directory="profiles/testing"
/>

<permission
id="plone.exportimport.testing.dummy.view"
title="plone.exportimport.testing: View Dummy"
/>

<permission
id="plone.exportimport.testing.dummy.edit"
title="plone.exportimport.testing: Edit Dummy"
/>
<permission
id="plone.exportimport.testing.dummy.add"
title="plone.exportimport.testing: Add Dummy"
/>

<plone:behavior
name="plone.exportimport.testing.dummy_settings"
title="Dummy settings"
description="Fields protected by custom read and write permissions"
provides=".content.dummy.IDummySettings"
/>

<!-- -*- extra stuff goes here -*- -->

</configure>
Empty file.
43 changes: 43 additions & 0 deletions src/plone/exportimport/testing/content/dummy.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
from plone.autoform import directives
from plone.autoform.interfaces import IFormFieldProvider
from plone.dexterity.content import Container
from plone.supermodel.model import Schema
from zope import schema
from zope.interface import implementer
from zope.interface import provider


class IDummyContent(Schema):
"""Schema with fields protected by custom read and write permissions."""

secure_field = schema.TextLine(
title="Secure field",
description="Secure field.",
required=False,
)

secure_setting = schema.Bool(
title="Secure Setting",
description="Secure setting.",
required=False,
default=True,
)

directives.read_permission(
secure_field="plone.exportimport.testing.dummy.view",
secure_setting="plone.exportimport.testing.dummy.view",
)
directives.write_permission(
secure_field="plone.exportimport.testing.dummy.edit",
secure_setting="plone.exportimport.testing.dummy.edit",
)


@provider(IFormFieldProvider)
class IDummySettings(IDummyContent):
"""Behavior exposing the protected fields, used on the Plone Site root."""


@implementer(IDummyContent)
class DummyContent(Container):
"""Dexterity container that represents a dummy content."""
5 changes: 5 additions & 0 deletions src/plone/exportimport/testing/profiles/testing/metadata.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<metadata>
<version>1000</version>
<dependencies />
</metadata>
24 changes: 24 additions & 0 deletions src/plone/exportimport/testing/profiles/testing/rolemap.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<rolemap>
<permissions>
<!-- -*- extra stuff goes here -*- -->
<!-- Mirrors the header settings permissions from sc.voltolighttheme -->
<permission acquire="False"
name="plone.exportimport.testing: View Dummy"
>
<role name="Anonymous" />
<role name="Authenticated" />
</permission>
<permission acquire="False"
name="plone.exportimport.testing: Edit Dummy"
>
<role name="Manager" />
<role name="Site Administrator" />
</permission>
<permission acquire="False"
name="plone.exportimport.testing: Add Dummy"
>
<role name="Manager" />
</permission>
</permissions>
</rolemap>
11 changes: 11 additions & 0 deletions src/plone/exportimport/testing/profiles/testing/types.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<object meta_type="Plone Types Tool"
name="portal_types"
>
<object meta_type="Dexterity FTI"
name="DummyContent"
/>
<object meta_type="Dexterity FTI"
name="Plone Site"
/>
</object>
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<?xml version="1.0" encoding="utf-8"?>
<object xmlns:i18n="http://xml.zope.org/namespaces/i18n"
meta_type="Dexterity FTI"
name="DummyContent"
i18n:domain="plone.exportimport"
>

<!-- Basic properties -->
<property name="title"
i18n:translate=""
>Dummy</property>
<property name="description"
i18n:translate=""
>Dummy content</property>

<property name="allow_discussion">True</property>
<property name="factory">DummyContent</property>

<!-- Hierarchy control -->
<property name="filter_content_types">True</property>
<property name="allowed_content_types">
<element value="Image" />
</property>
<property name="global_allow">True</property>

<!-- Schema, class and security -->
<property name="add_permission">plone.exportimport.testing.dummy.add</property>
<property name="klass">plone.exportimport.testing.content.dummy.DummyContent</property>
<property name="schema">plone.exportimport.testing.content.dummy.IDummyContent</property>

<!-- Enabled behaviors -->
<property name="behaviors"
purge="true"
>
<element value="plone.basic" />
<element value="plone.namefromtitle" />
<element value="volto.blocks" />
<element value="volto.preview_image" />
<element value="plone.categorization" />
<element value="volto.navtitle" />
<element value="plone.excludefromnavigation" />
<element value="plone.shortname" />
</property>

<!-- View information -->
<property name="add_view_expr">string:${folder_url}/++add++DummyContent</property>
<property name="immediate_view">view</property>
<property name="default_view">view</property>
<property name="default_view_fallback">False</property>
<property name="view_methods">
<element value="view" />
</property>
</object>
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<object meta_type="Dexterity FTI"
name="Plone Site"
>
<!-- Enabled behaviors -->
<property name="behaviors"
purge="false"
>
<element value="plone.exportimport.testing.dummy_settings" />
</property>
</object>
2 changes: 1 addition & 1 deletion src/plone/exportimport/utils/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ def get_app(zopeconf: Path):
return app


def get_site(app, site_id: str, logger: logging.Logger) -> PloneSite | None:
def get_site(app, site_id: str, logger: logging.Logger) -> PloneSite:
"""Get Plone Site"""
site = app.unrestrictedTraverse(site_id, None)
if not site:
Expand Down
6 changes: 4 additions & 2 deletions src/plone/exportimport/utils/content/blocks.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@ def _fix_image_paths(data: list) -> list[dict]:
"""
parsed = []
for info in data:
image_scales = info["image_scales"]
image_scales = info.get("image_scales") or {}
for field in image_scales:
if not image_scales[field]:
continue
field_data = image_scales[field][0]
field_data["download"] = f"@@images/{field}"
for key, scale in field_data["scales"].items():
for key, scale in field_data.get("scales", {}).items():
scale["download"] = f"@@images/{field}/{key}"
parsed.append(info)
return parsed
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{
"@id": "/dummy",
"@type": "DummyContent",
"UID": "0e1d9d4a2c3b4f5e8a7b6c5d4e3f2a1b",
"allow_discussion": null,
"blocks": {},
"blocks_layout": {
"items": []
},
"contributors": [],
"created": "2026-09-23T18:00:00+00:00",
"creators": [
"admin"
],
"description": "",
"exportimport.constrains": {},
"exportimport.conversation": [],
"exportimport.versions": {},
"id": "dummy",
"is_folderish": true,
"language": "##DEFAULT##",
"modified": "2026-09-23T18:00:00+00:00",
"parent": {
"@id": "/Plone",
"@type": "Plone Site",
"UID": "plone_site_root"
},
"review_state": "private",
"secure_field": "A secure value",
"secure_setting": false,
"title": "A Dummy content",
"type_title": "Dummy",
"workflow_history": {}
}
14 changes: 14 additions & 0 deletions tests/_resources/cli_import/content/__metadata__.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"__version__": "1.0.0",
"_blob_files_": [],
"_data_files_": [
"plone_site_root/data.json",
"0e1d9d4a2c3b4f5e8a7b6c5d4e3f2a1b/data.json"
],
"default_page": {},
"local_permissions": {},
"local_roles": {},
"ordering": {
"0e1d9d4a2c3b4f5e8a7b6c5d4e3f2a1b": 0
}
}
28 changes: 28 additions & 0 deletions tests/_resources/cli_import/content/plone_site_root/data.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"@id": "/Plone",
"@type": "Plone Site",
"UID": "plone_site_root",
"allow_discussion": null,
"blocks": {},
"blocks_layout": {
"items": []
},
"contributors": [],
"creators": [
"admin"
],
"description": "",
"exportimport.constrains": {},
"exportimport.conversation": [],
"exportimport.versions": {},
"id": "Plone",
"is_folderish": true,
"language": "##DEFAULT##",
"parent": {},
"review_state": null,
"secure_field": "A secure site value",
"secure_setting": false,
"title": "Site",
"type_title": "Plone Site",
"workflow_history": {}
}
Loading
Loading