web: upgrade to yesod 0.8
This commit is contained in:
		
							parent
							
								
									883bc240c9
								
							
						
					
					
						commit
						024cfdb7b1
					
				| @ -1,4 +1,4 @@ | |||||||
| {-# LANGUAGE CPP, QuasiQuotes, TemplateHaskell, TypeFamilies, GeneralizedNewtypeDeriving #-} | {-# LANGUAGE CPP, QuasiQuotes, TemplateHaskell, TypeFamilies, GeneralizedNewtypeDeriving, OverloadedStrings #-} | ||||||
| {-# LANGUAGE TypeSynonymInstances, FlexibleInstances, MultiParamTypeClasses, FlexibleContexts #-} | {-# LANGUAGE TypeSynonymInstances, FlexibleInstances, MultiParamTypeClasses, FlexibleContexts #-} | ||||||
| {-|  | {-|  | ||||||
| The web app providing a richer interface to hledger's data. | The web app providing a richer interface to hledger's data. | ||||||
| @ -11,11 +11,14 @@ module Hledger.Web.App | |||||||
| where | where | ||||||
| import Control.Applicative ((<$>), (<*>)) | import Control.Applicative ((<$>), (<*>)) | ||||||
| -- import Control.Failure | -- import Control.Failure | ||||||
| -- import qualified Data.ByteString.Lazy as L | import qualified Data.ByteString.Lazy as L | ||||||
| import Data.Either | 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.FilePath ((</>), takeFileName) | ||||||
| import System.IO.Storage (putValue, getValue) | import System.IO.Storage (putValue, getValue) | ||||||
|  | import Text.Jasmine (minifym) | ||||||
| import Text.ParserCombinators.Parsec hiding (string) | import Text.ParserCombinators.Parsec hiding (string) | ||||||
| 
 | 
 | ||||||
| -- import Database.Persist.GenericSql (ConnectionPool, SqlPersist, runMigration, migrate) | -- 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.Data hiding (insert, today) | ||||||
| import Hledger.Read (journalFromPathAndString) | import Hledger.Read (journalFromPathAndString) | ||||||
| import Hledger.Read.JournalReader (someamount) | import Hledger.Read.JournalReader (someamount) | ||||||
| import Hledger.Web.Settings ( | import Hledger.Web.Settings | ||||||
|     --   withConnectionPool |     -- --   withConnectionPool | ||||||
|     -- , runConnectionPool |     -- -- , runConnectionPool | ||||||
|     -- , staticroot |     -- -- , staticroot | ||||||
|       datadir |     --   datadir | ||||||
|     -- , hamletFile |     -- -- , hamletFile | ||||||
|     -- , cassiusFile |     -- -- , cassiusFile | ||||||
|     -- , juliusFile |     -- -- , juliusFile | ||||||
|     , hledgerorgurl |     -- -- , hledgerorgurl | ||||||
|     , manualurl |     -- , manualurl | ||||||
|     , style_css |     -- -- , style_css | ||||||
|     , hledger_js |     -- -- , hledger_js | ||||||
|     , jquery_js |     -- -- , jquery_js | ||||||
|     , jquery_url_js |     -- -- , jquery_url_js | ||||||
|     , dhtmlxcommon_js |     -- -- , dhtmlxcommon_js | ||||||
|     , dhtmlxcombo_js |     -- -- , dhtmlxcombo_js | ||||||
|     , robots_txt |     -- , robots_txt | ||||||
|     ) |     -- ) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| ---------------------------------------------------------------------- | ---------------------------------------------------------------------- | ||||||
| @ -80,12 +83,12 @@ import Hledger.Web.Settings ( | |||||||
| -- run-time data kept by the web app. | -- run-time data kept by the web app. | ||||||
| data App = App | data App = App | ||||||
|     { -- appConnPool :: Maybe ConnectionPool |     { -- appConnPool :: Maybe ConnectionPool | ||||||
|      appRoot    :: String |      appRoot    :: Text | ||||||
|     ,appDataDir :: FilePath |     ,appDataDir :: FilePath | ||||||
|     ,appOpts    :: [Opt] |     ,appOpts    :: [Opt] | ||||||
|     ,appArgs    :: [String] |     ,appArgs    :: [String] | ||||||
|  |     ,appStaticSettings :: Static | ||||||
|     ,appJournal :: Journal |     ,appJournal :: Journal | ||||||
|     ,appStatic  :: Static |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| -- parseRoutes (quasi-quoter from web-routes) defines a list of route patterns for the web app. | -- 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 | -- /auth            AuthR             Auth getAuth | ||||||
| mkYesod "App" [$parseRoutes| | mkYesod "App" [$parseRoutes| | ||||||
| /                IndexR            GET | /                IndexR            GET | ||||||
|  | /static          StaticR           Static appStaticSettings | ||||||
| /favicon.ico     FaviconR          GET | /favicon.ico     FaviconR          GET | ||||||
| /robots.txt      RobotsR           GET | /robots.txt      RobotsR           GET | ||||||
| /static          StaticR           Static appStatic |  | ||||||
| /journalonly     JournalOnlyR      GET POST | /journalonly     JournalOnlyR      GET POST | ||||||
| /registeronly    RegisterOnlyR     GET | /registeronly    RegisterOnlyR     GET | ||||||
| /accounts        AccountsOnlyR     GET | /accounts        AccountsOnlyR     GET | ||||||
| @ -108,6 +111,24 @@ type Handler = GHandler App App | |||||||
| 
 | 
 | ||||||
| instance Yesod App where | instance Yesod App where | ||||||
|     approot = appRoot |     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 |     -- defaultLayout widget = do | ||||||
|     --     mmsg <- getMessage |     --     mmsg <- getMessage | ||||||
| @ -230,6 +251,7 @@ withApp app f = toWaiApp app >>= f | |||||||
| -- handler utilities, common templates | -- handler utilities, common templates | ||||||
| ---------------------------------------------------------------------- | ---------------------------------------------------------------------- | ||||||
| 
 | 
 | ||||||
|  | nulltemplate :: Hamlet AppRoute | ||||||
| nulltemplate = [$hamlet||] | nulltemplate = [$hamlet||] | ||||||
| 
 | 
 | ||||||
| -- | A bundle of useful data passed to templates. | -- | A bundle of useful data passed to templates. | ||||||
| @ -243,6 +265,7 @@ data TemplateData = TD { | |||||||
|     ,today        :: Day                -- ^ the current day |     ,today        :: Day                -- ^ the current day | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | mktd :: TemplateData | ||||||
| mktd = TD { | mktd = TD { | ||||||
|       here = IndexR |       here = IndexR | ||||||
|      ,title = "hledger" |      ,title = "hledger" | ||||||
| @ -260,11 +283,11 @@ mktd = TD { | |||||||
| -- message if any. | -- message if any. | ||||||
| getHandlerData :: Handler (String, String, [Opt], FilterSpec, Journal, Maybe Html, AppRoute) | getHandlerData :: Handler (String, String, [Opt], FilterSpec, Journal, Maybe Html, AppRoute) | ||||||
| getHandlerData = do | getHandlerData = do | ||||||
|   Just here <- getCurrentRoute |   Just here' <- getCurrentRoute | ||||||
|   (a, p, opts, fspec) <- getReportParameters |   (a, p, opts, fspec) <- getReportParameters | ||||||
|   (j, err) <- getLatestJournal opts |   (j, err) <- getLatestJournal opts | ||||||
|   msg <- getMessage' err |   msg <- getMessage' err | ||||||
|   return (a, p, opts, fspec, j, msg, here) |   return (a, p, opts, fspec, j, msg, here') | ||||||
|     where |     where | ||||||
|       -- | Get current report parameters for this request. |       -- | Get current report parameters for this request. | ||||||
|       getReportParameters :: Handler (String, String, [Opt], FilterSpec) |       getReportParameters :: Handler (String, String, [Opt], FilterSpec) | ||||||
| @ -273,10 +296,11 @@ getHandlerData = do | |||||||
|           t <- liftIO $ getCurrentLocalTime |           t <- liftIO $ getCurrentLocalTime | ||||||
|           a <- fromMaybe "" <$> lookupGetParam "a" |           a <- fromMaybe "" <$> lookupGetParam "a" | ||||||
|           p <- fromMaybe "" <$> lookupGetParam "p" |           p <- fromMaybe "" <$> lookupGetParam "p" | ||||||
|           let opts = appOpts app ++ [Period p] |           let (a',p') = (unpack a, unpack p) | ||||||
|               args = appArgs app ++ words' a |               opts = appOpts app ++ [Period p'] | ||||||
|  |               args = appArgs app ++ words' a' | ||||||
|               fspec = optsToFilterSpec opts args t |               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. |       -- | Quote-sensitive words, ie don't split on spaces which are inside quotes. | ||||||
|       words' :: String -> [String] |       words' :: String -> [String] | ||||||
| @ -297,52 +321,52 @@ getHandlerData = do | |||||||
|          else case jE of |          else case jE of | ||||||
|                 Right j' -> do liftIO $ putValue "hledger" "journal" j' |                 Right j' -> do liftIO $ putValue "hledger" "journal" j' | ||||||
|                                return (j',Nothing) |                                return (j',Nothing) | ||||||
|                 Left e  -> do setMessage $ string "error while reading" {- ++ ": " ++ e-} |                 Left e  -> do setMessage $ "error while reading" {- ++ ": " ++ e-} | ||||||
|                               return (j, Just e) |                               return (j, Just e) | ||||||
| 
 | 
 | ||||||
|       -- | Helper to work around a yesod feature (can't set and get a message in the same request.) |       -- | 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' :: Maybe String -> Handler (Maybe Html) | ||||||
|       getMessage' newmsgstr = do |       getMessage' newmsgstr = do | ||||||
|         oldmsg <- getMessage |         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. | -- | Wrap a template with the standard hledger web ui page layout. | ||||||
| pageLayout :: TemplateData -> Hamlet AppRoute -> Hamlet AppRoute | pageLayout :: TemplateData -> Hamlet AppRoute -> Hamlet AppRoute | ||||||
| pageLayout td@TD{title=basetitle, msg=msg, p=p, j=j, today=today} content = [$hamlet| | pageLayout td@TD{title=basetitle, msg=msg, p=p, j=j, today=today} content = [$hamlet| | ||||||
| !!! | !!! | ||||||
| %html | <html | ||||||
|  %head |  <head | ||||||
|   %title $title'$ |   <title>#{title'} | ||||||
|   %meta!http-equiv=Content-Type!content=$metacontent$ |   <meta http-equiv=Content-Type content=#{metacontent} | ||||||
|   %script!type=text/javascript!src=@StaticR.jquery_js@ |   <script type=text/javascript src=@{StaticR jquery_js} | ||||||
|   %script!type=text/javascript!src=@StaticR.jquery_url_js@ |   <script type=text/javascript src=@{StaticR jquery_url_js} | ||||||
|   %script!type=text/javascript!src=@StaticR.dhtmlxcommon_js@ |   <script type=text/javascript src=@{StaticR dhtmlxcommon_js} | ||||||
|   %script!type=text/javascript!src=@StaticR.dhtmlxcombo_js@ |   <script type=text/javascript src=@{StaticR dhtmlxcombo_js} | ||||||
|   %script!type=text/javascript!src=@StaticR.hledger_js@ |   <script type=text/javascript src=@{StaticR hledger_js} | ||||||
|   %link!rel=stylesheet!type=text/css!media=all!href=@StaticR.style_css@ |   <link rel=stylesheet type=text/css media=all href=@{StaticR style_css} | ||||||
|  %body |  <body | ||||||
|   ^navbar.td^ |   ^{navbar td} | ||||||
|   #messages $m$ |   <div#messages>#{m} | ||||||
|   #content |   <div#content | ||||||
|    ^content^ |    ^{content} | ||||||
| |] | |] | ||||||
|  where title' = basetitle ++ " - " ++ journaltitle |  where title' = basetitle ++ " - " ++ journaltitle | ||||||
|        (journaltitle, _) = journalTitleDesc j p today |        (journaltitle, _) = journalTitleDesc j p today | ||||||
|        metacontent = "text/html; charset=utf-8" |        metacontent = "text/html; charset=utf-8" :: String | ||||||
|        m = fromMaybe (string "") msg |        m = fromMaybe "" msg | ||||||
| 
 | 
 | ||||||
| -- | Global toolbar/heading area. | -- | Global toolbar/heading area. | ||||||
| navbar :: TemplateData -> Hamlet AppRoute | navbar :: TemplateData -> Hamlet AppRoute | ||||||
| navbar TD{p=p,j=j,today=today} = [$hamlet| | navbar TD{p=p,j=j,today=today} = [$hamlet| | ||||||
|  #navbar |  <div#navbar | ||||||
|   %a.topleftlink!href=$hledgerorgurl$ |   <a.topleftlink href=#{hledgerorgurl} | ||||||
|    hledger |    hledger | ||||||
|    <br /> |    <br /> | ||||||
|    $version$ |    #{version} | ||||||
|   %a.toprightlink!href=$manualurl$!target=hledgerhelp manual |   <a.toprightlink href=#{manualurl} target=hledgerhelp manual | ||||||
|   %h1 $title$ |   <h1>#{title} | ||||||
|   \ $ |   \ # | ||||||
|   %span#journaldesc $desc$ |   <span#journaldesc>#{desc} | ||||||
| |] | |] | ||||||
|   where (title, desc) = journalTitleDesc j p today |   where (title, desc) = journalTitleDesc j p today | ||||||
| 
 | 
 | ||||||
| @ -360,73 +384,73 @@ journalTitleDesc j p today = (title, desc) | |||||||
| -- | Links to the main views. | -- | Links to the main views. | ||||||
| navlinks :: TemplateData -> Hamlet AppRoute | navlinks :: TemplateData -> Hamlet AppRoute | ||||||
| navlinks td = [$hamlet| | navlinks td = [$hamlet| | ||||||
|  #navlinks |  <div#navlinks | ||||||
|   ^accountsjournallink^ |   ^{accountsjournallink} | ||||||
|   \ | $ |   \ | # | ||||||
|   ^accountsregisterlink^ |   ^{accountsregisterlink} | ||||||
|   \ | $ |   \ | # | ||||||
|   %a#addformlink!href!onclick="return addformToggle(event)" add transaction |   <a#addformlink href onclick="return addformToggle(event)">add transaction | ||||||
|   %a#importformlink!href!onclick="return importformToggle(event)"!style=display:none; import transactions |   <a#importformlink href onclick="return importformToggle(event)" style="display:none;">import transactions | ||||||
|   \ | $ |   \ | # | ||||||
|   %a#editformlink!href!onclick="return editformToggle(event)" edit journal |   <a#editformlink href onclick="return editformToggle(event)">edit journal | ||||||
| |] | |] | ||||||
| --  \ | $ | --  \ | # | ||||||
|  where |  where | ||||||
|    accountsjournallink  = navlink td "journal" JournalR |    accountsjournallink  = navlink td "journal" JournalR | ||||||
|    accountsregisterlink = navlink td "register" RegisterR |    accountsregisterlink = navlink td "register" RegisterR | ||||||
| 
 | 
 | ||||||
| navlink :: TemplateData -> String -> AppRoute -> Hamlet AppRoute | navlink :: TemplateData -> String -> AppRoute -> Hamlet AppRoute | ||||||
| navlink TD{here=here,a=a,p=p} s dest = [$hamlet|%a#$s$link.$style$!href=@?u@ $s$|] | navlink TD{here=here,a=a,p=p} s dest = [$hamlet|<a##{s}link.#{style} href=@?{u}>#{s}</a>|] | ||||||
|  where u = (dest, concat [(if null a then [] else [("a", a)]) |  where u = (dest, concat [(if null a then [] else [("a", pack a)]) | ||||||
|                          ,(if null p then [] else [("p", p)])]) |                          ,(if null p then [] else [("p", pack p)])]) | ||||||
|        style | dest == here = "navlinkcurrent" |        style | dest == here = "navlinkcurrent" | ||||||
|              | otherwise    = "navlink" |              | otherwise    = "navlink" :: Text | ||||||
| 
 | 
 | ||||||
| -- | Form controlling journal filtering parameters. | -- | Form controlling journal filtering parameters. | ||||||
| filterform :: TemplateData -> Hamlet AppRoute | filterform :: TemplateData -> Hamlet AppRoute | ||||||
| filterform TD{here=here,a=a,p=p} = [$hamlet| | filterform TD{here=here,a=a,p=p} = [$hamlet| | ||||||
|  #filterformdiv |  <div#filterformdiv | ||||||
|   %form#filterform.form!method=GET!style=display:$visible$; |   <form#filterform.form method=GET style=display:#{visible}; | ||||||
|    %table.form |    <table.form | ||||||
|     %tr.$filteringperiodclass$ |     <tr.#{filteringperiodclass} | ||||||
|      %td |      <td | ||||||
|       filter by period: |       filter by period: | ||||||
|       \ $ |       \ # | ||||||
|      %td |      <td | ||||||
|       %input!name=p!size=60!value=$p$ |       <input name=p size=60 value=#{p} | ||||||
|       ^phelp^ |       ^{phelp} | ||||||
|       \ $ |       \ # | ||||||
|      %td!align=right |      <td align=right | ||||||
|       ^stopfilteringperiod^ |       ^{stopfilteringperiod} | ||||||
|     %tr.$filteringclass$ |     <tr.#{filteringclass} | ||||||
|      %td |      <td | ||||||
|       filter by account/description: |       filter by account/description: | ||||||
|       \ $ |       \ # | ||||||
|      %td |      <td | ||||||
|       %input!name=a!size=60!value=$a$ |       <input name=a size=60 value=#{a} | ||||||
|       ^ahelp^ |       ^{ahelp} | ||||||
|       \ $ |       \ # | ||||||
|       %input!type=submit!value=filter $ |       <input type=submit value=filter # | ||||||
|       \ $ |       \ # | ||||||
|      %td!align=right |      <td align=right | ||||||
|       ^stopfiltering^ |       ^{stopfiltering} | ||||||
| |] | |] | ||||||
|  where |  where | ||||||
|   ahelp = helplink "filter-patterns" "?" |   ahelp = helplink "filter-patterns" "?" | ||||||
|   phelp = helplink "period-expressions" "?" |   phelp = helplink "period-expressions" "?" | ||||||
|   filtering = not $ null a |   filtering = not $ null a | ||||||
|   filteringperiod = not $ null p |   filteringperiod = not $ null p | ||||||
|   visible = "block" |   visible = "block" :: String | ||||||
|   filteringclass = if filtering then "filtering" else "" |   filteringclass = if filtering then "filtering" else "" :: String | ||||||
|   filteringperiodclass = if filteringperiod then "filtering" else "" |   filteringperiodclass = if filteringperiod then "filtering" else "" :: String | ||||||
|   stopfiltering = if filtering then [$hamlet|%a#stopfilterlink!href=@?u@ clear filter|] else nulltemplate |   stopfiltering = if filtering then [$hamlet|<a#stopfilterlink href=@?{u} clear filter|] else nulltemplate | ||||||
|       where u = (here, if filteringperiod then [("p", p)] else []) |       where u = (here, if filteringperiod then [("p", pack p)] else []) | ||||||
|   stopfilteringperiod = if filteringperiod then [$hamlet|%a#stopfilterlink!href=@?u@ clear filter|] else nulltemplate |   stopfilteringperiod = if filteringperiod then [$hamlet|<a#stopfilterlink href=@?{u} clear filter|] else nulltemplate | ||||||
|       where u = (here, if filtering then [("a", a)] else []) |       where u = (here, if filtering then [("a", pack a)] else []) | ||||||
| 
 | 
 | ||||||
| -- | Link to a topic in the manual. | -- | Link to a topic in the manual. | ||||||
| helplink :: String -> String -> Hamlet AppRoute | helplink :: String -> String -> Hamlet AppRoute | ||||||
| helplink topic label = [$hamlet|%a!href=$u$!target=hledgerhelp $label$|] | helplink topic label = [$hamlet|<a href=#{u} target=hledgerhelp>#{label}|] | ||||||
|     where u = manualurl ++ if null topic then "" else '#':topic |     where u = manualurl ++ if null topic then "" else '#':topic | ||||||
| 
 | 
 | ||||||
| {- | {- | ||||||
| @ -521,17 +545,17 @@ getJournalR = do | |||||||
|       td = mktd{here=here, title="hledger journal", msg=msg, a=a, p=p, j=j, today=today} |       td = mktd{here=here, title="hledger journal", msg=msg, a=a, p=p, j=j, today=today} | ||||||
|       editform' = editform td |       editform' = editform td | ||||||
|   hamletToRepHtml $ pageLayout td [$hamlet| |   hamletToRepHtml $ pageLayout td [$hamlet| | ||||||
|  %div#content |  <div#content | ||||||
|   %div#sidebar |   <div#sidebar | ||||||
|    ^sidecontent^ |    ^{sidecontent} | ||||||
|   %div#main.journal |   <div#main.journal | ||||||
|    ^navlinks.td^ |    ^{navlinks td} | ||||||
|    %div#transactions |    <div#transactions | ||||||
|     ^filterform.td^ |     ^{filterform td} | ||||||
|     ^maincontent^ |     ^{maincontent} | ||||||
|    ^addform.td^ |    ^{addform td} | ||||||
|    ^editform'^ |    ^{editform'} | ||||||
|    ^importform^ |    ^{importform} | ||||||
| |] | |] | ||||||
| 
 | 
 | ||||||
| postJournalR :: Handler RepPlain | postJournalR :: Handler RepPlain | ||||||
| @ -554,17 +578,17 @@ getRegisterR = do | |||||||
|       td = mktd{here=here, title="hledger register", msg=msg, a=a, p=p, j=j, today=today} |       td = mktd{here=here, title="hledger register", msg=msg, a=a, p=p, j=j, today=today} | ||||||
|       editform' = editform td |       editform' = editform td | ||||||
|   hamletToRepHtml $ pageLayout td [$hamlet| |   hamletToRepHtml $ pageLayout td [$hamlet| | ||||||
|  %div#content |  <div#content | ||||||
|   %div#sidebar |   <div#sidebar | ||||||
|    ^sidecontent^ |    ^{sidecontent} | ||||||
|   %div#main.journal |   <div#main.journal | ||||||
|    ^navlinks.td^ |    ^{navlinks td} | ||||||
|    %div#transactions |    <div#transactions | ||||||
|     ^filterform.td^ |     ^{filterform td} | ||||||
|     ^maincontent^ |     ^{maincontent} | ||||||
|    ^addform.td^ |    ^{addform td} | ||||||
|    ^editform'^ |    ^{editform'} | ||||||
|    ^importform^ |    ^{importform} | ||||||
| |] | |] | ||||||
| 
 | 
 | ||||||
| postRegisterR :: Handler RepPlain | postRegisterR :: Handler RepPlain | ||||||
| @ -583,57 +607,57 @@ getAccountsOnlyR = do | |||||||
| -- | Render a balance report as HTML. | -- | Render a balance report as HTML. | ||||||
| balanceReportAsHtml :: [Opt] -> TemplateData -> BalanceReport -> Hamlet AppRoute | balanceReportAsHtml :: [Opt] -> TemplateData -> BalanceReport -> Hamlet AppRoute | ||||||
| balanceReportAsHtml _ td@TD{here=here,a=a,p=p} (items,total) = [$hamlet| | balanceReportAsHtml _ td@TD{here=here,a=a,p=p} (items,total) = [$hamlet| | ||||||
| ^accountsheading^ | ^{accountsheading} | ||||||
| %table.balancereport | <table.balancereport> | ||||||
|  $forall items i |  $forall i <- items | ||||||
|   ^itemAsHtml' i^ |   ^{itemAsHtml' i} | ||||||
|  %tr.totalrule |  <tr.totalrule> | ||||||
|   %td!colspan=2 |   <td colspan=2> | ||||||
|  %tr |  <tr> | ||||||
|   %td |   <td> | ||||||
|   %td!align=right $mixedAmountAsHtml.total$ |   <td align=right>#{mixedAmountAsHtml total} > | ||||||
| |] | |] | ||||||
|  where |  where | ||||||
|    accountsheading = [$hamlet| |    accountsheading = [$hamlet| | ||||||
|                       #accountsheading |                       <div#accountsheading | ||||||
|                        accounts |                        accounts | ||||||
|                        \ $ |                        \ # | ||||||
|                        ^showlinks^ |                        ^{showlinks} | ||||||
|                       |] |                       |] :: Hamlet AppRoute | ||||||
|        where |        where | ||||||
|          filteringaccts = not $ null a |          filteringaccts = not $ null a | ||||||
|          showlinks = [$hamlet|%span#showmoreaccounts ^showmore^ ^showall^|] |          showlinks = [$hamlet|<span#showmoreaccounts ^{showmore} ^{showall}|] :: Hamlet AppRoute | ||||||
|          showmore = case (filteringaccts, items) of |          showmore = case (filteringaccts, items) of | ||||||
|                       -- cunning parent account logic |                       -- cunning parent account logic | ||||||
|                       (True, ((acct, _, _, _):_)) -> |                       (True, ((acct, _, _, _):_)) -> | ||||||
|                           let a' = if isAccountRegex a then a else acct |                           let a' = if isAccountRegex a then a else acct | ||||||
|                               a'' = accountNameToAccountRegex $ parentAccountName $ accountRegexToAccountName a' |                               a'' = accountNameToAccountRegex $ parentAccountName $ accountRegexToAccountName a' | ||||||
|                               parenturl = (here, [("a",a''), ("p",p)]) |                               parenturl = (here, [("a",pack a''), ("p",pack p)]) | ||||||
|                           in [$hamlet| |                           in [$hamlet| | ||||||
|                               \ | $ |                               \ | # | ||||||
|                               %a!href=@?parenturl@ show more ↑ |                               <a href=@?{parenturl} show more ↑ | ||||||
|                               |] |                               |] | ||||||
|                       _ -> nulltemplate |                       _ -> nulltemplate | ||||||
|          showall = if filteringaccts |          showall = if filteringaccts | ||||||
|                     then [$hamlet| |                     then [$hamlet| | ||||||
|                           \ | $ |                           \ | # | ||||||
|                           %a!href=@?allurl@ show all |                           <a href=@?{allurl} show all | ||||||
|                           |] |                           |] | ||||||
|                     else nulltemplate |                     else nulltemplate | ||||||
|              where allurl = (here, [("p",p)]) |              where allurl = (here, [("p",pack p)]) | ||||||
|    itemAsHtml' = itemAsHtml td |    itemAsHtml' = itemAsHtml td | ||||||
|    itemAsHtml :: TemplateData -> BalanceReportItem -> Hamlet AppRoute |    itemAsHtml :: TemplateData -> BalanceReportItem -> Hamlet AppRoute | ||||||
|    itemAsHtml TD{p=p} (acct, adisplay, adepth, abal) = [$hamlet| |    itemAsHtml TD{p=p} (acct, adisplay, adepth, abal) = [$hamlet| | ||||||
|      %tr.item |      <tr.item | ||||||
|       %td.account |       <td.account | ||||||
|        $indent$ |        #{indent} | ||||||
|        %a!href=$aurl$ $adisplay$ |        <a href=#{aurl}>#{adisplay} | ||||||
|       %td.balance!align=right $mixedAmountAsHtml.abal$ |       <td.balance align=right>#{mixedAmountAsHtml abal} | ||||||
|      |] where |      |] where | ||||||
|        -- current = if not (null a) && containsRegex a acct then "current" else "" |        -- current = if not (null a) && containsRegex a acct then "current" else "" | ||||||
|        indent = preEscapedString $ concat $ replicate (2 * adepth) " " |        indent = preEscapedString $ concat $ replicate (2 * adepth) " " | ||||||
|        aurl = printf ".?a=%s%s" (accountNameToAccountRegex acct) p' :: String |        aurl = printf ".?a=%s%s" (accountNameToAccountRegex acct) p' :: String | ||||||
|        p' = if null p then "" else printf "&p=%s" p |        p' = if null p then "" else printf "&p=%s" p :: String | ||||||
| 
 | 
 | ||||||
| accountNameToAccountRegex :: String -> String | accountNameToAccountRegex :: String -> String | ||||||
| accountNameToAccountRegex "" = "" | accountNameToAccountRegex "" = "" | ||||||
| @ -656,39 +680,39 @@ getJournalOnlyR = do | |||||||
|       editform' = editform td |       editform' = editform td | ||||||
|       txns = journalReportAsHtml opts td $ journalReport opts fspec j |       txns = journalReportAsHtml opts td $ journalReport opts fspec j | ||||||
|   hamletToRepHtml $ pageLayout td [$hamlet| |   hamletToRepHtml $ pageLayout td [$hamlet| | ||||||
| %div#journal | <div#journal | ||||||
|  %div.nav2 |  <div.nav2 | ||||||
|   %a#addformlink!href!onclick="return addformToggle(event)" add one transaction |   <a#addformlink href onclick="return addformToggle(event)" add one transaction | ||||||
|   \ | $ |   \ | # | ||||||
|   %a#editformlink!href!onclick="return editformToggle(event)" edit the whole journal |   <a#editformlink href onclick="return editformToggle(event)" edit the whole journal | ||||||
|  #transactions ^txns^ |  <div#transactions ^{txns} | ||||||
|  ^addform.td^ |  ^{addform td} | ||||||
|  ^editform'^ |  ^{editform'} | ||||||
| |] | |] | ||||||
| 
 | 
 | ||||||
| -- | Render a journal report as HTML. | -- | Render a journal report as HTML. | ||||||
| journalReportAsHtml :: [Opt] -> TemplateData -> JournalReport -> Hamlet AppRoute | journalReportAsHtml :: [Opt] -> TemplateData -> JournalReport -> Hamlet AppRoute | ||||||
| journalReportAsHtml _ td items = [$hamlet| | journalReportAsHtml _ td items = [$hamlet| | ||||||
| %table.journalreport | <table.journalreport> | ||||||
|  $forall number.items i |  $forall i <- number items | ||||||
|   ^itemAsHtml' i^ |   ^{itemAsHtml' i} | ||||||
| |] | |] | ||||||
|  where |  where | ||||||
|    number = zip [1..] |    number = zip [1..] | ||||||
|    itemAsHtml' = itemAsHtml td |    itemAsHtml' = itemAsHtml td | ||||||
|    itemAsHtml :: TemplateData -> (Int, JournalReportItem) -> Hamlet AppRoute |    itemAsHtml :: TemplateData -> (Int, JournalReportItem) -> Hamlet AppRoute | ||||||
|    itemAsHtml _ (n, t) = [$hamlet| |    itemAsHtml _ (n, t) = [$hamlet| | ||||||
|      %tr.item.$evenodd$ |      <tr.item.#{evenodd} > | ||||||
|       %td.transaction |       <td.transaction> | ||||||
|        %pre $txn$ |        <pre> #{txn} | ||||||
|      |] where |      |] 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 |        txn = trimnl $ showTransaction t where trimnl = reverse . dropWhile (=='\n') . reverse | ||||||
| 
 | 
 | ||||||
| addform :: TemplateData -> Hamlet AppRoute | addform :: TemplateData -> Hamlet AppRoute | ||||||
| addform td = [$hamlet| | addform td = [$hamlet| | ||||||
| %script!type=text/javascript | <script type=text/javascript> | ||||||
|  $$(document).ready(function() { |  $(document).ready(function() { | ||||||
|     /* dhtmlxcombo setup */ |     /* dhtmlxcombo setup */ | ||||||
|     window.dhx_globalImgPath="../static/"; |     window.dhx_globalImgPath="../static/"; | ||||||
|     var desccombo  = new dhtmlXCombo("description"); |     var desccombo  = new dhtmlXCombo("description"); | ||||||
| @ -703,50 +727,50 @@ addform td = [$hamlet| | |||||||
|     /* desccombo.enableOptionAutoHeight(true, 20); */ |     /* desccombo.enableOptionAutoHeight(true, 20); */ | ||||||
|     /* desccombo.setOptionHeight(200); */ |     /* desccombo.setOptionHeight(200); */ | ||||||
|  }); |  }); | ||||||
| %form#addform!method=POST!style=display:none; | <form#addform method=POST style=display:none; | ||||||
|   %table.form |   <table.form | ||||||
|    %tr |    <tr | ||||||
|     %td!colspan=4 |     <td colspan=4 | ||||||
|      %table |      <table | ||||||
|       %tr#descriptionrow |       <tr#descriptionrow | ||||||
|        %td |        <td | ||||||
|         Date: |         Date: | ||||||
|        %td |        <td | ||||||
|         %input.textinput!size=15!name=date!value=$date$ |         <input.textinput size=15 name=date value=#{date} | ||||||
|        %td!style=padding-left:1em; |        <td style=padding-left:1em; | ||||||
|         Description: |         Description: | ||||||
|        %td |        <td | ||||||
|         %select!id=description!name=description |         <select id=description name=description | ||||||
|          %option |          <option | ||||||
|          $forall descriptions d |          $forall d <- descriptions | ||||||
|           %option!value=$d$ $d$ |           <option value=#{d}>#{d} | ||||||
|       %tr.helprow |       <tr.helprow | ||||||
|        %td |        <td | ||||||
|        %td |        <td | ||||||
|         .help $datehelp$ $ |         <span.help>#{datehelp} # | ||||||
|        %td |        <td | ||||||
|        %td |        <td | ||||||
|         .help $deschelp$ |         <span.help>#{deschelp} | ||||||
|    ^postingsfields.td^ |    ^{postingsfields td} | ||||||
|    %tr#addbuttonrow |    <tr#addbuttonrow | ||||||
|     %td!colspan=4 |     <td colspan=4 | ||||||
|      %input!type=hidden!name=action!value=add |      <input type=hidden name=action value=add | ||||||
|      %input!type=submit!name=submit!value="add transaction" |      <input type=submit name=submit value="add transaction" | ||||||
|      $if manyfiles |      $if manyfiles | ||||||
|       \ to: ^journalselect.files.j.td^ |       \ to: ^{journalselect $ files $ j td} | ||||||
| |] | |] | ||||||
|  where |  where | ||||||
|   -- datehelplink = helplink "dates" "..." |   -- datehelplink = helplink "dates" "..." | ||||||
|   datehelp = "eg: 2010/7/20" |   datehelp = "eg: 2010/7/20" :: String | ||||||
|   deschelp = "eg: supermarket (optional)" |   deschelp = "eg: supermarket (optional)" :: String | ||||||
|   date = "today" |   date = "today" :: String | ||||||
|   descriptions = sort $ nub $ map tdescription $ jtxns $ j td |   descriptions = sort $ nub $ map tdescription $ jtxns $ j td | ||||||
|   manyfiles = (length $ files $ j td) > 1 |   manyfiles = (length $ files $ j td) > 1 | ||||||
| 
 | 
 | ||||||
| postingsfields :: TemplateData -> Hamlet AppRoute | postingsfields :: TemplateData -> Hamlet AppRoute | ||||||
| postingsfields td = [$hamlet| | postingsfields td = [$hamlet| | ||||||
|  ^p1^ |  ^{p1} | ||||||
|  ^p2^ |  ^{p2} | ||||||
| |] | |] | ||||||
|   where |   where | ||||||
|     p1 = postingfields td 1 |     p1 = postingfields td 1 | ||||||
| @ -754,21 +778,21 @@ postingsfields td = [$hamlet| | |||||||
| 
 | 
 | ||||||
| postingfields :: TemplateData -> Int -> Hamlet AppRoute | postingfields :: TemplateData -> Int -> Hamlet AppRoute | ||||||
| postingfields TD{j=j} n = [$hamlet| | postingfields TD{j=j} n = [$hamlet| | ||||||
|  %tr#postingrow |  <tr#postingrow | ||||||
|   %td!align=right $acctlabel$: |   <td align=right>#{acctlabel}: | ||||||
|   %td |   <td | ||||||
|    %select!id=$acctvar$!name=$acctvar$ |    <select id=#{acctvar} name=#{acctvar} | ||||||
|     %option |     <option | ||||||
|     $forall acctnames a |     $forall a <- acctnames | ||||||
|      %option!value=$a$ $a$ |      <option value=#{a}>#{a} | ||||||
|   ^amtfield^ |   ^{amtfield} | ||||||
|  %tr.helprow |  <tr.helprow | ||||||
|   %td |   <td | ||||||
|   %td |   <td | ||||||
|    .help $accthelp$ |    <span.help>#{accthelp} | ||||||
|   %td |   <td | ||||||
|   %td |   <td | ||||||
|    .help $amthelp$ |    <span.help>#{amthelp} | ||||||
| |] | |] | ||||||
|  where |  where | ||||||
|   numbered = (++ show n) |   numbered = (++ show n) | ||||||
| @ -779,41 +803,41 @@ postingfields TD{j=j} n = [$hamlet| | |||||||
|        | n == 1     = ("To account" |        | n == 1     = ("To account" | ||||||
|                      ,"eg: expenses:food" |                      ,"eg: expenses:food" | ||||||
|                      ,[$hamlet| |                      ,[$hamlet| | ||||||
|                        %td!style=padding-left:1em; |                        <td style=padding-left:1em; | ||||||
|                         Amount: |                         Amount: | ||||||
|                        %td |                        <td | ||||||
|                         %input.textinput!size=15!name=$amtvar$!value="" |                         <input.textinput size=15 name=#{amtvar} value="" | ||||||
|                        |] |                        |] | ||||||
|                      ,"eg: $6" |                      ,"eg: $6" | ||||||
|                      ) |                      ) | ||||||
|        | otherwise = ("From account" |        | otherwise = ("From account" :: String | ||||||
|                      ,"eg: assets:bank:checking" |                      ,"eg: assets:bank:checking" :: String | ||||||
|                      ,nulltemplate |                      ,nulltemplate | ||||||
|                      ,"" |                      ,"" :: String | ||||||
|                      ) |                      ) | ||||||
| 
 | 
 | ||||||
| editform :: TemplateData -> Hamlet AppRoute | editform :: TemplateData -> Hamlet AppRoute | ||||||
| editform TD{j=j} = [$hamlet| | editform TD{j=j} = [$hamlet| | ||||||
|  %form#editform!method=POST!style=display:none; |  <form#editform method=POST style=display:none; | ||||||
|   %table.form#editform |   <table.form#editform | ||||||
|    $if manyfiles |    $if manyfiles | ||||||
|     %tr |     <tr | ||||||
|      %td!colspan=2 |      <td colspan=2 | ||||||
|       Editing ^journalselect.files.j^ |       Editing ^{journalselect $ files j} | ||||||
|    %tr |    <tr | ||||||
|     %td!colspan=2 |     <td colspan=2 | ||||||
|      $forall files.j f |      $forall f <- files j | ||||||
|       %textarea!id=$fst.f$_textarea!name=text!rows=25!cols=80!style=display:none;!disabled=disabled |       <textarea id=#{fst f}_textarea name=text rows=25 cols=80 style=display:none; disabled=disabled | ||||||
|        $snd.f$ |        #{snd f} | ||||||
|    %tr#addbuttonrow |    <tr#addbuttonrow | ||||||
|     %td |     <td | ||||||
|      %span.help ^formathelp^ |      <span.help ^{formathelp} | ||||||
|     %td!align=right |     <td align=right | ||||||
|      %span.help Are you sure ? This will overwrite the journal. $ |      <span.help Are you sure ? This will overwrite the journal. # | ||||||
|      %input!type=hidden!name=action!value=edit |      <input type=hidden name=action value=edit | ||||||
|      %input!type=submit!name=submit!value="save journal" |      <input type=submit name=submit value="save journal" | ||||||
|      \ or $ |      \ or # | ||||||
|      %a!href!onclick="return editformToggle(event)" cancel |      <a href onclick="return editformToggle(event)">cancel | ||||||
| |] -- XXX textarea ids are unquoted journal file paths, which is not valid html | |] -- XXX textarea ids are unquoted journal file paths, which is not valid html | ||||||
|   where |   where | ||||||
|     manyfiles = (length $ files j) > 1 |     manyfiles = (length $ files j) > 1 | ||||||
| @ -821,22 +845,22 @@ editform TD{j=j} = [$hamlet| | |||||||
| 
 | 
 | ||||||
| journalselect :: [(FilePath,String)] -> Hamlet AppRoute | journalselect :: [(FilePath,String)] -> Hamlet AppRoute | ||||||
| journalselect journalfiles = [$hamlet| | journalselect journalfiles = [$hamlet| | ||||||
|      %select!id=journalselect!name=journal!onchange="editformJournalSelect(event)" |      <select id=journalselect name=journal onchange="editformJournalSelect(event)" | ||||||
|       $forall journalfiles f |       $forall f <- journalfiles | ||||||
|        %option!value=$fst.f$ $fst.f$ |        <option value=#{fst f}>#{fst f} | ||||||
| |] | |] | ||||||
| 
 | 
 | ||||||
| importform :: Hamlet AppRoute | importform :: Hamlet AppRoute | ||||||
| importform = [$hamlet| | importform = [$hamlet| | ||||||
|  %form#importform!method=POST!style=display:none; |  <form#importform method=POST style=display:none; | ||||||
|   %table.form |   <table.form | ||||||
|    %tr |    <tr | ||||||
|     %td |     <td | ||||||
|      %input!type=file!name=file |      <input type=file name=file | ||||||
|      %input!type=hidden!name=action!value=import |      <input type=hidden name=action value=import | ||||||
|      %input!type=submit!name=submit!value="import from file" |      <input type=submit name=submit value="import from file" | ||||||
|      \ or $ |      \ or # | ||||||
|      %a!href!onclick="return importformToggle(event)" cancel |      <a href onclick="return importformToggle(event)" cancel | ||||||
| |] | |] | ||||||
| 
 | 
 | ||||||
| postJournalOnlyR :: Handler RepPlain | postJournalOnlyR :: Handler RepPlain | ||||||
| @ -862,16 +886,18 @@ postAddForm = do | |||||||
|     <*> maybeStringInput "amount2" |     <*> maybeStringInput "amount2" | ||||||
|     <*> maybeStringInput "journal" |     <*> maybeStringInput "journal" | ||||||
|   -- supply defaults and parse date and amounts, or get errors. |   -- supply defaults and parse date and amounts, or get errors. | ||||||
|   let dateE = maybe (Left "date required") (either (\e -> Left $ showDateParseError e) Right . fixSmartDateStrEither today) dateM |   let dateE = maybe (Left "date required") (either (\e -> Left $ showDateParseError e) Right . fixSmartDateStrEither today . unpack) dateM | ||||||
|       descE = Right $ fromMaybe "" descM |       descE = Right $ maybe "" unpack descM | ||||||
|       acct1E = maybe (Left "to account required") Right acct1M |       acct1E = maybe (Left "to account required") (Right . unpack) acct1M | ||||||
|       acct2E = maybe (Left "from account required") Right acct2M |       acct2E = maybe (Left "from account required") (Right . unpack) acct2M | ||||||
|       amt1E = maybe (Left "amount required") (either (const $ Left "could not parse amount") Right . parseWithCtx nullctx someamount) amt1M |       amt1E = maybe (Left "amount required") (either (const $ Left "could not parse amount") Right . parseWithCtx nullctx someamount . unpack) amt1M | ||||||
|       amt2E = maybe (Right missingamt)       (either (const $ Left "could not parse amount") Right . parseWithCtx nullctx someamount) amt2M |       amt2E = maybe (Right missingamt)       (either (const $ Left "could not parse amount") Right . parseWithCtx nullctx someamount . unpack) amt2M | ||||||
|       journalE = maybe (Right $ journalFilePath j) |       journalE = maybe (Right $ journalFilePath j) | ||||||
|                        (\f -> if f `elem` journalFilePaths j |                        (\f -> let f' = unpack f in | ||||||
|                               then Right f |                               if f' `elem` journalFilePaths j | ||||||
|                               else Left $ "unrecognised journal file path: " ++ f) |                               then Right f' | ||||||
|  |                               else Left $ "unrecognised journal file path: " ++ f' | ||||||
|  |                               ) | ||||||
|                        journalM |                        journalM | ||||||
|       strEs = [dateE, descE, acct1E, acct2E, journalE] |       strEs = [dateE, descE, acct1E, acct2E, journalE] | ||||||
|       amtEs = [amt1E, amt2E] |       amtEs = [amt1E, amt2E] | ||||||
| @ -893,13 +919,13 @@ postAddForm = do | |||||||
|   case tE of |   case tE of | ||||||
|    Left errs -> do |    Left errs -> do | ||||||
|     -- save current form values in session |     -- save current form values in session | ||||||
|     setMessage $ string $ intercalate "; " errs |     setMessage $ toHtml $ intercalate "; " errs | ||||||
|     redirect RedirectTemporary RegisterR |     redirect RedirectTemporary RegisterR | ||||||
| 
 | 
 | ||||||
|    Right t -> do |    Right t -> do | ||||||
|     let t' = txnTieKnot t -- XXX move into balanceTransaction |     let t' = txnTieKnot t -- XXX move into balanceTransaction | ||||||
|     liftIO $ appendToJournalFile journalpath $ showTransaction t' |     liftIO $ appendToJournalFile journalpath $ showTransaction t' | ||||||
|     setMessage $ string $ printf "Added transaction:\n%s" (show t') |     setMessage $ toHtml $ (printf "Added transaction:\n%s" (show t') :: String) | ||||||
|     redirect RedirectTemporary RegisterR |     redirect RedirectTemporary RegisterR | ||||||
| 
 | 
 | ||||||
| -- | Handle a journal edit form post. | -- | Handle a journal edit form post. | ||||||
| @ -912,10 +938,11 @@ postEditForm = do | |||||||
|     $ (,) |     $ (,) | ||||||
|     <$> maybeStringInput "text" |     <$> maybeStringInput "text" | ||||||
|     <*> maybeStringInput "journal" |     <*> maybeStringInput "journal" | ||||||
|   let textE = maybe (Left "No value provided") Right textM |   let textE = maybe (Left "No value provided") (Right . unpack) textM | ||||||
|       journalE = maybe (Right $ journalFilePath j) |       journalE = maybe (Right $ journalFilePath j) | ||||||
|                        (\f -> if f `elem` journalFilePaths j |                        (\f -> let f' = unpack f in | ||||||
|                               then Right f |                               if f' `elem` journalFilePaths j | ||||||
|  |                               then Right f' | ||||||
|                               else Left "unrecognised journal file path") |                               else Left "unrecognised journal file path") | ||||||
|                        journalM |                        journalM | ||||||
|       strEs = [textE, journalE] |       strEs = [textE, journalE] | ||||||
| @ -924,7 +951,7 @@ postEditForm = do | |||||||
|   -- display errors or perform edit |   -- display errors or perform edit | ||||||
|   if not $ null errs |   if not $ null errs | ||||||
|    then do |    then do | ||||||
|     setMessage $ string $ intercalate "; " errs |     setMessage $ toHtml (intercalate "; " errs :: String) | ||||||
|     redirect RedirectTemporary JournalR |     redirect RedirectTemporary JournalR | ||||||
| 
 | 
 | ||||||
|    else do |    else do | ||||||
| @ -935,24 +962,24 @@ postEditForm = do | |||||||
|         changed = tnew /= told || filechanged' |         changed = tnew /= told || filechanged' | ||||||
|     if not changed |     if not changed | ||||||
|      then do |      then do | ||||||
|        setMessage $ string $ "No change" |        setMessage "No change" | ||||||
|        redirect RedirectTemporary JournalR |        redirect RedirectTemporary JournalR | ||||||
|      else do |      else do | ||||||
|       jE <- liftIO $ journalFromPathAndString Nothing journalpath tnew |       jE <- liftIO $ journalFromPathAndString Nothing journalpath tnew | ||||||
|       either |       either | ||||||
|        (\e -> do |        (\e -> do | ||||||
|           setMessage $ string e |           setMessage $ toHtml e | ||||||
|           redirect RedirectTemporary JournalR) |           redirect RedirectTemporary JournalR) | ||||||
|        (const $ do |        (const $ do | ||||||
|           liftIO $ writeFileWithBackup journalpath tnew |           liftIO $ writeFileWithBackup journalpath tnew | ||||||
|           setMessage $ string $ printf "Saved journal %s\n" (show journalpath) |           setMessage $ toHtml (printf "Saved journal %s\n" (show journalpath) :: String) | ||||||
|           redirect RedirectTemporary JournalR) |           redirect RedirectTemporary JournalR) | ||||||
|        jE |        jE | ||||||
| 
 | 
 | ||||||
| -- | Handle an import page post. | -- | Handle an import page post. | ||||||
| postImportForm :: Handler RepPlain | postImportForm :: Handler RepPlain | ||||||
| postImportForm = do | postImportForm = do | ||||||
|   setMessage $ string $ "can't handle file upload yet" |   setMessage "can't handle file upload yet" | ||||||
|   redirect RedirectTemporary JournalR |   redirect RedirectTemporary JournalR | ||||||
|   -- -- get form input values, or basic validation errors. E means an Either value. |   -- -- get form input values, or basic validation errors. E means an Either value. | ||||||
|   -- fileM <- runFormPost' $ maybeFileInput "file" |   -- fileM <- runFormPost' $ maybeFileInput "file" | ||||||
| @ -960,11 +987,11 @@ postImportForm = do | |||||||
|   -- -- display errors or import transactions |   -- -- display errors or import transactions | ||||||
|   -- case fileE of |   -- case fileE of | ||||||
|   --  Left errs -> do |   --  Left errs -> do | ||||||
|   --   setMessage $ string errs |   --   setMessage errs | ||||||
|   --   redirect RedirectTemporary JournalR |   --   redirect RedirectTemporary JournalR | ||||||
| 
 | 
 | ||||||
|   --  Right s -> do |   --  Right s -> do | ||||||
|   --    setMessage $ string $ s |   --    setMessage s | ||||||
|   --    redirect RedirectTemporary JournalR |   --    redirect RedirectTemporary JournalR | ||||||
| 
 | 
 | ||||||
| ---------------------------------------------------------------------- | ---------------------------------------------------------------------- | ||||||
| @ -980,41 +1007,41 @@ getRegisterOnlyR = do | |||||||
| -- | Render a register report as HTML. | -- | Render a register report as HTML. | ||||||
| registerReportAsHtml :: [Opt] -> TemplateData -> RegisterReport -> Hamlet AppRoute | registerReportAsHtml :: [Opt] -> TemplateData -> RegisterReport -> Hamlet AppRoute | ||||||
| registerReportAsHtml _ td items = [$hamlet| | registerReportAsHtml _ td items = [$hamlet| | ||||||
| %table.registerreport | <table.registerreport | ||||||
|  %tr.headings |  <tr.headings | ||||||
|   ^headings^ |   ^{headings} | ||||||
|  $forall number.items i |  $forall i <- number items | ||||||
|   ^itemAsHtml' i^ |   ^{itemAsHtml' i} | ||||||
| |] | |] | ||||||
|  where |  where | ||||||
|    number = zip [1..] |    number = zip [1..] | ||||||
|    headings = [$hamlet| |    headings = [$hamlet| | ||||||
|                %th.date!align=left Date |                <th.date align=left Date | ||||||
|                %th.description!align=left Description |                <th.description align=left Description | ||||||
|                %th.account!align=left Account |                <th.account align=left Account | ||||||
|                %th.amount!align=right Amount |                <th.amount align=right Amount | ||||||
|                %th.balance!align=right Balance |                <th.balance align=right Balance | ||||||
|                |] |                |] :: Hamlet AppRoute | ||||||
|    itemAsHtml' = itemAsHtml td |    itemAsHtml' = itemAsHtml td | ||||||
|    itemAsHtml :: TemplateData -> (Int, RegisterReportItem) -> Hamlet AppRoute |    itemAsHtml :: TemplateData -> (Int, RegisterReportItem) -> Hamlet AppRoute | ||||||
|    itemAsHtml TD{p=p} (n, (ds, posting, b)) = [$hamlet| |    itemAsHtml TD{p=p} (n, (ds, posting, b)) = [$hamlet| | ||||||
|      %tr.item.$evenodd$.$firstposting$ |      <tr.item.#{evenodd}.#{firstposting} | ||||||
|       %td.date $date$ |       <td.date>#{date} | ||||||
|       %td.description $desc$ |       <td.description>#{desc} | ||||||
|       %td.account |       <td.account | ||||||
|        %a!href=$aurl$ $acct$ |        <a href=#{aurl}>#{acct} | ||||||
|       %td.amount!align=right $mixedAmountAsHtml.pamount.posting$ |       <td.amount align=right>#{mixedAmountAsHtml $ pamount posting} | ||||||
|       %td.balance!align=right $mixedAmountAsHtml.b$ |       <td.balance align=right>#{mixedAmountAsHtml b} | ||||||
|      |] where |      |] where | ||||||
|        evenodd = if even n then "even" else "odd" |        evenodd = if even n then "even" else "odd" :: String | ||||||
|        (firstposting, date, desc) = case ds of Just (da, de) -> ("firstposting", show da, de) |        (firstposting, date, desc) = case ds of Just (da, de) -> ("firstposting", show da, de) | ||||||
|                                                Nothing -> ("", "", "") |                                                Nothing -> ("", "", "") :: (String,String,String) | ||||||
|        acct = paccount posting |        acct = paccount posting | ||||||
|        aurl = printf ".?a=%s%s" (accountNameToAccountRegex acct) p' :: String |        aurl = printf ".?a=%s%s" (accountNameToAccountRegex acct) p' :: String | ||||||
|        p' = if null p then "" else printf "&p=%s" p |        p' = if null p then "" else printf "&p=%s" p :: String | ||||||
| 
 | 
 | ||||||
| mixedAmountAsHtml b = preEscapedString $ addclass $ intercalate "<br>" $ lines $ show b | mixedAmountAsHtml b = preEscapedString $ addclass $ intercalate "<br>" $ lines $ show b | ||||||
|     where addclass = printf "<span class=\"%s\">%s</span>" c |     where addclass = printf "<span class=\"%s\">%s</span>" (c :: String) | ||||||
|           c = case isNegativeMixedAmount b of Just True -> "negative amount" |           c = case isNegativeMixedAmount b of Just True -> "negative amount" | ||||||
|                                               _         -> "positive amount" |                                               _         -> "positive amount" | ||||||
| 
 | 
 | ||||||
| @ -1058,10 +1085,10 @@ getAddformRTR = do | |||||||
|                 | n == 1     = ("To account" |                 | n == 1     = ("To account" | ||||||
|                               ,"eg: expenses:food" |                               ,"eg: expenses:food" | ||||||
|                               ,[$hamlet| |                               ,[$hamlet| | ||||||
|                                 %td!style=padding-left:1em; |                                 %td style=padding-left:1em; | ||||||
|                                  Amount: |                                  Amount: | ||||||
|                                 %td |                                 %td | ||||||
|                                  %input.textinput!size=15!name=$amtvar$!value="" |                                  %input.textinput size=15 name=$amtvar$ value="" | ||||||
|                                 |] |                                 |] | ||||||
|                               ,"eg: $6" |                               ,"eg: $6" | ||||||
|                               ) |                               ) | ||||||
|  | |||||||
| @ -8,12 +8,12 @@ Released under GPL version 3 or later. | |||||||
| module Hledger.Web.Main where | module Hledger.Web.Main where | ||||||
| 
 | 
 | ||||||
| import Control.Concurrent (forkIO, threadDelay) | import Control.Concurrent (forkIO, threadDelay) | ||||||
| import Network.Wai.Handler.SimpleServer (run) | import Data.Text(pack) | ||||||
| import System.Exit (exitFailure) | import System.Exit (exitFailure) | ||||||
| import System.IO.Storage (withStore, putValue,) | import System.IO.Storage (withStore, putValue,) | ||||||
| import Yesod.Content (typeByExt) |  | ||||||
| import Yesod.Helpers.Static (fileLookupDir) |  | ||||||
| import System.Console.GetOpt | import System.Console.GetOpt | ||||||
|  | import Yesod | ||||||
|  | import Yesod.Helpers.Static | ||||||
| 
 | 
 | ||||||
| import Hledger.Cli.Options | import Hledger.Cli.Options | ||||||
| import Hledger.Cli.Utils (withJournalDo, openBrowserOn) | import Hledger.Cli.Utils (withJournalDo, openBrowserOn) | ||||||
| @ -21,7 +21,7 @@ import Hledger.Cli.Version (progversionstr, binaryfilename) | |||||||
| import Hledger.Data | import Hledger.Data | ||||||
| import Prelude hiding (putStr, putStrLn) | import Prelude hiding (putStr, putStrLn) | ||||||
| import Hledger.Data.UTF8 (putStr, putStrLn) | import Hledger.Data.UTF8 (putStr, putStrLn) | ||||||
| import Hledger.Web.App (App(..), withApp) | import Hledger.Web.App (App(..)) | ||||||
| import Hledger.Web.Files (createFilesIfMissing) | import Hledger.Web.Files (createFilesIfMissing) | ||||||
| import Hledger.Web.Settings (browserstartdelay, defhost, defport, datadir) | import Hledger.Web.Settings (browserstartdelay, defhost, defport, datadir) | ||||||
| 
 | 
 | ||||||
| @ -82,15 +82,15 @@ server baseurl port opts args j = do | |||||||
|   printf "Starting http server on port %d with base url %s\n" port baseurl |   printf "Starting http server on port %d with base url %s\n" port baseurl | ||||||
|   withStore "hledger" $ do |   withStore "hledger" $ do | ||||||
|     putValue "hledger" "journal" j |     putValue "hledger" "journal" j | ||||||
|     withApp App{ |     warpDebug port $ App{ | ||||||
|               -- appConnPool=Nothing |               -- appConnPool=Nothing | ||||||
|               appRoot=baseurl |               appRoot=pack baseurl | ||||||
|              ,appDataDir=datadir |              ,appDataDir=datadir | ||||||
|              ,appStatic=fileLookupDir datadir $ typeByExt -- ++[("hamlet","text/plain")] |              ,appStaticSettings=static datadir | ||||||
|              ,appOpts=opts |              ,appOpts=opts | ||||||
|              ,appArgs=args |              ,appArgs=args | ||||||
|              ,appJournal=j |              ,appJournal=j | ||||||
|              } $ run port |              } | ||||||
| 
 | 
 | ||||||
| browser :: String -> IO () | browser :: String -> IO () | ||||||
| browser baseurl = do | browser baseurl = do | ||||||
|  | |||||||
| @ -1,4 +1,4 @@ | |||||||
| {-# LANGUAGE CPP #-} | {-# LANGUAGE CPP, OverloadedStrings #-} | ||||||
| module Hledger.Web.Settings | module Hledger.Web.Settings | ||||||
|     ( |     ( | ||||||
|      hamletFile |      hamletFile | ||||||
| @ -42,17 +42,18 @@ browserstartdelay = 100000 -- microseconds | |||||||
| -- urls | -- urls | ||||||
| ---------------------------------------------------------------------- | ---------------------------------------------------------------------- | ||||||
| 
 | 
 | ||||||
|  | hledgerorgurl, manualurl :: String | ||||||
| hledgerorgurl     = "http://hledger.org" | hledgerorgurl     = "http://hledger.org" | ||||||
| manualurl         = hledgerorgurl++"/MANUAL.html" | manualurl         = hledgerorgurl++"/MANUAL.html" | ||||||
| 
 | 
 | ||||||
| defhost           = "localhost" | defhost           = "localhost" :: String | ||||||
| defport           = 5000 | defport           = 5000 | ||||||
| 
 | 
 | ||||||
| approot :: String | approot :: String | ||||||
| #ifdef PRODUCTION | #ifdef PRODUCTION | ||||||
| approot = printf "http://%s:%d" defhost (defport :: Int) | approot = printf "http://%s:%d" defhost (defport :: Int) :: String | ||||||
| #else | #else | ||||||
| approot = printf "http://%s:%d" defhost (defport :: Int) | approot = printf "http://%s:%d" defhost (defport :: Int) :: String | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| staticroot :: String | staticroot :: String | ||||||
|  | |||||||
| @ -68,13 +68,12 @@ executable hledger-web | |||||||
|                  -- ,time |                  -- ,time | ||||||
|                  -- ,utf8-string >= 0.3.5 && < 0.4 |                  -- ,utf8-string >= 0.3.5 && < 0.4 | ||||||
|                  ,io-storage >= 0.3 && < 0.4 |                  ,io-storage >= 0.3 && < 0.4 | ||||||
|                  ,yesod >= 0.6.1.2 && < 0.7 |                  ,yesod >= 0.8 && < 0.9 | ||||||
|                  ,hamlet >= 0.6.0.1 && < 0.7 |  | ||||||
|                  -- ,convertible-text >= 0.3.0.1 && < 0.4 |                  -- ,convertible-text >= 0.3.0.1 && < 0.4 | ||||||
|                  -- ,data-object >= 0.3.1.2 && < 0.4 |                  -- ,data-object >= 0.3.1.2 && < 0.4 | ||||||
|                  ,failure >= 0.1 && < 0.2 |                  ,failure >= 0.1 && < 0.2 | ||||||
|                  -- ,persistent == 0.2.* |                  -- ,persistent == 0.2.* | ||||||
|                  -- ,persistent-sqlite == 0.2.* |                  -- ,persistent-sqlite == 0.2.* | ||||||
|                  ,template-haskell >= 2.4 && < 2.6 |                  ,template-haskell >= 2.4 && < 2.6 | ||||||
|                  ,wai-extra == 0.2.* |                  ,wai-extra == 0.4.* | ||||||
|                  ,file-embed == 0.0.* |                  ,file-embed == 0.0.* | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user