🗃️ Make archiving tables/applications a personal option - #2476
Open
AndyScherzinger wants to merge 45 commits into
Open
🗃️ Make archiving tables/applications a personal option#2476AndyScherzinger wants to merge 45 commits into
AndyScherzinger wants to merge 45 commits into
Conversation
AndyScherzinger
force-pushed
the
feat/2125/personal-archiving-option
branch
5 times, most recently
from
April 12, 2026 15:34
008f4f3 to
0e2bbdc
Compare
AndyScherzinger
force-pushed
the
feat/2125/personal-archiving-option
branch
2 times, most recently
from
April 12, 2026 18:56
d3fce3e to
fd235a6
Compare
AndyScherzinger
marked this pull request as ready for review
April 12, 2026 19:36
AndyScherzinger
requested review from
benjaminfrueh,
jancborchardt,
marcoambrosini and
samin-z
April 15, 2026 22:19
jancborchardt
previously requested changes
Apr 28, 2026
Member
There was a problem hiding this comment.
Looks generally nice, 2 details
- I’d say to use outline icons instead of filled for both cases, archiving and unarchiving
- In the cases of "Archive app" and "Unarchive app", the sorting of actions in the menu is off. We always recommend the destructive action (Delete) to be the very last, and then the next most destructive like Archive could be directly above it.
AndyScherzinger
force-pushed
the
feat/2125/personal-archiving-option
branch
from
April 28, 2026 13:13
fd235a6 to
ba16212
Compare
AndyScherzinger
force-pushed
the
feat/2125/personal-archiving-option
branch
4 times, most recently
from
April 28, 2026 20:06
840a1ab to
086aad3
Compare
…ic also checks for it anyways The context endpoints lack the #[RequirePermission] attribute that the table endpoints have. However the mapper enforces the same constraint, but is less explicit for future maintenance and for consistency with other endpoints AI-assistant: Claude Code v2.1.119 (Claude Sonnet 4.6) Signed-off-by: Andy Scherzinger <info@andy-scherzinger.de>
Setting the parameter inside the loop preventing rebuilding the query representation if this really loops a lot. Co-authored-by: Christoph Wurst <1374172+ChristophWurst@users.noreply.github.com> Signed-off-by: Andy Scherzinger <info@andy-scherzinger.de>
…ust getXXX Co-authored-by: Christoph Wurst <1374172+ChristophWurst@users.noreply.github.com> Signed-off-by: Andy Scherzinger <info@andy-scherzinger.de>
Signed-off-by: Andy Scherzinger <info@andy-scherzinger.de>
AI-assistant: Claude Code 2.1.119 (Claude Sonnet 4.6) Signed-off-by: Andy Scherzinger <info@andy-scherzinger.de>
Signed-off-by: Enjeck C. <patrathewhiz@gmail.com>
The middleware only recognizes contexts when the type arrives as the string 'context'; an integer NODE_TYPE_CONTEXT is rejected as an invalid node type before the controller runs. Assisted-by: ClaudeCode:claude-fable-5 Signed-off-by: Andy Scherzinger <info@andy-scherzinger.de>
TableService::find() returns the entity-level archived flag; use
getTableForUser() so GET /tables/{id} reflects the requesting user's
personal archive override.
Assisted-by: ClaudeCode:claude-fable-5
Signed-off-by: Andy Scherzinger <info@andy-scherzinger.de>
Resolve the per-user archive state for nav-bar contexts and skip archived ones so archiving an application also declutters the global navigation. Assisted-by: ClaudeCode:claude-fable-5 Signed-off-by: Andy Scherzinger <info@andy-scherzinger.de>
The migration was created in April under version 2020 and would sort before migrations already released on main (2200-2210); rename it to 2400 with a current date stamp. Assisted-by: ClaudeCode:claude-fable-5 Signed-off-by: Andy Scherzinger <info@andy-scherzinger.de>
Replace hardcoded node type 0 with Application::NODE_TYPE_TABLE. Assisted-by: ClaudeCode:claude-fable-5 Signed-off-by: Andy Scherzinger <info@andy-scherzinger.de>
Prepare the share lookup and the archive check/insert queries a single time and rebind their parameters per iteration instead of recreating query builders for every archived table and recipient. Assisted-by: ClaudeCode:claude-fable-5 Signed-off-by: Andy Scherzinger <info@andy-scherzinger.de>
Replace the read-modify-write cycle with IDBConnection::setValues(), which is portable across all supported databases and closes the race between concurrent requests hitting the unique index. Assisted-by: ClaudeCode:claude-fable-5 Signed-off-by: Andy Scherzinger <info@andy-scherzinger.de>
Assisted-by: ClaudeCode:claude-fable-5 Signed-off-by: Andy Scherzinger <info@andy-scherzinger.de>
The method had no production caller; tests now verify row counts with a direct count query instead. Assisted-by: ClaudeCode:claude-fable-5 Signed-off-by: Andy Scherzinger <info@andy-scherzinger.de>
Extract the shared load-overrides-and-merge logic of the table and context enrichment methods into one private helper. Assisted-by: ClaudeCode:claude-fable-5 Signed-off-by: Andy Scherzinger <info@andy-scherzinger.de>
…r archive test Assisted-by: ClaudeCode:claude-fable-5 Signed-off-by: Andy Scherzinger <info@andy-scherzinger.de>
…ler archive test Assisted-by: ClaudeCode:claude-fable-5 Signed-off-by: Andy Scherzinger <info@andy-scherzinger.de>
Return early when the archived table or application is no longer in the local store instead of dereferencing undefined. Assisted-by: ClaudeCode:claude-fable-5 Signed-off-by: Andy Scherzinger <info@andy-scherzinger.de>
Vue 3 proxy reactivity tracks array index assignment, so the splice swap is unnecessary; drop the unrelated change. Assisted-by: ClaudeCode:claude-fable-5 Signed-off-by: Andy Scherzinger <info@andy-scherzinger.de>
Align the archived-applications counter with the archived-tables one, which already uses the count prop introduced with nextcloud/vue 9. Assisted-by: ClaudeCode:claude-fable-5 Signed-off-by: Andy Scherzinger <info@andy-scherzinger.de>
Wrap the ArchiveService calls in the same OCP\DB\Exception-to- InternalError conversion TableService uses so tables and contexts report archive failures consistently. Assisted-by: ClaudeCode:claude-fable-5 Signed-off-by: Andy Scherzinger <info@andy-scherzinger.de>
Assisted-by: ClaudeCode:claude-fable-5 Signed-off-by: Andy Scherzinger <info@andy-scherzinger.de>
…access An ownership transfer preserved the outgoing owner's archive view with a personal override even when they lose access to the node entirely, leaving orphaned rows in tables_archive_user. Remove the override inside the transfer transaction when the outgoing owner can no longer read the table or access the context. Assisted-by: ClaudeCode:claude-fable-5 Signed-off-by: Andy Scherzinger <info@andy-scherzinger.de>
tables_shares stores node_type as a string ('table'), so filtering with
the integer NODE_TYPE_TABLE constant never matched the direct user
shares (or, on MySQL loose casting, matched every node type). Recipient
archive records were therefore not migrated correctly.
Assisted-by: ClaudeCode:claude-fable-5
Signed-off-by: Andy Scherzinger <info@andy-scherzinger.de>
Add UserArchiveMapper::findUserIdsForNode() and deleteAllForUser(), plus ShareMapper::findNodesByReceiver(), as building blocks for cleaning up per-user archive overrides when access is lost. Assisted-by: ClaudeCode:claude-fable-5 Signed-off-by: Andy Scherzinger <info@andy-scherzinger.de>
Follow the pattern established by server group shares and the circles app: shares stay group/circle-targeted with dynamic access resolution, and only the materialized per-user state is removed once membership or sharing ends. The new ArchiveCleanupService drops a user's archive override after verifying they no longer have access to the node, and is triggered when - a user is removed from a group (UserRemovedEvent), - a member is removed from a circle (CircleMemberRemovedEvent, including inherited members of a removed nested circle), - a group is deleted or a circle destroyed (the existing receiver cleanup now also purges stale overrides on the affected nodes), - a share is deleted (single receiver check for user shares, full node purge for group and circle shares), - a user account is deleted (all overrides of that user are removed). Assisted-by: ClaudeCode:claude-fable-5 Signed-off-by: Andy Scherzinger <info@andy-scherzinger.de>
AndyScherzinger
force-pushed
the
feat/2125/personal-archiving-option
branch
from
September 5, 2026 06:30
24d6afb to
802890d
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
🖼️ Screenshots
The original ticket #2125 defined tables and views, while this PR tacked tables and apps. Given a clear view on how archived views should get displayed this could be added as a follow-up PR to not make this PR grow in review-size and complexity.
Summary
archivedflag on tables with a two-layer per-user model: an owner-level flag on the entity and a newtables_archive_usertable that stores individual user overrides, so archive state is resolved per-user at read time. When an owner archives a table or application, it is automatically treated as archived for every user with access, but any of those users may override this for themselves; an owner unarchiving resets the state for everyone by clearing all per-user overrides.tables_tables.archivedcolumn is repurposed from a simple global toggle into the owner-level signal, and the database migration converts all currently-archived tables into owner-archived entries to preserve existing data without disruption.ArchiveServicecentralizes all business logic for both resource types, four new API endpoints are split acrossApiTablesControllerandContextControllerfollowing existing naming conventions, and the frontend gains archive/unarchive actions in both navigation item menus alongside a new collapsible "Archived applications" section mirroring the already-existing "Archived tables" section.The database migration only covers the owner and direct user shares, not groups, etc. since they can't be resolved directly via SQL (think teams/groups). So in these cases archived tables would resurface until manually archived by the respective user. Also there is no cleanup for teams/groups, if a user is taken out of a team, the archive meta-info would not get deleted. Only simple but confusing way around it would be to prevent archiving/unarchiving for tables/apps where a user is neither the owner nor a user-share receiver. Expensive alternative would be a housekeeping job checking all these archive bits periodically, but that also sounds expensive with little use. Else we would need to consume a team/group changes event and check if some cleanup is needed. WDYT?
🏁 Checklist
/backport to stableX.X🤖 AI (if applicable)