-[submodule "shcgi"]
- path = shcgi
- url = http://git.plutz.net/git/shcgi
--- /dev/null
+.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
+++ /dev/null
-#!/bin/zsh
-
-# Copyright 2014,2015 Paul Hänsch
-#
-# This file is part of Confetti.
-#
-# Confetti is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# Confetti is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with Confetti. If not, see <http://www.gnu.org/licenses/>.
-
-echo -n "Content-Type: text/vcard;charset=utf-8\n\n"
-card="${_GET[card]}"
-cat "$_DATA/vcard/$card"
+++ /dev/null
-#!/bin/zsh
-
-# Copyright 2014, 2016, 2017 Paul Hänsch
-#
-# This file is part of Confetti.
-#
-# Confetti is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# Confetti is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with Confetti. If not, see <http://www.gnu.org/licenses/>.
-
-pdflatex="$(where pdflatex |head -n1 || echo false)"
-course="${_GET[course]}"
-fromdate="${_GET[fromdate]}"
-fromdate="$(date -d "$fromdate" +%s)" 2>/dev/null
-[ -z "$fromdate" ] && fromdate=$(date +%s)
-
-. ${_EXEC}/pages/courses.sh
-. ${_EXEC}/pages/cards.sh
-
-tex_clean() { #in dire need for improvement
- printf %s "$*" |tr -d '{&}\\"'
-}
-
-
-list_attendee() { #Parameter: Cardfile
- id="$1"
- cardfile="$_DATA/vcard/${id}"
-
- declare -A values
-
- if [ -r "$cardfile" ]; then
- vcf_parse "$cardfile"
-
- n=$(printf %s "$values[N]" \
- | sed -rn 's:^([^;]*)(;[^;]*)(;[^;]*)?(;[^;]*)?(;[^;]*)?$:\4 \2 \3 \1 \5:gp' \
- | sed -r 's:,: :;s:;: :g;s: +: :g;s:^ $::;'
- )
- fullname="${n:-${values[FN]:-${values[NICKNAME]}}}"
-
- tel=''
- for n in TEL TEL{0..10}; do if (echo "$values[$n]" |grep -Eq '[0-9]'); then
- [ -n "$tel" ] && tel="$tel\\newline $(tex_clean "$values[$n]")" || tel="$(tex_clean "$values[$n]")"
- fi; done
-
- note=''
- for n in NOTE NOTE{0..10}; do if [ -n "$values[$n]" ]; then
- [ -n "$note" ] && note="$note\\newline $(tex_clean "$values[$n]")" || note="$(tex_clean "$values[$n]")"
- fi; done
- printf '%s & %s & %s & %s\n' \
- "$(tex_clean $fullname)" "$(tex_clean $values[BDAY])" "$tel" "$note" \
- | sed -r ':X;N;$!bX; s;\n;\\newline ;g'
- fi
-}
-
-get_dates() { #Parameter: Calendarfile
- calendarfile="$_DATA/ical/$course"
-
- declare -A values
- ics_parse "$calendarfile"
-
- dtstart="$values[DTSTART]"
- [ -z "$dtstart" ] && dtstart=$(date +%Y%m%dT%H%M%S)
- echo "$dtstart" |case "$dtstart" in
- *Z) sed -rn 's:^([0-9]{4})([0-9]{2})([0-9]{2})T([0-9]{2})([0-9]{2})([0-9]{2})Z$:\1-\2-\3 \4\:\5\:\6 UTC:p';;
- TZID*) sed -rn 's:^TZID=(.+)\:([0-9]{4})([0-9]{2})([0-9]{2})T([0-9]{2})([0-9]{2})([0-9]{2})$:TZ="\1" \2-\3-\4 \5\:\6\:\7:p';;
- *) sed -rn 's:^([0-9]{4})([0-9]{2})([0-9]{2})T([0-9]{2})([0-9]{2})([0-9]{2})$:\1-\2-\3 \4\:\5\:\6:p';;
- esac |read dts_date
- rrule="$values[RRULE]"
- rr_int="$(echo $rrule |sed -rn 's:^.*INTERVAL=([0-9]+)(;.*)?$:\1:p')"
- rr_freq="$(echo $rrule |sed -rn 's:^.*FREQ=(YEARLY|MONTHLY|WEEKLY|DAILY)(;.*)?$:\1:p')"
- case "$rr_freq" in
- YEARLY) rec="$rr_int year";;
- MONTHLY) rec="$rr_int month";;
- DAILY) rec="$rr_int day";;
- *) rec="$rr_int week";;
- esac
-
- next_date="$dts_date"
- n=10
- while [ $n -gt 0 ]; do
- if [ "$(date -d "$next_date" +%s)" -gt "$(date +%s)" ]; then
- dtlist="$dtlist & $(date -d "$next_date" +"%d. %b.")"
- n=$(($n - 1))
- fi
- next_date="$(date -d "$next_date + $rec" +%Y-%m-%d)"
- done
-
- echo "$dtlist"
-}
-
-if [ -r "${_DATA}/ical/${course}" ]; then
- . ${_EXEC}/templates/course_print.sh >"${_DATA}/temp/courselist_${course}.tex"
- [ -e "${_DATA}/temp/courselist_${course}.pdf" ] && rm "${_DATA}/temp/courselist_${course}.pdf"
- "$pdflatex" -halt-on-error -output-directory "${_DATA}/temp/" "${_DATA}/temp/courselist_${course}.tex" |debug >/dev/null
- "$pdflatex" -halt-on-error -output-directory "${_DATA}/temp/" "${_DATA}/temp/courselist_${course}.tex" |debug >/dev/null
-fi
-if [ -r "${_DATA}/temp/courselist_${course}.pdf" ]; then
- echo 'Content-Type: application/x-pdf\n'
- cat "${_DATA}/temp/courselist_${course}.pdf"
-fi
+++ /dev/null
-#!/bin/zsh
-
-# Copyright 2014 Paul Hänsch
-#
-# This file is part of Confetti.
-#
-# Confetti is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# Confetti is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with Confetti. If not, see <http://www.gnu.org/licenses/>.
-
-cgi_post
-cgi_refdata
-
-filter="&filter=${_REF[filter]}"
-filtertype="&filtertype=${_REF[filtertype]}"
-order="&order=${_REF[order]}"
-
-uid=$(uuidgenerator)
-card="${uid}.vcf"
-
-tempfile="$_DATA/temp/$card"
-
-cat >"$tempfile" <<EOF
-BEGIN:VCARD
-VERSION:4.0
-N:;;;;
-BDAY:
-TEL:
-EMAIL:
-ADR:
-NOTE:
-UID:$uid
-END:VCARD
-EOF
-
-echo -n "Location: ?p=cards${filter}${filtertype}${order}&edit=$card\n\n"
+++ /dev/null
-#!/bin/zsh
-
-# Copyright 2016 Paul Hänsch
-#
-# This file is part of Confetti.
-#
-# Confetti is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# Confetti is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with Confetti. If not, see <http://www.gnu.org/licenses/>.
-
-cgi_refdata
-
-client="${_POST[client]:-${_GET[client]}}"
-
-uid=$(uuidgenerator)
-prescription="${client%.vcf}.${uid}.mpx"
-
-cardfile="$_DATA/vcard/$client"
-tempfile="$_DATA/temp/$prescription"
-
-cat >"$tempfile" <<EOF
-prescription:${prescription}
-insurance:$(sed -nr 's;^X-HEALTH-INSURANCE:(.*)\;.*\;\r?$;\1;p' "$cardfile")
-bday:$(sed -nr 's;^BDAY:(.*)\r?$;\1;p' "$cardfile")
-name:$(sed -rn '/^N[\;:]/{s;^N(\;[^:]*)?:([^\;]*)(\;[^\;]*)(\;[^\;]*)?(\;[^\;]*)?(\;[^\;]*)?\r?$;\5 \3 \4 \2 \6;;s;[\;,]; ;g;s; +; ;g;s;^ | $;;g;p}' "$cardfile")\n$(sed -nr 's;^ADR:(.*)\r?$;\1;p' "$cardfile")
-date:
-EOF
-
-echo -n "Location: ?p=prescriptions&edit=$prescription\n\n"
+++ /dev/null
-#!/bin/zsh
-
-# Copyright 2017 Paul Hänsch
-#
-# This file is part of Confetti.
-#
-# Confetti is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# Confetti is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with Confetti. If not, see <http://www.gnu.org/licenses/>.
-
-bmfile="${_DATA}/mappings/bookmarks"
- url="$(validate "${_POST[bm_url]}" '/.+' '/')"
-name="$(validate "${_POST[bm_name]}" '.+' "$url")"
-
-case "${_POST[submit]}" in
- add) printf '%s\t%s\n' "${url}" "${name}" >>"${bmfile}"
- ;;
- del) cp "${bmfile}" "${bmfile}.temp"
- grep -vF "${url} ${name}" "${bmfile}.temp" >"${bmfile}"
- rm "${bmfile}.temp"
- ;;
-esac
-
-redirect "${url}#CONFIGURE"
+++ /dev/null
-#!/bin/zsh
-
-# Copyright 2014, 2016 Paul Hänsch
-#
-# This file is part of Confetti.
-#
-# Confetti is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# Confetti is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with Confetti. If not, see <http://www.gnu.org/licenses/>.
-
-cgi_refdata
-
-filter="&filter=${_REF[filter]}"
-filtertype="&filtertype=${_REF[filtertype]}"
-order="&order=${_REF[order]}"
-
-card="${_POST[card]}"
-tempfile="$_DATA/temp/$card"
-cardfile="$_DATA/vcard/$card"
-attfile="$_DATA/mappings/attendance"
-
-vcf_escape(){
- for each in "$@"; do
- printf %s\\n "$each" \
- | sed -r ':X;$!{N;bX}; s;\r\n;\n;g; s;([;,\\]);\\\1;g; s;\n;\\n;g;'
- done \
- | sed -r ':X;$!{N;bX}; s;\n;\;;g'
-}
-
-[ "${_POST[hi_select]}" = "list" ] || _POST[hi_company]="${_POST[hi_other]}"
-[ -n "${_POST[hi_company]}${_POST[hi_number]}${_POST[hi_status]}" ] \
-&& _POST[X-HEALTH-INSURANCE]="$(vcf_escape "${_POST[hi_company]}" "${_POST[hi_number]}" "${_POST[hi_status]}")"
-
-sed -r 's;$;\r;' >"$tempfile" <<EOF
-BEGIN:VCARD
-VERSION:4.0
-N:$(vcf_escape "${_POST[0N]}" "${_POST[1N]}" "${_POST[2N]}" "${_POST[3N]}" "${_POST[4N]}")
-UID:${_POST[UID]}
-$(
- for field in $VCF_FIELDS; do for key in $field $field{0..100}; do
- [ -z "${_POST[$key]+x}" ] && break
- [ -z "${_POST[$key]}" ] && continue
- case "$key" in
- (TEL[0-9]*)
- printf '%s;TYPE=%s:%s\r\n' "${field}" "${_POST[phonetype${key#TEL}]}" "$(vcf_escape "${_POST[$key]}")"
- ;;
- (X-HEALTH-INSURANCE)
- printf '%s:%s\r\n' "${field}" "${_POST[$key]}"
- ;;
- (*)
- printf '%s:%s\r\n' "${field}" "$(vcf_escape "${_POST[$key]}")"
- ;;
- esac
- done; done
- [ "${_POST[action]}" = addfield ] && printf '%s:\r\n' "${_POST[newfield]}"
-)
-END:VCARD
-EOF
-
-case "${_POST[action]}" in
- addfield)
- redirect "?p=cards${filter}${filtertype}${order}&edit=$card"
- ;;
- update)
- attendance=()
- for att in attendance attendance{0..100}; do
- [ -n "${_POST[$att]}" ] && attendance+=("${_POST[$att]}")
- done
- sed -rn 's:^(.+)'$card'$:\1:p' "$attfile" |while read course; do
- touch "$_DATA/ical/$course"
- done
- sed -i -r '/^(.+)\t'$card'$/d' "$attfile"
- for each in $attendance; do
- echo "$each\t$card"
- done >>"$attfile"
- sed -rn 's:^(.+)'$card'$:\1:p' "$attfile" |while read course; do
- touch "$_DATA/ical/$course"
- done
-
- mv "$tempfile" "$cardfile"
- redirect "?p=cards${filter}${filtertype}${order}#$card"
- ;;
- cancel)
- rm "$tempfile"
- [ -f "$cardfile" ] \
- && redirect "?p=cards${filter}${filtertype}${order}#$card" \
- || redirect "?p=cards${filter}${filtertype}${order}"
- ;;
- delete)
- rm "$tempfile" "$cardfile"
- redirect "?p=cards${filter}${filtertype}${order}"
- ;;
-esac
+++ /dev/null
-#!/bin/zsh
-
-# Copyright 2014, 2015 Paul Hänsch
-#
-# This file is part of Confetti.
-#
-# Confetti is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# Confetti is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with Confetti. If not, see <http://www.gnu.org/licenses/>.
-
-cgi_post
-
-course="${_POST[course]}"
-tempfile="temp/$course"
-coursefile="ical/$course"
-attfile="$_DATA/mappings/attendance"
-
-# DURATION:
-uid="${_POST[UID]}"
-
-tzid=$(cat /etc/timezone)
-tstamp=$(TZ="$tzid" date +%Y%m%dT%H%M%S)
-
-dts_year="${_POST[DTSYEAR]}"
-dts_month="${_POST[DTSMONTH]}"
-dts_day="${_POST[DTSDAY]}"
-[ -n "${_POST[DTSDAY0]}" ] && dts_day="${_POST[DTSDAY0]}"
-dts_hour="${_POST[DTSHOUR]}"
-dts_minute="${_POST[DTSMINUTE]}"
-
-[ -z $dts_year ] && dts_year=$(date +%Y)
-[ -z $dts_month ] && dts_month=$(date +%m)
-[ -z $dts_day ] && dts_day=$(date +%d)
-date -d ${dts_year}-${dts_month}-${dts_day} >/dev/null 2>/dev/null || dts_day="01"
-[ -z $dts_hour ] && dts_hour=$(date +%H)
-[ -z $dts_minute ] && dts_minute=$(date +%M)
-
-dtstart="TZID=${tzid}:${dts_year}${dts_month}${dts_day}T${dts_hour}${dts_minute}00"
-
-rr_int="${_POST[RRULE_INTERVAL]}"
-rr_freq="${_POST[RRULE_FREQ]}"
-rr_limit="${_POST[RRULE_LIMIT]}"
-case "$rr_limit" in
- ETERN)
- rrule="FREQ=$rr_freq;INTERVAL=$rr_int"
- ;;
- COUNT)
- t="${_POST[RRULE_COUNT]}"
- rrule="FREQ=$rr_freq;INTERVAL=$rr_int;COUNT=$t"
- ;;
- UNTIL)
- uy="${_POST[RRULE_UYEAR]}"
- um="${_POST[RRULE_UMONTH]}"
- ud="${_POST[RRULE_UDAY]}"
- rrule="FREQ=$rr_freq;INTERVAL=$rr_int;UNTIL=${uy}${um}${ud}T000000Z"
- ;;
-esac
-
-echo "BEGIN:VCALENDAR\r" >"$tempfile"
-echo "VERSION:2.0\r" >>"$tempfile"
-echo "PRODID:Berlin RAW Confetti\r" >>"$tempfile"
-echo "BEGIN:VEVENT\r" >>"$tempfile"
-echo "UID:$uid\r" >>"$tempfile"
-echo "DTSTAMP:TZID=${tzid}:${tstamp}\r" >>"$tempfile"
-echo "DTSTART:${dtstart}\r" >>"$tempfile"
-echo "RRULE:${rrule}\r" >>"$tempfile"
-for field in SUMMARY COMMENT; do
- value="${_POST[$field]}"
- n=0
- while [ -n "$value" ]; do
- value="$(echo "$value" |sed -r ':a;N;$!ba;s:\n:\\\\n:g;s:\r:\\\\r:g')"
- echo "${field}:${value}\r"
- value="${_POST[$field$n]}"
- n=$(($n + 1))
- done
-done >>"$tempfile"
-
-case "${_POST[action]}" in
- addfield)
- echo "${_POST[newfield]}:\r" >>"$tempfile"
- echo "END:VEVENT\r" >>"$tempfile"
- echo "END:VCALENDAR\r" >>"$tempfile"
- echo -n "Location: ?p=courses&edit=$course\n\n"
- ;;
- update)
- attendance=()
- for att in attendance attendance{0..100}; do
- [ -n "${_POST[$att]}" ] && attendance+=("${_POST[$att]}")
- done
- sed -rn 's:^'$course'\t(.+)$:\1:p' "$attfile" |while read card; do
- touch "$_DATA/vcard/$card"
- done
- sed -i -r '/^'$course'\t(.+)$/d' "$attfile"
- for each in $attendance; do
- echo "$course\t$each"
- done >>"$attfile"
- sed -rn 's:^'$course'\t(.+)$:\1:p' "$attfile" |while read card; do
- touch "$_DATA/vcard/$card"
- done
-
- echo "END:VEVENT\r" >>"$tempfile"
- echo "END:VCALENDAR\r" >>"$tempfile"
- mv "$tempfile" "$coursefile"
- echo -n "Location: ?p=courses#$course\n\n"
- ;;
- cancel)
- rm "$tempfile"
- [ -f "$coursefile" ] \
- && echo -n "Location: ?p=courses#$course\n\n" \
- || echo -n "Location: ?p=courses\n\n"
- ;;
- delete)
- rm "$tempfile" "$coursefile"
- echo -n "Location: ?p=courses\n\n"
- ;;
- *)
- echo "END:VEVENT\r" >>"$tempfile"
- echo "END:VCALENDAR\r" >>"$tempfile"
- echo -n "Location: ?p=courses&edit=$course\n\n"
- ;;
-esac
+++ /dev/null
-#!/bin/zsh
-
-# Copyright 2016 Paul Hänsch
-#
-# This file is part of Confetti.
-#
-# Confetti is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# Confetti is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with Confetti. If not, see <http://www.gnu.org/licenses/>.
-
-BR='
-'
-prescription="${_POST[prescription]}"
-
-tempfile="$_DATA/temp/$prescription"
-prescfile="$_DATA/prescriptions/$prescription"
-client="${prescription%.*.mpx}.vcf"
-clientfile="$_DATA/vcard/$client"
-
-if [ -z "$prescription" -o \! -f "$clientfile" ]; then
- redirect "?p=error"
- exit 0
-fi
-
-[ "$_POST[issuer_select]" = "other" ] && _POST[issuer]="${_POST[issuer_other]}"
-
-# serialize POST array into file
-for key in ${(k)_POST}; do
- printf %s:%s\\n "$key" "${_POST[$key]//$BR/\\n}"
-done >"$tempfile"
-
-case "${_POST[action]}" in
- save)
- mv "$tempfile" "$prescfile"
- touch "$clientfile"
- ;;
- cancel)
- rm "$tempfile"
- ;;
- delete)
- rm "$tempfile" "$prescfile"
- touch "$clientfile"
- ;;
-esac
-
-redirect "?p=prescriptions&client=${client}#${prescription}"
+++ /dev/null
-#!/bin/zsh
-
-# Copyright 2016 Paul Hänsch
-#
-# This file is part of Confetti.
-#
-# Confetti is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# Confetti is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with Confetti. If not, see <http://www.gnu.org/licenses/>.
-
-BR='
-'
-tpy="${_POST[id]}"
-
-tpyfile="$_DATA/therapies/$tpy"
-tempfile="$_DATA/temp/$tpy"
-
-# serialize POST array into file
-for key in ${(k)_POST}; do
- [ "$key" != imagedata ] && printf %s:%s\\n "$key" "${_POST[$key]//$BR/\\n}"
-done >"$tempfile"
-
-if [ -n "$_POST[delete_session]" ]; then
- n="$_POST[delete_session]"
- sed -i -r '/^session'$n'[_:]/d' "$tempfile"
- rm "${tpyfile%.tpy}_session${n}.png"
-
- while grep -Eq '^session'$(($n + 1))'_' "$tempfile"; do
- sed -i -r 's;^session'$(($n + 1))'(_|:);session'$n'\1;' "$tempfile"
- mv "${tpyfile%.tpy}_session$(($n+1)).png" "${tpyfile%.tpy}_session${n}.png"
- n=$(($n+1))
- done
-
-elif [ -n "$_POST[new_session]" ]; then
- sid="$_POST[new_session]"
-
- identify "$_EXEC/static/therapy_background.png" \
- | sed -r 's;^.* ([0-9]+x[0-9]+) .*$;\1;' \
- | read dim
-
- convert -size "$dim" xc:transparent "${tpyfile%.tpy}_${sid}.png"
-
- printf '%s:exists\n' "$sid" >>"$tempfile"
- printf '%s_open:checked\n' "$sid" >>"$tempfile"
-
-elif [ -n "$_POST[imagedata]" ]; then
- sed -rn 's;^(session[0-9]+)_open:checked$;\1;p' "$tempfile" \
- | sort -n \
- | tail -n1 \
- | read sid
-
- convert "${tpyfile%.tpy}_${sid}.png" \
- -strokewidth 2 -fill '#00000000' \
- -draw "${_POST[imagedata]}" -transparent white \
- "${tpyfile%.tpy}_${sid}.png"
- sync
-fi
-
-mv "$tempfile" "$tpyfile"
-
-redirect "?p=therapy&id=${tpy}"
-#!/bin/zsh
+#!/bin/sh
-# Copyright 2014, 2017 Paul Hänsch
+# Copyright 2019 Paul Hänsch
#
# This file is part of Confetti.
#
# You should have received a copy of the GNU Affero General Public License
# along with Confetti. If not, see <http://www.gnu.org/licenses/>.
-cgi_post
+locktimeout=900
+. "$_EXEC"/session_lock.sh
-if [ "${_POST[choice]}" = new_filter ]; then
- filter="$(
- for n in filter{0..100}; do
- # [ -z "${_POST[$n]:+x}" ] && break
- printf %s "${_POST[$n]}"
- done \
- | sed -r 's;\|+;\|;g; s;\^+;\^;g; s;:\|;:;g; :X; s;\^[^:]*:\^;\^;g; /\^[^:]*:\^/bX; s;^\^;;; s;\^[^:]*:$;;;'
- )"
- printf 'Location: ?p=cards&order=%s&filter=%s\n\n' "${_POST[order]}" "$filter"
+card="$(GET card |PATH)"
+cardfile="$_DATA/vcard/${card##*/}"
+filter="$(REF f)"
+order="$(REF o)"
+
+if tempfile="$(SLOCK "$cardfile" "$locktimeout")"; then
+ REDIRECT "/cards/?o=${order}&f=${filter}&e=${card}"
+elif [ -f "$tempfile" ]; then
+ SET_COOKIE session message="SESSLOCK"
+ REDIRECT "/cards/?o=${order}&f=${filter}#${card}"
else
- printf 'Location: ?p=cards\n\n'
+ SET_COOKIE session message="EDITLOCK"
+ REDIRECT "/cards/?o=${order}&f=${filter}#${card}"
fi
-#!/bin/zsh
+#!/bin/sh
-# Copyright 2014 Paul Hänsch
+# Copyright 2014, 2015, 2021 Paul Hänsch
#
# This file is part of Confetti.
#
# You should have received a copy of the GNU Affero General Public License
# along with Confetti. If not, see <http://www.gnu.org/licenses/>.
-cgi_refdata
+card="$(GET card |PATH)"
+cardfile="$_DATA/vcard/${card##*/}"
-card="${_GET[card]}"
-filter="&filter=${_REF[filter]}"
-filtertype="&filtertype=${_REF[filtertype]}"
-order="&order=${_REF[order]}"
+. $_EXEC/pdiread.sh
+. $_EXEC/cgilite/file.sh
-echo -n "Location: ?p=cards${filter}${filtertype}${order}&edit=$card\n\n"
+printf 'Content-Disposition: inline; filename="%s.vcf"\r\n' "$(pdi_value "$(pdi_load "$cardfile")" FN)"
+
+FILE "$cardfile" "text/vcard; charset=utf-8"
--- /dev/null
+#!/bin/sh
+
+. $_EXEC/pdiread.sh
+. $_EXEC/cards/l10n.sh
+. $_EXEC/cards/list.sh
+
+upcase=' y;abcdefghijklmnopqrstuvwxyzäöüé;ABCDEFGHIJKLMNOPQRSTUVWXYZÄÖÜÉ;; '
+
+filter="$(GET f)"
+order="$(GET o)"
+
+[ "$order" ] || order=firstname
+
+list_attendance() {
+ grep -F " ${cardfile##*/}" "$_DATA/mappings/attendance" |while read each discard; do
+ { pdi_value "$(pdi_load "$_DATA/ical/$each")" SUMMARY || l10n "(unnamed course)"; } |unescape
+ done \
+ | sed -E 's;";\\";g;'
+}
+
+list_item() {
+ local item="$1"
+ local cnt="$(pdi_count "$card" "$item")"
+ local ret=''
+
+ seq 1 $cnt |while read n; do case $item in
+ TEL)
+ tel="$(pdi_value "$card" "$item" "$n" |unescape)"
+ ttype="$(pdi_attrib "$card" "$item" "$n" TYPE)"
+ if [ "$tel" -a "$ttype" ]; then
+ printf '%s: %s\n' "$(l10n "TYPE=$ttype")" "$tel"
+ elif [ "$tel" ]; then
+ printf '%s\n' "$tel"
+ fi
+ ;;
+ GENDER)
+ gen="$(pdi_value "$card" "$item" "$n" |unescape)"
+ [ "$gen" ] && l10n "gender_$gen"
+ ;;
+ *) pdi_value "$card" "$item" "$n" |unescape
+ ;;
+ esac; done \
+ | sed -E 's;";\\";g;'
+}
+
+printf '%s\r\n' \
+ 'Content-Type: text/csv; charset=utf-8' \
+ 'Content-Disposition: inline; filename="confetti_export_'$(date +%F_%T)'.csv"' \
+ ''
+
+printf '"%s";"%s";"%s";"%s";"%s";"%s";"%s";"%s";"%s"\n' \
+ "$(l10n FN)" "$(l10n GENDER)" "$(l10n BDAY)" \
+ "$(l10n TEL)" "$(l10n EMAIL)" "$(l10n ADR)" \
+ "$(l10n NOTE)" "$(l10n courses)" "$(l10n CATEGORIES)" \
+| sed -E 's;­\;;;g;'
+
+
+filter_cards \
+| order_cards \
+| while read cardfile; do
+ card="$(pdi_load "$cardfile")"
+ printf '"%s";"%s";"%s";"%s";"%s";"%s";"%s";"%s";"%s"\n' \
+ "$(list_item FN)" "$(list_item GENDER)" "$(list_item BDAY)" \
+ "$(list_item TEL)" "$(list_item EMAIL)" "$(list_item ADR)" \
+ "$(list_item NOTE)" "$(list_attendance)" "$(list_item CATEGORIES)"
+done
--- /dev/null
+#!/bin/sh
+
+# Copyright 2014, 2017, 2019 Paul Hänsch
+#
+# This file is part of Confetti.
+#
+# Confetti is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Confetti is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with Confetti. If not, see <http://www.gnu.org/licenses/>.
+
+filter="$(
+ seq 0 100 |while read n; do
+ filter_type="$(POST "filter_type${n}")"
+ filter_text="$(POST "filter_text${n}")"
+ if [ ! "$filter_type" -a ! "$filter_text" ]; then
+ break
+ elif [ "$filter_type" = CATEGORIES ]; then
+ printf '^CATEGORIES:'
+ seq 0 $(POST_COUNT filter_cat$n) |while read m; do
+ printf '|%s' "$(POST filter_cat$n $m)"
+ done
+ elif [ "$filter_type" = course ]; then
+ printf '^course:'
+ seq 0 $(POST_COUNT filter_course$n) |while read m; do
+ printf '|%s' "$(POST filter_course$n $m)"
+ done
+ else
+ printf '^%s:%s' "$filter_type" "$filter_text"
+ fi
+ done | sed -E \
+ 's;\|+;\|;g; s;\^+;\^;g; s;:\|;:;g;
+ :X; s;\^[^:]*:\^;\^;g; /\^[^:]*:\^/bX;
+ s;^\^;;; s;\^[^:]*:$;;;'
+)"
+
+case $(POST choice) in
+ filter)
+ REDIRECT "/cards/?o=$(POST order)&f=${filter}"
+ ;;
+ new_filter)
+ REDIRECT "/cards/?o=$(POST order)&f=${filter}&newfilter=yes"
+ ;;
+ export_csv)
+ REDIRECT "/cards/export_csv.sh?o=$(POST order)&f=${filter}"
+ ;;
+ *)
+ REDIRECT '/cards/'
+ ;;
+esac
--- /dev/null
+#!/bin/sh
+
+. $_EXEC/pdiread.sh
+. $_EXEC/cards/l10n.sh
+. $_EXEC/cards/widgets.sh
+. $_EXEC/cards/list.sh
+
+upcase=' y;abcdefghijklmnopqrstuvwxyzäöüé;ABCDEFGHIJKLMNOPQRSTUVWXYZÄÖÜÉ;; '
+
+filter="$(GET f)"
+order="$(GET o)"
+edit="$(GET e |PATH)"
+
+[ "$order" ] || order=firstname
+edit="${edit##*/}"
+
+{ w_filter_diag
+ printf '
+ [form class="newcard" action="/cards/new_card.sh" method="POST"
+ [button type="submit" %s]
+ [input name="seed" placeholder="%s"]
+ ]' "$(l10n newcard)" "$(l10n vcf_seed_label)"
+ [ "$edit" ] && edit_card "$edit"
+ list_cards
+} | yield_page cards #/cards/cards.css
--- /dev/null
+# Copyright 2014, 2016, 2019, 2021 Paul Hänsch
+#
+# This file is part of Confetti.
+#
+# Confetti is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Confetti is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with Confetti. If not, see <http://www.gnu.org/licenses/>.
+
+l10n(){
+ local word
+ [ $# -eq 0 ] && read -r word || word="$*"
+ case $word in
+ newcard) printf %s "Neuen Eintrag anlegen";;
+
+ X-HEALTH-INSURANCE) printf %s "Kran­ken­ver­sich­er­ung";;
+ hi_from_list) printf %s "Aus Liste";;
+ hi_other) printf %s "Andere";;
+ hi_company) printf %s "Ver­sich­er­ungs­ge­sell­schaft";;
+ hi_number) printf %s "Ver­sich­er­ten­num­mer";;
+ hi_status) printf %s "Ver­sich­er­ten­sta­tus";;
+ X-HEALTH-INSURANCE-NOCONTRIB) printf %s "Zu­zahl­ungs­be­frei­ung";;
+ X-CLIENT-REFERRAL) printf %s "Empfehl­ung durch";;
+ prescriptions) printf %s "Verord­nungen";;
+ new_prescription) printf %s "Neue Verord­nung";;
+ no_icd) printf %s "Kein ICD Code";;
+
+ X-ZACK-JOINDATE) printf %s "Anmelde­datum";;
+ X-ZACK-LEAVEDATE) printf %s "Abmelde­datum";;
+ X-ZACK-JOINDATE_short) printf %s "Anm.";;
+ X-ZACK-LEAVEDATE_short) printf %s "Abm.";;
+
+ *) l10n_global "$word";;
+ esac
+}
+
+# BEGIN) printf %s "";;
+# CALADRURI) printf %s "";;
+# CALURI) printf %s "";;
+# CLASS) printf %s "";;
+# CLIENTPIDMAP) printf %s "";;
+# END) printf %s "";;
+# FBURL) printf %s "";;
+# GEO) printf %s "";;
+# MAILER) printf %s "";;
+# NAME) printf %s "";;
+# PRODID) printf %s "";;
+# PROFILE) printf %s "";;
+# REV) printf %s "";;
+# SORT-STRING) printf %s "";;
+# SOURCE) printf %s "";;
+# TZ) printf %s "";;
+# UID) printf %s "";;
+# VERSION) printf %s "";;
+# XML) printf %s "";;
--- /dev/null
+#!/bin/sh
+
+. "${_EXEC}"/pdiread.sh
+
+edit_card(){
+ local cardfile="$_DATA/vcard/$1"
+ local tempfile card
+
+ . $_EXEC/session_lock.sh
+
+ if ! tempfile="$(CHECK_SLOCK "$cardfile")"; then
+ printf '[div .message %s]' "$(l10n "This card is not set up for editing within this session.")"
+ else
+ card="$(pdi_load "$tempfile")"
+ cat <<-EOF
+ [form .card #${cardfile##*/} action="/cards/update_card.sh" method="POST"
+ [input type="hidden" name="tid" value="$(transid ${tempfile})"]
+ [div .section .basic $(
+ edit_item "$card" N GENDER
+ [ "$(pdi_count "$card" NICKNAME)" -gt 0 ] \
+ && edit_item "$card" NICKNAME
+ edit_item "$card" BDAY
+ edit_item "$card" X-ZACK-JOINDATE
+ [ "$(pdi_count "$card" X-ZACK-LEAVEDATE)" -gt 0 ] \
+ && edit_item "$card" X-ZACK-LEAVEDATE
+ card_item "$card" SOUND PHOTO LOGO
+ )]
+ [div .section .phone $(edit_item "$card" TEL)]
+ [div .section .message $(
+ edit_item "$card" EMAIL
+ [ $(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 .note $(edit_item "$card" NOTE)]
+ [div .section .attendance
+ [h3 $(l10n course_attendance) ] $(
+ for course in "$_DATA"/ical/*.ics; do
+ printf '[label [input type="checkbox" name="attendance" value="%s" %s] %s]' \
+ "${course##*/}" \
+ "$(grep -qF "${course##*/} ${cardfile##*/}" "$_DATA/mappings/attendance" \
+ && printf 'checked="checked"'
+ )" \
+ "$(pdi_value "$(pdi_load "$course")" SUMMARY || l10n "(unnamed course)" |unescape |HTML)"
+ done)
+ [h3 $(l10n CATEGORIES) ] $(
+ grep -xE '[^ ]+' "$_DATA"/mappings/categories |while read -r cat; do
+ printf '[label [input type="checkbox" name="CATEGORIES" value="%s" %s] %s]' \
+ "$(HTML "$cat")" \
+ "$(seq 1 $(pdi_count "$card" CATEGORIES) |while read c; do
+ pdi_value "$card" CATEGORIES $c |grep -qxF "$cat" \
+ && printf 'checked="checked"' && break
+ done)" \
+ "$(HTML "$cat")"
+ done)
+ ]
+ [div .control
+ [div .item .delete label="$(l10n edit_delete)"
+ [input type="checkbox" #delete]
+ [label for="delete" $(l10n edit_delete)]
+ [button type="submit" name="action" value="delete" $(l10n edit_delete)]
+ ]
+ [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; do
+ printf '[option value="%s" %s] ' "$f" "$(l10n "$f")"
+ done)
+ ][button type="submit" name="action" value="addfield" $(l10n edit_addfield)]
+ ]
+ [button .item type="submit" name="action" value="update" $(l10n edit_update)]
+ [button .item type="submit" name="action" value="cancel" $(l10n edit_cancel)]
+ ]
+ [input type="hidden" name="UID" value="$(pdi_value "$card" UID |HTML)"]
+ [input type="hidden" name="card" value="${cardfile##*/}"]
+ ]
+ EOF
+ fi
+}
+
+print_card(){
+ local cardfile="$1"
+ local card="$(pdi_load "$cardfile")"
+ cat <<-EOF
+ [div .card #${cardfile##*/}
+ [div .section .basic . $(
+ card_item "$card" FN GENDER NICKNAME BDAY X-ZACK-JOINDATE X-ZACK-LEAVEDATE SOUND PHOTO LOGO
+ )]
+ [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 .note . $(card_item "$card" NOTE)]
+ [div .section .attendance [h3 $(l10n course_attendance) ] [ul
+ $(grep -F " ${cardfile##*/}" "$_DATA/mappings/attendance" |while read each discard; do
+ printf '[li [a .item .attendance href="/courses#%s" . %s]]' \
+ "$each" \
+ "$(pdi_value "$(pdi_load "$_DATA/ical/$each")" SUMMARY || l10n "(unnamed course)" |unescape |HTML)"
+ done)]
+ $(card_item "$card" CATEGORIES)
+ ]
+ [div .control
+ [a .item href="/cards/edit_card.sh?card=${cardfile##*/}" $(l10n edit)]
+ [a .item href="/cards/export_card.sh?card=${cardfile##*/}" $(l10n vcf_export)]
+ ]
+ ]
+ EOF
+}
+
+print_cards(){
+ local cardfile cachefile date size name ldate=0 lsize lname
+
+ while read cardfile; do
+ cachefile="${_DATA}/cache/${cardfile##*/}.cache"
+ # if [ -s "$cachefile" -a "$cachefile" -nt "$cardfile" \
+ # -a "$cachefile" -nt "${_EXEC}/cards" ]; then
+ if [ -s "$cachefile" -a "$cachefile" -nt "$cardfile" ]; then
+ cat "$cachefile"
+ else
+ print_card "$cardfile" |tee "$cachefile"
+ fi
+ done
+}
+
+filter_attendance(){
+ fatt="$1"
+ attfile="$_DATA/mappings/attendance"
+
+ if [ ! "$fatt" ]; then
+ # debug 'list all'
+ printf '%s\n' "$_DATA/vcard"/*.vcf
+ elif [ "${fatt#* }" = "${fatt}" ]; then
+ # debug "list $fatt"
+ grep -xiE "(${fatt}) .+vcf" "$attfile" \
+ | while read vcf; do
+ printf '%s/vcard/%s\n' "$_DATA" "${vcf##* }"
+ done
+ else
+ # debug "filter ${fatt%% *}"
+ filter_attendance "${fatt#* }" \
+ | while read vcf; do
+ grep -xiE "(${fatt%% *}) ${vcf##*/}" "$attfile"
+ done \
+ | while read vcf; do
+ printf '%s/vcard/%s\n' "$_DATA" "${vcf##* }"
+ done
+ fi
+}
+
+filter_cards(){
+ local filter f fex='x;p;'
+
+ filter="$(printf %s "${filter}" \
+ | sed -E 's;[]\/\(\)\\\$\?\.\+\*\;\[\{\}];\\&;g;
+ '"$upcase"
+ )^"
+
+ while [ "$filter" ]; do
+ f="${filter%%^*}" filter="${filter#*^}"
+ case $f in
+ '') break
+ ;;
+ COURSE:*) fatt="${fatt}${fatt:+ }${f#*:}"
+ ;;
+ ANY:*) fex="/\n.*(\;[^:]*)?:[^\n]*(${f#*:})[^\n]*\r?\n/{${fex}}"
+ ;;
+ NAME:*) fex="/\n(N|FN|NICKNAME)(\;[^:]*)?:[^\n]*(${f#*:})[^\n]*\r?\n/{${fex}}"
+ ;;
+ STREET:*|ZIP:*) fex="/\nADR(\;[^:]*)?:[^\n]*(${f#*:})[^\n]*\r?\n/{${fex}}"
+ ;;
+ *) fex="/\n${f%%:*}(\;[^:]*)?:[^\n]*(${f#*:})[^\n]*\r?\n/{${fex}}"
+ ;;
+ esac
+ done
+
+ # for cardfile in "${_DATA}"/vcard/*.vcf; do
+ filter_attendance "$fatt" |while read cardfile; do
+ printf '%s\n' "$cardfile"
+ cat "$cardfile"
+ done \
+ | sed -nE ':X; /\nEND\;?:VCARD\r?$/!{ N; bX; }; h; s;\n.*$;;; x; s;^[^\n]+\n;;;
+ '"$upcase""$fex"
+}
+
+order_cards() {
+ local cardfile card
+
+ while read cardfile; do
+ card="$(pdi_load "$cardfile")"
+
+ case $order in
+ firstname)
+ printf '%s %s\n' "$(pdi_value "$card" FN)" "$cardfile"
+ ;;
+ lastname)
+ printf '%s %s\n' "$(pdi_value "$card" N || pdi_value "$card" FN)" "$cardfile"
+ ;;
+ bdate)
+ printf '%s %s\n' "$(pdi_value "$card" BDAY || printf 0000-00-00)" "$cardfile"
+ ;;
+ esac
+ done \
+ | sort \
+ | sed -E 's;^.*\t;;g'
+}
+
+list_cards(){
+ filter_cards \
+ | order_cards \
+ | grep -xvF "$edit" \
+ | print_cards
+}
--- /dev/null
+#!/bin/sh
+
+# Copyright 2014, 2019 Paul Hänsch
+#
+# This file is part of Confetti.
+#
+# Confetti is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Confetti is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with Confetti. If not, see <http://www.gnu.org/licenses/>.
+
+filter="$(REF f)"
+order="$(REF o)"
+
+uid="$(timeid)$(randomid)" # 32 Octets UID, starting with timestamp
+card="${uid}.vcf"
+
+vcf_escape(){
+ for each in "$@"; do
+ printf %s\\n "$each" \
+ | sed -E ':X;$!{N;bX}; s;\r\n;\n;g; s;([;,\\]);\\\1;g; s;\n;\\n;g;'
+ done \
+ | sed -E ':X;$!{N;bX}; s;\n;\;;g'
+}
+
+IFS='|' read -r date fn ln bmonth byear tel tcell junk1 email junk2 note <<-EOF
+ $(POST seed |tr \\t \|)
+ EOF
+
+[ ${#byear} = 1 ] && byear="200$byear"
+[ ${#byear} = 2 ] && byear="20$byear"
+[ ${#bmonth} = 1 ] && bmonth="0$bmonth"
+
+mn=""
+case $fn in
+ *\ *)
+ mn="${fn#* }"
+ fn="${fn%% *}"
+ ;;
+esac
+
+mkdir -p "${_DATA}/lock/vcard/"
+lockdir="${_DATA}/lock/vcard/${card}/"
+lockfile=${lockdir}/${SESSION_ID}
+
+if mkdir "$lockdir"; then
+ cat >"$lockfile" <<-EOF
+ BEGIN:VCARD
+ VERSION:4.0
+ N:$(vcf_escape "$ln" "$fn" "$mn" "" "")
+ FN:$(vcf_escape "${fn}${mn:+ }${mn} ${ln}")
+ BDAY:$(parse_date "${byear}-${bmonth}-01")
+ TEL:$(vcf_escape "$tel")
+ TEL;TYPE=CELL:$(vcf_escape "$tcell")
+ EMAIL:$(vcf_escape "$email")
+ X-ZACK-JOINDATE:$(parse_date "$date")
+ ADR:
+ NOTE:$(vcf_escape "$note")
+ UID:${uid}
+ END:VCARD
+ EOF
+ REDIRECT "/cards/?o=${order}&f=${filter}&e=${card}"
+else
+ SET_COOKIE session message="EDITLOCK"
+ REDIRECT "/cards/?o=${order}&f=${filter}"
+fi
--- /dev/null
+#!/bin/sh
+
+# Copyright 2014, 2016, 2019, 2020, 2021 Paul Hänsch
+#
+# This file is part of Confetti.
+#
+# Confetti is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Confetti is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with Confetti. If not, see <http://www.gnu.org/licenses/>.
+
+. "$_EXEC/pdiread.sh"
+. "$_EXEC/session_lock.sh"
+. "$_EXEC/cgilite/storage.sh"
+
+unset filter order card action newfield
+unset cardfile attfile tempfile
+unset vcf field cnt delete_key
+
+filter="$(REF f)"
+order="$(REF o)"
+
+card="$(POST card |PATH)"; card="${card##*/}"
+cardfile="$_DATA/vcard/${card}"
+attfile="$_DATA/mappings/attendance"
+
+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
+ newfield="${action##* }"
+ action=addfield
+fi
+
+if ! tempfile=$(CHECK_SLOCK "$cardfile"); then
+ SET_COOKIE 0 message="NO VALID FILE LOCK"
+ REDIRECT "/cards/?o=${order}&f=${filter}&e=${card}"
+ exit 0
+elif [ "$(POST tid)" != "$(transid "$tempfile")" ]; then
+ SET_COOKIE 0 message="INVALID TRANSACTION ID"
+ REDIRECT "/cards/?o=${order}&f=${filter}&e=${card}"
+ exit 0
+fi
+
+vcf_escape(){
+ for each in "$@"; do
+ printf %s\\n "$each" \
+ | sed -E ':X;$!{N;bX}; s;\r\n;\n;g; s;([;,\\]);\\\1;g; s;\n;\\n;g;'
+ done \
+ | sed -E ':X;$!{N;bX}; s;\n;\;;g'
+}
+
+# [ "${_POST[hi_select]}" = "list" ] || _POST[hi_company]="${_POST[hi_other]}"
+# [ -n "${_POST[hi_company]}${_POST[hi_number]}${_POST[hi_status]}" ] \
+# && _POST[X-HEALTH-INSURANCE]="$(vcf_escape "${_POST[hi_company]}" "${_POST[hi_number]}" "${_POST[hi_status]}")"
+
+vcf="$(pdi_load "$tempfile")"
+
+n1="$(POST 1N)" n2="$(POST 2N)" n3="$(POST 3N)" n4="$(POST 4N)" n5="$(POST 5N)"
+# 3N (Middle Names) is not actually used
+n3="${n2#${n2%% *}}"
+
+vcf="$(pdi_update_value "$vcf" N 1 "$(vcf_escape "$n1" "${n2%% *}" "${n3# }" "$n4" "$n5")")"
+vcf="$(pdi_update_value "$vcf" FN 1 "$(vcf_escape "$n4 $n2 $n1 $n5" |sed -E 's;(^ +| +$);;g; s; +; ;g;')")"
+vcf="$(printf '%s\n' "$vcf" |sed -E "/^CATEGORIES;[^:]*:.*$/d")"
+
+for field in $(POST_KEYS |grep -xE '[A-Z][A-Z0-9-]*'); do
+ for cnt in $(seq 1 $(POST_COUNT "$field")); do
+ case "$field" in
+ # (TEL)
+ # printf '%s;TYPE=%s:%s\r\n' "${field}" "${_POST[phonetype${key#TEL}]}" "$(vcf_escape "$(POST "$field" "$cnt")")"
+ # ;;
+ TEL)
+ vcf="$(pdi_update_attrib "$vcf" TEL $cnt TYPE="$(POST teltype $cnt |grep -Exm1 'HOME|WORK|CELL|FAX')")"
+ vcf="$(pdi_update_value "$vcf" "$field" "$cnt" "$(vcf_escape "$(POST "$field" "$cnt")")")"
+ ;;
+ *)
+ vcf="$(pdi_update_value "$vcf" "$field" "$cnt" "$(vcf_escape "$(POST "$field" "$cnt")")")"
+ ;;
+ esac
+done; done
+
+# delete fields, first mark for deletion using delete_key
+# this way the field enumeration is preserved during the process
+# finally filter marked lines
+delete_key="$(randomid)"
+for delete in $(POST_KEYS |grep -xE '[A-Z][A-Z0-9-]*_delete_[0-9]+'); do
+ f="${delete%%_*}"; c="${delete##*_}";
+ [ "$(POST "$delete")" = "true" ] && vcf="$(pdi_update_value "$vcf" "$f" "$c" "delete=${delete_key}")"
+done
+vcf="$(printf '%s\n' "$vcf" |sed -E "/^[^:]+:delete=${delete_key}\$/d")"
+
+if [ "$action" = addfield ]; then
+ vcf="$(pdi_update_value "$vcf" "$newfield" $(( $(pdi_count "$vcf" "$newfield") + 1 )) '')"
+fi
+printf '%s' "$vcf" |grep -vx '' >"$tempfile"
+
+case "$action" in
+ addfield)
+ REDIRECT "/cards/?o=${order}&f=${filter}&e=${card}"
+ ;;
+ update)
+ if LOCK "$attfile"; then
+ grep -F " ${card}" "$attfile" |while read course junk; do
+ touch "$_DATA/ical/${course}"
+ done
+ sed -i -E "/^.+ ${card}\$/d" "$attfile"
+ seq 1 $(POST_COUNT attendance) |while read n; do
+ printf '%s %s\n' "$(POST attendance $n)" "$card"
+ done >>"$attfile"
+ grep -F " ${card}" "$attfile" |while read course junk; do
+ touch "$_DATA/ical/${course}"
+ done
+ RELEASE "$attfile"
+ else
+ SET_COOKIE 0 message="COULD NOT UPDATE COURSE MAPPINGS"
+ fi
+
+ cp "$tempfile" "$cardfile"
+ RELEASE_SLOCK "$cardfile"
+ REDIRECT "/cards/?o=${order}&f=${filter}#${card}"
+ ;;
+ cancel)
+ RELEASE_SLOCK "$cardfile"
+ [ -f "$cardfile" ] \
+ && REDIRECT "/cards/?o=${order}&f=${filter}#${card}" \
+ || REDIRECT "/cards/?o=${order}&f=${filter}"
+ ;;
+ delete)
+ rm "$cardfile"
+ RELEASE_SLOCK "$cardfile"
+ if LOCK "$attfile"; then
+ grep -F " ${card}" "$attfile" |while read course junk; do
+ touch "$_DATA/ical/${course}"
+ done
+ sed -i -E "/^.+ ${card}\$/d" "$attfile"
+ RELEASE "$attfile"
+ else
+ SET_COOKIE 0 message="COULD NOT UPDATE COURSE MAPPINGS"
+ fi
+ REDIRECT "/cards/?o=${order}&f=${filter}"
+ ;;
+esac
--- /dev/null
+# Copyright 2014 - 2019, 2021 Paul Hänsch
+#
+# This file is part of Confetti.
+#
+# Confetti is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Confetti is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with Confetti. If not, see <http://www.gnu.org/licenses/>.
+
+list_categories() {
+ grep -vxE '^[ ]*$' "${_DATA}/mappings/categories"
+}
+
+list_courses() {
+ local file name cachefile="${_DATA}/cache/courses.ui.cache"
+ if [ $cachefile -nt "${_DATA}/ical" ]; then
+ cat "$cachefile"
+ else
+ for file in "$_DATA/ical"/*.ics; do
+ name="$(pdi_value "$(pdi_load "$file")" SUMMARY |HTML)"
+ printf '%s %s\n' "$file" "$name"
+ done \
+ | sort -k2 |tee "$cachefile"
+ fi
+}
+
+w_filter_item() {
+n=$3
+cat <<EOF
+ [fieldset .item
+ [legend $(l10n filter_item):]
+
+ $(for field in any name street zip TEL BDAY CATEGORIES course; do
+ printf '[input id="%s%i" type="radio" name="filter_type%i" value="%s" %s][label for="%s%i" %s ]' \
+ "$field" "$n" "$n" "$field" "$([ "$1" = "$field" ] && printf checked )" \
+ "$field" "$n" "$(l10n filter_$field)"
+ done)
+ [input type="text" name="filter_text$n" value="$([ "$1" = CATEGORIES -o "$1" = course ] || HTML "$2")" placeholder="$(l10n filter_placeholder)"]
+ [fieldset .categories
+ $(list_categories | while read cat; do
+ printf '[label [checkbox "filter_cat%i" "|%s" %s] %s ] ' \
+ "$n" "$(HTML "$cat")" \
+ "$(printf %s "$cat" |grep -qxEe "$2" && printf checked )" \
+ "$(HTML "$cat")"
+ done)
+ [a href="/categories/" $(l10n edit_categories)]
+ ]
+ [fieldset .courses
+ $(list_courses | while read course coursename; do
+ printf '[label [checkbox "filter_course%i" "|%s" %s] %s ] ' \
+ "$n" "$(HTML "${course##*/}")" \
+ "$(printf %s "${course##*/}" |grep -qxEe "$2" && printf checked )" \
+ "$coursename"
+ done)
+ ]
+ ]
+EOF
+}
+
+w_filter_diag(){
+ cat <<EOF
+ [form .filter action="/cards/filter_card.sh" method="POST"
+ [h1 $(l10n filter_label)]
+ [input type="hidden" name="page" value="cards"]
+
+ $(n=0; filter="${filter}^"
+ while [ "${filter#^}" ]; do
+ fil="${filter%%^*}" filter="${filter#*^}"
+ w_filter_item "${fil%%:*}" "${fil#*:}" $n
+ n=$((n + 1))
+ done
+ [ "$n" -eq 0 -o "$(GET newfilter)" ] && w_filter_item any '' $n
+ )
+ [button type="submit" name="choice" value="new_filter" $(l10n filter_more)]
+ [fieldset class="order"
+ [legend $(l10n filter_order):]
+ [label [radio "order" "firstname" $( [ "$order" = firstname ] && printf checked )] $(l10n filter_firstname)]
+ [label [radio "order" "lastname" $( [ "$order" = lastname ] && printf checked )] $(l10n filter_lastname)]
+ [label [radio "order" "bdate" $( [ "$order" = bdate ] && printf checked )] $(l10n filter_bdate)]
+ ]
+ [button type="submit" name="choice" value="filter" $(l10n filter_apply)]
+ [button type="submit" name="choice" value="del_filter" $(l10n filter_cancel)]
+ [button type="submit" name="choice" value="export_csv" $(l10n export_csv)]
+ ]
+EOF
+}
+
+# listcards |grep ${edit:+-v} "$edit" \
+# | while read card; do
+# "${_EXEC}"/cgilite/html-sh.sed <<-ENDCARD
+# [div #${card} .card
+# $(view_card "$card")[!--
+# --][div .control
+# [a "?action=edit_card&card=${card}" .item $(l10n edit)]
+# [a "?action=export_vcard&card=${card}".item $(l10n vcf_export)]
+# ${profile_medical:+[a "?action=new_prescription&client=${card}" .item $(l10n new_prescription)]}
+# ]]
+# ENDCARD
+# done
+
+#!/bin/sh
+
+card_item(){
+ local card="$1"
+ local item cnt c
+ shift 1
+
+ for item in $@; do
+ cnt="$(pdi_count "$card" "$item")"
+
+ case $item in
+ FN) printf '[h2 .item .FN . %s]' "$(pdi_value "$card" FN |unescape |HTML)"
+ ;;
+ GENDER) printf '[span .item .GENDER . %s]' "$(pdi_value "$card" GENDER |l10n)"
+ ;;
+ NICKNAME) seq 1 $cnt |while read c; do
+ printf '[span .item .NICKNAME . aka. "%s"]' \
+ "$(pdi_value "$card" NICKNAME $c |unescape |HTML)"
+ done
+ ;;
+ X-ZACK-JOINDATE|X-ZACK-LEAVEDATE) if [ $cnt -gt 0 ]; then
+ printf '[span .item .%s [b %s:] %s]' \
+ "$item" "$(l10n "${item}_short")" \
+ "$(pdi_value "$card" "$item" |HTML)"
+ fi
+ ;;
+ BDAY)
+ [ $cnt -gt 0 ] && printf '[span .item .BDAY [b *:] %s]' \
+ "$(pdi_value "$card" BDAY |grep -xE '[0-9-]+')"
+ ;;
+ SOUND)
+ [ $cnt -gt 0 ] && printf '[audio .item .SOUND controls="controls"
+ [source type="audio/ogg" src="data:audio/ogg;base64,%s"]
+ ]' \
+ "$(pdi_value "$card" SOUND |grep -xE '[a-zA-Z0-9/+=]+')"
+ ;;
+ PHOTO|LOGO)
+ [ $cnt -gt 0 ] && printf '[img .item .%s src="data:image/%s;base64,%s"]' "$item" \
+ "$(pdi_attrib "$card" "$item" |sed -E 's;^(.*;)?TYPE="?(.+)"?(;.*)?$;\2;')" \
+ "$(pdi_value "$card" "$item" |grep -xE '[a-zA-Z0-9/+=]+')"
+ ;;
+ EMAIL)
+ [ $cnt -gt 0 ] && printf '[h3 %s]' "$(l10n EMAIL)"
+ seq 1 $cnt |while read c; do
+ printf '[a .item .EMAIL href="mailto:%s" . %s]' \
+ "$(pdi_value "$card" EMAIL $c |unescape |HTML)" \
+ "$(pdi_value "$card" EMAIL $c |unescape |HTML)"
+ done
+ ;;
+ TEL)
+ [ $cnt -gt 0 ] && printf '[h3 %s]' "$(l10n TEL)"
+ seq 1 $cnt |while read c; do
+ teltype="$(pdi_attrib "$card" TEL $c TYPE)"
+ [ "$teltype" ] \
+ && printf '[span .item .TEL [span .type . %s:] %s]' \
+ "$(l10n "TYPE=$teltype" |HTML)" \
+ "$(pdi_value "$card" TEL $c |unescape |HTML)" \
+ || printf '[span .item .TEL . %s]' \
+ "$(pdi_value "$card" TEL $c |unescape |HTML)"
+ done
+ ;;
+ *)[ $cnt -gt 0 ] && printf '[h3 %s]' "$(l10n "$item")"
+ seq 1 $cnt |while read c; do
+ printf '[span .item .%s . %s]' "$item" \
+ "$(pdi_value "$card" "$item" $c |unescape |HTML)"
+ done
+ ;;
+ esac
+ done
+}
+
+edit_item(){
+ local card="$1"
+ local item cnt c
+ shift 1
+
+ for item in $@; do
+ cnt="$(pdi_count "$card" "$item")"
+ [ $cnt -lt 1 ] && cnt=1
+
+ case $item in
+ N)N="$(pdi_value "$card" N)"
+ if [ "$N" ]; then
+ IFS=\; read n1 n2 n3 n4 n5 <<-EOF
+ $N
+ EOF
+ else
+ N="$(pdi_value "$card" FN |unescape)"
+ n1="${N##* }"
+ n2="${N%$n1}"
+ fi
+ printf '
+ [h3 %s]
+ [input .item .N name="4N" placeholder="%s" value="%s"]
+ [input .item .N name="2N" placeholder="%s" value="%s"]
+ [input .item .N name="1N" placeholder="%s" value="%s"]
+ [input .item .N name="5N" placeholder="%s" value="%s"]
+ ' "$(l10n "$item")" \
+ "$(l10n n_pre)" "$(HTML "$n4")" \
+ "$(l10n n_first)" "$(HTML "${n2}$([ "$n2" -a "$n3" ] && printf ' ')${n3}")" \
+ "$(l10n n_last)" "$(HTML "$n1")" \
+ "$(l10n n_post)" "$(HTML "$n5")"
+ ;;
+ GENDER)
+ gender="$(pdi_value "$card" GENDER)"
+ printf '
+ [select .item .GENDER name="GENDER"
+ [option value="" disabled="disabled" %s %s]
+ [option value="female" %s %s]
+ [option value="male" %s %s]
+ [option value="other" %s %s]
+ [option value="none" %s %s]
+ ]\n' \
+ "$([ "$gender" = '' ] && printf 'selected="selected"')" "$(l10n GENDER)" \
+ "$([ "$gender" = 'female' ] && printf 'selected="selected"')" "$(l10n gender_female)" \
+ "$([ "$gender" = 'male' ] && printf 'selected="selected"')" "$(l10n gender_male)" \
+ "$([ "$gender" = 'other' ] && printf 'selected="selected"')" "$(l10n gender_other)" \
+ "$([ "$gender" = 'none' ] && printf 'selected="selected"')" "$(l10n gender_none)"
+ ;;
+ BDAY|X-ZACK-JOINDATE|X-ZACK-LEAVEDATE)
+ printf '[h3 %s]
+ [input .item .%s name="%s" value="%s" placeholder="YYYY-MM-DD"]' \
+ "$(l10n "$item")" "$item" "$item" "$(pdi_value "$card" "$item" |grep -xE '[0-9-]+')"
+ ;;
+ ADR|NOTE)
+ printf '[h3 %s]' "$(l10n "$item")"
+ seq 1 $cnt |while read c; do
+ printf '[checkbox "%s_delete_%i" "true" .delete #%s_delete_%i][label for="%s_delete_%i" %s]' \
+ "$item" $c "$item" $c "$item" $c "$(l10n delete)"
+ printf '<textarea class="item %s" name="%s">%s</textarea>' \
+ "$item" "$item" "$(pdi_value "$card" "$item" $c |unescape |HTML)"
+ done
+ printf '[button type="submit" name="action" value="addfield %s" %s ]' "$item" "$(l10n edit_addfield)"
+ ;;
+ TEL) printf '[h3 %s]' "$(l10n "$item")"
+ seq 1 $cnt |while read c; do
+ printf '[checkbox "%s_delete_%i" "true" .delete #%s_delete_%i][label for="%s_delete_%i" %s]' \
+ "$item" $c "$item" $c "$item" $c "$(l10n delete)"
+ teltype="$(pdi_attrib "$card" TEL $c TYPE)"
+ printf '[select .item .teltype name="teltype"
+ [option value="" disabled="disabled" %s %s]
+ [option value="HOME" %s %s]
+ [option value="WORK" %s %s]
+ [option value="CELL" %s %s]
+ [option value="FAX" %s %s]
+ ]\n' \
+ "$([ "$teltype" = '' ] && printf 'selected="selected"')" "$(l10n teltype)" \
+ "$([ "$teltype" = 'HOME' ] && printf 'selected="selected"')" "$(l10n TYPE=HOME)" \
+ "$([ "$teltype" = 'WORK' ] && printf 'selected="selected"')" "$(l10n TYPE=WORK)" \
+ "$([ "$teltype" = 'CELL' ] && printf 'selected="selected"')" "$(l10n TYPE=CELL)" \
+ "$([ "$teltype" = 'FAX' ] && printf 'selected="selected"')" "$(l10n TYPE=FAX)"
+
+ printf '[input .item .%s name="%s" value="%s" placeholder="%s"]' \
+ "$item" "$item" "$(pdi_value "$card" "$item" $c |unescape |HTML)" "$(l10n "$item")"
+ done
+ printf '[button type="submit" name="action" value="addfield %s" %s ]' "$item" "$(l10n edit_addfield)"
+ ;;
+ *)printf '[h3 %s]' "$(l10n "$item")"
+ seq 1 $cnt |while read c; do
+ printf '[checkbox "%s_delete_%i" "true" .delete #%s_delete_%i][label for="%s_delete_%i" %s]' \
+ "$item" $c "$item" $c "$item" $c "$(l10n delete)"
+ printf '[input .item .%s name="%s" value="%s" placeholder="%s"]' \
+ "$item" "$item" "$(pdi_value "$card" "$item" $c |unescape |HTML)" "$(l10n "$item")"
+ done
+ printf '[button type="submit" name="action" value="addfield %s" %s ]' "$item" "$(l10n edit_addfield)"
+ ;;
+ esac
+ done
+}
catfile="${_DATA}/mappings/categories"
-remove="${_POST[remove]}"
-newcat="${_POST[newcat]}"
+remove="$(POST remove)"
+newcat="$(POST newcat)"
-if [ "${_POST[add]}" = "add" ]; then
- printf %s\\n "$newcat" >>"$catfile"
-elif [ -n "$remove" ]; then
- sed -ri '/^'"${remove}"'$/d' $catfile
+if [ "$(POST add)" = "add" ]; then
+ categories="$( {
+ cat "$catfile"
+ printf %s\\n "$newcat"
+ } |sort -u )"
+ printf %s\\n "$categories" >"$catfile"
+elif [ "$remove" ]; then
+ sed -E -i '/^'"${remove}"'$/d' "$catfile"
fi
-redirect "?p=categories"
+REDIRECT "/categories/"
--- /dev/null
+#!/bin/sh
+# Copyright 2015, 2017, 2018, 2021 Paul Hänsch
+#
+# This file is part of Confetti.
+#
+# Confetti is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Confetti is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with Confetti. If not, see <http://www.gnu.org/licenses/>.
+
+. $_EXEC/pdiread.sh
+. $_EXEC/categories/l10n.sh
+
+catfile="${_DATA}/mappings/categories"
+
+list_categories() {
+ grep -vxE '[ ]*' "$catfile" |sort -u
+}
+
+list_catsel(){
+ local vcf="$1" card="$2" n=1 cats="${BR}"
+ while cats="${cats}${BR}$(pdi_value "$vcf" CATEGORIES $n)"; do n=$((n + 1)); done
+
+ list_categories |while read cat; do
+ printf '[li [label [input %s type="checkbox" name="%s" value="%s"] %s]]' \
+ "$([ "${cats%*${BR}${cat}${BR}*}" != "$cats" ] && printf checked=checked)" \
+ "$(HTML "$card")" "$(HTML "$cat")" "$(HTML "$cat")"
+ done
+}
+
+cat <<EOF |yield_page categories
+[form .categories action="edit_categories.sh" method="POST"
+ [h1 $(l10n categories_label)]
+ [input type="hidden" name="page" value="categories"]
+ [ul
+ $(list_categories | while read cat; do
+ printf '[li . %s [button type="submit" name="remove" value="%s" . %s]]\n' \
+ "$(HTML "$cat")" "$(HTML "$cat")" "$(l10n cat_remove)"
+ done)
+ [li
+ [input type="text" name="newcat" placeholder="$(l10n cat_newlabel)"]
+ [button type="submit" name="add" value="add" . $(l10n cat_add)]
+ ]
+ ]
+]
+
+[form .namelist action="update_categories.sh" method="POST"
+ [fieldset
+ [button type="submit" name="submit" value="submit" . $(l10n cat_update)]
+ ]
+ [ul .namelist
+ $(for vcffile in "$_DATA"/vcard/*vcf; do
+ vcf="$(pdi_load "$vcffile")"
+ printf ' [li [h2 . %s][ul ' "$(pdi_value "$vcf" FN)"
+ list_catsel "$vcf" "${vcffile##*/}"
+ printf ']]\n'
+ done |sort)
+ ]
+ [fieldset
+ [button type="submit" name="submit" value="submit" . $(l10n cat_update)]
+ ]
+]
+EOF
-#!/bin/zsh
-
-# Copyright 2014,2015 Paul Hänsch
+# Copyright 2014, 2015, 2016, 2019, 2021 Paul Hänsch
#
# This file is part of Confetti.
#
# You should have received a copy of the GNU Affero General Public License
# along with Confetti. If not, see <http://www.gnu.org/licenses/>.
-echo -n "Content-Type: text/calendar;charset=utf-8\n\n"
-course="${_GET[course]}"
-cat "$_DATA/ical/$course"
+l10n(){
+ local word
+ [ $# -eq 0 ] && read -r word || word="$*"
+ case $word in
+ cat_remove) printf %s "-";;
+ cat_add) printf %s "+";;
+ cat_newlabel) printf %s "neue Kategorie";;
+ cat_update) printf %s "Zuweisungen übernehmen";;
+ categories_label) printf %s "Kategorien";;
+
+ *) l10n_global "$word";;
+ esac
+}
-#!/bin/zsh
+#!/bin/sh
-# Copyright 2016 Paul Hänsch
+# Copyright 2016, 2021 Paul Hänsch
#
# This file is part of Confetti.
#
# You should have received a copy of the GNU Affero General Public License
# along with Confetti. If not, see <http://www.gnu.org/licenses/>.
-. "$_EXEC"/pages/categories.sh
+. "$_EXEC"/cgilite/storage.sh
+. "$_EXEC"/pdiread.sh
catfile="${_DATA}/mappings/categories"
-set_categories(){
- card="${_DATA}/vcard/$1"
- cats="$2"
- cardcats="$(sed -nr 's;^CATEGORIES(\;[^":]+|\;"[^"]+")*:([^\r]+)\r?$;\2;p' "$card")"
+for card in "${_DATA}"/vcard/*.vcf; do
+ n='' postcats='' cardcats=''
+ vcf="$(pdi_load "$card")"
- debug "CARD: $card"
- debug "CATS: $cardcats"
- debug "NEW: $cats"
- if [ "$cats" != "$cardcats" ]; then
- sed -ri '
+ n=1; while postcats="${postcats}${postcats:+,}$(POST "${card##*/}" $n)"; do n=$((n+1)); done
+ n=1; while cardcats="${cardcats}${cardcats:+,}$(pdi_value "$vcf" CATEGORIES $n)"; do n=$((n+1)); done
+
+ if [ "${postcats}" != "${cardcats}" ] && LOCK "$card"; then
+ sed -E -i '
/^CATEGORIES[;:]/d
- /^END:VCARD *\r?$/iCATEGORIES:'"$cats"'\r
+ /^END;?:VCARD *\r?$/iCATEGORIES:'"${postcats%,}"'\r
' "${card}"
+ RELEASE "$card"
fi
-}
-
-listcards |while read card; do
- cardcats=''
- for n in "$card" "$card"{0..100}; do
- if [ -z "${_POST[$n]}" ]; then
- set_categories "$card" "$cardcats"
- break
- fi
- cardcats="${cardcats}${cardcats:+,}${_POST[$n]}"
- done
done
-redirect "?p=categories"
+REDIRECT /categories/
--- /dev/null
+#!/bin/sh
+
+# Copyright 2017 - 2020 Paul Hänsch
+#
+# This is CGIlite.
+# A collection of posix shell functions for writing CGI scripts.
+#
+# CGIlite is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# CGIlite is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with CGIlite. If not, see <http://www.gnu.org/licenses/>.
+
+[ -n "$include_cgilite" ] && return 0
+# guard set after webserver part
+
+# ksh and zsh workaround
+# set -o posix # ksh, not portable
+setopt -o OCTAL_ZEROES 2>&-
+
+CR="\r"
+BR='
+'
+cgilite_timeout=2
+
+PATH(){
+ local str seg out
+ [ $# -eq 0 ] && str="$(cat)" || str="$*"
+ while [ "$str" ]; do
+ seg=${str%%/*}; str="${str#*/}"
+ case $seg in
+ ..) out="${out%/}"; out="${out%/*}/";;
+ .|'') out="${out%/}/";;
+ *) out="${out%/}/${seg}";;
+ esac;
+ [ "$seg" = "$str" ] && break
+ done
+ [ "${str}" -a "${out}" ] && printf %s "$out" || printf %s/ "${out%/}"
+}
+
+HEX_DECODE='
+ s;\\;\\\\;g; :HEXDECODE_X; s;%([^0-9A-F]);\\045\1;g; tHEXDECODE_X;
+ # Hexadecimal { %00 - %FF } will be transformed to octal { \000 - \377 } for posix printf
+ s;%[0123].;&\\0;g; s;%[4567].;&\\1;g; s;%[89AB].;&\\2;g; s;%[CDEF].;&\\3;g;
+ s;%[048C][0-7]\\.;&0;g; s;%[048C][89A-F]\\.;&1;g; s;%[159D][0-7]\\.;&2;g; s;%[159D][89A-F]\\.;&3;g;
+ s;%[26AE][0-7]\\.;&4;g; s;%[26AE][89A-F]\\.;&5;g; s;%[37BF][0-7]\\.;&6;g; s;%[37BF][89A-F]\\.;&7;g;
+ s;%.[08](\\..);\10;g; s;%.[19](\\..);\11;g; s;%.[2A](\\..);\12;g; s;%.[3B](\\..);\13;g;
+ s;%.[4C](\\..);\14;g; s;%.[5D](\\..);\15;g; s;%.[6E](\\..);\16;g; s;%.[7F](\\..);\17;g;
+'
+
+HEX_DECODE(){
+ printf -- "$(printf %s "$1" |sed -E "$HEX_DECODE")"
+}
+
+if [ -z "$REQUEST_METHOD" ]; then
+ # no webserver variables means we are running via inetd / ncat
+ # so use builtin web server
+
+ # Use env from inetd as webserver variables
+ REMOTE_ADDR="${TCPREMOTEIP}"
+ SERVER_NAME="${TCPLOCALIP}"
+ SERVER_PORT="${TCPLOCALPORT}"
+
+ # Wait 2 seconds for request or kill connection through watchdog.
+ # Once Request is received the watchdog will be suspended (killed).
+ # At the end of the loop the watchdog will be restarted to enable
+ # timeout for the subsequent request.
+
+ (sleep $cgilite_timeout && kill $$) & cgilite_watchdog=$!
+ while read REQUEST_METHOD REQUEST_URI SERVER_PROTOCOL; do
+ [ "${SERVER_PROTOCOL#HTTP/1.[01]${CR}}" ] && break
+ kill $cgilite_watchdog
+
+ SERVER_PROTOCOL="${SERVER_PROTOCOL%${CR}}"
+ PATH_INFO="$(HEX_DECODE "${REQUEST_URI%\?*}" |PATH)"
+ [ "${REQUEST_URI}" = "${REQUEST_URI#*\?}" ] \
+ && QUERY_STRING='' \
+ || QUERY_STRING="${REQUEST_URI#*\?}"
+ cgilite_headers=''; while read -r hl; do
+ hl="${hl%${CR}}"; [ "$hl" ] || break
+ case $hl in
+ 'Content-Length: '*) CONTENT_LENGTH="${hl#*: }";;
+ 'Content-Type: '*) CONTENT_TYPE="${hl#*: }";;
+ esac
+ cgilite_headers="${cgilite_headers}${hl}${BR}"
+ done
+
+ export REMOTE_ADDR SERVER_NAME SERVER_PORT REQUEST_METHOD REQUEST_URI SERVER_PROTOCOL \
+ PATH_INFO QUERY_STRING CONTENT_TYPE CONTENT_LENGTH
+
+ # Try to serve multiple requests, provided that script serves a
+ # Content-Length header.
+ # Without Content-Length header, connection will terminate after
+ # script.
+
+ cgilite_status='200 OK'; cgilite_response=''; cgilite_cl="Connection: close${CR}${BR}";
+ . "$0" | while read -r l; do case $l in
+ Status:*)
+ cgilite_status="${l#Status: }";;
+ Content-Length:*)
+ cgilite_cl=""
+ cgilite_response="${cgilite_response:+${cgilite_response}${BR}}${l}";;
+ Connection:*)
+ cgilite_cl="${l}${BR}";;
+ $CR) printf '%s %s\r\n%s%s\r\n' \
+ 'HTTP/1.1' "${cgilite_status%${CR}}" \
+ "${cgilite_response}${cgilite_response:+${BR}}" "${cgilite_cl}"
+ cat || kill $$
+ [ "${cgilite_cl#Connection}" = "${cgilite_cl}" ]; exit;;
+ *) cgilite_response="${cgilite_response:+${cgilite_response}${BR}}${l}";;
+ esac; done || exit 0;
+ (sleep $cgilite_timeout && kill $$) & cgilite_watchdog=$!
+ done
+ kill $cgilite_watchdog
+ exit 0
+fi
+
+include_cgilite="$0"
+
+if [ "${REQUEST_METHOD}" = POST -a "${CONTENT_LENGTH:-0}" -gt 0 -a \
+ "${CONTENT_TYPE}" = "application/x-www-form-urlencoded" ]; then
+ cgilite_post="$(head -c "$CONTENT_LENGTH")"
+fi
+
+debug(){ [ $# -gt 0 ] && printf '%s\n' "$@" >&2 || tee -a /dev/stderr; }
+[ "${DEBUG+x}" ] && env >&2
+
+cgilite_count(){
+ printf %s "&$1" \
+ | grep -oE '&'"$2"'=[^&]*' \
+ | wc -l
+}
+
+cgilite_value(){
+ local str="&$1" name="$2" cnt="${3:-1}"
+ while [ $cnt -gt 0 ]; do
+ [ "${str}" = "${str#*&${name}=}" ] && return 1
+ str="${str#*&${name}=}"
+ cnt=$((cnt - 1))
+ done
+ printf -- "$(printf %s "${str%%&*}" |sed -E 's;\+; ;g;'"$HEX_DECODE")"
+}
+
+cgilite_keys(){
+ local str="&$1"
+ while [ "${str#*&}" != "${str}" ]; do
+ str="${str#*&}"
+ printf '%s\n' "${str%%=*}"
+ done \
+ | sort -u
+}
+
+GET(){ cgilite_value "${QUERY_STRING}" $@; }
+GET_COUNT(){ cgilite_count "${QUERY_STRING}" $1; }
+GET_KEYS(){ cgilite_keys "${QUERY_STRING}"; }
+
+POST(){ cgilite_value "${cgilite_post}" $@; }
+POST_COUNT(){ cgilite_count "${cgilite_post}" $1; }
+POST_KEYS(){ cgilite_keys "${cgilite_post}"; }
+
+REF(){ cgilite_value "${HTTP_REFERER#*\?}" $@; }
+REF_COUNT(){ cgilite_count "${HTTP_REFERER#*\?}" $1; }
+REF_KEYS(){ cgilite_keys "${HTTP_REFERER#*\?}"; }
+
+HEADER(){
+ # Read value of header line. Use this instead of
+ # referencing HTTP_* environment variables.
+ if [ -n "${cgilite_headers+x}" ]; then
+ local str="${BR}${cgilite_headers}"
+ [ "${str}" = "${str#*${BR}${1}: }" ] && return 1
+ str="${str#*${BR}${1}: }"
+ printf %s "${str%%${BR}*}"
+ else
+ local var="HTTP_$(printf %s "$1" |tr a-z- A-Z-)"
+ eval "[ \"\$$var\" ] && printf %s \"\$$var\" || return 1"
+ # eval "printf %s \"\$HTTP_$(printf %s "${1}" |tr a-z A-Z |tr -c A-Z _)\""
+ fi
+}
+
+COOKIE(){
+ HEX_DECODE "$(
+ HEADER Cookie \
+ | grep -oE '(^|; ?)'"$1"'=[^;]*' \
+ | sed -En "${2:-1}"'{s;^[^=]+=;;; s;\+; ;g; p;}'
+ )"
+}
+
+HTML(){
+ # Escape HTML cahracters
+ # Also escape [, ], and \n for use in html-sh
+ local str out
+ [ $# -eq 0 ] && str="$(cat)" || str="$*"
+ while [ "$str" ]; do
+ case $str in
+ \&*) out="${out}&";;
+ \<*) out="${out}<";;
+ \>*) out="${out}>";;
+ \"*) out="${out}"";;
+ \'*) out="${out}'";;
+ \[*) out="${out}[";;
+ \]*) out="${out}]";;
+ "${CR}"*) out="${out}
";;
+ "${BR}"*) out="${out}
";;
+ *) out="${out}${str%"${str#?}"}";;
+ esac
+ str="${str#?}"
+ done
+ printf %s "$out"
+}
+
+URL(){
+ # Escape pathes, so they can be used in link tags and HTTP Headers
+ local str out
+ [ $# -eq 0 ] && str="$(cat)" || str="$*"
+ while [ "$str" ]; do
+ case $str in
+ \&*) out="${out}%26";;
+ \"*) out="${out}%22";;
+ \'*) out="${out}%27";;
+ \?*) out="${out}%3F";;
+ \#*) out="${out}%23";;
+ \[*) out="${out}%5B";;
+ \]*) out="${out}%5D";;
+ \ *) out="${out}%20";;
+ " "*) out="${out}%09";;
+ "${CR}"*) out="${out}%0D";;
+ "${BR}"*) out="${out}%0A";;
+ %*) out="${out}%25";;
+ *) out="${out}${str%"${str#?}"}";;
+ esac
+ str="${str#?}"
+ done
+ printf %s "$out"
+}
+
+SET_COOKIE(){
+ # Param: session | +seconds | [date]
+ # Param: name=value
+ # Param: Path= | Domain= | Secure
+ local expire cookie
+ case "$1" in
+ ''|0|session) expire='';;
+ [+-][0-9]*) expire="$(date -R -d @$(($(date +%s) + $1)))";;
+ *) expire="$(date -R -d "$1")";;
+ esac
+ cookie="$2"
+
+ printf 'Set-Cookie: %s; HttpOnly; SameSite=Lax' "$cookie"
+ [ -n "$expire" ] && printf '; Expires=%s' "${expire%+????}${expire:+GMT}"
+ [ $# -ge 3 ] && shift 2 && printf '; %s' "$@"
+ printf '\r\n'
+}
+
+REDIRECT(){
+ printf '%s: %s\r\n' \
+ Status "303 See Other" \
+ Content-Length 0 \
+ Location "$*"
+ printf '\r\n'
+ exit 0
+}
--- /dev/null
+#!/bin/sh
+
+# Copyright 2016 - 2019 Paul Hänsch
+#
+# This file is part of cgilite.
+#
+# cgilite is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# cgilite is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with cgilite. If not, see <http://www.gnu.org/licenses/>.
+
+[ -n "$include_fileserve" ] && return 0
+include_fileserve="$0"
+
+file_type(){
+ case ${1##*.} in
+ html|html) printf 'text/html';;
+ css) printf 'text/css';;
+ js) printf 'text/javascript';;
+ txt) printf 'text/plain';;
+ sh) printf 'text/shellscript';;
+ jpg|jpeg) printf 'image/jpeg';;
+ png) printf 'image/png';;
+ svg) printf 'image/svg+xml';;
+ gif) printf 'image/gif';;
+ webm) printf 'video/webm';;
+ mp4|m4v) printf 'video/mp4';;
+ m4a) printf 'audio/mp4';;
+ ogg) printf 'audio/ogg';;
+ xml) printf 'application/xml';;
+ m3u8) printf 'application/x-mpegURL';;
+ ts) printf 'video/MP2T';;
+ mpd) printf 'application/dash+xml';;
+ m4s) printf 'video/iso.segment';;
+ *) printf 'application/octet-stream';;
+ esac
+}
+
+FILE(){
+ local file file_size file_date http_date cachedate range mime
+ file="$1" mime="$2"
+
+ if ! [ -f "$file" ]; then
+ printf 'Content-Length: 0\r\nStatus: 404 Not Found\r\n\r\n'
+ exit 0
+ elif ! [ -r "$file" ]; then
+ printf 'Content-Length: 0\r\nStatus: 403 Forbidden\r\n\r\n'
+ exit 0
+ fi
+
+ file_size="$(stat -Lc %s "$file")"
+ file_date="$(stat -Lc %Y "$file")"
+ http_date="$(date -uRd @$file_date)"
+ http_date="${http_date%+0000}GMT"
+ cachedate="$(
+ # Parse the allowable date formats from Section 3.3.1 of
+ # https://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html
+ HEADER If-Modified-Since \
+ | sed -E 's;^[^ ]+, ([0-9]{2}) (...) ([0-9]{4}) (..:..:..) GMT$;\3-\2-\1 \4;;
+ s;^[^ ]+, ([0-9]{2})-(...)-([789][0-9]) (..:..:..) GMT$;19\3-\2-\1 \4;;
+ s;^[^ ]+, ([0-9]{2})-(...)-([0-6][0-9]) (..:..:..) GMT$;20\3-\2-\1 \4;;
+ s;^[^ ]+ (...) ([0-9]{2}) (..:..:..) ([0-9]{4})$;\4-\1-\2 \3;;
+ s;^[^ ]+ (...) ([0-9]) (..:..:..) ([0-9]{4})$;\4-\1-\2 \3;;
+ s;Jan;01;; s;Feb;02;; s;Mar;03;; s;Apr;04;; s;May;05;; s;Jun;06;;
+ s;Jul;07;; s;Aug;08;; s;Sep;09;; s;Oct;10;; s;Nov;11;; s;Dec;12;;' \
+ | xargs -r0 date +%s -ud 2>&-
+ )"
+
+ range="$(HEADER Range |sed -nE 's;^bytes=([0-9]+-[0-9]*|-[0-9]+)$;\1;p;q;')"
+ case "$range" in
+ *-) range="${range}$((file_size - 1))";;
+ -*) [ ${range#-} -le $file_size ] \
+ && range="$((file_size - ${range#-}))-$((file_size - 1))" \
+ || range="0-$((file_size - 1))";;
+ *-*) [ ${range#*-} -ge $file_size ] \
+ && range="${range%-*}-$((file_size - 1))";;
+ esac
+
+ if [ "$file_date" -lt "$cachedate" ] 2>&-; then
+ printf '%s: %s\r\n' \
+ Status '304 Not Modified' \
+ Content-Length 0 \
+ Last-Modified "$http_date"
+ printf '\r\n'
+
+ elif [ -z "$range" ]; then
+ printf '%s: %s\r\n' \
+ Status "200 OK" \
+ Accept-Ranges bytes \
+ Last-Modified "$http_date" \
+ Content-Type "${mime:-$(file_type "$file")}" \
+ Content-Length $file_size
+ printf '\r\n'
+
+ [ "$REQUEST_METHOD" != HEAD ] && cat "$file"
+
+ elif [ "${range%-*}" -le "${range#*-}" ]; then
+ printf '%s: %s\r\n' \
+ Status "206 Partial Content" \
+ Accept-Ranges bytes \
+ Last-Modified "$http_date" \
+ Content-Type "${mime:-$(file_type "$file")}" \
+ Content-Range "bytes ${range}/${file_size}" \
+ Content-Length "$((${range#*-} - ${range%-*} + 1))"
+ printf '\r\n'
+
+ [ "$REQUEST_METHOD" != HEAD ] \
+ && tail -c+$((${range%-*} + 1)) "$file" \
+ | head -c "$((${range#*-} - ${range%-*} + 1))"
+
+ elif [ "${range%-*}" -gt "${range#*-}" ]; then
+ printf '%s: %s\r\n' \
+ Status "216 Range Not Satisfiable" \
+ Content-Length 0 \
+ Content-Range \*/${file_size}
+ printf '\r\n'
+ fi
+}
--- /dev/null
+#!/bin/sed -nEf
+
+:Escapes
+s,\\\\,\\,g; s,\\&,\&,g;
+s,\\<,\<,g; s,\\>,\>,g;
+s,\\",\",g; s,\\',\',g;
+s,\\\[,\[,g; s,\\\],\],g;
+s,\\\.,\.,g; s,\\#,\#,g;
+s,\\,,g;
+
+:CommentHandle
+x; /^<\/!-->/{
+ x; /--]/{
+ H; s;^(.*)--].*$;\1-->;p;
+ g; s;^.*--]([^\n]*)$;\1;
+ x; s;^</!-->\n(.*)\n[^\n]*$;\1;; x;
+ bCommentEnd
+ }
+ p; b;
+}
+x;
+:CommentEnd
+
+:shortcuts
+s;\[hidden[ \t]+"([^"]*)"[ \t]+"([^"]*)";[input type="hidden" name="\1" value="\2";g;
+s;\[checkbox[ \t]+"([^"]*)"[ \t]+"([^"]*)";[input type="checkbox" name="\1" value="\2";g;
+s;\[radio[ \t]+"([^"]*)"[ \t]+"([^"]*)";[input type="radio" name="\1" value="\2";g;
+s;\[submit[ \t]+"([^"]*)"[ \t]+"([^"]*)";[button type="submit" name="\1" value="\2";g;
+s;\[a[ \t]+"([^"]*)";[a href="\1";g;
+s;\[img[ \t]+"([^"]*)"[ \t]+"([^"]*)";[img src="\1" alt="\2";g;
+
+s;\[!([^]\[]*)\];<!\1>;g;
+s;\[!--([^]\[]*)--\];<!--\1-->;g;
+
+:tags
+s;\[([^]\[< \t]+)([^]\[]*)\];<\1>\2</\1>;g;
+t tags;
+
+G;
+:tagclose
+s;^([^]\n]*)\]([^\n]*)\n([^\n]+);\1\3\2;
+t tagclose;
+h; s;^([^\n]*)\n;;; x; s;\n.*$;;;
+
+:tagopen
+s;^([^\[\n]*)\[([^]\[< \t\n]+)([^\n]*);\1<\2>\3\n</\2>;
+t tagopen;
+G; h; s;^[^\n]*\n+;;; x; s;\n.*$;;;
+
+:attribs
+s;class="([^>]+)>[ \t]*\.([^< \t]+);class="\2 \1>;g; t attribs;
+s;(<[^/][^>]*)>[ \t]*\.([^< \t]+);\1 class="\2">;g;
+s;(<[^/][^>]*)>[ \t]*#([^< \t]+);\1 id="\2">;g;
+s;(<[^/][^>]*)>[ \t]*([^ \t=<]+=("[^"]*"|'[^']*'|[^< \t]*));\1 \2>;g;
+t attribs;
+s;(<input ([^>]+ )?type=(radio|"radio"|'radio')( [^>]+)?)>[ \t]*(checked|selected);\1 checked="checked">;g;
+s;(<input ([^>]+ )?type=(checkbox|"checkbox"|'checkbox')( [^>]+)?)>[ \t]*(checked|selected);\1 checked="checked">;g;
+s;(<option( [^>]+)?)>[ \t]*(checked|selected);\1 selected="selected">;g;
+s;(<select( [^>]+)?)>[ \t]*multiple;\1 multiple="multiple">;g;
+t attribs;
+s;(<[^/][^>]*>)[ \t]*\.[ \t];\1;g;
+
+s;(<[^/][^>]*>)[ \t]*;\1;g;
+# s;(<[^/][^>]*)>[ \t]*</[^>]+>;\1/>;g;
+s;(<(br|hr|img|input|link|meta|area|base|col|command|embed|keygen|param|source|track|wbr)[^>]*)>[ \t]*</\1>;\1>;g;
+
+s;<!-->;<!--;;
+
+p; ${g; s;\n+;;g; p;}
--- /dev/null
+#!/bin/sh
+
+# LOGLEVEL 1: Crash condition
+# LOGLEVEL 2: Unexpected condition
+# LOGLEVEL 3: Failed action (i.e. due to config error)
+# LOGLEVEL 4: Debug
+
+[ -n "$include_logging" ] && return 0
+include_logging="$0"
+
+LOGLEVEL="${LOGLEVEL:-3}"
+LOGFILE="${LOGFILE:-/dev/stderr}"
+
+logmsg(){
+ local ll="${1:-3}"
+ shift 1
+ if [ "$ll" -le "$LOGLEVEL" -a "$#" -gt 0 ]; then
+ printf %s\\n "$*" >>"${LOGFILE}"
+ elif [ "$ll" -le "$LOGLEVEL" ]; then
+ tee -a "${LOGFILE}"
+ elif [ ! "$#" -gt 0 ]; then
+ cat
+ fi
+}
+
+die(){
+ [ "$#" -gt 0 ] && logmsg 1 "$@"
+ exit 1
+}
+panic(){ logmsg 2 "$@"; }
+error(){ logmsg 3 "$@"; }
+debug(){ logmsg 4 "$@"; }
--- /dev/null
+#!/bin/sh
+
+[ -n "$include_session" ] && return 0
+include_session="$0"
+
+_DATE="$(date +%s)"
+SESSION_TIMEOUT="${SESSION_TIMEOUT:-7200}"
+
+server_key(){
+ IDFILE="${IDFILE:-${_DATA:-.}/serverkey}"
+ if [ "$(stat -c %s "$IDFILE")" -ne 512 ] || ! cat "$IDFILE"; then
+ dd count=1 bs=512 if=/dev/urandom \
+ | tee "$IDFILE"
+ fi 2>&-
+}
+
+slopecode(){
+ # 6-Bit Code that retains sort order of input data, while beeing safe to use
+ # in ascii transmissions, unix file names, HTTP URLs, and HTML attributes
+
+ uuencode -m - | sed '
+ 1d;$d;
+ y;ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/;0123456789:=ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz;
+ '
+}
+
+session_mac(){
+ if which openssl >/dev/null; then
+ openssl dgst -sha1 -hmac "$(server_key)" -binary |slopecode
+ else
+ { cat; server_key; } |sha256sum |cut -d\ -f1
+ fi
+}
+
+randomid(){
+ dd bs=12 count=1 if=/dev/urandom 2>&- \
+ | slopecode
+}
+
+timeid(){
+ d=$(($_DATE % 4294967296))
+ { printf "$(
+ printf \\%o \
+ $((d / 16777216 % 256)) \
+ $((d / 65536 % 256)) \
+ $((d / 256 % 256)) \
+ $((d % 256))
+ )"
+ dd bs=8 count=1 if=/dev/urandom 2>&-
+ } | slopecode
+}
+
+checkid(){ grep -m 1 -xE '[0-9a-zA-Z:=]{16}'; }
+
+transid(){
+ # transaction ID to modify a given file
+ local file="$1"
+ { stat -c %F%i%n%N%s%Y "$file" 2>&-
+ printf %s "$SESSION_ID"
+ } | session_mac
+}
+
+update_session(){
+ local session sid time sig serverkey checksig
+
+ IFS=- read -r sid time sig <<-END
+ $(POST session_key || COOKIE session)
+ END
+ serverkey="$(server_key)"
+
+ checksig="$(printf %s "$sid" "$time" |session_mac)"
+
+ if ! [ "$checksig" = "$sig" \
+ -a "$time" -ge "$_DATE" \
+ -a "$(printf %s "$sid" |checkid)" ] 2>&-
+ then
+ debug "Setting up new session"
+ sid="$(randomid)"
+ fi
+
+ time=$(( $_DATE + $SESSION_TIMEOUT ))
+ sig="$(printf %s "$sid" "$time" |session_mac)"
+ printf %s\\n "${sid}-${time}-${sig}"
+}
+
+SESSION_KEY="$(update_session)"
+SET_COOKIE 0 session="$SESSION_KEY" Path=/ SameSite=Strict HttpOnly
+SESSION_ID="${SESSION_KEY%%-*}"
--- /dev/null
+#!/bin/sh
+
+# Copyright 2018, 2019 Paul Hänsch
+#
+# This is a file format helper, part of CGIlite.
+#
+# CGIlite is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# CGIlite is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with CGIlite. If not, see <http://www.gnu.org/licenses/>.
+
+[ -n "$include_storage" ] && return 0
+include_storage="$0"
+
+CR="\r"
+BR='
+'
+
+LOCK(){
+ local lock timeout block
+ lock="${1}.lock"
+ timeout="${2-20}"
+ if [ \! -w "${lock%/*}" ] || [ -e "$lock" -a \! -d "$lock" ]; then
+ printf 'Impossible to get lock: %s\n' "$lock" >&2
+ return 1
+ fi
+
+ while ! mkdir "$lock" 2>&-; do
+ block="$(cat "$lock/pid" || printf 1)"
+ if ! { ps -eo pid |grep -qwF "$block"; }; then
+ printf 'Overriding stale lock: %s\n' "$lock" >&2
+ break
+ fi
+ if [ $timeout -le 0 ]; then
+ printf 'Timeout while trying to get lock: %s\n' "$lock" >&2
+ return 1
+ fi
+ timeout=$((timeout - 1))
+ sleep 1
+ done
+ printf '%i\n' $$ >"${lock}/pid"
+ return 0
+}
+
+RELEASE(){
+ local lock
+ lock="${1}.lock"
+ if [ "$(cat "$lock/pid")" = "$$" ]; then
+ rm "$lock/pid"
+ if ! rmdir "$lock"; then
+ printf 'Cannot remove tainted lock: %s\n' "$lock" >&2
+ printf '%i\n' $$ >"${lock}/pid"
+ return 1
+ fi
+ return 0
+ else
+ printf 'Refusing to release foreign lock: %s\n' "$lock" >&2
+ return 1
+ fi
+}
+
+STRING='
+ s;\\;\\\\;g;
+ s;\n;\\n;g;
+ s;\t;\\t;g;
+ s;\r;\\r;g;
+ s;\+;\\+;g;
+ s; ;+;g;
+'
+
+STRING_OLD(){
+ { [ $# -eq 0 ] && cat || printf %s "$*"; } \
+ | sed -E ':X; $!{N;bX;}'"$STRING"
+}
+
+STRING(){
+ local in out=''
+ [ $# -gt 0 ] && in="$*" || in="$(cat)"
+ while [ "$in" ]; do case $in in
+ \\*) out="${out}\\\\"; in="${in#\\}" ;;
+ "$BR"*) out="${out}\\n"; in="${in#${BR}}" ;;
+ "$CR"*) out="${out}\\r"; in="${in#${CR}}" ;;
+ " "*) out="${out}\\t"; in="${in# }" ;;
+ +*) out="${out}\\+"; in="${in#+}" ;;
+ " "*) out="${out}+"; in="${in# }" ;;
+ *) out="${out}${in%%[\\${CR}${BR} + ]*}"; in="${in#"${in%%[\\${BR}${CR} + ]*}"}" ;;
+ esac; done
+ printf '%s' "$out"
+}
+
+
+UNSTRING='
+ :UNSTRING_X
+ s;((^|[^\\])(\\\\)*)\\n;\1\n;g;
+ s;((^|[^\\])(\\\\)*)\\t;\1\t;g;
+ s;((^|[^\\])(\\\\)*)\\r;\1\r;g;
+ s;((^|[^\\])(\\\\)*)\+;\1 ;g;
+ tUNSTRING_X;
+ s;((^|[^\\])(\\\\)*)\\\+;\1+;g;
+ s;\\\\;\\;g;
+'
+UNSTRING_OLD(){
+ { [ $# -eq 0 ] && cat || printf %s "$*"; } \
+ | sed -E "$UNSTRING"
+}
+UNSTRING(){
+ local in out=''
+ [ $# -gt 0 ] && in="$*" || in="$(cat)"
+ while [ "$in" ]; do case $in in
+ \\\\*) out="${out}\\"; in="${in#\\\\}" ;;
+ \\n*) out="${out}${BR}"; in="${in#\\n}" ;;
+ \\r*) out="${out}${CR}"; in="${in#\\r}" ;;
+ \\t*) out="${out} "; in="${in#\\t}" ;;
+ \\+) out="${out}+"; in="${in#\\+}" ;;
+ +*) out="${out} "; in="${in#+}" ;;
+ \\*) in="${in#\\}" ;;
+ *) out="${out}${in%%[\\+]*}"; in="${in#"${in%%[\\+]*}"}" ;;
+ esac; done
+ printf '%s' "$out"
+}
+
-#!/bin/zsh
+#!/bin/sh
-# Copyright 2014 Paul Hänsch
+# Copyright 2014, 2019, 2020 Paul Hänsch
#
# This file is part of Confetti.
#
# You should have received a copy of the GNU Affero General Public License
# along with Confetti. If not, see <http://www.gnu.org/licenses/>.
-cgi_post
+locktimeout=900
+. "$_EXEC"/session_lock.sh
-uid=$(uuidgenerator)
-course="${uid}.ics"
+course="$(GET course |PATH)"
+coursefile="$_DATA/ical/${course##*/}"
-tzid=$(cat /etc/timezone)
-tstamp=$(TZ="$tzid" date +%Y%m%dT%H%M%S)
-
-tempfile="$_DATA/temp/$course"
-
-cat >"$tempfile" <<EOF
-BEGIN:VCALENDAR
-VERSION:2.0
-PRODID:Berlin RAW Confetti
-BEGIN:VEVENT
-UID:$uid
-DTSTAMP:TZID=${tzid}:${tstamp}
-DTSTART:
-DURATION:
-RRULE:
-SUMMARY:
-COMMENT:
-END:VEVENT
-END:VCARD
-EOF
-
-echo -n "Location: ?p=courses&edit=$course\n\n"
+if tempfile="$(SLOCK "$coursefile" "$locktimeout")"; then
+ REDIRECT "/courses/?e=${course}"
+elif [ -f "$tempfile" ]; then
+ SET_COOKIE session message="SESSLOCK"
+ REDIRECT "/courses/#${course}"
+else
+ SET_COOKIE session message="EDITLOCK"
+ REDIRECT "/courses/#${course}"
+fi
#!/bin/zsh
-# Copyright 2014 Paul Hänsch
+# Copyright 2014,2015,2021 Paul Hänsch
#
# This file is part of Confetti.
#
# You should have received a copy of the GNU Affero General Public License
# along with Confetti. If not, see <http://www.gnu.org/licenses/>.
-course="${_GET[course]}"
+course="$(GET course |PATH)"
+coursefile="$_DATA/ical/${course##*/}"
-echo -n "Location: ?p=courses&edit=$course\n\n"
+. $_EXEC/pdiread.sh
+. $_EXEC/cgilite/file.sh
+
+printf 'Content-Disposition: inline; filename="%s.ics"\r\n' "$(pdi_value "$(pdi_load "$coursefile")" SUMMARY)"
+
+FILE "$coursefile" "text/calendar; charset=utf-8"
--- /dev/null
+#!/bin/sh
+
+. "${_EXEC}/pdiread.sh"
+. "$_EXEC/cards/l10n.sh"
+
+coursefile="${_DATA}/ical/$(GET course)"
+
+if [ ! -r "$coursefile" ]; then
+ SET_COOKIE 0 message="Cannot read course file"
+ REDIRECT /courses/
+ return 0
+elif ! mkdir -p "$_DATA/export"; then
+ SET_COOKIE 0 message="Cannot create export directory"
+ REDIRECT /courses/
+ return 0
+fi
+
+ics="$(pdi_load "$coursefile")"
+htmlfile="${_DATA}/export/$(pdi_value "$ics" SUMMARY |unescape |tr \\n/ __).html"
+pdffile=${htmlfile%.html}.pdf
+
+pdi_date() {
+ local pdt y m d H M S Z
+ [ $# -eq 0 ] && read pdt || pdt="$*"
+
+ case $pdt in
+ *T*Z)
+ Z=UTC; pdt="${pdt%Z}";;
+ TZID=*:*T*)
+ Z="${pdt%%:*}"; Z=${Z#TZID=}; pdt=${pdt#TZID=*:};;
+ esac
+
+ y="${pdt%%????T*}" pdt=${pdt#????}
+ m="${pdt%%??T*}" pdt=${pdt#??}
+ d="${pdt%%T*}" pdt=${pdt#??T}
+ H="${pdt%%????}" pdt=${pdt#??}
+ M="${pdt%%??}" pdt=${pdt#??}
+ S="${pdt}" pdt=''
+
+ case Z in
+ UTC) date -d "${y}-${m}-${d} ${H}:${M}:${S} UTC" +%s;;
+ '') date -d "${y}-${m}-${d} ${H}:${M}:${S}" +%s;;
+ *) date -d "TZ=\"${Z}\" ${y}-${m}-${d} ${H}:${M}:${S}" +%s;;
+ esac
+}
+
+get_dates() {
+ local dts_date rrule rr_int rr_freq rec today="$(date +%Y%m%d)"
+
+ dts_date="$(pdi_value "$ics" DTSTART || printf %s "$today")"
+ dts_date="${dts_date#TZID=*:}" dts_date="${dts_date%%T*}"
+ rrule="$(pdi_value "$ics" RRULE)"
+ rr_int="${rrule##*INTERVAL=}" rr_int="${rr_int%%;*}"
+ rr_freq="${rrule##*FREQ=}" rr_freq="${rr_freq%%;*}"
+
+ [ "$rr_int" -ge 0 ] || rr_int=1 2>/dev/null
+ case "$rr_freq" in
+ YEARLY) rec="$rr_int year";;
+ MONTHLY) rec="$rr_int month";;
+ DAILY) rec="$rr_int day";;
+ WEEKLY) rec="$rr_int week";;
+ *) rec="$rr_int week";;
+ esac
+
+ while [ "$dts_date" -lt "$today" ]; do dts_date="$(date -d "${dts_date} + ${rec}" +%Y%m%d)"; done
+ for n in 1 2 3 4 5 6 7 8 9 10; do
+ LANG=de_DE.UTF-8 date -d "$dts_date" +"%d. %b."
+ dts_date="$(date -d "${dts_date} + ${rec}" +%Y%m%d)"
+ done
+}
+
+# some table styles need to be inline, because this is how libreoffice works
+style_td='style="border: 1pt solid; padding: 1mm 2mm; vertical-align: top;"'
+
+"$_EXEC/cgilite/html-sh.sed" <<-EOF >"$htmlfile"
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+[html [head
+ [meta http-equiv="content-type" content="text/html; charset=utf-8"]
+ [title]
+ [meta name="generator" content="Confetti"]
+ [meta name="created" content="$(date +%FT%T)"]
+ [meta name="changed" content="$(date +%FT%T)"]
+ [style type="text/css"
+ @page { size: 29.7cm 21cm; margin: 1.5cm; }
+ * { background: inherit; }
+ body { background: transparent; font-family: Liberation Sans, Sans-Serif; }
+
+ th { white-space: pre; }
+ th, td { text-align: left; }
+ ]
+][body lang="de_DE"
+ [table width="100%" style="page-break-after: always;"
+ [col width=10*] [col width=5*] [col width=10*] [col width=15*]
+ [thead
+ [tr [th $style_td . $(l10n N)] [th $style_td . $(l10n BDAY)] [th $style_td . $(l10n TEL)] [th $style_td . $(l10n NOTE)]]
+ ][tbody
+ $(grep -F "${coursefile##*/} " "$_DATA/mappings/attendance" |while read discard each; do
+ vcf="$(pdi_load "$_DATA/vcard/$each")"
+ tel="$( seq 1 $(pdi_count "$vcf" TEL) |while read n; do
+ type="$(pdi_attrib "$vcf" TEL $n TYPE)"
+ [ "$type" ] && type="$(l10n "TYPE=$type"):"
+ printf '%s %s<br>' "$type" "$(pdi_value "$vcf" TEL $n)"
+ done )"
+ printf '[tr valign=top [td %s .N . %s] [td %s .BDAY . %s] [td %s .TEL . %s] [td %s .NOTE . %s]]\n' \
+ "$style_td" "$(pdi_value "$vcf" FN |unescape |HTML)" \
+ "$style_td" "$(pdi_value "$vcf" BDAY |unescape |HTML)" \
+ "$style_td" "$tel" \
+ "$style_td" "$(pdi_value "$vcf" NOTE |unescape |HTML)"
+ done |sort)]
+ ]
+ [table width="100%"
+ [col width=30*] [col width=10*] [col width=10*] [col width=10*] [col width=10*] [col width=10*] [col width=10*] [col width=10*] [col width=10*] [col width=10*] [col width=10*]
+ [thead
+ [tr [th $style_td ] $(get_dates |xargs -d\\n printf "[th $style_td . %s]")]
+ ][tbody
+ $(grep -F "${coursefile##*/} " "$_DATA/mappings/attendance" |while read discard each; do
+ vcf="$(pdi_load "$_DATA/vcard/$each")"
+ printf '[tr [td %s .N . %s] [td %s] [td %s] [td %s] [td %s] [td %s] [td %s] [td %s] [td %s] [td %s] [td %s]]\n' \
+ "$style_td" "$(pdi_value "$vcf" FN |unescape |HTML)" \
+ "$style_td" "$style_td" "$style_td" "$style_td" "$style_td" "$style_td" "$style_td" "$style_td" "$style_td" "$style_td"
+ done |sort)]
+ ]
+]]
+EOF
+
+export HOME="$_DATA"
+export XDG_CONFIG_HOME="$_DATA/xdg_config"
+export XDG_CACHE_HOME="$_DATA/xdg_cache"
+export XDG_DATA_HOME="$_DATA/xdg_local"
+
+lowriter --convert-to pdf --outdir "$_DATA/export/" "$htmlfile" >/dev/null
+REDIRECT "$(URL "/export/${pdffile##*/}")"
--- /dev/null
+#!/bin/sh
+
+. $_EXEC/pdiread.sh
+. $_EXEC/courses/l10n.sh
+. $_EXEC/courses/widgets.sh
+. $_EXEC/courses/list.sh
+
+upcase=' y;abcdefghijklmnopqrstuvwxyzäöüé;ABCDEFGHIJKLMNOPQRSTUVWXYZÄÖÜÉ;; '
+
+order="$(GET o |grep -m1 -xE 'DOW|TOD')"
+edit="$(GET e |PATH)"
+
+[ "$order" ] || order=DOW
+edit="${edit##*/}"
+
+{ w_sort_courses
+ printf '
+ [form .newcourses action="/courses/new_course.sh" method="POST"
+ [button type="submit" %s]
+ ]' "$(l10n newcourse)"
+
+ [ "$edit" ] && edit_course "$edit"
+ printf '[div .courselist\n'
+ list_courses
+ printf ']'
+} | yield_page courses #/courses/courses.css
--- /dev/null
+# Copyright 2014, 2016, 2019 Paul Hänsch
+#
+# This file is part of Confetti.
+#
+# Confetti is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Confetti is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with Confetti. If not, see <http://www.gnu.org/licenses/>.
+
+l10n(){
+ local word
+ [ $# -eq 0 ] && read -r word || word="$*"
+
+ case $word in
+ newcourse) printf "Neuen Kurs anlegen";;
+ time) printf "Uhrzeit";;
+
+ edit_dtscal) printf "✓";;
+ edit) printf "Bearbeiten";;
+ ics_export) printf "ICal exportieren";;
+ courselist) printf "Kursliste (PDF)";;
+
+ course_mail) printf "Mail an Teilnehmende";;
+
+ sort_order) printf "Sortierung";;
+ order_DOW) printf "Wochentag";;
+ order_TOD) printf "Uhrzeit";;
+ order_apply) printf "Sortieren";;
+
+ t_every) printf "Alle";;
+ t_eternal) printf "ewig";;
+ t_times) printf "mal";;
+ t_until) printf "Bis";;
+ t_oclock) printf "Uhr";;
+
+ "Mon Tue Wed Thu Fri Sat Sun") printf "Mo Di Mi Do Fr Sa So";;
+ "January February March April May June July August September October November December")
+ printf "Januar Februar März April Mai Juni Juli August September Oktober November Dezember";;
+
+ *) l10n_global "$word";;
+ esac
+}
--- /dev/null
+#!/bin/sh
+
+. "${_EXEC}"/pdiread.sh
+
+SUP_FIELDS="COMMENT"
+
+edit_course(){
+ local coursefile="$_DATA/ical/$1"
+ local tempfile course
+
+ . $_EXEC/session_lock.sh
+
+ if ! tempfile="$(CHECK_SLOCK "$coursefile")"; then
+ printf '[div .message %s]' "$(l10n "This course is not set up for editing within this session.")"
+ else
+ course="$(pdi_load "$tempfile")"
+ cat <<-EOF
+ [form .course #${coursefile##*/} action="/courses/update_course.sh" method="POST"
+ [input type="hidden" name="course" value="${coursefile##*/}"]
+ [input type="hidden" name="tid" value="$(transid ${tempfile})"]
+ [div .section .basic . $(
+ edit_item "$course" SUMMARY COMMENT
+ )]
+ [div .section .dtstart . $(
+ edit_item "$course" DTSTART
+ )]
+ [div .section .recur . $(
+ edit_item "$course" RRULE
+ )]
+ [div .section .attendance . $(
+ edit_item "$course" attendance
+ )]
+ [div .control
+ [!-- select .item name=newfield
+ [option disabled="disabled" selected . $(l10n edit_addfieldtext)]
+ $(for f in $SUP_FIELDS; do printf '[option value="%s" . %s]\n' "$f" "$(l10n "$f")"; done)
+ ]
+ [button .item type="submit" name="action" value="addfield" . $(l10n edit_addfield) --]
+ [button .item type="submit" name="action" value="update" . $(l10n edit_update)]
+ [button .item type="submit" name="action" value="cancel" . $(l10n edit_cancel)]
+ [div .item .delete label="$(l10n edit_delete)"
+ [input type="checkbox" #delete]
+ [label for="delete" $(l10n edit_delete)]
+ [button type="submit" name="action" value="delete" $(l10n edit_delete)]
+ ]
+ ]
+ ]
+ EOF
+ fi
+}
+
+print_course(){
+ local coursefile="$1"
+ local course="$(pdi_load "$coursefile")"
+ cat <<-EOF
+ [div .course #${coursefile##*/}
+ [div .section .basic . $(
+ cal_item "$course" SUMMARY DTSTART RRULE
+ )]
+ [div .section .COMMENT . $(cal_item "$course" COMMENT)]
+ [div .section .attendance [h3 $(l10n course_attendance) ] [ul .
+ $(grep -F "${coursefile##*/} " "$_DATA/mappings/attendance" |while read discard each; do
+ printf '[li [a .item .attendance href="/cards/#%s" . %s]]\n' \
+ "$each" \
+ "$(pdi_value "$(pdi_load "$_DATA/vcard/$each")" FN |unescape |HTML)"
+ done |sort -k7)]
+ ]
+ [div .control
+ [a .item href="/courses/edit_course.sh?course=${coursefile##*/}" $(l10n edit)]
+ [a .item href="/courses/export_pdf.sh?course=${coursefile##*/}" target="blank" $(l10n courselist)]
+ [a .item href="/courses/export_ical.sh?course=${coursefile##*/}" $(l10n ics_export)]
+ [a .item href="mailto:zack@vuesch.org?bcc=$(course_mail "${coursefile##*/}" |HTML)" $(l10n course_mail)]
+ ]
+ ]
+ EOF
+}
+
+course_mail() {
+ course="$1"
+ grep -F "${course} " "$_DATA/mappings/attendance" |while read junk card; do
+ cat "${_DATA}/vcard/${card}"
+ done \
+ | pdi_load - \
+ | sed -nE 's;^EMAIL(\;[^:]*)*:(.+)\r?$;\2,;p' \
+ | tr -d \\n \
+ | unescape
+}
+
+print_courses(){
+ local calfile cachefile date size name ldate=0 lsize lname
+
+ while read calfile; do
+ cachefile="${_DATA}/cache/${calfile##*/}.cache"
+ if [ -s "$cachefile" -a "$cachefile" -nt "$calfile" ]; then
+ cat "$cachefile"
+ else
+ print_course "$calfile" |tee "$cachefile"
+ fi
+ done
+}
+
+order_courses() {
+ local calfile course
+
+ while read calfile; do
+ icstime="$(pdi_value "$(pdi_load "$calfile")" DTSTART |cal_date)"
+ case $order in
+ DOW) printf '%s %s\n' "$(date -d "$icstime" "+%u %H:%M:%S")" "$calfile";;
+ TOD) printf '%s %s\n' "$(date -d "$icstime" "+%H:%M:%S")" "$calfile";;
+ esac
+ done \
+ | sort \
+ | sed -E 's;^.*\t;;g'
+}
+
+list_courses(){
+ printf '%s\n' ${_DATA}/ical/*.ics \
+ | order_courses \
+ | print_courses
+}
-# Copyright 2016 Paul Hänsch
+#!/bin/sh
+
+# Copyright 2014, 2021 Paul Hänsch
#
# This file is part of Confetti.
#
# You should have received a copy of the GNU Affero General Public License
# along with Confetti. If not, see <http://www.gnu.org/licenses/>.
-client="${_GET[client]}"
-edit="${_GET[edit]}"
-[ -n "$edit" ] && client="${edit%.*.mpx}.vcf"
-
-cat <<EOF
-
-<div id="${card}" class="card">$(view_card "$client")</div>
-
-<!--h1>$(l10n prescriptions_current)</h1-->
-
-<div class="newprescription">
- <form action="?action=new_prescription" method="POST">
- <input type="hidden" name="client" value="$client">
- <button type="submit">$(l10n newprescription)</button>
- </form>
-</div>
-EOF
-
-list_prescriptions "$client" |grep -q "$edit" || edit_prescription "$edit"
-
-list_prescriptions "$client" \
-|while read pre; do
- [ "$pre" = "$edit" ] \
- && edit_prescription "$pre" \
- || view_prescription "$pre"
-done
-
-#<!--h1>$(l10n prescriptions_past)</h1-->
-
-# vi:set filetype=html:
+locktimeout=900
+. "$_EXEC"/session_lock.sh
+
+uid="$(timeid)$(randomid)" # 32 Octets UID, starting with timestamp
+course="${uid}.ics"
+
+tzid="$(cat /etc/timezone)"
+tstamp="$(TZ="$tzid" date +%Y%m%dT%H%M%S)"
+
+coursefile="$_DATA/ical/$course"
+
+if tempfile="$(SLOCK "$coursefile")"; then
+ cat >"$tempfile" <<-EOF
+ BEGIN:VCALENDAR
+ VERSION:2.0
+ PRODID:Berlin RAW Confetti
+ BEGIN:VEVENT
+ UID:$uid
+ DTSTAMP:TZID=${tzid}:${tstamp}
+ DTSTART:TZID=${tzid}:${tstamp}
+ DURATION:
+ RRULE:
+ SUMMARY:
+ COMMENT:
+ END:VEVENT
+ END:VCARD
+ EOF
+ REDIRECT "/courses/?e=${course}"
+else
+ SET_COOKIE session message="EDITLOCK"
+ REDIRECT "/courses/"
+fi
--- /dev/null
+#!/bin/sh
+
+# Copyright 2014, 2015, 2020, 2021 Paul Hänsch
+#
+# This file is part of Confetti.
+#
+# Confetti is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Confetti is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with Confetti. If not, see <http://www.gnu.org/licenses/>.
+
+. "$_EXEC/pdiread.sh"
+. "$_EXEC/session_lock.sh"
+. "$_EXEC/cgilite/storage.sh"
+
+unset coursefile attfile tempfile
+
+course="$(POST course |PATH)"; course="${course##*/}"
+coursefile="$_DATA/ical/$course"
+attfile="$_DATA/mappings/attendance"
+
+if ! tempfile="$(CHECK_SLOCK "$coursefile")"; then
+ SET_COOKIE 0 message="NO VALID FILE LOCK"
+ REDIRECT "/courses/?e=${course}"
+ exit 0
+elif [ "$(POST tid)" != "$(transid "$tempfile")" ]; then
+ SET_COOKIE 0 message="INVALID TRANSACTION ID"
+ REDIRECT "/courses/?e=${course}"
+ exit 0
+fi
+
+vcf_escape(){
+ for each in "$@"; do
+ printf %s\\n "$each" \
+ | sed -E ':X;$!{N;bX}; s;\r\n;\n;g; s;([;,\\]);\\\1;g; s;\n;\\n;g;'
+ done \
+ | sed -E ':X;$!{N;bX}; s;\n;\;;g'
+}
+
+ics="$(pdi_load "$tempfile")"
+
+tzid=$(cat /etc/timezone)
+
+ics="$(pdi_update_attrib "$ics" DTSTAMP 1 "TZID=${tzid}")"
+ics="$(pdi_update_value "$ics" DTSTAMP 1 "$(TZ="$tzid" date +%Y%m%dT%H%M%S)")"
+
+dts_year="$( POST DTS_YEAR |grep -m1 -xE '[0-9]{4}' || date +%Y)"
+dts_month="$( POST DTS_MONTH |grep -m1 -xE '0[1-9]|1[012]' || date +%m)"
+dts_day="$( POST DTS_DAY |grep -m1 -xE '0[1-9]|[12][0-9]|3[01]' || date +%d)"
+dts_hour="$( POST DTS_HOUR |grep -m1 -xE '[0-9]|1[0-9]|2[0-3]' || date +%H)"
+dts_minute="$(POST DTS_MINUTE |grep -m1 -xE '[0-9]|[1-5][0-9]' || date +%M)"
+[ ${#dts_hour} -eq 1 ] && dts_minute="0$dts_hour"
+[ ${#dts_minute} -eq 1 ] && dts_minute="0$dts_minute"
+DTSTART="${dts_year}${dts_month}${dts_day}T${dts_hour}${dts_minute}00"
+
+ics="$(pdi_update_attrib "$ics" DTSTART 1 "TZID=${tzid}")"
+ics="$(pdi_update_value "$ics" DTSTART 1 "$DTSTART")"
+
+rr_int=$( POST RRULE_INTERVAL |grep -m1 -xE '[0-9]+' || printf 1)
+rr_count=$(POST RRULE_COUNT |grep -m1 -xE '[0-9]+' || printf 1)
+rr_freq=$( POST RRULE_FREQ |grep -m1 -xE 'DAILY|WEEKLY|MONTHLY|YEARLY' || printf MONTHLY)
+rr_uy=$( POST RRULE_UYEAR |grep -m1 -xE '[0-9]{4}' || date +%Y)
+rr_um=$( POST RRULE_UMONTH |grep -m1 -xE '[1-9]|1[012]' || date +%m)
+rr_ud=$( POST RRULE_UDAY |grep -m1 -xE '[1-9]|[12][0-9]|3[01]' || date +%d)
+[ ${#rr_um} -eq 1 ] && rr_um="0$rr_um"
+[ ${#rr_ud} -eq 1 ] && rr_ud="0$rr_ud"
+
+case $(POST RRULE_LIMIT) in
+ COUNT) RRULE="FREQ=$rr_freq;INTERVAL=$rr_int;COUNT=$rr_count";;
+ UNTIL) RRULE="FREQ=$rr_freq;INTERVAL=$rr_int;UNTIL=${rr_uy}${rr_um}${rr_ud}T000000Z";;
+ ETERN|*) RRULE="FREQ=$rr_freq;INTERVAL=$rr_int";;
+esac
+
+ics="$(pdi_update_value "$ics" RRULE 1 "$RRULE")"
+
+for field in $(POST_KEYS |grep -xE '[A-Z][A-Z0-9-]*'); do
+ for cnt in $(seq 1 $(POST_COUNT "$field")); do
+ case "$field" in
+ *)
+ ics="$(pdi_update_value "$ics" "$field" "$cnt" "$(vcf_escape "$(POST "$field" "$cnt")")")"
+ ;;
+ esac
+done; done
+
+# delete fields, first mark for deletion using delete_key
+# this way the field enumeration is preserved during the process
+# finally filter marked lines
+delete_key="$(randomid)"
+for delete in $(POST_KEYS |grep -xE '[A-Z][A-Z0-9-]*_delete_[0-9]+'); do
+ f="${delete%%_*}"; c="${delete##*_}";
+ [ "$(POST "$delete")" = "true" ] && ics="$(pdi_update_value "$ics" "$f" "$c" "delete=${delete_key}")"
+done
+ics="$(printf '%s\n' "$ics" |sed -E "/^[^:]+:delete=${delete_key}\$/d")"
+
+case "$(POST action)" in
+ addfield)
+ newfield="$(POST newfield |grep -m 1 -xE '[A-Z][A-Z0-9-]*')"
+ ics="$(pdi_update_value "$ics" "$newfield" $(( $(pdi_count "$ics" "$newfield") + 1 )) '')"
+ printf '%s' "$ics" |grep -vx '' >"$tempfile"
+ REDIRECT "/courses/?e=${course}"
+ ;;
+ addfield\ [A-Z]*)
+ newfield="$(POST action |sed -nE '1s;^addfield ([A-Z][A-Z0-9-]*)$;\1;p')"
+ ics="$(pdi_update_value "$ics" "$newfield" $(( $(pdi_count "$ics" "$newfield") + 1 )) '')"
+ printf '%s' "$ics" |grep -vx '' >"$tempfile"
+ REDIRECT "/courses/?e=${course}"
+ ;;
+ update)
+ if LOCK "$attfile"; then
+ grep -F "${course} " "$attfile" |while read junk card; do
+ touch "$_DATA/vcard/${card}"
+ done
+ sed -E -i "/^${course} .+\$/d" "$attfile"
+ seq 1 $(POST_COUNT attendance) |while read n; do
+ printf '%s %s\n' "$course" "$(POST attendance $n)"
+ done >>"$attfile"
+ grep -F "${course} " "$attfile" |while read junk card; do
+ touch "$_DATA/vcard/${card}"
+ done
+ RELEASE "$attfile"
+ else
+ SET_COOKIE 0 message="COULD NOT UPDATE COURSE MAPPINGS"
+ fi
+
+ printf '%s' "$ics" |grep -vx '' >"${tempfile}.cp"
+ mv "${tempfile}.cp" "$coursefile"
+ RELEASE_SLOCK "$coursefile"
+ REDIRECT "/courses/#${course}"
+ ;;
+ cancel)
+ RELEASE_SLOCK "$coursefile"
+ [ -f "$coursefile" ] \
+ && REDIRECT "/courses/#${course}" \
+ || REDIRECT "/courses/"
+ ;;
+ delete)
+ rm "$coursefile"
+ RELEASE_SLOCK "$coursefile"
+ REDIRECT "/courses/"
+ ;;
+ *)
+ printf '%s' "$ics" |grep -vx '' >"$tempfile"
+ REDIRECT "/courses/?e=${course}"
+ ;;
+esac
--- /dev/null
+# Copyright 2014, 2019, 2020 Paul Hänsch
+#
+# This file is part of Confetti.
+#
+# Confetti is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Confetti is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with Confetti. If not, see <http://www.gnu.org/licenses/>.
+
+check(){
+ [ "$1" = "$2" ] && printf 'checked="checked"'
+}
+
+edit="$(GET e)"
+order="$(GET o |grep -m1 -xE 'DOW|TOD')"
+
+w_sort_courses(){
+ cat <<-EOF
+ [form .sort .search action="?" method="GET"
+ [fieldset .order [legend $(l10n sort_order):]
+ [radio "order" "DOW" $(check $order DOW) $(l10n order_DOW)]
+ [radio "order" "TOD" $(check $order TOD) $(l10n order_TOD)]
+ ]
+ [submit "" "" $(l10n order_apply)]
+ ]
+ EOF
+}
+
+cal_date(){
+ { [ $# -eq 0 ] && cat || printf %s "$*"; } |sed -nE '
+ 2q
+ s/^([0-9]{4})([0-9]{2})([0-9]{2})T([0-9]{2})([0-9]{2})([0-9]{2})Z$/\1-\2-\3 \4:\5:\6 UTC/p;t
+ s/^TZID=(.+)\:([0-9]{4})([0-9]{2})([0-9]{2})T([0-9]{2})([0-9]{2})([0-9]{2})$/TZ="\1" \2-\3-\4 \5:\6:\7/p;t
+ s/^([0-9]{4})([0-9]{2})([0-9]{2})T([0-9]{2})([0-9]{2})([0-9]{2})$/\1-\2-\3 \4:\5:\6/p;t
+ '
+}
+
+cal_item(){
+ local course="$1"
+ local item cnt c
+ shift 1
+
+ for item in $@; do
+ cnt="$(pdi_count "$course" "$item")"
+
+ case $item in
+ SUMMARY)
+ printf '[h2 ­%s]' "$(pdi_value "$course" SUMMARY)"
+ ;;
+ DTSTART)
+ printf '[span .text .DTSTART %s %s ]' \
+ "$(LANG=de_DE.UTF-8 date -d "$(pdi_value "$course" DTSTART |cal_date)" '+%A, %d. %B %Y - %H:%M')" \
+ "$(l10n t_oclock)"
+ ;;
+ RRULE)
+ dts_date="$(pdi_value "$course" DTSTART |cal_date)"
+ rrule=" $(pdi_value "$course" RRULE)"
+ rr_int="${rrule##*INTERVAL=}"; rr_int="${rr_int%%;*}"
+ rr_count="${rrule##*COUNT=}"; rr_count="${rr_count%%;*}"
+ rr_freq="${rrule##*FREQ=}"; rr_freq="${rr_freq%%;*}"
+ rr_until="${rrule##*UNTIL=}"; rr_until="${rr_until%%;*}"
+ rr_until="$(cal_date "${rr_until}")"
+
+ [ "$rr_int" -eq 1 ] \
+ && printf '[span .text .RRULE %s]' "$(l10n "s$rr_freq")" \
+ || printf '[span .text .RRULE %s %s %s]' "$(l10n t_every)" "${rr_int}" "$(l10n $rr_freq)"
+ case "$rrule $rr_freq" in
+ *COUNT*DAILY*)
+ printf '[span .text %s %s]' "$(l10n t_until)" "$(date -d "$dts_date + $((rr_int * rr_count)) day" "+%A %B %d, %Y - %H:%M")"
+ ;;
+ *COUNT*WEEKLY*)
+ printf '[span .text %s %s]' "$(l10n t_until)" "$(date -d "$dts_date + $((rr_int * rr_count)) week" "+%A %B %d, %Y - %H:%M")"
+ ;;
+ *COUNT*MONTHLY*)
+ printf '[span .text %s %s]' "$(l10n t_until)" "$(date -d "$dts_date + $((rr_int * rr_count)) month" "+%A %B %d, %Y - %H:%M")"
+ ;;
+ *COUNT*YEARLY*)
+ printf '[span .text %s %s]' "$(l10n t_until)" "$(date -d "$dts_date + $((rr_int * rr_count)) year" "+%A %B %d, %Y - %H:%M")"
+ ;;
+ *UNTIL*)
+ printf '[span .text %s %s]' "$(l10n t_until)" "$(date -d "$rr_until" "+%A %B %d, %Y - %H:%M")"
+ ;;
+ esac
+ ;;
+ attendance);;
+ COMMENT)[ $cnt -gt 0 ] && printf '[h3 %s]' "$(l10n "$item")"
+ seq 1 $cnt |while read c; do
+ printf '[p .item .%s . %s]' "$item" \
+ "$(pdi_value "$course" "$item" $c |unescape |HTML)"
+ done
+ ;;
+ *)[ $cnt -gt 0 ] && printf '[h3 %s]' "$(l10n "$item")"
+ seq 1 $cnt |while read c; do
+ printf '[span .item .%s . %s]' "$item" \
+ "$(pdi_value "$course" "$item" $c |unescape |HTML)"
+ done
+ ;;
+ esac
+ done
+}
+
+edit_item(){
+ local course="$1"
+ local item cnt c
+ shift 1
+
+ for item in $@; do
+ cnt="$(pdi_count "$course" "$item")"
+ [ "$cnt" -lt 1 ] && cnt=1
+
+ case $item in
+ DTSTART)
+ local dtstart="$(pdi_value "$course" DTSTART |cal_date)"
+ local ystart="${dtstart%%-*}"; ystart="${ystart##* }"
+ local mstart="${dtstart#*-}"; mstart="${mstart%%-*}"
+ local dstart="${dtstart##*-}"; dstart="${dstart%% *}"
+ local hhstart="${dtstart##* }"; hhstart="${hhstart%%:*}"
+ local mmstart="${dtstart##* }"; mmstart="${mmstart#*:}"; mmstart="${mmstart%:*}"
+ local m mn cdow d
+
+ cat <<-EOF
+ [h3 . $(l10n DTSTART)]
+ [input type="number" name="DTS_YEAR" value="${ystart}" placeholder="$(l10n YYYY)"]
+ [select name="DTS_MONTH" onchange="this.form.submit();"
+ $(m=1; for mn in $(l10n January February March April May June July August September October November December); do
+ printf ' [option value="%02i" %s . %s]\n' $m "$(selected $m $mstart)" "$mn"
+ m=$((m+1))
+ done)
+ ][submit "DTS" "update" . $(l10n edit_dtscal)]
+ [table .dtscalt
+ [tr $(printf '[th . %s]' $(l10n Mon Tue Wed Thu Fri Sat Sun))]
+ [tr $(
+ local cdow d
+ cdow="$(date -d ${ystart}-${mstart}-1 +%u)"
+ seq 2 $cdow |xargs -n1 printf '[td .padding .%s]'
+ d=1; while [ "$d" -lt 29 ] || [ "$(date -d ${ystart}-${mstart}-${d} +%m)" -eq "$mstart" ]; do
+ [ $cdow -eq 1 -a $d -ne 1 ] && printf ']\n [tr '
+ printf '[td [input type="radio" name="DTS_DAY" #DTSCAL_%i value="%02i" %s][label for="DTSCAL_%i" %i]]' \
+ $d $d "$(checked $d $dstart)" $d $d
+ d=$((d + 1)); cdow=$(((cdow + 1) % 7))
+ done 2>/dev/null
+ )]
+ ]
+ [label .DTSTIME $(l10n time):]
+ [input type="number" name="DTS_HOUR" value="$hhstart" min="0" max="23"]:[input type="number" name="DTS_MINUTE" value="$mmstart" min="0" max="59"]
+ EOF
+ ;;
+ RRULE)
+ local dtstart="$(pdi_value "$course" DTSTART |cal_date)"
+ local ystart="${dtstart%%-*}"; ystart="${ystart##* }"
+ local mstart="${dtstart#*-}"; mstart="${mstart%%-*}"
+ local dstart="${dtstart##*-}"; dstart="${dstart%% *}"
+
+ local rrule="$(pdi_value "$course" RRULE)"
+ local rr_int="$(printf %s "$rrule" |sed -nE 's;^(.*\;[ ]*)?INTERVAL=([0-9]+)(\;.*)?$;\2;p')"
+ local rr_count="$(printf %s "$rrule" |sed -nE 's;^(.*\;[ ]*)?COUNT=([0-9]+)(\;.*)?$;\2;p')"
+ local rr_freq="$(printf %s "$rrule" |sed -nE 's;^(.*\;[ ]*)?FREQ=(DAILY|WEEKLY|MONTHLY|YEARLY)(\;.*)?$;\2;p')"
+ local rr_until="$(printf %s "$rrule" |sed -nE 's;^(.*\;[ ]*)?UNTIL=([0-9]{8}T[0-9]{6}Z)(\;.*)?$;\2;p')"
+ local rr_uyear="${rr_until%????T??????Z}"
+ local rr_umonth=${rr_until#????}; rr_umonth="${rr_umonth%??T??????Z}"
+ local rr_uday=${rr_until#??????}; rr_uday="${rr_uday%T??????Z}"
+ local rr_limit="ETERN"
+ [ "$rr_count" ] && [ "$rr_count" -ge 0 ] && rr_limit="COUNT"
+ [ "$rr_uyear" ] && [ "$rr_uyear" -ge 0 ] && rr_limit="UNTIL"
+
+ cat <<-EOF
+ [h3 . $(l10n "$item")]
+ [span .item . $(l10n t_every)
+ [input type="number" .RRULE .INTERVAL name="RRULE_INTERVAL" placeholder="#N" value="${rr_int:-1}" min="1"]
+ [select .RRULE .FREQ name="RRULE_FREQ"
+ $(for f in DAILY WEEKLY MONTHLY YEARLY; do
+ printf ' [option value="%s" %s . %s]\n' "$f" "$(selected $f "$rr_freq")" "$(l10n $f)"
+ done)
+ ]]
+ [label .item [input type="radio" name="RRULE_LIMIT" value="ETERN" $(checked "$rr_limit" ETERN)] $(l10n t_eternal)]
+ [label .item
+ [input type="radio" name="RRULE_LIMIT" value="COUNT" $(checked "$rr_limit" COUNT)]
+ [input type="number" .RRULE .COUNT name="RRULE_COUNT" placeholder="#N" value="${rr_count:-1}" min="1"] $(l10n t_times)
+ ]
+ [label .item
+ [input type="radio" name="RRULE_LIMIT" value="UNTIL" $(checked "$rr_limit" UNTIL)] $(l10n t_until)
+ [input type="number" .RRULE .UYEAR name="RRULE_UYEAR" placeholder="$(l10n YYYY)" value="${rr_uyear:-$ystart}" min="$ystart"]
+ [input type="number" .RRULE .UMONTH name="RRULE_UMONTH" placeholder="$(l10n MM)" value="${rr_umonth:-$mstart}" min="1" max="12"]
+ [input type="number" .RRULE .UDAY name="RRULE_UDAY" placeholder="$(l10n DD)" value="${rr_uday:-$dstart}" min="1" max="31"]
+ ]
+ EOF
+ ;;
+ COMMENT)
+ printf '[h3 %s]' "$(l10n "$item")"
+ seq 1 $cnt |while read c; do
+ printf '[checkbox "%s_delete_%i" "true" .delete #%s_delete_%i][label for="%s_delete_%i" %s]' \
+ "$item" $c "$item" $c "$item" $c "$(l10n delete)"
+ printf '<textarea class="item %s" name="%s">%s</textarea>' \
+ "$item" "$item" "$(pdi_value "$course" "$item" $c |unescape |HTML)"
+ done
+ printf '[button type="submit" name="action" value="addfield %s" %s ]' "$item" "$(l10n edit_addfield)"
+ ;;
+ attendance)
+ printf '[h3 %s]' "$(l10n course_attendance)"
+ printf '[div .attendance\n'
+ for vcf in "$_DATA"/vcard/*.vcf; do
+ fn="$(pdi_value "$(pdi_load "$vcf")" FN)"
+ printf '%s/%s\n' "${vcf##*/}" "$fn"
+ done \
+ | sort -t/ -k2 \
+ | while IFS=/ read -r vcf fn; do
+ printf '[span .item [input type="checkbox" id="att%s" name="attendance" value="%s" %s][label for="att%s" . %s]]' \
+ "$vcf" "$vcf" "$(grep -qxF "${coursefile##*/} $vcf" "$_DATA/mappings/attendance" && printf 'checked="checked"')" "$vcf" "$fn"
+ done
+ printf ']'
+ ;;
+ SUMMARY)
+ printf '[h3 %s]' "$(l10n "$item")"
+ printf '[input .item .%s name="%s" value="%s" placeholder="%s"]' \
+ "$item" "$item" "$(pdi_value "$course" "$item" |unescape |HTML)" "$(l10n "$item")"
+ ;;
+ *)
+ printf '[h3 %s]' "$(l10n "$item")"
+ seq 1 $cnt |while read c; do
+ printf '[checkbox "%s_delete_%i" "true" .delete #%s_delete_%i][label for="%s_delete_%i" %s]' \
+ "$item" $c "$item" $c "$item" $c "$(l10n delete)"
+ printf '[input .item .%s name="%s" value="%s" placeholder="%s"]' \
+ "$item" "$item" "$(pdi_value "$course" "$item" $c |unescape |HTML)" "$(l10n "$item")"
+ done
+ printf '[button type="submit" name="action" value="addfield %s" %s ]' "$item" "$(l10n edit_addfield)"
+ ;;
+ esac
+ done
+}
+++ /dev/null
-#!/bin/zsh
-
-# Copyright 2014 - 2016 Paul Hänsch
-#
-# This file is part of Confetti.
-#
-# Confetti is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# Confetti is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with Confetti. If not, see <http://www.gnu.org/licenses/>.
-
-date() {
- LANG=de_DE.UTF-8 LC_ALL=de_DE.UTF-8 /bin/date $@
-}
-
-PROFILE=$(validate "$PROFILE" 'medical|circus' circus )
-
-PAGE="${_GET[p]:-${PAGE:-cards}}"
-
-uuidgenerator(){
- head -c16 /dev/urandom |sha1sum - |cut -c1-32
-}
-
-_checked=''
-checked(){
- if [ "$_checked" = "$1" ]; then
- printf 'checked="checked"'
- fi
-}
-
-
-VCF_FIELDS=(PHOTO LOGO FN NICKNAME SOUND GENDER KIND TITLE ROLE ORG MEMBER CATEGORIES ANNIVERSARY BDAY EMAIL TEL IMPP ADR URL LANG NOTE RELATED X-ZACK-JOINDATE X-ZACK-LEAVEDATE X-HEALTH-INSURANCE X-HEALTH-INSURANCE-NOCONTRIB X-CLIENT-REFERRAL)
-
-case "${PROFILE}" in
-medical)
- data_dirs vcard cache temp mappings prescriptions therapies
- [ -z "$NAVIGATION" ] && NAVIGATION=(cards)
-;;
-circus)
- data_dirs vcard ical cache temp mappings
- [ -z "$NAVIGATION" ] && NAVIGATION=(cards courses)
-;;
-esac
--- /dev/null
+#!/bin/sh
+
+for n in "$@"; do case ${n%%=*} in
+ data) _DATA="${n#data=}";;
+ exec) _EXEC="${n#exec=}";;
+ debug) DEBUG="${n#debug=}";;
+esac; done
+
+[ ! "${_EXEC%/}" ] && _EXEC="$(realpath "${0%/*}")" || _EXEC="${_EXEC%/}"
+[ ! "${_DATA%/}" ] && _DATA=. || _DATA="${_DATA%/}"
+[ "$DEBUG" ] && exec 2>>"$DEBUG"
+
+mkdir -p "${_DATA}/cache" "${_DATA}/mappings" "${_DATA}/export" "${_DATA}/lock" "${_DATA}/ical" "${_DATA}/vcard"
+
+debug() {
+ local dbg=/dev/stderr
+ if [ ! "$DEBUG" ]; then
+ [ "$#" -gt 0 ] && : || cat;
+ elif [ "$#" -gt 0 ]; then
+ printf '%s\n' "$@" >>"$dbg"
+ else
+ tee -a "$dbg"
+ fi
+}
+
+. "$_EXEC/cgilite/cgilite.sh"
+. "$_EXEC/cgilite/session.sh"
+
+. "$_EXEC/l10n.sh"
+
+_PATH="$(PATH "/${PATH_INFO}")"
+ACTION="$(GET a)"
+
+message="$(COOKIE message)"
+[ "$message" ] && SET_COOKIE 0 message=''
+
+checked(){
+ if [ "$1" = "$2" ] || [ "$1" -eq "$2" ]; then
+ printf 'checked="checked"'
+ fi 2>/dev/null
+}
+selected(){
+ if [ "$1" = "$2" ] || [ "$1" -eq "$2" ]; then
+ printf 'selected="selected"'
+ fi 2>/dev/null
+}
+
+yield_page() {
+ local class="$1" style="$2"
+ printf 'Content-Type: text/html; charset=utf-8\r\n\r\n'
+ { printf '
+ [html [head
+ [title Confetti]
+ [meta name="viewport" content="width=device-width"]
+ [link rel="stylesheet" type="text/css" href="/style.css"]
+ '
+ [ -n "$style" ] && printf '
+ [link rel="stylesheet" type="text/css" href="%s"]
+ ' "$style"
+ printf '
+ ] [body #top class="%s"
+ ' "$class"
+ printf '[ul .menu [li [a "/cards/" . %s]][li [a "/courses/" . %s]]]' "$(l10n cards)" "$(l10n courses)"
+ [ "$message" ] && printf '[p #message\n%s\n]' "$(l10n "$message")"
+ cat
+ printf '] ]'
+ } \
+ | "${_EXEC}/cgilite/html-sh.sed"
+}
+
+topdir="${_PATH#/}"
+topdir="/${topdir%%/*}"
+
+case ${_PATH} in
+ /) REDIRECT /cards/
+ ;;
+ /export/*.pdf) . "$_EXEC/cgilite/file.sh"
+ FILE "${_DATA}/${_PATH}" "application/pdf"
+ ;;
+ /export/*) . "$_EXEC/cgilite/file.sh"
+ FILE "${_DATA}/${_PATH}"
+ ;;
+ *)
+ if [ -d "${_EXEC}/${_PATH}" -a -x "${_EXEC}/${_PATH}/index.cgi" ]; then
+ . "${_EXEC}/${_PATH}/index.cgi"
+ elif [ -f "${_EXEC}/${_PATH}" -a -x "${_EXEC}/${_PATH}" ]; then
+ . "${_EXEC}/${_PATH}"
+ elif [ -f "${_EXEC}/${_PATH}" -a -r "${_EXEC}/${_PATH}" ]; then
+ . "$_EXEC/cgilite/file.sh"
+ FILE "${_EXEC}/${_PATH}"
+ elif [ -d "${_EXEC}/${topdir}" -a -x "${_EXEC}/${topdir}/index.cgi" ]; then
+ . "${_EXEC}/${topdir}/index.cgi"
+ else
+ printf '%s\r\n' 'Status: 404 Not Found' 'Content-Length: 0' ''
+ fi
+ ;;
+esac
--- /dev/null
+# Copyright 2014, 2016, 2019, 2021 Paul Hänsch
+#
+# This file is part of Confetti.
+#
+# Confetti is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Confetti is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with Confetti. If not, see <http://www.gnu.org/licenses/>.
+
+l10n(){
+ local word
+ [ $# -eq 0 ] && read -r word || word="$*"
+ l10n_global "$word"
+}
+
+l10n_global() {
+ case $1 in
+ # Nav Menu
+ cards) printf %s "Teil­neh­mende";;
+ courses) printf %s "Kurse";;
+
+ # VCF Default
+ PHOTO) printf %s "Foto";;
+ LOGO) printf %s "Logo";;
+ FN) printf %s "Voller Name";;
+ N) printf %s "Name";;
+ n_pre) printf %s "Titel";;
+ n_first) printf %s "Vorname";;
+ n_middle) printf %s "Mittel­namen";;
+ n_last) printf %s "Nachname";;
+ n_post) printf %s "Zusätze";;
+ NICKNAME) printf %s "Spitz­name";;
+ SOUND) printf %s "Aus­sprache";;
+ GENDER) printf %s "Ge­schlecht";;
+ KIND) printf %s "Typ";;
+ TITLE) printf %s "Beruf";;
+ ROLE) printf %s "Position";;
+ ORG) printf %s "Orga­ni­sation";;
+ MEMBER) printf %s "Mitglied";;
+ CATEGORIES) printf %s "Kategorien";;
+ ANNIVERSARY) printf %s "Jubiläum";;
+ BDAY) printf %s "Geburtstag";;
+ EMAIL) printf %s "E-Mail";;
+ TEL) printf %s "Telefon";;
+ teltype) printf %s "Anschluss­typ:";;
+ TYPE=HOME) printf %s "Privat";;
+ TYPE=WORK) printf %s "Geschäft­lich";;
+ TYPE=CELL) printf %s "Mobil";;
+ TYPE=FAX) printf %s "Fax";;
+ IMPP) printf %s "Chat";;
+ ADR) printf %s "Anschrift";;
+ URL) printf %s "Webseite";;
+ LANG) printf %s "Sprache";;
+ NOTE) printf %s "Notiz";;
+ RELATED) printf %s "Kontakte";;
+
+ # ICS Default
+ SUMMARY) printf "Bezeichnung";;
+ COMMENT) printf "Kommentar";;
+ DTSTART) printf "Beginn";;
+ DURATION) printf "Dauer";;
+ RRULE) printf "Regelmäßigkeit";;
+ DAILY) printf "Tage";;
+ WEEKLY) printf "Wochen";;
+ MONTHLY) printf "Monate";;
+ YEARLY) printf "Jahre";;
+ sDAILY) printf "Täglich";;
+ sWEEKLY) printf "Wöchentlich";;
+ sMONTHLY) printf "Monatlich";;
+ sYEARLY) printf "Jährlich";;
+
+ # UI labels
+ year) printf %s "Jahr";;
+ month) printf %s "Monat";;
+ day) printf %s "Tag";;
+ edit) printf %s "Bearbeiten";;
+ edit_categories) printf %s "Kategorien Bearbeiten";;
+ vcf_export) printf %s "Vcard Exportieren";;
+ control) printf %s "Aktionen";;
+ delete) printf %s "entfernen";;
+ edit_update) printf %s "Daten übernehmen";;
+ edit_cancel) printf %s "Abbrechen";;
+ edit_delete) printf %s "Eintrag löschen";;
+ edit_addfieldtext) printf %s "Neues Feld";;
+ edit_addfield) printf %s "+";;
+ edit_deletefield) printf %s "X";;
+
+ filter_label) printf %s "Filter";;
+ filter_item) printf %s "Eingrenzung nach";;
+ filter_placeholder) printf %s "Begriffe zur Eingrenzung eingeben";;
+ filter_type) printf %s "Filter­typ";;
+ filter_order) printf %s "Sortie­rung";;
+ filter_any) printf %s "Alles";;
+ filter_name) printf %s "Name";;
+ filter_firstname) printf %s "Vor­name";;
+ filter_lastname) printf %s "Nach­name";;
+ filter_street) printf %s "Straße";;
+ filter_zip) printf %s "PLZ.";;
+ filter_TEL) printf %s "Tele­fon";;
+ filter_BDAY) printf %s "Geburts­jahr";;
+ filter_bdate) printf %s "Geburts­datum";;
+ filter_course) printf %s "Kurs";;
+ filter_CATEGORIES) printf %s "Kate­go­rien";;
+ filter_more) printf %s "+ mehr Filter";;
+ filter_apply) printf %s "Filtern";;
+ filter_cancel) printf %s "Filter löschen";;
+ export_csv) printf %s "Liste als CSV-Datei";;
+
+ # UI Labels Special
+ course_attendance) printf %s "Kurs­teil­nahme";;
+ vcf_seed_label) printf "Anmeld. Vorn. Nachn. Geb. Monat Geb. Jahr Tel. Mobil () EMail () Notiz";;
+
+ gender_none) printf %s "keine Angabe";;
+ gender_female) printf %s "Weiblich";;
+ gender_male) printf %s "Männlich";;
+ gender_other) printf %s "Sonstiges";;
+
+ female) printf %s "♀";;
+ male) printf %s "♂";;
+ other) printf %s "⚥";;
+ none) printf %s "⚪";;
+
+ # Fallback
+ *) printf %s "$word";;
+ esac
+}
+
+l10n_time() {
+ [ $# -eq 0 ] && read -r time || time="$*"
+ printf '%s\n' "$time" |sed -E '
+ s;Monday;Mon\­\;tag;g; s;Mon\.;Mo.;g;
+ s;Tuesday;Diens\­\;tag;g; s;Tue\.;Di.;g;
+ s;Wednesday;Mitt\­\;woch;g; s;Wed\.;Mi.;g;
+ s;Thursday;Don\­\;ners\­\;tag;g; s;Thu\.;Do.;g;
+ s;Friday;Frei\­\;tag;g; s;Fri\.;Fr.;g;
+ s;Saturday;Sams\­\;tag;g; s;Sat\.;Sa.;g;
+ s;Sunday;Sonn\­\;tag;g; s;Sun\.;So.;g;
+
+ s;January;Ja\­\;nu\­\;ar;g; s;Jan\.;Jan.;g;
+ s;February;Fe\­\;bru\­\;ar;g; s;Feb\.;Feb.;g;
+ s;March;März;g; s;Mar\.;Mär.;g;
+ s;April;April;g; s;Apr\.;Apr.;g;
+ s;May;Mai;g; s;May\.;Mai.;g;
+ s;June;Juni;g; s;Jun\.;Jun.;g;
+ s;July;Juli;g; s;Jul\.;Jul.;g;
+ s;August;Au\­\;gust;g; s;Aug\.;Aug.;g;
+ s;September;Sep\­\;tem\­\;ber;g; s;Sep\.;Sep.;g;
+ s;October;Ok\­\;to\­\;ber;g; s;Oct\.;Okt.;g;
+ s;November;No\­\;vem\­\;ber;g; s;Nov\.;Nov.;g;
+ s;December;De\­\;zem\­\;ber;g; s;Dec\.;Dez.;g;
+ '
+}
+
+parse_date() {
+ [ $# -eq 0 ] && read -r date || date="$*"
+
+ case $date in
+ *[0-9].*[0-9].*[0-9])
+ d="${date%%.*}"
+ y="${date##*.}"
+ m="${date%.*}"
+ m="${m#*.}"
+ [ $y -lt 100 ] && y="$((y + 2000))"
+ date -d "$(printf '%04i-%02i-%02i' "$y" "$m" "$d")" +%F
+ ;;
+ *) date -d "$date" +%F
+ ;;
+ esac
+}
+++ /dev/null
-#!/bin/zsh
-
-# Copyright 2014 - 2017 Paul Hänsch
-#
-# This file is part of Confetti.
-#
-# Confetti is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# Confetti is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with Confetti. If not, see <http://www.gnu.org/licenses/>.
-
-BR='
-'
-
-force_items(){
- for each in "$@"; do
- [ -z "${values[$each]+x}" ] && values[${each}]=''
- done
-}
-
-case $PROFILE in
-medical)
- SUP_FIELDS=(N NICKNAME GENDER BDAY ADR TEL EMAIL X-HEALTH-INSURANCE X-HEALTH-INSURANCE-NOCONTRIB IMPP URL NOTE X-CLIENT-REFERRAL)
- FORCE_ITEMS=(ADR TEL EMAIL NOTE X-CLIENT-REFERRAL)
- _GET[order]="${_GET[order]:-lastname}"
- _GET[filtertype]="${_GET[filtertype]:-name}"
- profile_medical=x
-;;
-circus)
- SUP_FIELDS=(N NICKNAME GENDER BDAY X-ZACK-JOINDATE X-ZACK-LEAVEDATE EMAIL TEL IMPP ADR URL NOTE)
- FORCE_ITEMS=(BDAY X-ZACK-JOINDATE TEL EMAIL ADR NOTE)
- _GET[order]="${_GET[order]:-firstname}"
- _GET[filtertype]="${_GET[filtertype]:-any}"
- profile_circus=x
-;;
-esac
-
-edit="${_GET[edit]}"
-[ \! -f "vcard/$edit" -a \! -f "temp/$edit" ] && edit=''
-
-listcourses() {
- ls -1 ${_DATA}/ical/*ics |while read file; do
- icstime="$(sed -rn 's:^DTSTART\:(TZID=.*\:)?([0-9]{4})([0-9]{2})([0-9]{2})T([0-9]{2})([0-9]{2})([0-9]{2})Z?\r$:\2-\3-\4 \5\:\6\:\7:p' "$file")"
- echo "$(date -d "$icstime" "+%u %H%M%S")\t$file"
- done |sort |sed -r 's:^.*\t(.*/)([^/]+)$:\2:'
-}
-
-list_hi_companies(){
- sed -rn 's;^X-HEALTH-INSURANCE:([^\;]+)\;.*$;\1;p' ${_DATA}/vcard/*vcf \
- | sort -u
-}
-
-list_categories() {
- catfile="${_DATA}/mappings/categories"
- sort -u "$catfile" \
- | sed -r '/^[\t ]*$/d'
-}
-
-listcards_order(){
- while read file; do
- case "${_GET[order]}" in
- firstname)
- printf '%s\t%s\n' "$(sed -rn 's:^N(;.+)*\:([^;]*;){1} *([^;]*).*$:\3:p' "$file")" "$file"
- ;;
- lastname)
- printf '%s\t%s\n' "$(sed -rn 's:^N(;.+)*\:([^;]*;){0} *([^;]*).*$:\3:p' "$file")" "$file"
- ;;
- bdate)
- printf '%s\t%s\n' "$(sed -rn 's:^BDAY(;.+)*\:(.*)$:\2:p' "$file")" "$file"
- ;;
- *) printf 'x\t%s\n' "$file"
- ;;
- esac
- done \
- | sort -u |sed -r 's;^.*\t;;'
-}
-
-listcards_filter(){
- filterex='s;^([^\n]+)\n.*$;\1;p'
- printf '%s\n' "${_GET[filter]}" |tr '^' '\n' \
- | sed -r 's;[]\/\(\)\\\^\$\?\.\+\*\;\[\{\}];\\\\&;g' \
- | while read each; do
- case $each in
- name:*) expr='(FN|NICKNAME|N)(\;[^\n]+)*:[^\n]*'"(${each#*:})";;
- street:*) expr='ADR(\;[^\n]+)*:([^\;]*;){2}[^\;\n]*'"(${each#*:})";;
- zip:*) expr='ADR(\;[^\n]+)*:([^\;]*;){5}[^\;\n]*'"(${each#*:})";;
- any:*|:*) expr="[^\n]*"'(\;[^\n]+)*:[^\n]*'"(${each#*:})";;
- *:*) expr="${each%%:*}"'(\;[^\n]+)*:[^\n]*'"(${each#*:})";;
- *) expr="(${each})";;
- esac
- filterex='/(^|\n)'"${expr}"'/I{'"${filterex}"'}'
- done
-
- while read n; do
- { printf '%s\n' "$n"; cat "$n"; } \
- | sed -En ':X;N;$!bX; {'"$filterex"'}'
- done
-
-}
-
-listcards() {
- printf %s\\n "${_DATA}/vcard/"*.vcf \
- | listcards_filter \
- | listcards_order \
- | sed -E 's;^(.*/)*;;;'
-}
-
-listcards_mail() {
- printf %s\\n "${_DATA}/vcard/"*.vcf \
- | listcards_filter \
- | xargs -r -d\\n sed -En 's:^EMAIL(;.+)*\:(.+)\r$:\2,:p'
-}
-
-vcf_parse() {
- unset key
- sed -r ':X;N;$!bX; s;\r\n[ \t];;g; s;\r\n;\n;g;' "$1" \
- | sed -rn '
- # === turn property names to upper case, strip group names ===
- h; s;^([^;:]+);;;
- x; s;^([^;:\.]+\.)?([^;:]+).*$;\2;;
- y;abcdefghijklmnopqrstuvwxyz;ABCDEFGHIJKLMNOPQRSTUVWXYZ;
- G; s;\n;;;
-
- # === strip trailing CR (but keep CRs in property value) ===
- s;\r$;;;
-
- # === Normalise various known vendor properties ===
- s;^X-MS-CARDPICTURE(\;|:);PHOTO\1;;
- s;^X-GENDER(\;|:);GENDER\1;;
- s;^X-ANNIVERSARY(\;|:);ANNIVERSARY\1;;
- s;^X-EVOLUTION-ANNIVERSARY(\;|:);ANNIVERSARY\1;;
- s;^X-KADDRESSBOOK-X-ANNIVERSARY(\;|:);ANNIVERSARY\1;;
- s;^X-EVOLUTION-BLOG-URL(\;|:);URL\1;;
- s;^AGENT(\;|:);RELATED\;VALUE=text\;TYPE=agent\1;;
- s;^X-ASSISTANT(\;|:);RELATED\;VALUE=text\;TYPE=assistant\1;;
- s;^X-EVOLUTION-ASSISTANT(\;|:);RELATED\;VALUE=text\;TYPE=assistant\1;;
- s;^X-KADDRESSBOOK-X-ASSISTANTSNAME(\;|:);RELATED\;VALUE=text\;TYPE=assistant\1;;
- s;^X-MANAGER(\;|:);RELATED\;VALUE=text\;TYPE=manager\1;;
- s;^X-EVOLUTION-MANAGER(\;|:);RELATED\;VALUE=text\;TYPE=manager\1;;
- s;^X-KADDRESSBOOK-X-MANAGERSNAME(\;|:);RELATED\;VALUE=text\;TYPE=manager\1;;
- s;^X-SPOUSE(\;|:);RELATED\;VALUE=text\;TYPE=spouse\1;;
- s;^X-EVOLUTION-SPOUSE(\;|:);RELATED\;VALUE=text\;TYPE=spouse\1;;
- s;^X-KADDRESSBOOK-X-SPOUSENAME(\;|:);RELATED\;VALUE=text\;TYPE=spouse\1;;
-
- # === Normalise obsolete vendor IM properties ===
- s;^X-AIM((\;[A-z0-9-]+|\;[A-z0-9-]+=([^;,:"]+|"[^"]+")(,[^;,:"]+|,"[^"]+")*)*):;IMPP\1:aim:;;
- s;^X-ICQ((\;[A-z0-9-]+|\;[A-z0-9-]+=([^;,:"]+|"[^"]+")(,[^;,:"]+|,"[^"]+")*)*):;IMPP\1:aim:;;
- s;^X-GOOGLE-TALK((\;[A-z0-9-]+|\;[A-z0-9-]+=([^;,:"]+|"[^"]+")(,[^;,:"]+|,"[^"]+")*)*):;IMPP\1:xmpp:;;
- s;^X-JABBER((\;[A-z0-9-]+|\;[A-z0-9-]+=([^;,:"]+|"[^"]+")(,[^;,:"]+|,"[^"]+")*)*):;IMPP\1:xmpp:;;
- s;^X-MSN((\;[A-z0-9-]+|\;[A-z0-9-]+=([^;,:"]+|"[^"]+")(,[^;,:"]+|,"[^"]+")*)*):;IMPP\1:msn:;;
- s;^X-YAHOO((\;[A-z0-9-]+|\;[A-z0-9-]+=([^;,:"]+|"[^"]+")(,[^;,:"]+|,"[^"]+")*)*):;IMPP\1:ymsgr:;;
- s;^X-SIP((\;[A-z0-9-]+|\;[A-z0-9-]+=([^;,:"]+|"[^"]+")(,[^;,:"]+|,"[^"]+")*)*):(sip:)?;IMPP\1:sip:;;
-
- # === Update obsolete LABEL property ===
- s;^LABEL((\;[A-z0-9-]+|\;[A-z0-9-]+=([^;,:"]+|"[^"]+")(,[^;,:"]+|,"[^"]+")*)*):(.*)$;ADR\1\;LABEL="\5":;;
-
- /^([A-Z0-9-]+)((\;[A-z0-9-]+=?|\;[A-z0-9-]+=([^;,:"]+|"[^"]+")(,[^;,:"]+|,"[^"]+")*)*):(.*)$/{
- h;
- s;^([A-Z0-9-]+)((\;[A-z0-9-]+=?|\;[A-z0-9-]+=([^;,:"]+|"[^"]+")(,[^;,:"]+|,"[^"]+")*)*):(.*)$;key='\''\1'\'';;
- H; g;
-
- s;^([A-Z0-9-]+)((\;[A-z0-9-]+=?|\;[A-z0-9-]+=([^;,:"]+|"[^"]+")(,[^;,:"]+|,"[^"]+")*)*):([^\n]*)\n.*$;\2;;
- s;'\'';'\'\\\\\'\'';g
- s;\;([A-Z0-9-]+)(=|=(([^\;,:"]+|"[^"]+")(,[^\;,:"]+|,"[^"]+")*))?;\ntag[\1]='\''\3'\'';g
- s;^\n+;;; s;\n+$;;;
- /^.+$/H; g;
-
- s;^([A-Z0-9-]+)((\;[A-z0-9-]+=?|\;[A-z0-9-]+=([^;,:"]+|"[^"]+")(,[^;,:"]+|,"[^"]+")*)*):([^\n]*)\n.*$;\6;;
- # :X s;((^|[^\\])(\\\\)*)[;,];\1\n;g; s;((^|[^\\])(\\\\)*)\\([;,]);\4;g; tX;
- s;'\'';'\'\\\\\'\'';g
- s;^.*$;value='\''&'\'';
- H; g;
-
- s;^[^\n]*[\n ]+;;
- s;\n+$;;;
-
- p;
- }
- ' \
- | while read -r line; do
- declare -A tag
- case "$line" in
- value*) eval "$line";;
- tag*) eval "$line";;
- key*)
- if [ -z "$key" ]; then
- eval "$line"
- else
- printf '%s\n' "$value" |sed -rn '
- :X
- s;((^|[^\\])(\\\\)*),;\1\n;g;
- tX;
- s;\\,;,;g;
- p
- ' \
- | while read -r val; do
- while [ -n "${values[$key$n]+x}" ]; do n=$((${n=-1} + 1)); done
- if printf '%s\n' "$val" |grep -qE '((^|[^\\])(\\\\)*)\;'; then
- m=0
- values[${key}${n}]="${val}"
- printf '%s\n' "$val" |sed -rn '
- :X
- s;((^|[^\\])(\\\\)*)\;;\1\n;g;
- tX;
- s;\\\;;\;;g;
- p
- ' \
- | while read -r v; do
- values[${key}${n}+${m}]="$(
- printf %s\\n "${v}" \
- | sed -rn '
- :X
- s;((^|[^\\])(\\\\)*)\\n;\1\n;g;
- tX;
- s;\\\\;\\;g;
- p
- '
- )"
- m=$(($m + 1))
- done
- else
- values[${key}${n}]="$(
- printf %s\\n "${val}" \
- | sed -rn '
- :X
- s;((^|[^\\])(\\\\)*)\\n;\1\n;g;
- tX;
- s;\\\;;\;;g;
- s;\\\\;\\;g;
- p
- '
- )"
- fi
- for t in ${(k)tag}; do
- values[${key}${n}_${t}]="${tag[$t]}"
- done
- done
-
- eval "$line"
- unset n
- while [ -n "${values[$key$n]+x}" ]; do n=$((${n=-1} + 1)); done
- unset value
- unset tag
- fi
- ;;
- esac
- done
-}
-
-view_card() { #Parameter: Cardfile
- id="$1"
- cardfile="$_DATA/vcard/${id}"
- cachefile="$_DATA/cache/${id}.cache"
- if [ "$cachefile" -nt "$cardfile" ]; then
- cat "$cachefile"
- else
- declare -A values
- vcf_parse "$cardfile"
- . "$_EXEC/templates/view_card.sh" |tee "$cachefile"
- fi
-}
-
-edit_card() { #Parameter: Cardfile
- id="$1"
- cardfile="$_DATA/vcard/$id"
- tempfile="$_DATA/temp/$id"
- [ -f "$tempfile" ] && cardfile="$tempfile"
-
- declare -A values
- vcf_parse "$cardfile"
- force_items $FORCE_ITEMS
- . "$_EXEC/templates/edit_card.sh"
-}
+++ /dev/null
-#!/bin/zsh
-
-# Copyright 2015, 2017 Paul Hänsch
-#
-# This file is part of Confetti.
-#
-# Confetti is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# Confetti is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with Confetti. If not, see <http://www.gnu.org/licenses/>.
-
-catfile="${_DATA}/mappings/categories"
-
-listcards() {
- for file in "${_DATA}/vcard/"*.vcf; do
- printf '%s\t%s\n' \
- "$(sed -rn 's:^N(;.+)*\:([^;]*;){1} *([^;]*).*$:\3:p' "$file")" \
- "$file"
- done \
- | sort \
- | sed -r 's;^.*\t(.*/)([^/]+)$;\2;'
-}
-
-get_name() {
- cfile="${_DATA}/vcard/$1"
- sed -rn 's;^N(\;[^":]+|\;"[^"]+")*:([^\;]*)(\;[^\;]*)(\;[^\;]*)?(\;[^\;]*)?(\;[^\;]*)?\r?$;\5 \3 \4 \2 \6;;
- tX; b; :X s;[,\; ]+; ;g; p;' "$cfile"
-}
-
-get_categories(){
- cfile="${_DATA}/vcard/$1"
- sed -rn 's;^CATEGORIES(\;[^":]+|\;"[^"]+")*:(.+)\r?$;\2;;
- tX; b; :X
- s;(^|[^\\]+)((\\\\)+),;\1\2\n;g;
- s;(^|[^\\]),;\1\n;g; s;(^|[^\\]+)((\\\\)+),;\1\2\n;g;
- s;(^|[^\\]),;\1\n;g; s;\\,;,;g; p;' "$cfile" \
- | sort -u
-}
-
-list_categories() {
- sort -u "$catfile" \
- | sed -r '/^[\t ]*$/d'
-}
+++ /dev/null
-#!/bin/zsh
-
-# Copyright 2014, 2016, 2017 Paul Hänsch
-#
-# This file is part of Confetti.
-#
-# Confetti is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# Confetti is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with Confetti. If not, see <http://www.gnu.org/licenses/>.
-
-[ -z "${_GET[order]}" ] && _GET[order]=DOW
-
-listcards() {
- ls -1 ${_DATA}/vcard/*vcf 2>/dev/null |while read file; do
- fn=$(sed -rn 's:^N(;.+)*\:([^;]*;){1} *([^;]*).*$:\3:p' "$file")
- echo "$fn\t$file"
- done |sort |sed -r 's:^.*\t(.*/)([^/]+)$:\2:'
-}
-
-
-listcourses() {
- case "${_GET[filtertype]}" in
- *) ls -1 ${_DATA}/ical/*ics 2>/dev/null
- ;;
- esac |case "${_GET[order]}" in
- DOW)
- while read file; do
- icstime="$(sed -rn 's:^DTSTART\:(TZID=.*\:)?([0-9]{4})([0-9]{2})([0-9]{2})T([0-9]{2})([0-9]{2})([0-9]{2})Z?\r$:\2-\3-\4 \5\:\6\:\7:p' "$file")"
- echo "$(date -d "$icstime" "+%u %H%M%S")\t$file"
- done
- ;;
- TOD)
- while read file; do
- icstime="$(sed -rn 's:^DTSTART\:(TZID=.*\:)?([0-9]{4})([0-9]{2})([0-9]{2})T([0-9]{2})([0-9]{2})([0-9]{2})Z?\r$:\2-\3-\4 \5\:\6\:\7:p' "$file")"
- echo "$(date -d "$icstime" +%H%M%S)\t$file"
- done
- ;;
- *)
- sed -r 's:^.*$:x\t&:'
- ;;
- esac |sort |sed -r 's:^.*\t(.*/)([^/]+)$:\2:'
-}
-
-list_attendance() {
- id="$1"
- sed -rn 's:'$id'\t(.+)$:\1:p' "$_DATA/mappings/attendance" |while read each; do
- card="$_DATA/vcard/$each"
- if [ -r "$card" ]; then
- aname="$(sed -rn '
- /^N(;.+)*:/{
- h;
- s;^N(\;.+)*:([^\;]*\;){3} *([^\;]*).*$;\3;p; g;
- s;^N(\;.+)*:([^\;]*\;){1} *([^\;]*).*$;\3;p; g;
- s;^N(\;.+)*:([^\;]*\;){2} *([^\;]*).*$;\3;p; g;
- s;^N(\;.+)*:([^\;]*\;){0} *([^\;]*).*$;\3;p; g;
- s;^N(\;.+)*:([^\;]*\;){4} *([^\;]*).*$;\3;p; g;
- }' "$card" \
- | sed -r ':X;N;$!bX; s;([\;\n\r,]| )+; ;g;; s;^ +| +$;;g;'
- )"
- fname="$(sed -rn 's;^FN(\;.+)*:(.+)\r?$;\2;g; s;([\;\n,]| )+; ;g;; s;^ +| +$;;g;' "$card")"
- nname="$(sed -rn 's;^NICKNAME(\;.+)*:(.+)\r?$;\2;g; s;([\;\n,]| )+; ;g;; s;^ +| +$;;g;' "$card")"
- byear="$(sed -rn 's:^BDAY(\;.+)*\:([0-9]{4})(-[0-9][0-9]){2}.*$:\2:p' "$_DATA/vcard/$each")"
-
- printf '%s %s (*%04i)\n' "$each" "${aname:-${fname:-${nname}}}" "$byear"
- fi
- done
-}
-
-course_mail() {
- id="$1"
- coursemail=""
- sed -rn 's:'$id'\t(.+)$:\1:p' "$_DATA/mappings/attendance" |while read each; do
- coursemail="$coursemail$(sed -rn 's:^EMAIL(;.+)*\:(.+)\r$:\2,:p' "$_DATA/vcard/$each")"
- done
- echo "$coursemail"
-}
-
-ics_parse() {
- unset key
- sed -r ':X;N;$!bX; s;\r\n[ \t];;g; s;\r\n;\n;g;' "$1" \
- | sed -rn '
- # === turn property names to upper case, strip group names ===
- h; s;^([^;:]+);;;
- x; s;^([^;:\.]+\.)?([^;:]+).*$;\2;;
- y;abcdefghijklmnopqrstuvwxyz;ABCDEFGHIJKLMNOPQRSTUVWXYZ;
- G; s;\n;;;
-
- /^([A-Z0-9-]+)((\;[A-z0-9-]+=?|\;[A-z0-9-]+=([^;,:"]+|"[^"]+")(,[^;,:"]+|,"[^"]+")*)*):(.*)$/{
- h;
- s;^([A-Z0-9-]+)((\;[A-z0-9-]+=?|\;[A-z0-9-]+=([^;,:"]+|"[^"]+")(,[^;,:"]+|,"[^"]+")*)*):(.*)$;key='\''\1'\'';;
- H; g;
-
- s;^([A-Z0-9-]+)((\;[A-z0-9-]+=?|\;[A-z0-9-]+=([^;,:"]+|"[^"]+")(,[^;,:"]+|,"[^"]+")*)*):([^\n]*)\n.*$;\2;;
- s;'\'';'\'\\\\\'\'';g
- s;\;([A-Z0-9-]+)(=|=(([^\;,:"]+|"[^"]+")(,[^\;,:"]+|,"[^"]+")*))?;\ntag[\1]='\''\3'\'';g
- s;^\n+;;; s;\n+$;;;
- /^.+$/H; g;
-
- s;^([A-Z0-9-]+)((\;[A-z0-9-]+=?|\;[A-z0-9-]+=([^;,:"]+|"[^"]+")(,[^;,:"]+|,"[^"]+")*)*):([^\n]*)\n.*$;\6;;
- # :X s;((^|[^\\])(\\\\)*)[;,];\1\n;g; s;((^|[^\\])(\\\\)*)\\([;,]);\4;g; tX;
- s;'\'';'\'\\\\\'\'';g
- s;^.*$;value='\''&'\'';
- H; g;
-
- s;^[^\n]*[\n ]+;;
- s;\n+$;;;
-
- p;
- }
- ' \
- | while read -r line; do
- declare -A tag
- case "$line" in
- value*) eval "$line";;
- tag*) eval "$line";;
- key*)
- if [ -z "$key" ]; then
- eval "$line"
- else
- values[$key]="$value"
- for t in ${(k)tag}; do
- values[${key}_$t]="$tag[$t]"
- done
- eval "$line"
- if [ -n "$values[$key]" ]; then
- n=0
- while [ -n "$values[$key$n]" ]; do n=$(($n + 1)); done
- key=$key$n
- fi
- unset value
- unset tag
- fi
- ;;
- esac
- done
-}
-
-view_course() { #Parameter: Calendarfile
- id="$1"
- calendarfile="$_DATA/ical/${id}"
- cachefile="$_DATA/cache/${id}.cache"
- unset key
- if [ "$cachefile" -nt "$calendarfile" ]; then
- cat "$cachefile"
- else
- declare -A values
- ics_parse "$calendarfile"
- . ${_EXEC}/templates/view_course.sh |tee "$cachefile"
- fi
-}
-
-edit_course() { #Parameter: Calendarfile
- id="$1"
- calendarfile="$_DATA/ical/$id"
- tempfile="$_DATA/temp/$id"
- [ -f "$tempfile" ] && calendarfile="$tempfile"
- unset key
-
- declare -A values
- ics_parse "$calendarfile"
- . ${_EXEC}/templates/edit_course.sh
-}
+++ /dev/null
-#!/bin/zsh
-
-# Copyright 2014 Paul Hänsch
-#
-# This file is part of Confetti.
-#
-# Confetti is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# Confetti is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with Confetti. If not, see <http://www.gnu.org/licenses/>.
-
-[ -z "${_GET[order]}" ] && _GET[order]=DOW
-
-listcards() {
- ls -1 ${_DATA}/vcard/*vcf 2>/dev/null |while read file; do
- fn=$(sed -rn 's:^N(;.+)*\:([^;]*;){1} *([^;]*).*$:\3:p' "$file")
- echo "$fn\t$file"
- done |sort |sed -r 's:^.*\t(.*/)([^/]+)$:\2:'
-}
-
-listcourses() {
- ls -1 ${_DATA}/ical/*ics |while read file; do
- icstime="$(sed -rn 's:^DTSTART\:(TZID=.*\:)?([0-9]{4})([0-9]{2})([0-9]{2})T([0-9]{2})([0-9]{2})([0-9]{2})Z?\r$:\2-\3-\4 \5\:\6\:\7:p' "$file")"
- echo "$(date -d "$icstime" "+%u %H%M%S")\t$file"
- done |sort |sed -r 's:^.*\t(.*/)([^/]+)$:\2:'
-}
+++ /dev/null
-#!/bin/zsh
-
-# Copyright 2016 Paul Hänsch
-#
-# This file is part of Confetti.
-#
-# Confetti is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# Confetti is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with Confetti. If not, see <http://www.gnu.org/licenses/>.
-
-source "$_EXEC/pages/cards.sh"
-declare -A mpx
-
-BR='
-'
-
-view_card="$_EXEC/templates/view_client.sh"
-
-list_prescriptions(){
- client="$1"
- find "$_DATA/prescriptions/" -name "${client%.vcf}.*.mpx" \
- | while read pfile; do
- printf '%s\t%s\n' "$(grep '^date' "$pfile")" "${pfile##*/}"
- done \
- | sort -r | cut -f2
-}
-
-list_prescription_issuers(){
- sed -rn 's;^issuer:(.+)$;\1;p' ${_DATA}/prescriptions/*.mpx \
- | sort -u
-}
-
-edit_prescription(){
- id="$1"
- prescfile="$_DATA/prescriptions/$id"
- tempfile="$_DATA/temp/$id"
- [ -f "$tempfile" ] || cp "$prescfile" "$tempfile"
-
- mpx=()
- cat "$tempfile" |while read -r line; do
- val="${line#*:}"
- mpx[${line%%:*}]="${val//\\n/$BR}"
- done
-
- . "$_EXEC/templates/edit_prescription.sh"
-}
-
-view_prescription(){
- id="$1"
- prescfile="$_DATA/prescriptions/$id"
-
- mpx=()
- cat "$prescfile" |while read -r line; do
- val="${line#*:}"
- mpx[${line%%:*}]="$(htmlsafe "${val//\\n/$BR}")"
- done
-
- . "$_EXEC/templates/view_prescription.sh"
-}
-
-therapy_dates(){
- tpyfile="$_DATA/therapies/${1%.mpx}.tpy"
- sed -rn 's;^session[0-9]+_date:(.+)$;\1;p' "$tpyfile"
-}
+++ /dev/null
-#!/bin/zsh
-
-# Copyright 2016 Paul Hänsch
-#
-# This file is part of Confetti.
-#
-# Confetti is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# Confetti is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with Confetti. If not, see <http://www.gnu.org/licenses/>.
-
-declare -A tpy
-declare -A mpx
-
-BR='
-'
-
-id="$_GET[id]"
-
-tpyfile="$_DATA/therapies/$id"
-mpxfile="$_DATA/prescriptions/${id%.tpy}.mpx"
-client="$_DATA/vcard/${id%%.*}.vcf"
-
-identify "$_EXEC/static/therapy_background.png" \
-| sed -r 's;^.* ([0-9]+x[0-9]+) .*$;\1;' \
-| read bg_dim
-
-card_N="$(sed -nr 's:^(N)(;[^"\:]+|;"[^"]+")*\:(.*)$:\3:gp' "$client")"
-card_FN="$(sed -nr 's:^(FN)(;[^"\:]+|;"[^"]+")*\:(.*)$:\3:gp' "$client")"
-card_NICK="$(sed -nr 's:^(NICKNAME)(;[^"\:]+|;"[^"]+")*\:(.*)$:\3:gp' "$client")"
-
-n=$(printf %s "$card_N" \
- | sed -rn 's:^([^;]*)(\;[^;]*)(\;[^;]*)?(\;[^;]*)?(\;[^;]*)?$:\4 \2 \3 \1 \5:gp' \
- | sed -r 's:,: :;s:\;: :g;s: +: :g;s:^ $::;'
- )
-client_name="${n:-${card_FN:-${card_NICK}}}"
-
-mpx=()
-cat "$mpxfile" |while read -r line; do
- val="${line#*:}"
- mpx[${line%%:*}]="$(htmlsafe "${val//\\n/$BR}")"
-done
-
-tpy=()
-cat "$tpyfile" |while read -r line; do
- val="${line#*:}"
- tpy[${line%%:*}]="$(htmlsafe "${val//\\n/$BR}")"
-done
--- /dev/null
+#!/bin/zsh
+
+# Copyright 2014 - 2018 Paul Hänsch
+#
+# This file is part of Confetti.
+#
+# Confetti is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Confetti is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with Confetti. If not, see <http://www.gnu.org/licenses/>.
+
+# This is a parsing library for the Personal Data Interchange format (PDI)
+# PDI is the format for encoding VCard (.vcf) and iCalendar (.ics) files
+
+[ -n "$include_pdi" ] && return 0
+include_pdi="$0"
+
+BR='
+'
+
+unescape() {
+ local unescape='s;(^(\\\\)*|[^\\](\\\\)*)\\n;\1\n;g; s;\\(.);\1;g'
+ if [ $# -eq 0 ]; then
+ sed -E "$unescape"
+ else
+ printf %s "$*" \
+ | sed -E "$unescape"
+ fi
+}
+
+pdi_load() {
+ # normalise PDI file for processing with pdi_* functions
+ # functions in this library can only be applied to normalised data
+ # Usage example:
+ # data="$(pdi_load file.vcf)"
+
+ sed -srn '
+ # === Read entire file into buffer ===
+ :X $bY; N; bX; :Y s;^.*$;\n&\n;;
+
+ # === Join continuing lines, strip trailing CRs ===
+ s;\r*\n[ \t];;g;
+ s;\r*\n;\n;g;
+
+ # === turn property names to upper case, strip group names ===
+ s;\n([^;:\.\n]+\.)([^;:\n]+);\n\2;g;
+ :upcase
+ s;(\n[^;:]*)a;\1A;g; s;(\n[^;:]*)b;\1B;g; s;(\n[^;:]*)c;\1C;g; s;(\n[^;:]*)d;\1D;g; s;(\n[^;:]*)e;\1E;g;
+ s;(\n[^;:]*)f;\1F;g; s;(\n[^;:]*)g;\1G;g; s;(\n[^;:]*)h;\1H;g; s;(\n[^;:]*)i;\1I;g; s;(\n[^;:]*)j;\1J;g;
+ s;(\n[^;:]*)k;\1K;g; s;(\n[^;:]*)l;\1L;g; s;(\n[^;:]*)m;\1M;g; s;(\n[^;:]*)n;\1N;g; s;(\n[^;:]*)o;\1O;g;
+ s;(\n[^;:]*)p;\1P;g; s;(\n[^;:]*)q;\1Q;g; s;(\n[^;:]*)r;\1R;g; s;(\n[^;:]*)s;\1S;g; s;(\n[^;:]*)t;\1T;g;
+ s;(\n[^;:]*)u;\1U;g; s;(\n[^;:]*)v;\1V;g; s;(\n[^;:]*)w;\1W;g; s;(\n[^;:]*)x;\1X;g; s;(\n[^;:]*)y;\1Y;g;
+ s;(\n[^;:]*)z;\1Z;g;
+ t upcase;
+
+ # === Insert empty attribute fields where no attributes are present ===
+ s;\n([^;:]+):;\n\1\;:;g;
+
+ # === Unscramble aggregated fields ===
+ :disag
+ s;\n([^:\n]+:)(([^\n]*[^\])?(\\\\)*),;\n\1\2\n\1;;
+ t disag;
+
+ # === Insert FN when only N is present ===
+ /\nFN[;:]/!{
+ s,\nN(;[^:]*)?:([^;\n]*);([^;\n]*);([^;\n]*);([^;\n]*);([^;\n]*);?\n,&FN;:\5 \3 \4 \2 \6\n,;
+ :despace
+ s,(\nFN;:[^\n]*) ([^\n]*\n),\1 \2,;
+ s,(\nFN;:) ([^\n]*\n),\1\2,;
+ s,(\nFN;:[^\n]*) (\n),\1\2,;
+ t despace;
+ }
+ /\nFN[;:]/!{ s,\n(N[;:][^\n]*)\n,&F\1\n,; } # Fallback
+
+ # === Normalise various known vendor properties ===
+ s;\nX-MS-CARDPICTURE(\;|:);\nPHOTO\1;g;
+ s;\nX-GENDER(\;|:);\nGENDER\1;g;
+ s;\nX-ANNIVERSARY(\;|:);\nANNIVERSARY\1;g;
+ s;\nX-EVOLUTION-ANNIVERSARY(\;|:);\nANNIVERSARY\1;g;
+ s;\nX-KADDRESSBOOK-X-ANNIVERSARY(\;|:);\nANNIVERSARY\1;g;
+ s;\nX-EVOLUTION-BLOG-URL(\;|:);\nURL\1;g;
+ s;\nAGENT(\;|:);\nRELATED\;VALUE=text\;TYPE=agent\1;g;
+ s;\nX-ASSISTANT(\;|:);\nRELATED\;VALUE=text\;TYPE=assistant\1;g;
+ s;\nX-EVOLUTION-ASSISTANT(\;|:);\nRELATED\;VALUE=text\;TYPE=assistant\1;g;
+ s;\nX-KADDRESSBOOK-X-ASSISTANTSNAME(\;|:);\nRELATED\;VALUE=text\;TYPE=assistant\1;g;
+ s;\nX-MANAGER(\;|:);\nRELATED\;VALUE=text\;TYPE=manager\1;g;
+ s;\nX-EVOLUTION-MANAGER(\;|:);\nRELATED\;VALUE=text\;TYPE=manager\1;g;
+ s;\nX-KADDRESSBOOK-X-MANAGERSNAME(\;|:);\nRELATED\;VALUE=text\;TYPE=manager\1;g;
+ s;\nX-SPOUSE(\;|:);\nRELATED\;VALUE=text\;TYPE=spouse\1;g;
+ s;\nX-EVOLUTION-SPOUSE(\;|:);\nRELATED\;VALUE=text\;TYPE=spouse\1;g;
+ s;\nX-KADDRESSBOOK-X-SPOUSENAME(\;|:);\nRELATED\;VALUE=text\;TYPE=spouse\1;g;
+
+ # === Normalise obsolete vendor IM properties ===
+ s;\nX-AIM((\;[A-Za-z0-9-]+|\;[A-Za-z0-9-]+=([^;,:"]+|"[^"]+")(,[^;,:"]+|,"[^"]+")*)*):;\nIMPP\1:aim:;g;
+ s;\nX-ICQ((\;[A-Za-z0-9-]+|\;[A-Za-z0-9-]+=([^;,:"]+|"[^"]+")(,[^;,:"]+|,"[^"]+")*)*):;\nIMPP\1:aim:;g;
+ s;\nX-GOOGLE-TALK((\;[A-Za-z0-9-]+|\;[A-Za-z0-9-]+=([^;,:"]+|"[^"]+")(,[^;,:"]+|,"[^"]+")*)*):;\nIMPP\1:xmpp:;g;
+ s;\nX-JABBER((\;[A-Za-z0-9-]+|\;[A-Za-z0-9-]+=([^;,:"]+|"[^"]+")(,[^;,:"]+|,"[^"]+")*)*):;\nIMPP\1:xmpp:;g;
+ s;\nX-MSN((\;[A-Za-z0-9-]+|\;[A-Za-z0-9-]+=([^;,:"]+|"[^"]+")(,[^;,:"]+|,"[^"]+")*)*):;\nIMPP\1:msn:;g;
+ s;\nX-YAHOO((\;[A-Za-z0-9-]+|\;[A-Za-z0-9-]+=([^;,:"]+|"[^"]+")(,[^;,:"]+|,"[^"]+")*)*):;\nIMPP\1:ymsgr:;g;
+ s;\nX-SIP((\;[A-Za-z0-9-]+|\;[A-Za-z0-9-]+=([^;,:"]+|"[^"]+")(,[^;,:"]+|,"[^"]+")*)*):(sip:)?;\nIMPP\1:sip:;g;
+
+ # === Update obsolete LABEL property ===
+ s;\nLABEL((\;[A-Za-z0-9-]+|\;[A-Za-z0-9-]+=([^;,:"]+|"[^"]+")(,[^;,:"]+|,"[^"]+")*)*):(.*)\n;\nADR\1\;LABEL="\5":\n;g;
+
+ p;' "$@"
+}
+
+pdi_count(){
+ local card="$1" name="$2" rc='' cnt=0
+ while rc="${card#*${BR}${name};}"; do
+ [ "${rc}" != "${card}" ] || break
+ card="$rc"
+ cnt=$(($cnt + 1))
+ done
+ printf %i\\n $cnt
+}
+
+pdi_attrib(){
+ local card=":$1" name="$2" cnt="${3:-1}" attr="$4"
+ while [ $cnt -gt 0 ]; do
+ [ "${card#*${BR}${name};}" = "$card" ] && return 1
+ card="${card#*${BR}${name};}"
+ cnt=$((cnt - 1))
+ done
+ card="${card%%:*}"
+ if [ "$attr" ]; then
+ case $card in
+ *\;"$attr"=*) card="${card#*;${attr}=}";;
+ "$attr"=*) card="${card#${attr}=}";;
+ "$attr"|*\;"$attr"|"$attr"\;*|*\;"$attr"\;*) return 0;;
+ *) return 1;;
+ esac
+ case $card in
+ \"*\"\;*|\'*\'\;*)
+ card="${card#[\"\']}"; card="${card%%[\"\'];*}"
+ ;;
+ \"*\"|\'*\')
+ card="${card#[\"\']}"; card="${card%%[\"\']}"
+ ;;
+ *\;*) card="${card%%;*}";;
+ esac
+ fi
+ printf %s\\n "${card}"
+}
+
+pdi_value(){
+ local card="${BR}$1" name="$2" cnt="${3:-1}"
+ while [ "$cnt" -gt 0 ]; do
+ [ "${card#*${BR}${name};*:}" = "$card" ] && return 1
+ card="${card#*${BR}${name};*:}"
+ cnt=$((cnt - 1))
+ done
+ printf %s\\n "${card%%${BR}*}"
+}
+
+pdi_update_value(){
+ local card="${BR}$1" name="$2" cnt="$3" val="$4"
+ while [ "$cnt" -gt 0 ]; do
+ if [ "${card#*${BR}${name};*:}" = "${card}" ]; then
+ printf '%s\n%s;:' "${card%${BR}END;:VCARD*}" "${name}"
+ card="${BR}END;:VCARD"
+ break;
+ else
+ printf '%s\n%s;' "${card%%${BR}${name};*}" "${name}"
+ card="${card#*${BR}${name};}"
+ printf '%s:' "${card%%:*}"
+ card="${card#*:}"
+ fi
+ cnt=$((cnt - 1))
+ done
+ printf '%s\n%s\n' "$val" "${card#*${BR}}"
+}
+
+pdi_update_attrib(){
+ local card="${BR}$1" name="$2" cnt="$3" val="$4"
+ while [ "$cnt" -gt 0 ]; do
+ if [ "${card#*${BR}${name};*:}" = "${card}" ]; then
+ printf '%s\n%s;' "${card%${BR}END;:VCARD*}" "${name}"
+ card=":${BR}END;:VCARD"
+ break;
+ else
+ printf '%s\n%s;' "${card%%${BR}${name};*}" "${name}"
+ card="${card#*${BR}${name};}"
+ fi
+ cnt=$((cnt - 1))
+ done
+ printf '%s:%s\n' "$val" "${card#*:}"
+}
--- /dev/null
+#!/bin/sh
+
+[ "$include_session_lock" ] && return 0
+include_session_lock="$0"
+
+SLOCK(){
+ local file="$1";
+ local timeout="${2-900}"
+ local lockdir="$_DATA/lock/${file#$_DATA}"; lockdir="${lockdir%/}"
+ local ovlock="${lockdir%/*}/delete.${lockdir##*/}"
+ local tempfile="$lockdir/${SESSION_ID}"
+ local lockexpire=$(( $(date +%s) - timeout ))
+
+ mkdir -p "${lockdir%/*}"
+
+ if [ -e "$lockdir" ] \
+ && [ "$(stat -c %Y "$lockdir")" -lt "$lockexpire" ] \
+ && mkdir "$ovlock"; then
+ [ "$(stat -c %Y "$lockdir")" -lt "$lockexpire" ] \
+ && rm -r "$lockdir"
+ rmdir "$ovlock"
+ fi
+
+ printf '%s\n' "$tempfile"
+ if mkdir "$lockdir" 2>&-; then
+ cp "$file" "$tempfile"
+ return 0
+ else
+ return 1
+ fi
+}
+
+CHECK_SLOCK(){
+ local file="$1";
+ local lockdir="$_DATA/lock/${file#$_DATA}"; lockdir="${lockdir%/}"
+ local tempfile="$lockdir/${SESSION_ID}"
+
+ printf '%s\n' "$tempfile"
+ if [ -f "$tempfile" ]; then
+ touch "$lockdir"
+ return 0
+ else
+ return 1
+ fi
+}
+
+RELEASE_SLOCK(){
+ local file="$1";
+ local lockdir="$_DATA/lock/${file#$_DATA}"; lockdir="${lockdir%/}"
+ local ovlock="${lockdir%/*}/delete.${lockdir##*/}"
+ local tempfile="$lockdir/${SESSION_ID}"
+
+ if [ -f "$tempfile" ] && mkdir "$ovlock"; then
+ [ -f "$tempfile" ] && rm -r "$lockdir"
+ rmdir "$ovlock"
+ return 0
+ else
+ return 1
+ fi
+}
+++ /dev/null
-Subproject commit 8d60af41a80ea3760dd51c3bc34eab68d88b16ae
+++ /dev/null
-/*
-# Copyright 2014 - 2017 Paul Hänsch
-#
-# This file is part of Confetti.
-#
-# Confetti is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# Confetti is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with Confetti. If not, see <http://www.gnu.org/licenses/>.
-*/
-
-.filter {
- display: block;
- border: solid 1px;
- border-radius: 4px 4px 0 0;
- margin: .5em 2em .25em 2em;
- padding: 0 2ex .5em 2ex;
- background: #EFF;
-}
-
-.filter h1 {
- display: block;
- font-weight: bold;
- font-size: 1.25em;
- border-style: none none solid none;
- border-radius: 4px 4px 0 0;
- border-width: 1px;
- margin: 0 -1.625ex .5em -1.625ex;
- padding: .125em 1ex;
- background: #EEF;
-}
-.filter input[type="text"] {
- width: 100%;
- margin-bottom: .5em;
-}
-.filter fieldset {
- position: relative;
- display: block;
- border: none;
- padding: 0 0 0 12ex;
- margin: 0 0 .5em 0;
-}
-.filter fieldset legend {
- position: absolute;
- font-size: 1em;
- font-weight: bold;
- margin-left: -12ex;
-}
-.filter label {
- display: inline-block;
- vertical-align: top;
- margin: .125em 2ex 0 0;
- white-space: nowrap;
-}
-.filter button {
- margin-top: .5em;
-}
-
-.filter fieldset.item { padding-left: 0; }
-.filter fieldset.item legend { display: none; }
-.filter fieldset.item input[type=text],
-.filter fieldset.item fieldset.categories {
- background-color: #FFF;
- border: 1px solid #000;
- margin-top: -1px;
- padding: .25em .5em;
-}
-.filter fieldset.item input[type=radio] { display: none; }
-.filter fieldset.item input[type=radio] + label {
- position: relative; z-index: 1;
- margin: 0; padding: .25em 1em;
-
-}
-.filter fieldset.item input[type=radio]:checked + label {
- background-color: #FFF;
- border: 1px solid #000;
- border-bottom-color: #FFF;
-}
-.filter fieldset.item input[type=radio][value="CATEGORIES:"]:checked + label + input[type=text],
-.filter fieldset.item input[type=radio][value="CATEGORIES:"] + label + input[type=text] + fieldset.categories {
- display: none;
-}
-.filter fieldset.item input[type=radio][value="CATEGORIES:"]:checked + label + input[type=text] + fieldset.categories {
- display: block;
-}
-.filter fieldset.item fieldset.categories a {
- font-size: .875em;
- line-height: 1.625em;
- color: #44C;
-}
-
-.newcard {
- display: block;
- border: solid 1px;
- border-radius: 0 0 4px 4px;
- margin: .25em 2em 1em 2em;
- padding: .25em 2ex .25em 2ex;
- background: #EFF;
-}
-
-.dash {
- display: block;
- border: solid 1px;
- border-radius: 4px 4px 0 0;
- margin: .25em 2em .25em 2em;
- padding: .25em 2ex .25em 2ex;
- background: #EFF;
- text-align: right;
-}
-
-.card {
- display: block;
- position: relative;
- border: solid 1px;
- margin: .25em 2em;
- padding: 0 20ex 0 0;
- overflow: auto;
- background: #FFF;
- min-height: 8em;
-}
-.card .control {
- display: inline-block;
- position: absolute;
- top: 0; right: 0;
- text-align: right;
- background: #EEF;
- margin: 0;
- height: 100%;
- width: 20ex;
- max-width: 33%;
- padding: .25em 1ex;
-}
-.card .control .item {
- display: inline-block;
- width: 100%;
- min-height: 2.5em;
- color: #008;
- margin: .25em 0 .5em 0;
- border: 1px solid black;
- background: #FFF;
- padding: .25em .75ex 0 .75ex;
- text-decoration: none;
- font-size: .75em;
-}
-.card .control .item:hover{ border-color: #888; }
-.card .control input.item { font-size: normal; }
-.card .control button.item { text-align: right;}
-.card .control button.item:hover{ border-color: #888;}
-.card .control .item[name=newfield] { width: 85%;}
-.card .control .item[value=addfield] { width: 15%;}
-
-.card .control #delete + label + button,
-.card .control #delete { display: none; }
-.card .control #delete + label { background-color: #FEE; }
-.card .control #delete:checked + label + button {
- position: fixed; display: block;
- left: 40%; top: 30%; width: 20%;
- font-size: 1.5em;
- text-align: center;
- background-color: #FAA;
- color: #333;
- border-radius: .25em 0 0 .25em;
-}
-.card .control #delete:checked + label:after {
- position: fixed; display: block;
- left: 60%; top: 30%; width: 5%;
- margin: .25em 0; padding: .5em .75ex 0 .75ex;
- height: 1.5em;
- font-size: 2em;
- content: 'X';
- text-align: center;
- color: #AAA; background-color: #000;
- border-radius: 0 .25em .25em 0;
-}
-
-.card .section {
- display: inline-block;
- vertical-align: top;
- float: left;
- width: 20ex;
- min-width: 16.5%;
- min-height: 6em;
- margin: .125em 0 .5ex 0;
- padding: 0 1ex .25em 1ex;
-}
-.card .section:nth-of-type(2n){ background: #EEE;}
-
-.card .section h2, .card .section h3 {
- font-size: 1em;
- display: block;
- font-weight: bold;
- margin: .25em -.5ex .25em -.5ex;
- border-style: none none solid none;
- border-width: 1px;
-}
-.card .section .FN { /* will override h2 */
- font-size: 1.2em;
- margin-top: 0;
-}
-
-.card .section .item {
- display: block;
- max-width: 100%;
- word-wrap: break-word;
- white-space: pre-line;
-}
-.card .section .item label {
- font-weight: bold;
-}
-
-.card .section textarea.NOTE {
- min-height: 6em;
-}
-.card .section textarea.ADR {
- min-height: 4em;
-}
-
-.card .section .PHOTO {
- width: 100%;
- max-height: 10em;
-}
-.card .section .GENDER { display: inline-block; padding-right: .5ex;}
-.card .section .BDAY { display: inline-block;}
-
-.card .section select {
- display: block;
- background-color: #FFF;
- border: 1px solid black;
-}
-
-.card .section.attendance ul { margin: 0; padding: 0; }
-.card .section.attendance ul li {
- display: block;
- word-wrap: break-word;
-}
-
-.card .section.prescriptions ul { margin: 0; padding: 0; }
-.card .section.prescriptions ul li {
- display: block;
- word-wrap: break-word;
- margin: 0 .25ex;
-}
-
-.card .section.prescriptions { width: 40ex;}
-
-form.card .section input[type=text],
-form.card .section input:not([type]),
-form.card .section textarea,
-form.card .section select {width: 100%;}
-
-form.card .attendance {
- min-width: 66%;
-}
-form.card .attendance label {
- position: relative;
- display: inline-block;
- width: 25ex;
- padding-left: 3ex;
-}
-form.card .attendance label input[type=checkbox] {
- position: absolute;
- margin-left: -3ex;
-}
-
-form.card .insurance input[type=radio] {display: none;}
-form.card .insurance input[type=radio] + label {
- display: inline-block;
- width: 50%;
- padding: .25ex 0;
- text-align: center;
- border: 1px solid black;
-
-}
-form.card .insurance input[type=radio]:checked + label {
- font-weight: bold;
- background-color: #FFF;
- border-width: 1px 1px 0 1px;
-}
-form.card .insurance input[type=radio] + label + input + label + select,
-form.card .insurance input[type=radio] + label + select + input {display: none;}
-form.card .insurance input[type=radio]:checked + label + input + label + select,
-form.card .insurance input[type=radio]:checked + label + select + input {
- display: block;
- border: 1px solid black;
- background-color: #FFF;
- border-width: 0 1px 1px 1px;
- padding: .25ex 0;
- margin-top: -1px;
-}
+++ /dev/null
-/*
-# Copyright 2014, 2015, 2018 Paul Hänsch
-#
-# This file is part of Confetti.
-#
-# Confetti is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# Confetti is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with Confetti. If not, see <http://www.gnu.org/licenses/>.
-*/
-
-form.categories {
- display: block;
- border: solid 1px;
- border-radius: 4px 4px 0 0;
- margin: .5em 2em .25em 2em;
- padding: 0 2ex .5em 2ex;
- background: #EFF;
-}
-
-form.categories h1 {
- display: block;
- font-weight: bold;
- font-size: 1.25em;
- border-style: none none solid none;
- border-radius: 4px 4px 0 0;
- border-width: 1px;
- margin: 0 -1.625ex .5em -1.625ex;
- padding: .125em 1ex;
- background: #EEF;
-}
-
-form.categories ul { margin: 0; padding: 0; }
-form.categories ul li {
- display: inline-block;
- margin: 0 .25ex;
- padding: .125em .25ex .125em 1ex;
- border: 1px solid black;
- background-color: #EEF;
-}
-form.categories ul li button {
- margin: 0 0 0 1ex;
- background-color: #FCC;
- border: 1px solid black;
- min-width: 3ex;
- text-align: center;
-}
-form.categories ul li:last-of-type { padding: .125em .25ex .125em .25ex; }
-form.categories ul li:last-of-type button { background-color: #FFF; }
-
-form.namelist {
- display: block;
- margin: 1em 2em;
- padding: 0;
-}
-form.namelist > fieldset {
- display: block;
- border: solid 1px;
- border-radius: 4px 4px 0 0;
- margin: 0; padding: .25em 2ex;
- background: #EFF;
- text-align: right;
-}
-form.namelist > fieldset:last-of-type {
- border-radius: 0 0 4px 4px;
-}
-ul.namelist { margin: 0; padding: 0;}
-ul.namelist > li {
- display: block;
- border: solid 1px;
- margin: -1px 0;
- background: #FFF;
-}
-ul.namelist > li:nth-of-type(2n){ background: #EEE;}
-
-ul.namelist h2 {
- font-size: 1em;
- display: inline-block;
- margin: 0;
- padding: 0 1ex;
- min-width: 30ex;
-}
-ul.namelist ul { margin: 0; padding: 0; display: inline-block;}
-ul.namelist ul li {
- display: inline-block;
- margin: 0 1ex 0 0;
-}
-
-.fakeorder {
- position: absolute;
- width: 0; height: 0;
- left: -100%;
-}
+++ /dev/null
-* {
- font-family: sans-serif;
- -moz-box-sizing: border-box;
- -webkit-box-sizing: border-box;
- box-sizing: border-box;
- z-index: 0;
-}
-body {
- background: #EEE;
- margin: 0;
- padding: 0;
- padding-top: 2.5em;
- position: absolute;
- min-height: 100%;
- width: 100%;
- padding-bottom: 2em;
-}
-.NAVIGATION {
- position: fixed;
- top: 0;
- display: inline-block;
- border-style: none solid solid solid;
- border-width: 0 1px 1px 1px;
- border-radius: 0 0 4px 4px;
- margin: 0 2em;
- padding: .5em 1ex;
- background: #FFF;
- z-index: 1;
-}
-.NAVIGATION label,
-.NAVIGATION a {
- color: #008;
- border: solid 1px #BBF;
- margin: .5em .5ex .25em .5ex;
- padding: .2em 3ex .2em 3ex;
- background: #EFF;
-}
-.NAVIGATION label:hover,
-.NAVIGATION a:hover {
- border-width: 2px 1px;
- background: #F3FFFF;
-}
-
-#CONFIGURE label[for="navigationconfig"] { display: none; }
-#CONFIGURE:target label[for="navigationconfig"] { display: inline; }
-
-.NAVIGATION input#navigationconfig { display: none; }
-.NAVIGATION input + form.config {
- position: static;
- display: none;
-}
-
-.NAVIGATION .config a { display: block; }
-.NAVIGATION .config a:hover { border-width: 1px 2px; }
-.NAVIGATION input:checked + form.config { display: block; }
-.NAVIGATION .config input[type=text],
-.NAVIGATION .config button {
- font-size: .875em;
- line-height: 1.5em;
- height: 1.75em;
- padding: 0 .25em;
- vertical-align: bottom;
- border: 1px solid #000;
- background-color: #FFF;
-}
-.NAVIGATION .config input[type=text] {
- margin: .25em 0 0 .375em;
- border-right: none;
- width: 70%;
-}
-.NAVIGATION .config button {
- min-width: 2.5em;
- text-align: center;
- max-width: 30%;
-}
-.NAVIGATION .config button[value=del] {
- background: #FCC;
-}
-
-
-.trailbtn { display: none; }
-.trailbtn + .trailbox { display: none; }
-.trailbtn:checked + .trailbox { display: inline-block; }
-.trailbtn:checked + .trailbox + .trailbtn { display: block; }
-.trailbtn:checked + .trailbox + .trailbtn:before {
- display: block; content: '+';
- width: 3ex; text-align: center;
- margin-top: .25em; padding: .25em 0;
- background-color: #FFF;
- border-width: 1px; border-style: solid;
-}
-.trailbtn:checked + .trailbox + .trailbtn:checked,
-.trailbtn:checked { display: none; }
-
-#footer {
- width: 100%;
- background-color: #FFF;
- border-top: 1px solid #BBF;
- margin: 0; padding: .5em 2em;
- position: absolute; bottom: 0;
-}
+++ /dev/null
-/*
-# Copyright 2014 Paul Hänsch
-#
-# This file is part of Confetti.
-#
-# Confetti is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# Confetti is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with Confetti. If not, see <http://www.gnu.org/licenses/>.
-*/
-
-.sort {
- display: block;
- border: solid 1px;
- border-radius: 4px 4px 0 0 ;
- margin: .5em 2em .25em 2em;
- padding: .25em 2ex .25em 2ex;
- background: #EFF;
-}
-.sort .label {
- font-weight: bold;
-}
-
-.newcourse {
- display: block;
- border: solid 1px;
- border-radius: 0 0 4px 4px;
- margin: .25em 2em 1em 2em;
- padding: .25em 2ex .25em 2ex;
- background: #EFF;
-}
-
-.course {
- display: block;
- position: relative;
- border: solid 1px;
- margin: .25em 2em;
- padding: 0 20ex 0 0;
- overflow: auto;
- background: #FFF;
- min-height: 9em;
-}
-.course .control {
- display: inline-block;
- position: absolute;
- top 0; right: 0;
- text-align: right;
- background: #EEF;
- margin: 0;
- height: 100%;
- width: 20ex;
- max-width: 33%;
- padding: .25em 1ex;
-}
-.course .control .item {
- display: inline-block;
- vertical-align: top;
- width: 100%;
- color: #008;
- margin: .25em 0 0 0;
- border: 1px solid black;
- background: #FFF;
-}
-.course .control a.item {
- border-right: 3px double;
- padding: .125em 1ex;
-}
-.course .control a.item:hover{
- border-right: 1px solid;
-}
-
-.course .control .item[name=newfield] { width: 85%;}
-.course .control .item[value=addfield] { width: 15%;}
-
-.course .section {
- display: inline-block;
- vertical-align: top;
- float: left;
- width: 20ex;
- min-width: 16.5%;
- min-height: 6em;
- margin: .125em 0 .5ex 0;
- padding: 0 1ex .25em 1ex;
-}
-.course .section:nth-of-type(2n){ background: #EEE;}
-
-.course .section .item {
- display: block;
- max-width: 100%;
- word-wrap: break-word;
-}
-
-.course .section h2, .course .section h3 {
- font-size: 1em;
- display: block;
- font-weight: bold;
- margin: .25em -.5ex .25em -.5ex;
- border-style: none none solid none;
- border-width: 1px;
-}
-.course .section h2 {
- font-size: 1.2em;
- margin-top: 0;
-}
-
-.course .attendance {
- min-width: 66%;
-}
-.course .attendance a {
- display: inline-block;
- vertical-align: top;
- min-width: 13.5ex;
- width: 19%;
- color: #008;
- border-style: solid;
- border-width: 1px;
- margin: .25em .125ex .25em .125ex;
- padding: .125em 1ex .125em 1ex;
- background: #EFF;
-}
-
-.course .dtstart { width: 37ex; }
-.course .dtstart .DTSYEAR,
-.course .dtstart .DTSMONTH,
-.course .dtstart .DTS{
- font-size: .875em;
- margin: .25em .25ex 1em .25ex;
- display: inline-block;
- text-align: center;
- font-weight: bold;
-}
-
-.course .dtstart .DTSYEAR{ width: 11.5ex; }
-.course .dtstart .DTSMONTH{ width: 18.5ex; }
-.course .dtstart .DTS{ width: 5.5ex; }
-
-.course .dtstart .DTSCAL,
-.course .dtstart .DTSCALHEAD {
- display: inline-block;
- overflow: hidden;
- text-align: center;
- width: 5ex;
- margin: 0;
-}
-.course .dtstart .DTSCALHEAD {
- font-weight: bold;
- border-top: 1px solid;
- border-bottom: 1px solid;
-}
-
-.course .dtstart input[name=DTSDAY] { display: none; margin: 0; height: 0; width: 0; }
-.course .dtstart input[name=DTSDAY]:checked + label.DTSCAL { font-weight: bold; border: solid 1px; }
-
-.course .dtstart .DTSTIME{
- display: inline-block;
- width: 10ex;
- font-weight: bold;
-}
-
-.course .recur{ width: 37ex; }
-.course .section select,
-.course .section button,
-.course .section input {
- border: 1px solid;
- margin: .5em 0;
-}
-
-.course .select_attendance {
- max-height: 17em;
- overflow-y: scroll;
-}
-.course .select_attendance label{
- display: block;
- position: relative;
- margin: 0; padding: 0;
- padding-left: 3ex;
-}
-.course .select_attendance label input{
- position: absolute;
- margin: 0; padding: 0;
- margin-left: -3ex;
- top: 0;
-}
-
-.course .section input[type="text"] { width: 100%; }
-.course .section textarea.COMMENT { min-height: 6em; width: 100%; overflow: auto; }
-
+++ /dev/null
-/*
-# Copyright 2014 Paul Hänsch
-#
-# This file is part of Confetti.
-#
-# Confetti is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# Confetti is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with Confetti. If not, see <http://www.gnu.org/licenses/>.
-*/
-
-.filter {
- display: block;
- width: 128ex;
- border-width: 1px;
- border-style: solid;
- border-radius: 4px 4px 0 0;
- margin: .5em auto .25em auto;
- padding: .25em 1ex .25em 1ex;
- background: #EFF;
-}
-
-.filter > .label {
- display: block;
- font-weight: bold;
- font-size: 1.2em;
- border-style: none none solid none;
- border-width: 1px;
- margin: 0em 0ex .5em 0ex;
- background: #EEF;
-}
-
-.filter .search {
- margin: 0em 2ex .5em 2ex;
-}
-.filter .search .label {
- display: inline-block;
- width: 12ex;
- font-weight: bold;
- margin-top: .5em;
-}
-.filter .search input[type="radio"] {
- margin-top: .5em;
- margin-left: 1ex;
-}
-.filter .search button {
- margin-top: .5em;
-}
-
-.filter .search input[type="text"] {
- width: 124ex;
-}
-
-.newcard {
- display: block;
- width: 126ex;
- border-width: 1px;
- border-style: solid;
- border-radius: 0 0 4px 4px;
- margin: .25em auto 1em auto;
- padding: .25em 2ex .25em 2ex;
- background: #EFF;
-}
-
-.cardlist .card {
- display: block;
- width: 130ex;
- border-style: solid;
- border-width: 1px;
- margin: .25em auto;
- padding: 0;
- overflow: auto;
- background: #FFF;
-}
-
-.cardlist .card .section {
- display: inline-block;
- float:left;
- width: 20ex;
- margin: .125em .25ex .5ex .25ex;
- padding: 0 .2em .2em .2em;
- background: #EEE;
-}
-.cardlist .card .section a.attendance {
- display: inline-block;
- margin-right: 1ex;
- word-wrap: break-word;
-}
-
-.cardlist .card .attendance {
- width: 83.5ex;
-}
-.cardlist .card .attendance .check {
- display: inline-block;
- width: 27ex;
-}
-
-.cardlist .card .control {
- float: right;
- text-align: right;
- margin-right: 0;
- background: #EEF;
-}
-.cardlist .card .control .item {
- color: #008;
- margin-top: .2em;
- margin-right: 1ex;
-}
-.cardlist .card .control a.item {
- min-width: 10ex;
- border-style: solid double solid solid;
- border-width: 1px 3px 1px 1px;
- border-color: #000;
- padding: .1em 1ex;
- background: #FFF;
-}
-.cardlist .card .control a.item:hover{
- border-width: 1px 1px 1px 1px;
-}
-
-.cardlist .card .section .sectitle {
- display: block;
- font-weight: bold;
- margin: .2em .2em .2em .2em;
-}
-
-.cardlist .card .section .item {
- display: block;
- max-width: 20ex;
- word-wrap: break-word;
-}
-
-.cardlist .card .section textarea.NOTE {
- min-height: 6em;
-}
-.cardlist .card .section textarea.ADR {
- min-height: 4em;
-}
-
-.cardlist .card .section .PHOTO {
- width: 20ex;
- max-heigth: 30ex;
-}
-
-.cardlist .card .section .FN {
- font-weight: bold;
- font-size: 1.2em;
-}
-
+++ /dev/null
-/*
-# Copyright 2016 Paul Hänsch
-#
-# This file is part of Confetti.
-#
-# Confetti is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# Confetti is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with Confetti. If not, see <http://www.gnu.org/licenses/>.
-*/
-
-@import url("?static=cards.css");
-
-body {padding-bottom: 3em; }
-
-.trailbtn { display: none; }
-.trailbtn + .trailbox { display: none; }
-.trailbtn:checked + .trailbox { display: inline-block; }
-.trailbtn:checked + .trailbox + .trailbtn { display: block; }
-.trailbtn:checked + .trailbox + .trailbtn:before {
- display: block; content: '+';
- width: 3ex; text-align: center;
- margin-top: .25em; padding: .25em 0;
- background-color: #FFF;
- border-width: 1px; border-style: solid;
-}
-.trailbtn:checked + .trailbox + .trailbtn:checked,
-.trailbtn:checked { display: none; }
-
-.prescription {
- display: inline-block;
- width: 96%; max-width: 460px;
- color: #800;
- background-color: #DDD;
- margin: 1em -1% 0 2%; padding: 0;
- border: 1px solid #888;
- overflow: hidden;
- vertical-align: top;
-}
-form.prescription { padding-top: 1ex;}
-
-.newprescription {
- display: block;
- margin: 0 2em; padding: .5ex 2ex;
- background-color: #CFF;
- border: 1px solid #888;
- border-radius: 0 0 1ex 1ex;
-}
-
-.prescription * {
- display: inline-block;
- font-size: 1em;
- line-height: 1em;
- margin: 0; padding: 0;
-}
-.prescription label {
- padding-left: .5ex;
- font-size: .75em;
-}
-
-.prescription fieldset {
- display: inline-block;
- margin: 0; padding: 1ex;
- margin-right: -.625ex;
- border: none;
- vertical-align: top;
-}
-.prescription fieldset br { display: none;}
-
-.prescription span,
-.prescription input,
-.prescription textarea {
- height: 1.5em;
- border: 1px solid #800;
- padding: .25ex;
- background-color: #FFF;
-}
-.prescription span {
- background-color: #EEE;
- padding: .5ex .25ex;
- white-space: pre-wrap;
- font-size: .75em;
- overflow: hidden;
-}
-
-.prescription input[type=checkbox],
-.prescription input[type=radio] { display: none;}
-
-.prescription label.checkbox,
-.prescription label.radio,
-.prescription input[type=checkbox] + label,
-.prescription input[type=radio] + label { padding-left: 1.25em; font-size: 1em;}
-
-.prescription label.checkbox:before,
-.prescription label.radio:before,
-.prescription input[type=checkbox] + label:before,
-.prescription input[type=radio] + label:before {
- display: inline-block;
- position: absolute;
- margin-left: -1.25em;
- width: .75em; height: .75em;
- background-color: #FFF;
- border: 1px solid #800;
- content: ' ';
-}
-.prescription label.radio:before,
-.prescription input[type=radio] + label:before { border-radius: .5em;}
-.prescription label.checkbox.checked:before,
-.prescription label.radio.checked:before,
-.prescription input[type=checkbox]:checked + label:before,
-.prescription input[type=radio]:checked + label:before { content: "\2713";}
-
-.prescription a.button,
-.prescription input[type=submit],
-.prescription button {
- height: 1.5em;
- color: #FFF;
- background-color: #800;
- text-align: center;
- text-decoration: none;
- margin: 0; padding: .125em 0 0 0;
- border: none;
-}
-.prescription a.button {padding: .5ex;}
-
-/* ======== Specific ========== */
-
-.prescription label.presctype,
-.prescription input[name=presctype] + label {
- font-size: medium;
- width: 22%;
- margin: 0; margin-right: -.5ex;
- vertical-align: top;
- padding: .25em .5ex .25em 3ex;
- height: 2.5em;
- border-top: 1px solid #DDD;
-}
-.prescription label.presctype {
- text-align: right;
- font-weight: bold;
- font-size: .875em;
- padding-right: 1ex;
- padding-left: 0;
-}
-
-input[name=presctype][value\$=private]:checked ~ fieldset,
- input[name=presctype][value\$=private] + label,
- .prescription.private { background-color: #CFC;}
-input[name=presctype][value\$=selfpaid]:checked ~ fieldset,
- input[name=presctype][value\$=selfpaid] + label,
- .prescription.selfpaid { background-color: #FFC;}
-input[name=presctype][value=doctor_compulsory]:checked ~ fieldset,
- input[name=presctype][value=doctor_compulsory] + label,
- .prescription.doctor.compulsory { background-color: #CFF;}
-input[name=presctype][value=dentist_compulsory]:checked ~ fieldset,
- input[name=presctype][value=dentist_compulsory] + label,
- .prescription.dentist.compulsory { background-color: #FCC;}
-input[name=presctype][value^=altpractition]:checked ~ fieldset,
- input[name=presctype][value^=altpractition] + label,
- .prescription.altpractition { background-color: #FCF;}
-
-.prescription .baseinfo { width: 60%;}
-
- .baseinfo label[for=insurance],
- .baseinfo #insurance { width: 100%; }
-
- .baseinfo label[for=name],
- .baseinfo #name { width: 65%; margin-right: -.875ex;}
- .baseinfo #name { height: 4em; }
-
- .baseinfo label[for=bday],
- .baseinfo #bday { width: 35%;}
- .baseinfo #bday { height: 4em; text-align: center; vertical-align: top;}
-
- .baseinfo label[for=date],
- .baseinfo #date { width: 34%; margin-left: 65%;}
- .baseinfo #date { text-align: right;}
-
-.prescription .misc { width: 40%; }
-
- .misc h1 {
- font-size: 1.25em;
- font-weight: bold;
- width: 100%;
- }
- .misc label[for=addcontrib],
- .misc label[for=contribconfirm] {width: 100%;}
- .misc #addcontrib,
- .misc #contribconfirm {width: 100%; text-align: right;}
-
-.prescription label[for=prescreviewed] {
- margin-left: 1ex;
- font-weight: bold;
- text-decoration: underline;
- background-color: #FCC;
-}
-.prescription label[for=prescreviewed].checked,
-.prescription :checked + label[for=prescreviewed] {
- font-weight: normal;
- text-decoration: none;
- background-color: transparent;
-}
-
-.prescription .catalogue { width: 100%; }
-
- .catalogue h2:nth-of-type(1) {
- font-weight: bold;
- width: 100%;
- margin-bottom: .25em;
- }
- .catalogue label {
- display: inline-block;
- width: 33%;
- margin-right: -.625ex;
- margin-top: .25em;
- vertical-align: top;
- }
- .catalogue label[for=presccontinual] { margin-right: 33%;}
-
-.prescription .description { width: 100%; position: relative;}
- .description * { margin-right: -.625ex; vertical-align: top; }
- .description label {vertical-align: bottom;}
-
- .description label[for=quantity] { width: 20%;}
- .description label[for=remidy] { width: 60%; }
- .description label[for=quantity_weekly] { width: 20%;}
- .description #quantity,
- .description .quantity { width: 20%; height: 3em; text-align: center;}
- .description #remidy,
- .description .remidy { width: 60%; height: 3em;}
- .description #quantity_weekly,
- .description .quantity_weekly { width: 20%; height: 3em; text-align: center;}
-
- .description .indicator_codes {display: inline-block; width: 20%; padding: 0; padding-top: 1.5ex;}
- .description label[for=indicator],
- .description label[for=icd10] { display: block; width: 100%;}
- .description #icd10,
- .description #indicator {width: 100%; text-align: right;}
-
- .description .indicator_reading { display: inline-block; width: 78%; padding: 0; padding-top: 1.5ex; margin-left: 2%;}
- .description label[for=indicator_reading],
- .description #indicator_reading { width: 100%; display: block;}
- .description #indicator_reading { height: 4em;}
-
-.prescription .therapy_dates span { min-width: 8em; margin: 0 .5ex;}
-
-.prescription .issuer { display: inline-block; width: 50%; padding: 0; padding-top: 0; margin-left: 50%;}
-.prescription .issuer label:first-of-type {
- display: block;
- position: relative;
- width: 50%; left: -50%; top: 2.25em;
- font-size: 1em;
- text-align: right;
- padding-right: 1ex;
- }
-.prescription .issuer input[type=radio] + label:before { content: none; }
-.prescription .issuer input[type=radio] { display: none; }
-.prescription .issuer input[type=radio] + label {
- display: inline-block;
- width: 50%;
- padding: .25ex 0; margin: 0;
- text-align: center;
- border: 1px solid black;
-}
-.prescription .issuer input[type=radio]:checked + label {
- font-weight: bold;
- background-color: #FFF;
- border-width: 1px;
- border-bottom: 1px solid #FFF;
-}
-.prescription .issuer input[type=radio] + label + input + label + select,
-.prescription .issuer input[type=radio] + label + select + input { display: none; }
-.prescription .issuer input[type=radio]:checked + label + input + label + select,
-.prescription .issuer input[type=radio]:checked + label + select + input {
- display: block; width: 100%;
- border: 1px solid black;
- background-color: #FFF;
- border-width: 0 1px 1px 1px;
- padding: .25ex .5ex;
- margin-top: -1px;
-}
-.prescription .issuer input[type=radio]:checked + label + input + label + select option { display: block;}
-.prescription span#issuer { width: 100%; height: 3em; padding: 1ex 2ex;}
-
-.prescription .controls { width: 100%; }
- .controls a.button,
- .controls button[value=save],
- .controls button[value=cancel],
- .controls button[value=delete] { width: 25%;}
-
+++ /dev/null
-.trailbtn:checked + fieldset.trailbox { display: block;}
-
-.trailbtn:checked + .trailbox + .trailbtn {
- display: block;
- height: 2.25em; padding: 0 3ex;
- font-size: 1em; font-weight: normal;
- color: #000; background-color: #FDD;
- border: 1px solid #000;
- border-radius: 4px;
-}
-.trailbtn:checked + .trailbox + .trailbtn[type=submit]:before {content: none;}
-
-
-* { box-sizing: border-box; }
-body {
- overflow: scroll;
- position: relative;
- width: 100%;
- margin: 0; padding: 0;
- padding-top: 2em;
-}
-
-form > button[type=submit] {
- position: fixed; display: block;
- top: 0; right: 2.5em;
- height: 2.25em; padding: 0 3ex;
- font-size: 1em; font-weight: bold;
- color: #000; background-color: #FDD;
- border-width: 1px; border-color: #000;
- border-style: none solid solid solid;
- border-radius: 0 0 4px 4px;
- z-index: 3;
-}
-form > button[type=submit]:hover {
- background-color: #FEE;
-}
-
-input.tab { display: none; }
-input.tab + label.tab { display: block; }
-input.tab + label.tab::before { content: '\25b8 \00a0'; float: left;}
-input.tab:checked + label.tab::before { content: '\25be \00a0'; }
-input.tab + label.tab + div.tab { display: none; }
-input.tab:checked + label.tab + div.tab { display: block; }
-
-input.color { display: none }
-input.color + label{
- display: inline-block;
- width: 1em; height: 1em;
- border: 1px solid black;
-}
-input.color:checked + label{ border-width: 3px;}
-input.color[value="#000"] + label,
-input.color[value="#888"] + label { background-color: #888;}
-input.color[value="#00A"] + label { background-color: #00F;}
-input.color[value="#0A0"] + label { background-color: #0F0;}
-input.color[value="#0AA"] + label { background-color: #0FF;}
-input.color[value="#A00"] + label { background-color: #F00;}
-input.color[value="#A0A"] + label { background-color: #F0F;}
-input.color[value="#AA0"] + label { background-color: #FF0;}
-input.color[value="#FFF"] + label { background-color: #FFF;}
-
-h1, label.tab, div.tab, fieldset.tab,
-div.patient, div.prescription {
- display: block;
- width: 96%;
- margin: 0 2%;
-}
-
-div.prescription span {
- display: inline-block;
- width: 50%;
- margin-right: -.75ex;
- vertical-align: top;
-}
-div.prescription span label { font-weight: bold; margin-right: 1ex;}
-div.prescription span.prescno,
-div.prescription span.catalogue {
- width: 33%;
- font-weight: bold;
- margin-bottom: .5em;
- padding: .5ex 1ex;
-}
-
-div.prescription ul {margin-top: 0;}
-
-div.prescription label.checkbox,
-div.prescription label.radio {
- display: block;
- padding-left: 1.25em;
- font-size: 1em;
- margin: .5em 0;
-}
-
-div.prescription label.checkbox:before,
-div.prescription label.radio:before {
- display: inline-block;
- color: #000;
- background-color: #FFF;
- height: 1.375em; width: 1.125em;
- padding: .125em 0 0 .375em;
- margin: 0 .5em .25em -1.25em;
- border: 1px solid #000;
- vertical-align: middle;
- content: ' ';
-}
-div.prescription label.radio:before { border-radius: .5em;}
-div.prescription label.checkbox.checked:before,
-div.prescription label.radio.checked:before { content: "\2713";}
-
-div.prescription label[for=prescreviewed] {
- margin-left: 1ex;
- font-weight: bold;
- text-decoration: underline;
- background-color: #FCC;
-}
-div.prescription label[for=prescreviewed].checked {
- font-weight: normal;
- text-decoration: none;
- background-color: transparent;
-}
-
-div.prescription label.tab {width: 96%; border: none; border-bottom: 1px dotted;}
-div.prescription div.tab { width: 96%; background-color: #DDD;}
-
-input.stickynote { display:none; }
-input.stickynote + .stickynote {
- position: fixed;
- background-color: #FF8;
- top: 4em; bottom: 4em;
- left: -4.5em; width: 5em;
- padding: 1ex;
- max-height: 90%;
- z-index: 2;
-}
-input.stickynote + .stickynote:nth-of-type(2n) {
- background-color: #8FF;
- top: 8em;
-}
-
-input.stickynote + .stickynote > * { display: none; }
-input.stickynote + .stickynote > label {
- position: absolute;
- top: 0; bottom: 0; right: .5ex;
- display: block;
- text-align: right;
- font-weight: bold;
-}
-input.stickynote + .stickynote:hover {
- left: -1ex;
-}
-input.stickynote:checked + .stickynote {
- width: auto; left: 1em; right: 1em;
-}
-input.stickynote:checked + .stickynote > * { display: block; }
-input.stickynote:checked + .stickynote > textarea {
- display: block;
- position: absolute;
- left; 0; right: 0; bottom: 0; top: 0;
- width: 100%; height: 100%;
- background-color: #FF8;
- padding: 2em 1em;
-}
-input.stickynote:checked + .stickynote > button[type="submit"] {
- display: block;
- position: absolute;
- right: .5ex; bottom: .5ex;
- z-index: 2;
-}
-input.stickynote:checked + .stickynote > label {
- display: block;
- position: static;
- font-size: 0;
-}
-input.stickynote:checked + .stickynote > label:before {
- position: absolute;
- font-size: initial;
- content: "x";
- top: .5ex; right: .5ex;
- padding: .125ex .75ex;
- background-color: #000;
- color: #FFF;
- border-radius: 1ex;
- z-index: 2;
-}
-
-fieldset.penwidth,
-fieldset.color {
- position: absolute;
- right: 0; width: 2em;
- margin: .5em .5em .125em 2%;
- border: none;
- padding: 0;
-}
-fieldset.penwidth { bottom: 19em; }
-fieldset.penwidth > input {display: none;}
-fieldset.penwidth > input + label { display: none;}
-fieldset.penwidth > input:checked + label {
- display: block;
- width: 2em; height: 2em;
- background-color: #000;
- border: 1em solid #FFF;
- border-radius: 1em;
-}
-fieldset.penwidth > input[value="4"] + label { border-width: .75em; }
-fieldset.penwidth > input[value="12"] + label { border-width: .5em; }
-fieldset.penwidth > input[value="36"] + label { border-width: .25em; }
-
-fieldset.color { bottom: 0; }
-fieldset.color > input.color + label {
- width: 2em; height: 2em;
-}
-.dotmark {
- max-width: 90%;
- margin: .5em 1em .125em 2%; padding: 0;
- text-align: left;
- border: 1px solid black;
-}
-.dotmark.ov {
- position: absolute;
- left: 0; bottom: .25em;
- z-index: 1;
-}
-
-@media(min-width: 800px){
- h1, label.tab, div.tab, fieldset.tab,
- div.patient, div.prescription {
- width: 38%;
- margin-right: 0;
- }
- input.stickynote:checked + .stickynote { right: 50%; }
- fieldset.penwidth,
- fieldset.color { position: fixed; }
- .dotmark {
- position: fixed;
- max-width: 52%;
- max-height: 98%;
- right: 2em; bottom: .25em;
- }
- .dotmark.ov {
- position: fixed;
- right: 2em; left: auto;
- }
-}
-
-h1 {display: none;}
-
-div.patient, div.prescription { margin-top: 1em; }
-div.prescription {
- border: 1px solid black;
- background-color: #EEE;
- padding: .125em 1.25ex .5em 1.25ex;
-}
-div > h2 { margin: 0; border-bottom: 1px solid black; }
-div:nth-child(n+2) > a:first-of-type {
- display: block;
- margin: .125em 0 .5em 0;
- text-decoration: none;
-}
-
-#report fieldset.tab,
-#report label.tab {
- font-size: 1.25em;
- font-weight: bold;
- padding: .125em 1ex .25em 1ex;
- color: #FFF;
- background-color: #333;
- margin-top: .125em;
- text-align: right;
- border: none;
-}
-#report label.heading {
- background-color: #FFF;
- margin-top: 1em;
- border: 2px solid black;
- border-bottom-width: 1px;
- color: black;
-}
-#report label.heading > span {
- text-decoration: underline;
-}
-
-#report fieldset.tab > *,
-#report label > input,
-#report label > span {
- display: inline-block;
- text-align: right;
-}
-#report .tab > .no {
- width: 10%; float: left;
- border: solid 1px #FFF;
- background-color: #555;
- border-radius: 2ex;
- padding: 0;
- text-align: center;
-}
-#report label.heading > span.no {
- background-color: inherit;
- border: none;
-}
-#report .tab > .date { width: 30%; }
-#report .tab > .therapist { width: 30%; }
-#report .tab > .signature { width: 20%; }
-#report label.tab > .signature { font-size: .75em; }
-
-#report .signature > input[type=checkbox] {
- display: inline;
- font-weight: bold;
- font-size: 1.25em;
-}
-#report .signature > input[type=checkbox]:before {
- display: block; width: 1.25em;
- margin: -.125em 0 0 -.5ex;
- background-color: #FFF;
- text-align: center;
- content: "\00a0 \00a0 \00a0";
-}
-#report .signature > input[type=checkbox]:checked::before {
- content: "\2713";
-}
-
-#report input.tab + label.tab > input.date,
-#report input.tab + label.tab > input.therapist {
- display: none;
-}
-#report input.tab:checked + label.tab > input.date,
-#report input.tab:checked + label.tab > input.therapist {
- display: inline;
-}
-#report input.tab:checked + label.tab > span.date,
-#report input.tab:checked + label.tab > span.therapist {
- display: none;
-}
-
-#report div.tab {
- border: 2px solid #333;
- border-top-width: 1px;
- margin-top: -1px;
- padding: .25em .5ex 1em .5ex;
-}
-#report div.tab > fieldset.note {
- border: none;
- margin: 0; padding: 0;
-}
-#report div.tab > fieldset.note > textarea {
- display: block;
- width: 93%; height: 8em;
- margin: -8em 0 .5em 2em;
- font: normal 1em sans-serif;
-}
-div.tab > fieldset.note > input.color + label { margin: 0; display: block; }
-div.tab > fieldset.note > input.color[value="#888"]:checked ~ textarea { background-color: #AAA; }
-div.tab > fieldset.note > input.color[value="#00A"]:checked ~ textarea { background-color: #88F; }
-div.tab > fieldset.note > input.color[value="#0A0"]:checked ~ textarea { background-color: #8F8; }
-div.tab > fieldset.note > input.color[value="#0AA"]:checked ~ textarea { background-color: #8FF; }
-div.tab > fieldset.note > input.color[value="#A00"]:checked ~ textarea { background-color: #F88; }
-div.tab > fieldset.note > input.color[value="#A0A"]:checked ~ textarea { background-color: #F8F; }
-div.tab > fieldset.note > input.color[value="#AA0"]:checked ~ textarea { background-color: #FF8; }
-div.tab > fieldset.note > input.color[value="#FFF"]:checked ~ textarea { background-color: #FFF; }
-
-div.tab > button.delete {float: right; display: inline-block; margin-top: -1em; display: none;}
+++ /dev/null
-// Copyright 2016 Paul Hänsch
-//
-// This file is part of Confetti.
-//
-// Confetti is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Affero General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// Confetti is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Affero General Public License for more details.
-//
-// You should have received a copy of the GNU Affero General Public License
-// along with Confetti. If not, see <http://www.gnu.org/licenses/>.
-
-body = document.body
-dbg = document.getElementById("jsdebug")
-canvas = document.getElementById("canvas")
-data=document.getElementById("image_serialize")
-
-image = canvas.getContext("2d")
-mouse = 0
-image_serialize=""
-
-// start and current coordinates of a draw
-// serves for tracking, whether path ends close to its beginning
-stx=0, sty=0
-cux=0, cuy=0
-
-function setstroke(w) {
- image.lineWidth = w
- data.value += " stroke-width " + image.lineWidth
-}
-function setcol(c) {
- this.c = c
- image.strokeStyle = c
- image.fillStyle = c
- data.value += " stroke " + c + "F"
-}
-
-function relX(x){
- if ( body.clientWidth >= 800 ){
- return Math.floor(cscaleW * (x - canvas.offsetLeft))
- } else {
- return Math.floor(cscaleW * (x - canvas.offsetLeft + window.pageXOffset))
- }
-}
-function relY(y){
- if ( body.clientWidth >= 800 ){
- return Math.floor(cscaleH * (y - canvas.offsetTop))
- } else {
- return Math.floor(cscaleH * (y - canvas.offsetTop + window.pageYOffset))
- }
-}
-
-function draw(x, y) {
- if ( mouse == 1){
- cux=relX(x), cuy=relY(y)
-
- image.lineTo( cux, cuy )
- image.stroke()
-
- image_serialize += " " + cux + "," + cuy
- }
-}
-
-function drawstart(x, y) {
- mouse = 1
-
- cscaleW = canvas.width / canvas.clientWidth
- cscaleH = canvas.height / canvas.clientHeight
-
- stx=relX(x), sty=relY(y)
-
- setstroke(document.querySelector('input[name="penwidth"]:checked').value);
- setcol(document.querySelector('input[name="color"]:checked').value);
-
- image.beginPath()
- draw(x, y) // why must this not use relative Coords ???
-
- image_serialize = " polyline"
-}
-
-function drawstop() {
-
- // if path ends close to beginning ( < 50 px); then close path and fill
- if ( false && mouse == 1 && Math.sqrt( Math.pow(stx - cux, 2) + Math.pow(sty - cuy, 2)) <= 50 && c !== "#FFF" ){
- image.lineTo( stx, sty )
- image.stroke()
-
- image.globalAlpha = .5
- image.fill()
- image.globalAlpha = 1
-
- image_serialize += " " + stx + "," + sty
- data.value += " fill " + c + "8" + image_serialize
- } else if (mouse == 1) {
- data.value += " fill #0000 " + image_serialize
- }
- dbg.innerHTML = " stx: " + stx + " cux: " + cux + " sty: " + sty + " cuy: " + cuy
-
- image.closePath()
- image_serialize = ""
- mouse = 0
-}
-
-window.addEventListener( 'mouseup', function() { drawstop() } )
-canvas.addEventListener( 'mousedown', function(e) { drawstart(e.clientX, e.clientY) } )
-canvas.addEventListener( 'mousemove', function(e) { draw(e.clientX, e.clientY) } )
-
-window.addEventListener( 'touchend', function() { drawstop() } )
-canvas.addEventListener( 'touchstart', function(e) { drawstart(e.touches[0].clientX, e.touches[0].clientY) } )
-canvas.addEventListener( 'touchmove', function(e) { e.preventDefault(); draw(e.touches[0].clientX, e.touches[0].clientY) } )
--- /dev/null
+* {
+ position: relative;
+ box-sizing: border-box;
+ max-width: 100%;
+ font-family: sans-serif;
+ font-weight: normal;
+ font-size: initial;
+ font-style: normal;
+ text-decoration: none;
+ line-height: 1.5em;
+ color: inherit; background: transparent;
+ padding: 0; margin: 0;
+ border: none;
+}
+
+body {
+ color: #000; background-color: #FFF;
+}
+
+/* ======= GENERIC HTML STYLES =======*/
+
+p { margin-bottom: 1em; }
+
+a {
+ text-decoration: underline;
+ font-style: italic;
+ color: #068;
+}
+
+i, em { font-style: italic; }
+b, strong { font-weight: bolder; }
+ul, ol { margin-left: 1.125em; }
+table th { font-weight: bold; }
+
+h1, h2, h3 {
+ font-weight: bold;
+ margin-top: 1em;
+ margin-bottom: .5em;
+}
+
+h1:first-child, h2:first-child, h3:first-child,
+p + h1, p + h2, p + h3 {
+ margin-top: 0;
+}
+
+h4, h5, h6, form legend {
+ font-weight: bolder;
+ margin-bottom: .25em;
+}
+
+h1 { font-size: 1.5em; }
+h2 { font-size: 1.125em; }
+
+select, input, button, textarea,
+.control a {
+ display: inline-block;
+ background-color: #FFF;
+ border: 1pt solid;
+ padding: .25em .75em;
+ vertical-align: text-bottom;
+}
+.control a {
+ color: inherit;
+ font-style: normal;
+ text-decoration: none;
+}
+
+select { padding: .375em 0; }
+
+input[type=radio], input[type=checkbox] {
+ vertical-align: baseline;
+}
+input[type=number] { text-align: right; padding-right: 0; }
+
+button, input[type=button],
+.control a {
+ box-shadow: .125em .125em .25em;
+ cursor: pointer;
+}
+input[type=radio], input[type=checkbox],
+input[type=radio] + label, input[type=checkbox] + label {
+ cursor: pointer;
+}
+
+label { margin-right: .75em; }
+input + label {
+ margin-left: .375em;
+}
+
+/* ====== COMMON ELEMENTS ======*/
+
+body > ul.menu {
+ display: block;
+ height: 1.75em;
+ margin: 0; -padding: 0 .5em;
+ list-style: none;
+ color: #CCC;
+ background-color: #444;
+ box-shadow: inset 0 -.25em .25em #000;
+ overflow: hidden;
+ z-index: 3;
+}
+
+body > .menu li {
+ display: inline-block;
+}
+body > .menu a {
+ color: inherit;
+ padding: .5em 3em;
+ box-shadow: inset 0 0 .5em #000;
+}
+body.cards > .menu a[href="/cards/"],
+body.courses > .menu a[href="/courses/"] {
+ color: #000;
+ background-color: #FFF;
+ box-shadow: none;
+}
+
+/* =========== FILTER AND SEARCH Headers ========= */
+
+form.categories,
+form.search, form.sort, form.filter, form.newcard, form.newcourses {
+ margin-top: 1em; padding: 0 1em;
+ z-index: 1;
+}
+form.filter > h1 { display: none; }
+
+form.filter fieldset { margin-top: .5em; }
+form.filter fieldset.item + fieldset.item legend { display: none; }
+form.filter fieldset.item input[type=radio] { display: none; }
+form.filter fieldset.item input[type=radio] + label {
+ display: table-cell;
+ padding: .5em 1em;
+ background-color: #EEE;
+ border-style: solid;
+ border-width: .5pt .25pt 0 .25pt;
+ white-space: normal;
+}
+form.filter fieldset.item input[type=radio] + label:first-of-type {
+ border-left: 1pt solid;
+}
+form.filter fieldset.item input[type=radio]:checked + label {
+ position: relative;
+ background-color: #FFF;
+ box-shadow: .125em -.125em .125em #888;
+ z-index: 1;
+}
+form.filter fieldset.item input[type=text],
+form.filter fieldset.item fieldset.courses,
+form.filter fieldset.item fieldset.categories {
+ position: relative;
+ display: block;
+ width: 100%;
+ margin-top: -1pt;
+ padding: .25em .75em;
+ border: 1pt solid;
+ box-shadow: .125em .125em .25em #888;
+}
+form.filter fieldset.item fieldset.courses,
+form.filter fieldset.item fieldset.categories { display: none; }
+form.filter fieldset.item input[value=course]:checked ~ input[type=text],
+form.filter fieldset.item input[value=CATEGORIES]:checked ~ input[type=text] { display: none; }
+form.filter fieldset.item input[value=course]:checked ~ fieldset.courses,
+form.filter fieldset.item input[value=CATEGORIES]:checked ~ fieldset.categories { display: block; }
+
+form.filter fieldset.order legend {
+ float: left; margin-right: 1em;
+}
+
+form.filter fieldset label,
+form.filter fieldset a { white-space: pre;}
+form.filter button[type=submit] {
+ margin-top: .5em; margin-bottom: .5em;
+}
+
+form.filter button[value=export_csv] { margin-left: 1em; }
+
+body.courses form .order { display: inline-block; margin-right: 2em;}
+
+body.cards form.newcard { display: flex; }
+body.cards form.newcard input[name=seed] { flex: 1; }
+
+
+/* ============ LIST ITEMS, Generic ============= */
+
+body > form,
+div.card,
+div.course {
+ position: relative;
+ width: 98%; width: calc(100% - 2em);
+ margin-left: auto; margin-right: auto;
+ margin-bottom: 1em;
+ box-shadow: .125em .125em .25em;
+ z-index: 1;
+}
+
+/* HACK: put anchor point 10em above card and highlight target element */
+ div:target { box-shadow: none; z-index: 0; }
+ div:target:before {
+ content: '';
+ display: block;
+ margin-top: -10em;
+ height: 10em;
+ visibility: hidden;
+ }
+ div:target:after {
+ content: '';
+ display: block;
+ position: absolute;
+ left: 0; right: 0;
+ top: 10em; bottom: 0;
+ box-shadow: .125em .125em .25em;
+ animation: highlight 4s;
+ z-index: -1;
+ }
+ @keyframes highlight { from { background-color: #FF0; } to { background-color: transparent; } }
+/**/
+
+div .section, form .section {
+ display: block;
+ vertical-align: top;
+ padding: 0 1em;
+ overflow: hidden;
+ word-break: break-word;
+}
+
+div .section :last-child, form .section :last-child {
+ margin-bottom: 1em;
+}
+
+div .section h2, form .section h2,
+div .section h3, form .section h3 {
+ border-bottom: 1pt solid #EEE;
+}
+div .control, form .control {
+ background-color: #EEE;
+ padding: .25em;
+ text-align: right;
+}
+
+@media(min-width: 60em) {
+ div .section, form .section {
+ display: table-cell;
+ width: calc(100% / 10);
+ }
+ div .section :last-child { margin-bottom: 0; }
+ div .control, form .control {
+ background-color: transparent;
+ }
+ div .section:nth-of-type(2n) {
+ background-color: #EEE;
+ }
+}
+@media(min-width: 80em) {
+ div .control, form .control {
+ display: table-cell;
+ width: calc(100% / 10);
+ }
+ div .control .item, form .control .item {
+ display: block;
+ margin-bottom: .25em;
+ }
+}
+
+div .section .item, form .section .item,
+form .section.attendance > label {
+ display: block;
+ width: 100%;
+}
+
+div .section .item.NOTE {
+ white-space: pre-wrap;
+}
+
+form .section .item {
+ margin-bottom: .25em;
+}
+
+form .section button[value^=addfield] {
+ font-size: .75em;
+ margin-top: .5em; padding: 0 .375em;
+}
+
+/* HACK: "responsive" Delete Button above each field */
+ form input.delete { display: none; }
+ form input.delete + label {
+ float: right;
+ font-size: .75em;
+ line-height: 1;
+ max-width: 1.75em; height: 1.125em; overflow: hidden;
+ color: #FBB; background-color: #444;
+ margin: 0; padding: .125em .5em 0 .5em;
+ border-radius: 4pt 4pt 0 0;
+ transition: max-width .3s;
+ }
+ form input.delete + label:before { content: '\274c '; margin-right: .5em; }
+ form input.delete + label:hover { max-width: 10em; }
+ -form input.delete + label:hover:before { content: ''; }
+ -form input.delete + label:hover:after { content: ' \274c'; }
+ form input.delete:checked + label,
+ form input.delete:checked + label + *,
+ form input.delete:checked + label + .teltype + .TEL {
+ display: none;
+ }
+/**/
+
+
+/* ====== right hand Control Buttons on list items ====== */
+
+form .control {
+ position: relative;
+ padding-left: 11em;
+ padding-top: 1.5em;
+}
+form .control .item {
+ display: inline-block;
+ margin-bottom: .25em;
+ vertical-align: text-bottom;
+}
+
+/* Combined Select/Submit Box */
+ form .control .item.newfield { box-shadow: .125em .125em .25em; }
+ form .control .item.newfield select { margin-right: -1pt; }
+ form .control .item.newfield button { box-shadow: none; }
+/**/
+
+/* HACK: Delete Checkbox before delete Button */
+ form .control .item.delete {
+ position: absolute;
+ bottom: .375em; left: .25em; width: auto;
+ padding-bottom: calc(2.25em + 2pt);
+ }
+
+ form .control .item.delete input + label + button {
+ display: none;
+ position: absolute;
+ bottom: 0; width: 100%;
+ color: #800;
+ background-color: #FEE;
+ z-index: 1;
+ }
+ form .control .item.delete:after {
+ content: attr(label);
+ display: block;
+ position: absolute;
+ bottom: 0; width: 100%;
+ text-align: center;
+ color: #BAA;
+ padding: .25em 0;
+ border: 1pt solid;
+ box-shadow: .125em .125em .25em;
+ }
+ form .control .item.delete input:checked + label + button { display: block; }
+/**/
+
+@media(min-width: 80em) {
+ form .control { padding: .25em; min-height: 16em; }
+ form .control .item { width: 100%; }
+ form .control .item.newfield select { width: calc(100% - 2.5em); }
+ form .control .item.delete { bottom: .125em; right: .25em; }
+}
+
+/* ======= LIST ITEMS, Courses ======= */
+
+form.course .dtstart input[name=DTS_YEAR],
+form.course .dtstart select[name=DTS_MONTH] { width: calc(50% - 1.25em); }
+form.course .dtstart input[name=DTS_YEAR] { margin-right: -.375em; }
+form.course .dtstart table { width: 100%; margin: 1em 0; }
+form.course .dtstart table td { text-align: right; -border: .5pt solid; }
+form.course .dtstart table input[type=radio] { display: none; }
+form.course .dtstart table input[type=radio] + label {
+ display: block;
+ width: 100%;
+ margin: 0; padding: 0 3pt;
+}
+form.course .dtstart table input[type=radio]:checked + label {
+ font-weight: bold;
+ padding: 0 2pt;
+ box-shadow: .125em .125em .25em;
+}
+
+form.course .dtstart label.DTSTIME {
+ display: inline-block;
+ font-weight: bold;
+ margin: 0;
+ width: calc(100% - 7.875em);
+}
+form.course .dtstart input[name=DTS_HOUR],
+form.course .dtstart input[name=DTS_MINUTE] {
+ vertical-align: baseline;
+ width: 3.5em;
+ margin-bottom: 0;
+}
+
+form.course .recur .item { white-space: nowrap; }
+form.course .recur .item > * { margin-bottom: 0; vertical-align: baseline; }
+form.course .recur input[name=RRULE_INTERVAL],
+form.course .recur input[name=RRULE_COUNT],
+form.course .recur input[name=RRULE_UMONTH],
+form.course .recur input[name=RRULE_UDAY] { width: 3.5em; }
+form.course .recur input[name=RRULE_UYEAR] { width: 4.5em; }
+form.course .recur input[name=RRULE_UYEAR],
+form.course .recur input[name=RRULE_UMONTH],
+form.course .recur input[name=RRULE_UDAY] {
+ margin-right: -.375em;
+}
+
+form.course .attendance div.attendance {
+ max-height: 16em;
+ overflow-y: scroll;
+}
+form.course .attendance label {
+ display: inline-block;
+ max-width: calc(100% - 2em);
+ vertical-align: top;
+ margin-bottom: 0;
+}
+form.course .attendance input { margin-top: .375em; }
+
+/* ======== Categories Page ======== */
+
+body.categories form ul { list-style: none; margin: 0; }
+
+form.categories li {
+ display: inline-block;
+ background-color: #EEE;
+ margin-right: .5em; margin-bottom: .5em;
+ padding-left: .5em;
+ box-shadow: .125em .125em .25em;
+}
+form.categories li button[name=remove] {
+ font-size: .75em;
+ width: 2.5em;
+ background-color: #FBB;
+ overflow: hidden;
+ white-space: pre;
+}
+form.categories li button[name=remove]:before {
+ content: '\274C ';
+ margin-right: 3em;
+}
+
+form.categories li:last-child { padding-left: 0 }
+
+body.categories form.namelist ul.namelist > li:nth-of-type(2n + 1) { background-color: #EEE; }
+body.categories form.namelist ul.namelist > li h2,
+body.categories form.namelist ul.namelist > li ul {
+ display: inline-block;
+}
+body.categories form.namelist ul.namelist > li h2 {
+ width: 20%;
+ min-width: 10em;
+}
+body.categories form.namelist ul.namelist > li ul li {
+ display: inline-block;
+}
+++ /dev/null
-# Copyright 2014 - 2017 Paul Hänsch
-#
-# This file is part of Confetti.
-#
-# Confetti is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# Confetti is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with Confetti. If not, see <http://www.gnu.org/licenses/>.
-
-setchecked() {
- printf 'checked="checked"'
-}
-check_type(){
- [ "${_GET[filter]%%:*}" = "$1" ] && setchecked
-}
-check_order(){
- [ "${_GET[order]}" = "$1" ] && setchecked
-}
-
-filter_item() {
-cat <<EOF
- <fieldset class="item">
- <legend>$(l10n filter_item):</legend>
- <input type="hidden" name="filter${n}" value="^"/>
-
- <input id="any$n" type="radio" name="filter$((n + 1))" value="any:" $([ "$1" = any ] && setchecked)>
- <label for="any$n" >$(l10n filter_all)</label>
-
- <input id="name$n" type="radio" name="filter$((n + 1))" value="name:" $([ "$1" = name ] && setchecked)>
- <label for="name$n">$(l10n filter_name)</label>
-
- <!--
- <input id="adr$n" type="radio" name="filter$((n + 1))" value="ADR:" $([ "$1" = ADR ] && setchecked)>
- <label for="adr$n">$(l10n ADR)</label>
- -->
-
- <input id="street$n" type="radio" name="filter$((n + 1))" value="street:" $([ "$1" = street ] && setchecked)>
- <label for="street$n">$(l10n filter_street)</label>
-
- <input id="zip$n" type="radio" name="filter$((n + 1))" value="zip:" $([ "$1" = zip ] && setchecked)>
- <label for="zip$n">$(l10n filter_zip)</label>
-
- <input id="telephone$n" type="radio" name="filter$((n + 1))" value="TEL:" $([ "$1" = TEL ] && setchecked)>
- <label for="telephone$n">$(l10n filter_phone)</label>
-
- <input id="birth$n" type="radio" name="filter$((n + 1))" value="BDAY:" $([ "$1" = BDAY ] && setchecked)>
- <label for="birth$n">$(l10n filter_birthyear)</label>
-
- <input id="cat$n" type="radio" name="filter$((n + 1))" value="CATEGORIES:" $([ "$1" = CATEGORIES ] && setchecked)>
- <label for="cat$n">$(l10n CATEGORIES)</label>
- <input type="text" name="filter$((n + 2))" value="$([ "$1" = CATEGORIES ] || attribsafe "$2")" placeholder="$(l10n filter_placeholder)"/>
- <fieldset class="categories">
- $(m=3
- list_categories \
- | while read cat; do
- printf '<label><input type="checkbox" name="filter%i" value="|%s" %s/>%s</label>' \
- "$((n + m))" "$(attribsafe "$cat")" "$(printf %s "$cat" |grep -qEx "$2" && setchecked)" "$(htmlsafe "$cat")"
- m=$((m + 1))
- done
- )
- <a href="?p=categories">$(l10n edit_categories)</a>
- </fieldset>
-
- <!--
- ${profile_circus:+
- <input id="course" type="radio" name="filtertype" value="course" $(check_type course)>
- <label for="course">$(l10n filter_course)</label>
- }
- -->
- </fieldset>
-EOF
-}
-
-cat <<EOF
-<form class="filter" action="?action=filter_card" method="POST">
- <h1>$(l10n filter_label)</h1>
- <input type="hidden" name="page" value="cards"/>
-
- $(
- n=0; m="$(list_categories |wc -l)"
- printf '%s\n' "${_GET[filter]}" |tr '^' '\n' \
- | while read filter; do
- [ -n "$filter" ] && filter_item "${filter%%:*}" "${filter#*:}" "$n"
- n=$((n + 3 + m))
- done
- filter_item any '' "$n"
- )
-
- <fieldset class="order">
- <legend>$(l10n filter_order):</legend>
- <label><input type="radio" name="order" value="firstname" $(check_order firstname)>$(l10n filter_firstname)</label>
- <label><input type="radio" name="order" value="lastname" $(check_order lastname) >$(l10n filter_lastname)</label>
- <label><input type="radio" name="order" value="bdate" $(check_order bdate) >$(l10n filter_bdate)</label>
- </fieldset>
- <button type="submit" name="choice" value="new_filter">$(l10n filter_apply)</button>
- <button type="submit" name="choice" value="del_filter">$(l10n filter_cancel)</button>
-</form>
-
-<form class="newcard" action="?action=new_card" method="POST">
- <button type="submit">$(l10n newcard)</button>
-</form>
-
-${edit:+$(edit_card "$edit")}
-
-<div class="dash">
- <a href="mailto:zack@vuesch.org?bcc=$(attribsafe $(listcards_mail))">$(l10n mail_all)</a>
-</div>
-EOF
-
-listcards |grep ${edit:+-v} "$edit" \
-| while read card; do
- cat <<-ENDCARD
- <div id="${card}" class="card">
- $(view_card "$card")<!--
- --><div class="control">
- <a class="item" href="?action=edit_card&card=${card}">$(l10n edit)</a>
- <a class="item" href="?action=export_vcard&card=${card}">$(l10n vcf_export)</a>
- ${profile_medical:+
- <a class="item" href="?action=new_prescription&client=${card}">$(l10n new_prescription)</a>
- }
- </div>
- </div>
- ENDCARD
-done
-
-# vi:set filetype=html:
+++ /dev/null
-# Copyright 2015, 2017, 2018 Paul Hänsch
-#
-# This file is part of Confetti.
-#
-# Confetti is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# Confetti is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with Confetti. If not, see <http://www.gnu.org/licenses/>.
-
-cat_listing(){
- list_categories | while read cat; do
- printf '<li>%s<button type="submit" name="remove" value="%s">%s</button></li>\n' \
- "$(htmlsafe "$cat")" "$(attribsafe "$cat")" "$(l10n cat_remove)"
- done
-}
-
-list_catsel(){
- card="$1"
- cats="$(get_categories "$card")"
-
- list_categories |while read cat; do
- printf '<li><label><input %s type="checkbox" name="%s" value="%s">%s</label></li>' \
- "$(printf %s "$cats" |grep -qF "$cat" && printf 'checked="checked"')" \
- "$(attribsafe "$card")" "$(attribsafe "$cat")" "$(htmlsafe "$cat")"
- done
-}
-
-display_catsel(){
- card="$1"
- printf '<li><h2>%s</h2><ul>' "$(htmlsafe "$(get_name "$card")")"
- list_catsel "$card"
- printf '</ul></li>'
-}
-
-cat <<EOF
-<form class="categories" action="?action=edit_categories" method="POST">
- <input type="submit" class="fakeorder" name="add" value="add" />
- <h1>$(l10n categories_label)</h1>
- <input type="hidden" name="page" value="categories"/>
- <ul>
- $(cat_listing)
- <li>
- <input type="text" name="newcat" placeholder="$(l10n cat_newlabel)">
- <button type="submit" name="add" value="add">$(l10n cat_add)</button>
- </li>
- </ul>
-</form>
-
-<form class="namelist" action="?action=update_categories" method="POST">
- <fieldset>
- <button type="submit" name="submit" value="submit">$(l10n cat_update)</button>
- </fieldset>
- <ul class="namelist">
-EOF
-listcards \
-| while read card; do
- display_catsel "$card"
-done
-cat <<EOF
- </ul>
- <fieldset>
- <button type="submit" name="submit" value="submit">$(l10n cat_update)</button>
- </fieldset>
-</form>
-EOF
-
+++ /dev/null
-# Copyright 2014 - 2016 Paul Hänsch
-#
-# This file is part of Confetti.
-#
-# Confetti is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# Confetti is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with Confetti. If not, see <http://www.gnu.org/licenses/>.
-
-. ${_EXEC}/templates/text_cards.sh
-
-echo -E '
-\documentclass[landscape,10pt]{article}
-\usepackage[utf8x]{inputenc}
-\usepackage{ngerman}
-\usepackage{eurosym}
-\usepackage[landscape,margin=0.25in]{geometry}
-\usepackage{longtable}
-
-\begin{document}
-
-\section*{Teilnehmende}
-\begin{longtable}{|p{60mm}|l|p{50mm}|p{80mm}|}
-\hline
- \textbf{'"$(l10n N)"'} &
- \textbf{'"$(l10n BDAY)"'} &
- \textbf{'"$(l10n TEL)"'} &
- \textbf{'"$(l10n NOTE)"'} \\
-\hline
-\hline
-\endhead
-'"$(
-list_attendance "$course" |sort -k 2 |while read line; do
- cardfile="$(echo "$line" |cut -d\ -f1)"
- list_attendee "$cardfile" |sed -r 's:$:\\\\[3ex] \\hline:'
-done
-)"'
-\end{longtable}
-
-\newpage
-
-\section*{Termine}
-\begin{longtable}{|p{60mm}|c|c|c|c|c|c|c|c|c|c|}
-\hline
- '"$(get_dates)"' \\
-\hline
-\hline
-\endhead
-'"$(
-tex_clean "$(list_attendance "$course")" |sort -k 2 | debug |sed -r 's:^[0-9a-z\.]+ (.+) \(\*[0-9]{4}\)$:\1:;s:$: \& \& \& \& \& \& \& \& \& \& \\\\[3ex] \\hline:'
-)"'
-\end{longtable}
-
-\end{document}
-'
+++ /dev/null
-# Copyright 2014 Paul Hänsch
-#
-# This file is part of Confetti.
-#
-# Confetti is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# Confetti is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with Confetti. If not, see <http://www.gnu.org/licenses/>.
-
-edit="${_GET[edit]}"
-
-check_order(){
- [ "${_GET[order]}" = "$1" ] && echo 'checked="checked"'
-}
-
-cat <<EOF
-<div class="sort">
- <form class="search" action="?" method="GET">
- <input type="hidden" name="page" value="courses"/>
- <input type="hidden" name="edit" value="$edit"/>
- <span class="label">$(l10n sort_order):</span>
- <input type="radio" name="order" value="DOW" $(check_order DOW)>$(l10n order_DOW)</input>
- <input type="radio" name="order" value="TOD" $(check_order TOD)>$(l10n order_TOD)</input>
- <br>
- <button type="submit" name="" value="">$(l10n order_apply)</button>
- </form>
-</div>
-
-<div class="newcourse">
- <form action="?action=new_course" method="POST">
- <button type="submit">$(l10n newcourse)</button>
- </form>
-</div>
-
-<div class="courselist">
-$(
-[ -f "ical/$edit" -o -f "temp/$edit" ] && edit_course "$edit"
-if [ "$?" = 0 ]; then
- listcourses |grep -v "$edit"
-else
- listcourses
-fi |while read card; do
- view_course "$card"
-done
-)
-</div>
-EOF
-
-# vi:set filetype=html:
+++ /dev/null
-# Copyright 2014 - 2017 Paul Hänsch
-#
-# This file is part of Confetti.
-#
-# Confetti is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# Confetti is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with Confetti. If not, see <http://www.gnu.org/licenses/>.
-
-setchecked() {
- printf 'checked="checked"'
-}
-setselected() {
- printf 'selected="selected"'
-}
-check_gen() {
- [ "$values[GENDER]" = "$1" ] && setselected
-}
-check_a() {
- egrep -q "^${1}.${id}$" "$_DATA/mappings/attendance" && setchecked
-}
-check_c() {
- for n in CATEGORIES CATEGORIES{0..100}; do
- if [ -n "${values[$n]+x}" ]; then
- [ "${values[$n]}" = "$1" ] && setchecked && break
- else
- break
- fi
- done
-}
-
-teltype() {
- cat <<-EOF
- <select class="item phonetype" name="phonetype">
- <option value="" disabled="disabled" $([ -z "$1" ] && setselected )>$(l10n phone_typeselect)</option>
- <option value=""></option>
- <option value="HOME" $([ "$1" = HOME ] && setselected)>$(l10n phone_home)</option>
- <option value="CELL" $([ "$1" = CELL ] && setselected)>$(l10n phone_cell)</option>
- <option value="WORK" $([ "$1" = WORK ] && setselected)>$(l10n phone_work)</option>
- <option value="FAX" $([ "$1" = FAX ] && setselected)>$(l10n phone_fax )</option>
- </select>
- EOF
-}
-
-list_items(){
- item="$1"
- placeholder="$2"
- [ -n "${values[$item]+x}" ] && printf '<h3>%s</h3>\n' "$(l10n $item)"
- for n in "$item" "$item"{0..100}; do
- if [ -n "${values[$n]+x}" ]; then
- case "$item" in
- (ADR|NOTE)
- printf '<textarea class="item %s" name="%s">%s</textarea>\n' \
- "$item" "$item" "$(htmlsafe ${values[$n]})"
- ;;
- (TEL)
- teltype "${values[${n}_TYPE]}"
- printf '<input class="item %s" name="%s" value="%s" />\n' \
- "$item" "$item" "$(attribsafe ${values[$n]})"
- ;;
- (*)
- printf '<input class="item %s" name="%s" value="%s" placeholder="%s"/>\n' \
- "$item" "$item" "$(attribsafe ${values[$n]})" "$placeholder"
- ;;
- esac
- else
- break
- fi
- done
-}
-
-list_section(){
- printf '<div class="section %s">' "$1"
- shift 1
- for each in $@; do
- list_items "$each"
- done
- printf '</div>'
-}
-
-hi_company="${values[X-HEALTH-INSURANCE+0]}"
- hi_number="${values[X-HEALTH-INSURANCE+1]}"
- hi_status="${values[X-HEALTH-INSURANCE+2]}"
-
-[ -z "$values[UID]" ] && values[UID]="$(uuidgenerator)"
-
-cat <<END_HTML
-<form id="$id" class="card" action="?action=update_card" method="POST">
- <input type="hidden" name="card" value="${id}"/>
- <div class="section basic">
- <h3>$(l10n N)</h3>
- <input class="item N" name="3N" placeholder="$(l10n n_pre)" value="$values[N+3]" />
- <input class="item N" name="1N" placeholder="$(l10n n_first)" value="$values[N+1]" />
- <input class="item N" name="2N" placeholder="$(l10n n_middle)" value="$values[N+2]" />
- <input class="item N" name="0N" placeholder="$(l10n n_last)" value="$values[N+0]" />
- <input class="item N" name="4N" placeholder="$(l10n n_post)" value="$values[N+4]" />
-
- <select class="item GENDER" name="GENDER">
- <option value="" disabled="disabled">$(l10n GENDER)</option>
- <option value="female" $(check_gen female)>$(l10n gender_female)</option>
- <option value="male" $(check_gen male)>$(l10n gender_male)</option>
- <option value="other" $(check_gen other)>$(l10n gender_other)</option>
- <option value="none" $(check_gen none)>$(l10n gender_none)</option>
- </select>
-END_HTML
-
- for n in NICKNAME NICKNAME{0..100}; do
- [ -z "${values[$n]+x}" ] && break \
- || printf '
- <input class="item NICKNAME" name="NICKNAME" value="%s" placeholder="%s"/>
- ' "$(attribsafe ${values[$n]})" "$(l10n NICKNAME)"
- done
-
- list_items BDAY YYYY-MM-DD
- list_items X-ZACK-JOINDATE YYYY-MM-DD
- list_items X-ZACK-LEAVEDATE YYYY-MM-DD
-
- [ -n "$values[SOUND]" ] && printf '
- <audio controls="controls" class="item SOUND">
- <source type="audio/ogg" src="data:audio/ogg;base64,%s" />
- </audio>' "$values[SOUND]"
-
- [ -n "$values[PHOTO]" ] && printf '
- <img class="item PHOTO" src="data:image/%s;base64,%s" />
- ' "${values[PHOTO_TYPE]}" "${values[PHOTO]}"
-
- [ -n "$values[LOGO]" ] && printf '
- <img class="item PHOTO" src="data:image/%s;base64,%s" />
- ' "${values[LOGO_TYPE]}" "${values[LOGO]}"
-
-printf '</div>'
-
-if [ "$PROFILE" = circus ]; then
- list_section phone TEL
- list_section message EMAIL IMPP URL
- list_section address ADR
- list_section note NOTE X-CLIENT-REFERRAL
- cat <<-END_HTML
- <div class="section attendance">
- <h3>$(l10n course_attendance)</h3>
- $(listcourses |while read each; do
- cname="$(sed -rn 's:^SUMMARY\:(.*)$:\1:p' "$_DATA/ical/$each")"
- printf '<label><input type="checkbox" name="attendance" value="%s" %s/>%s</label>' \
- "$(attribsafe "$each")" "$(check_a "$each")" "$(htmlsafe "$cname")"
- done)
- <h3>$(l10n CATEGORIES)</h3>
- $(list_categories |while read each; do
- printf '<label><input type="checkbox" name="CATEGORIES" value="%s" %s/>%s</label>' \
- "$(attribsafe "$each")" "$(check_c "$each")" "$(htmlsafe "$each")"
- done)
- </div>
- END_HTML
-elif [ "$PROFILE" = medical ]; then
- list_section address ADR
- list_section phone TEL EMAIL IMPP URL
- cat <<-END_HTML
- <div class="section insurance">
- <h3>$(l10n X-HEALTH-INSURANCE)</h3>
- <input type="radio" name="hi_select" value="list" id="hi_select_list" checked /><label for="hi_select_list">$(l10n hi_from_list)</label
- ><input type="radio" name="hi_select" value="other" id="hi_other"><label for="hi_other">$(l10n hi_other)</label>
- <select class="item" name="hi_company">
- <option value="" disabled="disabled" ${hi_company:-selected}>$(l10n hi_company)</option>
- $(list_hi_companies |while read -r f; do
- printf '<option value="%s" %s>%s</option>
- ' "$(attribsafe "$f")" "$([ "$f" = "$hi_company" ] && setselected )" "$(htmlsafe "$f")"
- done)
- </select>
- <input type="text" name="hi_other" value="$hi_company" placeholder="$(l10n hi_company)" />
- <input name="hi_number" value="$hi_number" placeholder="$(l10n hi_number)" />
- <input name="hi_status" value="$hi_status" placeholder="$(l10n hi_status)" />
- </div>
- END_HTML
- list_section note NOTE X-CLIENT-REFERRAL
-fi
-
-
-cat <<END_HTML
- <div class="control">
- <select class="item" name="newfield">
- <option value="" disabled="disabled" selected="selected">$(l10n edit_addfieldtext)</option>
- $(for f in $SUP_FIELDS; do printf '<option value="%s">%s</option>\n' "$f" "$(l10n "$f")"; done)
- </select
- ><button class="item" type="submit" name="action" value="addfield">$(l10n edit_addfield)</button>
- <button class="item" type="submit" name="action" value="update">$(l10n edit_update)</button>
- <input type="checkbox" id="delete"><label class="item" for="delete">$(l10n edit_delete)</label>
- <button class="item" type="submit" name="action" value="delete">$(l10n edit_delete)</button>
- <button class="item" type="submit" name="action" value="cancel">$(l10n edit_cancel)</button>
- </div>
- <input type="hidden" name="UID" value="$values[UID]"/>
-</form>
-END_HTML
+++ /dev/null
-# Copyright 2014 Paul Hänsch
-#
-# This file is part of Confetti.
-#
-# Confetti is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# Confetti is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with Confetti. If not, see <http://www.gnu.org/licenses/>.
-
-SUP_FIELDS=(SUMMARY COMMENT)
-[ -z "$values[UID]" ] && values[UID]="$(uuidgenerator)"
-
-dtstart="$values[DTSTART]"
-[ -z "$dtstart" ] && dtstart=$(date +%Y%m%dT%H%M%S)
-
-echo "$dtstart" |case "$dtstart" in
- *Z)
- sed -rn 's:^([0-9]{4})([0-9]{2})([0-9]{2})T([0-9]{2})([0-9]{2})([0-9]{2})Z$:\1-\2-\3 \4\:\5\:\6 UTC:p'
- ;;
- TZID*)
- sed -rn 's:^TZID=(.+)\:([0-9]{4})([0-9]{2})([0-9]{2})T([0-9]{2})([0-9]{2})([0-9]{2})$:TZ="\1" \2-\3-\4 \5\:\6\:\7:p'
- ;;
- *)
- sed -rn 's:^([0-9]{4})([0-9]{2})([0-9]{2})T([0-9]{2})([0-9]{2})([0-9]{2})$:\1-\2-\3 \4\:\5\:\6:p'
- ;;
-esac |read dts_date
-
-dts_year=$(date -d "$dts_date" +%Y)
-dts_month=$(date -d "$dts_date" +%m)
-dts_dom=$(date -d "$dts_date" +%d)
-dts_dow=$(date -d "$dts_date" +%u)
-dts_weekday=$(date -d "$dts_date" +%A)
-dts_hour=$(date -d "$dts_date" +%H)
-dts_min=$(date -d "$dts_date" +%M)
-dts_sec=$(date -d "$dts_date" +%S)
-
-rrule="$values[RRULE]"
-rr_int="$(echo $rrule |sed -rn 's:^.*INTERVAL=([0-9]+)(;.*)?$:\1:p')"
-rr_count="$(echo $rrule |sed -rn 's:^.*COUNT=([0-9]+)(;.*)?$:\1:p')"
-rr_freq="$(echo $rrule |sed -rn 's:^.*FREQ=(YEARLY|MONTHLY|WEEKLY|DAILY)(;.*)?$:\1:p')"
-rr_uyear="$(echo $rrule |sed -rn 's:^.*UNTIL=([0-9]{4})([0-9]{2})([0-9]{2})T[0-9]{6}Z(;.*)?$:\1:p')"
-rr_umonth="$(echo $rrule |sed -rn 's:^.*UNTIL=([0-9]{4})([0-9]{2})([0-9]{2})T[0-9]{6}Z(;.*)?$:\2:p')"
-rr_uday="$(echo $rrule |sed -rn 's:^.*UNTIL=([0-9]{4})([0-9]{2})([0-9]{2})T[0-9]{6}Z(;.*)?$:\3:p')"
-if [ -n "$rr_count" ]; then
- rr_limit=COUNT
-elif [ -n "$rr_uyear" ]; then
- rr_limit=UNTIL
-else
- rr_limit=ETERN
-fi
-
-echo '<div id="'${id}'" class="course">'
-echo '<form action="?action=update_course" method="POST">'
-echo '<input type="hidden" name="course" value="'${id}'"/>'
-echo ' <div class="section basic">'
-echo ' <h2>'$(l10n SUMMARY)'</h2>'
-echo ' <input type="text" class="item SUMMARY" name="SUMMARY" value="'$values[SUMMARY]'" />'
-echo ' <h3>'$(l10n COMMENT)'</h3>'
-for n in COMMENT COMMENT{0..10}; do [ -n "$values[$n]" ] &&\
- echo ' <textarea class="item COMMENT" name="COMMENT">'$values[$n]'</textarea>'
-done
-
-echo ' </div><div class="section dtstart">'
-echo ' <h3>'$(l10n DTSTART)'</h3>'
-echo ' <select class="DTSYEAR" name="DTSYEAR" onchange="this.form.submit();">'
-for y in {$(($dts_year - 50))..$(($dts_year + 50))}; do
- echo ' <option value="'$y'" '$([ "$y" -eq "$dts_year" ] && echo 'selected="selected"')'>'$y'</option>'
-done
-echo ' </select>'
-echo ' <select class="DTSMONTH" name="DTSMONTH" onchange="this.form.submit();">'
-for m in {01..12}; do
- echo ' <option value="'$m'" '$([ "$m" -eq "$dts_month" ] && echo 'selected="selected"')'>'$(date -d ${dts_year}-${m}-1 +%B)'</option>'
-done
-echo ' </select>'
-echo ' <button class="DTS" type="submit" name="DTS" value="update">'$(l10n edit_dtscal)'</button><br/>'
-for wd in mon tue wed thu fri sat sun; do
- echo -n '<span class="DTSCALHEAD">'$(date -d $wd +%a)'</span>'
-done
-fdom=$(date -d ${dts_year}-${dts_month}-1 +%u)
-while [ "$fdom" -gt 1 ]; do
- echo -n '<span class="DTSCAL"></span>'
- fdom=$(($fdom - 1))
-done
-cnt=$(date -d ${dts_year}-${dts_month}-1 +%s)
-while [ "$(date -d @$cnt +%m)" = "$dts_month" ]; do
- dn=$(date -d @$cnt +%d)
- echo -n '<input type="radio" class="DTSCAL" name="DTSDAY" value="'$dn'" id="DTSCAL_'$dn'" '$([ $dn = $dts_dom ] && echo checked)'><label for="DTSCAL_'$dn'" class="DTSCAL">'$dn'</label>'
- cnt=$(($cnt + 86400))
-done
-echo ' <br />'
-echo ' <span class="DTSTIME">'$(l10n time)':</span>'
-echo ' <select class="DTSTIME" name="DTSHOUR">'
-for h in {00..23}; do
- echo ' <option value="'$h'" '$([ "$h" -eq "$dts_hour" ] && echo 'selected="selected"')'>'$h'</option>'
-done
-echo ' </select>:<select class="DTSTIME" name="DTSMINUTE">'
-for m in 00 05 10 15 20 25 30 35 40 45 50 55; do
- echo ' <option value="'$m'" '$([ "$m" -eq "$dts_min" ] && echo 'selected="selected"')'>'$m'</option>'
-done
-echo ' </select>'
-
-echo ' </div><div class="section recur">'
-echo ' <h3>'$(l10n RRULE)'</h3>'
-echo ' <span class="item">'
-echo ' '$(l10n t_every)''
-echo ' <select class="RRULE INTERVAL" name="RRULE_INTERVAL">'
-for i in {1..365}; do
- echo ' <option value="'$i'" '$([ "$i" = "$rr_int" ] && echo 'selected="selected"')'>'$i'</option>'
-done
-echo ' </select>'
-echo ' <select class="RRULE FREQ" name="RRULE_FREQ">'
-for f in DAILY WEEKLY MONTHLY YEARLY; do
- echo ' <option value="'$f'" '$([ "$f" = "$rr_freq" ] && echo 'selected="selected"')'>'$(l10n $f)'</option>'
-done
-echo ' </select></span>'
-echo ' <span class="item">'
-echo ' <input type="radio" name="RRULE_LIMIT" value="ETERN" '$([ "$rr_limit" = ETERN ] && echo 'checked="checked"')'>'$(l10n t_eternal)'</input><br/>'
-echo ' </span><span class="item">'
-echo ' <input type="radio" name="RRULE_LIMIT" value="COUNT" '$([ "$rr_limit" = COUNT ] && echo 'checked="checked"')'></input>'
-echo ' <select class="RRULE COUNT" name="RRULE_COUNT">'
-for t in {1..365}; do
- echo ' <option value="'$t'" '$([ "$t" = "$rr_count" ] && echo 'selected="selected"')'>'$t'</option>'
-done
-echo ' </select>'$(l10n t_times)'</span>'
-echo ' <span class="item">'
-echo ' <input type="radio" name="RRULE_LIMIT" value="UNTIL" '$([ "$rr_limit" = UNTIL ] && echo 'checked="checked"')'>'$(l10n t_until)'</input>'
-echo ' <select class="RRULE UYEAR" name="RRULE_UYEAR">'
-for uy in {${dts_year}..$(($dts_year + 50))}; do
- echo ' <option value="'$uy'" '$([ "$uy" = "$rr_uyear" ] && echo 'selected="selected"')'>'$uy'</option>'
-done
-echo ' </select>'
-echo ' <select class="RRULE UMONTH" name="RRULE_UMONTH">'
-for um in {01..12}; do
- echo ' <option value="'$um'" '$([ "$um" = "$rr_umonth" ] && echo 'selected="selected"')'>'$(date -d 2000-${um}-1 +%B)'</option>'
-done
-echo ' </select>'
-echo ' <select class="RRULE UDAY" name="RRULE_UDAY">'
-for ud in {01..31}; do
- echo ' <option value="'$ud'" '$([ "$ud" = "$rr_uday" ] && echo 'selected="selected"')'>'$ud'</option>'
-done
-echo ' </select></span>'
-
-echo ' </div><div class="section select_attendance">'
-echo ' <h3>'$(l10n course_attendance)'</h3>'
-listcards |while read card; do
- cfile="$_DATA/vcard/$card"
- aname=$(sed -rn 's:^N(;[^"\:]+|;"[^"]+")*\:([^;]*)(\;[^;]*)(\;[^;]*)?(\;[^;]*)?(\;[^;]*)?$:\5 \3 \4 \2 \6:p' "$cfile" \
- |sed -r 's:,: :;s:\;: :g;s: +: :g' \
- |tr -d '\r'
- )
- selected=$(sed -rn "/^$id\t$card\$/{s;^.*\$;checked=\"checked\";p;q}" "${_DATA}/mappings/attendance")
- echo ' <label><input type="checkbox" class="item" name="attendance" value="'$card'" '$selected'>'$aname'</label>'
-done
-
-echo ' </div><div class="control">'
-echo ' <select class="item" name="newfield">'
-echo ' <option value="" disabled="disabled" selected="selected">'$(l10n edit_addfieldtext)'</option>'
- for f in $SUP_FIELDS; do echo "<option value=\"$f\">$(l10n $f)</option>"; done
-echo ' </select'
-echo ' ><button class="item" type="submit" name="action" value="addfield">'$(l10n edit_addfield)'</button>'
-echo ' <button class="item" type="submit" name="action" value="update">'$(l10n edit_update)'</button>'
-echo ' <button class="item" type="submit" name="action" value="delete">'$(l10n edit_delete)'</button>'
-echo ' <button class="item" type="submit" name="action" value="cancel">'$(l10n edit_cancel)'</button>'
-echo ' </div>'
-echo '<input type="hidden" name="UID" value="'$values["UID"]'"/>'
-echo '</form>'
-echo '</div>'
-
+++ /dev/null
-# Copyright 2016 Paul Hänsch
-#
-# This file is part of Confetti.
-#
-# Confetti is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# Confetti is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with Confetti. If not, see <http://www.gnu.org/licenses/>.
-
-check(){ [ "$1" = "$2" ] && printf checked}
-[ -z $mpx[presctype] ] && mpx[presctype]=doctor_compulsory
-
-cat <<END_HTML
-<form class="prescription" action="?action=update_prescription" method="POST">
-<input type="hidden" name="prescription" value="${mpx[prescription]}">
-
-<label class=presctype>$(l10n doctor):</label>
-<input type=radio name=presctype id=doctor_selfpaid value=doctor_selfpaid $(check "$mpx[presctype]" doctor_selfpaid)>
-<label for=doctor_selfpaid>$(l10n selfpaid)</label>
-<input type=radio name=presctype id=doctor_private value=doctor_private $(check "$mpx[presctype]" doctor_private)>
-<label for=doctor_private>$(l10n private)</label>
-<input type=radio name=presctype id=doctor_compulsory value=doctor_compulsory $(check "$mpx[presctype]" doctor_compulsory)>
-<label for=doctor_compulsory>$(l10n compulsory)</label>
-<br>
-
-<label class=presctype>$(l10n dentist):</label>
-<input type=radio name=presctype id=dentist_selfpaid value=dentist_selfpaid $(check "$mpx[presctype]" dentist_selfpaid)>
-<label for=dentist_selfpaid>$(l10n selfpaid)</label>
-<input type=radio name=presctype id=dentist_private value=dentist_private $(check "$mpx[presctype]" dentist_private)>
-<label for=dentist_private>$(l10n private)</label>
-<input type=radio name=presctype id=dentist_compulsory value=dentist_compulsory $(check "$mpx[presctype]" dentist_compulsory)>
-<label for=dentist_compulsory>$(l10n compulsory)</label>
-<br>
-
-<label class=presctype>$(l10n noprescription):</label>
-<input type=radio name=presctype id=noprescription_selfpaid value=noprescription_selfpaid $(check "$mpx[presctype]" noprescription_selfpaid)>
-<label for=noprescription_selfpaid>$(l10n selfpaid)</label>
-<br>
-
-<label class=presctype>$(l10n altpractition):</label>
-<input type=radio name=presctype id=altpractition_selfpaid value=altpractition_selfpaid $(check "$mpx[presctype]" altpractition_selfpaid)>
-<label for=altpractition_selfpaid>$(l10n selfpaid)</label>
-<input type=radio name=presctype id=altpractition_private value=altpractition_private $(check "$mpx[presctype]" altpractition_private)>
-<label for=altpractition_private>$(l10n private)</label>
-<br>
-
-<fieldset class="baseinfo">
-<label for="insurance">$(l10n insurance)</label>
-<input id="insurance" name="insurance" value="${mpx[insurance]}" placeholder="$(l10n insurance)">
-<br>
-<label for="name">$(l10n name)</label>
-<label for="bday">$(l10n bday)</label>
-<br>
-<textarea id="name" name="name" placeholder="$(l10n name)">${mpx[name]}</textarea>
-<input id="bday" name="bday" value="${mpx[bday]}" placeholder="$(l10n bday)">
-
-<br>
-<label for="date">$(l10n date)</label>
-<input id="date" name="date" value="${mpx[date]}" placeholder="$(l10n date)">
-</fieldset>
-
-<fieldset class="misc">
-<h1 id="${mpx[prescription]}">$(l10n therapy_prescription)</h1>
-
-<label for="addcontrib">$(l10n addcontrib)</label>
-<input id="addcontrib" name="addcontrib" value="${mpx[addcontrib]}" placeholder="$(l10n addcontrib)">
-<label for="contribconfirm">$(l10n contribconfirm)</label>
-<input id="contribconfirm" name="contribconfirm" value="${mpx[contribconfirm]}" placeholder="$(l10n contribconfirm)">
-<input type="checkbox" id="contribreceipt" name="contribreceipt" value="true" ${mpx[contribreceipt]:+checked}>
-<label for="contribreceipt">$(l10n contribreceipt)</label>
-</fieldset>
-
-<input type="checkbox" id="prescreviewed" name="prescreviewed" value="true" ${mpx[prescreviewed]:+checked}>
-<label for="prescreviewed">$(l10n prescreviewed)</label>
-
-<fieldset class="catalogue">
-<h2>$(l10n prescription_by_catalogue)</h2>
-<input type="radio" id="prescfirst" name="prescno" value="first" $(check "$mpx[prescno]" first)>
-<label for="prescfirst">$(l10n prescfirst)</label>
-<br>
-<input type="radio" id="prescfollow1" name="prescno" value="follow1" $(check "$mpx[prescno]" follow1)>
-<label for="prescfollow1">$(l10n prescfollow1)</label>
-<br>
-<input type="radio" id="prescfollow2" name="prescno" value="follow2" $(check "$mpx[prescno]" follow2)>
-<label for="prescfollow2">$(l10n prescfollow2)</label>
-<br>
-<input type="radio" id="prescother" name="prescno" value="other" $(check "$mpx[prescno]" other)>
-<label for="prescother">$(l10n prescother)</label>
-<br>
-<input type="radio" id="presccontinual" name="prescno" value="continual" $(check "$mpx[prescno]" continual)>
-<label for="presccontinual">$(l10n presccontinual)</label>
-
-<br>
-<input type="checkbox" id="grouptherapy" name="grouptherapy" value="true" ${mpx[grouptherapy]:+checked}>
-<label for="grouptherapy">$(l10n grouptherapy)</label>
-<br>
-<input type="checkbox" id="housecall" name="housecall" value="true" ${mpx[housecall]:+checked}>
-<label for="housecall">$(l10n housecall)</label>
-<br>
-<input type="checkbox" id="report" name="report" value="true" ${mpx[report]:+checked}>
-<label for="report">$(l10n report)</label>
-</fieldset>
-
-<fieldset class="description">
-<label for="quantity">$(l10n quantity)</label>
-<label for="remidy">$(l10n remidy)</label>
-<label for="quantity_weekly">$(l10n quantity_weekly)</label>
-<p>
-<input id="quantity" name="quantity" value="${mpx[quantity]}" placeholder="$(l10n quantity)">
-<textarea id="remidy" name="remidy" placeholder="$(l10n remidy)">${mpx[remidy]}</textarea>
-<input id="quantity_weekly" name="quantity_weekly" value="${mpx[quantity_weekly]}" placeholder="$(l10n quantity_weekly)">
-</p>
-
-$( for n in {0..10}; do
- if [ "$n" -eq 0 -o -n "${mpx[quantity$n]}" -o -n "${mpx[remidy$n]}" -o -n "${mpx[quantity_weekly$n]}" ]; then
- printf '<input class="trailbtn" type="checkbox" checked="checked" />'
- else
- printf '<input class="trailbtn" type="checkbox" />'
- fi
- printf '<p class="trailbox">
- <input class="quantity" name="quantity" placeholder="%s" value="%s">
- <textarea class="remidy" name="remidy" placeholder="%s">%s</textarea>
- <input class="quantity_weekly" name="quantity_weekly" placeholder="%s" value="%s">
- </p>
- ' "$(l10n quantity)" "${mpx[quantity$n]}" \
- "$(l10n remidy)" "${mpx[remidy$n]}" \
- "$(l10n quantity_weekly)" "${mpx[quantity_weekly$n]}"
-done )
-
-<br>
-<p class="indicator_codes">
-<label for="indicator">$(l10n indicator)</label>
-<input id="indicator" name="indicator" value="${mpx[indicator]}" placeholder="$(l10n indicator)">
-<br>
-<label for="icd10">$(l10n icd10)</label>
-<input id="icd10" name="icd10" value="${mpx[icd10]}" placeholder="$(l10n icd10)">
-</p>
-<br>
-<p class="indicator_reading">
-<label for="indicator_reading">$(l10n indicator_reading)</label>
-<textarea id="indicator_reading" name="indicator_reading" placeholder="$(l10n indicator_reading)">${mpx[indicator_reading]}</textarea>
-</p>
-<br>
-<p class="issuer">
- <label>$(l10n issuer)</label>
- <input type="radio" name="issuer_select" value="list" id="issuer_select_list" checked /><label for="issuer_select_list">$(l10n issuer_from_list)</label><!--
- --><input type="radio" name="issuer_select" value="other" id="issuer_other"><label for="issuer_other">$(l10n issuer_other)</label>
- <select class="item" name="issuer">
- <option value="" disabled="disabled" $([ -z "${mpx[issuer]}" ] && printf 'selected' )>$(l10n issuer)...</option>
- $(list_prescription_issuers |while read f; do
- [ "$f" = "$mpx[issuer]" ] \
- && printf '<option value="%s" selected>%s</option>' "$f" "$f" \
- || printf '<option value="%s">%s</option>' "$f" "$f"
- done)
- </select>
- <input type="text" name="issuer_other" value="" placeholder="$(l10n issuer)..." />
-</p>
-</fieldset>
-
-<fieldset class="controls">
-<button type="submit" name="action" value="save">$(l10n save)</button>
-<button type="submit" name="action" value="cancel">$(l10n cancel)</button>
-<button type="submit" name="action" value="delete">$(l10n delete)</button>
-</fieldset>
-
-</form>
-END_HTML
+++ /dev/null
-# Copyright 2014 Paul Hänsch
-#
-# This file is part of Confetti.
-#
-# Confetti is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# Confetti is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with Confetti. If not, see <http://www.gnu.org/licenses/>.
-
-cat <<EOF
-<div class="select">
- <span class="label">$(l10n filter_label)</span>
- <form class="search" action="?action=select_mailing" method="POST">
-
- <div class="selectlist">
- <span class="label">$(l10n select_courses):</span>
- <select name="courses" multiple="multiple">
- $(listcourses |while read course; do
- name="$(sed -rn 's:^SUMMARY(;.+)*\:(.*)\r$:\2:p' "$_DATA/ical/$course")"
- echo '<option value="'$course'">'$name'</option>'
- done)
- </select>'
- </div>
-
- <div class="selectlist">
- <span class="label">$(l10n select_attendees):</span>
- <select name="courses" multiple="multiple">
- $(listcards |while read card; do
- n_last="$( sed -rn 's:^N(;.+)*\:([^;]*;){0} *([^;]*).*$:\3:p' "$_DATA/vcard/$card")"
- n_first="$( sed -rn 's:^N(;.+)*\:([^;]*;){1} *([^;]*).*$:\3:p' "$_DATA/vcard/$card")"
- n_middle="$(sed -rn 's:^N(;.+)*\:([^;]*;){2} *([^;]*).*$:\3:p' "$_DATA/vcard/$card")"
- n_pre="$( sed -rn 's:^N(;.+)*\:([^;]*;){3} *([^;]*).*$:\3:p' "$_DATA/vcard/$card")"
- n_post="$( sed -rn 's:^N(;.+)*\:([^;]*;){4} *([^;]*).*$:\3:p' "$_DATA/vcard/$card")"
- name="${n_pre} ${n_first} ${n_middle} ${n_last} ${n_post}"
- echo '<option value="'$card'">'$name'</option>'
- done)
- </select>'
- </div>
-
- <button type="submit" name="choice" value="new_selection">$(l10n selection_apply)</button>
- <button type="submit" name="choice" value="del_selection">$(l10n selection_clear)</button>
- </form>
-</div>
-
-<div class="maillist">
-</div>
-EOF
-
-# vi:set filetype=html:
+++ /dev/null
-#!/bin/zsh
-
-# Copyright 2014, 2015, 2017 Paul Hänsch
-#
-# This file is part of Confetti.
-#
-# Confetti is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# Confetti is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with Confetti. If not, see <http://www.gnu.org/licenses/>.
-
-. "${_EXEC}/templates/text_frame.sh"
-[ -x "${_EXEC}/templates/text_${PAGE}.sh" ] && . "${_EXEC}/templates/text_${PAGE}.sh"
-
-bmfile="${_DATA}/mappings/bookmarks"
-
-cat <<EOF
-<!Doctype HTML>
-
-<html>
- <head>
- <title>$(l10n p_${PAGE})</title>
- <meta name="viewport" content="width=device-width">
- <link rel="stylesheet" href="?static=common.css">
- <link rel="stylesheet" href="?static=${PAGE}.css">
- </head>
- <body class="$PAGE" id="CONFIGURE">
- <div class="NAVIGATION">
- $(sed -rn '/^[^ ]+\t[^ ]+$/p' "$bmfile" \
- | while read page name; do
- printf '<a href="%s">%s</a>' "${page}" "${name}"
- done)
- <label for="navigationconfig">$(l10n nc_edit)</label>
- <input id="navigationconfig" class="config" type="checkbox" />
- <form class="config" method="POST" action="?action=update_bookmarks">
- $(for page in "${_EXEC}/pages/"*.sh; do
- page="${page##*/}"
- page="${page%.sh}"
- printf '<a href="?p=%s">%s</a>\n' "${page}" "$(l10n "p_${page}")"
- done |grep -E '^<a href="\?p='"${page_filter}" )
- $(if grep -qF "$REQUEST_URI" "$bmfile"; then
- printf '<input type="hidden" name="bm_url" value="%s" />
- <input type="text" name="bm_name" value="%s" readonly="readonly" /
- ><button type="submit" name="submit" value="del">%s</button>' \
- "$(attribsafe ${REQUEST_URI})" \
- "$(attribsafe $(grep -m1 -F "$REQUEST_URI " "$bmfile" |sed -r 's;.*\t;;'))" \
- "$(l10n bm_del)"
- else
- printf '<input type="hidden" name="bm_url" value="%s" />
- <input type="text" name="bm_name" placeholder="%s" /
- ><button type="submit" name="submit" value="add">%s</button>' \
- "$(attribsafe ${REQUEST_URI})" "$(l10n Bookmark)" "$(l10n bm_add)"
- fi)
- </form>
- </div>
-EOF
-
-debug BODY; [ -x "${BODY}" ] && . "${BODY}" || printf %s 'Error'
-
-cat <<EOF
- <div id="footer">
- <a href="#CONFIGURE">$(l10n configure)</a>
- </div>
-</body></html>'
-EOF
-
-# vi:set filetype=html:
+++ /dev/null
-# Copyright 2014, 2016 Paul Hänsch
-#
-# This file is part of Confetti.
-#
-# Confetti is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# Confetti is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with Confetti. If not, see <http://www.gnu.org/licenses/>.
-
-item_name[PHOTO]="Foto"
-item_name[LOGO]="Logo"
-item_name[FN]="Voller Name"
-item_name[N]="Name"
-item_name[n_pre]="Titel"
-item_name[n_first]="Vorname"
-item_name[n_middle]="Mittelnamen"
-item_name[n_last]="Nachname"
-item_name[n_post]="Zusätze"
-item_name[NICKNAME]="Spitzname"
-item_name[SOUND]="Aussprache"
-item_name[GENDER]="Geschlecht"
-item_name[KIND]="Typ"
-item_name[TITLE]="Beruf"
-item_name[ROLE]="Position"
-item_name[ORG]="Organisation"
-item_name[MEMBER]="Mitglied"
-item_name[CATEGORIES]="Kategorien"
-item_name[ANNIVERSARY]="Jubiläum"
-item_name[BDAY]="Geburtstag"
-item_name[EMAIL]="E-Mail"
-item_name[TEL]="Telefon"
-item_name[phone_typeselect]="Typ"
-item_name[phone_home]="Privat"
-item_name[phone_cell]="Mobil"
-item_name[phone_work]="Büro"
-item_name[phone_fax]="Fax"
-item_name[tHOME]="Privat:"
-item_name[tWORK]="Büro:"
-item_name[tCELL]="Mobil:"
-item_name[tFAX]="Fax:"
-item_name[tVOICE]=""
-item_name[IMPP]="Chat"
-item_name[ADR]="Anschrift"
-item_name[URL]="Webseite"
-item_name[LANG]="Sprache"
-item_name[NOTE]="Notiz"
-
-item_name[RELATED]="Kontakte"
-
-item_name[BEGIN]=""
-item_name[CALADRURI]=""
-item_name[CALURI]=""
-item_name[CLASS]=""
-item_name[CLIENTPIDMAP]=""
-item_name[END]=""
-item_name[FBURL]=""
-item_name[GEO]=""
-item_name[MAILER]=""
-item_name[NAME]=""
-item_name[PRODID]=""
-item_name[PROFILE]=""
-item_name[REV]=""
-item_name[SORT-STRING]=""
-item_name[SOURCE]=""
-item_name[TZ]=""
-item_name[UID]=""
-item_name[VERSION]=""
-item_name[XML]=""
-
-item_name[X-HEALTH-INSURANCE]="Kran­ken­ver­sich­er­ung"
-item_name[hi_from_list]="Aus Liste"
-item_name[hi_other]="Andere"
-item_name[hi_company]="Ver­sich­er­ungs­ge­sell­schaft"
-item_name[hi_number]="Ver­sich­er­ten­num­mer"
-item_name[hi_status]="Ver­sich­er­ten­sta­tus"
-item_name[X-HEALTH-INSURANCE-NOCONTRIB]="Zu­zahl­ungs­be­frei­ung"
-item_name[X-CLIENT-REFERRAL]="Empfehl­ung durch"
-item_name[prescriptions]="Verordnungen"
-item_name[new_prescription]="Neue Verordnung"
-item_name[no_icd]="Kein ICD Code"
-
-item_name[X-ZACK-JOINDATE]="An­mel­de­da­tum"
-item_name[X-ZACK-LEAVEDATE]="Ab­mel­de­da­tum"
-item_name[label_join]="Anm."
-item_name[label_leave]="Abm."
-
-item_name[mail_all]="Mail an alle gelisteten"
-item_name[edit]="Bearbeiten"
-item_name[edit_categories]="Kategorien Bearbeiten"
-item_name[vcf_export]="Vcard Exportieren"
-item_name[control]="Aktionen"
-item_name[edit_update]="Daten übernehmen"
-item_name[edit_cancel]="Abbrechen"
-item_name[edit_delete]="Eintrag löschen"
-item_name[edit_addfieldtext]="Neues Feld"
-item_name[edit_addfield]="+"
-item_name[edit_deletefield]="X"
-item_name[filter_label]="Filter"
-item_name[filter_placeholder]="Begriffe zur Eingrenzung eingeben"
-item_name[filter_type]="Filtertyp"
-item_name[filter_order]="Sortierung"
-item_name[filter_all]="Alles"
-item_name[filter_name]="Name"
-item_name[filter_firstname]="Vorname"
-item_name[filter_lastname]="Nachname"
-item_name[filter_street]="Straße"
-item_name[filter_zip]="PLZ."
-item_name[filter_phone]="Telefon"
-item_name[filter_birthyear]="Geburtsjahr"
-item_name[filter_bdate]="Geburtsdatum"
-item_name[filter_course]="Kurs"
-item_name[filter_apply]="Filtern"
-item_name[filter_cancel]="Filter löschen"
-item_name[newcard]="Neuen Eintrag anlegen"
-item_name[course_attendance]="Kursteilnahme"
-
-item_name[gender_none]="keine Angabe"
-item_name[gender_female]="Weiblich"
-item_name[gender_male]="Männlich"
-item_name[gender_other]="Sonstiges"
-
-item_name[female]="♀"
-item_name[male]="♂"
-item_name[other]="⚥"
-item_name[none]="⚪"
-
+++ /dev/null
-# Copyright 2014, 2015 Paul Hänsch
-#
-# This file is part of Confetti.
-#
-# Confetti is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# Confetti is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with Confetti. If not, see <http://www.gnu.org/licenses/>.
-
-item_name[cat_remove]="-"
-item_name[cat_add]="+"
-item_name[cat_newlabel]="neue Kategorie"
-item_name[cat_update]="Zuweisungen übernehmen"
-item_name[categories_label]="Kategorien"
+++ /dev/null
-# Copyright 2014, 2016 Paul Hänsch
-#
-# This file is part of Confetti.
-#
-# Confetti is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# Confetti is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with Confetti. If not, see <http://www.gnu.org/licenses/>.
-
-declare -A item_name
-
-item_name[SUMMARY]="Bezeichnung"
-item_name[COMMENT]="Kommentar"
-item_name[DTSTART]="Beginn"
-item_name[DURATION]="Dauer"
-item_name[RRULE]="Regelmäßigkeit"
-
-item_name[DAILY]="Tage"
-item_name[WEEKLY]="Wochen"
-item_name[MONTHLY]="Monate"
-item_name[YEARLY]="Jahre"
-
-item_name[sDAILY]="Täglich"
-item_name[sWEEKLY]="Wöchentlich"
-item_name[sMONTHLY]="Monatlich"
-item_name[sYEARLY]="Jährlich"
-
-item_name[newcourse]="Neuen Eintrag anlegen"
-item_name[time]="Uhrzeit"
-item_name[edit_update]="Daten übernehmen"
-item_name[edit_cancel]="Abbrechen"
-item_name[edit_delete]="Eintrag löschen"
-item_name[edit_addfieldtext]="Neues Feld"
-item_name[edit_addfield]="+"
-item_name[edit_dtscal]="✓"
-item_name[edit]="Bearbeiten"
-item_name[ics_export]="ICal exportieren"
-item_name[courselist]="Kursliste (PDF)"
-
-item_name[course_attendance]="Teilnahme"
-item_name[course_mail]="Mail an Teilnehmende"
-
-item_name[sort_order]="Sortierung"
-item_name[order_DOW]="Wochentag"
-item_name[order_TOD]="Uhrzeit"
-item_name[order_apply]="Sortieren"
-
-item_name[t_every]="Alle"
-item_name[t_eternal]="ewig"
-item_name[t_times]="mal"
-item_name[t_until]="Bis"
-item_name[t_oclock]="Uhr"
+++ /dev/null
-# Copyright 2014 - 2016, 2018 Paul Hänsch
-#
-# This file is part of Confetti.
-#
-# Confetti is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# Confetti is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with Confetti. If not, see <http://www.gnu.org/licenses/>.
-
-item_name[p_cards]="Personen"
-item_name[p_courses]="Kurse"
-item_name[p_email]="Email"
-item_name[p_error]="Fehler"
-item_name[p_categories]="Kategorien"
-item_name[p_prescriptions]="Verschreibungen"
-item_name[p_therapy]="Therapie"
-item_name[nc_edit]="⚙"
-#item_name[nc_edit]="☰"
-item_name[bm_add]="+"
-item_name[bm_del]="-"
-item_name[configure]="Anpassen"
+++ /dev/null
-# Copyright 2016 Paul Hänsch
-#
-# This file is part of Confetti.
-#
-# Confetti is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# Confetti is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with Confetti. If not, see <http://www.gnu.org/licenses/>.
-
-. $_EXEC/templates/text_cards.sh
-
-item_name[therapy_prescription]="Heil­mit­tel­ver­ord­nung"
-item_name[prescriptions_current]="Aktuelle Verordnungen"
-item_name[prescriptions_past]="Frühere Verordnungen"
-item_name[newprescription]="Neue Verordnung"
-item_name[date]="Datum"
-item_name[name]="Name d. Versicherten"
-item_name[bday]="geb. am"
-item_name[addcontrib]="Zuzahlung"
-item_name[contribconfirm]="Zuzahlung erfolgt am..."
-item_name[contribreceipt]="Quit­tung heraus­ge­ge­ben"
-item_name[prescreviewed]="Verordnung geprüft"
-item_name[prescreview]="Verordnung prüfen!"
-item_name[quantity]="Ver­ord­nungs­men­ge"
-item_name[remidy]="Heil­mit­tel nach Maß­ga­be des Ka­ta­lo­ges"
-item_name[prescfirst]="Erst­ver­ord­nung"
-item_name[prescfollow1]="1. Folge-­VO"
-item_name[prescfollow2]="2. Folge-­VO"
-item_name[prescother]="VO außer­halb des Re­gel­falls"
-item_name[presccontinual]="Lang­frist­ver­ord­nung"
-item_name[grouptherapy]="Grup­pen­the­ra­pie"
-item_name[housecall]="Haus­be­such"
-item_name[report]="The­ra­pie­be­richt"
-item_name[indicator]="In­di­ka­tions­schlüssel"
-item_name[icd10]="ICD-10-Code"
-item_name[indicator_reading]="Befund Beschreibung"
-item_name[insurance]="Krankenkasse bzw. Kostenträger"
-item_name[prescription_by_catalogue]="Verordnung nach Maßgabe des Kataloges (Regelfall)"
-item_name[therapy_start]="Be­hand­lungs­be­ginn spä­test. am"
-item_name[quantity_weekly]="An­zahl pro Wo­che"
-item_name[save]="Speichern"
-item_name[cancel]="Abbrechen"
-item_name[delete]="Löschen"
-item_name[therapy]="Zur Therapie"
-
-item_name[doctor]="Arzt"
-item_name[dentist]="Zahn­arzt"
-item_name[altpractition]="Heil­prak­tiker"
-item_name[noprescription]="Ohne Ver­ord­nung"
-item_name[selfpaid]="Selbst­zah­lend"
-item_name[private]="Pri­vat"
-item_name[compulsory]="Ge­setz­lich"
-
-item_name[therapy_dates]="Be­hand­lungs­ter­mi­ne"
-
-item_name[issuer]="Ausgestellt durch"
-item_name[issuer_from_list]="Aus Liste"
-item_name[issuer_other]="Andere"
+++ /dev/null
-# Copyright 2016 Paul Hänsch
-#
-# This file is part of Confetti.
-#
-# Confetti is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# Confetti is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with Confetti. If not, see <http://www.gnu.org/licenses/>.
-
-. $_EXEC/templates/text_prescriptions.sh
-
-item_name[prescriptionlist]="Zur Verordnungsliste"
-item_name[delete_session]="Therapiesitzung entfernen"
-item_name[therapist]="Therapeut"
-item_name[number]="Nr."
-item_name[signature]="Un­ter­schrift"
-item_name[weekly]="p. Woche"
-item_name[notes]="Notizen"
-item_name[trailsave]="Speichern für weitere Felder"
+++ /dev/null
-# Copyright 2016, 2017 Paul Hänsch
-#
-# This file is part of Confetti.
-#
-# Confetti is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# Confetti is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with Confetti. If not, see <http://www.gnu.org/licenses/>.
-
-t_session_note(){
- session_n="$1"
- note_n="$2"
-
- color=session${session_n}_note${note_n}_color
- unset c0 c1 c2 c3 c4 c5 c6 c7
-
- if [ "$note_n" -eq 1 -o -n "${tpy[session${session_n}_note${note_n}]}" ]; then
- printf '<input class="trailbtn" type="checkbox" checked="checked">'
- else
- printf '<input class="trailbtn" type="checkbox">'
- fi
-
- _checked="$(validate "$tpy[$color]" '#(888|00A|0A0|0AA|A00|A0A|AA0)' '#FFF')"
- cat <<-EOF
- <fieldset class="note trailbox">
- <input class=color type=radio name="$color" value="#888" id=${color}_000 $(checked \#888)><label for=${color}_000></label>
- <input class=color type=radio name="$color" value="#00A" id=${color}_001 $(checked \#00A)><label for=${color}_001></label>
- <input class=color type=radio name="$color" value="#0A0" id=${color}_010 $(checked \#0A0)><label for=${color}_010></label>
- <input class=color type=radio name="$color" value="#0AA" id=${color}_011 $(checked \#0AA)><label for=${color}_011></label>
- <input class=color type=radio name="$color" value="#A00" id=${color}_100 $(checked \#A00)><label for=${color}_100></label>
- <input class=color type=radio name="$color" value="#A0A" id=${color}_101 $(checked \#A0A)><label for=${color}_101></label>
- <input class=color type=radio name="$color" value="#AA0" id=${color}_110 $(checked \#AA0)><label for=${color}_110></label>
- <input class=color type=radio name="$color" value="#FFF" id=${color}_111 $(checked \#FFF)><label for=${color}_111></label>
- <textarea name="session${session_n}_note${note_n}">${tpy[session${session_n}_note${note_n}]}</textarea>
- </fieldset>
- EOF
-}
-
-t_session(){
- session_n="$1"
-
- sid=session${session_n}
-
- if [ "$tpy[${sid}_sigset]" = pos ]; then
- sigcheck=checked
- else
- unset sigcheck
- fi
-
- cat <<-EOF
- <input type=hidden name=$sid value=exists>
- <input class="tab" type="checkbox" id=${sid}_open name="${sid}_open" value="checked" ${tpy[${sid}_open]}>
- <label class="tab" for="${sid}_open">
- <span class=no>${session_n}.</span><!--
- --><span class=date>${tpy[${sid}_date]}</span><!--
- --><input class="date" name="${sid}_date" value="${tpy[${sid}_date]}" placeholder="$(l10n date)"><!--
- --><span class=therapist>${tpy[${sid}_therapist]}</span><!--
- --><input class="therapist" name="${sid}_therapist" value="${tpy[${sid}_therapist]}" placeholder="$(l10n therapist)"><!--
- --><span class=signature><input type="checkbox" name="${sid}_sigset" value="pos" $sigcheck></span>
- </label>
- <div class=tab>
- <img class="dotmark ov" src="/therapies/${id%.tpy}_${sid}.png" alt="">
- $(n=1;
- while [ -n "${tpy[session${session_n}_note${n}]+x}" ]; do
- if [ -n "${tpy[session${session_n}_note${n}]}" ]; then
- x=$n
- fi
- n=$(($n + 1))
- done
- for n in $(seq 1 $((${x:-0} + 3)) ); do t_session_note $session_n $n; done
- )
- <button class="trailbtn" type="submit">$(l10n trailsave)</button>
- <button class=delete type=submit name=delete_session value=${session_n}>$(l10n delete_session)</button>
- </div>
- EOF
-}
-
-therapy_sessions(){
- n=1; while [ -n "${tpy[session${n}]}" ]; do
- t_session $n
- n=$(($n+1))
- done
-
- sid=session$n
-
- cat <<-EOF
- <fieldset class="tab">
- <button class=no type=submit name=new_session value=$sid>+</button><!--
- --><input class=date name=${sid}_date value="" placeholder="$(l10n date)" /><!--
- --><input class=therapist name=${sid}_therapist value="" placeholder="$(l10n therapist)" /><!--
- --><span class=signature><span></span>
- <input type=hidden name="${sid}_note1" value="">
- </fieldset>
- EOF
-}
-
-cat <<EOF
-<h1>$(l10n therapy)</h1>
-
-<div class="patient">
- <h2>$client_name</h2>
- <a href="?p=prescriptions&client=${id%%.*}.vcf">< $(l10n prescriptionlist)</a>
-</div>
-
-<div class="prescription">
- <h2>$(l10n therapy_prescription)</h2>
- <span class="insurance">${mpx[insurance]}</span>
- <span class="date"><label>$(l10n date):</label>${mpx[date]}</span>
-
- <label class="checkbox ${mpx[prescreviewed]:+checked}" for="prescreviewed">
- $([ -n "${mpx[prescreviewed]}" ] && printf %s "$(l10n prescreviewed)" \
- || printf %s "$(l10n prescreview)" )
- </label>
-
- ${mpx[prescno]:+<span class="prescno">$(l10n presc${mpx[prescno]})</span>}
- ${mpx[grouptherapy]:+<span class="catalogue">$(l10n grouptherapy)</span>}
- ${mpx[housecall]:+<span class="catalogue">$(l10n housecall)</span>}
- ${mpx[report]:+<span class="catalogue">$(l10n report)</span>}
-
- <ul>$(for n in '' {0..10}; do
- [ -n "${mpx[remidy$n]}" ] && \
- printf '<li>%s %s %s</li>' "${mpx[quantity$n]}" \
- "${mpx[remidy$n]}" \
- "${mpx[quantity_weekly$n]:+($mpx[quantity_weekly$n] $(l10n weekly))}"
- done)</ul>
-
- ${mpx[indicator]:+<span class="indicator"><label>$(l10n indicator):</label>${mpx[indicator]}</span>}
- ${mpx[icd10]:+<span class="icd10"> <label>$(l10n icd10):</label>${mpx[icd10]}</span>}
-
- ${mpx[addcontrib]:+
- <label class='checkbox ${mpx[contribconfirm]:+checked}' for="addcontrib">
- $([ -n "${mpx[contribconfirm]}" ] && printf %s "$(l10n contribconfirm)" ${mpx[contribconfirm]} \
- || printf %s "$(l10n addcontrib)" )
- </label>
- }
-
- <input class="tab" type="checkbox" id="indicator_reading">
- <label class="tab" for="indicator_reading">$(l10n indicator_reading)</label>
- <div class="tab">${mpx[indicator_reading]}</div>
-</div>
-
-<form id="report" method="POST" action="?action=update_therapy">
- <input type="hidden" name="id" value="${id}">
-
- <input class="stickynote" type="checkbox" name="c_stickynote" id="show_stickynote">
- <fieldset class="stickynote">
- <label for="show_stickynote">$(l10n notes)</label>
- <h2>$(l10n notes)</h2>
- <textarea name="stickynote">${tpy[stickynote]}</textarea>
- <button type="submit">$(l10n save)</button>
- </fieldset>
-
- <input class="stickynote" type="checkbox" name="c_timesheet" id="show_timesheet">
- <fieldset class="stickynote">
- <label for="show_timesheet">$(l10n timesheet)</label>
- <h2>$(l10n timesheet)</h2>
- <table><thead>
- <tr><th>$(l10n time_goal)</th><th>$(l10n time_actual)</th><th>$(l10n time_difference)</th></tr>
- </thead><tbody>
- $(for n in '' {0..10}; do
- printf '<tr><td><input type="number" name="tsgoal" value="%s"/></td>
- <td><input type="number" name="tsactual" value="%s"/></td>
- <td>%s</td>
- </tr>\n' \
- "$mpx[tsgoal${n}]" "$mpx[tsactual${n}]" "$((${mpx[tsgoal${n}]:-0} - ${mpx[tsactual${n}]:-0}))"
- done)
- </tbody></table>
- <button type="submit">$(l10n save)</button>
- </fieldset>
-
- <!--input class="tab" type="checkbox" id="lookout">
- <label class="tab" for="lookout">$(l10n indicator_reading)</label>
- <div class="tab"><textarea name="lookout">${mpx[lookout]}</textarea -->
-
- <label class="tab heading">
- <span class=no>$(l10n number)</span><!--
- --><span class=date>$(l10n date)</span><!--
- --><span class=therapist>$(l10n therapist)</span><!--
- --><span class=signature>$(l10n signature)</span>
- </label>
-EOF
-
-therapy_sessions
-
-_checked="$(validate "$tpy[penwidth]" '(4|12|36)' '4')"
-cat <<EOF
- <fieldset class="penwidth">
- <input type="radio" name="penwidth" value="4" id="pw1" $(checked 4)><label for="pw2"></label>
- <input type="radio" name="penwidth" value="12" id="pw2" $(checked 12)><label for="pw3"></label>
- <input type="radio" name="penwidth" value="36" id="pw3" $(checked 36)><label for="pw1"></label>
- </fieldset>
-EOF
-
-_checked="$(validate "$tpy[color]" '#(0[0A]{2}|A00|A0A|AA0|FFF)' '#000')"
-cat <<EOF
- <fieldset class="color">
- <input class="color" type="radio" name="color" value="#000" id="c000" $(checked \#000)><label for="c000"></label>
- <input class="color" type="radio" name="color" value="#00A" id="c001" $(checked \#00A)><label for="c001"></label>
- <input class="color" type="radio" name="color" value="#0A0" id="c010" $(checked \#0A0)><label for="c010"></label>
- <input class="color" type="radio" name="color" value="#0AA" id="c011" $(checked \#0AA)><label for="c011"></label>
- <input class="color" type="radio" name="color" value="#A00" id="c100" $(checked \#A00)><label for="c100"></label>
- <input class="color" type="radio" name="color" value="#A0A" id="c101" $(checked \#A0A)><label for="c101"></label>
- <input class="color" type="radio" name="color" value="#AA0" id="c110" $(checked \#AA0)><label for="c110"></label>
- <input class="color" type="radio" name="color" value="#FFF" id="c111" $(checked \#FFF)><label for="c111"></label>
- </fieldset>
- <img class="dotmark bg" src="?static=therapy_background.png" alt="WARNING: Background Image not available!">
- <canvas id="canvas" class="dotmark ov" width="${bg_dim%x*}" height="${bg_dim#*x}"></canvas>
-
- <input type=hidden id=image_serialize name=imagedata value="">
-
- <button type="submit">$(l10n save)</button>
-</form>
-
-<span id="jsdebug" style="display: none; position: fixed; right:0; bottom:0">Debug</span>
-
-<script type="text/javascript" src="?static=therapy_draw.js"></script>
-EOF
-
-# vi:set filetype=html:
+++ /dev/null
-# Copyright 2014 - 2017 Paul Hänsch
-#
-# This file is part of Confetti.
-#
-# Confetti is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# Confetti is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with Confetti. If not, see <http://www.gnu.org/licenses/>.
-
-list_items(){
- item="$1"
- [ -n "${values[$item]+x}" ] && \
- printf '<h3>%s</h3>\n' "$(l10n $item)"
- for n in "$item" "$item"{0..100}; do
- if [ -z "${values[$n]+x}" ]; then
- break
- else case "$item" in
- EMAIL)
- printf '<a class="item EMAIL" href="mailto:%s">%s</a>\n' \
- "$(attribsafe "${values[$n]}")" "$(htmlsafe "${values[$n]}")"
- ;;
- *)
- printf '<span class="item %s">%s</span>\n' \
- "$item" "$(htmlsafe ${values[$n]})"
- ;;
- esac; fi
- done
-}
-
-list_section(){
- printf '<div class="section %s">' "$1"
- shift 1
- for each in $@; do
- list_items "$each"
- done
- printf '</div>'
-}
-
-n=$(printf %s "$values[N+3] $values[N+1] $values[N+2] $values[N+0] $values[N+4]" \
- | sed -r ':X;$!{N;bX}; s;^[\n ]+;;; s;[\n ]+$;;; s;[\r\t\n ]+; ;g;'
- )
-fullname="${n:-${values[FN]:-${values[NICKNAME]}}}"
-
-hi_company="${values[X-HEALTH-INSURANCE+0]}"
- hi_number="${values[X-HEALTH-INSURANCE+1]}"
- hi_status="${values[X-HEALTH-INSURANCE+2]}"
-
-printf '<div class="section basic">
- <h2 class="item FN">%s</h2>
-' "$fullname"
-[ -n "$values[GENDER]" ] && printf '
- <span class="item GENDER">%s</span>
- ' "$(l10n "$values[GENDER]")"
-
-for n in NICKNAME NICKNAME{0..100}; do
- [ -z "${values[$n]+x}" ] && break \
- || printf '
- <span class="item NICKNAME">aka. %s</span>
- ' "$(htmlsafe ${values[$n]})"
-done
-
-[ -n "$values[BDAY]" ] && printf '
- <span class="item BDAY"><b>*:</b> %s</span>
- ' "$(htmlsafe "$values[BDAY]")"
-[ -n "$values[X-ZACK-JOINDATE]" ] && printf '
- <span class="item X-ZACK-JOINDATE"><b>%s:</b> %s</span>
- ' "$(l10n label_join)" "$(htmlsafe "$values[X-ZACK-JOINDATE]")"
-[ -n "$values[X-ZACK-LEAVEDATE]" ] && printf '
- <span class="item X-ZACK-LEAVEDATE"><b>%s:</b >%s</span>
- ' "$(l10n label_leave)" "$(htmlsafe "$values[X-ZACK-LEAVEDATE]")"
-
-[ -n "$values[SOUND]" ] && printf '
- <audio controls="controls" class="item SOUND">
- <source type="audio/ogg" src="data:audio/ogg;base64,%s" />
- </audio>' "$values[SOUND]"
-
-[ -n "$values[PHOTO]" ] && printf '
- <img class="item PHOTO" src="data:image/%s;base64,%s" />
- ' "${values[PHOTO_TYPE]}" "${values[PHOTO]}"
-
-[ -n "$values[LOGO]" ] && printf '
- <img class="item PHOTO" src="data:image/%s;base64,%s" />
- ' "${values[LOGO_TYPE]}" "${values[LOGO]}"
-
-if [ "$PROFILE" = circus ]; then
- printf '</div>'
-
- list_section phone TEL
- list_section message EMAIL IMPP URL
- list_section address ADR
- list_section note NOTE
-
- printf '<div class="section attendance"><h3>%s</h3><ul>' "$(l10n course_attendance)"
- sed -rn 's:(.*)\t'"$id"'$:\1:p' "$_DATA/mappings/attendance" |while read each; do
- cname="$(sed -rn 's:^SUMMARY\:(.*)$:\1:p' "$_DATA/ical/$each")"
- printf ' <li><a class="item attendance" href="?p=courses#%s">%s</a></li>' "$each" "$(htmlsafe $cname)"
- done
- printf '</ul>'
- list_items CATEGORIES
- printf '</div>'
-
-elif [ "$PROFILE" = medical ]; then
- list_items ADR
- list_items URL
- printf '</div>'
-
- list_section phone TEL EMAIL IMPP
-
- printf '<div class="section insurance"><h3>%s</h3>' "$(l10n X-HEALTH-INSURANCE)"
- [ -n "$hi_company" ] && printf '<span class="item hi_comapany">%s</span>' \
- "$(htmlsafe "$hi_company")"
- [ -n "$hi_number" ] && printf '<span class="item hi_number"><label>%s:</label> %s</span>' \
- "$(l10n hi_number)" "$(htmlsafe "$hi_number")"
- [ -n "$hi_status" ] && printf '<span class="item hi_status"><label>%s:</label> %s</span>' \
- "$(l10n hi_status)" "$(htmlsafe "$hi_status")"
- printf '</div>'
-
- list_section note NOTE X-CLIENT-REFERRAL
-
- printf '<div class="section prescriptions"><h3>%s</h3><ul>' "$(l10n prescriptions)"
- declare -A mpx
- find "$_DATA/prescriptions/" -name "${id%.vcf}.*.mpx" \
- | while read pfile; do
- mpx=(); cat "$pfile" |while read -r line; do
- val="${line#*:}"
- mpx[${line%%:*}]="$(htmlsafe "${val//\\n/$BR}")"
- done
- printf '<li><a href="?p=prescriptions&client=%s#%s" >%s: %s - %s</a></li>' \
- "${id}" "${pfile##*/}" "${mpx[date]}" "${mpx[indicator]}" \
- "$([ -n "${mpx[remidy]}" ] && printf '%s %s' "${mpx[quantity]}" "${mpx[remidy]}"
- for n in {0..10}; do
- [ -n "${mpx[remidy${n}]}" ] && printf ', %s %s' "${mpx[quantity${n}]}" "${mpx[remidy${n}]}"
- done
- )"
- done |sort -r
- printf '</ul></div>'
-fi
+++ /dev/null
-# Copyright 2014 Paul Hänsch
-#
-# This file is part of Confetti.
-#
-# Confetti is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# Confetti is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with Confetti. If not, see <http://www.gnu.org/licenses/>.
-
-dtstart="$values[DTSTART]"
-[ -z "$dtstart" ] && dtstart=$(date +%Y%m%dT%H%M%S)
-
-echo "$dtstart" |case "$dtstart" in
- *Z)
- sed -rn 's:^([0-9]{4})([0-9]{2})([0-9]{2})T([0-9]{2})([0-9]{2})([0-9]{2})Z$:\1-\2-\3 \4\:\5\:\6 UTC:p'
- ;;
- TZID*)
- sed -rn 's:^TZID=(.+)\:([0-9]{4})([0-9]{2})([0-9]{2})T([0-9]{2})([0-9]{2})([0-9]{2})$:TZ="\1" \2-\3-\4 \5\:\6\:\7:p'
- ;;
- *)
- sed -rn 's:^([0-9]{4})([0-9]{2})([0-9]{2})T([0-9]{2})([0-9]{2})([0-9]{2})$:\1-\2-\3 \4\:\5\:\6:p'
- ;;
-esac |read dts_date
-
-dts_year=$(date -d "$dts_date" +%Y)
-dts_month=$(date -d "$dts_date" +%m)
-dts_dom=$(date -d "$dts_date" +%d)
-dts_dow=$(date -d "$dts_date" +%u)
-dts_weekday=$(date -d "$dts_date" +%A)
-dts_hour=$(date -d "$dts_date" +%H)
-dts_min=$(date -d "$dts_date" +%M)
-dts_sec=$(date -d "$dts_date" +%S)
-
-rrule="$values[RRULE]"
-rr_int="$(echo $rrule |sed -rn 's:^.*INTERVAL=([0-9]+)(;.*)?$:\1:p')"
-rr_count="$(echo $rrule |sed -rn 's:^.*COUNT=([0-9]+)(;.*)?$:\1:p')"
-rr_freq="$(echo $rrule |sed -rn 's:^.*FREQ=(YEARLY|MONTHLY|WEEKLY|DAILY)(;.*)?$:\1:p')"
-rr_uyear="$(echo $rrule |sed -rn 's:^.*UNTIL=([0-9]{4})([0-9]{2})([0-9]{2})T[0-9]{6}Z(;.*)?$:\1:p')"
-rr_umonth="$(echo $rrule |sed -rn 's:^.*UNTIL=([0-9]{4})([0-9]{2})([0-9]{2})T[0-9]{6}Z(;.*)?$:\2:p')"
-rr_uday="$(echo $rrule |sed -rn 's:^.*UNTIL=([0-9]{4})([0-9]{2})([0-9]{2})T[0-9]{6}Z(;.*)?$:\3:p')"
-rr_udate="$(echo $rrule |sed -rn 's:^.*UNTIL=([0-9]{4})([0-9]{2})([0-9]{2})T([0-9]{2})([0-9]{2})([0-9]{2})Z(;.*)?$:\1-\2-\3 \4\:\5\:\6:p')"
-if [ -n "$rr_count" ]; then
- rr_limit=COUNT
-elif [ -n "$rr_uyear" ]; then
- rr_limit=UNTIL
-else
- rr_limit=ETERN
-fi
-
-coursemail=""
-
-echo '<div id="'${id}'" class="course">'
-echo '<form action="?action=update_course" method="POST">'
-echo '<input type="hidden" name="course" value="'${id}'"/>'
-echo ' <div class="section basic">'
-echo ' <h2>'$values[SUMMARY]'</h2>'
-echo ' <span class="text">'$(date -d "$dts_date" "+%A %B %d, %Y - %H:%M") $(l10n t_oclock)'</span>'
-[ "$rr_int" -eq 1 ] \
-&& echo '<span class="text">'$(l10n s$rr_freq)'</span>' \
-|| echo '<span class="text">'$(l10n t_every) ${rr_int} $(l10n $rr_freq)'</span>'
-case "$rr_limit" in
- COUNT)
- m1=$(echo $rr_freq |sed -r "s:DAILY:day:g;s:WEEKLY:week:g;s:MONTHLY:month:g;s:YEARLY:year:g;")
- m2=$(($rr_int * $rr_count))
- echo '<span class="text">'$(l10n t_until) $(date -d "$dts_date + $m2 $m1" "+%A %B %d, %Y - %H:%M")'</span>'
- ;;
- UNTIL)
- echo '<span class="text">'$(l10n t_until) $(date -d "$rr_udate" "+%A %B %d, %Y - %H:%M")'</span>'
- ;;
-esac
-echo ' </div>'
-
-echo ' <div class="section COMMENT">'
-echo ' <h3>'$(l10n COMMENT)'</h3>'
-for n in COMMENT COMMENT{0..10}; do [ -n "$values[$n]" ] &&\
- echo ' <p class="item COMMENT">'$(echo "$values[$n]" |sed -r "s:$:<br/>:g" )'</p>'
-done
-echo ' </div>'
-
-echo ' <div class="section attendance">'
-echo ' <h3>'$(l10n course_attendance)'</h3>'
-list_attendance "$id" |sort -k 2 |sed -r 's:^([^ ]+) (.*)$:<a class="attendance" href="?p=cards#\1">\2</a>:'
-echo ' </div>'
-
-echo ' <div class="control">'
-echo ' <a class="item" href="?action=edit_course&course='${id}'">'$(l10n edit)'</a>'
-echo ' <a class="item" href="?action=generate_courselist&course='${id}'" target="_blank">'$(l10n courselist)'</a>'
-echo ' <a class="item" href="?action=export_ical&course='${id}'">'$(l10n ics_export)'</a>'
-echo ' <a class="item" href="mailto:zack@vuesch.org?bcc='$(course_mail "$id")'">'$(l10n course_mail)'</a>'
-echo ' </div>'
-echo '<input type="hidden" name="UID" value="'$values["UID"]'"/>'
-echo '</form>'
-echo '</div>'
-
+++ /dev/null
-# Copyright 2016 Paul Hänsch
-#
-# This file is part of Confetti.
-#
-# Confetti is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# Confetti is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with Confetti. If not, see <http://www.gnu.org/licenses/>.
-
-check(){ [ "$1" = "$2" ] && printf checked}
-
-cat <<END_HTML
-<div class="prescription ${mpx[presctype]%_*} ${mpx[presctype]#*_}">
-<fieldset class="baseinfo">
-<label for="insurance">$(l10n insurance)</label>
-<span id="insurance">${mpx[insurance]}</span>
-<br>
-<label for="name">$(l10n name)</label>
-<label for="bday">$(l10n bday)</label>
-<span id="name">${mpx[name]}</span>
-<span id="bday">${mpx[bday]}</span>
-
-<br>
-<label for="date">$(l10n date)</label>
-<span id="date">${mpx[date]}</span>
-</fieldset>
-
-<fieldset class="misc">
-<h1 id="${mpx[prescription]}">$(l10n therapy_prescription)</h1>
-<br>
-<label for="addcontrib">$(l10n addcontrib)</label>
-<span id="addcontrib">${mpx[addcontrib]}</span>
-<label for="contribconfirm">$(l10n contribconfirm)</label>
-<span id="contribconfirm">${mpx[contribconfirm]}</span>
-<label class="checkbox ${mpx[contribreceipt]:+checked}" for="contribreceipt">$(l10n contribreceipt)</label>
-</fieldset>
-
-<label class="checkbox ${mpx[prescreviewed]:+checked}" for="prescreviewed">
- $([ -n "${mpx[prescreviewed]}" ] && printf %s "$(l10n prescreviewed)" \
- || printf %s "$(l10n prescreview)" )
-</label>
-
-<fieldset class="catalogue">
-<h2>$(l10n prescription_by_catalogue)</h2>
-<label class="radio $(check "$mpx[prescno]" first)" for="prescfirst">$(l10n prescfirst)</label>
-<br>
-<label class="radio $(check "$mpx[prescno]" follow1)" for="prescfollow1">$(l10n prescfollow1)</label>
-<br>
-<label class="radio $(check "$mpx[prescno]" follow2)" for="prescfollow2">$(l10n prescfollow2)</label>
-<br>
-<label class="radio $(check "$mpx[prescno]" other)" for="prescother">$(l10n prescother)</label>
-<br>
-<label class="radio $(check "$mpx[prescno]" continual)" for="presccontinual">$(l10n presccontinual)</label>
-
-<br>
-<label class="checkbox ${mpx[grouptherapy]:+checked}" for="grouptherapy">$(l10n grouptherapy)</label>
-<br>
-<label class="checkbox ${mpx[housecall]:+checked}" for="housecall">$(l10n housecall)</label>
-<br>
-<label class="checkbox ${mpx[report]:+checked}" for="report">$(l10n report)</label>
-</fieldset>
-
-<fieldset class="description">
- <label for="quantity">$(l10n quantity)</label>
- <label for="remidy">$(l10n remidy)</label>
- <label for="quantity_weekly">$(l10n quantity_weekly)</label>
- <span id="quantity">${mpx[quantity]}</span>
- <span id="remidy">${mpx[remidy]}</span>
- <span id="quantity_weekly">${mpx[quantity_weekly]}</span>
-$( for n in {0..10}; do
- [ -n "${mpx[quantity$n]}" -o -n "${mpx[remidy$n]}" -o -n "${mpx[quantity_weekly$n]}" ] \
- && printf '
- <span id="quantity">%s</span>
- <span id="remidy">%s</span>
- <span id="quantity_weekly">%s</span>
- ' "${mpx[quantity$n]}" "${mpx[remidy$n]}" "${mpx[quantity_weekly$n]}"
-done )
-<br>
-<p class="indicator_codes">
- <label for="indicator">$(l10n indicator)</label>
- <span id="indicator">${mpx[indicator]}</span>
- <br>
- <label for="icd10">$(l10n icd10)</label>
- <span id="icd10">${mpx[icd10]}</span>
-</p>
-<br>
-<p class="indicator_reading">
- <label for="indicator_reading">$(l10n indicator_reading)</label>
- <span id="indicator_reading">${mpx[indicator_reading]}</span>
-</p>
-</fieldset>
-
-<p class="therapy_dates">
- <label>$(l10n therapy_dates):</label>
- $(therapy_dates "$id" \
- | while read date; do
- printf '<span>%s</span>' "$date"
- done
- )
-</p>
-
-<p class="issuer">
- <label>$(l10n issuer)</label>
- <span id="issuer">$mpx[issuer]</span>
-</p>
-
-<fieldset class="controls">
-<a class="button" href="?p=prescriptions&edit=${mpx[prescription]}#${mpx[prescription]}">$(l10n edit)</a>
-<a class="button" href="?p=therapy&id=${mpx[prescription]%.mpx}.tpy">$(l10n therapy)</a>
-</fieldset>
-
-</div>
-END_HTML