feat(19.6.1-02): per-record locked permission values

- cabana.PermissionRecordLock and the optional PermissionRecordLockProvider
- RecordMeta.PermissionLocks (meta.permissionLocks) on show and save responses, notes localized
- save rule: a record-locked code keeps its stored value, a differing present value is 403
- Roles lock code-declared grants (locked_by_code); Administrators lock every override on your own record (locked_self)
- SPA: FORM_PERMISSION_LOCKS, locked codes never sent, record-locked rows in the permission editor
- regenerated admin OpenAPI and schema.d.ts; docs and README
This commit is contained in:
Jakub Zych
2026-10-22 11:29:25 +02:00
parent e2525d4956
commit 705f94d9e5
22 changed files with 588 additions and 60 deletions

View File

@@ -5,7 +5,7 @@
// provider (settings pages) gets the defaults of the injecting control.
import { ref, type InjectionKey, type Ref } from 'vue'
import type { FileRoutes } from '../../api/files'
import type { AdminDependentOptionsResult, AdminRecord, FormField } from '../../api/types'
import type { AdminDependentOptionsResult, AdminRecord, FormField, PermissionRecordLock } from '../../api/types'
/** Read-only view of the form's current values. */
export const FORM_VALUES: InjectionKey<Readonly<Ref<AdminRecord>>> = Symbol('summer.form.values')
@@ -90,6 +90,15 @@ export const FORM_SESSION: InjectionKey<FormSession> = Symbol('summer.form.sessi
*/
export const SETTINGS_SECRETS: InjectionKey<Readonly<Ref<Record<string, string>>>> = Symbol('summer.settings.secrets')
/**
* The record's permission locks (meta.permissionLocks, D-21): per
* permissioneditor field name, the codes the stored record fixes with the
* value and note to show. FormView and PreviewView provide it from the record
* response; a control without a provider sees no record lock.
*/
export const FORM_PERMISSION_LOCKS: InjectionKey<Readonly<Ref<Record<string, PermissionRecordLock[]>>>> =
Symbol('summer.form.permissionLocks')
/** Event a widget element dispatches to run its action (bubbles, composed). */
export const WIDGET_EVENT = 'summer-action'