{-# OPTIONS_GHC -fno-warn-orphans #-}
{-# LANGUAGE CPP #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeSynonymInstances #-}
{-# LANGUAGE ViewPatterns #-}

-- | Define the web application's foundation, in the usual Yesod style.
--   See a default Yesod app's comments for more details of each part.

module Hledger.Web.Foundation where

import Control.Monad (join, when)
import qualified Data.ByteString.Char8 as BC
import Data.Traversable (for)
import Data.IORef (IORef, readIORef, writeIORef)
import Data.Maybe (fromMaybe)
#if !(MIN_VERSION_base(4,13,0))
import Data.Monoid ((<>))
#endif
import Data.Text (Text)
import qualified Data.Text as T
import Data.Time.Calendar (Day)
import Network.HTTP.Conduit (Manager)
import Network.HTTP.Types (status403)
import Network.Wai (requestHeaders)
import System.FilePath (takeFileName)
import Text.Blaze (Markup)
import Text.Hamlet (hamletFile)
import Yesod
import Yesod.Static
import Yesod.Default.Config

#ifndef DEVELOPMENT
import Hledger.Web.Settings (staticDir)
import Text.Jasmine (minifym)
import Yesod.Default.Util (addStaticContentExternal)
#endif

import Hledger
import Hledger.Cli (CliOpts(..), journalReloadIfChanged)
import Hledger.Web.Settings (Extra(..), widgetFile)
import Hledger.Web.Settings.StaticFiles
import Hledger.Web.WebOptions
import Hledger.Web.Widget.Common (balanceReportAsHtml)

-- | The site argument for your application. This can be a good place to
-- keep settings and values requiring initialization before your application
-- starts running, such as database connections. Every handler will have
-- access to the data present here.
data App = App
    { App -> AppConfig DefaultEnv Extra
settings :: AppConfig DefaultEnv Extra
    , App -> Static
getStatic :: Static -- ^ Settings for static file serving.
    , App -> Manager
httpManager :: Manager
      --
    , App -> WebOpts
appOpts    :: WebOpts
    , App -> IORef Journal
appJournal :: IORef Journal
    }


-- This is where we define all of the routes in our application. For a full
-- explanation of the syntax, please see:
-- http://www.yesodweb.com/book/handler
--
-- This function does three things:
--
-- * Creates the route datatype AppRoute. Every valid URL in your
--   application can be represented as a value of this type.
-- * Creates the associated type:
--       type instance Route App = AppRoute
-- * Creates the value resourcesApp which contains information on the
--   resources declared below. This is used in Handler.hs by the call to
--   mkYesodDispatch
--
-- What this function does *not* do is create a YesodSite instance for
-- App. Creating that instance requires all of the handler functions
-- for our application to be in scope. However, the handler functions
-- usually require access to the AppRoute datatype. Therefore, we
-- split these actions into two functions and place them in separate files.
mkYesodData "App" $(parseRoutesFile "config/routes")

-- | A convenience alias.
type AppRoute = Route App

#if MIN_VERSION_yesod(1,6,0)
type Form x = Html -> MForm (HandlerFor App) (FormResult x, Widget)
#else
type Form x = Html -> MForm (HandlerT App IO) (FormResult x, Widget)
#endif

-- Please see the documentation for the Yesod typeclass. There are a number
-- of settings which can be configured by overriding methods here.
instance Yesod App where
  approot :: Approot App
approot = (App -> Text) -> Approot App
forall master. (master -> Text) -> Approot master
ApprootMaster ((App -> Text) -> Approot App) -> (App -> Text) -> Approot App
forall a b. (a -> b) -> a -> b
$ AppConfig DefaultEnv Extra -> Text
forall environment extra. AppConfig environment extra -> Text
appRoot (AppConfig DefaultEnv Extra -> Text)
-> (App -> AppConfig DefaultEnv Extra) -> App -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. App -> AppConfig DefaultEnv Extra
settings

  makeSessionBackend :: App -> IO (Maybe SessionBackend)
makeSessionBackend App
_ =
    let sessionexpirysecs :: Int
sessionexpirysecs = Int
120
    in  SessionBackend -> Maybe SessionBackend
forall a. a -> Maybe a
Just (SessionBackend -> Maybe SessionBackend)
-> IO SessionBackend -> IO (Maybe SessionBackend)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Int -> String -> IO SessionBackend
defaultClientSessionBackend Int
sessionexpirysecs String
".hledger-web_client_session_key.aes"

  -- defaultLayout :: WidgetFor site () -> HandlerFor site Html
  defaultLayout :: WidgetFor App () -> HandlerFor App Html
defaultLayout WidgetFor App ()
widget = do

    -- Don't run if server-side UI is disabled.
    -- This single check probably covers all the HTML-returning handlers,
    -- but for now they do the check as well.
    Handler ()
checkServerSideUiEnabled

    App
master <- HandlerFor App App
forall (m :: * -> *). MonadHandler m => m (HandlerSite m)
getYesod
    Route App
here <- Route App -> Maybe (Route App) -> Route App
forall a. a -> Maybe a -> a
fromMaybe Route App
RootR (Maybe (Route App) -> Route App)
-> HandlerFor App (Maybe (Route App)) -> HandlerFor App (Route App)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> HandlerFor App (Maybe (Route App))
forall (m :: * -> *).
MonadHandler m =>
m (Maybe (Route (HandlerSite m)))
getCurrentRoute
    VD {[Capability]
caps :: ViewData -> [Capability]
caps :: [Capability]
caps, Journal
j :: ViewData -> Journal
j :: Journal
j, Query
m :: ViewData -> Query
m :: Query
m, WebOpts
opts :: ViewData -> WebOpts
opts :: WebOpts
opts, Text
q :: ViewData -> Text
q :: Text
q, [QueryOpt]
qopts :: ViewData -> [QueryOpt]
qopts :: [QueryOpt]
qopts} <- Handler ViewData
getViewData
    Maybe Html
msg <- HandlerFor App (Maybe Html)
forall (m :: * -> *). MonadHandler m => m (Maybe Html)
getMessage
    Bool
showSidebar <- HandlerFor App Bool
shouldShowSidebar
    Bool
hideEmptyAccts <- (Maybe Text -> Maybe Text -> Bool
forall a. Eq a => a -> a -> Bool
== Text -> Maybe Text
forall a. a -> Maybe a
Just Text
"1") (Maybe Text -> Bool)
-> (YesodRequest -> Maybe Text) -> YesodRequest -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> [(Text, Text)] -> Maybe Text
forall a b. Eq a => a -> [(a, b)] -> Maybe b
lookup Text
"hideemptyaccts" ([(Text, Text)] -> Maybe Text)
-> (YesodRequest -> [(Text, Text)]) -> YesodRequest -> Maybe Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. YesodRequest -> [(Text, Text)]
reqCookies (YesodRequest -> Bool)
-> HandlerFor App YesodRequest -> HandlerFor App Bool
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> HandlerFor App YesodRequest
forall (m :: * -> *). MonadHandler m => m YesodRequest
getRequest

    let ropts :: ReportOpts
ropts = CliOpts -> ReportOpts
reportopts_ (WebOpts -> CliOpts
cliopts_ WebOpts
opts)
        -- flip the default for items with zero amounts, show them by default
        ropts' :: ReportOpts
ropts' = ReportOpts
ropts { empty_ :: Bool
empty_ = Bool -> Bool
not (ReportOpts -> Bool
empty_ ReportOpts
ropts) }
        accounts :: HtmlUrl (Route App)
accounts =
          (Route App, Route App)
-> Route App
-> Bool
-> Journal
-> Text
-> [QueryOpt]
-> BalanceReport
-> HtmlUrl (Route App)
forall r.
Eq r =>
(r, r)
-> r
-> Bool
-> Journal
-> Text
-> [QueryOpt]
-> BalanceReport
-> HtmlUrl r
balanceReportAsHtml (Route App
JournalR, Route App
RegisterR) Route App
here Bool
hideEmptyAccts Journal
j Text
q [QueryOpt]
qopts (BalanceReport -> HtmlUrl (Route App))
-> BalanceReport -> HtmlUrl (Route App)
forall a b. (a -> b) -> a -> b
$
          ReportOpts -> Query -> Journal -> BalanceReport
balanceReport ReportOpts
ropts' Query
m Journal
j

        topShowmd :: Text
topShowmd = if Bool
showSidebar then Text
"col-md-4" else Text
"col-any-0" :: Text
        topShowsm :: Text
topShowsm = if Bool
showSidebar then Text
"col-sm-4" else Text
"" :: Text
        sideShowmd :: Text
sideShowmd = if Bool
showSidebar then Text
"col-md-4" else Text
"col-any-0" :: Text
        sideShowsm :: Text
sideShowsm = if Bool
showSidebar then Text
"col-sm-4" else Text
"" :: Text
        mainShowmd :: Text
mainShowmd = if Bool
showSidebar then Text
"col-md-8" else Text
"col-md-12" :: Text
        mainShowsm :: Text
mainShowsm = if Bool
showSidebar then Text
"col-sm-8" else Text
"col-sm-12" :: Text

    -- We break up the default layout into two components:
    -- default-layout is the contents of the body tag, and
    -- default-layout-wrapper is the entire page. Since the final
    -- value passed to hamletToRepHtml cannot be a widget, this allows
    -- you to use normal widget features in default-layout.
    PageContent (Route App)
pc <- WidgetFor App () -> HandlerFor App (PageContent (Route App))
forall site.
Yesod site =>
WidgetFor site () -> HandlerFor site (PageContent (Route site))
widgetToPageContent (WidgetFor App () -> HandlerFor App (PageContent (Route App)))
-> WidgetFor App () -> HandlerFor App (PageContent (Route App))
forall a b. (a -> b) -> a -> b
$ do
      Route (HandlerSite (WidgetFor App)) -> WidgetFor App ()
forall (m :: * -> *).
MonadWidget m =>
Route (HandlerSite m) -> m ()
addStylesheet (Route (HandlerSite (WidgetFor App)) -> WidgetFor App ())
-> Route (HandlerSite (WidgetFor App)) -> WidgetFor App ()
forall a b. (a -> b) -> a -> b
$ Route Static -> Route App
StaticR Route Static
css_bootstrap_min_css
      Route (HandlerSite (WidgetFor App)) -> WidgetFor App ()
forall (m :: * -> *).
MonadWidget m =>
Route (HandlerSite m) -> m ()
addStylesheet (Route (HandlerSite (WidgetFor App)) -> WidgetFor App ())
-> Route (HandlerSite (WidgetFor App)) -> WidgetFor App ()
forall a b. (a -> b) -> a -> b
$ Route Static -> Route App
StaticR Route Static
css_bootstrap_datepicker_standalone_min_css
      -- load these things early, in HEAD:
      HtmlUrl (Route App) -> WidgetFor App ()
forall site a (m :: * -> *).
(ToWidgetHead site a, MonadWidget m, HandlerSite m ~ site) =>
a -> m ()
toWidgetHead [hamlet|
        <script type="text/javascript" src="@{StaticR js_jquery_min_js}">
        <script type="text/javascript" src="@{StaticR js_typeahead_bundle_min_js}">
      |]
      Route (HandlerSite (WidgetFor App)) -> WidgetFor App ()
forall (m :: * -> *).
MonadWidget m =>
Route (HandlerSite m) -> m ()
addScript (Route (HandlerSite (WidgetFor App)) -> WidgetFor App ())
-> Route (HandlerSite (WidgetFor App)) -> WidgetFor App ()
forall a b. (a -> b) -> a -> b
$ Route Static -> Route App
StaticR Route Static
js_bootstrap_min_js
      Route (HandlerSite (WidgetFor App)) -> WidgetFor App ()
forall (m :: * -> *).
MonadWidget m =>
Route (HandlerSite m) -> m ()
addScript (Route (HandlerSite (WidgetFor App)) -> WidgetFor App ())
-> Route (HandlerSite (WidgetFor App)) -> WidgetFor App ()
forall a b. (a -> b) -> a -> b
$ Route Static -> Route App
StaticR Route Static
js_bootstrap_datepicker_min_js
      Route (HandlerSite (WidgetFor App)) -> WidgetFor App ()
forall (m :: * -> *).
MonadWidget m =>
Route (HandlerSite m) -> m ()
addScript (Route (HandlerSite (WidgetFor App)) -> WidgetFor App ())
-> Route (HandlerSite (WidgetFor App)) -> WidgetFor App ()
forall a b. (a -> b) -> a -> b
$ Route Static -> Route App
StaticR Route Static
js_jquery_url_js
      Route (HandlerSite (WidgetFor App)) -> WidgetFor App ()
forall (m :: * -> *).
MonadWidget m =>
Route (HandlerSite m) -> m ()
addScript (Route (HandlerSite (WidgetFor App)) -> WidgetFor App ())
-> Route (HandlerSite (WidgetFor App)) -> WidgetFor App ()
forall a b. (a -> b) -> a -> b
$ Route Static -> Route App
StaticR Route Static
js_jquery_cookie_js
      Route (HandlerSite (WidgetFor App)) -> WidgetFor App ()
forall (m :: * -> *).
MonadWidget m =>
Route (HandlerSite m) -> m ()
addScript (Route (HandlerSite (WidgetFor App)) -> WidgetFor App ())
-> Route (HandlerSite (WidgetFor App)) -> WidgetFor App ()
forall a b. (a -> b) -> a -> b
$ Route Static -> Route App
StaticR Route Static
js_jquery_hotkeys_js
      Route (HandlerSite (WidgetFor App)) -> WidgetFor App ()
forall (m :: * -> *).
MonadWidget m =>
Route (HandlerSite m) -> m ()
addScript (Route (HandlerSite (WidgetFor App)) -> WidgetFor App ())
-> Route (HandlerSite (WidgetFor App)) -> WidgetFor App ()
forall a b. (a -> b) -> a -> b
$ Route Static -> Route App
StaticR Route Static
js_jquery_flot_min_js
      Route (HandlerSite (WidgetFor App)) -> WidgetFor App ()
forall (m :: * -> *).
MonadWidget m =>
Route (HandlerSite m) -> m ()
addScript (Route (HandlerSite (WidgetFor App)) -> WidgetFor App ())
-> Route (HandlerSite (WidgetFor App)) -> WidgetFor App ()
forall a b. (a -> b) -> a -> b
$ Route Static -> Route App
StaticR Route Static
js_jquery_flot_time_min_js
      Route (HandlerSite (WidgetFor App)) -> WidgetFor App ()
forall (m :: * -> *).
MonadWidget m =>
Route (HandlerSite m) -> m ()
addScript (Route (HandlerSite (WidgetFor App)) -> WidgetFor App ())
-> Route (HandlerSite (WidgetFor App)) -> WidgetFor App ()
forall a b. (a -> b) -> a -> b
$ Route Static -> Route App
StaticR Route Static
js_jquery_flot_tooltip_min_js
      HtmlUrl (Route App) -> WidgetFor App ()
forall site a (m :: * -> *).
(ToWidget site a, MonadWidget m, HandlerSite m ~ site) =>
a -> m ()
toWidget [hamlet| \<!--[if lte IE 8]> <script type="text/javascript" src="@{StaticR js_excanvas_min_js}"></script> <![endif]--> |]
      Route (HandlerSite (WidgetFor App)) -> WidgetFor App ()
forall (m :: * -> *).
MonadWidget m =>
Route (HandlerSite m) -> m ()
addStylesheet (Route (HandlerSite (WidgetFor App)) -> WidgetFor App ())
-> Route (HandlerSite (WidgetFor App)) -> WidgetFor App ()
forall a b. (a -> b) -> a -> b
$ Route Static -> Route App
StaticR Route Static
hledger_css
      Route (HandlerSite (WidgetFor App)) -> WidgetFor App ()
forall (m :: * -> *).
MonadWidget m =>
Route (HandlerSite m) -> m ()
addScript (Route (HandlerSite (WidgetFor App)) -> WidgetFor App ())
-> Route (HandlerSite (WidgetFor App)) -> WidgetFor App ()
forall a b. (a -> b) -> a -> b
$ Route Static -> Route App
StaticR Route Static
hledger_js
      $(widgetFile "default-layout")

    ((Route (HandlerSite (HandlerFor App)) -> [(Text, Text)] -> Text)
 -> Html)
-> HandlerFor App Html
forall (m :: * -> *) output.
MonadHandler m =>
((Route (HandlerSite m) -> [(Text, Text)] -> Text) -> output)
-> m output
withUrlRenderer $(hamletFile "templates/default-layout-wrapper.hamlet")

#ifndef DEVELOPMENT
  -- 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 :: Text
-> Text
-> ByteString
-> HandlerFor App (Maybe (Either Text (Route App, [(Text, Text)])))
addStaticContent = (ByteString -> Either String ByteString)
-> (ByteString -> String)
-> String
-> ([Text] -> Route App)
-> Text
-> Text
-> ByteString
-> HandlerFor App (Maybe (Either Text (Route App, [(Text, Text)])))
forall a master.
(ByteString -> Either a ByteString)
-> (ByteString -> String)
-> String
-> ([Text] -> Route master)
-> Text
-> Text
-> ByteString
-> HandlerFor
     master (Maybe (Either Text (Route master, [(Text, Text)])))
addStaticContentExternal ByteString -> Either String ByteString
minifym ByteString -> String
base64md5 String
staticDir (Route Static -> Route App
StaticR (Route Static -> Route App)
-> ([Text] -> Route Static) -> [Text] -> Route App
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ([Text] -> [(Text, Text)] -> Route Static)
-> [(Text, Text)] -> [Text] -> Route Static
forall a b c. (a -> b -> c) -> b -> a -> c
flip [Text] -> [(Text, Text)] -> Route Static
StaticRoute [])
#endif

-- This instance is required to use forms. You can modify renderMessage to
-- achieve customized and internationalized form validation messages.
instance RenderMessage App FormMessage where
    renderMessage :: App -> [Text] -> FormMessage -> Text
renderMessage App
_ [Text]
_ = FormMessage -> Text
defaultFormMessage


----------------------------------------------------------------------
-- template and handler utilities

-- view data, used by the add form and handlers
-- XXX Parameter p - show/hide postings

-- | A bundle of data useful for hledger-web request handlers and templates.
data ViewData = VD
  { ViewData -> WebOpts
opts  :: WebOpts    -- ^ the command-line options at startup
  , ViewData -> Day
today :: Day        -- ^ today's date (for queries containing relative dates)
  , ViewData -> Journal
j     :: Journal    -- ^ the up-to-date parsed unfiltered journal
  , ViewData -> Text
q     :: Text       -- ^ the current q parameter, the main query expression
  , ViewData -> Query
m     :: Query      -- ^ a query parsed from the q parameter
  , ViewData -> [QueryOpt]
qopts :: [QueryOpt] -- ^ query options parsed from the q parameter
  , ViewData -> [Capability]
caps  :: [Capability] -- ^ capabilities enabled for this request
  } deriving (Int -> ViewData -> ShowS
[ViewData] -> ShowS
ViewData -> String
(Int -> ViewData -> ShowS)
-> (ViewData -> String) -> ([ViewData] -> ShowS) -> Show ViewData
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [ViewData] -> ShowS
$cshowList :: [ViewData] -> ShowS
show :: ViewData -> String
$cshow :: ViewData -> String
showsPrec :: Int -> ViewData -> ShowS
$cshowsPrec :: Int -> ViewData -> ShowS
Show)

instance Show Text.Blaze.Markup where show :: Html -> String
show Html
_ = String
"<blaze markup>"

-- | Gather data used by handlers and templates in the current request.
getViewData :: Handler ViewData
getViewData :: Handler ViewData
getViewData = do
  App {appOpts :: App -> WebOpts
appOpts = WebOpts
opts, IORef Journal
appJournal :: IORef Journal
appJournal :: App -> IORef Journal
appJournal} <- HandlerFor App App
forall (m :: * -> *). MonadHandler m => m (HandlerSite m)
getYesod
  Day
today <- IO Day -> HandlerFor App Day
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO IO Day
getCurrentDay
  let copts :: CliOpts
copts = WebOpts -> CliOpts
cliopts_ WebOpts
opts
  (Journal
j, Maybe String
merr) <-
    IORef Journal -> CliOpts -> Day -> Handler (Journal, Maybe String)
getCurrentJournal
      IORef Journal
appJournal
      CliOpts
copts {reportopts_ :: ReportOpts
reportopts_ = (CliOpts -> ReportOpts
reportopts_ CliOpts
copts) {no_elide_ :: Bool
no_elide_ = Bool
True}}
      Day
today
  Handler () -> (String -> Handler ()) -> Maybe String -> Handler ()
forall b a. b -> (a -> b) -> Maybe a -> b
maybe (() -> Handler ()
forall (f :: * -> *) a. Applicative f => a -> f a
pure ()) (Html -> Handler ()
forall (m :: * -> *). MonadHandler m => Html -> m ()
setMessage (Html -> Handler ()) -> (String -> Html) -> String -> Handler ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> Html
forall a. ToMarkup a => a -> Html
toHtml) Maybe String
merr
  Text
q <- Text -> Maybe Text -> Text
forall a. a -> Maybe a -> a
fromMaybe Text
"" (Maybe Text -> Text)
-> HandlerFor App (Maybe Text) -> HandlerFor App Text
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Text -> HandlerFor App (Maybe Text)
forall (m :: * -> *). MonadHandler m => Text -> m (Maybe Text)
lookupGetParam Text
"q"
  let (Query
m, [QueryOpt]
qopts) = Day -> Text -> (Query, [QueryOpt])
parseQuery Day
today Text
q
  [Capability]
caps <- case WebOpts -> Maybe (CI ByteString)
capabilitiesHeader_ WebOpts
opts of
    Maybe (CI ByteString)
Nothing -> [Capability] -> HandlerFor App [Capability]
forall (m :: * -> *) a. Monad m => a -> m a
return (WebOpts -> [Capability]
capabilities_ WebOpts
opts)
    Just CI ByteString
h -> do
      [[ByteString]]
hs <- ((CI ByteString, ByteString) -> [ByteString])
-> [(CI ByteString, ByteString)] -> [[ByteString]]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (Char -> ByteString -> [ByteString]
BC.split Char
',' (ByteString -> [ByteString])
-> ((CI ByteString, ByteString) -> ByteString)
-> (CI ByteString, ByteString)
-> [ByteString]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (CI ByteString, ByteString) -> ByteString
forall a b. (a, b) -> b
snd) ([(CI ByteString, ByteString)] -> [[ByteString]])
-> (Request -> [(CI ByteString, ByteString)])
-> Request
-> [[ByteString]]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ((CI ByteString, ByteString) -> Bool)
-> [(CI ByteString, ByteString)] -> [(CI ByteString, ByteString)]
forall a. (a -> Bool) -> [a] -> [a]
filter ((CI ByteString -> CI ByteString -> Bool
forall a. Eq a => a -> a -> Bool
== CI ByteString
h) (CI ByteString -> Bool)
-> ((CI ByteString, ByteString) -> CI ByteString)
-> (CI ByteString, ByteString)
-> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (CI ByteString, ByteString) -> CI ByteString
forall a b. (a, b) -> a
fst) ([(CI ByteString, ByteString)] -> [(CI ByteString, ByteString)])
-> (Request -> [(CI ByteString, ByteString)])
-> Request
-> [(CI ByteString, ByteString)]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Request -> [(CI ByteString, ByteString)]
requestHeaders (Request -> [[ByteString]])
-> HandlerFor App Request -> HandlerFor App [[ByteString]]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> HandlerFor App Request
forall (m :: * -> *). MonadHandler m => m Request
waiRequest
      ([[Capability]] -> [Capability])
-> HandlerFor App [[Capability]] -> HandlerFor App [Capability]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap [[Capability]] -> [Capability]
forall (m :: * -> *) a. Monad m => m (m a) -> m a
join (HandlerFor App [[Capability]] -> HandlerFor App [Capability])
-> ((ByteString -> HandlerFor App [Capability])
    -> HandlerFor App [[Capability]])
-> (ByteString -> HandlerFor App [Capability])
-> HandlerFor App [Capability]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [ByteString]
-> (ByteString -> HandlerFor App [Capability])
-> HandlerFor App [[Capability]]
forall (t :: * -> *) (f :: * -> *) a b.
(Traversable t, Applicative f) =>
t a -> (a -> f b) -> f (t b)
for ([[ByteString]] -> [ByteString]
forall (m :: * -> *) a. Monad m => m (m a) -> m a
join [[ByteString]]
hs) ((ByteString -> HandlerFor App [Capability])
 -> HandlerFor App [Capability])
-> (ByteString -> HandlerFor App [Capability])
-> HandlerFor App [Capability]
forall a b. (a -> b) -> a -> b
$ \ByteString
x -> case ByteString -> Either ByteString Capability
capabilityFromBS ByteString
x of
        Left ByteString
e -> [] [Capability] -> Handler () -> HandlerFor App [Capability]
forall (f :: * -> *) a b. Functor f => a -> f b -> f a
<$ Text -> Html -> Handler ()
forall (m :: * -> *). MonadHandler m => Text -> Html -> m ()
addMessage Text
"" (Html
"Unknown permission: " Html -> Html -> Html
forall a. Semigroup a => a -> a -> a
<> String -> Html
forall a. ToMarkup a => a -> Html
toHtml (ByteString -> String
BC.unpack ByteString
e))
        Right Capability
c -> [Capability] -> HandlerFor App [Capability]
forall (f :: * -> *) a. Applicative f => a -> f a
pure [Capability
c]
  ViewData -> Handler ViewData
forall (m :: * -> *) a. Monad m => a -> m a
return VD :: WebOpts
-> Day
-> Journal
-> Text
-> Query
-> [QueryOpt]
-> [Capability]
-> ViewData
VD {WebOpts
opts :: WebOpts
opts :: WebOpts
opts, Day
today :: Day
today :: Day
today, Journal
j :: Journal
j :: Journal
j, Text
q :: Text
q :: Text
q, Query
m :: Query
m :: Query
m, [QueryOpt]
qopts :: [QueryOpt]
qopts :: [QueryOpt]
qopts, [Capability]
caps :: [Capability]
caps :: [Capability]
caps}

checkServerSideUiEnabled :: Handler ()
checkServerSideUiEnabled :: Handler ()
checkServerSideUiEnabled = do
  VD{opts :: ViewData -> WebOpts
opts=WebOpts{Bool
serve_api_ :: WebOpts -> Bool
serve_api_ :: Bool
serve_api_}} <- Handler ViewData
getViewData
  Bool -> Handler () -> Handler ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when Bool
serve_api_ (Handler () -> Handler ()) -> Handler () -> Handler ()
forall a b. (a -> b) -> a -> b
$
    -- this one gives 500 internal server error when called from defaultLayout:
    --  permissionDenied "server-side UI is disabled due to --serve-api"
    Status -> Text -> Handler ()
forall (m :: * -> *) c a.
(MonadHandler m, ToTypedContent c) =>
Status -> c -> m a
sendResponseStatus Status
status403 (Text
"server-side UI is disabled due to --serve-api" :: Text)

-- | Find out if the sidebar should be visible. Show it, unless there is a
-- showsidebar cookie set to "0", or a ?sidebar=0 query parameter.
shouldShowSidebar :: Handler Bool
shouldShowSidebar :: HandlerFor App Bool
shouldShowSidebar = do
  Maybe Text
msidebarparam <- Text -> HandlerFor App (Maybe Text)
forall (m :: * -> *). MonadHandler m => Text -> m (Maybe Text)
lookupGetParam Text
"sidebar"
  Maybe Text
msidebarcookie <- Text -> [(Text, Text)] -> Maybe Text
forall a b. Eq a => a -> [(a, b)] -> Maybe b
lookup Text
"showsidebar" ([(Text, Text)] -> Maybe Text)
-> (YesodRequest -> [(Text, Text)]) -> YesodRequest -> Maybe Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. YesodRequest -> [(Text, Text)]
reqCookies (YesodRequest -> Maybe Text)
-> HandlerFor App YesodRequest -> HandlerFor App (Maybe Text)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> HandlerFor App YesodRequest
forall (m :: * -> *). MonadHandler m => m YesodRequest
getRequest
  Bool -> HandlerFor App Bool
forall (m :: * -> *) a. Monad m => a -> m a
return (Bool -> HandlerFor App Bool) -> Bool -> HandlerFor App Bool
forall a b. (a -> b) -> a -> b
$
    let disablevalues :: [Text]
disablevalues = [Text
"",Text
"0"]
    in Bool -> (Text -> Bool) -> Maybe Text -> Bool
forall b a. b -> (a -> b) -> Maybe a -> b
maybe
         (Bool -> Bool
not (Bool -> Bool) -> Bool -> Bool
forall a b. (a -> b) -> a -> b
$ Maybe Text
msidebarcookie Maybe Text -> [Maybe Text] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` (Text -> Maybe Text) -> [Text] -> [Maybe Text]
forall a b. (a -> b) -> [a] -> [b]
map Text -> Maybe Text
forall a. a -> Maybe a
Just [Text]
disablevalues)
         (Bool -> Bool
not (Bool -> Bool) -> (Text -> Bool) -> Text -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Text -> [Text] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [Text]
disablevalues))
         Maybe Text
msidebarparam

-- | Update our copy of the journal if the file changed. If there is an
-- error while reloading, keep the old one and return the error, and set a
-- ui message.
getCurrentJournal :: IORef Journal -> CliOpts -> Day -> Handler (Journal, Maybe String)
getCurrentJournal :: IORef Journal -> CliOpts -> Day -> Handler (Journal, Maybe String)
getCurrentJournal IORef Journal
jref CliOpts
opts Day
d = do
  -- XXX put this inside atomicModifyIORef' for thread safety
  Journal
j <- IO Journal -> HandlerFor App Journal
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IORef Journal -> IO Journal
forall a. IORef a -> IO a
readIORef IORef Journal
jref)
  (Either String Journal
ej, Bool
changed) <- IO (Either String Journal, Bool)
-> HandlerFor App (Either String Journal, Bool)
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO (Either String Journal, Bool)
 -> HandlerFor App (Either String Journal, Bool))
-> IO (Either String Journal, Bool)
-> HandlerFor App (Either String Journal, Bool)
forall a b. (a -> b) -> a -> b
$ CliOpts -> Day -> Journal -> IO (Either String Journal, Bool)
journalReloadIfChanged CliOpts
opts Day
d Journal
j
  -- re-apply any initial filter specified at startup
  let initq :: Query
initq = Day -> ReportOpts -> Query
queryFromOpts Day
d (CliOpts -> ReportOpts
reportopts_ CliOpts
opts)
  case (Bool
changed, Query -> Journal -> Journal
filterJournalTransactions Query
initq (Journal -> Journal)
-> Either String Journal -> Either String Journal
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Either String Journal
ej) of
    (Bool
False, Either String Journal
_) -> (Journal, Maybe String) -> Handler (Journal, Maybe String)
forall (m :: * -> *) a. Monad m => a -> m a
return (Journal
j, Maybe String
forall a. Maybe a
Nothing)
    (Bool
True, Right Journal
j') -> do
      IO () -> Handler ()
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO () -> Handler ()) -> IO () -> Handler ()
forall a b. (a -> b) -> a -> b
$ IORef Journal -> Journal -> IO ()
forall a. IORef a -> a -> IO ()
writeIORef IORef Journal
jref Journal
j'
      (Journal, Maybe String) -> Handler (Journal, Maybe String)
forall (m :: * -> *) a. Monad m => a -> m a
return (Journal
j',Maybe String
forall a. Maybe a
Nothing)
    (Bool
True, Left String
e) -> do
      Html -> Handler ()
forall (m :: * -> *). MonadHandler m => Html -> m ()
setMessage Html
"error while reading journal"
      (Journal, Maybe String) -> Handler (Journal, Maybe String)
forall (m :: * -> *) a. Monad m => a -> m a
return (Journal
j, String -> Maybe String
forall a. a -> Maybe a
Just String
e)