Skip to content
This repository was archived by the owner on May 29, 2026. It is now read-only.

[2/8] feat(nav): top-bar entries per published virtual app + per-app icon#59

Merged
rubenvdlinde merged 7 commits into
developmentfrom
feature/openbuilt-nextcloud-nav
May 16, 2026
Merged

[2/8] feat(nav): top-bar entries per published virtual app + per-app icon#59
rubenvdlinde merged 7 commits into
developmentfrom
feature/openbuilt-nextcloud-nav

Conversation

@rubenvdlinde

Copy link
Copy Markdown
Contributor

Summary

Adds a Nextcloud top-bar nav entry per published virtual app, gated by `permissions.{owners,editors,viewers}` with a `group:*` sentinel for public apps. Icons (light + dark) are stored as files-attached-to-object on the Application record per ADR-001 — survive promotions, apply across all version tiers.

Note: targets PR #58 (the foundation) because the icon fields are added to the Application schema that #58 reshapes. Once #58 merges, GitHub auto-rebases this onto development.

What changes

  • New: `IconService` + `IconController` (light/dark fallback chain; 60s cache; signed-in users only)
  • New: `AppNavigationService` (per-request closure for INavigationManager)
  • New: top-level `icon` / `iconDark` fields on Application schema (NOT inside manifest blob — keeps the change orthogonal to nextcloud-vue's app-manifest schema)
  • New: icon picker/uploader on detail page
  • Modified: `ApplicationCard.vue` renders icon before title; legacy "Live" chip removed (status pill already signals Published)
  • Also fixes 3 pre-existing hydra gate failures (gate-1 SPDX in 8 template files, gate-5 missing HealthController/MetricsController, gate-7 missing 401 guards on diffVersions/ExportsController::download/SettingsController)

Test plan

  • PHPUnit — IconServiceTest (6), IconControllerTest (5), AppNavigationServiceTest (9) — 20 new tests
  • All 14 hydra gates — green
  • Icon endpoint returns 404 in dev container — needs runtime investigation (route registered correctly, controller may have DI issue or path mismatch) — see follow-up issue

🤖 Generated with Claude Code

… schema

Adds icon and iconDark as optional top-level properties (sibling to slug/name/permissions)
on the Application schema in openbuilt_register.json, each holding a {ref: string}
object pointing to an OR-attached file. Implements task 1 of openbuilt-nextcloud-nav.
Implements the icon-serving HTTP layer (tasks 2.1-2.4 of openbuilt-nextcloud-nav):
- IconService resolves per-app SVG icons via OR-attached files with a light/dark
  fallback chain (icon.ref → /img/app.svg; iconDark.ref → icon.ref → app-dark.svg → app.svg)
- IconController exposes GET /icons/{slug}.svg and GET /icons/{slug}-dark.svg with
  explicit 401 guards, Content-Type: image/svg+xml, Cache-Control: public, max-age=60
- Routes registered in appinfo/routes.php before the SPA catch-all
- psalm.xml + phpstan.neon updated with required class suppressions
…n::boot()

Implements task 3 of openbuilt-nextcloud-nav:
- AppNavigationService queries OR for all published Applications and registers a
  per-request INavigationManager closure for each, with group:* wildcard sentinel,
  user:<uid> match, group-intersection, and NC admin bypass (REQ-OBNAV-002/003/004)
- Application::boot() wires the service via INavigationManager with a try/catch
  so boot never throws on OR unavailability
…onCard and detail page

Implements tasks 4 and 5 of openbuilt-nextcloud-nav:
- ApplicationCard shows a 20px icon img from the icon endpoint; falls back to /img/app.svg
  on error; removes the Live chip (task 4.2)
- IconUploadSection.vue: drop-zone + preview for light and dark icons, calls OR file-attachment
  endpoints and patches the Application record's icon/iconDark refs
- ApplicationIconTab.vue: sidebar tab wrapping IconUploadSection in the app detail page
- customComponents.js exports ApplicationIconTab; manifest.json registers the Icons tab
  at order 50 in the VirtualAppDetail sidebarTabs
…vigationService

Implements tasks 7.1-7.3 of openbuilt-nextcloud-nav plus updates SettingsControllerTest:
- IconServiceTest: 6 tests covering light/dark fallback chains, OR miss, service error
- IconControllerTest: 5 tests covering 200/401/404/500 paths with Reflection header reads
- AppNavigationServiceTest: 9 tests covering all permission branches, wildcard, OR failure,
  closure shape, and admin bypass
- SettingsControllerTest: updated to inject IUserSession (required by auth guard); adds
  testIndexReturns401WhenNoSession
…5/7)

Gate-1 (spdx-headers): add @copyright + @license docblocks to 8 template files
  that had only inline // SPDX-License-Identifier comments.

Gate-5 (route-auth): create HealthController + MetricsController (routes declared
  but files missing); add #[NoAdminRequired] + #[NoCSRFRequired] to SettingsController::load.

Gate-7 (no-admin-idor): add explicit Http::STATUS_UNAUTHORIZED guards to
  ApplicationsController::diffVersions, ExportsController::download, and all three
  SettingsController methods; inject IUserSession into SettingsController constructor.
…otes

Records deferred items: seed data attachment (tasks 6.1/6.2, blocked on
SeedHelloWorld deletion by spec C) and E2E/Newman tests (tasks 7.4-7.6,
no existing harness). All implemented items (tasks 1-5, 7.1-7.3) complete.
@rubenvdlinde rubenvdlinde changed the base branch from feature/openbuilt-versioning-model to development May 16, 2026 08:45
@rubenvdlinde rubenvdlinde merged commit 22ccbb2 into development May 16, 2026
17 of 24 checks passed
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant