26 function TS
{ echo $
(( (`date +%m%S%N`-$TS0)/1000000 )) $@
; } #date +%H:%m:%S.%N; }
28 BASEDIR
=~
/doc
/org
/gtd
/own
/
29 TODO
=$BASEDIR/todo.txt
30 TMPFILE
=/tmp
/gtd.tmp.
`cat /dev/urandom | tr -cd a-z0-9 | head -c 5`
31 STATES
=""; PROJECTS
=""; CONTEXTS
=""; KEYS
="";
32 TIMESTAMP_DEFAULT
=99999999 #9999
33 TIMESTAMP_NOW
=`date +%Y%m%d` #%H%m`
39 #ASCII <-> CGI encoded
41 [[ $SHELL_DEBUG == 1 ]] && TS ascii
- cgi
102 [[ $SHELL_DEBUG == 1 ]] && TS todo
file handling
103 function MK_IDX
{ awk '{printf("I:%.3d ",NR);print$0;}'; }
104 function RM_IDX
{ sed 's/I:[0-9]* //g'; }
105 function CK_IDX
{ echo $1 |
awk '{printf("%.3d",$1)}'; }
106 function SHOWENTRY
{ cat $TODO | MK_IDX |
grep "^I:$1" | RM_IDX
; }
107 function RM_ENTRY
{ cat $TODO | MK_IDX |
grep -v "^I:$1" | RM_IDX
; }
109 function COMPLETE_TIMESTAMP
{
111 length($0)==4{print"'`date +%Y`'"$0;exit;}
112 length($0)==2{print"'`date +%Y%m`'"$0;exit;}
113 length($0)==7{yearmonth=$0;day=$0;gsub(".$","",yearmonth);gsub("^......","",day);print yearmonth"0"day}
114 length($0)==8{print;exit;}
121 if [[ $TAG == INDEX
]]; then
122 echo "$@" |
sed 's/&/ /g;s/%/_percent_/g' |
awk '{for(i=1;i<=NF;i++){if($i~/'$TAG'/){gsub("'$TAG'=","",$i);printf $i" "}}}' |
sed 's/ $//;s/_percent_/%/g;'
123 elif [[ $TAG == projects
]]; then
124 echo "$@" |
sed 's/&/ /g;s/%/_percent_/g' |
awk '{for(i=1;i<=NF;i++){if($i~/'$TAG'/){gsub("'$TAG'=","",$i);printf $i" "}}}' |
sed 's/ $//;s/_percent_/%/g;'
125 elif [[ $TAG == contexts
]]; then
126 echo "$@" |
sed 's/&/ /g;s/%/_percent_/g' |
awk '{for(i=1;i<=NF;i++){if($i~/'$TAG'/){gsub("'$TAG'=","",$i);printf $i" "}}}' |
sed 's/ $//;s/_percent_/%/g;'
128 echo "$@" |
grep "$TAG" |
sed 's/\&/ /g' |
grep "$TAG" |
sed -r -e 's/\&/ /g;s/.*'$TAG'=([^ ]*).*/\1/';
134 #General GTD Operations
136 [[ $SHELL_DEBUG == 1 ]] && TS general gtd operations
138 [[ -z $STATES ]] && local STATES
=aw
139 [[ -z $IMPORTANCE ]] && local IMPORTANCE
=0
140 [[ -z $TIMESTAMP ]] && local TIMESTAMP
=$TIMESTAMP_DEFAULT
141 REGEX_PROJ
="`echo "($PROJECTS)" | sed 's/ /|/g;s/|)/)/;'`";
142 REGEX_CONT
="`echo "($CONTEXTS)" | sed 's/ /|/g;s/|)/)/;'`";
144 cat $TODO | MK_IDX |
egrep "$REGEX_PROJ" |
egrep "$REGEX_CONT" |
awk -v timestamp
=$TIMESTAMP -v states
=$STATES -v importance
=$IMPORTANCE '
149 if ($i ~ /timestamp/) { gsub("timestamp=","",$i); if ($i > timestamp) { flag=1; } }
150 else if ($i ~ /importance/) { gsub("importance=","",$i); if ($i<importance) { flag=1; } }
152 if (flag == 0) { print entry; }
153 }' |
sort -k 4.11,4.19 -k 3.12r
# FIXME: Magic Numerz!
154 [[ $SHELL_DEBUG == 1 ]] && TS show
>&2
158 ( [[ -z $STATES ]] ||
[[ ${#STATES} -gt 1 ]] ) && STATES
=a
159 [[ -z $IMPORTANCE ]] && local IMPORTANCE
=0
160 [[ -z $TIMESTAMP ]] && local TIMESTAMP
=$TIMESTAMP_DEFAULT
161 [[ -z $LABEL ]] && [[ -n $ARGS ]] && LABEL
="`echo $ARGS | STR2CGI`"
162 local ITEM
="status=$STATES importance=$IMPORTANCE timestamp=$TIMESTAMP project=$PROJECTS context=$CONTEXTS label=$LABEL"
164 echo "$ITEM" >> $TODO
168 local INDEX
=$1; shift;
169 local ENTRY
="`SHOWENTRY $INDEX`"
170 echo "Editing $INDEX: $ENTRY"
171 ( [[ -z $STATES ]] ||
[[ ${#STATES} -gt 1 ]] ) && local STATES
="`getval status $ENTRY`"
172 [[ -z $IMPORTANCE ]] && local IMPORTANCE
="`getval importance $ENTRY`"
173 [[ -z $TIMESTAMP ]] && local TIMESTAMP
="`getval timestamp $ENTRY`"
174 [[ -z $PROJECTS ]] && local PROJECTS
="`getval project $ENTRY`"
175 [[ -z $CONTEXTS ]] && local CONTEXTS
="`getval context $ENTRY`"
176 [[ -z $LABEL ]] && local LABEL
="`getval label $ENTRY`"
177 local ITEM
="status=$STATES importance=$IMPORTANCE timestamp=$TIMESTAMP project=$PROJECTS context=$CONTEXTS label=$LABEL"
178 RM_ENTRY
$INDEX > $TMPFILE
179 echo "$ITEM" >> $TMPFILE
185 local INDEX
=$1; shift;
186 local ENTRY
="`SHOWENTRY $INDEX`"
187 local ITEM
="`echo "$ENTRY" | sed 's/status=[aw]/status=d/g'`"
188 RM_ENTRY
$INDEX > $TMPFILE
189 echo "$ITEM" >> $TMPFILE
191 echo "Done $INDEX: $ENTRY"
195 local INDEX
=$1; shift;
196 local ENTRY
="`SHOWENTRY $INDEX`"
197 echo "Remove $INDEX: $ENTRY"
198 RM_ENTRY
$INDEX > $TMPFILE
202 function LIST_PROJ
{ cat $TODO |
sed -r -e 's/\&/ /g;s/.*project=([^ ]*).*/\1/' |
awk '! a[$0]++&&!/^$/' |
sed -e :a
-e '$!N;s/\n/ /;ta' ; }
203 function LIST_CONT
{ cat $TODO |
sed -r -e 's/\&/ /g;s/.*context=([^ ]*).*/\1/' |
awk '! a[$0]++&&!/^$/' |
sed -e :a
-e '$!N;s/\n/ /;ta' ; }
209 [[ $SHELL_DEBUG == 1 ]] && TS archiving
210 function CLEANUP_TODO
{
211 echo "Cleaning up $TODO"
212 cat $TODO |
awk '/status=d/' > $TMPFILE.
done
213 cat $TODO |
awk '!/status=d/' > $TMPFILE.clean
214 cat $TMPFILE.
done |
gzip > $BASEDIR/todo.
$TIMESTAMP_NOW.gz
&& rm $TMPFILE.
done && echo "Created: todo.$TIMESTAMP_NOW.gz"
215 mv $TMPFILE.clean
$TODO && echo "Created clean $TODO"
222 [[ $SHELL_DEBUG == 1 ]] && TS shell functionality
223 function SHELL_PARSE
{
224 while getopts "s:i:t:p:c:l:ae:d:r:CT:h" opt
; do
226 s
) STATES
=$STATES$OPTARG; ;;
227 i
) IMPORTANCE
=$OPTARG; ;;
228 t
) TIMESTAMP
="`echo $OPTARG | COMPLETE_TIMESTAMP`"; ;;
229 p
) PROJECTS
=${PROJECTS}"`echo $OPTARG | STR2CGI` "; ;;
230 c
) CONTEXTS
=${CONTEXTS}"`echo $OPTARG | STR2CGI` "; ;;
231 l
) LABEL
="`echo $OPTARG | STR2CGI`"; ;;
234 e
) ACTION
="EDIT"; INDEX
=$INDEX"`CK_IDX $OPTARG` "; ;;
235 d
) ACTION
="DONE"; INDEX
=$INDEX"`CK_IDX $OPTARG` "; ;;
236 r
) ACTION
="REMOVE"; INDEX
=$INDEX"`CK_IDX $OPTARG` "; ;;
237 C
) ACTION
="CLEANUP_TODO"; ;;
239 T
) TODO
="$OPTARG"; ;;
245 shift $
(( $OPTIND - 1 ))
250 function SHELL_OUTPUT
{
251 local fg_blue
="\033[0;34m"
252 local fg_green_lt
="\033[1;32m"
253 local fg_cyan
="\033[36m"
254 local fg_purple
="\033[0;35m"
255 local fg_black
="\033[30m"
256 local fg_white
="\033[37m"
257 local fg_gray
="\033[1;30m"
258 local bg_red
="\033[41m"
259 local bg_yellow
="\033[43m"
260 local bg_green
="\033[42m"
261 local bg_white
="\033[47m"
262 local bg_black
="\033[40m"
269 echo "${fg_green_lt} .:| GNU-ToDo |:. ${none}"
270 echo "${fg_purple} Contexts: ${fg_cyan}`LIST_CONT | CGI2STR`${none}"
271 echo "${fg_blue} Projects: ${fg_cyan}`LIST_PROJ | CGI2STR`${none}"
273 SHOW |
awk -v timestamp_ref
=$TIMESTAMP -v timestamp_def
=$TIMESTAMP_DEFAULT -v columns
=$COLUMNS \
274 -v fg_gray
=$fg_gray -v none
=$none '
276 for (i=1;i<=NF;i++) {
279 ind="'${fg_cyan}'"$i"'${none}'";
280 } else if ($i ~ /status/) {
281 gsub("status=","",$i)i;
282 if ($i == "a") { status=">"; }
283 else if ($i == "w") { status="<"; }
284 } else if ($i ~ /importance/) {
285 gsub("importance=","",$i);
286 if ($i == 0) { importance="."; }
287 else if ($i == 1) { importance="o"; }
288 else if ($i >= 1) { importance="O"; }
289 } else if ($i ~ /timestamp/) {
290 gsub("timestamp=","",$i);
291 if ($i == timestamp_def) { timestamp="'${fg_white}${bg_black}'-'${none}'"; }
292 else if ($i-timestamp_ref < 0) { timestamp
="'${bg_red}${fg_black}'"$i"'${none}'"; }
293 else if ($i-timestamp_ref < '${TS_YELLOW}') { timestamp="'${bg_yellow}${fg_black}'"$i"'${none}'"; }
294 else if ($i-timestamp_ref < '${TS_GREEN}') { timestamp="'${bg_green}${fg_black}'"$i"'${none}'"; }
295 else if ($i-timestamp_ref >= '${TS_GREEN}') { timestamp="'${bg_white}${fg_black}'"$i"'${none}'"; }
296 } else if ($i ~ /project/) {
297 gsub("project
=","",$i);
298 project="'${fg_blue}'"$i"'${none}'";
299 } else if ($i ~ /context/) {
300 gsub("context
=","",$i);
301 context="'${fg_purple}'"$i"'${none}'";
302 } else if ($i ~ /label/) {
303 gsub("label
=","",$i);
307 wdth=columns-16-10-10-10
308 printf (fg_gray"%1s
%1s
%s
"fg_gray"%.1s
"none" % -"wdth"s
%.20s
%.20s
\n",
309 importance, timestamp, ind, status, label, project, context);
311 [[ $SHELL_DEBUG == 1 ]] && TS eof
318 [[ $SHELL_DEBUG == 1 ]] && TS webinterface
319 if [[ -z ${QUERY_STRING} ]]; then
321 [[ -z $ACTION ]] && ACTION="SHOW
"
322 if [[ -n $INDEX ]]; then
323 for i in `echo $INDEX | sed 's/ /\n/g' | sort -nr`; do $ACTION $i; done
326 if [[ $ACTION == "SHOW
" ]]; then SHELL_OUTPUT;
332 function HTML_HEADER {
334 Content-type: text/html
340 <meta http-equiv="Content-Type
" content="text
/html
; charset
=UTF-8
" />
344 background-color: #f0f0f0;
348 font-family: Verdana;
351 A:link { TEXT-DECORATION: none }
352 A:visited { COLOR: #606060; TEXT-DECORATION: none }
353 A:active { COLOR: #888888; TEXT-DECORATION: none; }
354 A:hover { BACKGROUND-COLOR: #ffffff; COLOR: #ff0000; TEXT-DECORATION: none }
356 td { font-family: Verdana; font-size: 11px; text-decoration: none }
357 tr { font-family: Verdana; font-size: 11px; text-decoration: none }
359 h1 { font-family: Times; font-size: 38px; font-weight: normal; padding:2px; background: #999; }
360 h2 { font-family: Times; font-size: 30px; font-weight: normal; padding:2px; background: #999}
361 h3 { font-family: Times; font-size: 22px; font-weight: normal; padding:2px;}
363 .red { background: #f00; padding: 1px; }
364 .amber { background: #ff6600; padding: 1px; }
365 .yellow { background: #FFD700; padding: 1px; }
366 .orange { background: #FFA500; padding: 1px; }
367 .green { background: #33cc00; padding: 1px; }
368 .grey { background: #e0e0e0; padding: 1px; }
369 .button { color: #202020; background: #b0b0b0; padding: 1px; }
370 .project { color: #404040; background: #D0D0D0; padding: 1px; margin-botton: 20px; }
371 .context { color: #404040; background: #D0D0D0; padding: 1px; }
372 .selected { color: #fff; background: #FFA500; padding: 1px; }
373 .date { color: #404040; }
374 .inputfield { color: #404040; background: #D0D0D0; padding: 1px; }
376 .waiting { background: #e0e0e0; padding: 1px; }
377 .done { background: #e0e0e0; padding: 1px; }
378 .action { background: #ff6600; padding: 1px; }
382 <SCRIPT LANGUAGE="JavaScript
">
384 function checkAll(field) {
385 for (i = 0; i < field.length; i++)
386 field[i].checked = true ;
389 function uncheckAll(field) {
390 for (i = 0; i < field.length; i++)
391 field[i].checked = false ;
395 function set_timestamp (ts) {
396 document.gtd.timestamp.value=ts;
400 var inNav = navigator.appVersion.indexOf("MSIE
") < 0;
401 function print_key (event, what) {
402 var key = (inNav==1) ? event.which : event.keyCode;
403 document.data.keyspressed.value = what + ":" + key + "=" + String.fromCharCode(key) + "\n" + document.data.keyspressed.value;
404 alert(String.fromCharCode(key));
407 function eval_key (event) {
408 key=String.fromCharCode((inNav==1) ? event.which : event.keyCode);
409 if (event.keyCode == 13) {
410 document.gtd.submit();
411 } else if (event.keyCode == 27) {
412 document.gtd.reset();
416 function fkt_chk(val) {
418 for (i = 0; i < document.gtd.INDEX.length; i++)
419 if (document.gtd.INDEX[i].checked == true)
421 if (n_checked == 0) {
422 } else if (n_checked == 1) {
428 function switchdiv(id){
429 if (document.getElementById) { // DOM3 = IE5, NS6
430 if (document.getElementById(id).style.display == 'none') {
432 } else { hidediv(id); }
433 } else if (document.layers) { // Netscape 4
434 if (document.id.display == 'none') {
436 } else { hidediv(id); }
438 if (document.all.id.style.display == 'none') {
440 } else { hidediv(id); }
444 function hidediv(id) {
445 //safe function to hide an element with a specified id
446 if (document.getElementById) { // DOM3 = IE5, NS6
447 document.getElementById(id).style.display = 'none';
449 if (document.layers) { // Netscape 4
450 document.id.display = 'none';
452 document.all.id.style.display = 'none';
457 function showdiv(id) {
458 //safe function to show an element with a specified id
459 if (document.getElementById) { // DOM3 = IE5, NS6
460 document.getElementById(id).style.display = 'block';
462 if (document.layers) { // Netscape 4
463 document.id.display = 'block';
465 document.all.id.style.display = 'block';
472 <body onkeypress="eval_key
(event
)">
476 function HTML_PARSE {
477 CONTEXTS="`getval contexts ${QUERY_STRING} | awk 'NF>0{print $NF}'`"
478 PROJECTS="`getval projects ${QUERY_STRING} | awk 'NF>0{print $NF}'`"
479 TIMESTAMP="`getval timestamp ${QUERY_STRING} | awk 'NF>0{print $NF}'`"
480 IMPORTANCE="`getval importance ${QUERY_STRING} | awk 'NF>0{print $NF}'`"
481 STATES="`getval states ${QUERY_STRING}`"
482 ACTION="`getval ACTION ${QUERY_STRING} | awk 'NF>0{print $NF}'`"
483 INDEX="`getval INDEX ${QUERY_STRING}`"
484 LABEL="`getval LABEL ${QUERY_STRING}`"
486 if [[ -z $ACTION ]]; then
487 if [[ -z $INDEX ]]; then
488 [[ -n $LABEL ]] && ACTION="ADD
"
490 if ( [[ -n $LABEL ]] || [[ -n $PROJECTS ]] || [[ -n $CONTEXTS ]] || [[ -n $TIMESTAMP ]] || [[ -n $IMPORTANCE ]] ); then
497 elif [[ $ACTION != SHOW ]]; then
498 echo "<span id
='ACTION' class
='action'>"
500 if [[ $ACTION == ADD ]]; then
502 elif [[ $ACTION != SHOW ]]; then
503 for i in `echo $INDEX | sed 's/ /\n/g' | sort -nr`; do
510 TIMESTAMP=$TIMESTAMP_DEFAULT
516 [[ -z $TIMESTAMP ]] && TIMESTAMP=$TIMESTAMP_DEFAULT
522 local YEAR="`date +%Y`"
523 local MONTH="`date +%m`"
524 local NEXTMONTH=`echo $(( $MONTH+1 )) | awk '{printf("%.2d
",$1)}'`
525 [[ $1 == "CURRENT
" ]] && cal -m | sed -r -e 's/\n//g;s/([^ ]*) '$YEAR'/ \1/;s/[0-9]*/<a href=\"javascript:set_timestamp('$YEAR$MONTH'&)\" class=\"date\">&<\/a>/g'
526 [[ $1 == "NEXT
" ]] && cal -m -3 | sed -r -e 's/^.{44}//g;s/\n//g;s/([^ ]*) '$YEAR'/ \1/;s/[0-9]*/<a href=\"javascript:set_timestamp('$YEAR$NEXTMONTH'&)\" class=\"date\">&<\/a>/g'
530 function SHOW_CONTEXTS {
532 <SCRIPT LANGUAGE="JavaScript
">window.setTimeout("hidediv
('ACTION')",$DELAY)</script>
540 <td style="border-bottom
:1px solid
;">
549 print "<a href
=\"'${BASE_URL}'&contexts
="$i"&projects
='${PROJECTS}'\" class
=\"context
\">"$i"</a
> 
; 
; 
;"
556 <a href="${BASE_URL}&contexts
=&projects
=${PROJECTS}" class="context
">All</a>
561 <td style="border-bottom
:1px solid
; border-left
:1px solid
">
562 <a href="${BASE_URL}&projects
=&contexts
=" class="context project
">All</a>
563 <a href="#">Today</a>
564 <a href
="#">Statistics
</a
> 
; 
;
570 <form name
="gtd" ACTION
="${FULL_URL}" method
=get
>
574 function SHOW_PROJECTS
{
578 <td valign=top style="border-left:1px solid; line-height: 2;">
579 <a href="${BASE_URL}&projects=&contexts=${CONTEXTS}" class="project">All</a><br class="project"><br class="project">
585 print "<a href=\"'${BASE_URL}'&projects="$i"&contexts='${CONTEXTS}'\" class=\"project\">"$i"</a><br class=\"project\">"
596 function SHOW_ACTIONS
{
597 SHOW |
awk -v timestamp_ref
=`date +%Y%m%d` '
600 for (i=1;i<=NF;i++) {
603 ind="<td><input onclick=\"fkt_chk("$i")\" type=\"checkbox\" name=\"INDEX\" value=\""$i"\"></td>";
604 } else if ($i ~ /status/) {
605 gsub("status=","",$i);
606 if ($i ~ /a/) { status="<td><span class=active>></span></td>"; }
607 else if ($i == "w") { status="<td><span class=waiting><</span></td>"; }
608 else { status="<td></td>"; }
609 } else if ($i ~ /importance/) {
610 gsub("importance=","",$i);
611 } else if ($i ~ /timestamp/) {
612 gsub("timestamp=","",$i);
613 if ($i == $TIMESTAMP_DEFAULT) { timestamp="<td></td>"; class="grey"; }
614 else if ($i-timestamp_ref < 0) { timestamp="<td><span class=red>"$i"</span></td>"; class="red"; }
615 else if ($i-timestamp_ref < 1) { timestamp="<td><span class=yellow>"$i"</span></td>"; class="yellow"; }
616 else if ($i-timestamp_ref < 3) { timestamp="<td><span class=green>"$i"</span></td>"; class="green"; }
617 else if ($i-timestamp_ref >= 3) { timestamp="<td><span class=gray>"$i"</span></td>"; class="grey"; }
618 } else if ($i ~ /label/) {
619 gsub("label=","",$i);
620 label="<td><span class=\""class"\">"$i"</span></td>";
623 print ind"\n"status"\n"timestamp"\n"label"\n</tr>"
628 function SHOW_INPUT
{
631 <table style="border:2px solid">
633 <td align="center" class="inputfield">
634 <a href="javascript:checkAll(document.gtd.INDEX)" class=button>Check All</a>
635 <a href="javascript:uncheckAll(document.gtd.INDEX)" class=button>Uncheck All</a>
640 <td class="inputfield"><input type="text" size="50" name="LABEL" value="`echo ${LABEL} | CGI2STR | sed 's/^ //'`"></td>
644 <td class="inputfield">
645 <input type="text" size="20" name="projects" value="`echo ${PROJECTS} | CGI2STR | sed 's/^ //'`">
646 <select name="projects">
647 <option selected></option>
653 # if ($i ~ /'${PROJECTS}'/) { printf "<option selected>"; }
664 <td class="inputfield">
665 <input type="text" size="20" name="contexts" value="`echo ${CONTEXTS} | CGI2STR | sed 's/^ //'`">
666 <select name="contexts">
683 <td class="inputfield">
697 <td colspan="2" align="center">
698 <input type="text" size="20" name="timestamp" value="">
709 <td class="inputfield">
710 <select name="importance">
711 <option selected></option>
718 <td class="inputfield">
719 <select name="status">
720 <option selected></option>
732 <td align="center" class="inputfield">
733 <input type="hidden" name="ACTION" />
734 <a href="javascript:document.gtd.ACTION.value='ADD';document.gtd.submit();" class=button>Add</a>
735 <a href="javascript:document.gtd.ACTION.value='DONE';document.gtd.submit();" class=button>Done</a>
736 <a href="javascript:document.gtd.ACTION.value='EDIT';document.gtd.submit();" class=button>Edit</a>
737 <a href="javascript:document.gtd.ACTION.value='REMOVE';document.gtd.submit();" class=button>Remove</a>
738 <a href="javascript:document.gtd.reset();" class=button>Reset</a>
744 <script language="JavaScript">document.gtd.LABEL.focus();</script>
748 function HTML_TRAILER
{
760 BASE_URL
="`basename $0`?"
761 FULL_URL
="`basename $0`?contexts=`echo $CONTEXTS | STR2CGI`&projects=`echo $PROJECTS | STR2CGI`"
773 [[ $SHELL_DEBUG == 1 ]] && TS eof