Folder Sidebar
This commit is contained in:
@@ -4,6 +4,7 @@ import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
@@ -11,6 +12,9 @@ import (
|
||||
type treeEntry struct {
|
||||
Name string `json:"name"`
|
||||
Kind string `json:"kind"`
|
||||
// Children is populated only along the expandTo chain (see handleTree);
|
||||
// omitted otherwise so the flat picker listing keeps its original shape.
|
||||
Children []treeEntry `json:"children,omitempty"`
|
||||
}
|
||||
|
||||
type treeResponse struct {
|
||||
@@ -42,6 +46,14 @@ func (h *handler) handleTree(w http.ResponseWriter, r *http.Request, urlPath, fs
|
||||
return
|
||||
}
|
||||
|
||||
// expandTo asks for a nested listing: each folder along the ancestor chain
|
||||
// carries its own children, recursively, down to the target. Used by the
|
||||
// tree sidebar to render the current page's chain in one request. The flat
|
||||
// picker omits expandTo and is unaffected.
|
||||
if expandTo := r.URL.Query().Get("expandTo"); expandTo != "" {
|
||||
expandTreeChain(fsPath, entries, treePathSegments(expandTo))
|
||||
}
|
||||
|
||||
resp := treeResponse{Path: canonicalTreePath(urlPath), Entries: entries}
|
||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
_ = json.NewEncoder(w).Encode(resp)
|
||||
@@ -83,3 +95,39 @@ func listTreeEntries(fsPath string) ([]treeEntry, error) {
|
||||
sort.Slice(files, func(i, j int) bool { return files[i].Name < files[j].Name })
|
||||
return append(folders, files...), nil
|
||||
}
|
||||
|
||||
// treePathSegments splits a wiki path into its non-empty segments.
|
||||
func treePathSegments(p string) []string {
|
||||
var segs []string
|
||||
for _, s := range strings.Split(p, "/") {
|
||||
if s != "" {
|
||||
segs = append(segs, s)
|
||||
}
|
||||
}
|
||||
return segs
|
||||
}
|
||||
|
||||
// expandTreeChain walks segs, matching each against a folder in entries by
|
||||
// name, loading that folder's children in place, and recursing. The walk stops
|
||||
// at the deepest matching segment, so a stale or deleted path simply expands as
|
||||
// far as it still exists. Segments only ever match real directory names from
|
||||
// listTreeEntries (no "." or ".." entries), so this cannot traverse outside the
|
||||
// listed tree.
|
||||
func expandTreeChain(fsPath string, entries []treeEntry, segs []string) {
|
||||
if len(segs) == 0 {
|
||||
return
|
||||
}
|
||||
for i := range entries {
|
||||
if entries[i].Kind != "folder" || entries[i].Name != segs[0] {
|
||||
continue
|
||||
}
|
||||
childFs := filepath.Join(fsPath, segs[0])
|
||||
kids, err := listTreeEntries(childFs)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
entries[i].Children = kids
|
||||
expandTreeChain(childFs, entries[i].Children, segs[1:])
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user