diff --git a/hledger-web/Hledger/Web/App.hs b/hledger-web/Hledger/Web/App.hs index 207f26e05..074edffe5 100644 --- a/hledger-web/Hledger/Web/App.hs +++ b/hledger-web/Hledger/Web/App.hs @@ -1,4 +1,4 @@ -{-# LANGUAGE CPP, QuasiQuotes, TemplateHaskell, TypeFamilies, GeneralizedNewtypeDeriving #-} +{-# LANGUAGE CPP, QuasiQuotes, TemplateHaskell, TypeFamilies, GeneralizedNewtypeDeriving, OverloadedStrings #-} {-# LANGUAGE TypeSynonymInstances, FlexibleInstances, MultiParamTypeClasses, FlexibleContexts #-} {-| The web app providing a richer interface to hledger's data. @@ -11,11 +11,14 @@ module Hledger.Web.App where import Control.Applicative ((<$>), (<*>)) -- import Control.Failure --- import qualified Data.ByteString.Lazy as L +import qualified Data.ByteString.Lazy as L import Data.Either --- import System.Directory +-- import qualified Data.Text as T +import Data.Text(Text,pack,unpack) +import System.Directory import System.FilePath ((>), takeFileName) import System.IO.Storage (putValue, getValue) +import Text.Jasmine (minifym) import Text.ParserCombinators.Parsec hiding (string) -- import Database.Persist.GenericSql (ConnectionPool, SqlPersist, runMigration, migrate) @@ -37,24 +40,24 @@ import Hledger.Cli.Version (version) import Hledger.Data hiding (insert, today) import Hledger.Read (journalFromPathAndString) import Hledger.Read.JournalReader (someamount) -import Hledger.Web.Settings ( - -- withConnectionPool - -- , runConnectionPool - -- , staticroot - datadir - -- , hamletFile - -- , cassiusFile - -- , juliusFile - , hledgerorgurl - , manualurl - , style_css - , hledger_js - , jquery_js - , jquery_url_js - , dhtmlxcommon_js - , dhtmlxcombo_js - , robots_txt - ) +import Hledger.Web.Settings + -- -- withConnectionPool + -- -- , runConnectionPool + -- -- , staticroot + -- datadir + -- -- , hamletFile + -- -- , cassiusFile + -- -- , juliusFile + -- -- , hledgerorgurl + -- , manualurl + -- -- , style_css + -- -- , hledger_js + -- -- , jquery_js + -- -- , jquery_url_js + -- -- , dhtmlxcommon_js + -- -- , dhtmlxcombo_js + -- , robots_txt + -- ) ---------------------------------------------------------------------- @@ -80,12 +83,12 @@ import Hledger.Web.Settings ( -- run-time data kept by the web app. data App = App { -- appConnPool :: Maybe ConnectionPool - appRoot :: String + appRoot :: Text ,appDataDir :: FilePath ,appOpts :: [Opt] ,appArgs :: [String] + ,appStaticSettings :: Static ,appJournal :: Journal - ,appStatic :: Static } -- parseRoutes (quasi-quoter from web-routes) defines a list of route patterns for the web app. @@ -93,9 +96,9 @@ data App = App -- /auth AuthR Auth getAuth mkYesod "App" [$parseRoutes| / IndexR GET +/static StaticR Static appStaticSettings /favicon.ico FaviconR GET /robots.txt RobotsR GET -/static StaticR Static appStatic /journalonly JournalOnlyR GET POST /registeronly RegisterOnlyR GET /accounts AccountsOnlyR GET @@ -108,6 +111,24 @@ type Handler = GHandler App App instance Yesod App where approot = appRoot + -- This function creates static content files in the static folder + -- and names them based on a hash of their content. This allows + -- expiration dates to be set far in the future without worry of + -- users receiving stale content. + addStaticContent ext' _ content = do + let fn = base64md5 content ++ '.' : unpack ext' + let content' = + if ext' == "js" + then case minifym content of + Left _ -> content + Right y -> y + else content + let statictmp = Hledger.Web.Settings.datadir ++ "/tmp/" + liftIO $ createDirectoryIfMissing True statictmp + let fn' = statictmp ++ fn + exists <- liftIO $ doesFileExist fn' + unless exists $ liftIO $ L.writeFile fn' content' + return $ Just $ Right (StaticR $ StaticRoute ["tmp", pack fn] [], []) -- defaultLayout widget = do -- mmsg <- getMessage @@ -230,6 +251,7 @@ withApp app f = toWaiApp app >>= f -- handler utilities, common templates ---------------------------------------------------------------------- +nulltemplate :: Hamlet AppRoute nulltemplate = [$hamlet||] -- | A bundle of useful data passed to templates. @@ -243,6 +265,7 @@ data TemplateData = TD { ,today :: Day -- ^ the current day } +mktd :: TemplateData mktd = TD { here = IndexR ,title = "hledger" @@ -260,11 +283,11 @@ mktd = TD { -- message if any. getHandlerData :: Handler (String, String, [Opt], FilterSpec, Journal, Maybe Html, AppRoute) getHandlerData = do - Just here <- getCurrentRoute + Just here' <- getCurrentRoute (a, p, opts, fspec) <- getReportParameters (j, err) <- getLatestJournal opts msg <- getMessage' err - return (a, p, opts, fspec, j, msg, here) + return (a, p, opts, fspec, j, msg, here') where -- | Get current report parameters for this request. getReportParameters :: Handler (String, String, [Opt], FilterSpec) @@ -273,10 +296,11 @@ getHandlerData = do t <- liftIO $ getCurrentLocalTime a <- fromMaybe "" <$> lookupGetParam "a" p <- fromMaybe "" <$> lookupGetParam "p" - let opts = appOpts app ++ [Period p] - args = appArgs app ++ words' a + let (a',p') = (unpack a, unpack p) + opts = appOpts app ++ [Period p'] + args = appArgs app ++ words' a' fspec = optsToFilterSpec opts args t - return (a, p, opts, fspec) + return (a', p', opts, fspec) -- | Quote-sensitive words, ie don't split on spaces which are inside quotes. words' :: String -> [String] @@ -297,52 +321,52 @@ getHandlerData = do else case jE of Right j' -> do liftIO $ putValue "hledger" "journal" j' return (j',Nothing) - Left e -> do setMessage $ string "error while reading" {- ++ ": " ++ e-} + Left e -> do setMessage $ "error while reading" {- ++ ": " ++ e-} return (j, Just e) -- | Helper to work around a yesod feature (can't set and get a message in the same request.) getMessage' :: Maybe String -> Handler (Maybe Html) getMessage' newmsgstr = do oldmsg <- getMessage - return $ maybe oldmsg (Just . string) newmsgstr + return $ maybe oldmsg (Just . toHtml) newmsgstr -- | Wrap a template with the standard hledger web ui page layout. pageLayout :: TemplateData -> Hamlet AppRoute -> Hamlet AppRoute pageLayout td@TD{title=basetitle, msg=msg, p=p, j=j, today=today} content = [$hamlet| !!! -%html - %head - %title $title'$ - %meta!http-equiv=Content-Type!content=$metacontent$ - %script!type=text/javascript!src=@StaticR.jquery_js@ - %script!type=text/javascript!src=@StaticR.jquery_url_js@ - %script!type=text/javascript!src=@StaticR.dhtmlxcommon_js@ - %script!type=text/javascript!src=@StaticR.dhtmlxcombo_js@ - %script!type=text/javascript!src=@StaticR.hledger_js@ - %link!rel=stylesheet!type=text/css!media=all!href=@StaticR.style_css@ - %body - ^navbar.td^ - #messages $m$ - #content - ^content^ +#{title'} + #{m} +
 #{txn}
      |] where
-       evenodd = if even n then "even" else "odd"
+       evenodd = if even n then "even" else "odd" :: String
        txn = trimnl $ showTransaction t where trimnl = reverse . dropWhile (=='\n') . reverse
 
 addform :: TemplateData -> Hamlet AppRoute
 addform td = [$hamlet|
-%script!type=text/javascript
- $$(document).ready(function() {
+