From: Paul Hänsch Date: Sat, 24 Jul 2021 20:08:41 +0000 (+0200) Subject: Merge commit '752db311013e53c3c16f685f86b507e191b2239c' X-Git-Url: https://git.plutz.net/?a=commitdiff_plain;h=57b8376ada25742ac4d1078997bdeeeb20c56911;hp=752db311013e53c3c16f685f86b507e191b2239c;p=webpoll Merge commit '752db311013e53c3c16f685f86b507e191b2239c' --- diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..24781a9 --- /dev/null +++ b/Makefile @@ -0,0 +1,9 @@ +.PHONY: _subtrees + +_subtrees: _cgilite + +cgilite: + git subtree add --squash -P $@ https://git.plutz.net/git/$@ master + +_cgilite: cgilite + git subtree pull --squash -P $< https://git.plutz.net/git/$< master diff --git a/cgilite.sh b/cgilite/cgilite.sh similarity index 100% rename from cgilite.sh rename to cgilite/cgilite.sh diff --git a/common.css b/cgilite/common.css similarity index 100% rename from common.css rename to cgilite/common.css diff --git a/file.sh b/cgilite/file.sh similarity index 100% rename from file.sh rename to cgilite/file.sh diff --git a/html-sh.sed b/cgilite/html-sh.sed similarity index 100% rename from html-sh.sed rename to cgilite/html-sh.sed diff --git a/logging.sh b/cgilite/logging.sh similarity index 100% rename from logging.sh rename to cgilite/logging.sh diff --git a/markdown.awk b/cgilite/markdown.awk similarity index 100% rename from markdown.awk rename to cgilite/markdown.awk diff --git a/session.sh b/cgilite/session.sh similarity index 100% rename from session.sh rename to cgilite/session.sh diff --git a/storage.sh b/cgilite/storage.sh similarity index 100% rename from storage.sh rename to cgilite/storage.sh diff --git a/index.cgi b/index.cgi new file mode 100755 index 0000000..d1469cd --- /dev/null +++ b/index.cgi @@ -0,0 +1,139 @@ +#!/bin/sh + +_EXEC="${_EXEC:-${0%/*}/}" +_DATA="${_DATA:-.}" +_BASE="${_BASE%/}" + +. "$_EXEC"/cgilite/cgilite.sh +. "$_EXEC"/cgilite/session.sh +. "$_EXEC"/cgilite/file.sh +. "$_EXEC"/cgilite/storage.sh +#. "$_EXEC"/session_lock.sh +. "$_EXEC"/widgets.sh + +PATH_INFO="$(PATH "/${PATH_INFO#${_BASE}}")" + +#git init "$_DATA" >/dev/null & + +yield_page(){ + title="${1:-Webpoll}" page="$2" + printf '%s\r\n' 'Content-Type: text/html; charset=utf-8' \ + "Content-Security-Policy: script-src 'none'" \ + '' + { printf '[html + [head + [meta name="viewport" content="width=device-width"] + [link rel="stylesheet" type="text/css" href="%s/common.css"] + [link rel="stylesheet" type="text/css" href="%s/widgets.css"] + [link rel="stylesheet" type="text/css" href="%s/webpoll.css"] + [title %s] + ] [body class="%s" + ' "$_BASE" "$_BASE" "$_BASE" "$title" "$page" + cat + printf '] ]' + } |"$_EXEC/cgilite/html-sh.sed" -u +} + +pagename() { + local id="$1" + local file="$_DATA/$id" + if [ -f "$file" ]; then + DBM "$file" get title || printf 'Unnamed Page' + else + return 1; + fi +} + +[ "$REQUEST_METHOD" = POST ] && case ${PATH_INFO} in + /) + case $(POST start) in + date) + id="$(randomid)" + touch "$_DATA/$id" + REDIRECT "$_BASE/$id/newdate" + ;; + options) + id="$(randomid)" + touch "$_DATA/$id" + REDIRECT "$_BASE/$id/newoptions" + ;; + *) REDIRECT "$_BASE/";; + esac + ;; + /*/newdate) + id="${PATH_INFO%/newdate}"; id="${id#/}" + file="$_DATA/$id" + month="$(POST month |grep -m1 -xE '[0-9]{4}-(0[1-9]|1[012])')" + DBM "$file" set title "$(POST title)" + DBM "$file" set description "$(POST description)" + DBM "$file" set dates "$( + for date in $(seq 1 $(POST_COUNT date)); do + POST date "$date" + printf \\n + done |sort + )" + if [ "$(POST cancel)" = cancel ]; then + rm -- "$file" + REDIRECT "$_BASE/" + elif [ "$month" ]; then + REDIRECT "$_BASE$PATH_INFO?month=$month" + else + REDIRECT "$_BASE$PATH_INFO" + fi + ;; +esac + + +case ${PATH_INFO} in + /favicon.ico) printf '%s\r\n' 'Content-Length: 0' '';; + /common.css) FILE "$_EXEC/cgilite/common.css";; + /widgets.css|/webpoll.css) FILE "${_EXEC}/${PATH_INFO}";; + /) recent="$(COOKIE pages)" + yield_page "Start a Poll" "home" <<-EOF + [form method=post + [submit "start" "date" Start a new poll] + $(if [ "$recent" ]; then + printf '[h2 Recent Polls][ul .recent' + for page in $recent; do + [ -f "$_DATA/$(printf %s "$page" |checkid)" ] \ + && printf '[li [a href="./%s" . %s]]' "$page" "$(pagename "$page" |HTML)" + done + printf ']' + fi) + ] + EOF + ;; + /*/newdate) + id="${PATH_INFO%/newdate}"; id="${id#/}" + file="$_DATA/$id" + month="$(GET month |grep -m1 -xE '[0-9]{4}-(0[1-9]|1[012])' || date +%Y-%m)" + Y="${month%-*}"; m="${month#*-}"; Y=${Y#0}; m=${m#0}; + [ "$m" = 1 ] && prev=$(printf '%04i-%02i' $((Y - 1)) 12) || prev=$(printf '%04i-%02i' $Y $((m - 1))) + [ "$m" = 12 ] && next=$(printf '%04i-%02i' $((Y + 1)) 01) || next=$(printf '%04i-%02i' $Y $((m + 1))) + dates="$(DBM "$file" get dates)" + days="$(printf %s "$dates" |sed -E "/^${month}-/!d; s;^.*-([0-9]{2})$;\1;g")" + additional="$(printf %s "$dates" |sed -E "/^${month}-/d;")" + yield_page "$(pagename "$id")" "newdate" <<-EOF + [form method=post + [input name=title value="$(DBM "$file" get title |HTML)" placeholder="Title"] + [textarea name=description placeholder="Description" . $(DBM "$file" get description |HTML)] + [submit "month" "$prev" Previous Month] + $(w_month multiple date "$month" $days) + [submit "month" "$next" Next Month] + $(printf '[hidden "date" "%s"]' $additional) + [submit "cancel" "cancel" Cancel] + [submit "post" "post" Post Event] + ] + EOF + ;; + /*/newoptions);; + *);; + /) yield_page <<-EOF + $(w_month none date 2019-12 24 25 26) + $(w_month select date 2020-01) + $(w_month multiple date 2020-02) + EOF + return 0 + ;; +esac + diff --git a/webpoll.css b/webpoll.css new file mode 100644 index 0000000..b7d845a --- /dev/null +++ b/webpoll.css @@ -0,0 +1,47 @@ +body.home form { + position: fixed; + left: 50%; top: 50%; + transform: translate(-50%, -50%); +} + +body.newdate form { + text-align: center; + margin: auto; + max-width: 24em; +} + +body.newdate form input[name=title], +body.newdate form textarea[name=description] { + display: block; + width: 100%; + margin-bottom: .75em; +} +body.newdate form textarea[name=description] { + height: 8em; +} + +body.newdate form button[name=month] { + display: inline-block; + width: calc(50% - 9em); + vertical-align: middle; + color: transparent; + overflow: hidden; + height: 4em; + border: none; +} +body.newdate form textarea + button[name=month]:before, +body.newdate form table + button[name=month]:before { + display: block; + content: '<'; + font-size: 2em; + font-weight: bold; + margin-top: .375em; + color: #666; +} +body.newdate form table + button[name=month]:before { + content: '>'; +} + +body.newdate form table.calendar { + vertical-align: middle; +} diff --git a/widgets.css b/widgets.css new file mode 100644 index 0000000..bb48f52 --- /dev/null +++ b/widgets.css @@ -0,0 +1,52 @@ +table.calendar { + display: inline-block; + border-collapse: collapse; + vertical-align: top; +} +table.calendar td { + border: 1pt solid; +} + +table.calendar thead tr.monthname { + border-style: solid; + border-width: 1pt 1pt 1pt 1pt; +} +table.calendar thead tr.weekday th:first-of-type { + border-style: solid; + border-width: 0pt 0pt 0pt 1pt; +} +table.calendar thead tr.weekday th:last-child { + border-right: 1pt solid; +} +table.calendar tbody tr th { + border-left: 1pt solid #000; +} +table.calendar tbody tr:last-child th { + border-bottom: 1pt solid #000; +} + +table.calendar tbody tr th.weekno { + width: 2em; + padding: 0 .25em; + text-align: right; + font-weight: normal; + color: #888; +} + +table.calendar input[type=radio], +table.calendar input[type=checkbox] { + display: none; +} +table.calendar td label { + display: inline-block; + width: 2em; + margin: 0; padding: .25em; + text-align: right; + line-height: 1em; +} +table.calendar td input:checked + label, +table.calendar td label[checked] { + font-weight: bold; + line-height: .75em; + border: .125em solid; +} diff --git a/widgets.sh b/widgets.sh new file mode 100755 index 0000000..5e7132e --- /dev/null +++ b/widgets.sh @@ -0,0 +1,96 @@ +#!/bin/sh + +checked(){ + local check="$1"; shift 1; + for comp in "$@"; do + if [ "$check" = "$comp" ] || [ "$check" -eq "$comp" ]; then + printf 'checked="checked"' + break; + fi 2>/dev/null + done +} +selected(){ + local check="$1"; shift 1; + for comp in "$@"; do + if [ "$check" = "$comp" ] || [ "$check" -eq "$comp" ]; then + printf 'selected="selected"' + break; + fi 2>/dev/null + done +} + +w_month() { + # Arguments: + # 1. (optional) select, multiple, none - default: select + # 2. (optional) Name of form field - default: "date" + # 3. (optional) Month to display in format: YYYY-MM - default: current month + # 4. (optional, multiple) Days to preselect in format: DD - default: none + + local type="${1:-select}" input="${2:-date}" month="$3" + shift 3; local selected="$*" + local dow dom days n=1 Y m d V w B + if [ $month ]; then + read Y m d V w B<<-EOF + $(date -d "${month}-01" +"%_Y %_m %_d %_V %w %B") + EOF + else + read Y m d V w <<-EOF + $(date +"%Y %m %d %V %w") + EOF + month="$Y-$m" + V="$((V - d / 7))" + [ $V -lt 1 ] && V=$((V + 53)) + fi + + case $m in + [13578]|10|12) + days="1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31";; + [469]|11) + days="1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30";; + 2) if [ $(( Y / 400 )) = 0 ]; then + days="1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29"; + elif [ $(( Y / 100 )) = 0 ]; then + days="1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28"; + elif [ $(( Y / 4 )) = 0 ]; then + days="1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29"; + else + days="1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28"; + fi;; + esac + + printf '[table .calendar month=%s [thead + [tr .monthname [th colspan=8 . %s]] + [tr .weekday [th][th . %s][th . %s][th . %s][th . %s][th . %s][th . %s][th . %s]] + ][tbody + ' "$month" "$B $Y" Mo Tu We Th Fr Sa Su + for dom in $days; do + dow=$(( ( w - d + 35 + dom ) % 7)) + [ $dow = 1 -o $dom = 1 ] && printf '[tr [th .weekno . %i]' $V + [ $dom = 1 ] && while [ $n -lt $(( ($dow + 6) % 7 + 1)) ]; do printf '[td ]'; n=$((n + 1)); done + date="$(printf "%04i-%02i-%02i" $Y $m $dom)" + case $type in + none) + printf '[td [label %s . %i]]' \ + "$(checked $dom $selected)" "$dom" + ;; + multiple) + printf '[td [input type=checkbox id="%s_%s" name="%s" value="%s" %s][label for="%s_%s" . %i]]' \ + "$input" "$date" "$input" "$date" "$(checked $dom $selected)" "$input" "$date" "$dom" + ;; + select|*) + printf '[td [input type=radio id="%s_%s" name="%s" value="%s" %s][label for="%s_%s" . %i]]' \ + "$input" "$date" "$input" "$date" "$(checked $dom $selected)" "$input" "$date" "$dom" + ;; + esac + if [ $dow = 0 ]; then + printf ']\n' + V=$((V + 1)) + [ $m = 1 -a $V -ge 53 ] && V=1 + fi + done + if [ $dow -gt 0 ]; then + while [ $dow -le 6 ]; do printf '[td ]'; dow=$((dow + 1)) ; done + printf ']\n' + fi + printf ']]' +}