Skip to content

Include site title in members export filename#28358

Merged
9larsons merged 2 commits into
TryGhost:mainfrom
puneetdixit200:fix-members-export-filename
Jun 5, 2026
Merged

Include site title in members export filename#28358
9larsons merged 2 commits into
TryGhost:mainfrom
puneetdixit200:fix-members-export-filename

Conversation

@puneetdixit200
Copy link
Copy Markdown
Contributor

Fixes #28310

Summary

  • add a members export filename helper that prefixes CSV exports with the slugified site title
  • pass the existing settings title value from the members page into the export action
  • keep the current ghost.members.YYYY-MM-DD.csv fallback when the site title is unavailable

Verification

  • pnpm vitest run test/unit/views/members/members-actions.test.tsx from apps/posts (7 tests passed)
  • pnpm --dir apps/posts exec eslint src/views/members/components/members-actions.tsx test/unit/views/members/members-actions.test.tsx
  • commit hook ESLint also covered apps/posts/src/views/members/members.tsx
  • pnpm --dir apps/posts exec tsc --noEmit
  • git diff --check
  • pnpm --filter @tryghost/posts build got through TypeScript and then failed in Vite/Rollup on virtual \0react with Node v24.15.0; this checkout warns the repo expects Node ^22.13.1

AI-assisted: yes. I reviewed the generated changes and test output before opening this PR.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jun 4, 2026

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: e93bef4f-2887-4e37-80e2-02b5421e84ca

📥 Commits

Reviewing files that changed from the base of the PR and between 87f82a7 and ca8eeae.

📒 Files selected for processing (1)
  • e2e/tests/admin/members/export.test.ts

Walkthrough

This PR adds the site title to the members CSV export filename to match the naming convention of JSON content exports. A new getMembersExportFileName() function generates filenames in the format <site-title>.ghost.members.YYYY-MM-DD.csv, with a fallback to ghost when the title is absent. The exportMembers() function accepts an optional siteTitle parameter and uses the new filename generator. The site title is threaded through the component hierarchy from the Members wrapper (which reads it from settings) through MembersPage to MembersActions. The export handler now passes the site title and includes error toast feedback on failures. Tests validate the filename generation logic and export behavior.

Possibly related PRs

  • TryGhost/Ghost#28362: Both PRs change the members CSV export filename to include a slugified site title (with a ghost fallback), updating filename generation logic and tests accordingly.

Suggested reviewers

  • rob-ghost
  • 9larsons
  • sagzy
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: adding the site title to the members export filename.
Description check ✅ Passed The description is directly related to the changeset, explaining the fix and listing key changes with verification steps performed.
Linked Issues check ✅ Passed The code changes fully implement the requirements from issue #28310: adding site title to members CSV exports, including fallback behavior, and achieving consistency with JSON exports.
Out of Scope Changes check ✅ Passed All changes are directly aligned with issue #28310 objectives—adding site title to export filename, updating related component props, and updating test assertions.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/posts/src/views/members/components/members-actions.tsx (1)

191-200: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Pass siteTitle in the backup export flow too.

handleExportBackup still calls exportMembers(nql, search) without siteTitle, so backup exports keep the old ghost.members... filename and diverge from the main export action.

Proposed fix
 const handleExportBackup = useCallback(async () => {
     try {
-        await exportMembers(nql, search);
+        await exportMembers(nql, search, siteTitle);
     } catch (e) {
         toast.error('Export failed', {
             description: 'There was a problem downloading your backup. Please check your connection and try again.'
         });
         throw e;
     }
-}, [nql, search]);
+}, [nql, search, siteTitle]);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/posts/src/views/members/components/members-actions.tsx` around lines 191
- 200, handleExportBackup currently calls exportMembers(nql, search) without the
siteTitle so backup filenames use the old ghost.members... name; update the call
inside handleExportBackup to include the siteTitle argument (i.e.,
exportMembers(nql, search, siteTitle)) and ensure siteTitle is in the hook’s
closure (add it to the useCallback dependency array if not already present) so
the backup export uses the correct filename.
🧹 Nitpick comments (1)
apps/posts/test/unit/views/members/members-actions.test.tsx (1)

22-31: ⚡ Quick win

Add regression coverage for the onExportBackup callback path.

The new tests assert exportMembers() directly, but they don’t cover the DeleteModal-driven backup export path in MembersActions. Capturing DeleteModal props and invoking onExportBackup would have caught the missing siteTitle propagation regression.

Also applies to: 97-107

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/posts/test/unit/views/members/members-actions.test.tsx` around lines 22
- 31, The test is missing coverage for the DeleteModal-driven export path: add a
captured props ref for DeleteModal (e.g., deleteModalPropsRef.current) in the
same mock block that captures ImportMembersModal, set DeleteModal to a function
that stores props and returns a simple element, then in the test call
MembersActions/trigger the branch that renders DeleteModal and invoke
deleteModalPropsRef.current.onExportBackup(...); assert that exportMembers was
called and that the payload includes the expected siteTitle to catch the
regression where siteTitle wasn't propagated. Ensure you reference the
DeleteModal mock, deleteModalPropsRef, onExportBackup, exportMembers and
MembersActions when making the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/posts/src/views/members/components/members-actions.tsx`:
- Around line 25-29: The current titleSlug computation drops non-ASCII
characters because it restricts to [a-z0-9]; update members-actions.tsx to use
the project’s Unicode-aware slugification used by other exports (or a
Unicode-safe slug strategy) instead of that regex: replace the titleSlug logic
that depends on siteTitle?.toLowerCase().replace(/[^a-z0-9]+/g,
'-').replace(/^-|-$/g, '') with a call to the shared slug function (or a
Unicode-safe slugifier) so titleSlug (and consequently titlePrefix) preserves
non-Latin titles like 日本語ブログ and produces a non-empty prefix when siteTitle
exists (refer to the existing slug utility or export code for the exact function
name to reuse).

---

Outside diff comments:
In `@apps/posts/src/views/members/components/members-actions.tsx`:
- Around line 191-200: handleExportBackup currently calls exportMembers(nql,
search) without the siteTitle so backup filenames use the old ghost.members...
name; update the call inside handleExportBackup to include the siteTitle
argument (i.e., exportMembers(nql, search, siteTitle)) and ensure siteTitle is
in the hook’s closure (add it to the useCallback dependency array if not already
present) so the backup export uses the correct filename.

---

Nitpick comments:
In `@apps/posts/test/unit/views/members/members-actions.test.tsx`:
- Around line 22-31: The test is missing coverage for the DeleteModal-driven
export path: add a captured props ref for DeleteModal (e.g.,
deleteModalPropsRef.current) in the same mock block that captures
ImportMembersModal, set DeleteModal to a function that stores props and returns
a simple element, then in the test call MembersActions/trigger the branch that
renders DeleteModal and invoke deleteModalPropsRef.current.onExportBackup(...);
assert that exportMembers was called and that the payload includes the expected
siteTitle to catch the regression where siteTitle wasn't propagated. Ensure you
reference the DeleteModal mock, deleteModalPropsRef, onExportBackup,
exportMembers and MembersActions when making the change.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: fd4d3830-1baf-4ee2-9134-1a7c73bf6cfb

📥 Commits

Reviewing files that changed from the base of the PR and between 5edf3dd and 87f82a7.

📒 Files selected for processing (3)
  • apps/posts/src/views/members/components/members-actions.tsx
  • apps/posts/src/views/members/members.tsx
  • apps/posts/test/unit/views/members/members-actions.test.tsx

Comment on lines +25 to +29
const titleSlug = siteTitle
?.toLowerCase()
.replace(/[^a-z0-9]+/g, '-')
.replace(/^-|-$/g, '');
const titlePrefix = titleSlug ? `${titleSlug}.` : '';
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.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Non-ASCII site titles are dropped from the filename prefix.

At Line 25-Line 29, the slug regex only keeps [a-z0-9]. Titles like 日本語ブログ become an empty slug and incorrectly fall back to ghost.members... even though siteTitle is present. Please switch this path to the same slugification behavior used by other exports (or a Unicode-safe slug strategy) so non-empty titles consistently contribute a prefix.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/posts/src/views/members/components/members-actions.tsx` around lines 25
- 29, The current titleSlug computation drops non-ASCII characters because it
restricts to [a-z0-9]; update members-actions.tsx to use the project’s
Unicode-aware slugification used by other exports (or a Unicode-safe slug
strategy) instead of that regex: replace the titleSlug logic that depends on
siteTitle?.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '') with
a call to the shared slug function (or a Unicode-safe slugifier) so titleSlug
(and consequently titlePrefix) preserves non-Latin titles like 日本語ブログ and
produces a non-empty prefix when siteTitle exists (refer to the existing slug
utility or export code for the exact function name to reuse).

Copy link
Copy Markdown
Contributor

@9larsons 9larsons left a comment

Choose a reason for hiding this comment

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

LGTM, thanks! I'm going to follow up with another PR to unify our handling, since we're now re-creating this title handling (not to mention streaming exports) in multiple places.

@9larsons 9larsons enabled auto-merge (squash) June 4, 2026 23:48
ref TryGhost#28310

- the members CSV export filename now follows `<site>.ghost.members.<date>.csv`,
  so the previous `startsWith('members')` assertion no longer holds and the e2e
  suite failed
- assert the `ghost.members.<date>.csv` convention instead, which holds whether
  or not a site title prefix is present
@9larsons 9larsons merged commit b76a6d4 into TryGhost:main Jun 5, 2026
45 checks passed
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.

Members CSV export should include site name in filename (consistent with JSON export)

2 participants