Skip to content

fix(plugin-detail): related table offers Export only to a reader who may export - #30156

Merged
objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/issue-30069-related-export-permission
Nov 5, 2026
Merged

objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/issue-30069-related-export-permission

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #30069

Clause-②: no
Metadata-protocol: none

维护者速读

  • 改了什么:记录页「相关」页签里的表格,只有当读者在列表页也能看到「导出」时才显示「导出」;导出被服务器拒绝时,相关表格、列表页和表格视图三处都显示界面语言的提示,不再显示服务器的英文原句。
  • 为什么改:同一个读者在商机列表页看不到「导出」,在客户记录的商机页签里却能看到,点了才收到一句英文报错,产品对同一权限说了两种话。
  • 风险与回滚:只读权限缺省时(老后端、无权限提供者)仍显示按钮,行为与列表页一致;服务器照旧拦截导出。回滚即撤销本 PR,无数据或元数据迁移。
  • 先看:ui/packages/plugin-detail/src/RelatedList.tsx(exportPermitted 一段与导出失败分支);ui/packages/plugin-list/src/ListView.tsx 与 ui/packages/plugin-grid/src/ObjectGrid.tsx 的导出 catch。
  • 两分钟验证:pnpm --dir ui exec vitest run packages/plugin-detail/src/__tests__/RelatedList.enlargedToolbar-27524.test.tsx,预期 9 个测试全部通过,其中 4 个是本卡新加的(无导出权限不见按钮、有权限只导出本关系的行、中文界面下 403 显示中文、非拒绝类失败仍显示服务器原句)。
  • 需要你做的:无,席位落地。

What changes

  • The related table asks the list page's export question. The enlarged related list (the record page's active tab) drew Export whenever its data source could export. It now also reads the child object's effective API operation set through usePermissions().getObjectApiOperations, the same source and the same default-allow rule as ListView's exportPermitted (and ObjectGrid's and ReportExportControl's). A reader whose set leaves out export sees no Export; a missing set keeps the button, as on the list page. The export request itself is unchanged, so a reader who may export still exports this relation's rows alone (the parent scope, the list filter, the chip, the filter text and the order).
  • An export refusal reads in the Console's language, in every door that calls exportDownload. The three doors are the related table, the list page and the grid. Each showed the thrown error's message, which for the export route's 403 is the server's English sentence ("Export is not permitted on object ..."). Each now asks the existing shared classifier isPermissionError from @object-ui/react (403, PERMISSION_DENIED, FORBIDDEN) and, when it answers true, shows the new view.exportNotPermitted sentence. Any other failure keeps the server's sentence.
  • view.exportNotPermitted is added to all ten locale packs, and to the three packages' provider-less default maps, byte-identical to the en pack (the precedent is view.malformedFilter, one sentence shared by several surfaces).
  • Landing point: the refusal is localized ui-side by status and code. The route sends a machine code (EXPORT_NOT_PERMITTED) with status 403, so no server change is needed and packages/** is untouched.

Tests

Run on head 31bb7e7cb.

  • RelatedList.enlargedToolbar-27524.test.tsx gains one top-level describe (the component's shared harness) with four cases under a real MePermissionsProvider and I18nProvider: a reader without export sees no Export and the export route is never called; a reader with export sees it and the request is scoped to the parent (lead = L-1); under zh a 403 EXPORT_NOT_PERMITTED shows the Chinese sentence and not the server's; a 504 control still shows the server's sentence.
  • ListView.test.tsx and exportServer.test.tsx (grid): the existing "surfaces export failures" pins used new Error('Permission denied'), which is itself a refusal shape. They now use a 504 so they keep pinning "a failure is surfaced, not swallowed", and a refusal case beside each pins the Console sentence and the absence of the server's text.
Affected suites and type-check
pnpm --dir ui exec vitest run --maxWorkers=2 packages/plugin-detail/ packages/plugin-grid/ packages/plugin-list/ packages/i18n/ packages/app-shell/src/__tests__/defaults-maps-mirror-en-pack.test.tsx scripts/__tests__/check-i18n-call-site-keys.test.ts
 Test Files  932 passed | 1 skipped (933)
      Tests  9033 passed | 31 skipped (9064)
os-verify-lock: VERDICT command-exit 0

pnpm exec turbo run type-check --concurrency=2 --filter=./ui/packages/plugin-detail --filter=./ui/packages/plugin-list --filter=./ui/packages/plugin-grid --filter=./ui/packages/i18n
 Tasks:    56 successful, 56 total
os-verify-lock: VERDICT command-exit 0
Ablations (one-time proofs; each restored, blob == HEAD and git diff HEAD empty)

The test imports ../RelatedList from source, so no build sits between the mutation and the run. Both mutations went through scripts/ablation-replace.mjs (anchor x1 to x0, replacement x0 to x1).

1. exportPermitted forced true
   × a reader without export sees no 导出, and nothing is asked of the export route
   Tests  1 failed | 8 passed (9)

2. the refusal branch replaced by false
   × the server's 403 reads in the Console's language, not as the route's English sentence
   Tests  1 failed | 8 passed (9)

Acceptance notes

  • The related table reads apiOperations of api, the object its export request is sent to, so the question and the request name the same object.
  • Not changed: an export whose object has the operation switched off by its enable block (the api-disabled kind of classifyLoadError) is not a permission refusal, and it still shows the server's sentence in all three doors. Observation only, not measured against a running server.
  • ReportExportControl builds its file in the browser and calls no export route, so it has no server refusal to localize.

Verification

Local gate families (derived with dispatch-gates --commands from the branch's change set, 36 families, each exit captured before any pipe)
Run reconciliation — 36 derived, 36 run, 0 NOT-MEASURED, 0 UNRUN.
every family exit 0, including:
node scripts/check-closing-keyword-parity.mjs (+ --self-test)
node scripts/check-sdui-manifest.mjs (+ --self-test)
node scripts/check-adr-0087-registration.mjs --base origin/main
pnpm check:undeclared-dep-imports
pnpm check:nul-bytes
pnpm check:published-files
pnpm check:test-source-alias
pnpm check:type-check-coverage
pnpm check:type-check-debt
pnpm --dir ui exec vitest run scripts/   Test Files 170 passed | 2 skipped (172)
pnpm --dir ui check:new-line-citations   0 new citation(s)

The repo-wide scans (pnpm lint and the rest of the farm) are CI's.


Generated by Claude Code

@github-actions github-actions Bot added size/m documentation Improvements or additions to documentation tooling Fix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface labels Nov 4, 2026
@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review November 5, 2026 00:37
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Nov 5, 2026
Merged via the queue into main with commit 5f80129 Nov 5, 2026
56 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-30069-related-export-permission branch November 5, 2026 01:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/m tooling Fix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Related table on a record page offers 导出 to a reader who may not export, and the click ends in an English error

1 participant