]> git.plutz.net Git - webpoll/commitdiff
Merge commit '752db311013e53c3c16f685f86b507e191b2239c'
authorPaul Hänsch <paul@plutz.net>
Sat, 24 Jul 2021 20:08:41 +0000 (22:08 +0200)
committerPaul Hänsch <paul@plutz.net>
Sat, 24 Jul 2021 20:08:41 +0000 (22:08 +0200)
13 files changed:
Makefile [new file with mode: 0644]
cgilite/cgilite.sh [moved from cgilite.sh with 100% similarity]
cgilite/common.css [moved from common.css with 100% similarity]
cgilite/file.sh [moved from file.sh with 100% similarity]
cgilite/html-sh.sed [moved from html-sh.sed with 100% similarity]
cgilite/logging.sh [moved from logging.sh with 100% similarity]
cgilite/markdown.awk [moved from markdown.awk with 100% similarity]
cgilite/session.sh [moved from session.sh with 100% similarity]
cgilite/storage.sh [moved from storage.sh with 100% similarity]
index.cgi [new file with mode: 0755]
webpoll.css [new file with mode: 0644]
widgets.css [new file with mode: 0644]
widgets.sh [new file with mode: 0755]

diff --git a/Makefile b/Makefile
new file mode 100644 (file)
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
similarity index 100%
rename from cgilite.sh
rename to cgilite/cgilite.sh
similarity index 100%
rename from common.css
rename to cgilite/common.css
similarity index 100%
rename from file.sh
rename to cgilite/file.sh
similarity index 100%
rename from html-sh.sed
rename to cgilite/html-sh.sed
similarity index 100%
rename from logging.sh
rename to cgilite/logging.sh
similarity index 100%
rename from markdown.awk
rename to cgilite/markdown.awk
similarity index 100%
rename from session.sh
rename to cgilite/session.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 (executable)
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 (file)
index 0000000..b7d845a
--- /dev/null
@@ -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 (file)
index 0000000..bb48f52
--- /dev/null
@@ -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 (executable)
index 0000000..5e7132e
--- /dev/null
@@ -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 ']]'
+}