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())
}