Files
datascape/pagesettings.go
T
2026-05-29 09:21:19 +02:00

90 lines
2.6 KiB
Go

package main
import (
"net/http"
"os"
"path/filepath"
"strings"
)
// handleSettings persists the listing view/sort/order to the folder's
// .page-settings file. Values are validated against the allowed sets (unknown
// values fall back to defaults). Triggered by POST /{path}?settings.
func (h *handler) handleSettings(w http.ResponseWriter, r *http.Request, urlPath, fsPath string) {
if err := r.ParseForm(); err != nil {
http.Error(w, "bad request", http.StatusBadRequest)
return
}
view := validateView(r.FormValue("view"))
sortKey := validateSort(r.FormValue("sort"))
order := validateOrder(r.FormValue("order"))
if err := writePageSettings(fsPath, view, sortKey, order); err != nil {
http.Error(w, "write failed: "+err.Error(), http.StatusInternalServerError)
return
}
http.Redirect(w, r, urlPath, http.StatusSeeOther)
}
// writePageSettings performs a read-modify-write of <dir>/.page-settings,
// updating the view/sort/order lines while preserving every other line
// (other keys, comments, blank lines, ordering) verbatim. Missing keys are
// appended. The write is atomic (temp file + rename).
func writePageSettings(dir, view, sortKey, order string) error {
if err := os.MkdirAll(dir, 0755); err != nil {
return err
}
p := filepath.Join(dir, ".page-settings")
existing, err := os.ReadFile(p)
if err != nil && !os.IsNotExist(err) {
return err
}
updated := updateSettingsLines(existing, view, sortKey, order)
return writeFileAtomic(p, updated, 0644)
}
// updateSettingsLines rewrites the view/sort/order lines in existing while
// leaving all other lines untouched. Every occurrence of a known key is
// updated (so the reader's last-wins parse stays consistent); keys absent from
// the file are appended in a stable order. The result always ends in a newline.
func updateSettingsLines(existing []byte, view, sortKey, order string) []byte {
targets := map[string]string{"view": view, "sort": sortKey, "order": order}
appendOrder := []string{"view", "sort", "order"}
seen := map[string]bool{}
var lines []string
if len(existing) > 0 {
s := string(existing)
s = strings.TrimSuffix(s, "\n")
lines = strings.Split(s, "\n")
}
for i, line := range lines {
trimmed := strings.TrimSpace(line)
if trimmed == "" || strings.HasPrefix(trimmed, "#") {
continue
}
eq := strings.IndexByte(line, '=')
if eq < 0 {
continue
}
key := strings.TrimSpace(line[:eq])
if val, ok := targets[key]; ok {
lines[i] = key + " = " + val
seen[key] = true
}
}
for _, k := range appendOrder {
if !seen[k] {
lines = append(lines, k+" = "+targets[k])
}
}
out := strings.Join(lines, "\n")
if out != "" {
out += "\n"
}
return []byte(out)
}