From 3c750be4fa892b6f79b5141f09701752c7249930 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Paul=20H=C3=A4nsch?= Date: Wed, 14 Nov 2018 01:26:12 +0100 Subject: [PATCH] initial commit --- .gitmodules | 3 + cgilite | 1 + invoices.cgi | 198 +++++++++++++++++++++++++++++++++++++++++++++++++++ invoices.css | 97 +++++++++++++++++++++++++ 4 files changed, 299 insertions(+) create mode 100644 .gitmodules create mode 160000 cgilite create mode 100755 invoices.cgi create mode 100755 invoices.css diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..5c32300 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "cgilite"] + path = cgilite + url = http://git.plutz.net/git/cgilite diff --git a/cgilite b/cgilite new file mode 160000 index 0000000..5c24129 --- /dev/null +++ b/cgilite @@ -0,0 +1 @@ +Subproject commit 5c24129a2347b2be54fedfec74acc156758e3533 diff --git a/invoices.cgi b/invoices.cgi new file mode 100755 index 0000000..d079ced --- /dev/null +++ b/invoices.cgi @@ -0,0 +1,198 @@ +#!/bin/sh + +_EXEC="$(realpath "${0%/*}")" +. "$_EXEC/cgilite/cgilite.sh" +. "$_EXEC/cgilite/storage.sh" + +debug(){ + if [ $# = 0 ]; then + tee /dev/stderr + else + printf %s\\n "$*" >/dev/stderr + fi +} + +timeid(){ + # generate time based ID + # Fixme: Unix time stamps assumed to be 32bit always + d=$(date +%s) + { printf $( + while [ "$d" -gt 0 ]; do + printf \\%o\\n $((d % 256)) + d=$((d / 256)) + done |tac |tr -d \\n + ) + head -c5 /dev/urandom + } \ + | uuencode -m - \ + | sed -n '2{y;+/;:_;;p}' +} + +checkid(){ + grep -m 1 -xE '[0-9a-zA-Z:_]{12}'; +} + +yield_page(){ +printf 'Content-Type: text/html; charset=utf-8\r\n\r\n' +./cgilite/html-sh.sed < +%s + [label for=hourly Hourly Rate:] + [input #hourly type=number name=hourly value="%s"] + [submit "update" "update" Update] + ]' \ + "$(HTML $id)" \ + "$(UNSTRING "${address#address=}" |HTML)" \ + "$(UNSTRING "${hourly#hourly=}" |grep -xE '[0-9]+')" +} + +edit_sender(){ + id="$1" + if [ -f "senders/$id" ]; then + address="$(cat "senders/$id")" + fi + [ "$address" ] || address="Name +Street +City + +Phone: +000 000000 + +Tax no. +xxx / 000 / ### +" + printf ' + [form method="POST" action="/update_sender" + [hidden "id" "%s"] + + [submit "update" "update" Update] + ]' \ + "$(HTML $id)" \ + "$(HTML "${address}")" +} + +list_clients(){ + [ -d clients/ ] && for c in clients/*; do + read -r address hourly <"$c" + address="$(UNSTRING "${address#address=}")" + [ "$address" ] || address="(no address)" + printf '[div .client .address %s[a href="/clients/%s" Edit]] + ' "$(HTML "$address")" "$(HTML "${c#clients/}")" + done +} + +list_senders(){ + [ -d senders/ ] && for s in senders/*; do + address=$(cat "$s") + [ "$address" ] || address="(no address)" + printf '[div .sender .address %s[a href="/senders/%s" Edit]] + ' "$(HTML "$address")" "$(HTML "${s#senders/}")" + done +} + +list_invoices(){ + [ -d invoices/ ] && for i in invoices/*; do + read -r sender client date number vat<<-EOF + sed q "$i" + EOF + [ -f "senders/${sender#sender=}" ] \ + && sender="$(sed q "senders/${sender#sender=}")" \ + || sender="(unset)" + [ -f "clients/${client#client=}" ] \ + && client="$(sed q "client/${client#client=}")" \ + || client="(unset)" + [ "$date" -gt 0 ] \ + && date="$(date -d @$date +%x)" \ + || date="(unset)" + + printf '[div .invoice + [h2 + %s] + [label From:] %s [label To:] %s [label on] %s + [a href="/invoices/%s" Edit] + ]' "$(HTML "$number")" "$(HTML "$sender")" \ + "$(HTML "$client")" "$(HTML "$date")" + done +} + +info="$(PATH "${PATH_INFO}")" + +case $info in + /invoices.css) + . "$_EXEC/cgilite/file.sh" + FILE "$_EXEC/invoices.css" + ;; + /clients) + { list_clients + printf '[a .new href="/clients/%s" New]' "$(timeid)" + } | yield_page clients + ;; + /clients/*) + edit_client "${info#/clients/}" |yield_page client + ;; + /update_client) + id="$(POST id |checkid)" + if [ "$(POST update)" = update -a "$id" ]; then + mkdir -p clients + printf 'address=%s hourly=%s' \ + "$(POST address |STRING)" "$(POST hourly |STRING)" \ + >"clients/$id" + else + echo Invalid Data "$(POST id)" "$(POST update)" >&2 + fi + REDIRECT /clients/ + ;; + /senders) + { list_senders + printf '[a .new href="/senders/%s" New]' "$(timeid)" + } | yield_page senders + ;; + /senders/*) + edit_sender "${info#/senders/}" |yield_page sender + ;; + /update_sender) + id="$(POST id |checkid)" + if [ "$(POST update)" = update -a "$id" ]; then + mkdir -p senders + POST address >"senders/$id" + fi + REDIRECT /senders/ + ;; + /invoices) + { list_invoices + printf '[a href="/invoices/%s" New]' "$(timeid)" + } | yield_page invoices + ;; + /invoice/*) + edit_invoice "${info#/invoices/}" |yield_page invoice + ;; + *) REDIRECT /invoices + ;; +esac diff --git a/invoices.css b/invoices.css new file mode 100755 index 0000000..d495fc8 --- /dev/null +++ b/invoices.css @@ -0,0 +1,97 @@ +* { + margin: 0; padding: 0; + color: inherit; background-color: inherit; + font-size: medium; font-weight: normal; + text-decoration: none; + box-sizing: border-box; +} +body { + color: black; background-color: white; +} +#menu { + margin: 0; padding: .5em 1em; + border-bottom: 1px solid black; +} +#menu a { + background-color: #EEF; + border: 1px solid black; + margin: .5em 1em .5em 0; + padding: .125em .5em; +} +#menu a:hover { + background-color: #FFF; + border-color: #888; +} + +.clients .address, +.senders .address { + display: inline-block; + background-color: #EEE; + white-space: pre; + padding: 1em 1em .5em 1em; + margin: 1em 0 0 1em; + width: 20em; +} +.clients .address a, +.senders .address a, +a.new { + display: block; + background-color: #DEF; + text-align: center; + margin: .5em 3em 0 3em; + padding: .125em 1em; + border: 1px solid #08F; +} +.clients .address a:hover, +.senders .address a:hover, +a.new:hover { + border-color: #8EF; +} +a.new { + display: inline-block; + margin: 1em; +} + +form textarea, +form input { + padding: .25em .5em; + background-color: #FFF; + text-align: left; +} +.client form, +.sender form { + position: relative; + display: inline-block; + margin: 1em; padding: 1em; + background-color: #EEE; + text-align: right; +} +.client form textarea, +.sender form textarea { + display: block; + min-width: 30em; + min-height: 7em; + margin-bottom: .5em; +} +.sender form textarea { + min-height: 13em; +} +.client form label[for=hourly] { + margin-right: .5em; + line-height: 2em; + text-align: right; + font-weight: bold; +} +.client form input#hourly { + min-width: 4em; width: 5em; + text-align: right; +} +.client form button[type=submit], +.sender form button[type=submit] { + display: block; + margin: .5em 0 0 auto; + padding: .25em .5em; + background-color: #DEF; + border-radius: .25em; + border: 1px solid #08F; +} -- 2.39.2