Skip to content

📚❌ Add copy/delete row actions#2456

Merged
enjeck merged 9 commits into
mainfrom
feat/noid/row-actions
May 19, 2026
Merged

📚❌ Add copy/delete row actions#2456
enjeck merged 9 commits into
mainfrom
feat/noid/row-actions

Conversation

@AndyScherzinger

@AndyScherzinger AndyScherzinger commented Apr 11, 2026

Copy link
Copy Markdown
Member
  • Alternative approach to @juliusknorr's Add row actions to delete or duplicate a row #1713
    • follow-up is opening the edit dialog on the original PR (see link above)
    • this PR opens a create dialog but pre-populated with the row values
  • Any of the two would resolve one of the release stretch goals
  • PR changes the edit Icon-Button into a 3-dot menu with edit/delete/copy + respective icons and updates the cypress tests

@AndyScherzinger AndyScherzinger added enhancement New feature or request 3. to review Waiting for reviews labels Apr 11, 2026
@AndyScherzinger AndyScherzinger requested a review from enjeck April 11, 2026 18:55
@AndyScherzinger AndyScherzinger requested a review from blizzz as a code owner April 11, 2026 18:55
Comment thread cypress/e2e/tables-table.cy.js Outdated
@AndyScherzinger AndyScherzinger force-pushed the feat/noid/row-actions branch 6 times, most recently from a2ee2c1 to cbd28cd Compare April 11, 2026 21:52
@AndyScherzinger AndyScherzinger requested a review from enjeck April 11, 2026 22:03
@AndyScherzinger AndyScherzinger changed the title Add copy/delete row actions 📚❌ Add copy/delete row actions Apr 12, 2026
@blizzz blizzz linked an issue Apr 30, 2026 that may be closed by this pull request
@AndyScherzinger AndyScherzinger force-pushed the feat/noid/row-actions branch 3 times, most recently from 24c8c07 to 112f0b4 Compare May 3, 2026 21:37
@enjeck

enjeck commented May 15, 2026

Copy link
Copy Markdown
Contributor

Alternative approach to @juliusknorr's Add row actions to delete or duplicate a row #1713

  • follow-up is opening the edit dialog on the original PR (see link above)
  • this PR opens a create dialog but pre-populated with the row values

I closed #2456. I think this is better. Also safer around unique/mandatory/read-only fields than #1713’s immediate backend insert

@AndyScherzinger AndyScherzinger force-pushed the feat/noid/row-actions branch from 112f0b4 to 4348a57 Compare May 15, 2026 11:50
@AndyScherzinger

Copy link
Copy Markdown
Member Author

I rebased the PR and fixed the conflicts, still need to do a round of testing on latest rebase 👍

@AndyScherzinger

Copy link
Copy Markdown
Member Author

Tested various column types/unique/default etc. and works for me 👍

@enjeck enjeck left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a few comments, thx

Comment thread src/shared/components/ncTable/partials/TableRow.vue
Comment thread src/shared/components/ncTable/partials/TableRow.vue
@AndyScherzinger AndyScherzinger force-pushed the feat/noid/row-actions branch 2 times, most recently from 0b9d9f4 to 5ee587c Compare May 15, 2026 21:42
@AndyScherzinger AndyScherzinger force-pushed the feat/noid/row-actions branch from 5ee587c to 3d97e04 Compare May 15, 2026 21:45
enjeck
enjeck previously requested changes May 17, 2026

@enjeck enjeck left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the row actions do not work for me on publicly shared tables.
Delete doesn't work in smartpicker

@enjeck

enjeck commented May 18, 2026

Copy link
Copy Markdown
Contributor
nextcloud-1  | {"reqId":"Fn4RSJWiSFhqiVtVmT2c","level":3,"time":"2026-05-18T05:57:28+00:00","remoteAddr":"192.168.21.4","user":"--","app":"tables","method":"PUT","url":"/ocs/v2.php/apps/tables/api/2/public/HYc49vtxyhbqBpKa/rows/3","scriptName":"/ocs/v2.php","message":"An internal error or exception occurred: OCA\\Tables\\Service\\RowService - updateSet: No user id in context, but needed.","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/147.0.0.0 Safari/537.36","version":"34.0.0.7","exception":{"Exception":"OCA\\Tables\\Errors\\InternalError","Message":"OCA\\Tables\\Service\\RowService - updateSet: No user id in context, but needed.","Code":0,"Trace":[{"file":"/var/www/html/apps-extra/tables/lib/Controller/PublicRowOCSController.php","line":205,"function":"updateSet","class":"OCA\\Tables\\Service\\RowService","type":"->","args":["*** sensitive parameters replaced ***"]},{"file":"/var/www/html/lib/private/AppFramework/Http/Dispatcher.php","line":165,"function":"updateRow","class":"OCA\\Tables\\Controller\\PublicRowOCSController","type":"->","args":["*** sensitive parameters replaced ***"]},{"file":"/var/www/html/lib/private/AppFramework/Http/Dispatcher.php","line":78,"function":"executeController","class":"OC\\AppFramework\\Http\\Dispatcher","type":"->","args":[{"__class__":"OCA\\Tables\\Controller\\PublicRowOCSController"},"updateRow"]},{"file":"/var/www/html/lib/private/AppFramework/App.php","line":137,"function":"dispatch","class":"OC\\AppFramework\\Http\\Dispatcher","type":"->","args":[{"__class__":"OCA\\Tables\\Controller\\PublicRowOCSController"},"updateRow"]},{"file":"/var/www/html/lib/private/Route/Router.php","line":324,"function":"main","class":"OC\\AppFramework\\App","type":"::","args":["OCA\\Tables\\Controller\\PublicRowOCSController","updateRow",{"__class__":"OC\\AppFramework\\DependencyInjection\\DIContainer"},{"_route":"ocs.tables.publicrowocs.updaterow","token":"*** sensitive parameters replaced ***","rowId":"3"}]},{"file":"/var/www/html/ocs/v1.php","line":63,"function":"match","class":"OC\\Route\\Router","type":"->","args":["/ocsapp/apps/tables/api/2/public/HYc49vtxyhbqBpKa/rows/3"]},{"file":"/var/www/html/ocs/v2.php","line":10,"args":["/var/www/html/ocs/v1.php"],"function":"require_once"}],"File":"/var/www/html/apps-extra/tables/lib/Service/RowService.php","Line":577,"message":"An internal error or exception occurred: OCA\\Tables\\Service\\RowService - updateSet: No user id in context, but needed.","exception":"{\"class\":\"OCA\\Tables\\Errors\\InternalError\",\"message\":\"OCA\\Tables\\Service\\RowService - updateSet: No user id in context, but needed.\",\"code\":0,\"file\":\"/var/www/html/apps-extra/tables/lib/Service/RowService.php:577\",\"trace\":\"#0 /var/www/html/apps-extra/tables/lib/Controller/PublicRowOCSController.php(205): OCA\\Tables\\Service\\RowService->updateSet(3, 1, Array, '', NULL)\\n#1 /var/www/html/lib/private/AppFramework/Http/Dispatcher.php(165): OCA\\Tables\\Controller\\PublicRowOCSController->updateRow('HYc49vtxyhbqBpK...', 3, Array)\\n#2 /var/www/html/lib/private/AppFramework/Http/Dispatcher.php(78): OC\\AppFramework\\Http\\Dispatcher->executeController(Object(OCA\\Tables\\Controller\\PublicRowOCSController), 'updateRow')\\n#3 /var/www/html/lib/private/AppFramework/App.php(137): OC\\AppFramework\\Http\\Dispatcher->dispatch(Object(OCA\\Tables\\Controller\\PublicRowOCSController), 'updateRow')\\n#4 /var/www/html/lib/private/Route/Router.php(324): OC\\AppFramework\\App::main('OCA\\\\Tables\\\\Cont...', 'updateRow', Object(OC\\AppFramework\\DependencyInjection\\DIContainer), Array)\\n#5 /var/www/html/ocs/v1.php(63): OC\\Route\\Router->match('/ocsapp/apps/ta...')\\n#6 /var/www/html/ocs/v2.php(10): require_once('/var/www/html/o...')\\n#7 {main}\"}","CustomMessage":"An internal error or exception occurred: OCA\\Tables\\Service\\RowService - updateSet: No user id in context, but needed."}}

...changing the action button to a 3-dot menu

AI-assistant: Claude Code v2.1.101 (Claude Sonnet 4.6)

Signed-off-by: Andy Scherzinger <info@andy-scherzinger.de>
AI-assistant: Claude Code v2.1.101 (Claude Sonnet 4.6)

Signed-off-by: Andy Scherzinger <info@andy-scherzinger.de>
AI-assistant: Claude Code v2.1.126 (Claude Sonnet 4.6)
AI-assistant: Claude Code v2.1.126 (Claude Sonnet 4.6)
Signed-off-by: Andy Scherzinger <info@andy-scherzinger.de>
AI-assistant: Claude Code v2.1.142 (Claude Sonnet 4.6)
Signed-off-by: Andy Scherzinger <info@andy-scherzinger.de>
…bedded tables

AI-assistant: Claude Code v2.1.142 (Claude Sonnet 4.6)
Signed-off-by: Andy Scherzinger <info@andy-scherzinger.de>
…sync import guard

ef3db38 tightened the userId guard in RowService::create() and ::updateSet()
from `=== null` to `=== null || === ''` to prevent async import background jobs
from silently creating rows without a real user in context.

However, the codebase intentionally uses an empty string as the public-context
sentinel: PublicRowOCSController calls setPublicContext(), which sets
$this->userId = '' in SuperService and $isPublicContext = true in
PermissionsService. PermissionsService::basisCheck() explicitly handles
$userId === '' by returning true when isPublicContext is set. The tightened
guard in ef3db38 rejected '' before it ever reached the permissions layer,
breaking row create and update for all public link shares.

The fix tracks the public context flag in SuperService itself via a new
$isPublicContext property, set alongside $this->userId = '' in
setPublicContext(). The guard in RowService is updated to:

  null              → always throws (DI never injected a user)
  '' + !publicCtx   → throws (background job running without a user — the
                       case ef3db38 was protecting against)
  '' + publicCtx    → passes (setPublicContext() was called intentionally)
  'alice'           → always passes (normal authenticated user)

Async imports remain protected because ImportTableJob never calls
setPublicContext(), so $isPublicContext stays false for that code path.

AI-assistant: Claude Code v2.1.143 (Claude Sonnet 4.6)
Signed-off-by: Andy Scherzinger <info@andy-scherzinger.de>
@AndyScherzinger AndyScherzinger force-pushed the feat/noid/row-actions branch from 3d97e04 to 009d6f7 Compare May 18, 2026 07:58
@AndyScherzinger

Copy link
Copy Markdown
Member Author

Fixed via 009d6f7
Bug introduced by #1801 blocking any row actions without a user which is correct for imports in the background but not for public share actions

Signed-off-by: Andy Scherzinger <info@andy-scherzinger.de>

@benjaminfrueh benjaminfrueh left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just tested this feature too, works great.

@AndyScherzinger One minor thing I noticed during testing, when a public share only has READ and DELETE permission, it still shows the "Edit Row", even though this is not functional without UPDATE permission. Maybe a rare use-case, but still possible to configure.

It could directly show the delete icon in this case, like it does currently show the "Edit" pencil directly with only READ+UPDATE permissions.

Signed-off-by: Andy Scherzinger <info@andy-scherzinger.de>
@AndyScherzinger

Copy link
Copy Markdown
Member Author

@benjaminfrueh nice catch, fixed via 6cec622 - feel free to retest. I can' t properly test embeddings. I can't get text to properly work so edits etc. semi-fail on the UI but persist on the backend, also for changes outside of the introduced menu. So it is not related to that. If you have a properly working setup for embedding to test, that would be highly appreciated 🙏

@AndyScherzinger AndyScherzinger force-pushed the feat/noid/row-actions branch from 5cf9a72 to be6e465 Compare May 18, 2026 19:55
Signed-off-by: Andy Scherzinger <info@andy-scherzinger.de>
@AndyScherzinger AndyScherzinger force-pushed the feat/noid/row-actions branch from be6e465 to bf11f2c Compare May 18, 2026 20:04
@AndyScherzinger AndyScherzinger requested a review from enjeck May 18, 2026 20:16
@AndyScherzinger AndyScherzinger dismissed enjeck’s stale review May 18, 2026 20:17

issues fixed while not originated from this PR itself

@benjaminfrueh

benjaminfrueh commented May 19, 2026

Copy link
Copy Markdown
Contributor

@benjaminfrueh nice catch, fixed via 6cec622 - feel free to retest. I can' t properly test embeddings. I can't get text to properly work so edits etc. semi-fail on the UI but persist on the backend, also for changes outside of the introduced menu. So it is not related to that. If you have a properly working setup for embedding to test, that would be highly appreciated 🙏

There are two pre-existing issues with the smart-picker embedded table in text that I experienced while testing. They are also currently happening on our instance.

  1. inline row updates in smart-picker fail in the frontend with error: TypeError: Cannot read properties of null (reading 'toString') - happens here https://github.com/nextcloud/tables/blob/main/src/store/data.js#L16 because elementId is null in this case, but the request succeeds and the row is updated. The row delete request succeeds but the change is not reflected in the frontend (row is still there until reload and no error). The normal edit via modal is working correctly and also shown.
  2. The action button (now three-dot), which is position: sticky is always visible in smart picker embeds but is unclickable unless scrolled all the way to the right, as it renders behind other elements. A z-index of 3 or higher on the sticky container would fix this.

@enjeck as these are pre-existing issues, maybe we can merge this one first and move the smart-picker fixes to a follow-up PR, what do you think?

@enjeck

enjeck commented May 19, 2026

Copy link
Copy Markdown
Contributor

@enjeck as these are pre-existing issues, maybe we can merge this one first and move the smart-picker fixes to a follow-up PR, what do you think?

yeah, sure, thx

@enjeck enjeck merged commit 94255b6 into main May 19, 2026
66 checks passed
@enjeck enjeck deleted the feat/noid/row-actions branch May 19, 2026 20:00
@AndyScherzinger AndyScherzinger added this to the v2.2.0 milestone May 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

3. to review Waiting for reviews AI assisted enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

duplicate rows / copy and modify

3 participants