{-| A happs-based web UI for hledger. -} module WebCommand where import Control.Monad.Trans (liftIO) import Data.ByteString.Lazy.UTF8 (toString) import qualified Data.Map as M import Data.Map ((!)) import Data.Time.Clock import Data.Time.Format import System.Locale import Control.Concurrent import qualified Data.ByteString.Lazy.Char8 as B import Happstack.Data (defaultValue) import Happstack.Server import Happstack.Server.HTTP.FileServe (fileServe) import Happstack.State.Control (waitForTermination) import System.Cmd (system) import System.Info (os) import System.Exit import Ledger import Options import BalanceCommand import RegisterCommand import PrintCommand import HistogramCommand tcpport = 5000 web :: [Opt] -> [String] -> Ledger -> IO () web opts args l = if Debug `elem` opts then do putStrLn $ printf "starting web server on port %d in debug mode" tcpport simpleHTTP nullConf{port=tcpport} handlers else do putStrLn $ printf "starting web server on port %d" tcpport tid <- forkIO $ simpleHTTP nullConf{port=tcpport} handlers putStrLn "starting web browser" openBrowserOn $ printf "http://localhost:%d/balance" tcpport waitForTermination putStrLn "shutting down web server..." killThread tid putStrLn "shutdown complete" where handlers :: ServerPartT IO Response handlers = msum [methodSP GET $ withDataFn (look "a") $ \a -> templatise $ balancereport [a] ,methodSP GET $ templatise $ balancereport [] ,dir "print" $ withDataFn (look "a") $ \a -> templatise $ printreport [a] ,dir "print" $ templatise $ printreport [] ,dir "register" $ withDataFn (look "a") $ \a -> templatise $ registerreport [a] ,dir "register" $ templatise $ registerreport [] ,dir "balance" $ withDataFn (look "a") $ \a -> templatise $ balancereport [a] ,dir "balance" $ templatise $ balancereport [] ,dir "histogram" $ withDataFn (look "a") $ \a -> templatise $ histogramreport [a] ,dir "histogram" $ templatise $ histogramreport [] ] printreport apats = showLedgerTransactions opts (apats ++ args) l registerreport apats = showRegisterReport opts (apats ++ args) l balancereport [] = showBalanceReport opts args l balancereport apats = showBalanceReport opts (apats ++ args) l' where l' = cacheLedger apats (rawledger l) -- re-filter by account pattern each time histogramreport [] = showHistogram opts args l histogramreport apats = showHistogram opts (apats ++ args) l' where l' = cacheLedger apats (rawledger l) -- re-filter by account pattern each time templatise :: String -> ServerPartT IO Response templatise s = do r <- askRq return $ setHeader "Content-Type" "text/html" $ toResponse $ maintemplate r s maintemplate :: Request -> String -> String maintemplate r = printf (unlines ["
" ,"
search: 
" ,"
" ,"
" ," balance" ,"|" ," register" ,"|" ," print" ,"|" ," histogram" ,"
" ,"
%s
" ]) (dropWhile (=='/') $ rqUri r) (fromMaybe "" $ queryValue "a" r) queryValues :: String -> Request -> [String] queryValues q r = map (B.unpack . inputValue . snd) $ filter ((==q).fst) $ rqInputs r queryValue :: String -> Request -> Maybe String queryValue q r = case filter ((==q).fst) $ rqInputs r of [] -> Nothing is -> Just $ B.unpack $ inputValue $ snd $ head is -- | Attempt to open a web browser on the given url, all platforms. openBrowserOn :: String -> IO ExitCode openBrowserOn u = trybrowsers browsers u where trybrowsers (b:bs) u = do e <- system $ printf "%s %s" b u case e of ExitSuccess -> return ExitSuccess ExitFailure _ -> trybrowsers bs u trybrowsers [] u = do putStrLn $ printf "Sorry, I could not start a browser (tried: %s)" $ intercalate ", " browsers putStrLn $ printf "Please open your browser and visit %s" u return $ ExitFailure 127 browsers | os=="darwin" = ["open"] | os=="mingw32" = ["firefox","safari","opera","iexplore"] | otherwise = ["sensible-browser","firefox"] -- jeffz: write a ffi binding for it using the Win32 package as a basis -- start by adding System/Win32/Shell.hsc and follow the style of any -- other module in that directory for types, headers, error handling and -- what not. -- ::ShellExecute(NULL, "open", "www.somepage.com", NULL, NULL, SW_SHOWNORMAL); -- ::ShellExecute(NULL, "open", "firefox.exe", "www.somepage.com" NULL, SW_SHOWNORMAL);