Added pinned comment moderation#27653
Conversation
|
It looks like this PR contains a migration 👀 General requirements
Schema changes
Data changes
|
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
WalkthroughThis pull request implements comment pinning functionality. A new nullable ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
|
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
ghost/core/test/unit/frontend/src/admin-auth-message-handler.test.js (1)
72-79:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winAssert the pin/unpin request body too.
These new cases only prove URL + method. Since
pinComment/unpinCommenthit the samePUT /comments/:id/route as hide/show, this test would still pass if the handler sentstatusinstead ofpinned.Suggested diff
const ACTION_CASES = [ {action: 'browseComments', idField: 'postId', expectedPath: `/comments/post/${VALID_ID}/`}, {action: 'getReplies', idField: 'commentId', expectedPath: `/comments/${VALID_ID}/replies/`}, {action: 'readComment', idField: 'commentId', expectedPath: `/comments/${VALID_ID}/`}, {action: 'hideComment', idField: 'id', expectedPath: `/comments/${VALID_ID}/`, expectedMethod: 'PUT'}, {action: 'showComment', idField: 'id', expectedPath: `/comments/${VALID_ID}/`, expectedMethod: 'PUT'}, - {action: 'pinComment', idField: 'id', expectedPath: `/comments/${VALID_ID}/`, expectedMethod: 'PUT'}, - {action: 'unpinComment', idField: 'id', expectedPath: `/comments/${VALID_ID}/`, expectedMethod: 'PUT'} + { + action: 'pinComment', + idField: 'id', + expectedPath: `/comments/${VALID_ID}/`, + expectedMethod: 'PUT', + expectedBody: {comments: [{id: VALID_ID, pinned: true}]} + }, + { + action: 'unpinComment', + idField: 'id', + expectedPath: `/comments/${VALID_ID}/`, + expectedMethod: 'PUT', + expectedBody: {comments: [{id: VALID_ID, pinned: false}]} + } ];if (testCase.expectedMethod) { assert.equal(calls[0].opts.method, testCase.expectedMethod); } + if (testCase.expectedBody) { + assert.deepEqual(JSON.parse(calls[0].opts.body), testCase.expectedBody); + } assert.equal(responses.length, 1);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@ghost/core/test/unit/frontend/src/admin-auth-message-handler.test.js` around lines 72 - 79, The test currently only validates URL and method in ACTION_CASES, so add assertions to ensure pinComment/unpinComment send the correct request body (e.g., include a pinned boolean) rather than some other field like status; update ACTION_CASES to include an expectedBody for the pinComment and unpinComment entries (or add explicit cases) and modify the test loop that uses ACTION_CASES to assert the outgoing request body equals the expectedBody for those actions (refer to ACTION_CASES and the 'pinComment'/'unpinComment' action names in admin-auth-message-handler.test.js).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In
`@ghost/core/core/server/api/endpoints/utils/serializers/output/mappers/comments.js`:
- Around line 91-99: The redaction branches in commentMapper currently always
assign response.html which causes returned objects to include html: null even
when the requested fields don't include 'html'; update those redaction branches
to only set response.html when the caller requested it by guarding assignments
with the same condition used above (i.e. use !fields || fields.has('html')) so
that response respects the initial _.pick(fields) selection and remains
deterministic for requests like fields=id,pinned; locate the assignments to
response.html inside commentMapper and wrap them with that guard.
In `@ghost/core/core/server/services/comments/comments-service.js`:
- Around line 238-261: When handling the pinned update in comments-service.js,
avoid resetting pinned_at for repeat PUT {pinned: true} calls: fetch the
existing comment (using this.models.Comment.findOne / existingComment) and if
data.pinned is true and existingComment.get('pinned') is already true, preserve
existingComment.get('pinned_at') instead of assigning new Date(); only set
editData.pinned_at = new Date() when transitioning from unpinned to pinned, and
set it to null when data.pinned is false. Ensure checks around
existingComment.get('parent_id') and deleted status remain unchanged.
---
Outside diff comments:
In `@ghost/core/test/unit/frontend/src/admin-auth-message-handler.test.js`:
- Around line 72-79: The test currently only validates URL and method in
ACTION_CASES, so add assertions to ensure pinComment/unpinComment send the
correct request body (e.g., include a pinned boolean) rather than some other
field like status; update ACTION_CASES to include an expectedBody for the
pinComment and unpinComment entries (or add explicit cases) and modify the test
loop that uses ACTION_CASES to assert the outgoing request body equals the
expectedBody for those actions (refer to ACTION_CASES and the
'pinComment'/'unpinComment' action names in admin-auth-message-handler.test.js).
🪄 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: bf8e43cd-0966-4a7c-86b4-48ab6c854ab4
⛔ Files ignored due to path filters (11)
apps/comments-ui/src/images/icons/external-link.svgis excluded by!**/*.svgapps/comments-ui/src/images/icons/eye-off.svgis excluded by!**/*.svgapps/comments-ui/src/images/icons/eye.svgis excluded by!**/*.svgapps/comments-ui/src/images/icons/flag.svgis excluded by!**/*.svgapps/comments-ui/src/images/icons/pencil.svgis excluded by!**/*.svgapps/comments-ui/src/images/icons/pin-off.svgis excluded by!**/*.svgapps/comments-ui/src/images/icons/pin.svgis excluded by!**/*.svgapps/comments-ui/src/images/icons/trash.svgis excluded by!**/*.svgghost/core/core/frontend/public/admin-auth/admin-auth.min.jsis excluded by!**/*.min.jsghost/core/test/e2e-api/admin/__snapshots__/comments.test.js.snapis excluded by!**/*.snapghost/core/test/e2e-api/members-comments/__snapshots__/comments.test.js.snapis excluded by!**/*.snap
📒 Files selected for processing (95)
apps/admin-x-framework/src/api/comments.tsapps/comments-ui/src/actions.tsapps/comments-ui/src/app-context.tsapps/comments-ui/src/components/content/comment.tsxapps/comments-ui/src/components/content/context-menus/admin-context-menu.tsxapps/comments-ui/src/components/content/context-menus/author-context-menu.tsxapps/comments-ui/src/components/content/context-menus/comment-context-menu.tsxapps/comments-ui/src/components/content/context-menus/not-author-context-menu.tsxapps/comments-ui/src/utils/admin-api.tsapps/comments-ui/test/e2e/admin-moderation.test.tsapps/comments-ui/test/unit/actions.test.jsapps/comments-ui/test/unit/components/content/comment.test.jsxapps/comments-ui/test/unit/components/content/context-menus/comment-context-menu.test.jsxapps/comments-ui/test/utils/fixtures.tsapps/posts/src/views/comments/components/comment-header.tsxapps/posts/src/views/comments/components/comment-menu.tsxapps/posts/src/views/comments/components/comment-thread-list.tsxapps/posts/src/views/comments/components/comments-list.tsxapps/posts/test/unit/views/comments/components/comment-header.test.tsxghost/core/core/frontend/src/admin-auth/message-handler.jsghost/core/core/server/api/endpoints/comments.jsghost/core/core/server/api/endpoints/utils/serializers/input/comments.jsghost/core/core/server/api/endpoints/utils/serializers/output/mappers/comments.jsghost/core/core/server/data/migrations/versions/6.36/2026-05-02-00-06-36-add-pinned-at-to-comments.jsghost/core/core/server/data/schema/schema.jsghost/core/core/server/models/comment.jsghost/core/core/server/services/comments/comments-controller.jsghost/core/core/server/services/comments/comments-service.jsghost/core/test/e2e-api/admin/comments.test.jsghost/core/test/e2e-api/members-comments/comments.test.jsghost/core/test/unit/frontend/src/admin-auth-message-handler.test.jsghost/core/test/unit/server/data/schema/integrity.test.jsghost/i18n/locales/af/comments.jsonghost/i18n/locales/ar/comments.jsonghost/i18n/locales/bg/comments.jsonghost/i18n/locales/bn/comments.jsonghost/i18n/locales/bs/comments.jsonghost/i18n/locales/ca/comments.jsonghost/i18n/locales/context.jsonghost/i18n/locales/cs/comments.jsonghost/i18n/locales/da/comments.jsonghost/i18n/locales/de-CH/comments.jsonghost/i18n/locales/de/comments.jsonghost/i18n/locales/el/comments.jsonghost/i18n/locales/en/comments.jsonghost/i18n/locales/eo/comments.jsonghost/i18n/locales/es/comments.jsonghost/i18n/locales/et/comments.jsonghost/i18n/locales/eu/comments.jsonghost/i18n/locales/fa/comments.jsonghost/i18n/locales/fi/comments.jsonghost/i18n/locales/fr/comments.jsonghost/i18n/locales/gd/comments.jsonghost/i18n/locales/he/comments.jsonghost/i18n/locales/hi/comments.jsonghost/i18n/locales/hr/comments.jsonghost/i18n/locales/hu/comments.jsonghost/i18n/locales/id/comments.jsonghost/i18n/locales/is/comments.jsonghost/i18n/locales/it/comments.jsonghost/i18n/locales/ja/comments.jsonghost/i18n/locales/ko/comments.jsonghost/i18n/locales/kz/comments.jsonghost/i18n/locales/lt/comments.jsonghost/i18n/locales/lv/comments.jsonghost/i18n/locales/mk/comments.jsonghost/i18n/locales/mn/comments.jsonghost/i18n/locales/ms/comments.jsonghost/i18n/locales/nb/comments.jsonghost/i18n/locales/ne/comments.jsonghost/i18n/locales/nl/comments.jsonghost/i18n/locales/nn/comments.jsonghost/i18n/locales/pa/comments.jsonghost/i18n/locales/pl/comments.jsonghost/i18n/locales/pt-BR/comments.jsonghost/i18n/locales/pt/comments.jsonghost/i18n/locales/ro/comments.jsonghost/i18n/locales/ru/comments.jsonghost/i18n/locales/si/comments.jsonghost/i18n/locales/sk/comments.jsonghost/i18n/locales/sl/comments.jsonghost/i18n/locales/sq/comments.jsonghost/i18n/locales/sr-Cyrl/comments.jsonghost/i18n/locales/sr/comments.jsonghost/i18n/locales/sv/comments.jsonghost/i18n/locales/sw/comments.jsonghost/i18n/locales/ta/comments.jsonghost/i18n/locales/th/comments.jsonghost/i18n/locales/tr/comments.jsonghost/i18n/locales/uk/comments.jsonghost/i18n/locales/ur/comments.jsonghost/i18n/locales/uz/comments.jsonghost/i18n/locales/vi/comments.jsonghost/i18n/locales/zh-Hant/comments.jsonghost/i18n/locales/zh/comments.json
08b895a to
fd3d83f
Compare
There was a problem hiding this comment.
🧹 Nitpick comments (1)
ghost/core/test/unit/frontend/src/admin-auth-message-handler.test.js (1)
78-79: ⚡ Quick winAdd payload assertions for
pinComment/unpinCommentcases.These new cases only assert URL/method, so they won’t catch a regression where
pinnedis sent incorrectly. Please assert request body for these actions.Proposed test hardening
const ACTION_CASES = [ {action: 'browseComments', idField: 'postId', expectedPath: `/comments/post/${VALID_ID}/`}, {action: 'getReplies', idField: 'commentId', expectedPath: `/comments/${VALID_ID}/replies/`}, {action: 'readComment', idField: 'commentId', expectedPath: `/comments/${VALID_ID}/`}, {action: 'hideComment', idField: 'id', expectedPath: `/comments/${VALID_ID}/`, expectedMethod: 'PUT'}, {action: 'showComment', idField: 'id', expectedPath: `/comments/${VALID_ID}/`, expectedMethod: 'PUT'}, - {action: 'pinComment', idField: 'id', expectedPath: `/comments/${VALID_ID}/`, expectedMethod: 'PUT'}, - {action: 'unpinComment', idField: 'id', expectedPath: `/comments/${VALID_ID}/`, expectedMethod: 'PUT'} + {action: 'pinComment', idField: 'id', expectedPath: `/comments/${VALID_ID}/`, expectedMethod: 'PUT', expectedBody: {comments: [{id: VALID_ID, pinned: true}]}}, + {action: 'unpinComment', idField: 'id', expectedPath: `/comments/${VALID_ID}/`, expectedMethod: 'PUT', expectedBody: {comments: [{id: VALID_ID, pinned: false}]}} ]; @@ if (testCase.expectedMethod) { assert.equal(calls[0].opts.method, testCase.expectedMethod); } + if (testCase.expectedBody) { + assert.deepEqual(JSON.parse(calls[0].opts.body), testCase.expectedBody); + } assert.equal(responses.length, 1);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@ghost/core/test/unit/frontend/src/admin-auth-message-handler.test.js` around lines 78 - 79, Update the two test cases for actions 'pinComment' and 'unpinComment' so they also assert the request payload; add an expectedBody (e.g. {pinned: true} for 'pinComment' and {pinned: false} for 'unpinComment') alongside the existing expectedPath/expectedMethod, and enhance the assertion logic that validates the fetch call (the code that inspects calls for the case matching action and VALID_ID) to JSON.parse the request body and compare it to expectedBody (or assert it contains the pinned property with the correct boolean). This change should reference the existing test case objects (action: 'pinComment' / 'unpinComment', expectedPath: `/comments/${VALID_ID}/`) and the fetch assertion block so the body is checked as part of the test.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@ghost/core/test/unit/frontend/src/admin-auth-message-handler.test.js`:
- Around line 78-79: Update the two test cases for actions 'pinComment' and
'unpinComment' so they also assert the request payload; add an expectedBody
(e.g. {pinned: true} for 'pinComment' and {pinned: false} for 'unpinComment')
alongside the existing expectedPath/expectedMethod, and enhance the assertion
logic that validates the fetch call (the code that inspects calls for the case
matching action and VALID_ID) to JSON.parse the request body and compare it to
expectedBody (or assert it contains the pinned property with the correct
boolean). This change should reference the existing test case objects (action:
'pinComment' / 'unpinComment', expectedPath: `/comments/${VALID_ID}/`) and the
fetch assertion block so the body is checked as part of the test.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: e755a21d-327c-4b56-bd9a-dbab3a66ea04
⛔ Files ignored due to path filters (11)
apps/comments-ui/src/images/icons/external-link.svgis excluded by!**/*.svgapps/comments-ui/src/images/icons/eye-off.svgis excluded by!**/*.svgapps/comments-ui/src/images/icons/eye.svgis excluded by!**/*.svgapps/comments-ui/src/images/icons/flag.svgis excluded by!**/*.svgapps/comments-ui/src/images/icons/pencil.svgis excluded by!**/*.svgapps/comments-ui/src/images/icons/pin-off.svgis excluded by!**/*.svgapps/comments-ui/src/images/icons/pin.svgis excluded by!**/*.svgapps/comments-ui/src/images/icons/trash.svgis excluded by!**/*.svgghost/core/core/frontend/public/admin-auth/admin-auth.min.jsis excluded by!**/*.min.jsghost/core/test/e2e-api/admin/__snapshots__/comments.test.js.snapis excluded by!**/*.snapghost/core/test/e2e-api/members-comments/__snapshots__/comments.test.js.snapis excluded by!**/*.snap
📒 Files selected for processing (95)
apps/admin-x-framework/src/api/comments.tsapps/comments-ui/src/actions.tsapps/comments-ui/src/app-context.tsapps/comments-ui/src/components/content/comment.tsxapps/comments-ui/src/components/content/context-menus/admin-context-menu.tsxapps/comments-ui/src/components/content/context-menus/author-context-menu.tsxapps/comments-ui/src/components/content/context-menus/comment-context-menu.tsxapps/comments-ui/src/components/content/context-menus/not-author-context-menu.tsxapps/comments-ui/src/utils/admin-api.tsapps/comments-ui/test/e2e/admin-moderation.test.tsapps/comments-ui/test/unit/actions.test.jsapps/comments-ui/test/unit/components/content/comment.test.jsxapps/comments-ui/test/unit/components/content/context-menus/comment-context-menu.test.jsxapps/comments-ui/test/utils/fixtures.tsapps/posts/src/views/comments/components/comment-header.tsxapps/posts/src/views/comments/components/comment-menu.tsxapps/posts/src/views/comments/components/comment-thread-list.tsxapps/posts/src/views/comments/components/comments-list.tsxapps/posts/test/unit/views/comments/components/comment-header.test.tsxghost/core/core/frontend/src/admin-auth/message-handler.jsghost/core/core/server/api/endpoints/comments.jsghost/core/core/server/api/endpoints/utils/serializers/input/comments.jsghost/core/core/server/api/endpoints/utils/serializers/output/mappers/comments.jsghost/core/core/server/data/migrations/versions/6.36/2026-05-02-00-06-36-add-pinned-at-to-comments.jsghost/core/core/server/data/schema/schema.jsghost/core/core/server/models/comment.jsghost/core/core/server/services/comments/comments-controller.jsghost/core/core/server/services/comments/comments-service.jsghost/core/test/e2e-api/admin/comments.test.jsghost/core/test/e2e-api/members-comments/comments.test.jsghost/core/test/unit/frontend/src/admin-auth-message-handler.test.jsghost/core/test/unit/server/data/schema/integrity.test.jsghost/i18n/locales/af/comments.jsonghost/i18n/locales/ar/comments.jsonghost/i18n/locales/bg/comments.jsonghost/i18n/locales/bn/comments.jsonghost/i18n/locales/bs/comments.jsonghost/i18n/locales/ca/comments.jsonghost/i18n/locales/context.jsonghost/i18n/locales/cs/comments.jsonghost/i18n/locales/da/comments.jsonghost/i18n/locales/de-CH/comments.jsonghost/i18n/locales/de/comments.jsonghost/i18n/locales/el/comments.jsonghost/i18n/locales/en/comments.jsonghost/i18n/locales/eo/comments.jsonghost/i18n/locales/es/comments.jsonghost/i18n/locales/et/comments.jsonghost/i18n/locales/eu/comments.jsonghost/i18n/locales/fa/comments.jsonghost/i18n/locales/fi/comments.jsonghost/i18n/locales/fr/comments.jsonghost/i18n/locales/gd/comments.jsonghost/i18n/locales/he/comments.jsonghost/i18n/locales/hi/comments.jsonghost/i18n/locales/hr/comments.jsonghost/i18n/locales/hu/comments.jsonghost/i18n/locales/id/comments.jsonghost/i18n/locales/is/comments.jsonghost/i18n/locales/it/comments.jsonghost/i18n/locales/ja/comments.jsonghost/i18n/locales/ko/comments.jsonghost/i18n/locales/kz/comments.jsonghost/i18n/locales/lt/comments.jsonghost/i18n/locales/lv/comments.jsonghost/i18n/locales/mk/comments.jsonghost/i18n/locales/mn/comments.jsonghost/i18n/locales/ms/comments.jsonghost/i18n/locales/nb/comments.jsonghost/i18n/locales/ne/comments.jsonghost/i18n/locales/nl/comments.jsonghost/i18n/locales/nn/comments.jsonghost/i18n/locales/pa/comments.jsonghost/i18n/locales/pl/comments.jsonghost/i18n/locales/pt-BR/comments.jsonghost/i18n/locales/pt/comments.jsonghost/i18n/locales/ro/comments.jsonghost/i18n/locales/ru/comments.jsonghost/i18n/locales/si/comments.jsonghost/i18n/locales/sk/comments.jsonghost/i18n/locales/sl/comments.jsonghost/i18n/locales/sq/comments.jsonghost/i18n/locales/sr-Cyrl/comments.jsonghost/i18n/locales/sr/comments.jsonghost/i18n/locales/sv/comments.jsonghost/i18n/locales/sw/comments.jsonghost/i18n/locales/ta/comments.jsonghost/i18n/locales/th/comments.jsonghost/i18n/locales/tr/comments.jsonghost/i18n/locales/uk/comments.jsonghost/i18n/locales/ur/comments.jsonghost/i18n/locales/uz/comments.jsonghost/i18n/locales/vi/comments.jsonghost/i18n/locales/zh-Hant/comments.jsonghost/i18n/locales/zh/comments.json
✅ Files skipped from review due to trivial changes (51)
- ghost/i18n/locales/mk/comments.json
- ghost/i18n/locales/bg/comments.json
- ghost/i18n/locales/de-CH/comments.json
- ghost/i18n/locales/ms/comments.json
- apps/comments-ui/src/components/content/context-menus/author-context-menu.tsx
- apps/comments-ui/test/unit/actions.test.js
- ghost/core/core/frontend/src/admin-auth/message-handler.js
- ghost/core/core/server/data/migrations/versions/6.36/2026-05-02-00-06-36-add-pinned-at-to-comments.js
- apps/comments-ui/src/app-context.ts
- ghost/i18n/locales/mn/comments.json
- ghost/i18n/locales/id/comments.json
- apps/posts/test/unit/views/comments/components/comment-header.test.tsx
- ghost/i18n/locales/uz/comments.json
- apps/comments-ui/test/unit/components/content/comment.test.jsx
- ghost/i18n/locales/si/comments.json
- ghost/i18n/locales/ur/comments.json
- ghost/core/test/unit/server/data/schema/integrity.test.js
- apps/comments-ui/src/components/content/context-menus/comment-context-menu.tsx
- ghost/i18n/locales/af/comments.json
- ghost/i18n/locales/bn/comments.json
- ghost/i18n/locales/eo/comments.json
- ghost/i18n/locales/is/comments.json
- ghost/i18n/locales/ro/comments.json
- ghost/i18n/locales/ca/comments.json
- ghost/i18n/locales/bs/comments.json
- ghost/i18n/locales/ne/comments.json
- apps/comments-ui/src/components/content/context-menus/not-author-context-menu.tsx
- ghost/i18n/locales/pt/comments.json
- ghost/i18n/locales/pt-BR/comments.json
- ghost/i18n/locales/ta/comments.json
- ghost/i18n/locales/ar/comments.json
- ghost/i18n/locales/nb/comments.json
- ghost/i18n/locales/fi/comments.json
- ghost/i18n/locales/uk/comments.json
- ghost/i18n/locales/en/comments.json
- ghost/i18n/locales/gd/comments.json
- ghost/i18n/locales/sq/comments.json
- ghost/i18n/locales/sv/comments.json
- ghost/i18n/locales/pl/comments.json
- ghost/i18n/locales/zh/comments.json
- ghost/i18n/locales/el/comments.json
- ghost/i18n/locales/vi/comments.json
- ghost/i18n/locales/fa/comments.json
- ghost/i18n/locales/sr/comments.json
- ghost/i18n/locales/kz/comments.json
- ghost/core/test/e2e-api/members-comments/comments.test.js
- ghost/i18n/locales/da/comments.json
- ghost/i18n/locales/lv/comments.json
- ghost/i18n/locales/nn/comments.json
- ghost/i18n/locales/ko/comments.json
- apps/comments-ui/src/components/content/comment.tsx
🚧 Files skipped from review as they are similar to previous changes (32)
- ghost/i18n/locales/th/comments.json
- apps/posts/src/views/comments/components/comment-menu.tsx
- apps/comments-ui/test/e2e/admin-moderation.test.ts
- apps/posts/src/views/comments/components/comments-list.tsx
- ghost/i18n/locales/et/comments.json
- ghost/i18n/locales/ru/comments.json
- ghost/core/core/server/api/endpoints/utils/serializers/input/comments.js
- ghost/core/core/server/api/endpoints/utils/serializers/output/mappers/comments.js
- ghost/i18n/locales/zh-Hant/comments.json
- apps/comments-ui/test/unit/components/content/context-menus/comment-context-menu.test.jsx
- ghost/core/core/server/services/comments/comments-controller.js
- ghost/i18n/locales/sk/comments.json
- ghost/i18n/locales/eu/comments.json
- ghost/core/core/server/models/comment.js
- ghost/i18n/locales/hr/comments.json
- ghost/i18n/locales/pa/comments.json
- apps/admin-x-framework/src/api/comments.ts
- ghost/i18n/locales/hi/comments.json
- ghost/i18n/locales/hu/comments.json
- ghost/i18n/locales/ja/comments.json
- apps/comments-ui/test/utils/fixtures.ts
- ghost/i18n/locales/tr/comments.json
- ghost/i18n/locales/context.json
- ghost/core/core/server/services/comments/comments-service.js
- ghost/core/core/server/data/schema/schema.js
- ghost/i18n/locales/sl/comments.json
- ghost/i18n/locales/sw/comments.json
- ghost/i18n/locales/sr-Cyrl/comments.json
- apps/posts/src/views/comments/components/comment-header.tsx
- apps/comments-ui/src/components/content/context-menus/admin-context-menu.tsx
- apps/comments-ui/src/actions.ts
- ghost/i18n/locales/it/comments.json
fd3d83f to
387b70a
Compare
There was a problem hiding this comment.
🧹 Nitpick comments (3)
ghost/core/test/unit/frontend/src/admin-auth-message-handler.test.js (1)
77-79: ⚡ Quick winAssert pin/unpin payload shape, not just URL/method.
These cases currently pass even if the handler sends the wrong request body. Add expected payload assertions for
pinned: true/falseto lock behavior.Proposed test hardening
const ACTION_CASES = [ {action: 'browseComments', idField: 'postId', expectedPath: `/comments/post/${VALID_ID}/`}, {action: 'getReplies', idField: 'commentId', expectedPath: `/comments/${VALID_ID}/replies/`}, {action: 'readComment', idField: 'commentId', expectedPath: `/comments/${VALID_ID}/`}, - {action: 'hideComment', idField: 'id', expectedPath: `/comments/${VALID_ID}/`, expectedMethod: 'PUT'}, - {action: 'showComment', idField: 'id', expectedPath: `/comments/${VALID_ID}/`, expectedMethod: 'PUT'}, - {action: 'pinComment', idField: 'id', expectedPath: `/comments/${VALID_ID}/`, expectedMethod: 'PUT'}, - {action: 'unpinComment', idField: 'id', expectedPath: `/comments/${VALID_ID}/`, expectedMethod: 'PUT'} + {action: 'hideComment', idField: 'id', expectedPath: `/comments/${VALID_ID}/`, expectedMethod: 'PUT', expectedBody: {comments: [{id: VALID_ID, status: 'hidden'}]}}, + {action: 'showComment', idField: 'id', expectedPath: `/comments/${VALID_ID}/`, expectedMethod: 'PUT', expectedBody: {comments: [{id: VALID_ID, status: 'published'}]}}, + {action: 'pinComment', idField: 'id', expectedPath: `/comments/${VALID_ID}/`, expectedMethod: 'PUT', expectedBody: {comments: [{id: VALID_ID, pinned: true}]}}, + {action: 'unpinComment', idField: 'id', expectedPath: `/comments/${VALID_ID}/`, expectedMethod: 'PUT', expectedBody: {comments: [{id: VALID_ID, pinned: false}]}} ];if (testCase.expectedMethod) { assert.equal(calls[0].opts.method, testCase.expectedMethod); } + if (testCase.expectedBody) { + assert.deepEqual(JSON.parse(calls[0].opts.body), testCase.expectedBody); + }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@ghost/core/test/unit/frontend/src/admin-auth-message-handler.test.js` around lines 77 - 79, The pin/unpin tests only assert URL/method; add payload assertions so the handler must send {pinned: true} for 'pinComment' and {pinned: false} for 'unpinComment'. Update the test cases (the objects with action: 'pinComment' and action: 'unpinComment') to include an expectedPayload property (expectedPayload: {pinned: true} and expectedPayload: {pinned: false}), and update the test assertion that inspects the simulated request body to compare it against expectedPayload (where the test currently checks expectedPath/expectedMethod) so the request body is validated as well.ghost/core/test/e2e-api/admin/comments.test.js (1)
565-567: ⚡ Quick winTighten reply
pinnedassertions tofalseAt Line 566, Line 683, Line 724, and Line 766,
pinned: anyBooleanis too permissive for replies and can hide regressions where replies become pinnable.Suggested diff
- pinned: anyBoolean + pinned: false- pinned: anyBoolean + pinned: false- pinned: anyBoolean + pinned: false- pinned: anyBoolean + pinned: falseAlso applies to: 682-684, 723-725, 765-767
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@ghost/core/test/e2e-api/admin/comments.test.js` around lines 565 - 567, Replace the permissive pinned: anyBoolean used in reply assertions with pinned: false so replies are asserted non-pinnable; locate the reply expectation objects in the admin comments E2E tests (the assertion blocks that define reply objects with a pinned property) and change pinned: anyBoolean to pinned: false for the reply cases (leave top-level comment assertions as-is if they should remain flexible), update all occurrences mentioned in the review (the reply assertions around the three reply-check blocks) and re-run the test suite to confirm no regressions.apps/comments-ui/test/utils/fixtures.ts (1)
71-82: ⚡ Quick winKeep override support in
buildCommentsReplyfor fixture flexibility.Line 71 currently drops the optional override path, which makes targeted fixture customization harder in tests and can break existing usage patterns.
♻️ Suggested patch
-export function buildCommentsReply() { +export function buildCommentsReply(override: any = {}) { return { comments: [], meta: { pagination: { pages: 1, total: 0, page: 1, limit: 5 } - } + }, + ...override }; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/comments-ui/test/utils/fixtures.ts` around lines 71 - 82, buildCommentsReply currently returns a fixed object and removed support for overrides; restore an optional parameter (e.g., overrides?: Partial<typeof defaultReturn>) and merge it into the default fixture (using object spread or a deep-merge helper) so callers can pass partial overrides for comments, meta, pagination, etc.; update the function signature buildCommentsReply to accept the overrides arg and return { ...defaultValues, ...overrides } (ensuring nested objects like meta.pagination are merged appropriately).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@apps/comments-ui/test/utils/fixtures.ts`:
- Around line 71-82: buildCommentsReply currently returns a fixed object and
removed support for overrides; restore an optional parameter (e.g., overrides?:
Partial<typeof defaultReturn>) and merge it into the default fixture (using
object spread or a deep-merge helper) so callers can pass partial overrides for
comments, meta, pagination, etc.; update the function signature
buildCommentsReply to accept the overrides arg and return { ...defaultValues,
...overrides } (ensuring nested objects like meta.pagination are merged
appropriately).
In `@ghost/core/test/e2e-api/admin/comments.test.js`:
- Around line 565-567: Replace the permissive pinned: anyBoolean used in reply
assertions with pinned: false so replies are asserted non-pinnable; locate the
reply expectation objects in the admin comments E2E tests (the assertion blocks
that define reply objects with a pinned property) and change pinned: anyBoolean
to pinned: false for the reply cases (leave top-level comment assertions as-is
if they should remain flexible), update all occurrences mentioned in the review
(the reply assertions around the three reply-check blocks) and re-run the test
suite to confirm no regressions.
In `@ghost/core/test/unit/frontend/src/admin-auth-message-handler.test.js`:
- Around line 77-79: The pin/unpin tests only assert URL/method; add payload
assertions so the handler must send {pinned: true} for 'pinComment' and {pinned:
false} for 'unpinComment'. Update the test cases (the objects with action:
'pinComment' and action: 'unpinComment') to include an expectedPayload property
(expectedPayload: {pinned: true} and expectedPayload: {pinned: false}), and
update the test assertion that inspects the simulated request body to compare it
against expectedPayload (where the test currently checks
expectedPath/expectedMethod) so the request body is validated as well.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 01192958-5df8-457e-9f76-92de8e21e15e
⛔ Files ignored due to path filters (12)
apps/comments-ui/src/images/icons/external-link.svgis excluded by!**/*.svgapps/comments-ui/src/images/icons/eye-off.svgis excluded by!**/*.svgapps/comments-ui/src/images/icons/eye.svgis excluded by!**/*.svgapps/comments-ui/src/images/icons/flag.svgis excluded by!**/*.svgapps/comments-ui/src/images/icons/pencil.svgis excluded by!**/*.svgapps/comments-ui/src/images/icons/pin-off.svgis excluded by!**/*.svgapps/comments-ui/src/images/icons/pin.svgis excluded by!**/*.svgapps/comments-ui/src/images/icons/trash.svgis excluded by!**/*.svgghost/core/core/frontend/public/admin-auth/admin-auth.min.jsis excluded by!**/*.min.jsghost/core/test/e2e-api/admin/__snapshots__/activity-feed.test.js.snapis excluded by!**/*.snapghost/core/test/e2e-api/admin/__snapshots__/comments.test.js.snapis excluded by!**/*.snapghost/core/test/e2e-api/members-comments/__snapshots__/comments.test.js.snapis excluded by!**/*.snap
📒 Files selected for processing (96)
apps/admin-x-framework/src/api/comments.tsapps/comments-ui/package.jsonapps/comments-ui/src/actions.tsapps/comments-ui/src/app-context.tsapps/comments-ui/src/components/content/comment.tsxapps/comments-ui/src/components/content/context-menus/admin-context-menu.tsxapps/comments-ui/src/components/content/context-menus/author-context-menu.tsxapps/comments-ui/src/components/content/context-menus/comment-context-menu.tsxapps/comments-ui/src/components/content/context-menus/not-author-context-menu.tsxapps/comments-ui/src/utils/admin-api.tsapps/comments-ui/test/e2e/admin-moderation.test.tsapps/comments-ui/test/unit/actions.test.jsapps/comments-ui/test/unit/components/content/comment.test.jsxapps/comments-ui/test/unit/components/content/context-menus/comment-context-menu.test.jsxapps/comments-ui/test/utils/fixtures.tsapps/posts/src/views/comments/components/comment-header.tsxapps/posts/src/views/comments/components/comment-menu.tsxapps/posts/src/views/comments/components/comment-thread-list.tsxapps/posts/src/views/comments/components/comments-list.tsxapps/posts/test/unit/views/comments/components/comment-header.test.tsxghost/core/core/frontend/src/admin-auth/message-handler.jsghost/core/core/server/api/endpoints/comments.jsghost/core/core/server/api/endpoints/utils/serializers/input/comments.jsghost/core/core/server/api/endpoints/utils/serializers/output/mappers/comments.jsghost/core/core/server/data/migrations/versions/6.36/2026-05-02-00-06-36-add-pinned-at-to-comments.jsghost/core/core/server/data/schema/schema.jsghost/core/core/server/models/comment.jsghost/core/core/server/services/comments/comments-controller.jsghost/core/core/server/services/comments/comments-service.jsghost/core/test/e2e-api/admin/comments.test.jsghost/core/test/e2e-api/members-comments/comments.test.jsghost/core/test/unit/frontend/src/admin-auth-message-handler.test.jsghost/core/test/unit/server/data/schema/integrity.test.jsghost/i18n/locales/af/comments.jsonghost/i18n/locales/ar/comments.jsonghost/i18n/locales/bg/comments.jsonghost/i18n/locales/bn/comments.jsonghost/i18n/locales/bs/comments.jsonghost/i18n/locales/ca/comments.jsonghost/i18n/locales/context.jsonghost/i18n/locales/cs/comments.jsonghost/i18n/locales/da/comments.jsonghost/i18n/locales/de-CH/comments.jsonghost/i18n/locales/de/comments.jsonghost/i18n/locales/el/comments.jsonghost/i18n/locales/en/comments.jsonghost/i18n/locales/eo/comments.jsonghost/i18n/locales/es/comments.jsonghost/i18n/locales/et/comments.jsonghost/i18n/locales/eu/comments.jsonghost/i18n/locales/fa/comments.jsonghost/i18n/locales/fi/comments.jsonghost/i18n/locales/fr/comments.jsonghost/i18n/locales/gd/comments.jsonghost/i18n/locales/he/comments.jsonghost/i18n/locales/hi/comments.jsonghost/i18n/locales/hr/comments.jsonghost/i18n/locales/hu/comments.jsonghost/i18n/locales/id/comments.jsonghost/i18n/locales/is/comments.jsonghost/i18n/locales/it/comments.jsonghost/i18n/locales/ja/comments.jsonghost/i18n/locales/ko/comments.jsonghost/i18n/locales/kz/comments.jsonghost/i18n/locales/lt/comments.jsonghost/i18n/locales/lv/comments.jsonghost/i18n/locales/mk/comments.jsonghost/i18n/locales/mn/comments.jsonghost/i18n/locales/ms/comments.jsonghost/i18n/locales/nb/comments.jsonghost/i18n/locales/ne/comments.jsonghost/i18n/locales/nl/comments.jsonghost/i18n/locales/nn/comments.jsonghost/i18n/locales/pa/comments.jsonghost/i18n/locales/pl/comments.jsonghost/i18n/locales/pt-BR/comments.jsonghost/i18n/locales/pt/comments.jsonghost/i18n/locales/ro/comments.jsonghost/i18n/locales/ru/comments.jsonghost/i18n/locales/si/comments.jsonghost/i18n/locales/sk/comments.jsonghost/i18n/locales/sl/comments.jsonghost/i18n/locales/sq/comments.jsonghost/i18n/locales/sr-Cyrl/comments.jsonghost/i18n/locales/sr/comments.jsonghost/i18n/locales/sv/comments.jsonghost/i18n/locales/sw/comments.jsonghost/i18n/locales/ta/comments.jsonghost/i18n/locales/th/comments.jsonghost/i18n/locales/tr/comments.jsonghost/i18n/locales/uk/comments.jsonghost/i18n/locales/ur/comments.jsonghost/i18n/locales/uz/comments.jsonghost/i18n/locales/vi/comments.jsonghost/i18n/locales/zh-Hant/comments.jsonghost/i18n/locales/zh/comments.json
✅ Files skipped from review due to trivial changes (56)
- apps/comments-ui/src/app-context.ts
- ghost/i18n/locales/nb/comments.json
- apps/comments-ui/package.json
- ghost/core/test/unit/server/data/schema/integrity.test.js
- ghost/i18n/locales/it/comments.json
- ghost/i18n/locales/mn/comments.json
- ghost/i18n/locales/ne/comments.json
- ghost/i18n/locales/sq/comments.json
- ghost/i18n/locales/kz/comments.json
- ghost/i18n/locales/el/comments.json
- apps/comments-ui/src/components/content/context-menus/author-context-menu.tsx
- ghost/i18n/locales/bn/comments.json
- ghost/core/core/server/data/migrations/versions/6.36/2026-05-02-00-06-36-add-pinned-at-to-comments.js
- ghost/i18n/locales/ur/comments.json
- apps/comments-ui/test/unit/components/content/context-menus/comment-context-menu.test.jsx
- apps/posts/test/unit/views/comments/components/comment-header.test.tsx
- apps/posts/src/views/comments/components/comments-list.tsx
- ghost/i18n/locales/context.json
- ghost/core/core/server/api/endpoints/comments.js
- ghost/i18n/locales/eo/comments.json
- ghost/i18n/locales/ta/comments.json
- ghost/i18n/locales/af/comments.json
- ghost/i18n/locales/pt/comments.json
- ghost/i18n/locales/ja/comments.json
- ghost/i18n/locales/lt/comments.json
- ghost/i18n/locales/uz/comments.json
- ghost/i18n/locales/pl/comments.json
- ghost/core/core/server/api/endpoints/utils/serializers/input/comments.js
- ghost/i18n/locales/uk/comments.json
- ghost/i18n/locales/nn/comments.json
- apps/comments-ui/src/actions.ts
- ghost/i18n/locales/zh/comments.json
- ghost/i18n/locales/es/comments.json
- ghost/i18n/locales/zh-Hant/comments.json
- ghost/i18n/locales/de-CH/comments.json
- ghost/i18n/locales/mk/comments.json
- apps/comments-ui/src/components/content/context-menus/not-author-context-menu.tsx
- apps/posts/src/views/comments/components/comment-header.tsx
- ghost/i18n/locales/ca/comments.json
- ghost/i18n/locales/hr/comments.json
- ghost/i18n/locales/id/comments.json
- ghost/i18n/locales/ar/comments.json
- ghost/i18n/locales/lv/comments.json
- ghost/i18n/locales/pa/comments.json
- ghost/i18n/locales/sr-Cyrl/comments.json
- ghost/i18n/locales/ko/comments.json
- ghost/i18n/locales/ms/comments.json
- ghost/i18n/locales/da/comments.json
- apps/comments-ui/test/unit/actions.test.js
- ghost/i18n/locales/bg/comments.json
- ghost/i18n/locales/he/comments.json
- ghost/i18n/locales/sl/comments.json
- ghost/i18n/locales/sv/comments.json
- ghost/i18n/locales/et/comments.json
- ghost/i18n/locales/pt-BR/comments.json
- ghost/i18n/locales/gd/comments.json
🚧 Files skipped from review as they are similar to previous changes (28)
- apps/posts/src/views/comments/components/comment-menu.tsx
- ghost/i18n/locales/bs/comments.json
- ghost/i18n/locales/fa/comments.json
- ghost/i18n/locales/hu/comments.json
- apps/comments-ui/test/unit/components/content/comment.test.jsx
- apps/admin-x-framework/src/api/comments.ts
- ghost/i18n/locales/fi/comments.json
- ghost/i18n/locales/vi/comments.json
- ghost/i18n/locales/si/comments.json
- ghost/i18n/locales/is/comments.json
- apps/comments-ui/src/components/content/context-menus/comment-context-menu.tsx
- ghost/core/core/server/services/comments/comments-service.js
- ghost/i18n/locales/cs/comments.json
- ghost/core/core/server/models/comment.js
- ghost/i18n/locales/fr/comments.json
- ghost/i18n/locales/de/comments.json
- ghost/core/core/server/data/schema/schema.js
- ghost/i18n/locales/sr/comments.json
- ghost/i18n/locales/sw/comments.json
- ghost/i18n/locales/hi/comments.json
- apps/comments-ui/src/components/content/context-menus/admin-context-menu.tsx
- ghost/core/core/server/api/endpoints/utils/serializers/output/mappers/comments.js
- apps/comments-ui/test/e2e/admin-moderation.test.ts
- ghost/i18n/locales/sk/comments.json
- ghost/i18n/locales/ru/comments.json
- ghost/i18n/locales/ro/comments.json
- ghost/core/test/e2e-api/members-comments/comments.test.js
- ghost/i18n/locales/en/comments.json
387b70a to
35239a8
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@ghost/core/core/server/services/comments/comments-service.js`:
- Around line 21-33: The getCommentByID path currently bypasses the pinned-field
shim; update the single-comment read to apply withPinnedSelect so the "pinned"
synthetic column is returned when requested: locate getCommentByID (or the
method that calls this.models.Comment.findOne) and change the call to pass
withPinnedSelect(options) instead of options (e.g.,
this.models.Comment.findOne({id}, withPinnedSelect(options))); ensure the same
options shape (columns/selectRaw) is preserved and that no duplicate selectRaw
merging occurs.
🪄 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: d37ddba8-0faa-4668-a692-3c0ba4795a0b
⛔ Files ignored due to path filters (12)
apps/comments-ui/src/images/icons/external-link.svgis excluded by!**/*.svgapps/comments-ui/src/images/icons/eye-off.svgis excluded by!**/*.svgapps/comments-ui/src/images/icons/eye.svgis excluded by!**/*.svgapps/comments-ui/src/images/icons/flag.svgis excluded by!**/*.svgapps/comments-ui/src/images/icons/pencil.svgis excluded by!**/*.svgapps/comments-ui/src/images/icons/pin-off.svgis excluded by!**/*.svgapps/comments-ui/src/images/icons/pin.svgis excluded by!**/*.svgapps/comments-ui/src/images/icons/trash.svgis excluded by!**/*.svgghost/core/core/frontend/public/admin-auth/admin-auth.min.jsis excluded by!**/*.min.jsghost/core/test/e2e-api/admin/__snapshots__/activity-feed.test.js.snapis excluded by!**/*.snapghost/core/test/e2e-api/admin/__snapshots__/comments.test.js.snapis excluded by!**/*.snapghost/core/test/e2e-api/members-comments/__snapshots__/comments.test.js.snapis excluded by!**/*.snap
📒 Files selected for processing (96)
apps/admin-x-framework/src/api/comments.tsapps/comments-ui/package.jsonapps/comments-ui/src/actions.tsapps/comments-ui/src/app-context.tsapps/comments-ui/src/components/content/comment.tsxapps/comments-ui/src/components/content/context-menus/admin-context-menu.tsxapps/comments-ui/src/components/content/context-menus/author-context-menu.tsxapps/comments-ui/src/components/content/context-menus/comment-context-menu.tsxapps/comments-ui/src/components/content/context-menus/not-author-context-menu.tsxapps/comments-ui/src/utils/admin-api.tsapps/comments-ui/test/e2e/admin-moderation.test.tsapps/comments-ui/test/unit/actions.test.jsapps/comments-ui/test/unit/components/content/comment.test.jsxapps/comments-ui/test/unit/components/content/context-menus/comment-context-menu.test.jsxapps/comments-ui/test/utils/fixtures.tsapps/posts/src/views/comments/components/comment-header.tsxapps/posts/src/views/comments/components/comment-menu.tsxapps/posts/src/views/comments/components/comment-thread-list.tsxapps/posts/src/views/comments/components/comments-list.tsxapps/posts/test/unit/views/comments/components/comment-header.test.tsxghost/core/core/frontend/src/admin-auth/message-handler.jsghost/core/core/server/api/endpoints/comments.jsghost/core/core/server/api/endpoints/utils/serializers/input/comments.jsghost/core/core/server/api/endpoints/utils/serializers/output/mappers/comments.jsghost/core/core/server/data/migrations/versions/6.36/2026-05-02-00-06-36-add-pinned-at-to-comments.jsghost/core/core/server/data/schema/schema.jsghost/core/core/server/models/comment.jsghost/core/core/server/services/comments/comments-controller.jsghost/core/core/server/services/comments/comments-service.jsghost/core/test/e2e-api/admin/comments.test.jsghost/core/test/e2e-api/members-comments/comments.test.jsghost/core/test/unit/frontend/src/admin-auth-message-handler.test.jsghost/core/test/unit/server/data/schema/integrity.test.jsghost/i18n/locales/af/comments.jsonghost/i18n/locales/ar/comments.jsonghost/i18n/locales/bg/comments.jsonghost/i18n/locales/bn/comments.jsonghost/i18n/locales/bs/comments.jsonghost/i18n/locales/ca/comments.jsonghost/i18n/locales/context.jsonghost/i18n/locales/cs/comments.jsonghost/i18n/locales/da/comments.jsonghost/i18n/locales/de-CH/comments.jsonghost/i18n/locales/de/comments.jsonghost/i18n/locales/el/comments.jsonghost/i18n/locales/en/comments.jsonghost/i18n/locales/eo/comments.jsonghost/i18n/locales/es/comments.jsonghost/i18n/locales/et/comments.jsonghost/i18n/locales/eu/comments.jsonghost/i18n/locales/fa/comments.jsonghost/i18n/locales/fi/comments.jsonghost/i18n/locales/fr/comments.jsonghost/i18n/locales/gd/comments.jsonghost/i18n/locales/he/comments.jsonghost/i18n/locales/hi/comments.jsonghost/i18n/locales/hr/comments.jsonghost/i18n/locales/hu/comments.jsonghost/i18n/locales/id/comments.jsonghost/i18n/locales/is/comments.jsonghost/i18n/locales/it/comments.jsonghost/i18n/locales/ja/comments.jsonghost/i18n/locales/ko/comments.jsonghost/i18n/locales/kz/comments.jsonghost/i18n/locales/lt/comments.jsonghost/i18n/locales/lv/comments.jsonghost/i18n/locales/mk/comments.jsonghost/i18n/locales/mn/comments.jsonghost/i18n/locales/ms/comments.jsonghost/i18n/locales/nb/comments.jsonghost/i18n/locales/ne/comments.jsonghost/i18n/locales/nl/comments.jsonghost/i18n/locales/nn/comments.jsonghost/i18n/locales/pa/comments.jsonghost/i18n/locales/pl/comments.jsonghost/i18n/locales/pt-BR/comments.jsonghost/i18n/locales/pt/comments.jsonghost/i18n/locales/ro/comments.jsonghost/i18n/locales/ru/comments.jsonghost/i18n/locales/si/comments.jsonghost/i18n/locales/sk/comments.jsonghost/i18n/locales/sl/comments.jsonghost/i18n/locales/sq/comments.jsonghost/i18n/locales/sr-Cyrl/comments.jsonghost/i18n/locales/sr/comments.jsonghost/i18n/locales/sv/comments.jsonghost/i18n/locales/sw/comments.jsonghost/i18n/locales/ta/comments.jsonghost/i18n/locales/th/comments.jsonghost/i18n/locales/tr/comments.jsonghost/i18n/locales/uk/comments.jsonghost/i18n/locales/ur/comments.jsonghost/i18n/locales/uz/comments.jsonghost/i18n/locales/vi/comments.jsonghost/i18n/locales/zh-Hant/comments.jsonghost/i18n/locales/zh/comments.json
✅ Files skipped from review due to trivial changes (50)
- ghost/i18n/locales/ru/comments.json
- ghost/core/test/unit/server/data/schema/integrity.test.js
- apps/comments-ui/package.json
- ghost/i18n/locales/nb/comments.json
- apps/comments-ui/src/components/content/context-menus/not-author-context-menu.tsx
- apps/posts/test/unit/views/comments/components/comment-header.test.tsx
- ghost/i18n/locales/ar/comments.json
- apps/comments-ui/src/app-context.ts
- apps/comments-ui/src/components/content/context-menus/author-context-menu.tsx
- apps/comments-ui/test/unit/actions.test.js
- apps/comments-ui/src/utils/admin-api.ts
- ghost/i18n/locales/ne/comments.json
- apps/posts/src/views/comments/components/comments-list.tsx
- ghost/i18n/locales/mk/comments.json
- ghost/i18n/locales/et/comments.json
- ghost/i18n/locales/sl/comments.json
- apps/admin-x-framework/src/api/comments.ts
- ghost/i18n/locales/vi/comments.json
- ghost/i18n/locales/pl/comments.json
- ghost/i18n/locales/is/comments.json
- ghost/i18n/locales/hu/comments.json
- ghost/i18n/locales/si/comments.json
- ghost/i18n/locales/el/comments.json
- ghost/i18n/locales/sk/comments.json
- ghost/i18n/locales/bs/comments.json
- apps/comments-ui/test/e2e/admin-moderation.test.ts
- ghost/i18n/locales/cs/comments.json
- ghost/i18n/locales/ta/comments.json
- ghost/i18n/locales/es/comments.json
- ghost/core/test/unit/frontend/src/admin-auth-message-handler.test.js
- ghost/i18n/locales/tr/comments.json
- ghost/i18n/locales/sv/comments.json
- apps/comments-ui/src/components/content/context-menus/comment-context-menu.tsx
- ghost/i18n/locales/id/comments.json
- ghost/i18n/locales/hi/comments.json
- apps/comments-ui/test/unit/components/content/comment.test.jsx
- ghost/i18n/locales/pa/comments.json
- ghost/i18n/locales/uz/comments.json
- ghost/i18n/locales/da/comments.json
- ghost/i18n/locales/it/comments.json
- ghost/i18n/locales/pt/comments.json
- ghost/i18n/locales/pt-BR/comments.json
- ghost/i18n/locales/lv/comments.json
- ghost/core/test/e2e-api/admin/comments.test.js
- ghost/i18n/locales/context.json
- ghost/i18n/locales/eo/comments.json
- ghost/i18n/locales/nn/comments.json
- ghost/i18n/locales/mn/comments.json
- ghost/i18n/locales/lt/comments.json
- ghost/i18n/locales/sr-Cyrl/comments.json
🚧 Files skipped from review as they are similar to previous changes (31)
- apps/posts/src/views/comments/components/comment-thread-list.tsx
- ghost/i18n/locales/af/comments.json
- ghost/i18n/locales/nl/comments.json
- ghost/i18n/locales/bg/comments.json
- ghost/i18n/locales/fa/comments.json
- ghost/i18n/locales/ro/comments.json
- ghost/i18n/locales/uk/comments.json
- ghost/i18n/locales/bn/comments.json
- ghost/i18n/locales/sr/comments.json
- apps/comments-ui/test/unit/components/content/context-menus/comment-context-menu.test.jsx
- ghost/core/core/server/api/endpoints/utils/serializers/output/mappers/comments.js
- ghost/i18n/locales/de/comments.json
- ghost/i18n/locales/hr/comments.json
- ghost/i18n/locales/sq/comments.json
- apps/comments-ui/src/actions.ts
- ghost/core/core/server/data/migrations/versions/6.36/2026-05-02-00-06-36-add-pinned-at-to-comments.js
- ghost/i18n/locales/ko/comments.json
- ghost/i18n/locales/ur/comments.json
- ghost/i18n/locales/de-CH/comments.json
- ghost/i18n/locales/th/comments.json
- ghost/i18n/locales/sw/comments.json
- ghost/core/core/server/api/endpoints/comments.js
- apps/comments-ui/src/components/content/comment.tsx
- ghost/i18n/locales/ms/comments.json
- apps/comments-ui/test/utils/fixtures.ts
- ghost/i18n/locales/ca/comments.json
- ghost/i18n/locales/he/comments.json
- ghost/i18n/locales/fi/comments.json
- ghost/i18n/locales/zh/comments.json
- apps/comments-ui/src/components/content/context-menus/admin-context-menu.tsx
- ghost/i18n/locales/zh-Hant/comments.json
35239a8 to
82c82e0
Compare
There was a problem hiding this comment.
🧹 Nitpick comments (2)
apps/comments-ui/test/unit/components/content/context-menus/comment-context-menu.test.jsx (1)
83-90: ⚡ Quick winAdd a deleted-comment pin-gating test to complete coverage.
You already cover top-level vs reply, but
canPinalso excludes deleted comments. Adding that case will lock the constraint and prevent regressions.✅ Suggested test addition
+ it('does not show pin action for deleted comments', () => { + const comment = buildComment({status: 'deleted'}); + + contextualRender(<CommentContextMenu close={() => {}} comment={comment} />, {appContext: {isAdmin: true}}); + + expect(screen.queryByTestId('pin-button')).not.toBeInTheDocument(); + expect(screen.queryByTestId('unpin-button')).not.toBeInTheDocument(); + });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/comments-ui/test/unit/components/content/context-menus/comment-context-menu.test.jsx` around lines 83 - 90, Add a unit test mirroring the "does not show pin action for replies" case but for deleted comments: render <CommentContextMenu close={() => {}} comment={buildComment({deleted: true})} /> with appContext {isAdmin: true} using contextualRender, then assert screen.queryByTestId('pin-button') and screen.queryByTestId('unpin-button') are not in the document; place this alongside the existing tests to cover the canPin gate for deleted comments.apps/posts/src/views/comments/components/comment-menu.tsx (1)
74-86: ⚡ Quick winWrap menu labels in
t()for internationalization.The PR is labeled
affects:i18n, but these menu item labels are hardcoded English strings. Per coding guidelines, use translation functions for UI text.- <DropdownMenuItem onClick={() => unpinComment({id: commentId})}> - <LucideIcon.PinOff className="size-4" /> - Unpin comment + <DropdownMenuItem onClick={() => unpinComment({id: commentId})}> + <LucideIcon.PinOff className="size-4" /> + {t('Unpin comment')} </DropdownMenuItem> ) : ( - <DropdownMenuItem onClick={() => pinComment({id: commentId})}> - <LucideIcon.Pin className="size-4" /> - Pin comment + <DropdownMenuItem onClick={() => pinComment({id: commentId})}> + <LucideIcon.Pin className="size-4" /> + {t('Pin comment')} </DropdownMenuItem>As per coding guidelines:
**/*.{ts,tsx,js,jsx}: Never split sentences across multiplet()calls in translation strings.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/posts/src/views/comments/components/comment-menu.tsx` around lines 74 - 86, The menu labels "Pin comment" and "Unpin comment" are hardcoded; update the JSX in the DropdownMenuItem instances (the branch that checks comment.pinned and calls pinComment/unpinComment with commentId) to wrap each full label in the translation function t() (e.g., t("Pin comment") and t("Unpin comment")), ensuring each label is a single t() call and not split across multiple t() calls.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In
`@apps/comments-ui/test/unit/components/content/context-menus/comment-context-menu.test.jsx`:
- Around line 83-90: Add a unit test mirroring the "does not show pin action for
replies" case but for deleted comments: render <CommentContextMenu close={() =>
{}} comment={buildComment({deleted: true})} /> with appContext {isAdmin: true}
using contextualRender, then assert screen.queryByTestId('pin-button') and
screen.queryByTestId('unpin-button') are not in the document; place this
alongside the existing tests to cover the canPin gate for deleted comments.
In `@apps/posts/src/views/comments/components/comment-menu.tsx`:
- Around line 74-86: The menu labels "Pin comment" and "Unpin comment" are
hardcoded; update the JSX in the DropdownMenuItem instances (the branch that
checks comment.pinned and calls pinComment/unpinComment with commentId) to wrap
each full label in the translation function t() (e.g., t("Pin comment") and
t("Unpin comment")), ensuring each label is a single t() call and not split
across multiple t() calls.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 4b1f4567-2c4a-4e6e-aef1-558f4230dc35
⛔ Files ignored due to path filters (12)
apps/comments-ui/src/images/icons/external-link.svgis excluded by!**/*.svgapps/comments-ui/src/images/icons/eye-off.svgis excluded by!**/*.svgapps/comments-ui/src/images/icons/eye.svgis excluded by!**/*.svgapps/comments-ui/src/images/icons/flag.svgis excluded by!**/*.svgapps/comments-ui/src/images/icons/pencil.svgis excluded by!**/*.svgapps/comments-ui/src/images/icons/pin-off.svgis excluded by!**/*.svgapps/comments-ui/src/images/icons/pin.svgis excluded by!**/*.svgapps/comments-ui/src/images/icons/trash.svgis excluded by!**/*.svgghost/core/core/frontend/public/admin-auth/admin-auth.min.jsis excluded by!**/*.min.jsghost/core/test/e2e-api/admin/__snapshots__/activity-feed.test.js.snapis excluded by!**/*.snapghost/core/test/e2e-api/admin/__snapshots__/comments.test.js.snapis excluded by!**/*.snapghost/core/test/e2e-api/members-comments/__snapshots__/comments.test.js.snapis excluded by!**/*.snap
📒 Files selected for processing (99)
apps/admin-x-framework/src/api/comments.tsapps/comments-ui/package.jsonapps/comments-ui/src/actions.tsapps/comments-ui/src/app-context.tsapps/comments-ui/src/components/content/comment.tsxapps/comments-ui/src/components/content/context-menus/admin-context-menu.tsxapps/comments-ui/src/components/content/context-menus/author-context-menu.tsxapps/comments-ui/src/components/content/context-menus/comment-context-menu.tsxapps/comments-ui/src/components/content/context-menus/not-author-context-menu.tsxapps/comments-ui/src/utils/admin-api.tsapps/comments-ui/test/e2e/admin-moderation.test.tsapps/comments-ui/test/unit/actions.test.jsapps/comments-ui/test/unit/components/content/comment.test.jsxapps/comments-ui/test/unit/components/content/context-menus/comment-context-menu.test.jsxapps/comments-ui/test/utils/fixtures.tsapps/posts/src/views/comments/components/comment-header.tsxapps/posts/src/views/comments/components/comment-menu.tsxapps/posts/src/views/comments/components/comment-thread-list.tsxapps/posts/src/views/comments/components/comments-list.tsxapps/posts/test/unit/views/comments/components/comment-header.test.tsxghost/admin/package.jsonghost/core/core/frontend/src/admin-auth/message-handler.jsghost/core/core/server/api/endpoints/comments.jsghost/core/core/server/api/endpoints/utils/serializers/input/comments.jsghost/core/core/server/api/endpoints/utils/serializers/output/mappers/comments.jsghost/core/core/server/data/migrations/versions/6.37/2026-05-03-00-46-46-add-pinned-at-to-comments.jsghost/core/core/server/data/schema/schema.jsghost/core/core/server/models/comment.jsghost/core/core/server/services/comments/comments-controller.jsghost/core/core/server/services/comments/comments-service.jsghost/core/package.jsonghost/core/test/e2e-api/admin/comments.test.jsghost/core/test/e2e-api/members-comments/comments.test.jsghost/core/test/unit/api/canary/utils/serializers/output/mapper.test.jsghost/core/test/unit/frontend/src/admin-auth-message-handler.test.jsghost/core/test/unit/server/data/schema/integrity.test.jsghost/i18n/locales/af/comments.jsonghost/i18n/locales/ar/comments.jsonghost/i18n/locales/bg/comments.jsonghost/i18n/locales/bn/comments.jsonghost/i18n/locales/bs/comments.jsonghost/i18n/locales/ca/comments.jsonghost/i18n/locales/context.jsonghost/i18n/locales/cs/comments.jsonghost/i18n/locales/da/comments.jsonghost/i18n/locales/de-CH/comments.jsonghost/i18n/locales/de/comments.jsonghost/i18n/locales/el/comments.jsonghost/i18n/locales/en/comments.jsonghost/i18n/locales/eo/comments.jsonghost/i18n/locales/es/comments.jsonghost/i18n/locales/et/comments.jsonghost/i18n/locales/eu/comments.jsonghost/i18n/locales/fa/comments.jsonghost/i18n/locales/fi/comments.jsonghost/i18n/locales/fr/comments.jsonghost/i18n/locales/gd/comments.jsonghost/i18n/locales/he/comments.jsonghost/i18n/locales/hi/comments.jsonghost/i18n/locales/hr/comments.jsonghost/i18n/locales/hu/comments.jsonghost/i18n/locales/id/comments.jsonghost/i18n/locales/is/comments.jsonghost/i18n/locales/it/comments.jsonghost/i18n/locales/ja/comments.jsonghost/i18n/locales/ko/comments.jsonghost/i18n/locales/kz/comments.jsonghost/i18n/locales/lt/comments.jsonghost/i18n/locales/lv/comments.jsonghost/i18n/locales/mk/comments.jsonghost/i18n/locales/mn/comments.jsonghost/i18n/locales/ms/comments.jsonghost/i18n/locales/nb/comments.jsonghost/i18n/locales/ne/comments.jsonghost/i18n/locales/nl/comments.jsonghost/i18n/locales/nn/comments.jsonghost/i18n/locales/pa/comments.jsonghost/i18n/locales/pl/comments.jsonghost/i18n/locales/pt-BR/comments.jsonghost/i18n/locales/pt/comments.jsonghost/i18n/locales/ro/comments.jsonghost/i18n/locales/ru/comments.jsonghost/i18n/locales/si/comments.jsonghost/i18n/locales/sk/comments.jsonghost/i18n/locales/sl/comments.jsonghost/i18n/locales/sq/comments.jsonghost/i18n/locales/sr-Cyrl/comments.jsonghost/i18n/locales/sr/comments.jsonghost/i18n/locales/sv/comments.jsonghost/i18n/locales/sw/comments.jsonghost/i18n/locales/ta/comments.jsonghost/i18n/locales/th/comments.jsonghost/i18n/locales/tr/comments.jsonghost/i18n/locales/uk/comments.jsonghost/i18n/locales/ur/comments.jsonghost/i18n/locales/uz/comments.jsonghost/i18n/locales/vi/comments.jsonghost/i18n/locales/zh-Hant/comments.jsonghost/i18n/locales/zh/comments.json
✅ Files skipped from review due to trivial changes (58)
- ghost/i18n/locales/ur/comments.json
- ghost/core/test/unit/server/data/schema/integrity.test.js
- ghost/admin/package.json
- apps/comments-ui/package.json
- ghost/i18n/locales/af/comments.json
- ghost/core/package.json
- apps/posts/test/unit/views/comments/components/comment-header.test.tsx
- ghost/i18n/locales/de/comments.json
- apps/comments-ui/test/e2e/admin-moderation.test.ts
- ghost/i18n/locales/bs/comments.json
- ghost/i18n/locales/pa/comments.json
- apps/comments-ui/src/app-context.ts
- ghost/i18n/locales/mk/comments.json
- apps/comments-ui/src/components/content/context-menus/not-author-context-menu.tsx
- ghost/i18n/locales/fi/comments.json
- ghost/i18n/locales/sv/comments.json
- ghost/i18n/locales/is/comments.json
- apps/comments-ui/src/actions.ts
- ghost/i18n/locales/ja/comments.json
- ghost/i18n/locales/lt/comments.json
- ghost/i18n/locales/pt/comments.json
- ghost/i18n/locales/bg/comments.json
- apps/comments-ui/test/unit/components/content/comment.test.jsx
- ghost/i18n/locales/ta/comments.json
- ghost/i18n/locales/et/comments.json
- ghost/i18n/locales/nn/comments.json
- apps/comments-ui/test/utils/fixtures.ts
- ghost/i18n/locales/en/comments.json
- ghost/i18n/locales/sr-Cyrl/comments.json
- ghost/i18n/locales/it/comments.json
- ghost/i18n/locales/sk/comments.json
- ghost/i18n/locales/id/comments.json
- ghost/i18n/locales/sl/comments.json
- ghost/i18n/locales/fa/comments.json
- ghost/i18n/locales/nb/comments.json
- ghost/i18n/locales/lv/comments.json
- ghost/i18n/locales/si/comments.json
- ghost/core/core/frontend/src/admin-auth/message-handler.js
- ghost/i18n/locales/el/comments.json
- apps/comments-ui/test/unit/actions.test.js
- ghost/i18n/locales/es/comments.json
- ghost/i18n/locales/mn/comments.json
- ghost/i18n/locales/da/comments.json
- ghost/i18n/locales/ca/comments.json
- ghost/i18n/locales/sq/comments.json
- ghost/core/test/e2e-api/members-comments/comments.test.js
- ghost/i18n/locales/uz/comments.json
- ghost/i18n/locales/eu/comments.json
- ghost/i18n/locales/hi/comments.json
- ghost/i18n/locales/ro/comments.json
- ghost/i18n/locales/uk/comments.json
- ghost/i18n/locales/ko/comments.json
- ghost/i18n/locales/pt-BR/comments.json
- ghost/i18n/locales/eo/comments.json
- ghost/i18n/locales/kz/comments.json
- ghost/i18n/locales/ne/comments.json
- ghost/i18n/locales/pl/comments.json
- ghost/core/test/e2e-api/admin/comments.test.js
🚧 Files skipped from review as they are similar to previous changes (23)
- ghost/i18n/locales/ru/comments.json
- apps/posts/src/views/comments/components/comment-thread-list.tsx
- ghost/i18n/locales/bn/comments.json
- ghost/i18n/locales/nl/comments.json
- ghost/i18n/locales/ms/comments.json
- ghost/i18n/locales/hu/comments.json
- ghost/core/core/server/api/endpoints/utils/serializers/output/mappers/comments.js
- ghost/i18n/locales/sr/comments.json
- ghost/i18n/locales/he/comments.json
- ghost/i18n/locales/vi/comments.json
- ghost/core/core/server/api/endpoints/comments.js
- ghost/core/test/unit/frontend/src/admin-auth-message-handler.test.js
- ghost/i18n/locales/th/comments.json
- apps/admin-x-framework/src/api/comments.ts
- apps/comments-ui/src/components/content/comment.tsx
- ghost/i18n/locales/cs/comments.json
- ghost/i18n/locales/de-CH/comments.json
- ghost/i18n/locales/sw/comments.json
- apps/comments-ui/src/utils/admin-api.ts
- ghost/i18n/locales/zh-Hant/comments.json
- apps/comments-ui/src/components/content/context-menus/admin-context-menu.tsx
- ghost/i18n/locales/zh/comments.json
- ghost/i18n/locales/context.json
cceb344 to
e14e945
Compare
Staff need a way to elevate important discussion without changing admin moderation order. This adds top-level comment pinning with public pinned-first ordering, staff pin/unpin controls, visual pinned badges, API support, schema changes, and focused coverage for the backend and UI paths.
e14e945 to
c386fa2
Compare
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #27653 +/- ##
==========================================
+ Coverage 73.17% 73.19% +0.01%
==========================================
Files 1561 1561
Lines 127051 127168 +117
Branches 15383 15420 +37
==========================================
+ Hits 92970 93080 +110
- Misses 33105 33130 +25
+ Partials 976 958 -18
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
ref #27653 New sites should allow members to comment without requiring staff to find and enable the setting first. When comments are explicitly set to off, the Admin sidebar now hides the Comments section so the navigation reflects the disabled state.
ref #27653 Comments are now enabled by default for new sites, so default email previews include the comment CTA. Updating these snapshots keeps the acceptance suite aligned with the new default.
ref #27653 Comments are now enabled by default for new sites, so default email previews and settings responses changed. Updating these snapshots keeps the acceptance suite aligned with the new default.
ef70401 to
01ae1ea
Compare
ref #27653 Comments are now enabled by default for new sites, so default email previews, settings responses, and Content API responses changed. Updating these snapshots keeps the acceptance suite aligned with the new default.
01ae1ea to
53dcc55
Compare
ref #27653 Comments are now enabled by default for new sites, so default email previews, email card rendering, settings responses, and Content API responses changed. Updating these snapshots keeps CI aligned with the new default.
53dcc55 to
07e7dfa
Compare
ref TryGhost#27653 Comments are now enabled by default for new sites, so default email previews, email card rendering, settings responses, and Content API responses changed. Updating these snapshots keeps CI aligned with the new default.
07e7dfa to
2ae90c0
Compare
E2E Tests FailedTo view the Playwright test report locally, run: REPORT_DIR=$(mktemp -d) && gh run download 25268138056 -n playwright-report -D "$REPORT_DIR" && npx playwright show-report "$REPORT_DIR" |
E2E Tests FailedTo view the Playwright test report locally, run: REPORT_DIR=$(mktemp -d) && gh run download 25268472078 -n playwright-report -D "$REPORT_DIR" && npx playwright show-report "$REPORT_DIR" |
ref #27653 Comments are now enabled by default for new sites, so default email previews, email card rendering, settings responses, and Content API responses changed. Updating these snapshots keeps CI aligned with the new default.
2ae90c0 to
e222a97
Compare
E2E Tests FailedTo view the Playwright test report locally, run: REPORT_DIR=$(mktemp -d) && gh run download 25268954611 -n playwright-report -D "$REPORT_DIR" && npx playwright show-report "$REPORT_DIR" |
ref #27653 Comments are now enabled by default for new sites, so default email previews, email card rendering, settings responses, and Content API responses changed. Updating these snapshots keeps CI aligned with the new default.
e222a97 to
73a02bb
Compare
E2E Tests FailedTo view the Playwright test report locally, run: REPORT_DIR=$(mktemp -d) && gh run download 25269390196 -n playwright-report -D "$REPORT_DIR" && npx playwright show-report "$REPORT_DIR" |
ref #27653 Comments are now enabled by default for new sites, so default email previews, email card rendering, settings responses, and Content API responses changed. Updating these snapshots keeps CI aligned with the new default.
73a02bb to
e6b2a2a
Compare
|
I'm curious - what's the rationale behind having comments on by default? Probably half the folks who do not want comments because they're a moderation headache and potential spam source. Some of the other half are very interested in comments as an engagement mechanism, so improving comments is awesome -- I'm just not sure that making comments opt-out is desirable. It's a lot easier to tell someone how to turn on commenting than to tell someone how to clear out a bunch of spammy comments that they didn't know were enabled. |
Summary
Following user feedback, add support for pinning member comments both on front-end and in admin, if the current user has moderation access.
Also refined moderation user menu design to be more consistent with admin comment moderation, adding icons for actions.
2nd/3rd commits fix two additional comments-related issues:
(I thought this part was going to be a tiny change but it ended up needing all sorts of test fixtures to be updated so it ended up being a larger change)
Front end
Admin