#!/home/htdocs/cgi-bin/newlisp # Written by Eddie Rucker -- updated 10-22-2004 # updated to 1.13 - November 14th # updated to 1.14 - November 16th - changed all deprecated integer to int # version 1.15 fix for new context behaviour in (save-file-lock ...) L.M. March 2, 2006 # versiob 1.1.6 replaces deprecated 'symbol' with 'sym' # On install # 1. change the shbang above to your newlisp install location # 2. change the constant exit-URL below to a URL to exit to # 3. change the cookie-domain to your server (for alert events) (constant 'exit-URL "http://newlisp.org/index.cgi?page=Code_Contributions") (constant 'cookie-domain "newlisp.org") # version 0.1 added bubbles to some of the buttons # version 0.2 bug fixes # version 0.3 bug fixes # version 0.4 bug fixes # version 0.5 del button clears panel # version 0.6 bug fix and added exit link -- see the exit-URL below # version 0.7 bug fix in edit with no end time # version 0.8 added reminder function warns about 5 minutes before event # version 0.9 bug fix # version 0.91 a function clean up # version 1.0 cookie expiration for 5 minutes past current time and conditional disabling of input fields # version 1.1 javascript fixes and change in font size for events # version 1.2 function cleanup, # font changes -- large type for main, small-type for week and month (printer friendly), # added time-zone function # version 1.3 bad bubble name next month and next year were called previous month # changed the "#" for reminder to registered mark (is this legal?) # version 1.4 added img bell.png for Reminders # version 1.5 bugfix in timezone # version 1.6 bugfix -- clear event list so that todays events don't carry over # version 1.7 cleanup and bugfix set timezone as a constant # version 1.8 fixed a bug in the previous or next month with the day value being wrong -- thanks Sam! # version 1.81 function cleanup - rid extra setqs in next year and previous year # a few documentation and format changes # version 1.82 changed new button to clear "its meaning is more clear" tables:collapsed # version 1.9 previously overlooked the cookie's domain server # version 1.10 changed all 'collect' to 'select', which since 8.2.0 accepts both syntax's # version 1.11 changed all 'getenv' to 'env' # version 1.12 catch for load for version 8.7.0 # version 1.13 took away quite from inc argument L.M (context 'CGI) # get cgi request and convert hex values to characters (setq req (replace {%([0-9A-F]{2})} (or (env "QUERY_STRING") "") (char (int (append "0x" $1))) 0)) # parse out value pairs (map (fn (x) (let (L (parse x "=")) (set (sym (first L)) (last L)))) (parse (replace "+" req " ") "[&;]" 0)) # get time cookies (setq alert-cookies (map (fn (x) (last (parse x "="))) (parse (or (env "HTTP_COOKIE") "") "[&;]" 0))) (context 'EDB) # initalize event list (define (clear-events) (setq elist '())) # write event list with a file lock (define (save-file-lock fname) (while (file? "lock") (sleep 100)) (write-file "lock" "semaphore") (context 'MAIN) ; fix for 8.8.0 (save fname 'elist) (context 'EDB) ; fix for 8.8.0 (delete-file "lock")) # get event from file (define (get y m d) (if (catch (load (format "%d/%d/%d.lsp" y m d)) 'error) elist '())) # put event into file (define (put y m d start-time end-time event remind) (setq elist (unique (sort (cons (list start-time (if (= end-time -1) -1 (max start-time end-time)) event remind) elist)))) (make-dir (format "%d/" y)) (make-dir (format "%d/%d/" y m)) (save-file-lock (format "%d/%d/%d.lsp" y m d) elist) elist) # delete nth event from file (define (del y m d item) (pop elist item) (let (fname (format "%d/%d/%d.lsp" y m d)) (if elist (save-file-lock fname elist) (delete-file fname)))) (define (event? y m d) (file? (format "%d/%d/%d.lsp" y m d))) (context 'TIME) (constant 'mdays '(0 31 28 31 30 31 30 31 31 30 31 30 31)) (constant 'timezone (nth 9 (now))) (define (month-name m) (nth m '("" "Jan" "Feb" "Mar" "Apr" "May" "Jun" "Jul" "Aug" "Sep" "Oct" "Nov" "Dec"))) (define (leap? y) (and (= 0 (% y 4)) (or (!= 0 (% y 100)) (= 0 (% y 400))))) (define (days-in-month y m) (+ (nth m mdays) (if (and (leap? y) (= m 2)) 1 0))) (define (first-day-of-month y m) (% (+ (mul y 365.24258) (apply + (slice mdays 0 m)) (if (and (leap? y) (< m 3)) -1 0)) 7)) (define (days-until-end-of-month y m d) (- (days-in-month y m) d)) (define (days-until-end-of-year y m d) (- (apply + (map (fn (x) (days-in-month x)) (sequence m 12))) d)) (define (next-day y m d) (if (= (days-until-end-of-year y m d) 0) (list (+ y 1) 1 1) (= (days-until-end-of-month y m d) 0) (list y (+ m 1) 1) (list y m (+ d 1)))) (define (next-month y m d , yy mm dd) (list (setq yy (if (= m 12) (+ y 1) y)) (setq mm (if (= m 12) 1 (+ m 1))) (if (> d (setq dd (days-in-month yy mm))) dd d))) (define (previous-month y m d , yy mm dd) (list (setq yy (if (= m 1) (- y 1) y)) (setq mm (if (= m 1) 12 (- m 1))) (if (> d (setq dd (days-in-month yy mm))) dd d))) (define (next-year y m d , yy dd) (list (setq yy (+ y 1)) m (if (> d (setq dd (days-in-month yy m))) dd d))) (define (previous-year y m d , yy dd) (list (setq yy (- y 1)) m (if (> d (setq dd (days-in-month yy m))) dd d))) (define (mil->string t) (if (<= 0 t 2359) (let (p (/ t 100)) (let (h (% p 12)) (format "%d:%02d %sM" (if (= 0 h) 12 h) (% t 100) (if (< p 12) "A" "P")))) "")) (define (time->mil h m ap) (+ (* 100 (% h 12)) m (if (= "AM" ap) 0 1200))) (define (time->parts t) (if (= t -1) (list "1" "00" "AM") (let (p (/ t 100)) (let (h (% p 12)) (list (string (if (= 0 h) 12 h)) (format "%02d" (% t 100)) (if (< p 12) "AM" "PM")))))) ;(define (dup s n) ; (if (= n 0) '() (map (fn (x) s) (sequence 1 n)))) (define (->weeks L) (if (<= (length L) 7) (list (append L (dup 0 (- 7 (length L))))) (cons (slice L 0 7) (->weeks (slice L 7))))) (define (month y m) (->weeks (append (dup 0 (first-day-of-month y m)) (sequence 1 (days-in-month y m))))) (context 'MAIN) (define (events->html L interactive) (if (empty? L) (format "
%s
\n" (if interactive "

No Events

\n" "")) (format "
%s
\n" (if interactive "class=\"big\"" "") (let (st 0 et 0 i -1) (join (map (fn (x) (let (start-time (first x) end-time (nth 1 x) event (append (if interactive (append (format "\n" (inc i) (if (= i item) "checked=\"checked\"" "")) (if (= (last x) 1) " " "")) "") (nth 2 x))) (if (or (and (= st start-time) (= et end-time)) (= start-time -1)) (format "
%s
" event) (format "
%s - %s
\n
%s
" (TIME:mil->string (setq st start-time)) (if (= end-time -1) "" (TIME:mil->string (setq et end-time))) event)))) L) "\n"))))) (define (get-week_ L d) (if (empty? L) '() (find d (first L)) (first L) (get-week_ (rest L) d))) (define (get-week y m d) (get-week_ (TIME:month y m) d)) (define (week->html y m d) (append "\n\n" (join (map (fn (x) (format "\n" x)) '("Sunday" "Monday" "Tuesday" "Wednesday" "Thursday" "Friday" "Saturday"))) "\n\n" (join (map (fn (x) (format {} (if (= x 0) " " (append (format "

%d

" x) (if (EDB:event? y m x) (events->html (EDB:get y m x)) ""))))) (get-week y m d))) "\n
%s
%s
\n")) (define (month->html y m interactive) (append "\n\n" (join (map (fn (x) (format "\n" x)) (if interactive '("S" "M" "T" "W" "T" "F" "S") '("Sunday" "Monday" "Tuesday" "Wednesday" "Thursday" "Friday" "Saturday")))) "\n" (join (map (fn (a) (format "%s\n" (join (map (fn (b) (format "\n" (if interactive "" "style=\"height:80px\"") (if (= b 0) " " # 0 means blank day (if interactive (format "" b (if (EDB:event? y m b) ";color:red" "")) (append (format "

%d

" b) (if (EDB:event? y m b) (events->html (EDB:get y m b)) "")))))) a)))) (TIME:month y m))) "\n
%s
%s
\n")) # --- main program starts here --- # initialize variables (map set '(cy cm cd c-hr c-mn) (select (now (- 0 TIME:timezone)) 0 1 2 3 4)) (setq CGI:y (if CGI:y (int CGI:y) cy) CGI:m (if CGI:m (int CGI:m) cm) CGI:d (if CGI:chday (int CGI:chday) (if CGI:d (int CGI:d) cd))) # see if there are any reminders (setq c-time (+ (* c-hr 100) c-mn) reminders (filter (fn (x) (and (= (last x) 1) (not (find (string (first x)) CGI:alert-cookies)) (<= (- (first x) 5) c-time (first x)))) (EDB:get cy cm cd)) alert-time (if reminders (first (first reminders)) nil)) # get events (EDB:clear-events) (setq event-list (EDB:get CGI:y CGI:m CGI:d)) # execute command (case CGI:cmd ("Clear" (setq CGI:text nil CGI:allday nil CGI:remind nil CGI:endtime "yes" CGI:s_hr "1" CGI:s_mn "00" CGI:s_ap "AM" CGI:e_hr "1" CGI:e_mn "00" CGI:e_ap "AM")) ("Add" (EDB:put CGI:y CGI:m CGI:d (if CGI:allday -1 # -1 is indicates all day event (TIME:time->mil (int CGI:s_hr) (int CGI:s_mn) CGI:s_ap)) (if (not CGI:endtime) -1 # -1 is indicates no end time (TIME:time->mil (int CGI:e_hr) (int CGI:e_mn) CGI:e_ap)) CGI:text (if CGI:remind 1))) ("Del" (if CGI:item (begin (EDB:del CGI:y CGI:m CGI:d (int CGI:item)) (setq CGI:text nil CGI:allday nil CGI:remind nil CGI:endtime "yes" CGI:s_hr "1" CGI:s_mn "00" CGI:s_ap "AM" CGI:e_hr "1" CGI:e_mn "00" CGI:e_ap "AM")))) ("Edit" (if (and CGI:item event-list) (begin (map set '(start-time end-time CGI:text) (nth (int CGI:item) event-list)) (if (= start-time -1) (setq CGI:allday "on") (map set '(CGI:s_hr CGI:s_mn CGI:s_ap) (TIME:time->parts start-time))) (if (= end-time -1) (setq CGI:endtime nil) (map set '(CGI:e_hr CGI:e_mn CGI:e_ap) (TIME:time->parts end-time))) (EDB:del CGI:y CGI:m CGI:d (int CGI:item))))) ("<<" (map set '(CGI:y CGI:m CGI:d) (TIME:previous-year CGI:y CGI:m CGI:d))) (">>" (map set '(CGI:y CGI:m CGI:d) (TIME:next-year CGI:y CGI:m CGI:d))) ("<" (map set '(CGI:y CGI:m CGI:d) (TIME:previous-month CGI:y CGI:m CGI:d))) (">" (map set '(CGI:y CGI:m CGI:d) (TIME:next-month CGI:y CGI:m CGI:d))) ("Week" (setq html-body (append (format "

%s %d

\n" (TIME:month-name (int CGI:m)) (int CGI:y)) (week->html (int CGI:y) (int CGI:m) (int CGI:d))))) ("Month" (setq html-body (append (format "

%s %d

\n" (TIME:month-name (int CGI:m)) (int CGI:y)) (month->html (int CGI:y) (int CGI:m)))))) # print header (if alert-time (print (setq expires (format "Set-cookie: alert%d=%d; expires=%s; path=/; domain=%s\n" alert-time alert-time (apply (fn (d0 d1 d2 d3 d4) (format "%s, %s-%s-%s %s GMT" d0 d1 d2 d3 d4)) (select (parse (date (apply date-value (now (+ 5 TIME:timezone)))) " ") 0 2 1 5 3)) cookie-domain)))) (print [text]Content-type: text/html Calendar
[/text] (format "\n" CGI:y) (format "\n" CGI:m) (format "\n" CGI:d) (or html-body (append [text]
Today is [/text] (format "%s %d, %d   %s" (TIME:month-name cm) cd cy (TIME:mil->string (+ (* c-hr 100) c-mn))) [text] Events for [/text] (format "%s %d, %d" (TIME:month-name CGI:m) CGI:d CGI:y) [text]
[/text] (format "%s   %d, %d" (TIME:month-name CGI:m) CGI:d CGI:y) [text]
[/text] (month->html CGI:y CGI:m "interactive") [text]
[/text] (events->html (EDB:get CGI:y CGI:m CGI:d) "interactive") [text]
All day event: [/text] (format "\n" (if CGI:allday "checked=checked" "")) "   Reminder :" (format "\n" (if CGI:remind "checked=checked" "")) [text]
Start time: End time: [/text] (format "\n" (if CGI:endtime "checked=\"checked\"" "")) "  
[/text] (format "\n" (or CGI:text "")) [text]
    [/text] (format "Exit\n" exit-URL) [text]
[/text])) (if alert-time (format "\n" (TIME:mil->string (int alert-time))) "") [text]
[/text]) (exit)