From 258959637ea2f9499c8807daf21d7e1057c4ad14 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Paul=20H=C3=A4nsch?= Date: Tue, 27 Jul 2021 00:52:14 +0200 Subject: [PATCH] added polling page --- index.cgi | 12 +++- newdate.sh | 2 + poll.sh | 160 ++++++++++++++++++++++++++++++++++++++++++++++++++++ webpoll.css | 70 +++++++++++++++++++++++ 4 files changed, 242 insertions(+), 2 deletions(-) create mode 100644 poll.sh diff --git a/index.cgi b/index.cgi index 9037c33..0694c6e 100755 --- a/index.cgi +++ b/index.cgi @@ -11,6 +11,13 @@ _BASE="${_BASE%/}" #. "$_EXEC"/session_lock.sh . "$_EXEC"/widgets.sh +export MD_HTML="false" +if [ "$(which awk)" ]; then + markdown() { awk -f "$_EXEC/cgilite/markdown.awk"; } +else + markdown() { busybox awk -f "$_EXEC/cgilite/markdown.awk"; } +fi + PATH_INFO="$(PATH "/${PATH_INFO#${_BASE}}")" #git init "$_DATA" >/dev/null & @@ -28,7 +35,7 @@ yield_page(){ [link rel="stylesheet" type="text/css" href="%s/webpoll.css"] [title %s] ] [body class="%s" - ' "$_BASE" "$_BASE" "$_BASE" "$title" "$page" + ' "$_BASE" "$_BASE" "$_BASE" "$(HTML "$title")" "$page" cat printf '] ]' } |"$_EXEC/cgilite/html-sh.sed" -u @@ -77,6 +84,7 @@ page_home() { fi } page_newdate() { . "$_EXEC"/newdate.sh; } +page_poll() { . "$_EXEC/poll.sh"; } case ${PATH_INFO} in /favicon.ico) printf '%s\r\n' 'Content-Length: 0' '';; @@ -85,7 +93,7 @@ case ${PATH_INFO} in /) page_home;; /*/newdate) page_newdate;; /*/newoptions);; - *);; + /[0-9a-zA-Z:=]???????????????) page_poll;; esac exit 0 diff --git a/newdate.sh b/newdate.sh index b71bdef..b2e35a6 100755 --- a/newdate.sh +++ b/newdate.sh @@ -116,6 +116,8 @@ if [ "$REQUEST_METHOD" = POST ]; then if [ "$(POST cancel)" = cancel ]; then rm -- "$file" REDIRECT "$_BASE/" + elif [ "$(POST post)" = post ]; then + REDIRECT "$_BASE${PATH_INFO%/*}" else REDIRECT "$_BASE$PATH_INFO${month:+?month=}${month}" fi diff --git a/poll.sh b/poll.sh new file mode 100644 index 0000000..74fa3e1 --- /dev/null +++ b/poll.sh @@ -0,0 +1,160 @@ +#!/bin/sh + +id="$(checkid "${PATH_INFO#/}")" +file="${_DATA}/${id}" + +#cancel if poll is invalid +[ "$id" -a -f "$file" ] || REDIRECT "$_BASE/" + +tkey() { + # convert time stamps for use in POST keys + local str="$1" out + while [ "$str" ]; do + case $str in + :*) out="${out}.";; + *) out="${out}${str%"${str#?}"}";; + esac + str="${str#?}" + done + printf %s "$out" +} + +timelist() { + local dates todall splittimes + local date tod todsplit + + if [ "$splittimes" = no -a "$dates" -a "$todall" ]; then + for date in $dates; do for tod in $todall; do + printf %s\\n "${date}_${tod%-}" + done ;done + + elif [ "$splittimes" = no -a "$dates" ]; then + for date in $dates; do + printf %s\\n "${date}" + done + + elif [ "$splittimes" = no -a "$todall" ]; then + for tod in $todall; do + printf %s\\n "${tod%-}" + done + + elif [ "$splittimes" = yes ]; then + for date in $dates; do + todsplit="$(DBM "$file" get "tod_$date")" + [ "$todsplit" ] \ + && for tod in $todsplit; do printf %s\\n "${date}_${tod%-}"; done \ + || printf %s\\n "${date}" + done + + else + return 1 + + fi +} + +table_poll() { + local splittimes="$(DBM "$file" get splittimes || printf no)" + local dates="$(DBM "$file" get dates)" + local todall="$(DBM "$file" get todall)" + local timelist="$(timelist)" + local time date span name + + [ "$timelist" ] || return 1 + + printf '[table .poll [thead\n' + # date header + if [ "$dates" ]; then + printf '[tr .dates [th]' + for date in $dates; do + span=0; for time in $timelist; do case $time in + ${date}*) span=$((span + 1));; + esac; done + date -d "$date" +"[th colspan=\"${span}\" . %A
%B %_d, %Y]"; + done + printf '[th]]\n' + fi + + # tod header + if [ "$splittimes" = yes -o "$todall" ]; then + printf '[tr .tod [th]' + for time in $timelist; do + [ "${time#*_}" = "${time}" ] && time="${time}_" + printf '[th . %s]' "${time#*_}" + done + printf '[th]]\n' + fi + + printf '][tbody\n' + + { DBM "$file" get participants; printf \\n; } |while read -r name; do + yes="$(DBM "$file" get "reply_yes_${name}")" + no="$(DBM "$file" get "reply_no_${name}")" + maybe="$(DBM "$file" get "reply_maybe_${name}")" + + printf '[tr [th .name . %s]' "$(HTML "$name")" + for time in $timelist; do + printf %s "$yes" |grep -qwF "$time" && printf '[td .yes Yes]' && continue + printf %s "$no" |grep -qwF "$time" && printf '[td .no No]' && continue + printf %s "$maybe" |grep -qwF "$time" && printf '[td .maybe Maybe]' && continue + printf '[td .missing . ?]' + done + printf '[td]]' + done + + # Submit line + printf '[tr .new [td [input name="name" value="" placeholder="Your Name" autocomplete=off]]' + for time in $timelist; do + time="$(tkey "$time")" + printf '[td [radio "%s" "yes" #yes_%s][label for="yes_%s" Yes] + [radio "%s" "no" #no_%s][label for="no_%s" No] + [radio "%s" "maybe" #maybe_%s][label for="maybe_%s" Maybe] + ]' "${time}" "${time}" "${time}" \ + "${time}" "${time}" "${time}" \ + "${time}" "${time}" "${time}" + done + printf '[td [submit "new" "new" Submit]]]\n' + + printf ']]' +} + +if [ "$REQUEST_METHOD" = POST ]; then + local name="$(POST name |grep -m 1 -xE '.*[^ ].*')" + local splittimes="$(DBM "$file" get splittimes || printf no)" + local dates="$(DBM "$file" get dates)" + local todall="$(DBM "$file" get todall)" + local timelist="$(timelist)" + local time yes no maybe reply + + if [ "$(POST new)" = new ]; then + if [ ! "$name" ]; then + REDIRECT "${_BASE}${PATH_INFO}#ERROR_NONAME" + elif DBM "$file" get participants |grep -qxF "$name"; then + REDIRECT "${_BASE}${PATH_INFO}#ERROR_NAMEEXISTS" + fi + DBM "$file" append participants "${BR}${name}" || DBM "$file" insert participants "${name}" \ + || REDIRECT "${_BASE}${PATH_INFO}#ERROR_DBACCESS" + + for time in $timelist; do reply="$(POST "$(tkey "$time")")"; case $reply in + yes) yes="${yes}${yes:+ }${time}";; + no) no="${no}${no:+ }${time}";; + maybe) maybe="${maybe}${maybe:+ }${time}";; + esac; done + DBM "$file" set "reply_yes_${name}" "$yes" + DBM "$file" set "reply_no_${name}" "$no" + DBM "$file" set "reply_maybe_${name}" "$maybe" + REDIRECT "${_BASE}${PATH_INFO}" + fi + +else + pagename="$(pagename "$id")" + + yield_page "$pagename" poll <<-EOF + [form method=POST + [section .description + [h1 .title $(HTML "$pagename")] + $(DBM "$file" get description |markdown) + ] + $(table_poll || printf '[p Poll parameters are invalid]') + ] + EOF +fi diff --git a/webpoll.css b/webpoll.css index fafb9f4..cfc363b 100644 --- a/webpoll.css +++ b/webpoll.css @@ -18,6 +18,76 @@ body.home form { transform: translate(-50%, -50%); } +body.poll form { + text-align: center; + max-width: 95%; +} +body.poll .description { + text-align: left; + max-width: 50em; + padding: 1pt 1em 1em 1em; + margin: auto; + margin-bottom: 1em; + background-color: rgba(255,255,255,.5); +} +body.poll .description .title { + text-align: center; +} +body.poll table { + background-color: rgba(255,255,255,.5); + border-collapse: collapse; + margin: auto; + -border: .5pt solid; + box-shadow: #000 .25em .25em .5em; + border-radius: 2pt; +} +body.poll table thead tr.dates th { + padding: .25em; +} +body.poll table thead tr.tod th { + border-width: .5pt; + border-style: none solid none solid; + padding: .25em; +} +body.poll table tbody tr td { + text-align: center; + border: .5pt solid; + padding: 0 .25em; +} +body.poll table tbody tr td:first-child, +body.poll table tbody tr td:last-child, +body.poll table thead tr th:first-child, +body.poll table thead tr th:last-child { border: none; } + +body.poll table tbody tr th.name { padding: .25em .5em; text-align: right; } +body.poll table tbody tr td.yes { background-color: #AFA; } +body.poll table tbody tr td.no { background-color: #FAA; } +body.poll table tbody tr td.maybe { background-color: #FFA; } + +body.poll table td input[type=radio] { display: none; } +body.poll table td input[type=radio] + label { + font-size: .875em; + text-decoration: underline; + color: #066; + padding: .25em; + margin: 0; +} +body.poll table td input[type=radio]:checked + label { + font-weight: bold; +} +body.poll table td input[type=radio][value=yes]:checked + label { + background-color: #AFA; + margin: 0 -1.5pt; +} +body.poll table td input[type=radio][value=no]:checked + label { + background-color: #FAA; + margin: 0 -.75pt; +} +body.poll table td input[type=radio][value=maybe]:checked + label { + background-color: #FFA; + margin: 0 -1.75pt; +} + body.newdate form { text-align: center; max-width: 100%; -- 2.39.2