unification of card editing functions
[confetti] / pages / cards.sh
1 #!/bin/zsh
2
3 # Copyright 2014 - 2016 Paul Hänsch
4 #
5 # This file is part of Confetti.
6
7 # Confetti is free software: you can redistribute it and/or modify
8 # it under the terms of the GNU Affero General Public License as published by
9 # the Free Software Foundation, either version 3 of the License, or
10 # (at your option) any later version.
11
12 # Confetti is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 # GNU Affero General Public License for more details.
16
17 # You should have received a copy of the GNU Affero General Public License
18 # along with Confetti.  If not, see <http://www.gnu.org/licenses/>. 
19
20 BR='
21 '
22
23 force_items(){
24   for each in "$@"; do
25     [ -z "${values[$each]+x}" ] && values[${each}]=''
26   done
27 }
28
29 case $PROFILE in
30 medical)
31   SUP_FIELDS=(N NICKNAME GENDER BDAY ADR TEL EMAIL X-HEALTH-INSURANCE X-HEALTH-INSURANCE-NOCONTRIB IMPP URL NOTE X-CLIENT-REFERRAL)
32   FORCE_ITEMS=(ADR TEL EMAIL NOTE X-CLIENT-REFERRAL)
33   view_card="$_EXEC/templates/view_client.sh"
34   edit_card="$_EXEC/templates/edit_client.sh"
35   _GET[order]="${_GET[order]:-lastname}"
36   _GET[filtertype]="${_GET[filtertype]:-name}"
37   profile_medical=x
38 ;;
39 circus)
40   SUP_FIELDS=(N NICKNAME GENDER BDAY X-ZACK-JOINDATE X-ZACK-LEAVEDATE EMAIL TEL IMPP ADR URL NOTE)
41   FORCE_ITEMS=(BDAY X-ZACK-JOINDATE TEL EMAIL ADR NOTE)
42   view_card="$_EXEC/templates/view_attendee.sh"
43   edit_card="$_EXEC/templates/edit_attendee.sh"
44   _GET[order]="${_GET[order]:-firstname}"
45   _GET[filtertype]="${_GET[filtertype]:-any}"
46   profile_circus=x
47 ;;
48 esac
49
50 edit="${_GET[edit]}"
51 [ \! -f "vcard/$edit" -a \! -f "temp/$edit" ] && edit=''
52
53 listcourses() {
54   ls -1 ${_DATA}/ical/*ics |while read file; do
55     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")"
56     echo "$(date -d "$icstime" "+%u %H%M%S")\t$file"
57   done |sort |sed -r 's:^.*\t(.*/)([^/]+)$:\2:'
58 }
59
60 list_hi_companies(){
61   sed -rn 's;^X-HEALTH-INSURANCE:([^\;]+)\;.*$;\1;p' ${_DATA}/vcard/*vcf \
62   | sort -u
63 }
64
65 listcards() {
66   case "${_GET[filtertype]}" in
67     any)
68        grep -il "${_GET[filter]}" ${_DATA}/vcard/*vcf
69       ;;
70     name)
71        egrep -xil "(FN|NICKNAME|N)(;.+)*:.*${_GET[filter]}.*" ${_DATA}/vcard/*vcf
72       ;;
73     street)
74        egrep -xil "(ADR)(;.+)*:([^;]*;){2}${_GET[filter]}.*" ${_DATA}/vcard/*vcf
75       ;;
76     zip)
77        egrep -xil "(ADR)(;.+)*:([^;]*;){5}${_GET[filter]}.*" ${_DATA}/vcard/*vcf
78       ;;
79     telephone)
80        egrep -xil "(TEL)(;.+)*:.*${_GET[filter]}.*" ${_DATA}/vcard/*vcf
81       ;;
82     birth)
83        egrep -xil "(BDAY)(;.+)*:${_GET[filter]}.*" ${_DATA}/vcard/*vcf
84       ;;
85     course)
86       ;;
87     *) ls -1 ${_DATA}/vcard/*vcf 2>/dev/null
88       ;;
89   esac |case "${_GET[order]}" in
90     firstname)
91       while read file; do
92         fn=$(sed -rn 's:^N(;.+)*\:([^;]*;){1} *([^;]*).*$:\3:p' "$file")
93         echo "$fn\t$file"
94       done
95       ;;
96     lastname)
97       while read file; do
98         ln=$(sed -rn 's:^N(;.+)*\:([^;]*;){0} *([^;]*).*$:\3:p' "$file")
99         echo "$ln\t$file"
100       done
101       ;;
102     bdate)
103       while read file; do
104         bd=$(sed -rn 's:^BDAY(;.+)*\:(.*)$:\2:p' "$file")
105         echo "$bd\t$file"
106       done
107       ;;
108     *)
109       sed -r 's:^.*$:x\t&:'
110       ;;
111   esac |sort |sed -r 's:^.*\t(.*/)([^/]+)$:\2:'
112 }
113
114 vcf_parse() {
115   unset key
116   sed -r ':X;N;$!bX; s;\r\n[ \t];;g; s;\r\n;\n;g;' "$1" \
117   | sed -rn '
118     # === turn property names to upper case, strip group names ===
119     h; s;^([^;:]+);;;
120     x; s;^([^;:\.]+\.)?([^;:]+).*$;\2;;
121     y;abcdefghijklmnopqrstuvwxyz;ABCDEFGHIJKLMNOPQRSTUVWXYZ;
122     G; s;\n;;;
123
124     # === strip trailing CR (but keep CRs in property value) ===
125     s;\r$;;;
126
127     # === Normalise various known vendor properties ===
128                 s;^X-MS-CARDPICTURE(\;|:);PHOTO\1;;
129                         s;^X-GENDER(\;|:);GENDER\1;;
130                    s;^X-ANNIVERSARY(\;|:);ANNIVERSARY\1;;
131          s;^X-EVOLUTION-ANNIVERSARY(\;|:);ANNIVERSARY\1;;
132     s;^X-KADDRESSBOOK-X-ANNIVERSARY(\;|:);ANNIVERSARY\1;;
133             s;^X-EVOLUTION-BLOG-URL(\;|:);URL\1;;
134                               s;^AGENT(\;|:);RELATED\;VALUE=text\;TYPE=agent\1;;
135                         s;^X-ASSISTANT(\;|:);RELATED\;VALUE=text\;TYPE=assistant\1;;
136               s;^X-EVOLUTION-ASSISTANT(\;|:);RELATED\;VALUE=text\;TYPE=assistant\1;;
137     s;^X-KADDRESSBOOK-X-ASSISTANTSNAME(\;|:);RELATED\;VALUE=text\;TYPE=assistant\1;;
138                           s;^X-MANAGER(\;|:);RELATED\;VALUE=text\;TYPE=manager\1;;
139                 s;^X-EVOLUTION-MANAGER(\;|:);RELATED\;VALUE=text\;TYPE=manager\1;;
140       s;^X-KADDRESSBOOK-X-MANAGERSNAME(\;|:);RELATED\;VALUE=text\;TYPE=manager\1;;
141                            s;^X-SPOUSE(\;|:);RELATED\;VALUE=text\;TYPE=spouse\1;;
142                  s;^X-EVOLUTION-SPOUSE(\;|:);RELATED\;VALUE=text\;TYPE=spouse\1;;
143         s;^X-KADDRESSBOOK-X-SPOUSENAME(\;|:);RELATED\;VALUE=text\;TYPE=spouse\1;;
144
145     # === Normalise obsolete vendor IM properties ===
146             s;^X-AIM((\;[a-Z0-9-]+|\;[a-Z0-9-]+=([^;,:"]+|"[^"]+")(,[^;,:"]+|,"[^"]+")*)*):;IMPP\1:aim:;;
147             s;^X-ICQ((\;[a-Z0-9-]+|\;[a-Z0-9-]+=([^;,:"]+|"[^"]+")(,[^;,:"]+|,"[^"]+")*)*):;IMPP\1:aim:;;
148     s;^X-GOOGLE-TALK((\;[a-Z0-9-]+|\;[a-Z0-9-]+=([^;,:"]+|"[^"]+")(,[^;,:"]+|,"[^"]+")*)*):;IMPP\1:xmpp:;;
149          s;^X-JABBER((\;[a-Z0-9-]+|\;[a-Z0-9-]+=([^;,:"]+|"[^"]+")(,[^;,:"]+|,"[^"]+")*)*):;IMPP\1:xmpp:;;
150             s;^X-MSN((\;[a-Z0-9-]+|\;[a-Z0-9-]+=([^;,:"]+|"[^"]+")(,[^;,:"]+|,"[^"]+")*)*):;IMPP\1:msn:;;
151           s;^X-YAHOO((\;[a-Z0-9-]+|\;[a-Z0-9-]+=([^;,:"]+|"[^"]+")(,[^;,:"]+|,"[^"]+")*)*):;IMPP\1:ymsgr:;;
152             s;^X-SIP((\;[a-Z0-9-]+|\;[a-Z0-9-]+=([^;,:"]+|"[^"]+")(,[^;,:"]+|,"[^"]+")*)*):(sip:)?;IMPP\1:sip:;;
153
154     # === Update obsolete LABEL property ===
155     s;^LABEL((\;[a-Z0-9-]+|\;[a-Z0-9-]+=([^;,:"]+|"[^"]+")(,[^;,:"]+|,"[^"]+")*)*):(.*)$;ADR\1\;LABEL="\5":;;
156
157     /^([A-Z0-9-]+)((\;[a-Z0-9-]+=?|\;[a-Z0-9-]+=([^;,:"]+|"[^"]+")(,[^;,:"]+|,"[^"]+")*)*):(.*)$/{
158       h;
159       s;^([A-Z0-9-]+)((\;[a-Z0-9-]+=?|\;[a-Z0-9-]+=([^;,:"]+|"[^"]+")(,[^;,:"]+|,"[^"]+")*)*):(.*)$;key='\''\1'\'';;
160       H; g;
161
162       s;^([A-Z0-9-]+)((\;[a-Z0-9-]+=?|\;[a-Z0-9-]+=([^;,:"]+|"[^"]+")(,[^;,:"]+|,"[^"]+")*)*):([^\n]*)\n.*$;\2;;
163       s;'\'';'\'\\\\\'\'';g
164       s;\;([A-Z0-9-]+)(=|=(([^\;,:"]+|"[^"]+")(,[^\;,:"]+|,"[^"]+")*))?;\ntag[\1]='\''\3'\'';g
165       s;^\n+;;; s;\n+$;;;
166       /^.+$/H; g;
167
168       s;^([A-Z0-9-]+)((\;[a-Z0-9-]+=?|\;[a-Z0-9-]+=([^;,:"]+|"[^"]+")(,[^;,:"]+|,"[^"]+")*)*):([^\n]*)\n.*$;\6;;
169       # :X s;((^|[^\\])(\\\\)*)[;,];\1\n;g; s;((^|[^\\])(\\\\)*)\\([;,]);\4;g; tX;
170       s;'\'';'\'\\\\\'\'';g
171       s;^.*$;value='\''&'\'';
172       H; g;
173
174       s;^[^\n]*[\n ]+;;
175       s;\n+$;;;
176
177       p;
178     }
179     ' \
180   | while read -r line; do
181     declare -A tag
182     case "$line" in
183       value*) eval "$line";;
184       tag*)   eval "$line";;
185       key*)
186         if [ -z "$key" ]; then
187           eval "$line"
188         else
189           printf '%s\n' "$value" |sed -rn '
190             :X
191             s;((^|[^\\])(\\\\)*),;\1\n;g; 
192             tX;
193             s;\\,;,;g;
194             p
195           ' \
196           | while read -r val; do
197             while [ -n "${values[$key$n]+x}" ]; do n=$((${n=-1} + 1)); done
198             if printf '%s\n' "$val" |grep -qE '((^|[^\\])(\\\\)*)\;'; then
199               m=0
200               values[${key}${n}]="${val}"
201               printf '%s\n' "$val" |sed -rn '
202                 :X
203                 s;((^|[^\\])(\\\\)*)\;;\1\n;g; 
204                 tX;
205                 s;\\\;;\;;g;
206                 p
207               ' \
208               | while read -r v; do
209                 values[${key}${n}+${m}]="$(
210                   printf %s\\n "${v}" \
211                   | sed -rn '
212                     :X
213                     s;((^|[^\\])(\\\\)*)\\n;\1\n;g; 
214                     tX;
215                     s;\\\\;\\;g;
216                     p
217                   '
218                 )"
219                 m=$(($m + 1))
220               done
221             else
222               values[${key}${n}]="$(
223                 printf %s\\n "${val}" \
224                 | sed -rn '
225                   :X
226                   s;((^|[^\\])(\\\\)*)\\n;\1\n;g; 
227                   tX;
228                   s;\\\;;\;;g;
229                   s;\\\\;\\;g;
230                   p
231                 '
232               )"
233             fi
234             for t in ${(k)tag}; do
235               values[${key}${n}_${t}]="${tag[$t]}"
236             done
237           done
238
239           eval "$line"
240           unset n
241           while [ -n "${values[$key$n]+x}" ]; do n=$((${n=-1} + 1)); done
242           unset value
243           unset tag
244         fi
245       ;;
246     esac
247   done
248 }
249
250 view_card() {  #Parameter: Cardfile
251   id="$1"
252   cardfile="$_DATA/vcard/${id}"
253   cachefile="$_DATA/cache/${id}.cache"
254   if [ "$cachefile" -nt "$cardfile" ]; then
255     cat "$cachefile"
256   else
257     declare -A values
258     vcf_parse "$cardfile"
259     . $view_card |tee "$cachefile"
260   fi
261 }
262
263 edit_card() {  #Parameter: Cardfile
264   id="$1"
265   cardfile="$_DATA/vcard/$id"
266   tempfile="$_DATA/temp/$id"
267   [ -f "$tempfile" ] && cardfile="$tempfile"
268
269   declare -A values
270   vcf_parse "$cardfile"
271   force_items $FORCE_ITEMS
272   . $edit_card
273 }