From 243c74fe5440a0920f04709883b03ced2012d927 Mon Sep 17 00:00:00 2001 From: David Glick Date: Tue, 16 Apr 2024 15:48:00 -0600 Subject: [PATCH 1/4] Refactor exporter/importer utilities to site adapters --- src/plone/exportimport/cli/__init__.py | 27 ++------- src/plone/exportimport/exporters/__init__.py | 56 +++++++++++-------- src/plone/exportimport/exporters/base.py | 4 +- .../exportimport/exporters/configure.zcml | 9 ++- src/plone/exportimport/exporters/content.py | 6 +- src/plone/exportimport/importers/__init__.py | 46 +++++++++------ .../exportimport/importers/configure.zcml | 9 ++- src/plone/exportimport/interfaces.py | 4 +- tests/conftest.py | 16 ------ tests/exporters/conftest.py | 13 +++-- tests/exporters/test_exporters.py | 43 +++++++------- tests/importers/test_importers.py | 25 ++++----- 12 files changed, 122 insertions(+), 136 deletions(-) diff --git a/src/plone/exportimport/cli/__init__.py b/src/plone/exportimport/cli/__init__.py index 014a1c4..ebad7e4 100644 --- a/src/plone/exportimport/cli/__init__.py +++ b/src/plone/exportimport/cli/__init__.py @@ -1,10 +1,7 @@ -from pathlib import Path from plone import api -from plone.exportimport import interfaces +from plone.exportimport.exporters import get_exporter +from plone.exportimport.importers import get_importer from plone.exportimport.utils import cli as cli_helpers -from Products.CMFPlone.Portal import PloneSite -from zope.component import getUtility -from zope.component.hooks import setSite import argparse import sys @@ -38,13 +35,6 @@ def _parse_args(description: str, options: dict, args: list): return namespace -def _export_site(site: PloneSite, export_path: Path): - setSite(site) - utility = getUtility(interfaces.IExporterUtility, "plone.exporter") - with api.env.adopt_roles(["Manager"]): - return utility.export_site(site, export_path) - - def exporter_cli(args=sys.argv): """Export a Plone site.""" logger = cli_helpers.get_logger("Exporter") @@ -56,19 +46,13 @@ 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) - results = _export_site(site, path) + with api.env.adopt_roles(["Manager"]): + results = get_exporter(site).export_site(path) logger.info(f" Using path {path} to export content from Plone site at /{site.id}") for item in results[1:]: logger.info(f" Wrote {item}") -def _import_site(site: PloneSite, export_path: Path): - setSite(site) - utility = getUtility(interfaces.IImporterUtility, "plone.importer") - with api.env.adopt_roles(["Manager"]): - return utility.import_site(site, export_path) - - def importer_cli(args=sys.argv): """Import content to a Plone site.""" logger = cli_helpers.get_logger("Importer") @@ -80,7 +64,8 @@ def importer_cli(args=sys.argv): logger.error(f"{namespace.path} does not exist, aborting import.") sys.exit(1) site = cli_helpers.get_site(app, namespace.site, logger) - results = _import_site(site, path) + with api.env.adopt_roles(["Manager"]): + results = get_importer(site).import_site(path) logger.info(f" Using path {path} to import content to Plone site at /{site.id}") for item in results: logger.info(f" - {item}") diff --git a/src/plone/exportimport/exporters/__init__.py b/src/plone/exportimport/exporters/__init__.py index ad5cc6d..6a72b8a 100644 --- a/src/plone/exportimport/exporters/__init__.py +++ b/src/plone/exportimport/exporters/__init__.py @@ -5,15 +5,20 @@ from plone.exportimport import PACKAGE_NAME from Products.CMFPlone.Portal import PloneSite from tempfile import mkdtemp +from typing import Dict from typing import List from typing import Optional +from zope.component import getAdapter +from zope.component import hooks from zope.component import queryAdapter -from zope.component.hooks import setSite from zope.interface import implementer -@implementer(interfaces.IExporterUtility) -class ExporterUtility: +ExporterMapping = Dict[str, BaseExporter] + + +@implementer(interfaces.IExporter) +class Exporter: """Export content from a Plone Site.""" exporter_names = ( @@ -24,12 +29,22 @@ class ExporterUtility: "plone.exporter.translations", "plone.exporter.discussions", ) + exporters: ExporterMapping - @staticmethod - def _prepare_site(site: PloneSite) -> PloneSite: - """Use setSite to register the site with global site manager.""" - setSite(site) - return site + def __init__(self, site): + self.site = site + self.exporters = self.all_exporters() + + def all_exporters(self) -> ExporterMapping: + """Return all exporters.""" + exporters = {} + for exporter_name in self.exporter_names: + exporter = queryAdapter( + self.site, interfaces.INamedExporter, name=exporter_name + ) + if exporter: + exporters[exporter_name] = exporter + return exporters @staticmethod def _prepare_path(path: Optional[Path] = None) -> Path: @@ -43,22 +58,17 @@ def _prepare_path(path: Optional[Path] = None) -> Path: path = Path(mkdtemp(prefix=PACKAGE_NAME)) return path - def all_exporters(self, site: PloneSite) -> List[BaseExporter]: - """Return all exporters.""" - exporters = {} - for exporter_name in self.exporter_names: - exporter = queryAdapter(site, interfaces.INamedExporter, name=exporter_name) - if exporter: - exporters[exporter_name] = exporter - return exporters - - def export_site(self, site: PloneSite, path: Optional[Path] = None) -> List[Path]: + def export_site(self, path: Optional[Path] = None) -> List[Path]: """Export the given site to the filesystem.""" - site = self._prepare_site(site) path = self._prepare_path(path) paths: List[Path] = [path] - all_exporters = self.all_exporters(site) - for exporter_name, exporter in all_exporters.items(): - logger.debug(f"Exporting {site} with {exporter_name} to {path}") - paths.extend(exporter.export_data(path)) + with hooks.site(self.site): + for exporter_name, exporter in self.exporters.items(): + logger.debug(f"Exporting {self.site} with {exporter_name} to {path}") + paths.extend(exporter.export_data(path)) return paths + + +def get_exporter(site: PloneSite = None) -> Exporter: + """Get the exporter.""" + return getAdapter(site, interfaces.IExporter) diff --git a/src/plone/exportimport/exporters/base.py b/src/plone/exportimport/exporters/base.py index adb2265..c9db693 100644 --- a/src/plone/exportimport/exporters/base.py +++ b/src/plone/exportimport/exporters/base.py @@ -60,7 +60,7 @@ def export_data( if not base_path.exists(): base_path.mkdir(parents=True) self.base_path = base_path - self.data_hooks = data_hooks if data_hooks else [] - self.obj_hooks = obj_hooks if obj_hooks else [] + self.data_hooks = self.data_hooks or data_hooks or [] + self.obj_hooks = self.obj_hooks or obj_hooks or [] paths = self.dump() return paths diff --git a/src/plone/exportimport/exporters/configure.zcml b/src/plone/exportimport/exporters/configure.zcml index b80c7c9..cefd4a3 100644 --- a/src/plone/exportimport/exporters/configure.zcml +++ b/src/plone/exportimport/exporters/configure.zcml @@ -1,9 +1,8 @@ - - + diff --git a/src/plone/exportimport/exporters/content.py b/src/plone/exportimport/exporters/content.py index f7f2c7d..df8478d 100644 --- a/src/plone/exportimport/exporters/content.py +++ b/src/plone/exportimport/exporters/content.py @@ -90,9 +90,9 @@ def serialize(self, obj: DexterityContent) -> dict: # Enrich for enricher in content_utils.enrichers(): logger.debug(f"{config.logger_prefix} Running {enricher.name}") - addittional = enricher.func(obj, config) - if addittional: - data.update(addittional) + additional = enricher.func(obj, config) + if additional: + data.update(additional) # Apply data hooks for func in self.data_hooks: diff --git a/src/plone/exportimport/importers/__init__.py b/src/plone/exportimport/importers/__init__.py index 95dc0af..1a77de2 100644 --- a/src/plone/exportimport/importers/__init__.py +++ b/src/plone/exportimport/importers/__init__.py @@ -3,14 +3,19 @@ from plone.exportimport import interfaces from plone.exportimport import logger from Products.CMFPlone.Portal import PloneSite +from typing import Dict from typing import List +from zope.component import getAdapter +from zope.component import hooks from zope.component import queryAdapter -from zope.component.hooks import setSite from zope.interface import implementer -@implementer(interfaces.IImporterUtility) -class ImporterUtility: +ImporterMapping = Dict[str, BaseImporter] + + +@implementer(interfaces.IImporter) +class Importer: """Import content into a Plone Site.""" importer_names = ( @@ -21,28 +26,35 @@ class ImporterUtility: "plone.importer.translations", "plone.importer.discussions", ) + importers: ImporterMapping - @staticmethod - def _prepare_site(site: PloneSite) -> PloneSite: - """Use setSite to register the site with global site manager.""" - setSite(site) - return site + def __init__(self, site): + self.site = site + self.importers = self.all_importers() - def all_importers(self, site: PloneSite) -> List[BaseImporter]: + def all_importers(self) -> List[BaseImporter]: """Return all importers.""" importers = {} for importer_name in self.importer_names: - importer = queryAdapter(site, interfaces.INamedImporter, name=importer_name) + importer = queryAdapter( + self.site, interfaces.INamedImporter, name=importer_name + ) if importer: importers[importer_name] = importer return importers - def import_site(self, site: PloneSite, path: Path) -> List[str]: - """Import the given site to the filesystem.""" + def import_site(self, path: Path) -> List[str]: + """Import the given site from the filesystem.""" report = [] - site = self._prepare_site(site) - all_importers = self.all_importers(site) - for importer_name, importer in all_importers.items(): - logger.debug(f"Importing from {path} to {site} with {importer_name}") - report.append(importer.import_data(path)) + with hooks.site(self.site): + for importer_name, importer in self.importers.items(): + logger.debug( + f"Importing from {path} to {self.site} with {importer_name}" + ) + report.append(importer.import_data(path)) return report + + +def get_importer(site: PloneSite = None) -> Importer: + """Get the importer.""" + return getAdapter(site, interfaces.IImporter) diff --git a/src/plone/exportimport/importers/configure.zcml b/src/plone/exportimport/importers/configure.zcml index c306c0f..1af6e2a 100644 --- a/src/plone/exportimport/importers/configure.zcml +++ b/src/plone/exportimport/importers/configure.zcml @@ -1,9 +1,8 @@ - - + diff --git a/src/plone/exportimport/interfaces.py b/src/plone/exportimport/interfaces.py index bd2b3a7..3c0e518 100644 --- a/src/plone/exportimport/interfaces.py +++ b/src/plone/exportimport/interfaces.py @@ -7,11 +7,11 @@ class IExportImportBlobsMarker(Interface): """A marker interface to override default serializers.""" -class IExporterUtility(Interface): +class IExporter(Interface): """Component to export content from a Plone Site.""" -class IImporterUtility(Interface): +class IImporter(Interface): """Component to import content from a Plone Site.""" diff --git a/tests/conftest.py b/tests/conftest.py index c5484d9..eb8e38a 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -64,22 +64,6 @@ def func(base_path: Path, path: Path) -> dict | list: return func -@pytest.fixture -def get_exporter(): - from plone.exportimport import interfaces - from zope.component import getUtility - - return getUtility(interfaces.IExporterUtility, "plone.importer") - - -@pytest.fixture -def get_importer(): - from plone.exportimport import interfaces - from zope.component import getUtility - - return getUtility(interfaces.IImporterUtility, "plone.importer") - - @pytest.fixture def create_example_content(): def func(container, language="en"): diff --git a/tests/exporters/conftest.py b/tests/exporters/conftest.py index 82b715f..4bdaea0 100644 --- a/tests/exporters/conftest.py +++ b/tests/exporters/conftest.py @@ -1,14 +1,16 @@ +from plone.exportimport.importers import get_importer + import pytest import transaction @pytest.fixture() -def portal(app, base_import_path, get_importer, add_users_groups): +def portal(app, base_import_path, add_users_groups): """Plone portal with imported content.""" portal = app["plone"] - importer = get_importer - importer.import_site(site=portal, path=base_import_path) + importer = get_importer(portal) + importer.import_site(path=base_import_path) # Create new users and groups add_users_groups() yield portal @@ -19,15 +21,14 @@ def portal_multilingual( app, setup_multilingual_site, multilingual_import_path, - get_importer, add_users_groups, ): """Plone portal with imported content.""" portal = app["plone"] with transaction.manager: setup_multilingual_site(portal, "en", ["en", "de", "es"]) - importer = get_importer - importer.import_site(site=portal, path=multilingual_import_path) + importer = get_importer(portal) + importer.import_site(path=multilingual_import_path) # Create new users and groups add_users_groups() yield portal diff --git a/tests/exporters/test_exporters.py b/tests/exporters/test_exporters.py index ccc8643..586e583 100644 --- a/tests/exporters/test_exporters.py +++ b/tests/exporters/test_exporters.py @@ -1,22 +1,21 @@ from pathlib import Path -from plone.exportimport import interfaces -from plone.exportimport.exporters import ExporterUtility -from zope.component import getUtility +from plone.exportimport.exporters import Exporter +from plone.exportimport.exporters import get_exporter import pytest -class TestExporterUtility: +class TestExporter: @pytest.fixture(autouse=True) def _init(self, portal): self.src_portal = portal - self.utility = getUtility(interfaces.IExporterUtility, "plone.exporter") + self.exporter = get_exporter(self.src_portal) - def test_utility_is_correct_instance(self): - assert isinstance(self.utility, ExporterUtility) + def test_exporter_is_correct_instance(self): + assert isinstance(self.exporter, Exporter) - def test_utility_all_exporters(self): - exporters = self.utility.all_exporters(self.src_portal) + def test_all_exporters(self): + exporters = self.exporter.all_exporters() assert isinstance(exporters, dict) assert len(exporters) == 6 @@ -31,12 +30,12 @@ def test_utility_all_exporters(self): "plone.exporter.discussions", ], ) - def test_utility_exporter_present(self, exporter_name: str): - exporters = self.utility.all_exporters(self.src_portal) + def test_exporter_present(self, exporter_name: str): + exporters = self.exporter.all_exporters() assert exporter_name in exporters - def test_utility_export_site(self, export_path): - results = self.utility.export_site(self.src_portal, export_path) + def test_export_site(self, export_path): + results = self.exporter.export_site(export_path) assert isinstance(results, list) # First item is always the export path path = results[0] @@ -63,29 +62,27 @@ def test_utility_export_site(self, export_path): ["content/plone_site_root/data.json", False, True], ], ) - def test_utility_export_site_has_file( + def test_export_site_has_file( self, export_path, paths_as_relative, path: str, is_dir: bool, is_file: bool ): - results = paths_as_relative( - export_path, self.utility.export_site(self.src_portal, export_path) - ) + results = paths_as_relative(export_path, self.exporter.export_site(export_path)) assert path in results filepath = (export_path / path).resolve() assert filepath.is_dir() is is_dir assert filepath.is_file() is is_file -class TestExporterUtilityMultilingual: +class TestExporterMultilingual: @pytest.fixture(autouse=True) def _init(self, portal): self.src_portal = portal - self.utility = getUtility(interfaces.IExporterUtility, "plone.exporter") + self.exporter = get_exporter(self.src_portal) - def test_utility_is_correct_instance(self): - assert isinstance(self.utility, ExporterUtility) + def test_exporter_is_correct_instance(self): + assert isinstance(self.exporter, Exporter) - def test_utility_all_exporters(self): - exporters = self.utility.all_exporters(self.src_portal) + def test_all_exporters(self): + exporters = self.exporter.all_exporters() assert isinstance(exporters, dict) assert len(exporters) == 6 diff --git a/tests/importers/test_importers.py b/tests/importers/test_importers.py index d3b3b0e..a63b1e3 100644 --- a/tests/importers/test_importers.py +++ b/tests/importers/test_importers.py @@ -1,21 +1,20 @@ -from plone.exportimport import interfaces -from plone.exportimport.importers import ImporterUtility -from zope.component import getUtility +from plone.exportimport.importers import get_importer +from plone.exportimport.importers import Importer import pytest -class TestImporterUtility: +class TestImporter: @pytest.fixture(autouse=True) def _init(self, portal): self.src_portal = portal - self.utility = getUtility(interfaces.IImporterUtility, "plone.importer") + self.importer = get_importer(self.src_portal) - def test_utility_is_correct_instance(self): - assert isinstance(self.utility, ImporterUtility) + def test_importer_is_correct_class(self): + assert isinstance(self.importer, Importer) - def test_utility_all_importers(self): - importers = self.utility.all_importers(self.src_portal) + def test_all_importers(self): + importers = self.importer.all_importers() assert isinstance(importers, dict) assert len(importers) == 6 @@ -30,8 +29,8 @@ def test_utility_all_importers(self): "plone.importer.discussions", ], ) - def test_utility_importer_present(self, importer_name: str): - importers = self.utility.all_importers(self.src_portal) + def test_importer_present(self, importer_name: str): + importers = self.importer.all_importers() assert importer_name in importers @pytest.mark.parametrize( @@ -42,8 +41,8 @@ def test_utility_importer_present(self, importer_name: str): "RedirectsImporter: Imported 0 redirects", ], ) - def test_utility_import_site(self, base_import_path, msg: str): - results = self.utility.import_site(self.src_portal, base_import_path) + def test_import_site(self, base_import_path, msg: str): + results = self.importer.import_site(base_import_path) assert isinstance(results, list) # One entry per importer assert len(results) == 6 From d100c02a052cd6ffc698b4c25dc069e7f66dde09 Mon Sep 17 00:00:00 2001 From: David Glick Date: Tue, 16 Apr 2024 18:04:39 -0600 Subject: [PATCH 2/4] Don't use transaction.manager, it begins a new transaction and loses previous work --- src/plone/exportimport/importers/content.py | 49 ++++++++++----------- 1 file changed, 24 insertions(+), 25 deletions(-) diff --git a/src/plone/exportimport/importers/content.py b/src/plone/exportimport/importers/content.py index 65ef0e2..7dd8af6 100644 --- a/src/plone/exportimport/importers/content.py +++ b/src/plone/exportimport/importers/content.py @@ -94,33 +94,32 @@ def do_import(self) -> str: objs = [] modified = set() with request_provides(self.request, IExportImportBlobsMarker): - with transaction.manager as tm: - for index, item in enumerate(self.all_objects(), start=1): - obj = self.construct(item) - obj_path = "/".join(obj.getPhysicalPath()) - objs.append(obj_path) + for index, item in enumerate(self.all_objects(), start=1): + obj = self.construct(item) + obj_path = "/".join(obj.getPhysicalPath()) + objs.append(obj_path) + if not index % 100: + transaction.savepoint() + logger.info(f"Handled {index} items...") + for setter in content_utils.metadata_setters(): + data = getattr(self.metadata, setter.name) + logger.info(f"Processing {setter.name}: {len(data)} entries") + for index, uid in enumerate(data, start=index): + value = data[uid] + if setter.func(uid, value): + modified.add(uid) if not index % 100: - tm.savepoint() + transaction.savepoint() logger.info(f"Handled {index} items...") - for setter in content_utils.metadata_setters(): - data = getattr(self.metadata, setter.name) - logger.info(f"Processing {setter.name}: {len(data)} entries") - for index, uid in enumerate(data, start=index): - value = data[uid] - if setter.func(uid, value): - modified.add(uid) - if not index % 100: - tm.savepoint() - logger.info(f"Handled {index} items...") - # Reindex objects - idxs = [ - "allowedRolesAndUsers", - "getObjPositionInParent", - "is_default_page", - "modified", - "created", - ] - content_utils.recatalog_uids(modified, idxs=idxs) + # Reindex objects + idxs = [ + "allowedRolesAndUsers", + "getObjPositionInParent", + "is_default_page", + "modified", + "created", + ] + content_utils.recatalog_uids(modified, idxs=idxs) return f"{self.__class__.__name__}: Imported {len(objs)} objects" def import_data( From 6228d33bc05dad3193af3bb5bdaa3c77ed6853ef Mon Sep 17 00:00:00 2001 From: David Glick Date: Tue, 16 Apr 2024 18:15:22 -0600 Subject: [PATCH 3/4] Handle site=None --- src/plone/exportimport/exporters/__init__.py | 3 +++ src/plone/exportimport/importers/__init__.py | 3 +++ 2 files changed, 6 insertions(+) diff --git a/src/plone/exportimport/exporters/__init__.py b/src/plone/exportimport/exporters/__init__.py index 6a72b8a..50e330f 100644 --- a/src/plone/exportimport/exporters/__init__.py +++ b/src/plone/exportimport/exporters/__init__.py @@ -1,5 +1,6 @@ from .base import BaseExporter from pathlib import Path +from plone import api from plone.exportimport import interfaces from plone.exportimport import logger from plone.exportimport import PACKAGE_NAME @@ -71,4 +72,6 @@ def export_site(self, path: Optional[Path] = None) -> List[Path]: def get_exporter(site: PloneSite = None) -> Exporter: """Get the exporter.""" + if site is None: + site = api.portal.get() return getAdapter(site, interfaces.IExporter) diff --git a/src/plone/exportimport/importers/__init__.py b/src/plone/exportimport/importers/__init__.py index 1a77de2..a2f7536 100644 --- a/src/plone/exportimport/importers/__init__.py +++ b/src/plone/exportimport/importers/__init__.py @@ -1,5 +1,6 @@ from .base import BaseImporter from pathlib import Path +from plone import api from plone.exportimport import interfaces from plone.exportimport import logger from Products.CMFPlone.Portal import PloneSite @@ -57,4 +58,6 @@ def import_site(self, path: Path) -> List[str]: def get_importer(site: PloneSite = None) -> Importer: """Get the importer.""" + if site is None: + site = api.portal.get() return getAdapter(site, interfaces.IImporter) From 10700491d6da61952c14fd721d37a48e1bef6860 Mon Sep 17 00:00:00 2001 From: David Glick Date: Tue, 16 Apr 2024 18:50:06 -0600 Subject: [PATCH 4/4] avoid frontend block serializers; just transport the raw data (based on https://github.com/collective/collective.exportimport/pull/241) --- src/plone/exportimport/deserializers/blob.py | 4 ++-- src/plone/exportimport/deserializers/blocks.py | 15 +++++++++++++++ .../exportimport/deserializers/configure.zcml | 1 + src/plone/exportimport/exporters/content.py | 4 ++-- src/plone/exportimport/importers/content.py | 4 ++-- src/plone/exportimport/interfaces.py | 2 +- src/plone/exportimport/serializers/blob.py | 10 +++++----- src/plone/exportimport/serializers/blocks.py | 15 +++++++++++++++ src/plone/exportimport/serializers/configure.zcml | 2 +- 9 files changed, 44 insertions(+), 13 deletions(-) create mode 100644 src/plone/exportimport/deserializers/blocks.py create mode 100644 src/plone/exportimport/serializers/blocks.py diff --git a/src/plone/exportimport/deserializers/blob.py b/src/plone/exportimport/deserializers/blob.py index d7ec817..94e84c2 100644 --- a/src/plone/exportimport/deserializers/blob.py +++ b/src/plone/exportimport/deserializers/blob.py @@ -1,7 +1,7 @@ from pathlib import Path from plone.dexterity.interfaces import IDexterityContent from plone.exportimport import settings -from plone.exportimport.interfaces import IExportImportBlobsMarker +from plone.exportimport.interfaces import IExportImportRequestMarker from plone.exportimport.utils import path as path_utils from plone.namedfile.interfaces import INamedField from plone.restapi.deserializer.dxfields import DefaultFieldDeserializer @@ -24,7 +24,7 @@ def load_blob(path: str) -> bytes: return codecs.decode(data, "base64") -@adapter(INamedField, IDexterityContent, IExportImportBlobsMarker) +@adapter(INamedField, IDexterityContent, IExportImportRequestMarker) @implementer(IFieldDeserializer) class ExportImportNamedFieldDeserializer(DefaultFieldDeserializer): def __call__(self, value): diff --git a/src/plone/exportimport/deserializers/blocks.py b/src/plone/exportimport/deserializers/blocks.py new file mode 100644 index 0000000..d7c1888 --- /dev/null +++ b/src/plone/exportimport/deserializers/blocks.py @@ -0,0 +1,15 @@ +from plone.exportimport.interfaces import IExportImportRequestMarker +from plone.restapi.behaviors import IBlocks +from plone.restapi.deserializer.dxfields import DefaultFieldDeserializer +from plone.restapi.interfaces import IFieldDeserializer +from plone.schema import IJSONField +from zope.component import adapter +from zope.interface import implementer + + +@implementer(IFieldDeserializer) +@adapter(IJSONField, IBlocks, IExportImportRequestMarker) +class ExportImportBlocksDeserializer(DefaultFieldDeserializer): + """We skip the subscribers that deserialize the blocks from the frontend. + We only need the raw data. + """ diff --git a/src/plone/exportimport/deserializers/configure.zcml b/src/plone/exportimport/deserializers/configure.zcml index c8b5b4b..699b793 100644 --- a/src/plone/exportimport/deserializers/configure.zcml +++ b/src/plone/exportimport/deserializers/configure.zcml @@ -1,3 +1,4 @@ + diff --git a/src/plone/exportimport/exporters/content.py b/src/plone/exportimport/exporters/content.py index df8478d..51c31ba 100644 --- a/src/plone/exportimport/exporters/content.py +++ b/src/plone/exportimport/exporters/content.py @@ -7,7 +7,7 @@ from plone.exportimport import logger from plone.exportimport import settings from plone.exportimport import types -from plone.exportimport.interfaces import IExportImportBlobsMarker +from plone.exportimport.interfaces import IExportImportRequestMarker from plone.exportimport.utils import content as content_utils from plone.exportimport.utils import request_provides from typing import Callable @@ -130,7 +130,7 @@ def dump_metadata(self) -> Path: def dump(self) -> List[Path]: """Serialize contents and dump them to disk.""" paths = [] - with request_provides(self.request, IExportImportBlobsMarker): + with request_provides(self.request, IExportImportRequestMarker): for obj in self.all_objects(): path = self.dump_one(obj) if path: diff --git a/src/plone/exportimport/importers/content.py b/src/plone/exportimport/importers/content.py index 7dd8af6..f6d94a6 100644 --- a/src/plone/exportimport/importers/content.py +++ b/src/plone/exportimport/importers/content.py @@ -4,7 +4,7 @@ from plone.exportimport import logger from plone.exportimport import settings from plone.exportimport import types -from plone.exportimport.interfaces import IExportImportBlobsMarker +from plone.exportimport.interfaces import IExportImportRequestMarker from plone.exportimport.utils import content as content_utils from plone.exportimport.utils import request_provides from typing import Callable @@ -93,7 +93,7 @@ def construct(self, item: dict) -> DexterityContent: def do_import(self) -> str: objs = [] modified = set() - with request_provides(self.request, IExportImportBlobsMarker): + with request_provides(self.request, IExportImportRequestMarker): for index, item in enumerate(self.all_objects(), start=1): obj = self.construct(item) obj_path = "/".join(obj.getPhysicalPath()) diff --git a/src/plone/exportimport/interfaces.py b/src/plone/exportimport/interfaces.py index 3c0e518..0f3c732 100644 --- a/src/plone/exportimport/interfaces.py +++ b/src/plone/exportimport/interfaces.py @@ -3,7 +3,7 @@ from zope.interface import Interface -class IExportImportBlobsMarker(Interface): +class IExportImportRequestMarker(Interface): """A marker interface to override default serializers.""" diff --git a/src/plone/exportimport/serializers/blob.py b/src/plone/exportimport/serializers/blob.py index 4b1179a..6202fec 100644 --- a/src/plone/exportimport/serializers/blob.py +++ b/src/plone/exportimport/serializers/blob.py @@ -4,7 +4,7 @@ from plone.exportimport import logger from plone.exportimport import settings from plone.exportimport import types -from plone.exportimport.interfaces import IExportImportBlobsMarker +from plone.exportimport.interfaces import IExportImportRequestMarker from plone.exportimport.utils import content as content_utils from plone.exportimport.utils import path as path_utils from plone.namedfile.interfaces import INamedBlobFileField @@ -19,7 +19,7 @@ from zope.interface import implementer -@adapter(INamedBlobFileField, IDexterityContent, IExportImportBlobsMarker) +@adapter(INamedBlobFileField, IDexterityContent, IExportImportRequestMarker) @implementer(IFieldSerializer) class ExportImportFileFieldSerializer(DefaultFieldSerializer): def __call__(self): @@ -37,13 +37,13 @@ def __call__(self): return json_compatible(result) -@adapter(INamedFileField, IDexterityContent, IExportImportBlobsMarker) +@adapter(INamedFileField, IDexterityContent, IExportImportRequestMarker) @implementer(IFieldSerializer) class ExportImportSimpleFileFieldSerializer(ExportImportFileFieldSerializer): """Same as above but less specific field interface""" -@adapter(INamedBlobImageField, IDexterityContent, IExportImportBlobsMarker) +@adapter(INamedBlobImageField, IDexterityContent, IExportImportRequestMarker) @implementer(IFieldSerializer) class ExportImportImageFieldSerializer(DefaultFieldSerializer): def __call__(self): @@ -65,7 +65,7 @@ def __call__(self): return json_compatible(result) -@adapter(INamedImageField, IDexterityContent, IExportImportBlobsMarker) +@adapter(INamedImageField, IDexterityContent, IExportImportRequestMarker) @implementer(IFieldSerializer) class ExportImportSimpleImageFieldSerializer(ExportImportImageFieldSerializer): """Same as above but less specific field interface""" diff --git a/src/plone/exportimport/serializers/blocks.py b/src/plone/exportimport/serializers/blocks.py new file mode 100644 index 0000000..7d37c48 --- /dev/null +++ b/src/plone/exportimport/serializers/blocks.py @@ -0,0 +1,15 @@ +from plone.exportimport.interfaces import IExportImportRequestMarker +from plone.restapi.behaviors import IBlocks +from plone.restapi.interfaces import IFieldSerializer +from plone.restapi.serializer.dxfields import DefaultFieldSerializer +from plone.schema import IJSONField +from zope.component import adapter +from zope.interface import implementer + + +@adapter(IJSONField, IBlocks, IExportImportRequestMarker) +@implementer(IFieldSerializer) +class ExportImportBlocksSerializer(DefaultFieldSerializer): + """We skip the subscribers that serialize the blocks for the frontend. + We only need the raw data. + """ diff --git a/src/plone/exportimport/serializers/configure.zcml b/src/plone/exportimport/serializers/configure.zcml index a3372e7..ede113e 100644 --- a/src/plone/exportimport/serializers/configure.zcml +++ b/src/plone/exportimport/serializers/configure.zcml @@ -3,5 +3,5 @@ - +