Skip to content

fix(core): import password-protected WordPress posts as drafts - #4742

Open
danielmlr wants to merge 4 commits into
mainfrom
daniel/wp-import-password-drafts
Open

danielmlr wants to merge 4 commits into
mainfrom
daniel/wp-import-password-drafts

Conversation

@danielmlr

Copy link
Copy Markdown
Contributor

What does this PR do?

Imports password-protected WordPress posts as drafts instead of publishing them. The parser reads each post's password from a WXR export, but neither the admin import nor emdash import wordpress used it. So a published, protected post came in published. After an admin import, anyone could read it on its page, in the post list and in the RSS feed.

EmDash has no per-entry password, so both importers now bring such a post in as a draft and never schedule it, as the admin import already does with private posts. The Import Complete screen and the CLI output list these posts. The EmDash Exporter doesn't send post passwords, so imports through it are unchanged.

Related: Discussion #345 proposes per-post passwords.

Type of change

  • Bug fix
  • Feature (requires an accepted feature plan or RFC)
  • Refactor (no behavior change)
  • Translation
  • Documentation
  • Performance improvement
  • Tests
  • Chore (dependencies, CI, tooling)

Your connection to this change

  • I use EmDash and ran into the problem this PR addresses
  • I maintain a tool, dependency, or service that EmDash uses, and this change concerns that integration
  • A Maintainer approved this specific change before I opened the PR

Checklist

  • I have read CONTRIBUTING.md
  • pnpm typecheck passes
  • pnpm lint passes
  • pnpm test passes (or targeted tests for my change)
  • pnpm format has been run
  • I have added/updated tests for my changes (if applicable)
  • User-visible strings in the admin UI are wrapped for translation (if applicable). Do not include messages.po changes except in translation PRs — a workflow extracts catalogs on merge to main.
  • I have added and reviewed the user-facing changeset (if this PR changes a published package)
  • New features link to their merged design PR: https://github.com/emdash-cms/emdash/pull/... (n/a: bug fix)
  • I have included screenshots below if this PR changes the UI

AI assistance disclosure

  • This PR includes AI-generated code or text — model/tool: Claude Opus 5.5

Screenshots / test output

The Import Complete screen after importing a WXR file with one public post and one password-protected post:

Before After
Import Complete screen before the change: two content items imported, no mention of the password-protected post Import Complete screen after the change: a Password-Protected Entries card lists the post Members only, imported as a draft, with a link to it
Arabic (right to left)

Import Complete screen in Arabic after the change: the Password-Protected Entries card is mirrored, with the post title on the right and the collection on the left

The new strings show in English until the catalogs are extracted after merge.

The new cases in wxr-execute-chunked.test.ts and cli/import-wordpress.test.ts fail on main, where a published post with a password comes in published.

A WXR export carries each post's password in wp:post_password. The
parser read it into postPassword, but neither the admin WXR import nor
`emdash import wordpress` looked at it, so a published, password-protected
post came in as published and anyone could read it on the new site.

EmDash has no per-entry password, so both importers now bring such a
post in as a draft, whatever its WordPress status, and never schedule it.
The import result lists these posts: the admin's Import Complete screen
shows them with a link to each entry, and the CLI prints them and adds
them to its --json result. The EmDash Exporter plugin route is unchanged,
because the exporter does not send post passwords.
…us table

The content import page's status table still said a published post
always comes in published, which the WXR import no longer does for a
post with a password. The execute response sentence now names
passwordProtected only for posts the request imports, since posts it
skips are not listed. The changeset's first sentence reads in one pass.
The card lists only entries that had a password in WordPress, but
ordinary WordPress drafts are imported as drafts too, so a heading of
"Imported as Drafts (1)" undercounted what the import brought in as
drafts. The heading now names what the card lists.
The Coming from WordPress page sums up the import's status mapping as
publish to published and everything else to drafts. A WXR import now
brings in a published, password-protected post as a draft, so the
summary names that case, in the same words as Migrate from WordPress.
@changeset-bot

changeset-bot Bot commented Nov 7, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: b3e9724

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 12 packages
Name Type
emdash Patch
@emdash-cms/admin Patch
@emdash-cms/cloudflare Patch
@emdash-cms/plugin-test Patch
@emdash-cms/sandbox-workerd Patch
@emdash-cms/auth Patch
@emdash-cms/blocks Patch
create-emdash Patch
@emdash-cms/gutenberg-to-portable-text Patch
@emdash-cms/x402 Patch
@emdash-cms/auth-atproto Patch
@emdash-cms/plugin-embeds Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@emdashbot emdashbot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The approach is right. EmDash has no per-entry passwords, so importing password-protected WordPress posts as unscheduled drafts avoids publishing content that was meant to be protected. It matches how private posts are already handled. Both reviewers approved and found nothing that needs fixing. They checked the admin /api/import/wordpress/execute route, the CLI emdash import wordpress --execute, chunk-result merging in the admin client, the new Import Complete card, the regression tests, the docs and the changeset. Two minor notes are not defects in this change. Posts skipped via skipExisting or on CLI resume are not listed as password-protected. The exported wxrSource adapter still maps status without looking at the password, though no route uses its fetchContent. Review was static, and tests, builds and lint were not run.


Reviewed by anthropic/claude-opus-5-5 and openai/gpt-6.1-sol.

@cloudflare-workers-and-pages

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
✅ Deployment successful!
View logs
docs b3e9724 Nov 07 2026, 05:02 PM

@cloudflare-workers-and-pages

Copy link
Copy Markdown

🚀 Deploying Preview to Cloudflare 🚀

Preview URL: https://daniel-wp-import-password-drafts.try.emdashcms.com, https://daniel-wp-import-password-drafts-emdash-playground.emdash-cms.workers.dev (commit b3e9724)

This URL reflects your latest Preview deployment

Preview Deployments by commit

Status Deployment URL Commit Updated (UTC) See this deployment's details
  • Build: Success ✅
  • Deployment: Success ✅

View logs ↗
https://f58df6e9.try.emdashcms.com, https://f58df6e9-emdash-playground.emdash-cms.workers.dev b3e9724 2026-11-07T17:03:57.069Z Visit the dashboard ↗

@pkg-pr-new

pkg-pr-new Bot commented Nov 7, 2026

Copy link
Copy Markdown

Open in StackBlitz

@emdash-cms/admin

npm i https://pkg.pr.new/@emdash-cms/admin@4742

@emdash-cms/auth

npm i https://pkg.pr.new/@emdash-cms/auth@4742

@emdash-cms/auth-atproto

npm i https://pkg.pr.new/@emdash-cms/auth-atproto@4742

@emdash-cms/blocks

npm i https://pkg.pr.new/@emdash-cms/blocks@4742

@emdash-cms/cloudflare

npm i https://pkg.pr.new/@emdash-cms/cloudflare@4742

@emdash-cms/contentful-to-portable-text

npm i https://pkg.pr.new/@emdash-cms/contentful-to-portable-text@4742

emdash

npm i https://pkg.pr.new/emdash@4742

create-emdash

npm i https://pkg.pr.new/create-emdash@4742

@emdash-cms/gutenberg-to-portable-text

npm i https://pkg.pr.new/@emdash-cms/gutenberg-to-portable-text@4742

@emdash-cms/plugin-cli

npm i https://pkg.pr.new/@emdash-cms/plugin-cli@4742

@emdash-cms/plugin-test

npm i https://pkg.pr.new/@emdash-cms/plugin-test@4742

@emdash-cms/plugin-types

npm i https://pkg.pr.new/@emdash-cms/plugin-types@4742

@emdash-cms/registry-client

npm i https://pkg.pr.new/@emdash-cms/registry-client@4742

@emdash-cms/registry-lexicons

npm i https://pkg.pr.new/@emdash-cms/registry-lexicons@4742

@emdash-cms/registry-loader

npm i https://pkg.pr.new/@emdash-cms/registry-loader@4742

@emdash-cms/registry-moderation

npm i https://pkg.pr.new/@emdash-cms/registry-moderation@4742

@emdash-cms/registry-verification

npm i https://pkg.pr.new/@emdash-cms/registry-verification@4742

upgrade-emdash

npm i https://pkg.pr.new/upgrade-emdash@4742

@emdash-cms/sandbox-workerd

npm i https://pkg.pr.new/@emdash-cms/sandbox-workerd@4742

@emdash-cms/x402

npm i https://pkg.pr.new/@emdash-cms/x402@4742

@emdash-cms/plugin-ai-moderation

npm i https://pkg.pr.new/@emdash-cms/plugin-ai-moderation@4742

@emdash-cms/plugin-ai-search

npm i https://pkg.pr.new/@emdash-cms/plugin-ai-search@4742

@emdash-cms/plugin-atproto

npm i https://pkg.pr.new/@emdash-cms/plugin-atproto@4742

@emdash-cms/plugin-audit-log

npm i https://pkg.pr.new/@emdash-cms/plugin-audit-log@4742

@emdash-cms/plugin-color

npm i https://pkg.pr.new/@emdash-cms/plugin-color@4742

@emdash-cms/plugin-embeds

npm i https://pkg.pr.new/@emdash-cms/plugin-embeds@4742

@emdash-cms/plugin-field-kit

npm i https://pkg.pr.new/@emdash-cms/plugin-field-kit@4742

@emdash-cms/plugin-forms

npm i https://pkg.pr.new/@emdash-cms/plugin-forms@4742

@emdash-cms/plugin-webhook-notifier

npm i https://pkg.pr.new/@emdash-cms/plugin-webhook-notifier@4742

commit: b3e9724

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant