module Template ( template, navigation ) where
import IO
import Data.Generics
import Time
import Locale ( defaultTimeLocale )
import List ( intersperse, transpose, sort )
import System.Random hiding ( split )
import System.Directory -- ( doesFileExist )
import Data.Maybe ( catMaybes )
import Char ( isLower )
import Doc
import Mmlparse
import Doc2Html
import XHTML
import HTML
import Useful
import OTP
-- horizontal align 2 elements
a <<>> b = matrix2tableClass "horiz" [[[a], [b]]]
horz a b c = matrix2tableClass "horiz" [[[a], [b], [c]]]
-- vertical align 2 elements
a /// b = matrix2tableClass "vert" [[ [a] ],
[ [b] ]]
-- something more nifty
mytable a b = elm "table" ["class"-->"mytable"] rows
where rows = [elm "tr" [] tds]
tds = [elm "td" ["class"-->"goleft"] [a],
elm "td" ["class"-->"goright"] [b]]
iframe = around "iframe"
--elm "a" ["id"-->name, "href"-->target] [elm "img" ["src"-->"/images/search-blank.gif", "alt"-->name] []]
searchbar_form = elm "form" form_attrs [formtable]
where form_attrs = ["action"-->"javascript:submit();", "id"-->"searchform", "name"-->"f"]
formtable = horz xbutton (txt "") xinput
xinput = "input" `with` ["size"-->"60", "id"-->"field",
"name"-->"q", "type"-->"text"]
-- xbutton = "input" `with` ["name"-->"btnG", "id"-->"image", "class"-->"search",
-- "type"-->"image", "src"-->"/images/search-blank.gif", "maxlength"-->"256"]
xbutton = "input" `with` ["name"-->"btnG",
"type"-->"submit", "value"-->" Search ", "maxlength"-->"256"]
tdClass name cs = elm "td" ["class"-->name] cs
tdClass2 name cs = elm "td" ["colspan"-->"2", "class"-->name] cs
tdClassN n name cs = elm "td" ["colspan"-->show n, "class"-->name] cs
tr = around "tr"
center el = around "center" [el]
pageLayout loggedin {-atitle-} quote author left message pageName xs = {-center-} elm "table" ["class"-->"bigtable"] [row1, row2, row3]
where row1 = tr [tdClass2 "paneTopLeft" (topleft_top loggedin pageName {-atitle-})]
row2 = tr [tdClass "paneLeft" left, tdClass "paneContent" (message ++ xs)]
row3 = tr [tdClass "paneLeftBottom" bottom_left, tdClass "paneFooter" (footer quote author)]
----------------------------------------------
-- BUTTONS
----------------------------------------------
--makeFlipper name target = elm "a" ["id"-->name, "href"-->target] [elm "img" ["src"-->"/images/blank.gif", "alt"-->name] []]
makeFlipper name target = elm "a" ["href"-->target] [txt name]
--makeFlipper name target = elm "a" ["href"-->target] [imgSrcAlt ("/images/"++name++".png") name]
loginBtn p = makeFlipper "login" ("/index.cgi?action=login&page=" ++ p)
logoutBtn p = makeFlipper "logout" ("/index.cgi?action=logout&page=" ++ p)
newBtn p = makeFlipper "add" ("/index.cgi?action=add&page=" ++ p)
pdfBtn p = makeFlipper "pdf" ("/index.cgi?action=viewpdf&page=" ++ p)
editBtn p = makeFlipper "edit" ("/index.cgi?action=edit&page=" ++ p ++ "#Top")
loggedoutBtns p = [pdfBtn p, loginBtn p]
loggedinBtns p = [editBtn p, pdfBtn p, newBtn p, logoutBtn p]
banner = imgSrcAlt "http://www.updike.org/images/updike8org.png" "UPDIKE.ORG"
buttons loggedin p = intersperse spacePad2 (if loggedin then loggedinBtns p else loggedoutBtns p)
-- [elm "p" ["class"-->"goright"] (intersperse spacePad2 buttons)]
-------------------------- where lbuttons = [lbut "pdf", lbut "login", banner]
-- where buttons = (if loggedin then loggedinBtns p else loggedoutBtns p) -- ++ [banner]
----------------------------------------------
fig8 = elm "a" ["href"-->"/index.cgi?Fig8"] [imgSrcAlt "/images/fig8-small.png" "Hypnotic Figure Eight Klein Bottle Emblem"]
spacePad = txt " "
spacePad2 = txt " "
blankline = txt "
"
-- header heading top banner part, whatumcallit
topleft_top loggedin p {-atitle-} = [mytable (fig8 <<>> (h1 [spacePad, spacePad, spacePad, txt ""] -- ///
-- imgSrcAlt "/images/banner.png" "home of Jared Updike on the web
))
(elm "p" ["class"-->"goright"] (intersperse spacePad buttons))]
where buttons = if loggedin then loggedinBtns p else loggedoutBtns p
ppClass c es = elm "p" ["class"-->c] es
bottom_left = [pp extras]
footer quote author = [pp [txt " "]]
{-
footer quote author = [ppClass "goright" [txt quote],
ppClass "goright" [ii [txt$ " —"++author]],
pp [txt "Copyright (C) 2007 Jared Updike"]]
-}
hiddeninput k v = "input" `with` ["type"-->"hidden", "name"-->k, "value"-->v]
loginform pageName otp =
elm "form" ["method"-->"post", "id"-->"loginform", "name"-->"f",
"action"-->("/index.cgi?action=validate&page="++pageName), "onsubmit"-->"scramble();"]
([matrix2tableClass "horiz" [[ [txt "Username "],[xusername] ] ,
[ [txt "Password "],[xpassword] ] ,
[ [] ,[xsubmit ] ]] , xhidden])
where xusername = "input" `with` ["size"-->"20", "name"-->"username", "type"-->"text"]
xpassword = "input" `with` ["size"-->"20", "name"-->"password", "type"-->"password"]
xsubmit = "input" `with` ["type"-->"submit", "value"-->"Log In"]
xhidden = hiddeninput "otp" otp
jscriptAts = ["type"-->"text/javascript"]
script fname = "script" `with` (jscriptAts ++ ["src"-->fname])
linkcssAts = ["rel"-->"stylesheet", "type"-->"text/css"]
linkcss fname = "link" `with` (linkcssAts ++ ["href"-->fname])
scripts somescripts =
(if sIFRuse then sIFRinclude else []) ++ somescripts ++
[ comment ["[if gte IE 5.5000]>",
"",
" gets added
---- sIFR stuff ----
--------------------
sIFRuse = False
sIFRinclude = ["link" `with` ["href"-->"css/sIFR-screen.css", "rel"-->"stylesheet", "media"-->"screen",
"type"-->"text/css"],
"link" `with` ["href"-->"css/sIFR-print.css", "rel"-->"stylesheet", "media"-->"print",
"type"-->"text/css"],
script "/scripts/sifr.js",
script "/scripts/sifr-addons.js"]
sIFR = Comm "" [blah] "" --elm "script" jscriptAts [Comm "//"]
where blah = "\n"
white = "ffffff"
black = "000000"
skyblue = "5eb4ff"
sIFRscriptlines = "if (typeof sIFR == \"function\") {\n" ++ replacers ++ "};\n"
where replacers = concatMap replacer [("body h1", black, white),
("body h2", black, white),
("body h3", white, skyblue),
("body h4", black, white)]
replacer (selector,col,bgcol) = "sIFR.replaceElement(named({sSelector:\""++selector++"\", sFlashSrc:\"tradegothic.swf\", sColor:\"#"++col++"\", sLinkColor:\"#000000\", sBgColor:\"#"++bgcol++"\", sHoverColor:\"#CCCCCC\"}));\n"
------------------------
---- end sIFR stuff ----
validateUrlCSS = "http://jigsaw.w3.org/css-validator/validator?uri=http://www.updike.org/"
validateUrlXHTML = "http://validator.w3.org/check?uri=http://www.updike.org/"
--"http://validator.w3.org/check?uri=referer"
extras = [lnk validateUrlXHTML [validateIconXHTML],
lnk validateUrlCSS [validateIconCSS],
txt " "]
where validateIconXHTML = imgSrcAlt "/images/w3c-valid-xhtml.gif" "Valid XHTML 1.0 Transitional!"
validateIconCSS = imgSrcAlt "/images/w3c-valid-css.gif" "Valid CSS!"
----------- Variable substitution
type Env = [(String, Elem)]
type XMLEnv = [(String, XML)]
-- stage 1: substitute in the structured Document
defaultEnv = [("edit", Txt ".")]
varSubst :: Env -> Elem -> Elem
varSubst env = varInt
where varInt (VarInt es) = lookupper es
varInt x = x
lookupper es =
let k = (stringize es) in
case lookup k env of
Nothing -> VarInt es
Just v -> v
subst :: Env -> Document -> Document
subst env = everywhere (mkT (varSubst env))
-- stage 2: substitute XML
xmlvarSubst :: XMLEnv -> XML -> XML
xmlvarSubst env = varInt
where varInt (Hole es) = lookupper es
varInt x = x
lookupper k =
case lookup k env of
Nothing -> tt [txt $ "<<" ++ k ++ ">>"]
Just v -> v
xmlSubst :: XMLEnv -> [XML] -> [XML]
xmlSubst env = everywhere (mkT (xmlvarSubst env))
tryfilter = replace "%s" "QUERY
" . replace "?" "?
" . eat "http://" . eat "http://www."
items2table items = matrix2table$ [[bb [txt "Keyword "]], [bb [txt "URL"]]] : pairs
where pairs = [[[txt a], [txt$ tryfilter b]] | (a:b:_) <- (map (split ',') items)]
getLink :: Document -> [Elem]
getLink = everything (++) ([] `mkQ` lnkMatcher)
where lnkMatcher :: Elem -> [Elem]
lnkMatcher e@(Lnk _ _) = [e]
lnkMatcher _ = []
-- bullet = txt "∘" --ring operator or composition dot
bullet = txt "•" -- => bullet = txt "•" -- black bullet
--bullet = txt "○" -- white circle
linkLike :: FilePath -> [String] -> String -> IO [XML]
linkLike root ortags link = do
cnts <- readFile (root++"links/"++link++".tags")
let linktags = filter (/="") $ lines cnts -- (\x -> x /= "" && (not $ isLower $ head x)) $ lines cnts
let cond = any (`elem` ortags) linktags ||
(filter (not . isLower . head) ortags == [] && filter (not . isLower . head) linktags == [])
if cond
then do cs <- readFile (root++"links/"++link++".txt")
let d = doc cs
let ls = getLink d
return $ if length ls > 0 then
[lnk ("/index.cgi?action=edit&page=links/"++link) [bullet], txt " ", elemToXML $ head ls, br]
else []
else return []
linksLike :: FilePath -> [String] -> IO [XML]
linksLike root tags = do
files <- getDirectoryContents (root ++ "/links")
let possible_links = map (replace ".tags" "") $ filter (`contains` ".tags") files
links <- mapM (linkLike root tags) possible_links
return$ concat links
getNewOTP :: Int -> Bool -> IO String
getNewOTP secs needed = do
if not needed then return ""
else do
pw <- otpNew secs
return pw
getXmlEnv pageName root newPage isLogin loggedin = do
let pageFilename = root++pageName++".txt"
-- cnts <- readFile "/scripts/search.js"
-- let items = tail $ takeWhile (/="//END")
-- $ dropWhile (/="//BEGIN")
-- $ lines
-- $ filter (/= '"') cnts
allstr <- readFile (root++"tags.txt")
let alltags = lines allstr
--let tagstrs = unlines alltagstr --zip (map show [1..22]) (map odd [1..22])
fileExists <- doesFileExist (root++pageName++".tags")
somestr <- if fileExists then readFile (root++pageName++".tags") else return ""
let sometags = filter (/="") $ lines somestr
let tags = zip alltags (map (`elem` sometags) alltags)
editpageform <- editform tags pageName pageFilename newPage
links <- linksLike root sometags
otp <- getNewOTP 30 isLogin
return [("searchbar", searchbar_form)
,("loginform", loginform pageName otp)
-- ,("searchhelp", items2table items)
,("editpage", editpageform)
,("renamepage", renamepageform pageName)
,("numlinks", txt $ show (length links `div` 4))
,("buttons", spann (buttons loggedin pageName))
,("links", pp links) --(map (\x->"li" `around` [x]) links))
]
spann = around "span"
textarea cnts = elm "textarea" ["name"-->"content", "cols"-->"72", "rows"-->"20"] [txt $ cnts]
maketagcheckbox ("", _) = [spacePad]
maketagcheckbox (label, checked) = ["input" `with` checkedAtt++["type"-->"checkbox",
"name"-->("TAG_"++(tagencode label))],
txt (' ':label), spacePad, spacePad]
where checkedAtt = if checked then ["checked"-->"on"] else []
tagcheckboxtable labels = matrix2table rows
where n = length labels
n' = numrows*numcols
labels' = labels ++ replicate (n'-n) empty
empty = ("", False)
numrows = (n+numcols-1) `div` numcols
numcols = 3
cols = let f [] = []
f xs = let (x,rest) = splitAt numrows xs
in x : f rest
in f $ map maketagcheckbox labels
rows = transpose cols
renamepageform oldpagename =
elm "form" ["method"-->"post", "id"-->"loginform", "name"-->"f",
"action"-->("/index.cgi?action=movepage&page="++oldpagename)]
([matrix2tableClass "horiz" [[ [txt "Old page name "],[txt oldpagename] ] ,
[ [txt "New page name "],[xnewpage] ] ,
[ [ ] ,[xsubmit ] ]] ]) -- ++ xhiddens)
where xnewpage = "input" `with` ["size"-->"40", "name"-->"newpagename", "type"-->"text"]
xsubmit = "input" `with` ["type"-->"submit", "value"-->"Rename page"]
-- ==> ["a" `with` ["name"-->"Top"] ]
editform tags page filename newPage = do
-- let (newPage, newtitle) = case newpagetitle of { Nothing -> (False, "") ; Just a -> (True, a) }
cnts <- if newPage then return "" else do { cnts' <- readFile filename; return $ fixEndingNewlines cnts' }
let editTitleLine = [ txt page, spacePad, spacePad, spacePad, spacePad, spacePad, spacePad,
elm "a" ["style"-->"font-size: 68%;",
"href"-->("/index.cgi?action=rename&page="++page)] [txt "(Rename)"] ]
let addTitleLine = [ xtitle ]
let firstLine = if newPage then addTitleLine else editTitleLine
-- let targetpage = if newPage then page else newtitle
return $ elm "form" ["method"-->"post", "id"-->"loginform", "name"-->"f",
"action"-->("/index.cgi?action=modify")]
([matrix2tableClass "horiz"
[[ firstLine
],
[ [textarea cnts] ],
-- [ [bb [txt "Tags"]] ],
-- [ xtags ],
[ [bb [txt "Tags"], spacePad, spacePad, spacePad, xtagsbox] ],
[ [xsubmit,
spacePad, spacePad, spacePad, spacePad, spacePad, spacePad,
spacePad, spacePad, spacePad, spacePad, spacePad, spacePad,
aa ("/index.cgi?"++page) "Cancel"] ]
]
] ++ xhiddens)
where xtitle = "input" `with` ["size"-->"20", "name"-->"page", "type"-->"text",
"value"-->page]
xtagsbox = divv "statesautocomplete" [ xtagsinput, divv "statescontainer" [] ]
xtagsinput = "input" `with` ["size"-->"65", "id"-->"tagsbox", "name"-->"tagsbox", "type"-->"text",
"value"--> xtagsstr]
-- xtagsbox = "input" `with` ["size"-->"65", "name"-->"tagsbox", "type"-->"text",
-- "value"--> xtagsstr]
xtagsstr = concat $ intersperse ", " $ sort $ filter (/="") keepers
where keepers = map (\(x,checked) -> if checked then x else "") tags
xsubmit = "input" `with` ["type"-->"submit", "value"-->"Submit these changes"]
--xcancel = "input" `with` ["type"-->"button", "value"-->"Cancel",
-- "onClick"-->("/index.cgi?"++page)]
xhiddens = [ hiddeninput "action" "modify",
hiddeninput "page" page ]
xtags = [ tagcheckboxtable tags ]
divv id x = Elm "div" ["id"-->id] x
-- find holes in the document
findVarSubsts :: Document -> [String]
findVarSubsts = everything (++) ([] `mkQ` varIntMatcher)
where varIntMatcher :: Elem -> [String]
varIntMatcher (VarInt es) = [stringize es]
varIntMatcher _ = []
lmap :: (Eq a) => [(a,b)] -> [a] -> [b]
lmap env xs = catMaybes (map (\k-> lookup k env) xs)
-- find the first Head4 for use as the Title
findHead4sSubsts :: Document -> [String]
findHead4sSubsts = everything (++) ([] `mkQ` head4Matcher)
where head4Matcher :: TopElem -> [String]
head4Matcher (SpecPar Head4 es) = [stringize es]
head4Matcher _ = []
-- certain widgets need certain javascript files loaded (these
-- javascripts are also responsible to move the cursor where they want
-- it in a startup() function
widgetsScriptsEnv =
[("searchbar", [script "/scripts/search.js"])
,("editpage", [script "http://yui.yahooapis.com/2.3.1/build/yahoo-dom-event/yahoo-dom-event.js?_yuiversion=2.3.1",
script "http://yui.yahooapis.com/2.3.1/build/animation/animation.js?_yuiversion=2.3.1",
script "http://yui.yahooapis.com/2.3.1/build/autocomplete/autocomplete-min.js?_yuiversion=2.3.1",
linkcss "http://yui.yahooapis.com/2.3.1/build/fonts/fonts-min.css?_yuiversion=2.3.1",
linkcss "http://yui.yahooapis.com/2.3.1/build/autocomplete/assets/skins/sam/autocomplete.css?_yuiversion=2.3.1",
script "2data.py"
])
,("loginform", [script "/scripts/login.js"])
,("renamepage", [script "/scripts/rename.js"])
]
noTimeDiff = TimeDiff { tdYear = 0, tdMonth = 0, tdDay = 0, tdHour = 0, tdMin = 0, tdSec = 0, tdPicosec = 0 }
--- MAIN I/O -----------------------------------
navigation loggedin root = do
let pageName = "navigation"
leftstr <- readFile (root++"navigation.txt")
let navigation = doc2xml $ doc leftstr
xmlEnv <- getXmlEnv pageName root False False loggedin
let left = xmlSubst xmlEnv (navigation) -- ++ links)
let leftString = xmlToStr left
--return leftString
putStrLn leftString
template loggedin messagestr pageName root filename = do
mytemplate <- readFile "/home/public/template.html"
--titlestr <- readFile (root++"titles.txt")
--let possibletitles = filter (/="") (lines titlestr)
--rint <- (randomIO :: IO Int)
--let rindex = rint `mod` (length possibletitles)
--let atitle = possibletitles !! rindex
leftstr <- readFile (root++"navigation.txt")
let navigation = doc2xml $ doc leftstr
--linkstr <- readFile (root++"links.txt")
--let links = doc2xml $ doc linkstr
let newPage = filename `endsWith` "add.txt"
let isLogin = filename `endsWith` "login.txt"
time' <- getClockTime
let time = addToClockTime (noTimeDiff { tdHour = -8 }) time'
ctime <- toCalendarTime time
-- let CalendarTime { ctHour = utchour } = ctime'
-- let ctime = ctime' -- { ctHour = (utchour+16) `mod` 24 } -- PST is -8 hours
-- --let datestr = formatCalendarTime defaultTimeLocale "%Y%m%d%H%M%S" ctime
let datestr1 = formatCalendarTime defaultTimeLocale "%A, " ctime
let datestr2 = formatCalendarTime defaultTimeLocale "%d %B %Y" ctime
-- chop leading zero off day of the month number
let datestr = datestr1 ++ (if head datestr2 == '0' then tail datestr2 else datestr2)
let env = ("date", Txt datestr):defaultEnv
let quote = "Quote me as saying I was mis-quoted."
let author = "Groucho Marx"
-- read in the input file or stdin if filename is - or lop off - if there are more characters and use the tail as the string, i.e. -text data
s <- if filename=="-" then getContents else (if head filename == '-' then return (tail filename) else readFile filename)
let d = subst env $ doc s
xmlEnv <- getXmlEnv pageName root newPage isLogin loggedin
let xs = xmlSubst xmlEnv (doc2xml d) -- get rid of all holes
let xmlString = xmlToStr xs
let left = xmlSubst xmlEnv (navigation) -- ++ links)
let leftString = xmlToStr left
let message = if messagestr /= "" then [ppClass "message" [aa ("/articles/"++pageName) "X", txt " ", txt messagestr]] else []
let messagefinal = xmlToStr message
let body = hbody ["onload"-->"startup();"]
([pageLayout loggedin {-atitle-} quote author left message pageName xs] ++ (if sIFRuse then [sIFR] else []))
let somescripts = concat $ lmap widgetsScriptsEnv (findVarSubsts d)
let somescriptstr = xmlToStr somescripts
let title_maybe = findHead4sSubsts d
let title = if title_maybe == [] then pageName else head title_maybe
let page = xhtmlPage title "css/alpha.css" (scripts somescripts) body
-- let buttonstr = xmlToStr $ buttons loggedin pageName
--putStrLn $ page
putStrLn $ templateReplace mytemplate -- ("@buttons", buttonstr)
[ ("@content", messagefinal ++ xmlString)
, ("@navigation", leftString)
, ("@title", title)
, ("@scripts", somescriptstr)
]
templateReplace templ pairs = unlines . map doReplace . lines $ templ
where
doReplace x@('@':rest) = elookup x pairs
doReplace x = x
elookup a kv = case lookup a kv of
Nothing -> ""
Just b -> b
{-
templateReplace templ [] = templ
templateReplace templ ((a,b):rest) = templateReplace (replace a b templ) rest
-}
-- main = template False "" "-"
-- tester = template False "" "/home/jared/alpha/login.txt"