Artifact b07f90a00cce4df9d614ed389c5ddbd9e8f118496e53e7446f122cd1070f2d9b:
- File
internal/api/poll.go
— part of check-in
[e674e718a8]
at
2026-08-21 19:34:28
on branch quarry-forum
— forum polls: fenced-block definition, OLTP ballots, no-script widget
The block syncs as markdown (fossil peers render it readably); option range and close date re-read from the body at cast time, never the form. (user: dmestas, size: 2132) [annotate] [blame] [check-ins using] [more...]
// poll.go parses the ```poll fenced block — quarry's poll convention. // The DEFINITION lives in the forum post body, so it syncs like any // artifact and a fossil peer renders it as readable markdown; only the // ballots (OLTP) are forge-local. Full compatibility BY degradation. package api import ( "strings" "time" ) type pollDef struct { Question string Options []string Closes string // YYYY-MM-DD (UTC); "" never closes } // parsePoll extracts the first ```poll block. rest is the body with the // block removed (quarry renders the widget instead); def is nil when no // well-formed poll (a question and at least two options) is present — // then the body renders untouched, exactly as a fossil peer would. func parsePoll(body string) (def *pollDef, rest string) { lines := strings.Split(body, "\n") start := -1 for i, l := range lines { if strings.TrimSpace(l) == "```poll" { start = i break } } if start < 0 { return nil, body } end := -1 for i := start + 1; i < len(lines); i++ { if strings.TrimSpace(lines[i]) == "```" { end = i break } } if end < 0 { return nil, body } d := &pollDef{} for _, l := range lines[start+1 : end] { l = strings.TrimSpace(l) switch { case strings.HasPrefix(l, "question:"): d.Question = strings.TrimSpace(strings.TrimPrefix(l, "question:")) case strings.HasPrefix(l, "closes:"): d.Closes = strings.TrimSpace(strings.TrimPrefix(l, "closes:")) case strings.HasPrefix(l, "- "): d.Options = append(d.Options, strings.TrimSpace(strings.TrimPrefix(l, "- "))) } } if d.Question == "" || len(d.Options) < 2 { return nil, body } return d, strings.Join(append(append([]string{}, lines[:start]...), lines[end+1:]...), "\n") } // closed reports whether the poll has passed its close DATE: ballots // are accepted through the named day (UTC), frozen after it. func (d *pollDef) closed(now time.Time) bool { if d.Closes == "" { return false } t, err := time.Parse("2006-01-02", d.Closes) if err != nil { return false // an unreadable date never silently freezes a poll } return now.UTC().After(t.AddDate(0, 0, 1)) }