VPR-59 [4/4] CMS: delegated block editing via per-block edit permissions#255
VPR-59 [4/4] CMS: delegated block editing via per-block edit permissions#255rlorenzo wants to merge 14 commits into
Conversation
There was a problem hiding this comment.
Pull request overview
Adds “delegated block editing” to the CMS migration stack by introducing per-block edit-permission lists (separate from view permissions) and a content-scoped editing/file workflow that allows non-managers to edit only content + attachments for blocks they’re delegated.
Changes:
- Introduces
ContentBlockToEditPermission(entity + EF mapping) and edit-authorization inCmsContentBlockService(CanEditAsync, editable-block listing, attachable-file search, rollback-delete eligibility). - Expands the CMS content API with content-scoped endpoints for delegated editors (editable blocks list, attachable-files search, block-scoped file check-name/upload/rollback-delete) and adds attachment deltas to the content-only PATCH.
- Updates the Vue CMS editor and hub to support “capability mode” (manager vs delegated UI) and adds/updates frontend + backend tests for delegated scenarios.
Reviewed changes
Copilot reviewed 20 out of 20 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| web/Models/VIPER/ContentBlockToEditPermission.cs | New EF entity representing a block → edit-permission row. |
| web/Models/VIPER/ContentBlock.cs | Adds navigation collection for edit-permission rows. |
| web/Classes/SQLContext/VIPERContext.cs | Registers DbSet + model configuration for the new table. |
| web/Areas/CMS/Services/CmsContentBlockService.cs | Core authorization + delegated editing behaviors (content-only update with attachment deltas, editable listing, attachable-file search, rollback-delete checks). |
| web/Areas/CMS/Models/DTOs/ContentBlockDto.cs | Adds EditPermissions to block DTO; adds minimal CmsAttachableFileDto. |
| web/Areas/CMS/Models/CmsContentBlockMapper.cs | Maps edit-permission list into DTO. |
| web/Areas/CMS/Models/CMSBlockAddEdit.cs | Adds EditPermissions to create/update request model. |
| web/Areas/CMS/Controllers/CMSContentController.cs | Widens selected endpoints to SVMSecure + CanEditAsync gate; adds content-scoped endpoints for delegated file attach/upload/rollback-delete. |
| VueApp/src/CMS/types/index.ts | Adds editPermissions, plus types for editable blocks and attachable file search results. |
| VueApp/src/CMS/router/routes.ts | Broadens content-block edit route permission to SVMSecure (server is source of truth). |
| VueApp/src/CMS/pages/ContentBlockEdit.vue | Implements manager vs delegated capability UI; uses PATCH for delegated saves; switches attach/search and rollback paths to content-scoped APIs. |
| VueApp/src/CMS/pages/CmsHome.vue | Adds “Blocks you can edit” hub card (delegated editors). |
| VueApp/src/CMS/components/PermissionSelector.vue | Makes hint configurable to reuse for edit-permissions selector. |
| VueApp/src/CMS/components/InlineFileUpload.vue | Adds block-scoped upload mode (check-name/upload/rollback-delete) for delegated editors. |
| VueApp/src/CMS/tests/inline-file-upload.test.ts | Tests block-scoped uploader behavior (no folder/permission fields; no use-existing; POST overwrite). |
| VueApp/src/CMS/tests/content-block-edit-save.test.ts | Updates save payload expectations; tests edit-access selector; updates rollback URL expectations; updates attachable-files behavior. |
| VueApp/src/CMS/tests/content-block-edit-delegated.test.ts | New delegated-mode editor tests (read-only settings, PATCH save, conflict handling). |
| VueApp/src/CMS/tests/cms-home.test.ts | New tests for delegated editable-blocks hub card behavior. |
| test/CMS/CMSContentControllerTests.cs | Extends controller tests for CanEditAsync gating + content-scoped file ops. |
| test/CMS/CmsContentBlockServiceTests.cs | Adds service-level tests for delegated authorization, attachment deltas, editable listing, attachable search, and rollback-delete eligibility. |
📝 WalkthroughWalkthroughDelegated CMS editors can discover assigned blocks, edit content without manager settings access, replace attachments, and use block-scoped file APIs. Backend permission storage, authorization, controller endpoints, frontend flows, and tests were updated accordingly. ChangesDelegated CMS editing
Estimated code review effort: 4 (Complex) | ~60 minutes Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
f1ca704 to
c7bd327
Compare
26628ea to
45255eb
Compare
c7bd327 to
bdacb1a
Compare
192b05e to
cb5b979
Compare
cb5b979 to
87cecc9
Compare
Slice 3/3 of the restacked CMS migration (tip file states). The Vue 3 management UI: files, content blocks with QEditor and version diffs, left-nav editor, link collections, import and bulk-encrypt tools, the redesigned hub with the recent-activity rail, shared sortable-list / server-table / dialog components, mobile card mode, and the full frontend test suite. Completes the stack; the tree now matches the development branch tip exactly.
- extract getAuditActionColor to a shared composable so the CMS file audit and the Effort audit list (and any future audit surface) color the same verb classes identically, per PR #253 review feedback - extract ChangeDetailDiff from AuditList's three layouts to remove the old/new diff markup duplication flagged by the JSCPD gate
feature/clinical-scheduler-audit-log merges to main first and reworked the same regions of AuditList (filter card, search row, change-detail markup). Adopt its version wholesale - including AuditChangeDetail, byte-identical, extended to the desktop layout - and re-apply only the shared audit palette, so the later main merge resolves cleanly. ChangeDetailDiff is superseded and removed.
Adds a "view source" toolbar command so editors can inspect and edit the underlying HTML directly (img alt text, a href/target) until richer attribute dialogs exist.
…ady on the page The "Add Left-Nav Menu" link used onMounted to check ?add=1, so re-clicking it while already on the page did nothing (dialog didn't open, query param wasn't stripped, nav highlight was wrong). Switched to the same consume-the-query-flag watcher pattern Files.vue already uses for ?upload=1.
…system selects - File upload's "VIPER app (folder)" select now notes the folder can't be changed after upload, matching the pattern Content Blocks already uses for its section-path select - Content block's "System" select now explains the Viper/Public choice (which site renders the block) instead of relying on the label alone
Deleted/all views gain a sortable "Purges" column (purgeOn = deletedOn + 30 days; sorts by the API's deletedOn for an identical order), a matching card-mode field, and a warning badge when a file's purge is within 7 days or overdue -- previously only visible per-file via the "Deleted ... purges ..." row label.
… page - Strip a leading "viper" path segment on the import page to match the legacy paste format, mirroring the backend's ResolveSource handling
- Add a "Diff vs current" row action alongside the existing diff-with-previous-version action, so reviewers can see what changed since a given saved version without it being superseded first - Disable the action for rows whose block has since been deleted
…PA base path - Both create-spa-router and bootstrap-spa stripped only a single trailing slash from VITE_VIPER_HOME, so a misconfigured value with more than one (e.g. "/2///") would leave a stray slash in the router base / area-path prefix check
- Mirrors the backend's new blank-URL rejection with the same client-side check the empty-text validation already uses, so the error surfaces immediately instead of after a round trip
Admins attach RAPS permissions to a content block as its EDIT permissions; holders of any of them can edit that block without SVMSecure.CMS.ManageContentBlocks. - New ContentBlockToEditPermission entity/table (schema applied manually per environment; DDL in the PR description). An empty edit list means manager-only: delegation is explicit, deliberately not the view list "empty means all SVMSecure" rule - CanEditAsync (manager OR edit-permission intersection, fail-closed, single-resolve HashSet) gates block load, history, diff, and the content-only PATCH, which now also carries attachment deltas; the delegated DTO has no settings fields, so field whitelisting is server-side by construction - Content-scoped file endpoints (attachable search capped at 25 with a minimal DTO, per-block check-name/upload, rollback-only delete constrained to uploader+folder+not-attached-elsewhere) give the editor one code path for managers and delegates alike - Editor renders a capability mode: read-only settings summary for delegates, full editor plus a new "Edit access" selector for managers; the hub gains a "Blocks you can edit" card; the editor route relies on server enforcement - Delegates can view and load version history (restoring is a content save); soft-deleted blocks are not delegate-editable; the anonymous fn endpoint discloses nothing new - 48 new tests (authorization matrix, field scoping, attachment deltas, rollback constraints, capability-mode rendering, hub card)
…of the default hint - PermissionSelector's hint prop now accepts null to suppress the default hint text entirely (no reserved bottom space), instead of every consumer being stuck with "Users need any one of the selected permissions" - Left nav edit hoists that copy to a single explanatory line above the item list instead of repeating it under every item's selector
…nt attach - RollbackDeleteFileAsync's "not attached elsewhere" recheck and the soft-delete it guards were two separate statements with no transactional isolation, leaving a race window where a concurrent attach to a different block could land in the gap and get stranded by the delete anyway - Wrap both in a Serializable transaction via a shared TransactionHelper (also usable by other services with the same needs); tests override the new virtual ExecuteInTransactionAsync hook to bypass transactions, since the EF in-memory provider doesn't support them
faa3eb1 to
f91a9e1
Compare
87cecc9 to
f41adea
Compare
c73f33b to
055a9a5
Compare
Slice 4 of the CMS migration stack (stacks on #253; the diff shows only this feature). Adds delegated block editing: admins attach RAPS permissions to a block as its edit permissions, and holders of any of them can edit that block's content and attached files — without
SVMSecure.CMS.ManageContentBlocksand without access to anything else.Apply to dev → TEST → PROD ahead of the corresponding deploy (same manual-DDL process as VPR-143). Additive only; invisible to legacy ColdFusion (which reads only the view-permission table), so coexistence is unaffected.
Semantics
ManageContentBlocksor any of B's edit permissions (ANY-OF).Implementation
ContentBlockToEditPermissionentity/table +CanEditAsyncauthorization (single-resolve permission HashSet, fail-closed).GET /editable,GET /attachable-files, per-blockfiles/check-name,POST {id}/files, rollback-onlyDELETE {id}/files/{guid}(owner + folder + not-attached-elsewhere constrained).fnendpoint DTO unchanged (no edit-permission disclosure).