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:
@@ -20,6 +20,7 @@ import {
|
||||
FORM_FEEDBACK,
|
||||
FORM_LOCALE,
|
||||
FORM_PATCH,
|
||||
FORM_PERMISSION_LOCKS,
|
||||
FORM_SESSION,
|
||||
FORM_VALUES,
|
||||
createUploadGate,
|
||||
@@ -78,6 +79,9 @@ const recordId = mode === 'update' ? Number(route.params.id) : null
|
||||
const schema = ref<FormView | null>(null)
|
||||
const values = ref<AdminRecord>({})
|
||||
const labels = ref<RecordMeta['labels']>({})
|
||||
// The record's permission locks (meta.permissionLocks, D-21): codes the
|
||||
// stored record fixes; they are shown locked and never sent.
|
||||
const permissionLocks = ref<NonNullable<RecordMeta['permissionLocks']>>({})
|
||||
/** The controller's record title (meta.title, pact.FormRecordTitle); '' when it names none. */
|
||||
const recordTitle = ref('')
|
||||
const errors = ref<Record<string, string[]>>({})
|
||||
@@ -205,7 +209,7 @@ const dirty = computed(
|
||||
!loading.value &&
|
||||
(pendingChanges.value > 0 ||
|
||||
activeUploads.value > 0 ||
|
||||
snapshot(editablePayload(fields.value, values.value, mode)) !== saved.value),
|
||||
snapshot(editablePayload(fields.value, values.value, mode, permissionLocks.value)) !== saved.value),
|
||||
)
|
||||
|
||||
function adopt(record: RecordEnvelope | undefined): void {
|
||||
@@ -221,9 +225,10 @@ function adopt(record: RecordEnvelope | undefined): void {
|
||||
}
|
||||
values.value = next
|
||||
labels.value = record.meta.labels ?? {}
|
||||
permissionLocks.value = record.meta.permissionLocks ?? {}
|
||||
recordTitle.value = record.meta.title ?? ''
|
||||
}
|
||||
saved.value = snapshot(editablePayload(fields.value, values.value, mode))
|
||||
saved.value = snapshot(editablePayload(fields.value, values.value, mode, permissionLocks.value))
|
||||
}
|
||||
|
||||
async function load(): Promise<void> {
|
||||
@@ -316,6 +321,7 @@ provide(FORM_PATCH, update)
|
||||
provide(FORM_LOCALE, computed(() => schema.value?.meta.locale || currentLocale.value))
|
||||
provide(FORM_ENABLED_LOCALES, computed(() => schemaEnabledLocales(schema.value?.meta)))
|
||||
provide(FORM_ASSETS, () => assetsReady)
|
||||
provide(FORM_PERMISSION_LOCKS, computed(() => permissionLocks.value))
|
||||
// The form's single polite live region (dependent dropdown clears, repeater
|
||||
// moves) and the feedback nested controls read: errors and notices by row
|
||||
// path, the confirm dialog (Phase 17, D-07, D-08).
|
||||
@@ -395,7 +401,7 @@ async function save(): Promise<RecordEnvelope | null> {
|
||||
forbidden.value = null
|
||||
notices.value = {}
|
||||
try {
|
||||
const body = editablePayload(fields.value, values.value, mode)
|
||||
const body = editablePayload(fields.value, values.value, mode, permissionLocks.value)
|
||||
const result =
|
||||
recordId === null
|
||||
? await api.POST('/{vendor}/{plugin}/{controller}', { params: { path, header: sessionHeader }, body })
|
||||
|
||||
Reference in New Issue
Block a user