[pull] main from TryGhost:main - #1527
Merged
Merged
Conversation
no ref Two Core tests failed outside UTC: the cron assertion ignored Bree's active scheduler timezone, and an email fixture used local midnight despite mocking a UTC publication. Match the scheduler's calendar and give the fixture an explicit UTC timestamp.
no ref The deep-queue regression test could time out under load while waiting through 50,000 real event-loop turns. Use controlled `setImmediate` timers, keeping the full queue depth and verifying that other queued work runs before the drain completes.
fixes https://linear.app/ghost/issue/BER-3974/an-integration-reads-the-custom-fields-a-site-defines An integration such as Zapier has to learn which custom fields a site collects before it can write them to a member, and every integration key was refused the definitions with a 403. The Admin API decides which endpoints an integration key may reach by naming the resource from the first segment of the request path, and the definitions are served by a router mounted under /members/, which strips its mount from the path before that check runs, so the check saw a resource called "custom" and refused it. Authenticating as a route alongside the mount, where the path is still whole, restores the access integrations had before the definitions moved behind a mounted router, reads and writes alike. The check itself is left alone: it is due to be replaced as authentication moves to Better Auth, and this keeps the fix to where the routes are declared rather than reshaping something that is about to change.
no ref Flaky test patch. A new post's create acknowledgement could render before React Router published a blocked exit, replacing the intended Posts destination with the new editor URL. Added checking the synchronous navigation guard before replacing that URL, keeping cancellation from reviving stale blocked state.
no ref Admin reloads to /ghost/ when an API request says the session has expired. That also happened on page loads where nobody was signed in yet, and the reload could beat Ember to saving the route the visitor asked for, so they landed on the dashboard after signing in instead of where they were going. The user's path should now be preserved through auth.
no ref Sites created before 2048-bit key generation still sign member and staff tokens with 1024-bit RSA keys, and jsonwebtoken 9 won't sign with those. These rows let a later change rotate each keypair without breaking verifiers: the next key is published before it's used to sign, and the previous public key stays published briefly after the switch. The next public key is derived from its private key, and row `updated_at` records when each was written, so neither needs its own row. The active key stays in the existing settings rows, so downgrading keeps working.
Improves the React editor’s title and feature image layout. Titles match the Ember editor’s typography and resize to fit their content, and feature images keep their original aspect ratio instead of being clipped at 480px. Fixes [PLA-447 — Editor header refinements](https://linear.app/ghost/issue/PLA-447/editor-header-refinements). ## Changes - Match title typography across desktop and mobile, and remove inherited textarea constraints that prevented titles from growing and shrinking correctly. - Place feature image upload and Unsplash controls side by side with a 13px label and 20px gap. Keep the Unsplash icon at 14px inside a circular 32 × 32px button. - Use consistent 13px sans-serif typography for feature image captions, placeholders, and alt text, with a black-and-white selected Alt toggle. - Display full feature images at their original proportions, including tall portrait images. - Reset virtual-list row windows before layout effects so opening Members from the sidebar reliably starts at the top, while Back and breadcrumb navigation retain their saved position. ## Validation - [CI passed](https://github.com/TryGhost/Ghost/actions/runs/36559439696) on `58ec72537c`: all 12 E2E shards, both Admin acceptance shards, both unit-test jobs, lint, and build checks passed. CodeRabbit reported no actionable findings. - Fixed the CI failure in the members virtual-window E2E test. Both Back and breadcrumb flows passed three repeated runs each; all 20 focused virtual-window and scroll-restoration unit tests passed, including a new regression for reset timing. - Post editor, feature image, and X card acceptance suites passed, with regressions covering title resizing and portrait/landscape image clipping. All 12 feature-image tests passed after the final button adjustment. - Admin TypeScript, focused ESLint, formatting, and pre-commit checks passed. - Visually checked the editor at 1280px, 600px, and 390px widths, and verified the Unsplash button’s 32 × 32px dimensions and circular shape. - Full `pnpm check` passed formatting and lint, but the Admin unit suite encountered a Koenig module-resolution failure in `engine/__fixtures__/after-load.test.tsx` and a timeout in the members custom-field country filter test. Neither failing file is changed by this PR.
…31078) no ref Flaky test fix. After returning to a scrolled list, clicking its sidebar link could leave the fresh list at the old position instead of the top (flaky `e2e/tests/admin/members/virtual-window.test.ts`). The reset wrote `scrollTop` directly, but the virtualizer only picks up the new offset from the next frame's `scroll` event. If the shorter fresh list rendered first, it corrected row sizes against the old offset and scrolled back. Scroll writes now dispatch the `scroll` event themselves, and the departing entry's listener ignores it so Back still restores that entry.
no ref Adds React versions of Admin's sign in, 2FA verification, password reset, staff invite signup, setup and sign out. They are served when the new private Labs flag `authReact` is on, and by Ember otherwise. Behaviour, copy and validation match the Ember screens, though there's a visual change that'll need to be reviewed. The purpose is to make these screens Better Auth 'shaped' so that when we're able to turn to implementing that, that there's less churn needed.
no ref Opening Post history on a post with no saved revisions (e.g. created via the API or an import and never saved in the editor) crashed the editor with `Cannot read properties of undefined (reading 'feature_image_caption')`. Since 6.54.1 the `selectedRevision` getter sanitizes the caption without checking that a revision exists; it now returns early, so the preview falls back to the post's own title as before. Covered by a new Ember acceptance test that fails without the fix.
…31079) Refines the React editor settings sidebar so its fields, navigation and metadata tools match the intended editor layout. Fixes [PLA-449 — Sidebar refinements](https://linear.app/ghost/issue/PLA-449/sidebar-refinements). - Keeps every settings pane at the sidebar width, with larger headings, circular back buttons with the same hover background and icon stroke weight as the sidebar toggle, a separator above history and consistent navigation rows with medium-weight labels. - Adds the URL icon and a published-post link, and gives publish date/time equal space with icons and an inline timezone. Adds a shared Shade TimePicker, also used by publishing schedules. - Adds validated canonical URLs (rejecting incomplete schemes and malformed hosts while preserving root-relative paths), restores the Google-style search preview, and gives code injection fields a white background. - Shares token-field sizing between tags, authors and member labels; tag/author chevrons stay at the top right as pills wrap. - Gives X and Facebook image uploaders a white background, subtle dashed border and centered upload icon above the label, matching tag details. - Places the primary header action last before the sidebar toggle; Unpublish and Unschedule precede Update and use ghost styling. - Fixes keyboard-shortcut labels clipped by legacy definition-list CSS. Uses individual Shade keycaps, compact hover rows and larger underlined group headings. Adds a reusable `Kbd variant="contrast"` with a slightly darker background and applies it in the sidebar. - Confirms the existing inline-excerpt flag already hides the sidebar excerpt. Also updates Shade’s disabled primary buttons globally: an opaque light grey surface with softened grey text, with semantic dark-mode colors. This applies in both host modes without a feature flag. The Button stories compare enabled and disabled controls in light/dark and current/legacy modes. Validation: focused editor acceptance suites (including scheduling, all 32 header tests and 9 shortcut tests with Mac/Windows legacy-host CSS regression coverage), 135 focused Admin unit tests, all 284 Shade tests, Admin typecheck, repository lint/boundary checks and commit hooks passed. The canonical URL follow-up passed 36 focused unit tests and 19 metadata acceptance tests, including invalid-URL rejection and recovery. Visually checked desktop and 390px mobile layouts, time-picker states and wrapping tokens in Storybook. Full `pnpm check` encountered Ghost Core test timeouts and temporary-file errors. Two Admin tests also timed out under the full run; both passed on an isolated rerun (44 tests). Koenig browser tests then loaded a different local app occupying port 5174, so the remaining full run was stopped. These failures are outside the sidebar changes. - [x] I've read and followed the Contributor Guide - [x] I've explained my change - [x] I've written automated tests for changed behavior
no ref `generate-data --quantities redirects:0` (or `comments:0`, etc.) silently generated the importer's *default* quantity instead of nothing. The CLI, `DataGenerator` and `TableImporter` already treat an explicit 0 as zero rows, but most importers that override `import()` tested the argument for truthiness: ```js this.quantity = quantity ? quantity / posts.length : 10; ``` These fan out per model, so on a large dataset asking for none of a table gave you the most of it — e.g. ~1.4M redirect rows on a 300k-post dataset, all buffered in `importForEach` before insert. - 21 importers now check `quantity !== undefined`. Defaults (including the function defaults in the recommendation importers) are unchanged, so any invocation without an explicit 0 behaves exactly as before. - `posts-tags-importer` had the same bug split across lines; `comment-reports-importer` used `if (quantity)` and its `Math.max(1, …)` would have produced reports even for 0 — it now returns early. - `offer-redemptions-importer` is intentionally untouched: its `defaultQuantity` is 0, so falsy already means nothing. - Added `DataGenerator` tests covering explicit 0 for `importForEach`-style importers (`posts_authors`, `redirects`) and the default when omitted. The zero test fails without the fix. - README notes the contract for importer authors.
closes https://linear.app/ghost/issue/NY-1631 ## What Members can no longer enter the same automation twice. For example, let's say a publisher has a "paid subscription started" automation. If a member signs up for a paid plan, then downgrades, then signs up *again*, they won't get the automation's emails twice. ## Why In the near future, members will be able to trigger the same automation multiple times more often. For example, we might add an automation like "member has label", which we might poll for every 5 minutes. (This is just an idea.) This change prevents these cases. It also simplifies the current state. Now there's no way a member could go from free → paid → free → paid and get the same automation emails. (We may want to make this configurable in the future.)
towards https://linear.app/ghost/issue/NY-1617 It should be impossible to change an automation's slug, which could cause all sorts of problems. This prevents that. Currently, the frontend deploys before the backend. That's safe because `slug` isn't required by the server, so nothing breaks if we stop sending it. In addition to automated tests, I manually tested this by changing a welcome email and verifying that things worked fine. I think this is useful on its own, but it also makes [some future work](https://linear.app/ghost/issue/NY-1617) easier.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
See Commits and Changes for more details.
Created by
pull[bot] (v2.0.0-alpha.4)
Can you help keep this open source service alive? 💖 Please sponsor : )