Skip to content

feat(editor): choose the beehiiv publication on each post - #191

Merged
phpbits merged 14 commits into
release/1.1.0from
feature/19812409-post-selector
Oct 15, 2026
Merged

phpbits merged 14 commits into
release/1.1.0from
feature/19812409-post-selector

Conversation

@phpbits

@phpbits phpbits commented Oct 8, 2026

Copy link
Copy Markdown
Collaborator

Summary

Publishers can now pick which beehiiv publication each post goes to, instead of every post using the single site-wide publication. The settings page keeps a "Default publication", which new and older posts fall back to. Both the settings page and the editor can refresh the publication list from beehiiv.

Depends on the E2E/PHPUnit setup PR (#). Review that first, or review only the 11 feature commits here.

Changes

Editor (editor-integration/post-level-newsletter-overrides/post-publication-selector)

  • New "Publication" selector above the post template in the beehiiv panel. The default publication is preselected, and a placeholder shows when no default is set.
  • Only users who can publish posts can change a post's publication.
  • Posts without a saved publication use the site default.
  • The template list, the refresh action and the "(default)" label all follow the chosen publication.
  • A publication or template change moves a scheduled, unsent newsletter. Cancelling unlocks it and keeps the choice.
  • The publication and template lock once beehiiv sends the newsletter.
  • Readiness is checked per post. A publication that has been disconnected falls back to the default and shows a notice.
  • New Refresh publications control next to the selector. It reloads the list from beehiiv, bypassing the cache, keeps the post's choice, and shows a confirmation. It is unavailable once the newsletter is sent.

Settings

  • The settings field is now labelled "Default publication".
  • New Refresh publications control on the settings page. It reloads the list from beehiiv, bypassing the cache, keeps the selected default, and shows a confirmation. The editor reads the same refreshed list.

Newsletter send

  • The newsletter is created in, and recorded against, the post's publication.
  • Sync, reschedule, cancel and the sent check use the recorded publication.
  • New PublicationResolver and PublicationsController (REST) handle the publication lookup. Sender.php has the largest changes.

Tests and tooling

  • New post-publication-selector.spec.js (E2E) covering AC-001 to AC-022.
  • New PHPUnit tests: NewsletterPublicationResolverTest, NewsletterPublicationSendTest and RestPublicationsControllerTest. EditorPostSettingsTest is extended.
  • The settings-storage spec now expects the two new editor config keys, defaultPublicationId and publications.
  • The template-refresh E2E tests now mock beehiiv's template list, so they no longer hit the live API with a fake token and time out.
  • The tests environment fails WordPress.org requests instantly. Core's update checks used to stall the first wp-admin load past the 30s test timeout on slow networks. beehiiv requests are unaffected.
  • Fixes a race in the shared loginAs helper. wp-login.php refocuses the username field on a timer, so the password could be typed into the username field. The helper now re-fills until both fields hold the expected values.

Known gaps

AC-013 test send for the post's publication is pending feature/19812449-send-test-email.

How to test

  1. In settings, check that the field reads "Default publication" and that "Refresh publications" picks up a newly created beehiiv publication.
  2. Open a post. The Publication selector should appear above Post template with the default preselected. Posts with no saved publication use the default.
  3. Change the post's publication. The template list should change to match, and the publication should be saved with the post.
  4. Send the newsletter. It should be created in the post's publication. With a scheduled, unsent newsletter, changing the publication or template should move it. Cancelling should unlock it and keep the choice.
  5. Once beehiiv has sent it, the publication and template should be locked, and Refresh publications should be unavailable.
  6. Disconnect the chosen publication in beehiiv. The post should fall back to the default and show a notice.
  7. Confirm users without publish rights can't change the selector.
npm run test:e2e
npx wp-env run tests-cli -- bash -c "cd wp-content/plugins/beehiiv && composer test"

phpbits added 12 commits October 8, 2026 20:08
Feature: editor-integration/post-level-newsletter-overrides/post-publication-selector

Implements:
- US-001: Choose a publication per post
- US-002: Templates follow the chosen publication
- US-003: Newsletter goes to the post's publication
- US-004: Locked after send
- US-005: Readiness and fallback
- US-006: Settings label

Verified:
- AC-001..AC-004: Publication selector above Post template, default preselected, placeholder when no default ✓
- AC-005: Posts without a publication use the site default ✓
- AC-006: Only publish_posts users can change a post's publication ✓
- AC-007..AC-011: Template list, refresh and "(default)" label follow the chosen publication ✓
- AC-012: Newsletter created in, and recorded against, the post's publication ✓
- AC-013: Sync, reschedule, cancel and sent check use the recorded publication ✓ (test send pending feature/19812449-send-test-email)
- AC-014: Publication or template change moves a scheduled, unsent newsletter ✓
- AC-015: Cancel unlocks and keeps the choice ✓
- AC-016..AC-017: Publication and template lock once beehiiv sends ✓
- AC-018: Per-post readiness ✓
- AC-019: Disconnected publication falls back to the default with a notice ✓
- AC-020: Settings field reads "Default publication" ✓
…ngs page

Feature: editor-integration/post-level-newsletter-overrides/post-publication-selector

Implements:
- US-006: Settings: default publication (Refresh publications)

Verified:
- AC-021: Refresh publications reloads the list from beehiiv, bypassing the cache, keeps the selection and confirms; the editor reads the same refreshed list ✓
…ation selector

Feature: editor-integration/post-level-newsletter-overrides/post-publication-selector

Implements:
- US-001: Choose a publication per post (Refresh publications in the editor)

Verified:
- AC-022: Refresh publications next to the post's Publication selector reloads the list from beehiiv, bypassing the cache, keeps the post's choice, confirms, and is unavailable once sent ✓
…lication selector

The settings storage spec pins the exact key set of window.beehiivPostSettings.
PRD-06.8.02 added defaultPublicationId and publications to that object, which
failed AC-003, AC-004 and AC-006 even though settings retrieval is unchanged.
…mplates E2E tests

AC-008 and AC-009 let the refresh hit the live beehiiv API with a fake token,
waiting out a failed request plus a failed token refresh (up to 30s each),
which intermittently exceeded the 45s test timeout. Mock the list call
instead; AC-008 now asserts the cache holds the new list rather than being
empty.
…efresh-after-save E2E test

AC-013's save clears the template cache, so the settings page that loads after
the redirect fetched templates from the live beehiiv API with a fake token
(failed request plus failed token refresh), intermittently exceeding the 30s
test timeout. Mock the list call with the new template and assert it replaces
the old one after the save.
…ment

WordPress core runs its core/plugin/theme update checks synchronously on the
first wp-admin load after its 12-hour cache expires. On a slow network that
load stalled past the 30s test timeout, so whichever test logged in first
failed with page.waitForURL('**/wp-admin/**') timing out. No spec needs
WordPress.org; beehiiv requests are unaffected.
wp-login.php focuses and selects the username field on a timer after
load. When it fired between the two fills, the password was typed into
the username field, the empty password blocked the submit, and the
waitForURL timed out (seen in oauth-authorization AC-005's beforeEach).
Re-fill until both fields hold the expected values before submitting.
…selector

Brings in the CI fixes (lint, PHPUnit, PHP 7.4/8.0 wp-env images, asset
build before E2E, 40-minute E2E timeout) and the shared E2E helper updates.

Conflicts resolved:
- includes/Editor/PostSettings.php: keep both the readonly-key guard in
  authorize_meta() and the publish_posts gate for the post's publication.
- tests/e2e/specs/publication-template-config.spec.js: keep the mocked
  beehiiv template list (no live API call, no 45s timeout) in the reformatted
  tests.

Follow-up fixes in this merge:
- The readonly guard made every block-editor save of a never-sent post fail
  with a 403, because the editor sends all registered meta, including empty
  readonly keys that have no stored row. Strip readonly beehiiv keys from
  post REST writes before WordPress applies them, so they stay unwritable via
  REST without failing the save. Covered by a new PHPUnit test.
- Lint: format post-publication-selector.spec.js and give its helpers typed
  JSDoc params.

Verified: npm run lint clean; PHPUnit 30 tests OK (1 skipped); full E2E suite
113 expected, 0 failed, 0 flaky.
@phpbits phpbits mentioned this pull request Oct 13, 2026
5 tasks
…2409-post-selector

# Conflicts:
#	includes/Editor/PostSettings.php
#	includes/Newsletter/PostSettingsBuilder.php
#	src/js/editor/post-settings/hooks/use-beehiiv-post-meta.js
#	src/js/editor/post-settings/index.js
…2409-post-selector

# Conflicts:
#	includes/Newsletter/Sender.php
#	src/js/editor/post-settings/components/newsletter-template-select.js
#	tests/e2e/utils/auth.js
@phpbits
phpbits merged commit 3c270c2 into release/1.1.0 Oct 15, 2026
19 of 20 checks passed
@phpbits
phpbits deleted the feature/19812409-post-selector branch October 15, 2026 13:45
phpbits added a commit that referenced this pull request Oct 16, 2026
Conflicts resolved:
- tests/e2e/utils/auth.js: both branches fixed the wp-login focus race.
  Wait for the page's own focus first (release/1.1.0), then fill with
  the verify-and-retry loop.
- tests/e2e/plugins/beehiiv-options.php: keep release/1.1.0's request
  log, per-mock needle and WordPress.org blocking; add back the regex
  pattern, delay, response sequence and headers, the answering mock's
  key in the log, and the test-send filters the test-email spec uses.
- languages/beehiiv.pot: regenerated from the merged code.

Integration fixes for the merged features:
- Test sends use the post's resolved publication (per-post publication,
  #191) for the temporary draft, the linked newsletter and the payload,
  not the site-wide default.
- A newsletter the sync flow has recorded as sent (#192) can't be
  tested, even while its stored schedule is still in the future.
- PHPUnit covers both.
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.

1 participant