(require (lib "unitsig.ss")
(lib "pregexp.ss")
(lib "servlet-sig.ss" "web-server")
(lib "servlet-helpers.ss" "web-server"))
(require "database.ss")
(unit/sig ()
(import servlet^)
(define (make-page title user body)
(lambda (url)
`(html (head (title ,title)
(script [(language "JavaScript")
(src "/java/timestamp_picker.js")])
(link [(rel "stylesheet")
(href "/default.css")
(type "text/css")
(title "specified")]))
(body
(div [(style "background-color: blue; color: white")]
(table [(width "100%")]
(tr
(td
(div [(style "color: white; text-align: center")]
(div
(span [(style "font-size: large")]
"Memories Incorporated")
(span " powered by ")
(span [(style "font-size: large")]
"Scheme and PostgreSQL")))))))
(div [(style "border-color: blue; border-style: solid")]
(table [(width "100%")]
(tr [(style "text-align: center")]
(td ,(make-main-link 'upload "Upload File" url))
(td ,(make-main-link 'search "Search" url))
(td ,(make-main-link 'about "About" url))
(td ,(if user
(make-main-link 'logout "Log Out" url)
(make-main-link 'login "Log In" url))))))
(div [(style "padding: 4ex 8ex 4ex 8ex")]
,(body url))
(div [(style "background-color: blue; color: white; font-weight: bold")]
,(if user
`(span "Logged in as " (em ,user))
`(span "Not logged in")))))))
(define (make-main-link sym text url)
(let [(target (format "~a?action=~a" url sym))]
`(a [(href ,target)] ,text)))
(define (login-body)
(lambda (url)
`(div (h2 "Log into your account:")
(form [(action ,url) (method "post")]
(input [(type "hidden") (name "action") (value "checklogin")])
(table
(tr (td "Username")
(td (input [(type "text") (name "username")])))
(tr (td "Password")
(td (input [(type "password") (name "password")])))
(tr (td (input [(type "submit") (name "submit")
(value "Log in")]))))))))
(define (logout-body)
(lambda (url)
`(div (h2 "Logged out."))))
(define (failed-login-body)
(lambda (url)
`(div (h2 "Login failed!"))))
(define (search-body)
(lambda (url)
`(div (h2 "Search for documents in the database:")
(form [(name "search") (action ,url) (method "get")]
(input [(type "hidden") (name "action") (value "listing")])
(div (p "Enter search keywords: " (br)
(input [(name "searchstring") (type "text") (size "48") (value "")]))
(p "Start of the search interval: " (br)
(input [(name "begin") (type "text") (size "32") (value "")])
(a [(href "javascript:show_calendar('document.search.begin',
document.search.begin.value)")]
(img [(src "/images/cal.gif") (width "16") (height "16") (border "0")
(alt "Click here to pick the start of search interval")])))
(p "End of the search interval: " (br)
(input [(name "end") (type "text") (size "32") (value "")])
(a [(href "javascript:show_calendar('document.search.end',
document.search.end.value)")]
(img [(src "/images/cal.gif") (width "16") (height "16") (border "0")
(alt "Click here to pick the end of search interval")])))
(p (input [(name "submitsearch") (type "submit") (value "Search")])))))))
(define (about-body)
(lambda (url)
`(div (h2 "Information about this project:")
(p "This experimental document management system "
"is designed to provide a framework for testing "
"new information retrieval techniques."))))
(define (upload-body)
(lambda (url)
`(div (h2 "Add a document to the database:")
(form [(action ,url) (enctype "multipart/form-data") (method "post")]
(input [(type "hidden") (name "action") (value "insert")])
"Select file: "
(div (input [(name "file") (type "file")
(size "32") (maxlength "100000")]))
(br) "File type:"
(div ,(select-mime-type))
(br) "Description: "
(div (textarea [(name "description") (cols "64")
(rows "4") (wrap "soft") (value "")]))
(div (input [(type "submit") (value "Upload file")]))))))
(define (select-mime-type)
`(select [(size "1") (name "mime-type")]
(option [(value "none")] "NONE")
(option [(value "text/html")] "HTML")
(option [(value "text/plain")] "TEXT")
(option [(value "application/vnd.ms-excel")] "EXCEL")
(option [(value "application/x-tex")] "LATEX")
(option [(value "application/pdf")] "PDF")
(option [(value "application/postscript")] "POSTSCRIPT")
(option [(value "application/vnd.ms-powerpoint")] "POWERPOINT")
(option [(value "application/rtf")] "RTF")
(option [(value "application/msword")] "WORD")
(option [(value "image/gif")] "GIF")
(option [(value "image/jpeg")] "JPG")))
(define (failed-upload-body)
(lambda (url)
`(div (h2 "The system currently only handles HTML files."))))
(define (failed-display-body)
(lambda (url)
`(div (h2 "No selection!"))))
(define (listing-body bindings)
(lambda (url)
(let* [(searchstring (extract-binding/single 'searchstring bindings))
(keywords (pregexp-split "\\s+" searchstring))
(begin (extract-binding/single 'begin bindings))
(end (extract-binding/single 'end bindings))]
(if (null? keywords)
`(div (h2 "No keywords!"))
`(form [(action ,url) (method "post")]
(input [(type "hidden") (name "action") (value "display")])
(table [(border "1") (cellpadding "5") (cellspacing "2")]
(tr [(style "text-align: left")]
(th "View:")
(th "Date:")
(th "Description:")
(th (input [(type "submit")
(name "score")
(value "update")]))
(th "Score:"))
,@(process-search-query
keywords
(rationalize-date begin "0001-1-1 00:00:00.00")
(rationalize-date end "9999-1-1 00:00:00.00")
listing-item)))))))
(define (listing-item id mark description score)
`(tr [(style "text-align: left")]
(td (input [(type "submit")
(name "selection")
(value ,(number->string id))]))
(td ,mark)
(td ,description)
(th (input [(type "checkbox")
(name ,(string-append "OID-" (number->string id)))
(value ,(number->string id))]))
(td ,score)))
(define iso-8601-date-pregexp
(pregexp
(string-append "([[:digit:]]{4}).([[:digit:]]{1,2}).([[:digit:]]{1,2})"
"[[:space:]]+([[:digit:]]+[:][[:digit:]]+[:][[:digit:]\\.]+)")))
(define american-date-pregexp
(pregexp
(string-append "([[:digit:]]{1,2}).([[:digit:]]{1,2}).([[:digit:]]{4})"
"[[:space:]]+([[:digit:]]+[:][[:digit:]]+[:][[:digit:]\\.]+)")))
(define (rationalize-date string alternative)
(cond ((pregexp-match iso-8601-date-pregexp string) string)
((pregexp-match american-date-pregexp string)
(pregexp-replace american-date-pregexp string "\\3-\\2-\\1 \\4"))
(else alternative)))
(define (extract-oids bindings)
(do [(bindings bindings (cdr bindings)) (oids ())]
[(null? bindings) oids]
(if (pregexp-match "(?i:OID-)"
(symbol->string (caar bindings)))
(set! oids (cons (cdar bindings) oids)))))
(adjust-timeout! 300)
(let [(last-search-bindings #f)]
(let reset [(user #f) (request initial-request)]
(let interact [(request request)]
(let* [(bindings (request-bindings request))
(action (cond ((exists-binding? 'username bindings) 'checklogin)
((not user) 'login)
((exists-binding? 'action bindings)
(string->symbol (extract-binding/single 'action bindings)))
(else 'search)))]
(case action
[(about)
(interact (send/suspend (make-page "About" user (about-body))))]
[(login)
(interact (send/suspend (make-page "Log in" user (login-body))))]
[(checklogin)
(let* [(username (extract-binding/single 'username bindings))
(password (extract-binding/single 'password bindings))]
(if (login-valid? username password)
(reset username (send/suspend (lambda (url) (redirect-to url))))
(interact (send/suspend
(make-page "Login failed" user (failed-login-body))))))]
[(logout)
(reset #f (send/suspend (make-page "Logged out" #f (logout-body))))]
[(search)
(interact (send/suspend (make-page "Search" user (search-body))))]
[(listing)
(set! last-search-bindings bindings)
(interact (send/suspend (make-page "Search Results" user
(listing-body bindings))))]
[(display)
(let [(selection (if (exists-binding? 'selection bindings)
(extract-binding/single 'selection bindings) #f))
(oids (extract-oids bindings))]
(cond ((not (null? oids))
(update-ranking oids)
(interact (send/suspend
(make-page "Search Results" user
(listing-body last-search-bindings)))))
(selection
(send/back (redirect-to (export-document user selection))))
(else (interact (send/suspend
(make-page "Search" user (failed-display-body)))))))]
[(upload)
(interact (send/suspend (make-page "File Upload" user (upload-body))))]
[(insert)
(let [(file-contents (extract-binding/single 'file bindings))
(description (extract-binding/single 'description bindings))
(mime-type (extract-binding/single 'mime-type bindings))]
(if (import-document user mime-type file-contents description)
(interact (send/suspend (make-page "File Upload" user (upload-body))))
(interact (send/suspend (make-page "Upload Failed" user
(failed-upload-body))))))]
))))))