Skip to content

feat(contexts): carry the menu through export and import - #3138

Open
rubenvdlinde wants to merge 2 commits into
feat/noid/app-menu-nestingfrom
feat/noid/app-menu-export-import
Open

rubenvdlinde wants to merge 2 commits into
feat/noid/app-menu-nestingfrom
feat/noid/app-menu-export-import

Conversation

@rubenvdlinde

@rubenvdlinde rubenvdlinde commented Oct 22, 2026 •

Copy link
Copy Markdown

Part of #3099, on top of #3137 (menu nesting). A menu now travels with its application when the application's scheme is exported and imported, and when a user's data is moved to another instance.

Size: 868 lines: 390 code, 453 tests and 25 generated OpenAPI.

What changes

  • Export writes each item with its target by a stable reference: tables and views by UUID, pages and parent items by technical name, automatic names like item_7 included, so nesting survives the round trip.
  • Import links each item to the matching table or view on the receiving side. A page item is linked to the page with the same technical name, and skipped if there's none, because a scheme import doesn't create pages yet. An item whose target didn't come along is skipped too.
  • Moving a user's data (TablesMigrator) carries the menu's links and the items for the moved table and pages. View items are skipped for now, because that import has no mapping from old view ids to new ones.
  • Every import keeps the menu one level deep, takes at most 200 items, skips items with broken ids, and logs every skipped item with the reason. Both imports nest and name items through the same code and save through the same path as an update. An item with an invalid technical name is kept and gets a name of its own.
  • The scheme preview now also shows the menu: what it is now, what it would become, and which items would be skipped.
  • ViewMapper::findByUuid and TableMapper::findByUuid were already declared to return nothing when the UUID is unknown, but threw an exception instead. They now return null, which the import relies on.

How it was tested

On Nextcloud 34 with PostgreSQL:

  • FindByUuidTest: both mappers return null for an unknown UUID
  • database round trips of a menu through importContext() and through a scheme, including a child under an automatically named parent
  • a crafted file with three levels, and a broken menu in an export file
  • the full unit suite on PostgreSQL and SQLite: 665 tests
  • composer cs:check, psalm and rector, all clean

🖼️ Screenshots

Backend only, no UI changes.

🏁 Checklist

  • ⛑️ Tests (unit and/or integration) are included or not needed
  • 🔙 Backport requests are created or not needed: /backport to stableX.X
  • 📅 Milestone is set
  • 🌸 PR title is meaningful (if it should be in the changelog: is it meaningful to users?)

🤖 AI (if applicable)

  • The content of this PR was partly or fully generated using AI

Menus travel with the application's scheme and with a user's data,
with targets referenced by UUID or technical name. TableMapper and
ViewMapper findByUuid now return null when nothing matches, as their
signatures already said.

Assisted-by: ClaudeCode:claude-opus-5-5
Signed-off-by: Ruben van der Linde <ruben@conduction.nl>
Assisted-by: ClaudeCode:claude-opus-5-5
Signed-off-by: Ruben van der Linde <ruben@conduction.nl>
@rubenvdlinde
rubenvdlinde force-pushed the feat/noid/app-menu-export-import branch from 8ac7add to bfdf4e9 Compare October 26, 2026 09:24
@rubenvdlinde
rubenvdlinde marked this pull request as ready for review October 26, 2026 09:26
@samin-z
samin-z self-requested a review October 26, 2026 17:03

@samin-z samin-z 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.

thank you @rubenvdlinde

i tested the pr manually for now, what i found is the API part works fine, the exported file contains 'menuItems' and the import saves them when they are in the body. however the import screen does not send them.

from what i see, ImportContextSchemePreview.vue builds the import body in emitPayload() from the name, icon,description, tables and nodes. 'menuItems' is left out, so the application keeps the menu it already has. the preview response already returns the menu under 'menuItem' and it needs to be included in the import request, otherwise a user of the screen never gets the menu from the file.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants