diff --git a/README.md b/README.md index 262858f..284ce3d 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ Import: `gitea.neitzel.de/konrad/go-nc-exapp` (package `gonexapp`). - **Groups** — Users and Groups reads: `UserGroups`, `GroupMembers`, `ListGroups` (no search/paging). Directory calls (`GroupMembers` / `ListGroups`) run as the Credentials user and need an admin or subadmin - **Access Gate** — optional Required Groups enforcement (`Wrap` + `Check`), English denied HTML for browsers (200 + `frame-ancestors 'self'`), positive membership cache; default skip for lifecycle paths and **`/js/`** top-menu scripts; env helpers for `REQUIRED_GROUPS` / `REQUIRED_GROUPS_CACHE_SECONDS` - **Top Menu visibility** — `TopMenuAdminRequired` helper for deploy env `TOP_MENU_ADMIN_REQUIRED` (`0` / `1` for AppAPI top-menu OCS) -- **App navigation** — optional Files-style shell (`AppNavigation.Handler`). The ExApp supplies the tree, the page for each item, and an optional header. The selected item is the `item` query parameter (override with `SelectKey`). Other query parameters, including a Visit folder, are left in place. Not mounting the handler keeps the ExApp’s own page. Lifecycle routes, HaRP startup, and Top Menu registration stay in the ExApp. +- **App navigation** — optional Files-style shell (`AppNavigation.Handler`). The ExApp supplies the tree, the page for each item, and an optional header. The selected item is the `item` query parameter (override with `SelectKey`). Other query parameters, including a Visit folder, are left in place. By default the shell loads the Nextcloud theme stylesheets, paints `--image-background`, and copies the surrounding page’s `data-theme-*` markers; set `DisableTheme` to skip the stylesheets. An item may set `Icon` to a same-origin image URL. `NextcloudIcons` names the core SVGs Nextcloud already serves (`NextcloudIcons.Folder`, `NextcloudIcons.Password`, and the rest). Not mounting the handler keeps the ExApp’s own page. Lifecycle routes, HaRP startup, and Top Menu registration stay in the ExApp. **Excluded** diff --git a/icons.go b/icons.go new file mode 100644 index 0000000..f9eecd4 --- /dev/null +++ b/icons.go @@ -0,0 +1,93 @@ +package gonexapp + +// NextcloudIcons are SVG paths served by Nextcloud core under /core/img/. +// The library does not ship the files. Set Item.Icon to one of these. +// Dark mode inverts them with Nextcloud’s --background-invert-if-dark filter. +var NextcloudIcons = nextcloudIcons{ + Folder: "/core/img/filetypes/folder.svg", + FolderShared: "/core/img/filetypes/folder-shared.svg", + FolderPublic: "/core/img/filetypes/folder-public.svg", + FolderStarred: "/core/img/filetypes/folder-starred.svg", + FolderEncrypted: "/core/img/filetypes/folder-encrypted.svg", + File: "/core/img/filetypes/file.svg", + Text: "/core/img/filetypes/text.svg", + Image: "/core/img/filetypes/image.svg", + Audio: "/core/img/filetypes/audio.svg", + Video: "/core/img/filetypes/video.svg", + PDF: "/core/img/filetypes/application-pdf.svg", + Document: "/core/img/filetypes/x-office-document.svg", + Spreadsheet: "/core/img/filetypes/x-office-spreadsheet.svg", + Presentation: "/core/img/filetypes/x-office-presentation.svg", + Files: "/core/img/places/files.svg", + Home: "/core/img/places/home.svg", + Calendar: "/core/img/places/calendar.svg", + Contacts: "/core/img/places/contacts.svg", + Add: "/core/img/actions/add.svg", + Delete: "/core/img/actions/delete.svg", + Edit: "/core/img/actions/edit.svg", + Rename: "/core/img/actions/rename.svg", + Download: "/core/img/actions/download.svg", + Upload: "/core/img/actions/upload.svg", + Share: "/core/img/actions/share.svg", + Search: "/core/img/actions/search.svg", + Settings: "/core/img/actions/settings.svg", + Info: "/core/img/actions/info.svg", + History: "/core/img/actions/history.svg", + Password: "/core/img/actions/password.svg", + Confirm: "/core/img/actions/confirm.svg", + Close: "/core/img/actions/close.svg", + Star: "/core/img/actions/star.svg", + User: "/core/img/actions/user.svg", + Group: "/core/img/actions/group.svg", + Mail: "/core/img/actions/mail.svg", + Menu: "/core/img/actions/menu.svg", + External: "/core/img/actions/external.svg", + Filter: "/core/img/actions/filter.svg", + Recent: "/core/img/actions/recent.svg", + Tag: "/core/img/actions/tag.svg", +} + +// nextcloudIcons is the named set of core SVGs. Fields are paths, not image bytes. +type nextcloudIcons struct { + Folder string + FolderShared string + FolderPublic string + FolderStarred string + FolderEncrypted string + File string + Text string + Image string + Audio string + Video string + PDF string + Document string + Spreadsheet string + Presentation string + Files string + Home string + Calendar string + Contacts string + Add string + Delete string + Edit string + Rename string + Download string + Upload string + Share string + Search string + Settings string + Info string + History string + Password string + Confirm string + Close string + Star string + User string + Group string + Mail string + Menu string + External string + Filter string + Recent string + Tag string +} diff --git a/navigation.go b/navigation.go index f37c0f6..236fd1a 100644 --- a/navigation.go +++ b/navigation.go @@ -7,13 +7,15 @@ import ( // shellCSP lets the ExApp iframe render this document. AppAPI blanks a frame // whose response omits frame-ancestors. -const shellCSP = "default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; connect-src 'self'; img-src 'self' data:" +const shellCSP = "default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; connect-src 'self'; img-src 'self' data:; font-src 'self' data:" // Item is one App navigation entry. The ExApp chooses the ids and labels. // Children may nest. The shell does not interpret the ids. +// Icon is an optional same-origin image URL. NextcloudIcons names the core SVGs. type Item struct { ID string Label string + Icon string Children []Item } @@ -31,6 +33,9 @@ type AppNavigation struct { SelectKey string // Title is the document title. Empty means "App navigation". Title string + // DisableTheme skips the Nextcloud theme stylesheets. The zero value + // loads the active theme. + DisableTheme bool } // Handler serves the shell. The selected item is the SelectKey query parameter. @@ -99,6 +104,7 @@ func (n AppNavigation) serve(w http.ResponseWriter, r *http.Request) { Page: template.HTML(page), Missing: msg, Title: title, + Theme: !n.DisableTheme, }) } @@ -128,11 +134,13 @@ type shellView struct { Page template.HTML Missing string Title string + Theme bool } type itemView struct { ID string Label string + Icon string Href string Current bool HasChildren bool @@ -146,6 +154,7 @@ func viewItems(r *http.Request, items []Item, selected string, href func(*http.R out = append(out, itemView{ ID: it.ID, Label: it.Label, + Icon: it.Icon, Href: href(r, it.ID), Current: it.ID == selected, HasChildren: len(children) > 0, @@ -160,18 +169,128 @@ var shellTmpl = template.Must(template.New("shell").Parse(` {{.Title}} +{{if .Theme}} + + + + +{{end}} @@ -179,8 +298,8 @@ var shellTmpl = template.Must(template.New("shell").Parse(`
-