X-ZACK-LEAVEDATE) printf %s "Abmelde­datum";;
X-ZACK-JOINDATE_short) printf %s "Anm.";;
X-ZACK-LEAVEDATE_short) printf %s "Abm.";;
+ X-IBAN) printf %s "IBAN";;
*) l10n_global "$word";;
esac
[ $(pdi_count "$card" IMPP) -gt 0 ] && edit_item "$card" IMPP
[ $(pdi_count "$card" URL ) -gt 0 ] && edit_item "$card" URL
)]
- [div .section .address $(edit_item "$card" ADR)]
+ [div .section .address $(
+ edit_item "$card" ADR
+ [ $(pdi_count "$card" X-IBAN) -gt 0 ] && edit_item "$card" X-IBAN
+ )]
[div .section .note $(edit_item "$card" NOTE)]
[div .section .attendance
[h3 $(l10n course_attendance) ] [div .attendance $(
[div .item .newfield
[select name="newfield"
[option value="" disabled="disabled" selected="selected" $(l10n edit_addfieldtext)]
- $(for f in NICKNAME EMAIL TEL IMPP ADR URL NOTE X-ZACK-LEAVEDATE; do
+ $(for f in NICKNAME EMAIL TEL IMPP ADR URL NOTE X-ZACK-LEAVEDATE X-IBAN; do
printf '[option value="%s" %s] ' "$f" "$(l10n "$f")"
done)
][button type="submit" name="action" value="addfield" $(l10n edit_addfield)]
)]
[div .section .phone . $(card_item "$card" TEL)]
[div .section .message . $(card_item "$card" EMAIL IMPP URL)]
- [div .section .address . $(card_item "$card" ADR)]
+ [div .section .address . $(card_item "$card" ADR X-IBAN)]
[div .section .note . $(card_item "$card" NOTE)]
[div .section .attendance [h3 $(l10n course_attendance) ] [ul
$(grep -F " ${cardfile##*/}" "$_DATA/mappings/attendance" |while read each discard; do
action="$(POST action)"
newfield="$(POST newfield |grep -m 1 -xE '[A-Z][A-Z0-9-]*')"
-if printf '%s\n' "$action" |grep -qxE 'addfield [A-Z][A-Z0-9]*'; then
+if printf '%s\n' "$action" |grep -qxE 'addfield [A-Z][A-Z0-9-]*'; then
newfield="${action##* }"
action=addfield
fi
_PATH="${_PATH#${_BASE}}"
ACTION="$(GET a)"
+SESSION_COOKIE
+
message="$(COOKIE message)"
[ "$message" ] && SET_COOKIE 0 message=''
printf '
] [body #top class="%s"
' "$class"
- printf '[ul .menu [li [a "%s/cards/" . %s]][li [a "%s/courses/" . %s]]]' "${_BASE}" "$(l10n cards)" "${_BASE}" "$(l10n courses)"
+ printf '[ul .menu [li [a "%s/cards/" . %s]][li [a "%s/courses/" . %s]][li [a "%s/ledgers/" . %s]]]' \
+ "${_BASE}" "$(l10n cards)" "${_BASE}" "$(l10n courses)" "${_BASE}" "$(l10n ledgers)"
[ "$message" ] && printf '[p #message\n%s\n]' "$(l10n "$message")"
cat
printf '] ]'
# Nav Menu
cards) printf %s "Teil­neh­mende";;
courses) printf %s "Kurse";;
+ ledgers) printf %s "Bei­trä­ge";;
# VCF Default
PHOTO) printf %s "Foto";;
--- /dev/null
+#!/bin/awk -f
+
+function STRING( inp ) {
+ gsub(/\\/, "\\\\", inp);
+ gsub(/\n/, "\\n", inp);
+ gsub(/\r/, "\\r", inp);
+ gsub(/\t/, "\\t", inp);
+ gsub(/\+/, "\\+", inp);
+ gsub(/ /, "+", inp);
+ return inp ? inp : "\\";
+}
+
+function UNSTRING( inp, out, tmp ) {
+ while ( inp ) {
+ if ( inp ~ /^\\\\/) { out = out "\\"; sub(/^\\\\/, "", inp); }
+ else if ( inp ~ /^\\n/) { out = out "\n"; sub(/^\\n/, "", inp); }
+ else if ( inp ~ /^\\r/) { out = out "\r"; sub(/^\\r/, "", inp); }
+ else if ( inp ~ /^\\t/) { out = out "\t"; sub(/^\\t/, "", inp); }
+ else if ( inp ~ /^\\+/) { out = out "+"; sub(/^\\+/, "", inp); }
+ else if ( inp ~ /^\+/) { out = out " "; sub(/^\+/, "", inp); }
+ else if ( inp ~ /^\\/) { out = out ""; sub(/^\+/, "", inp); }
+ else { tmp = inp; sub(/[\\+].*$/, "", tmp); out = out tmp; sub(/^[^\\+]*/, "", inp); }
+ }
+ return out;
+}
+
+function isdate( date, dt, y, m, d ) {
+ if ( match( date,
+ /^[0-9]{4}-((01|03|05|07|08|10|12)-(0[1-9]|[12][0-9]|3[01])|(04|06|09|11)-(0[1-9]|[12][0-9]|30)|02-(0[1-9]|[12][0-9]))$/ )) {
+ split( date, dt, "-");
+ y = dt[1]; m = dt[2]; d = dt[3];
+
+ } else if ( match( date,
+ /^((0?1|0?3|0?5|0?7|0?8|10|12)\/(0?[1-9]|[12][0-9]|3[01])|(0?4|0?6|0?9|11)\/(0?[1-9]|[12][0-9]|30)|0?2\/(0[1-9]|[12][0-9]))\/([0-9]{2}|[0-9]{4})$/ )) {
+ split( date, dt, "/");
+ m = dt[1]; d = dt[2]; y = dt[3];
+
+ } else if ( match( date,
+ /^((0?[1-9]|[12][0-9]|3[01])[\.\/](0?1|0?3|0?5|0?7|0?8|10|12)|(0?[1-9]|[12][0-9]|30)[\.\/](0?4|0?6|0?9|11)|(0[1-9]|[12][0-9])[\.\/]0?2)[\.\/]([0-9]{2}|[0-9]{4})$/ )) {
+ split( date, dt, /[\.\/]/);
+ d = dt[1]; m = dt[2]; y = dt[3];
+
+ } else return "";
+
+ if ( y < 100 && y > 50 ) y = y + 1900;
+ if ( y <= 50 ) y = y + 2000;
+
+ # leap year
+ if ( m == 2 && d == 29 ) {
+ if ( y % 400 == 0 ) y = y;
+ else if ( y % 100 == 0 ) return "";
+ else if ( y % 4 == 0 ) y = y;
+ else return "";
+ }
+
+ return sprintf("%04i-%02i-%02i", y, m, d);
+}
+
+function cents( val ) {
+ gsub(/\./, "", val); sub(/,/, ".", val);
+ return val * 100;
+}
+
+BEGIN {
+ FS = ";";
+ dtrange_end = dt_from = dt_to = balance_start = balance_end = "";
+ split("", rec);
+ rec[0] = "Date DateU IBAN Name Subject Amount"
+}
+
+/^([012]?[0-9]|30|31).(0?[1-9]|1[012]).[0-9]{4} - ([012]?[0-9]|30|31).(0?[1-9]|1[012]).[0-9]{4}$/ {
+ dtrange_end = $0; sub(/^.* - /, "", dtrange_end);
+ dt_from = $0; sub(/ - .*$/, "", dt_from); dt_from = isdate(dt_from);
+ dt_to = $0; sub(/^.* - /, "", dt_to ); dt_to = isdate(dt_to );
+}
+
+/^Letzter Kontostand;;;;[0-9\.,]+;EUR$/ {
+ balance_start = cents($5);
+}
+
+/Kontostand;[^;]+;;;[0-9\.,]+;EUR/ {
+ if ( $2 = dtrange_end ) balance_end = cents($5)
+}
+
+$18 == "EUR" {
+ rec_date = isdate($1); gsub(/-/, " ", rec_date); rec_date = mktime(rec_date " 00 00 00", "UTC");
+ rec[length(rec)] = sprintf("%s %i %s %s %s %i",
+ isdate($1), rec_date, $6 ? $6 : "\\", STRING($4), STRING($5), cents($12));
+}
+
+END {
+ if ( dt_from && dt_to ) {
+ dtu_from = dt_from; gsub(/-/, " ", dtu_from); dtu_from = mktime( dtu_from " 00 00 00", "UTC");
+ dtu_to = dt_to ; gsub(/-/, " ", dtu_to ); dtu_to = mktime( dtu_to " 00 00 00", "UTC");
+
+ printf "%i %s %i %s %i %i\n",
+ dtu_from, dt_from, dtu_to, dt_to, balance_start, balance_end;
+ for ( k = 1; k < length(rec); k++ ) print rec[k];
+ }
+}
--- /dev/null
+#!/bin/sh
+
+# Copyright 2024 Paul Hänsch
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED “AS IS” AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+# SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+# IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+if [ "${CONTENT_TYPE%%;*}" != "multipart/form-data" ]; then
+ SET_COOKIE 0 message="Not an upload"
+ REDIRECT "${_BASE}/ledgers/"
+fi
+
+. "$_EXEC/multipart.sh"
+multipart_cache
+
+# Validate session id from form to prevent CSRF
+if [ "$(multipart session_id)" != "$SESSION_ID" ]; then
+ rm -- "$multipart_cachefile"
+ SET_COOKIE 0 message="INVALID SESSION ID IN FORM"
+ REDIRECT "${_BASE}/ledgers/"
+fi
+
+mkdir -p "$_DATA/ledgers/"
+CSV="$(multipart "csv" 1 | "$_EXEC/ledgers/csv_upload.awk")"
+rm -- "$multipart_cachefile"
+
+read dtu_start dt_start dtu_end dt_end balance_start balance_end <<-EOF
+ ${CSV%%${BR}*}
+EOF
+
+if [ ! "$dtu_end" -o ! "$dtu_start" ] || [ "$dtu_end" -lt "$dtu_start" ]; then
+ SET_COOKIE 0 message="No valid date range in upload"
+else
+ num=0; while [ ! "$filename" -o -f "$_DATA/ledgers/$filename" ]; do
+ num=$((num + 1)); filename="${dt_start} - ${dt_end} - $(printf '%04i' $num).tbl"
+ done
+ printf '%s\n' "$CSV" >"$_DATA/ledgers/$filename"
+fi
+
+REDIRECT "${_BASE}/ledgers/"
--- /dev/null
+#!/bin/awk -f
+
+function STRING( inp ) {
+ gsub(/\\/, "\\\\", inp);
+ gsub(/\n/, "\\n", inp);
+ gsub(/\r/, "\\r", inp);
+ gsub(/\t/, "\\t", inp);
+ gsub(/\+/, "\\+", inp);
+ gsub(/ /, "+", inp);
+ return inp ? inp : "\\";
+}
+
+function UNSTRING( inp, out, tmp ) {
+ while ( inp ) {
+ if ( inp ~ /^\\\\/) { out = out "\\"; sub(/^\\\\/, "", inp); }
+ else if ( inp ~ /^\\n/) { out = out "\n"; sub(/^\\n/, "", inp); }
+ else if ( inp ~ /^\\r/) { out = out "\r"; sub(/^\\r/, "", inp); }
+ else if ( inp ~ /^\\t/) { out = out "\t"; sub(/^\\t/, "", inp); }
+ else if ( inp ~ /^\\+/) { out = out "+"; sub(/^\\+/, "", inp); }
+ else if ( inp ~ /^\+/) { out = out " "; sub(/^\+/, "", inp); }
+ else if ( inp ~ /^\\/) { out = out ""; sub(/^\+/, "", inp); }
+ else { tmp = inp; sub(/[\\+].*$/, "", tmp); out = out tmp; sub(/^[^\\+]*/, "", inp); }
+ }
+ return out;
+}
+
+function rx( regex ) {
+ gsub(/[].*+?^${}()|\\[]/, "\\\\&", regex);
+ return regex;
+}
+
+BEGIN {
+ FS = " "; ledger = 0;
+ fn = n = uid = iban = tmp = "";
+ split("", uid_n); split("", uid_fn); split("", uid_iban);
+ split("", iban_uid); split("", ibans); split("", uids);
+
+ split("", sure); split("", unsure); split("", unknown);
+ split("", unsure_rec)
+}
+
+/^BEGIN;:VCARD$/ { fn = n = id = iban = tmp = ""; }
+
+ /^UID;[^:]*:/ { uid = $0; sub(/^[^;]+;[^:]*:/, "", uid); }
+ /^FN;[^:]*:/ { fn = $0; sub(/^[^;]+;[^:]*:/, "", fn); }
+ /^N;[^:]*:/ { n = $0; sub(/^[^;]+;[^:]*:/, "", n); sub(/;.*$/, "", n); }
+/^X-IBAN;[^:]*:/ { iban = $0; sub(/^[^;]+;[^:]*:/, "", iban); ibans[length(ibans)] = iban; }
+
+/^END;:VCARD$/ {
+ uid_n[uid] = n; uid_fn[uid] = fn; uid_iban[uid] = iban;
+ for (iban in ibans) iban_uid[ibans[iban]] = iban_uid[ibans[iban]] ? iban_uid[ibans[iban]] " " uid : uid;
+ fn = n = uid = iban = tmp = ""; split("", ibans);
+}
+
+strftime("%Y-%m-%d", $1, "UTC") == $2 && strftime("%Y-%m-%d", $3, "UTC") == $4 { ledger = 1; }
+
+ledger && strftime("%Y-%m-%d", $2, "UTC") == $1 {
+ if ($3 in iban_uid) {
+ sure[$3] = iban_uid[$3];
+ } else {
+ for (uid in uid_fn) if ( match(UNSTRING($5), rx(uid_fn[uid])) ) {
+ if (! match(unsure[$3], rx(uid))) unsure[$3] = unsure[$3] ? unsure[$3] " " uid : uid;
+ unsure_rec[$3] = $0
+ }
+ for (uid in uid_n) if ( uid_n[uid] && match(UNSTRING($4), rx(uid_n[uid])) ) {
+ if (! match(unsure[$3], rx(uid))) unsure[$3] = unsure[$3] ? unsure[$3] " " uid : uid;
+ unsure_rec[$3] = $0
+ }
+ }
+ if (!($3 in sure) && !($3 in unsure)) unknown[$3] = $0;
+}
+
+END {
+ for (iban in sure) {
+ line = "sure " iban;
+ split(sure[iban], uids, / /);
+ for (k in uids) line = line " " STRING(uids[k] "/" uid_fn[uids[k]]);
+ print line;
+ }
+ for (iban in unsure) {
+ line = "guess " iban " " STRING(unsure_rec[iban]);
+ split(unsure[iban], uids, / /);
+ for (k in uids) line = line " " STRING(uids[k] "/" uid_fn[uids[k]]);
+ print line;
+ }
+ for (iban in unknown) {
+ line = "unknown " iban " " unknown[iban];
+ print line;
+ }
+}
--- /dev/null
+#!/bin/sh
+
+. "$_EXEC/cgilite/storage.sh"
+. "$_EXEC/pdiread.sh"
+
+credit() {
+ printf '%03i\n' "$1" \
+ | sed -E 's;[0-9]{2}$;d&;; :0 s;([0-9])([0-9]{3}[dm]);\1m\2;; t0; y;dm;,.;'
+}
+
+{ printf '
+ [form .upload action="%s/ledgers/csv_upload.sh" method="POST" enctype="multipart/form-data"
+ [label for=ledger_upload . %s:]
+ [input #ledger_upload type="file" name="csv" accept=".csv,text/csv"]
+ [input type=hidden name=session_id value="%s"]
+ [button type="submit" %s]
+ ]' \
+ "${_BASE}" "$(l10n "Postbank CSV")" "$SESSION_ID" "$(l10n Upload)"
+ printf '
+ [form .ledgers action="%s/ledgers/delete.sh" method=POST
+ [input type=hidden name=session_id value="%s"]
+ [h3 . %s]
+ ' "${_BASE}" "$SESSION_ID" "$(l10n Ledgers)"
+ for ledger in "$_DATA"/ledgers/????-??-??\ -\ ????-??-??\ -\ ????.tbl; do
+ ledger="${ledger##*/}"
+ [ "$ledger" = "????-??-?? - ????-??-?? - ????.tbl" ] && continue
+ printf '[p .ledger . %s [button type=submit name=delete value="%s" . %s]]' \
+ "$(HTML "${ledger% - ????.tbl}")" "$(HTML "$ledger")" "$(l10n delete)"
+ done
+ printf ' ]'
+ printf '
+ [form .ibanassign action="%s/ledgers/iban_assign.sh" method=POST
+ [input type=hidden name=session_id value="%s"]
+ [h3 . %s]
+ ' "${_BASE}" "$SESSION_ID" "$(l10n "IBAN Assignments")"
+ printf '[datalist id=lattendants .'
+ pdi_load "${_DATA}"/vcard/*.vcf |sed -n '/^FN\;:/!b; s;^FN\;:;;; p;' \
+ | while read name; do
+ printf '[option value="%s"]' "$(HTML "$name")"
+ done
+ printf ']'
+ l10n_attendant="$(l10n attendant)"
+ { pdi_load "${_DATA}"/vcard/*.vcf
+ cat "${_DATA}"/ledgers/????-??-??\ -\ ????-??-??\ -\ ????.tbl
+ } | "${_EXEC}"/ledgers/iban_assign.awk \
+ | while read -r state iban data; do
+ printf '[fieldset .iban .%s [legend . %s ]' \
+ "$state" "$iban"
+ if [ $state = sure ]; then
+ for card in $data; do
+ uid="${card%%/*}" name="$(UNSTRING "${card#*/}")"
+ printf '[span .card . %s]' "$(HTML "${name}")"
+ done
+ :
+ elif [ $state = guess ]; then
+ record="$(UNSTRING "${data%% *}")"
+ cards="${data#* }"
+ date="${record%% *}"
+ principal="${record#* * * }" principal="${principal%% *}"
+ subject="${record#* * * * }" subject="${subject%% *}"
+ amount="${record#* * * * * }" amount="${amount%% *}"
+ printf '[p .principal . %s][p .date %s][p .amount %s][p .subject . %s]' \
+ "$(UNSTRING "$principal" |HTML)" "$date" "$(credit "$amount")" "$(UNSTRING "$subject" |HTML)"
+ n=0; for card in $cards; do
+ n=$((n+1)); uid="${card%%/*}" name="$(UNSTRING "${card#*/}")"
+ cat <<-EOF
+ [input type=checkbox id="check_${iban}_$n" name="check_${iban}_$n" value=true checked=checked]
+ [input .card name="fn_${iban}_$n" value="$(HTML "$name")" disabled=disabled]
+ [label .del for="check_${iban}_$n" . -]
+ EOF
+ done
+ for m in 1 2 3 4 5 6 7 8; do
+ cat <<-EOF
+ [input type=checkbox id="check_${iban}_$((n+m))" name="check_${iban}_$((n+m))" value=false]
+ [input .card name="fn_${iban}_$((n+m))" value="" placeholder="${l10n_attendant}" list="lattendants"]
+ [label .add for="check_${iban}_$((n+m))" . +]
+ EOF
+ done
+ elif [ $state = unknown ]; then
+ date="${data%% *}"
+ principal="${data#* * * }" principal="${principal%% *}"
+ subject="${data#* * * * }" subject="${subject%% *}"
+ amount="${data#* * * * * }" amount="${amount%% *}"
+ printf '[p .principal . %s][p .date %s][p .amount %s][p .subject . %s]' \
+ "$(UNSTRING "$principal" |HTML)" "$date" "$(credit "$amount")" "$(UNSTRING "$subject" |HTML)"
+ printf '[input name="check_" type=hidden][input type=hidden][label .del]'
+ n=0; for m in 1 2 3 4 5 6 7 8; do
+ cat <<-EOF
+ [input type=checkbox id="check_${iban}_$((n+m))" name="check_${iban}_$((n+m))" value=false]
+ [input .card name="fn_${iban}_$((n+m))" value="" placeholder="${l10n_attendant}" list="lattendants"]
+ [label .add for="check_${iban}_$((n+m))" . +]
+ EOF
+ done
+ fi
+ printf ']'
+ done
+ printf ' ]'
+} | yield_page ledgers
--- /dev/null
+#!/bin/sh
+
+[ "$include_multipart" ] && return 0
+inlude_multipart="$0"
+
+# Copyright 2022 - 2023 Paul Hänsch
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED “AS IS” AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+# SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+# IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+if [ "${CONTENT_TYPE}" -a ! "${CONTENT_TYPE##multipart/form-data;*}" ]; then
+ multipart_boundary="${CONTENT_TYPE#*; boundary=}"
+ multipart_boundary="${multipart_boundary%%;*}"
+ multipart_boundary="${multipart_boundary%${CR}}"
+fi
+multipart_cachefile="/tmp/multipart.$$"
+
+readbytes(){
+ # read n bytes, like `head -c` but do not consume input
+ local size="$1" block
+
+ for block in 65536 32768 16384 8192 4096 2048 1024 512 256 128 64 32 16 8 4 2 1; do
+ if [ $size -ge $block ]; then
+ dd status=none bs="$block" count="$((size / block))"
+ size="$((size % block))"
+ fi
+ done
+}
+
+multipart_cache() {
+ multipart_cachefile="${1:-${multipart_cachefile}}" # global
+
+ if [ "${multipart_boundary}" ]; then
+ # readbytes "$(( CONTENT_LENGTH ))" >"${multipart_cachefile}"
+ head -c "$(( CONTENT_LENGTH ))" >"${multipart_cachefile}"
+ else
+ return 1
+ fi
+}
+
+multipart(){
+ local name="$1" count="${2:-1}"
+ local formdata state=begin
+
+ while IFS='' read -r formdata; do case "$formdata" in
+ "--${multipart_boundary}--${CR}")
+ [ $state = data ] && return 0 \
+ || return 1
+ ;;
+ "--${multipart_boundary}${CR}")
+ [ $state = data ] && return 0 \
+ || state=header
+ ;;
+ "Content-Disposition: form-data; name=\"${name}\""*"${CR}")
+ [ $state = header -a $count -eq 1 ] && state=dheader
+ [ $state = header -a $count -gt 1 ] && count=$((count - 1))
+ [ $state = data ] && printf "%s\n" "$formdata"
+ ;;
+ "${CR}")
+ if [ $state = dheader ]; then
+ # Do not use `sed -n` (or busybox sed will "convert" NULL to LF)
+ sed "/--${multipart_boundary}\(--\)\?${CR}/{x;q;}" \
+ | head -c-3
+ return 0;
+ fi
+ [ $state = header ] && state=junk
+ ;;
+ esac; done <"${multipart_cachefile}"
+}
+
+multipart_filename(){
+ local name="$1" count="${2:-1}"
+ local formdata state=begin
+
+ while read -r formdata; do case "$formdata" in
+ "--${multipart_boundary}--${CR}")
+ return 1
+ ;;
+ "--${multipart_boundary}${CR}")
+ state=header
+ ;;
+ "Content-Disposition: form-data; name=\"${name}\"; filename=\""*"\""*"${CR}")
+ [ $state = header -a $count -eq 1 ] && break
+ [ $state = header -a $count -gt 1 ] && count=$((count - 1))
+ ;;
+ "${CR}")
+ [ $state = header ] && state=junk
+ ;;
+ esac; done <"${multipart_cachefile}"
+
+ filename="${formdata#*; filename=\"}"
+ filename="${filename%%\"${CR}}"
+ filename="${filename%%\";*}"
+
+ HEX_DECODE % "$filename"
+}
padding: .5em 3em;
box-shadow: inset 0 0 .5em #000;
}
-body.cards > .menu a[href$="/cards/"],
-body.courses > .menu a[href$="/courses/"] {
+body.ledgers > .menu a[href$="/ledgers/"],
+body.courses > .menu a[href$="/courses/"],
+body.cards > .menu a[href$="/cards/"] {
color: #000;
background-color: #FFF;
box-shadow: none;
/* =========== FILTER AND SEARCH Headers ========= */
-form.categories,
+form.upload, form.categories,
form.search, form.sort, form.filter, form.newcard, form.newcourses {
- margin-top: 1em; padding: 0 1em;
+ margin-top: 1em; padding: .125em 1em 0 1em;
z-index: 1;
}
form.filter > h1 { display: none; }
form.filter button[value=export_csv] { margin-left: 1em; }
body.courses form .order { display: inline-block; margin-right: 2em;}
+body.courses form.search.sort fieldset { margin-top: .5em; }
body.cards form.newcard { display: flex; }
body.cards form.newcard input[name=seed] { flex: 1; }
+form.upload label {
+ display: block;
+ font-weight: bold;
+ margin-top: .5em;
+}
+
/* ============ LIST ITEMS, Generic ============= */
body.categories form.namelist ul.namelist > li ul li {
display: inline-block;
}
+
+
+/* ======== Ledgers Page ======== */
+
+form.ibanassign,
+form.ledgers {
+ padding: .125em 1em 0 1em;
+}
+
+.ibanassign fieldset.iban.sure { background-color: #DFD; }
+.ibanassign fieldset.iban.guess { background-color: #FFD; }
+.ibanassign fieldset.iban.unknown { background-color: #FDD; }
+
+.ibanassign fieldset.iban {
+ padding: 0 .75em;
+ margin-top: -.5em;
+ box-shadow: .25em .25em .25em #AAA;
+}
+.ibanassign fieldset.iban legend {
+ top: .75em;
+}
+.ibanassign fieldset.iban p.principal {
+ font-size: .875em;
+}
+.ibanassign fieldset.iban p.date,
+.ibanassign fieldset.iban p.amount {
+ font-size: .875em;
+ display: inline-block;
+ vertical-align: top;
+ margin-right: .75em;
+ margin-bottom: 0;
+}
+.ibanassign fieldset.iban p.amount {
+ font-weight: bold;
+}
+
+.ibanassign fieldset.iban input[name^="check_"],
+.ibanassign fieldset.iban input[name^="check_"] + input,
+.ibanassign fieldset.iban input[name^="check_"] + input + label {
+ display: none;
+}
+.ibanassign fieldset.iban input[name^="check_"]:checked + input,
+.ibanassign fieldset.iban input[name^="check_"]:checked + input + label.del,
+.ibanassign fieldset.iban input[name^="check_"] + input + label.del + input + input + label.add,
+.ibanassign fieldset.iban input[name^="check_"]:checked + input + label + input + input + label.add {
+ display: inline;
+}
+.ibanassign fieldset.iban input[name^="check_"]:checked + input + label.add,
+.ibanassign fieldset.iban input[name^="check_"] + input + label.del + input:checked + input + label.add,
+.ibanassign fieldset.iban input[name^="check_"]:checked + input + label + input:checked + input + label.add {
+ display: none;
+}
+
+.ibanassign fieldset.iban input[name^="check_"] + input + label {
+ vertical-align: bottom;
+ line-height: 2.5em;
+ padding: .375em .625em;
+ border: .5pt solid;
+}
+.ibanassign fieldset.iban input[name^="check_"] + input + label.add {
+ background-color: #DFD;
+ border-radius: 2pt;
+}
+.ibanassign fieldset.iban input[name^="check_"] + input + label.del {
+ margin: 0 .5em 0 -.25em;
+ background-color: #FDD;
+ border-radius: 0 2pt 2pt 0;
+}