Skip to content

Commit 5fc9c61

Browse files
committed
fix(app-shell): read the save-to-view grant through personalGrant and enter its write site (#30073)
Claude-Session: https://claude.ai/code/session_01QSeKYpFBMr917PmVkG9up7 Co-authored-by: Claude <noreply@anthropic.com>
1 parent 3ee4384 commit 5fc9c61

2 files changed

Lines changed: 7 additions & 5 deletions

File tree

‎ui/packages/app-shell/src/views/ObjectView.tsx‎

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -718,9 +718,11 @@ function ObjectViewInner({ dataSource, objects, onEdit, externalRefreshKey }: Co
718718
// shape is the same), then the reader's list state for it is cleared and
719719
// the list opens again on the saved view. A panel the fold cannot write
720720
// losslessly keeps the refusal Save as my view gives.
721-
const canUpdatePersonalView = activeIsPersonal
722-
&& personalViews.status === 'ready'
723-
&& (!perms.isLoaded || perms.can(PERSONAL_VIEW_OBJECT, 'update'));
721+
// The reader's own `sys_personal_view` rows: one grant read for Save as my
722+
// view (create) and for Rename and 保存 (update).
723+
const personalViewsReady = personalViews.status === 'ready';
724+
const personalGrant = (action: 'create' | 'update') => !perms.isLoaded || perms.can(PERSONAL_VIEW_OBJECT, action);
725+
const canUpdatePersonalView = activeIsPersonal && personalViewsReady && personalGrant('update');
724726
const handleSaveToPersonalView = async () => {
725727
if (!activeView || !activeIsPersonal) return;
726728
const built = buildPersonalViewConfig(objectDef.name, activeView, {
@@ -797,8 +799,6 @@ function ObjectViewInner({ dataSource, objects, onEdit, externalRefreshKey }: Co
797799
// one (`sys_personal_view` read and create), not to admins only; the name
798800
// is asked in `PersonalViewNameDialog`. A reader who cannot read the
799801
// list's own object is offered no Save: there is no list to keep.
800-
const personalViewsReady = personalViews.status === 'ready';
801-
const personalGrant = (action: 'create' | 'update') => !perms.isLoaded || perms.can(PERSONAL_VIEW_OBJECT, action);
802802
const listReadable = !perms.isLoaded || perms.can(objectDef.name, 'read');
803803
const [personalViewDialog, setPersonalViewDialog] = useState<{ mode: 'save' } | { mode: 'rename'; viewId: string } | null>(null);
804804
const openPersonalView = (id: string) => {

‎ui/packages/plugin-form/src/affordanceGrantMap-12082.test.tsx‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -739,6 +739,8 @@ const WRITE_SITES: Record<string, WriteSite> = {
739739
outOfFamily("the caller's OWN sys_personal_view row (Save as my view), offered behind perms.can(sys_personal_view, create) in ObjectView; the server keeps the rows owner-scoped; no record the caller browses is written"),
740740
'packages/app-shell/src/views/personalViews.ts :: usePersonalViews > rename :: update':
741741
outOfFamily("the caller's OWN sys_personal_view row (Rename), offered behind perms.can(sys_personal_view, update) in ObjectView; the server keeps the rows owner-scoped and fixes the owner; no record the caller browses is written"),
742+
'packages/app-shell/src/views/personalViews.ts :: usePersonalViews > update :: update':
743+
outOfFamily("the caller's OWN sys_personal_view row (保存, the list written back into the open personal view), offered behind the same perms.can(sys_personal_view, update) read as Rename in ObjectView; the server keeps the rows owner-scoped and fixes the owner; no record the caller browses is written"),
742744
// ── core ──
743745
'packages/core/src/actions/TransactionManager.ts :: TransactionManager > executeBatch :: batch':
744746
door('the action engine\'s transaction executor runs the operations its caller hands it'),

0 commit comments

Comments
 (0)