117 lines
3.3 KiB
Go
117 lines
3.3 KiB
Go
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"html/template"
|
|
"log"
|
|
"net/http"
|
|
"os"
|
|
"path/filepath"
|
|
"sort"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
func init() {
|
|
pageTypeHandlers = append(pageTypeHandlers, &calendarsHandler{})
|
|
}
|
|
|
|
// calendarsHandler renders folders whose .page-settings declares
|
|
// type = calendars: the maintenance table and the sync status. Markdown
|
|
// content and the listing stay visible, as in the fitness folder.
|
|
type calendarsHandler struct{}
|
|
|
|
func (c *calendarsHandler) redirect(root, fsPath, urlPath string, r *http.Request) (string, bool) {
|
|
return "", false
|
|
}
|
|
|
|
func (c *calendarsHandler) handle(root, fsPath, urlPath string, r *http.Request) *specialPage {
|
|
s := readPageSettings(fsPath)
|
|
if s == nil || s.Type != "calendars" {
|
|
return nil
|
|
}
|
|
return &specialPage{Content: renderCalendarsPage(fsPath), SuppressTOC: true}
|
|
}
|
|
|
|
type calFileRow struct {
|
|
Name string
|
|
Roles string
|
|
Events string
|
|
Updated string // file modification time
|
|
Error string // last sync or parse error
|
|
}
|
|
|
|
type calPageData struct {
|
|
HasMaintenance bool
|
|
Maintenance []maintSeriesRow
|
|
Calendars []calFileRow
|
|
// Sync is only shown for the configured calendars folder.
|
|
IsSyncDir bool
|
|
SyncEnabled bool
|
|
LastRun string
|
|
LastError string
|
|
}
|
|
|
|
var calPageTmpl = template.Must(template.ParseFS(assets, "assets/calendar/page.html"))
|
|
|
|
func renderCalendarsPage(dir string) template.HTML {
|
|
s := readCalendarSettings(dir)
|
|
data := calPageData{
|
|
HasMaintenance: len(s.Maintenance) > 0,
|
|
Maintenance: maintenanceSeries(dir, s.Maintenance, time.Now()),
|
|
IsSyncDir: calendarsDir != "" && dir == calendarsDir,
|
|
SyncEnabled: s.CaldavURL != "",
|
|
}
|
|
|
|
var results map[string]calSyncResult
|
|
if data.IsSyncDir {
|
|
var lastRun time.Time
|
|
lastRun, data.LastError, results = calSyncSnapshot()
|
|
if !lastRun.IsZero() {
|
|
data.LastRun = lastRun.Format("2006-01-02 15:04")
|
|
}
|
|
}
|
|
|
|
roles := map[string][]string{}
|
|
for role, names := range map[string][]string{"diary": s.Diary, "weight": s.Weight, "maintenance": s.Maintenance} {
|
|
for _, n := range names {
|
|
roles[n] = append(roles[n], role)
|
|
}
|
|
}
|
|
entries, _ := os.ReadDir(dir)
|
|
for _, e := range entries {
|
|
name := e.Name()
|
|
if e.IsDir() || strings.HasPrefix(name, ".") || !strings.EqualFold(filepath.Ext(name), ".ics") {
|
|
continue
|
|
}
|
|
name = strings.TrimSuffix(name, filepath.Ext(name))
|
|
sort.Strings(roles[name])
|
|
row := calFileRow{Name: name, Roles: strings.Join(roles[name], ", "), Error: results[name].Error}
|
|
if info, err := e.Info(); err == nil {
|
|
row.Updated = info.ModTime().Format("2006-01-02 15:04")
|
|
}
|
|
if f := loadCalendar(filepath.Join(dir, e.Name())); f != nil {
|
|
row.Events = strconv.Itoa(f.events)
|
|
} else if row.Error == "" {
|
|
row.Error = "cannot parse file"
|
|
}
|
|
data.Calendars = append(data.Calendars, row)
|
|
delete(results, name)
|
|
}
|
|
// Calendars whose download failed before a file ever existed.
|
|
for name, r := range results {
|
|
if r.Error != "" {
|
|
data.Calendars = append(data.Calendars, calFileRow{Name: name, Error: r.Error})
|
|
}
|
|
}
|
|
sort.SliceStable(data.Calendars, func(i, j int) bool { return data.Calendars[i].Name < data.Calendars[j].Name })
|
|
|
|
var buf bytes.Buffer
|
|
if err := calPageTmpl.Execute(&buf, data); err != nil {
|
|
log.Printf("calendars page template: %v", err)
|
|
return ""
|
|
}
|
|
return template.HTML(buf.String())
|
|
}
|