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

@@ -2666,7 +2666,7 @@ export interface paths {
};
/**
* Show an admin record
* @description meta.labels carries the display labels of relation fields. meta.actions lists the declared record actions the requesting admin may run and that apply to the record's current state; it is absent when none is offered.
* @description meta.labels carries the display labels of relation fields. meta.actions lists the declared record actions the requesting admin may run and that apply to the record's current state; it is absent when none is offered. meta.permissionLocks maps a `type: permissioneditor` field to the permissions this record fixes (the controller's cabana.PermissionRecordLockProvider), each with code, value (1, -1, or 0 for inherit) and note (localized): the admin shows that value and the note on the row and never sends the code; it is absent when no record lock applies.
*/
get: {
parameters: {
@@ -2726,7 +2726,7 @@ export interface paths {
};
/**
* Update an admin record
* @description A plugin field type's value is decoded by its type inside the save; a value equal to the one the form was served for that field and locale is neither decoded nor written. When a type reports what it changed, meta.notices lists the localized messages per field (and content locale), and the key is absent otherwise.
* @description A plugin field type's value is decoded by its type inside the save; a value equal to the one the form was served for that field and locale is neither decoded nor written. When a type reports what it changed, meta.notices lists the localized messages per field (and content locale), and the key is absent otherwise. A record-locked permission (meta.permissionLocks) keeps its stored value when its code is absent from the body; a present value that differs from the stored one is a 403 on the field. The response carries meta.permissionLocks like the show route.
*/
put: {
parameters: {
@@ -6138,6 +6138,11 @@ export interface components {
locked?: boolean;
tab?: string;
};
"cabana.PermissionRecordLock": {
code: string;
note: string;
value: number;
};
"cabana.RecordAction": {
confirm?: string;
/**
@@ -6158,6 +6163,16 @@ export interface components {
[key: string]: components["schemas"]["cabana.RelationOption"][];
};
notices?: components["schemas"]["cabana.FieldNotice"][];
/**
* @description PermissionLocks are, per permissioneditor field, the permissions this
* record fixes (the controller's PermissionRecordLockProvider), with the
* value to show and the note, translated in the request locale. The show
* and save responses carry it; it is omitted when no record lock applies
* and is never part of the form schema.
*/
permissionLocks?: {
[key: string]: components["schemas"]["cabana.PermissionRecordLock"][];
};
/**
* @description Title is the controller's record title (pact.FormRecordTitle), omitted
* when the controller names none.