Skip to content

Export parts of a site with --path and --paths - #112

Merged
ericof merged 2 commits into
mainfrom
issue-86
Oct 1, 2026
Merged

ericof merged 2 commits into
mainfrom
issue-86

Conversation

@ericof

@ericof ericof commented Sep 25, 2026

Copy link
Copy Markdown
Member

Summary

Adds a partial export to plone-exporter: only the listed content, and everything inside it, is exported. The request is described in #86: exports of huge sites are too large to hand to developers or external partners, and may contain data that should not leave the site.

Command line

Two new options, which can be combined:

  • --path CONTENT_PATH: a content path, relative to the site root. Can be repeated.
  • --paths FILE: a file with one content path per line. Empty lines and lines starting with # are ignored.
plone-exporter instance/etc/zope.conf Plone /tmp/plone_data/ --path /news --path /about/team
plone-exporter instance/etc/zope.conf Plone /tmp/plone_data/ --paths paths.txt
  • Each path selects that content and everything inside it. Parents are not added, so exporting /about/team does not export /about or the site root.
  • A path without content is skipped with a warning. If no path has content, or the paths file does not exist, the export aborts.
  • Paths are checked in the catalog, not by traversal: acquisition would otherwise resolve /folder/page to /page.
  • Without these options, the whole site is exported, as before.

Other exporters

When a partial export is requested, the other exporters keep only data related to the exported content:

Exporter Kept
relations relations whose source and target were exported
translations exported members of each translation group; groups left with a single member are dropped
discussions conversations on exported content
portlets assignments on exported content
redirects redirects pointing to exported content
principals everything (users and groups are not content)

Implementation

  • The CLI turns the paths into a catalog query, stored as options.query. ContentExporter already used options.query; an explicit query argument to export_data still takes precedence.
  • BaseExporter.exported_content() returns the UID and path of the exported content, or None for a full export. It runs the catalog query itself, so every exporter also works on its own.
  • The utils functions behind each exporter get an optional uids (or paths) argument. Existing callers are unaffected.
  • CLI_SPEC options are now argparse.add_argument keyword dicts, instead of help strings.

Tests

  • tests/exporters/test_exporters_partial.py: content and each filtered exporter, on the multilingual fixture. Every filtering test fails when the filtering is disabled.
  • tests/cli/test_cli_exporter_partial.py: the real CLI entry point with --path and --paths.
  • tests/utils/test_utils_cli.py: path file parsing and query building.

This PR also fixes an intermittent failure in test_export_import_roundtrip. The test compared two exported ZIP files byte by byte. Each ZIP entry stores the file modification time, with a 2 second resolution, so two exports made a few seconds apart could differ even though their data was the same. The test now compares file names and contents.

Documentation

The command line options need to be documented in docs/admin-guide/export-import.md in plone/documentation. A follow-up issue will be opened there.

Closes #86

plone-exporter accepts --path (repeatable) and --paths FILE, with content
paths relative to the site root. Each path selects that content and
everything inside it. Paths without content are skipped with a warning;
the export aborts if none is left.

The paths become options.query, used by the content exporter. The
relations, translations, discussions, portlets and redirects exporters
keep only data related to the exported content. Principals are exported
in full.

Refs #86
Each ZIP entry stores the file modification time, with a 2 second
resolution, so two exports made a few seconds apart differ byte by byte
even when their data is the same. Compare file names and contents instead.
@mister-roboto

Copy link
Copy Markdown

@ericof thanks for creating this Pull Request and helping to improve Plone!

TL;DR: Finish pushing changes, pass all other checks, then paste a comment:

@jenkins-plone-org please run jobs

To ensure that these changes do not break other parts of Plone, the Plone test suite matrix needs to pass, but it takes 30-60 min. Other CI checks are usually much faster and the Plone Jenkins resources are limited, so when done pushing changes and all other checks pass either start all Jenkins PR jobs yourself, or simply add the comment above in this PR to start all the jobs automatically.

Happy hacking!

@ericof

ericof commented Sep 25, 2026

Copy link
Copy Markdown
Member Author

@jenkins-plone-org please run jobs

@gforcada

Copy link
Copy Markdown
Member

Nice addition, but unfortunately for sites that have some folders with LOTS of content inside, that's not going to be that easy to handle with this approach.

That's why I created the ObjectsExporter so you can customize, within your deployed code, what is to be exported.

The three options are all valid (full export, path based, or custom logic), so we don't need to remove any of them, but ensure we keep all them working. 👍

@ericof

ericof commented Sep 28, 2026

Copy link
Copy Markdown
Member Author

@gforcada Could we merge this, then?

@ericof
ericof merged commit 7898772 into main Oct 1, 2026
12 checks passed
@ericof
ericof deleted the issue-86 branch October 1, 2026 00:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Export parts of the site

3 participants