summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES.md17
-rw-r--r--Hledger/Web/Application.hs6
-rw-r--r--Hledger/Web/Foundation.hs29
-rw-r--r--Hledger/Web/Handler/AddR.hs9
-rw-r--r--Hledger/Web/Handler/EditR.hs5
-rw-r--r--Hledger/Web/Handler/JournalR.hs3
-rw-r--r--Hledger/Web/Handler/MiscR.hs18
-rw-r--r--Hledger/Web/Handler/RegisterR.hs57
-rw-r--r--Hledger/Web/Handler/UploadR.hs5
-rw-r--r--Hledger/Web/Json.hs18
-rw-r--r--Hledger/Web/Main.hs10
-rw-r--r--Hledger/Web/WebOptions.hs9
-rw-r--r--hledger-web.1352
-rw-r--r--hledger-web.cabal16
-rw-r--r--hledger-web.info72
-rw-r--r--hledger-web.txt137
-rw-r--r--templates/register.hamlet6
17 files changed, 472 insertions, 297 deletions
diff --git a/CHANGES.md b/CHANGES.md
index 90f8bfa..950b7ed 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,6 +1,23 @@
User-visible changes in hledger-web.
See also the hledger changelog.
+# 1.15 2019-09-01
+
+- web: ?sidebar= hides the sidebar, like ?sidebar=0
+
+- web: --serve-api disables server-side UI
+
+- register page: clip linked accounts to 40 characters (Henning Thielemann)
+ This was the behaviour before adding the links.
+
+- avoid spaces before commas in register account links (Henning Thielemann)
+
+- add links to accounts in register transactions (Henning Thielemann)
+
+- fix "_create_locale could not be located" error on windows 7 (#1039)
+
+- use hledger 1.15
+
# 1.14.1 2019-03-20
- /accounts JSON: return all accounts, not just top-level ones (#985)
diff --git a/Hledger/Web/Application.hs b/Hledger/Web/Application.hs
index 07c546a..aa3a9cd 100644
--- a/Hledger/Web/Application.hs
+++ b/Hledger/Web/Application.hs
@@ -17,13 +17,13 @@ import Yesod.Default.Config
import Hledger.Data (Journal, nulljournal)
import Hledger.Web.Handler.AddR
-import Hledger.Web.Handler.MiscR
+import Hledger.Web.Handler.MiscR
import Hledger.Web.Handler.EditR
import Hledger.Web.Handler.UploadR
import Hledger.Web.Handler.JournalR
import Hledger.Web.Handler.RegisterR
import Hledger.Web.Import
-import Hledger.Web.WebOptions (WebOpts(serve_))
+import Hledger.Web.WebOptions (WebOpts(serve_,serve_api_))
-- This line actually creates our YesodDispatch instance. It is the second half
-- of the call to mkYesodData which occurs in Foundation.hs. Please see the
@@ -41,7 +41,7 @@ makeApplication opts' j' conf' = do
logWare <$> toWaiApp foundation
where
logWare | development = logStdoutDev
- | serve_ opts' = logStdout
+ | serve_ opts' || serve_api_ opts' = logStdout
| otherwise = id
makeFoundation :: AppConfig DefaultEnv Extra -> WebOpts -> IO App
diff --git a/Hledger/Web/Foundation.hs b/Hledger/Web/Foundation.hs
index 67dd03b..82a0be9 100644
--- a/Hledger/Web/Foundation.hs
+++ b/Hledger/Web/Foundation.hs
@@ -16,7 +16,7 @@
module Hledger.Web.Foundation where
-import Control.Monad (join)
+import Control.Monad (join, when)
import qualified Data.ByteString.Char8 as BC
import Data.Traversable (for)
import Data.IORef (IORef, readIORef, writeIORef)
@@ -26,6 +26,7 @@ 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)
@@ -96,9 +97,18 @@ type Form x = Html -> MForm (HandlerT App IO) (FormResult x, Widget)
instance Yesod App where
approot = ApprootMaster $ appRoot . settings
- makeSessionBackend _ = Just <$> defaultClientSessionBackend 120 ".hledger-web_client_session_key.aes"
+ makeSessionBackend _ =
+ let sessionexpirysecs = 120
+ in Just <$> defaultClientSessionBackend sessionexpirysecs ".hledger-web_client_session_key.aes"
+ -- defaultLayout :: WidgetFor site () -> HandlerFor site Html
defaultLayout 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.
+ checkServerSideUiEnabled
+
master <- getYesod
here <- fromMaybe RootR <$> getCurrentRoute
VD {caps, j, m, opts, q, qopts} <- getViewData
@@ -204,13 +214,26 @@ getViewData = do
Right c -> pure [c]
return VD {opts, today, j, q, m, qopts, caps}
+checkServerSideUiEnabled :: Handler ()
+checkServerSideUiEnabled = do
+ VD{opts=WebOpts{serve_api_}} <- getViewData
+ when serve_api_ $
+ -- this one gives 500 internal server error when called from defaultLayout:
+ -- permissionDenied "server-side UI is disabled due to --serve-api"
+ sendResponseStatus status403 ("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 = do
msidebarparam <- lookupGetParam "sidebar"
msidebarcookie <- lookup "showsidebar" . reqCookies <$> getRequest
- return $ maybe (msidebarcookie /= Just "0") (/="0") msidebarparam
+ return $
+ let disablevalues = ["","0"]
+ in maybe
+ (not $ msidebarcookie `elem` map Just disablevalues)
+ (not . (`elem` disablevalues))
+ 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
diff --git a/Hledger/Web/Handler/AddR.hs b/Hledger/Web/Handler/AddR.hs
index 5b74cb9..4507e09 100644
--- a/Hledger/Web/Handler/AddR.hs
+++ b/Hledger/Web/Handler/AddR.hs
@@ -24,10 +24,13 @@ import Hledger.Web.WebOptions (WebOpts(..))
import Hledger.Web.Widget.AddForm (addForm)
getAddR :: Handler ()
-getAddR = postAddR
+getAddR = do
+ checkServerSideUiEnabled
+ postAddR
postAddR :: Handler ()
postAddR = do
+ checkServerSideUiEnabled
VD{caps, j, today} <- getViewData
when (CapAdd `notElem` caps) (permissionDenied "Missing the 'add' capability")
@@ -55,7 +58,7 @@ postAddR = do
|]
-- Add a single new transaction, send as JSON via PUT, to the journal.
--- The web form handler above should probably use PUT as well.
+-- The web form handler above should probably use PUT as well.
putAddR :: Handler RepJson
putAddR = do
VD{caps, j, opts} <- getViewData
@@ -66,4 +69,4 @@ putAddR = do
Error err -> sendStatusJSON status400 ("could not parse json: " ++ err ::String)
Success t -> do
void $ liftIO $ journalAddTransaction j (cliopts_ opts) t
- sendResponseCreated TransactionsR
+ sendResponseCreated TransactionsR
diff --git a/Hledger/Web/Handler/EditR.hs b/Hledger/Web/Handler/EditR.hs
index 8c9eed3..5bf2ca4 100644
--- a/Hledger/Web/Handler/EditR.hs
+++ b/Hledger/Web/Handler/EditR.hs
@@ -23,10 +23,13 @@ editForm f txt =
fs = FieldSettings "text" mzero mzero mzero [("class", "form-control"), ("rows", "25")]
getEditR :: FilePath -> Handler ()
-getEditR = postEditR
+getEditR f = do
+ checkServerSideUiEnabled
+ postEditR f
postEditR :: FilePath -> Handler ()
postEditR f = do
+ checkServerSideUiEnabled
VD {caps, j} <- getViewData
when (CapManage `notElem` caps) (permissionDenied "Missing the 'manage' capability")
diff --git a/Hledger/Web/Handler/JournalR.hs b/Hledger/Web/Handler/JournalR.hs
index fe78de0..7effc05 100644
--- a/Hledger/Web/Handler/JournalR.hs
+++ b/Hledger/Web/Handler/JournalR.hs
@@ -17,6 +17,7 @@ import Hledger.Web.Widget.Common (accountQuery, mixedAmountAsHtml)
-- | The formatted journal view, with sidebar.
getJournalR :: Handler Html
getJournalR = do
+ checkServerSideUiEnabled
VD{caps, j, m, opts, qopts, today} <- getViewData
when (CapView `notElem` caps) (permissionDenied "Missing the 'view' capability")
let title = case inAccount qopts of
@@ -24,7 +25,7 @@ getJournalR = do
Just (a, inclsubs) -> "Transactions in " <> a <> if inclsubs then "" else " (excluding subaccounts)"
title' = title <> if m /= Any then ", filtered" else ""
acctlink a = (RegisterR, [("q", accountQuery a)])
- (_, items) = journalTransactionsReport (reportopts_ $ cliopts_ opts) j m
+ (_, items) = transactionsReport (reportopts_ $ cliopts_ opts) j m
defaultLayout $ do
setTitle "journal - hledger-web"
diff --git a/Hledger/Web/Handler/MiscR.hs b/Hledger/Web/Handler/MiscR.hs
index 3ffecdd..fe04c74 100644
--- a/Hledger/Web/Handler/MiscR.hs
+++ b/Hledger/Web/Handler/MiscR.hs
@@ -7,11 +7,11 @@
{-# LANGUAGE TemplateHaskell #-}
module Hledger.Web.Handler.MiscR
- ( getAccountnamesR
- , getTransactionsR
- , getPricesR
- , getCommoditiesR
- , getAccountsR
+ ( getAccountnamesR
+ , getTransactionsR
+ , getPricesR
+ , getCommoditiesR
+ , getAccountsR
, getAccounttransactionsR
, getDownloadR
, getFaviconR
@@ -30,10 +30,13 @@ import Hledger.Web.Import
import Hledger.Web.Widget.Common (journalFile404)
getRootR :: Handler Html
-getRootR = redirect JournalR
+getRootR = do
+ checkServerSideUiEnabled
+ redirect JournalR
getManageR :: Handler Html
getManageR = do
+ checkServerSideUiEnabled
VD{caps, j} <- getViewData
when (CapManage `notElem` caps) (permissionDenied "Missing the 'manage' capability")
defaultLayout $ do
@@ -42,6 +45,7 @@ getManageR = do
getDownloadR :: FilePath -> Handler TypedContent
getDownloadR f = do
+ checkServerSideUiEnabled
VD{caps, j} <- getViewData
when (CapManage `notElem` caps) (permissionDenied "Missing the 'manage' capability")
(f', txt) <- journalFile404 f j
@@ -69,7 +73,7 @@ getPricesR = do
VD{caps, j} <- getViewData
when (CapView `notElem` caps) (permissionDenied "Missing the 'view' capability")
selectRep $ do
- provideJson $ jmarketprices j
+ provideJson $ map priceDirectiveToMarketPrice $ jpricedirectives j
getCommoditiesR :: Handler TypedContent
getCommoditiesR = do
diff --git a/Hledger/Web/Handler/RegisterR.hs b/Hledger/Web/Handler/RegisterR.hs
index 5c663f4..3d22920 100644
--- a/Hledger/Web/Handler/RegisterR.hs
+++ b/Hledger/Web/Handler/RegisterR.hs
@@ -8,7 +8,7 @@
module Hledger.Web.Handler.RegisterR where
-import Data.List (intersperse)
+import Data.List (intersperse, nub, partition)
import qualified Data.Text as T
import Text.Hamlet (hamletFile)
@@ -17,11 +17,12 @@ import Hledger.Cli.CliOptions
import Hledger.Web.Import
import Hledger.Web.WebOptions
import Hledger.Web.Widget.AddForm (addModal)
-import Hledger.Web.Widget.Common (mixedAmountAsHtml)
+import Hledger.Web.Widget.Common (accountQuery, mixedAmountAsHtml)
-- | The main journal/account register view, with accounts sidebar.
getRegisterR :: Handler Html
getRegisterR = do
+ checkServerSideUiEnabled
VD{caps, j, m, opts, qopts, today} <- getViewData
when (CapView `notElem` caps) (permissionDenied "Missing the 'view' capability")
@@ -32,12 +33,64 @@ getRegisterR = do
let ropts = reportopts_ (cliopts_ opts)
acctQuery = fromMaybe Any (inAccountQuery qopts)
+ acctlink acc = (RegisterR, [("q", accountQuery acc)])
+ otherTransAccounts =
+ map (\(acct,(name,comma)) -> (acct, (T.pack name, T.pack comma))) .
+ undecorateLinks . elideRightDecorated 40 . decorateLinks .
+ addCommas . preferReal . otherTransactionAccounts m acctQuery
+ addCommas xs =
+ zip xs $
+ zip (map (T.unpack . accountSummarisedName . paccount) xs) $
+ tail $ (", "<$xs) ++ [""]
r@(balancelabel,items) = accountTransactionsReport ropts j m acctQuery
balancelabel' = if isJust (inAccount qopts) then balancelabel else "Total"
defaultLayout $ do
setTitle "register - hledger-web"
$(widgetFile "register")
+-- cf. Hledger.Reports.AccountTransactionsReport.accountTransactionsReportItems
+otherTransactionAccounts :: Query -> Query -> Transaction -> [Posting]
+otherTransactionAccounts reportq thisacctq torig
+ -- no current account ? summarise all matched postings
+ | thisacctq == None = reportps
+ -- only postings to current account ? summarise those
+ | null otheraccts = thisacctps
+ -- summarise matched postings to other account(s)
+ | otherwise = otheracctps
+ where
+ reportps = tpostings $ filterTransactionPostings reportq torig
+ (thisacctps, otheracctps) = partition (matchesPosting thisacctq) reportps
+ otheraccts = nub $ map paccount otheracctps
+
+-- cf. Hledger.Reports.AccountTransactionsReport.summarisePostingAccounts
+preferReal :: [Posting] -> [Posting]
+preferReal ps
+ | null realps = ps
+ | otherwise = realps
+ where realps = filter isReal ps
+
+elideRightDecorated :: Int -> [(Maybe d, Char)] -> [(Maybe d, Char)]
+elideRightDecorated width s =
+ if length s > width
+ then take (width - 2) s ++ map ((,) Nothing) ".."
+ else s
+
+undecorateLinks :: [(Maybe acct, char)] -> [(acct, ([char], [char]))]
+undecorateLinks [] = []
+undecorateLinks xs0@(x:_) =
+ case x of
+ (Just acct, _) ->
+ let (link, xs1) = span (isJust . fst) xs0
+ (comma, xs2) = span (isNothing . fst) xs1
+ in (acct, (map snd link, map snd comma)) : undecorateLinks xs2
+ _ -> error "link name not decorated with account"
+
+decorateLinks :: [(acct, ([char], [char]))] -> [(Maybe acct, char)]
+decorateLinks =
+ concatMap
+ (\(acct, (name, comma)) ->
+ map ((,) (Just acct)) name ++ map ((,) Nothing) comma)
+
-- | Generate javascript/html for a register balance line chart based on
-- the provided "TransactionsReportItem"s.
registerChartHtml :: [(CommoditySymbol, (String, [TransactionsReportItem]))] -> HtmlUrl AppRoute
diff --git a/Hledger/Web/Handler/UploadR.hs b/Hledger/Web/Handler/UploadR.hs
index c7d02d6..241c07d 100644
--- a/Hledger/Web/Handler/UploadR.hs
+++ b/Hledger/Web/Handler/UploadR.hs
@@ -27,10 +27,13 @@ uploadForm f =
fs = FieldSettings "file" Nothing (Just "file") (Just "file") []
getUploadR :: FilePath -> Handler ()
-getUploadR = postUploadR
+getUploadR f = do
+ checkServerSideUiEnabled
+ postUploadR f
postUploadR :: FilePath -> Handler ()
postUploadR f = do
+ checkServerSideUiEnabled
VD {caps, j} <- getViewData
when (CapManage `notElem` caps) (permissionDenied "Missing the 'manage' capability")
diff --git a/Hledger/Web/Json.hs b/Hledger/Web/Json.hs
index ab9572d..9b7b498 100644
--- a/Hledger/Web/Json.hs
+++ b/Hledger/Web/Json.hs
@@ -21,7 +21,7 @@
--{-# LANGUAGE TypeFamilies #-}
--{-# LANGUAGE TypeOperators #-}
-module Hledger.Web.Json (
+module Hledger.Web.Json (
-- * Instances
-- * Utilities
readJsonFile
@@ -51,7 +51,7 @@ instance ToJSON Side
instance ToJSON DigitGroupStyle
instance ToJSON MixedAmount
instance ToJSON BalanceAssertion
-instance ToJSON Price
+instance ToJSON AmountPrice
instance ToJSON MarketPrice
instance ToJSON PostingType
@@ -66,11 +66,11 @@ instance ToJSON Posting where
,"ptype" .= toJSON ptype
,"ptags" .= toJSON ptags
,"pbalanceassertion" .= toJSON pbalanceassertion
- -- To avoid a cycle, show just the parent transaction's index number
+ -- To avoid a cycle, show just the parent transaction's index number
-- in a dummy field. When re-parsed, there will be no parent.
,"ptransaction_" .= toJSON (maybe "" (show.tindex) ptransaction)
-- This is probably not wanted in json, we discard it.
- ,"poriginal" .= toJSON (Nothing :: Maybe Posting)
+ ,"poriginal" .= toJSON (Nothing :: Maybe Posting)
]
instance ToJSON Transaction
@@ -82,7 +82,7 @@ instance ToJSON Account where
,"aibalance" .= toJSON (aibalance a)
,"anumpostings" .= toJSON (anumpostings a)
,"aboring" .= toJSON (aboring a)
- -- To avoid a cycle, show just the parent account's name
+ -- To avoid a cycle, show just the parent account's name
-- in a dummy field. When re-parsed, there will be no parent.
,"aparent_" .= toJSON (maybe "" aname $ aparent a)
-- Just the names of subaccounts, as a dummy field, ignored when parsed.
@@ -103,21 +103,21 @@ instance FromJSON Side
instance FromJSON DigitGroupStyle
instance FromJSON MixedAmount
instance FromJSON BalanceAssertion
-instance FromJSON Price
+instance FromJSON AmountPrice
instance FromJSON MarketPrice
instance FromJSON PostingType
instance FromJSON Posting
instance FromJSON Transaction
instance FromJSON AccountDeclarationInfo
-- XXX The ToJSON instance replaces subaccounts with just names.
--- Here we should try to make use of those to reconstruct the
+-- Here we should try to make use of those to reconstruct the
-- parent-child relationships.
instance FromJSON Account
-- Decimal, various attempts
--
-- https://stackoverflow.com/questions/40331851/haskell-data-decimal-as-aeson-type
-----instance FromJSON Decimal where parseJSON =
+----instance FromJSON Decimal where parseJSON =
---- A.withScientific "Decimal" (return . right . eitherFromRational . toRational)
--
-- https://github.com/bos/aeson/issues/474
@@ -156,7 +156,7 @@ instance FromJSON (DecimalRaw Integer)
readJsonFile :: FromJSON a => FilePath -> IO a
readJsonFile f = do
bs <- BL.readFile f
- let v = fromMaybe (error "could not decode bytestring as json value") (decode bs :: Maybe Value)
+ let v = fromMaybe (error "could not decode bytestring as json value") (decode bs :: Maybe Value)
case fromJSON v :: FromJSON a => Result a of
Error e -> error e
Success t -> return t
diff --git a/Hledger/Web/Main.hs b/Hledger/Web/Main.hs
index 4b53216..c3173d1 100644
--- a/Hledger/Web/Main.hs
+++ b/Hledger/Web/Main.hs
@@ -69,16 +69,18 @@ web opts j = do
}
app <- makeApplication opts j' appconfig
-- XXX would like to allow a host name not just an IP address here
- _ <- printf "Starting web app on IP address %s port %d with base url %s\n" h p u
- if serve_ opts
+ _ <- printf "Serving web %s on %s:%d with base url %s\n"
+ (if serve_api_ opts then "API" else "UI and API" :: String) h p u
+ if serve_ opts || serve_api_ opts
then do
putStrLn "Press ctrl-c to quit"
hFlush stdout
let warpsettings = setHost (fromString h) (setPort p defaultSettings)
Network.Wai.Handler.Warp.runSettings warpsettings app
else do
- putStrLn "Starting web browser..."
- putStrLn "Web app will auto-exit after a few minutes with no browsers (or press ctrl-c)"
+ putStrLn "This server will exit after 2m with no browser windows open (or press ctrl-c)"
+ putStrLn "Opening web browser..."
hFlush stdout
+ -- exits after 2m of inactivity (hardcoded)
Network.Wai.Handler.Launch.runHostPortUrl h p "" app
diff --git a/Hledger/Web/WebOptions.hs b/Hledger/Web/WebOptions.hs
index d6f6411..ef5ac10 100644
--- a/Hledger/Web/WebOptions.hs
+++ b/Hledger/Web/WebOptions.hs
@@ -31,6 +31,10 @@ webflags =
["serve", "server"]
(setboolopt "serve")
"serve and log requests, don't browse or auto-exit"
+ , flagNone
+ ["serve-api"]
+ (setboolopt "serve-api")
+ "like --serve, but serve only the JSON web API, without the server-side web UI"
, flagReq
["host"]
(\s opts -> Right $ setopt "host" s opts)
@@ -75,6 +79,7 @@ webmode =
Group
{ groupUnnamed = webflags
, groupHidden =
+ hiddenflags ++
[ flagNone
["binary-filename"]
(setboolopt "binary-filename")
@@ -88,6 +93,7 @@ webmode =
-- hledger-web options, used in hledger-web and above
data WebOpts = WebOpts
{ serve_ :: Bool
+ , serve_api_ :: Bool
, host_ :: String
, port_ :: Int
, base_url_ :: String
@@ -98,7 +104,7 @@ data WebOpts = WebOpts
} deriving (Show)
defwebopts :: WebOpts
-defwebopts = WebOpts def def def def def [CapView, CapAdd] Nothing def
+defwebopts = WebOpts def def def def def def [CapView, CapAdd] Nothing def
instance Default WebOpts where def = defwebopts
@@ -119,6 +125,7 @@ rawOptsToWebOpts rawopts =
return
defwebopts
{ serve_ = boolopt "serve" rawopts
+ , serve_api_ = boolopt "serve-api" rawopts
, host_ = h
, port_ = p
, base_url_ = b
diff --git a/hledger-web.1 b/hledger-web.1
index 99625a8..1ec7240 100644
--- a/hledger-web.1
+++ b/hledger-web.1
@@ -1,34 +1,34 @@
-.TH "hledger\-web" "1" "March 2019" "hledger\-web 1.14" "hledger User Manuals"
+.TH "hledger-web" "1" "August 2019" "hledger-web 1.15" "hledger User Manuals"
.SH NAME
.PP
-hledger\-web \- web interface for the hledger accounting tool
+hledger-web - web interface for the hledger accounting tool
.SH SYNOPSIS
.PP
-\f[C]hledger\-web\ [OPTIONS]\f[]
+\f[C]hledger-web [OPTIONS]\f[R]
.PD 0
.P
.PD
-\f[C]hledger\ web\ \-\-\ [OPTIONS]\f[]
+\f[C]hledger web -- [OPTIONS]\f[R]
.SH DESCRIPTION
.PP
-hledger is a cross\-platform program for tracking money, time, or any
-other commodity, using double\-entry accounting and a simple, editable
+hledger is a cross-platform program for tracking money, time, or any
+other commodity, using double-entry accounting and a simple, editable
file format.
hledger is inspired by and largely compatible with ledger(1).
.PP
-hledger\-web is hledger\[aq]s web interface.
+hledger-web is hledger\[aq]s web interface.
It starts a simple web application for browsing and adding transactions,
and optionally opens it in a web browser window if possible.
-It provides a more user\-friendly UI than the hledger CLI or hledger\-ui
+It provides a more user-friendly UI than the hledger CLI or hledger-ui
interface, showing more at once (accounts, the current account register,
-balance charts) and allowing history\-aware data entry, interactive
+balance charts) and allowing history-aware data entry, interactive
searching, and bookmarking.
.PP
-hledger\-web also lets you share a ledger with multiple users, or even
+hledger-web also lets you share a ledger with multiple users, or even
the public web.
There is no access control, so if you need that you should put it behind
a suitable web proxy.
@@ -37,201 +37,139 @@ instance, it writes a numbered backup of the main journal file (only ?)
on every edit.
.PP
Like hledger, it reads data from one or more files in hledger journal,
-timeclock, timedot, or CSV format specified with \f[C]\-f\f[], or
-\f[C]$LEDGER_FILE\f[], or \f[C]$HOME/.hledger.journal\f[] (on windows,
-perhaps \f[C]C:/Users/USER/.hledger.journal\f[]).
+timeclock, timedot, or CSV format specified with \f[C]-f\f[R], or
+\f[C]$LEDGER_FILE\f[R], or \f[C]$HOME/.hledger.journal\f[R] (on windows,
+perhaps \f[C]C:/Users/USER/.hledger.journal\f[R]).
For more about this see hledger(1), hledger_journal(5) etc.
.SH OPTIONS
.PP
-Command\-line options and arguments may be used to set an initial filter
+Command-line options and arguments may be used to set an initial filter
on the data.
These filter options are not shown in the web UI, but it will be applied
in addition to any search query entered there.
.PP
-Note: if invoking hledger\-web as a hledger subcommand, write
-\f[C]\-\-\f[] before options, as shown in the synopsis above.
+Note: if invoking hledger-web as a hledger subcommand, write
+\f[C]--\f[R] before options, as shown in the synopsis above.
.TP
-.B \f[C]\-\-serve\f[]
-serve and log requests, don\[aq]t browse or auto\-exit
-.RS
-.RE
+.B \f[C]--serve\f[R]
+serve and log requests, don\[aq]t browse or auto-exit
+.TP
+.B \f[C]--serve-api\f[R]
+like --serve, but serve only the JSON web API, without the server-side
+web UI
.TP
-.B \f[C]\-\-host=IPADDR\f[]
+.B \f[C]--host=IPADDR\f[R]
listen on this IP address (default: 127.0.0.1)
-.RS
-.RE
.TP
-.B \f[C]\-\-port=PORT\f[]
+.B \f[C]--port=PORT\f[R]
listen on this TCP port (default: 5000)
-.RS
-.RE
.TP
-.B \f[C]\-\-base\-url=URL\f[]
+.B \f[C]--base-url=URL\f[R]
set the base url (default: http://IPADDR:PORT).
You would change this when sharing over the network, or integrating
within a larger website.
-.RS
-.RE
.TP
-.B \f[C]\-\-file\-url=URL\f[]
+.B \f[C]--file-url=URL\f[R]
set the static files url (default: BASEURL/static).
-hledger\-web normally serves static files itself, but if you wanted to
+hledger-web normally serves static files itself, but if you wanted to
serve them from another server for efficiency, you would set the url
with this.
-.RS
-.RE
.TP
-.B \f[C]\-\-capabilities=CAP[,CAP..]\f[]
+.B \f[C]--capabilities=CAP[,CAP..]\f[R]
enable the view, add, and/or manage capabilities (default: view,add)
-.RS
-.RE
.TP
-.B \f[C]\-\-capabilities\-header=HTTPHEADER\f[]
+.B \f[C]--capabilities-header=HTTPHEADER\f[R]
read capabilities to enable from a HTTP header, like
-X\-Sandstorm\-Permissions (default: disabled)
-.RS
-.RE
+X-Sandstorm-Permissions (default: disabled)
.PP
hledger input options:
.TP
-.B \f[C]\-f\ FILE\ \-\-file=FILE\f[]
+.B \f[C]-f FILE --file=FILE\f[R]
use a different input file.
-For stdin, use \- (default: \f[C]$LEDGER_FILE\f[] or
-\f[C]$HOME/.hledger.journal\f[])
-.RS
-.RE
+For stdin, use - (default: \f[C]$LEDGER_FILE\f[R] or
+\f[C]$HOME/.hledger.journal\f[R])
.TP
-.B \f[C]\-\-rules\-file=RULESFILE\f[]
+.B \f[C]--rules-file=RULESFILE\f[R]
Conversion rules file to use when reading CSV (default: FILE.rules)
-.RS
-.RE
.TP
-.B \f[C]\-\-separator=CHAR\f[]
+.B \f[C]--separator=CHAR\f[R]
Field separator to expect when reading CSV (default: \[aq],\[aq])
-.RS
-.RE
.TP
-.B \f[C]\-\-alias=OLD=NEW\f[]
+.B \f[C]--alias=OLD=NEW\f[R]
rename accounts named OLD to NEW
-.RS
-.RE
.TP
-.B \f[C]\-\-anon\f[]
+.B \f[C]--anon\f[R]
anonymize accounts and payees
-.RS
-.RE
.TP
-.B \f[C]\-\-pivot\ FIELDNAME\f[]
+.B \f[C]--pivot FIELDNAME\f[R]
use some other field or tag for the account name
-.RS
-.RE
.TP
-.B \f[C]\-I\ \-\-ignore\-assertions\f[]
+.B \f[C]-I --ignore-assertions\f[R]
ignore any failing balance assertions
-.RS
-.RE
.PP
hledger reporting options:
.TP
-.B \f[C]\-b\ \-\-begin=DATE\f[]
+.B \f[C]-b --begin=DATE\f[R]
include postings/txns on or after this date
-.RS
-.RE
.TP
-.B \f[C]\-e\ \-\-end=DATE\f[]
+.B \f[C]-e --end=DATE\f[R]
include postings/txns before this date
-.RS
-.RE
.TP
-.B \f[C]\-D\ \-\-daily\f[]
+.B \f[C]-D --daily\f[R]
multiperiod/multicolumn report by day
-.RS
-.RE
.TP
-.B \f[C]\-W\ \-\-weekly\f[]
+.B \f[C]-W --weekly\f[R]
multiperiod/multicolumn report by week
-.RS
-.RE
.TP
-.B \f[C]\-M\ \-\-monthly\f[]
+.B \f[C]-M --monthly\f[R]
multiperiod/multicolumn report by month
-.RS
-.RE
.TP
-.B \f[C]\-Q\ \-\-quarterly\f[]
+.B \f[C]-Q --quarterly\f[R]
multiperiod/multicolumn report by quarter
-.RS
-.RE
.TP
-.B \f[C]\-Y\ \-\-yearly\f[]
+.B \f[C]-Y --yearly\f[R]
multiperiod/multicolumn report by year
-.RS
-.RE
.TP
-.B \f[C]\-p\ \-\-period=PERIODEXP\f[]
+.B \f[C]-p --period=PERIODEXP\f[R]
set start date, end date, and/or reporting interval all at once using
-period expressions syntax (overrides the flags above)
-.RS
-.RE
+period expressions syntax
.TP
-.B \f[C]\-\-date2\f[]
+.B \f[C]--date2\f[R]
match the secondary date instead (see command help for other effects)
-.RS
-.RE
.TP
-.B \f[C]\-U\ \-\-unmarked\f[]
-include only unmarked postings/txns (can combine with \-P or \-C)
-.RS
-.RE
+.B \f[C]-U --unmarked\f[R]
+include only unmarked postings/txns (can combine with -P or -C)
.TP
-.B \f[C]\-P\ \-\-pending\f[]
+.B \f[C]-P --pending\f[R]
include only pending postings/txns
-.RS
-.RE
.TP
-.B \f[C]\-C\ \-\-cleared\f[]
+.B \f[C]-C --cleared\f[R]
include only cleared postings/txns
-.RS
-.RE
.TP
-.B \f[C]\-R\ \-\-real\f[]
-include only non\-virtual postings
-.RS
-.RE
+.B \f[C]-R --real\f[R]
+include only non-virtual postings
.TP
-.B \f[C]\-NUM\ \-\-depth=NUM\f[]
+.B \f[C]-NUM --depth=NUM\f[R]
hide/aggregate accounts or postings more than NUM levels deep
-.RS
-.RE
.TP
-.B \f[C]\-E\ \-\-empty\f[]
-show items with zero amount, normally hidden (and vice\-versa in
-hledger\-ui/hledger\-web)
-.RS
-.RE
+.B \f[C]-E --empty\f[R]
+show items with zero amount, normally hidden (and vice-versa in
+hledger-ui/hledger-web)
.TP
-.B \f[C]\-B\ \-\-cost\f[]
+.B \f[C]-B --cost\f[R]
convert amounts to their cost at transaction time (using the transaction
price, if any)
-.RS
-.RE
.TP
-.B \f[C]\-V\ \-\-value\f[]
+.B \f[C]-V --value\f[R]
convert amounts to their market value on the report end date (using the
most recent applicable market price, if any)
-.RS
-.RE
.TP
-.B \f[C]\-\-auto\f[]
+.B \f[C]--auto\f[R]
apply automated posting rules to modify transactions.
-.RS
-.RE
.TP
-.B \f[C]\-\-forecast\f[]
+.B \f[C]--forecast\f[R]
apply periodic transaction rules to generate future transactions, to 6
months from now or report end date.
-.RS
-.RE
.PP
When a reporting option appears more than once in the command line, the
last one takes precedence.
@@ -240,59 +178,55 @@ Some reporting options can also be written as query arguments.
.PP
hledger help options:
.TP
-.B \f[C]\-h\ \-\-help\f[]
+.B \f[C]-h --help\f[R]
show general usage (or after COMMAND, command usage)
-.RS
-.RE
.TP
-.B \f[C]\-\-version\f[]
+.B \f[C]--version\f[R]
show version
-.RS
-.RE
.TP
-.B \f[C]\-\-debug[=N]\f[]
-show debug output (levels 1\-9, default: 1)
-.RS
-.RE
+.B \f[C]--debug[=N]\f[R]
+show debug output (levels 1-9, default: 1)
.PP
-A \@FILE argument will be expanded to the contents of FILE, which should
-contain one command line option/argument per line.
-(To prevent this, insert a \f[C]\-\-\f[] argument before.)
+A \[at]FILE argument will be expanded to the contents of FILE, which
+should contain one command line option/argument per line.
+(To prevent this, insert a \f[C]--\f[R] argument before.)
.PP
-By default, hledger\-web starts the web app in "transient mode" and also
-opens it in your default web browser if possible.
+By default, hledger-web starts the web app in \[dq]transient mode\[dq]
+and also opens it in your default web browser if possible.
In this mode the web app will keep running for as long as you have it
open in a browser window, and will exit after two minutes of inactivity
(no requests and no browser windows viewing it).
-With \f[C]\-\-serve\f[], it just runs the web app without exiting, and
+With \f[C]--serve\f[R], it just runs the web app without exiting, and
logs requests to the console.
+With \f[C]--serve-api\f[R], only the JSON web api (see below) is served,
+with the usual HTML server-side web UI disabled.
.PP
By default the server listens on IP address 127.0.0.1, accessible only
to local requests.
-You can use \f[C]\-\-host\f[] to change this, eg
-\f[C]\-\-host\ 0.0.0.0\f[] to listen on all configured addresses.
+You can use \f[C]--host\f[R] to change this, eg \f[C]--host 0.0.0.0\f[R]
+to listen on all configured addresses.
.PP
-Similarly, use \f[C]\-\-port\f[] to set a TCP port other than 5000, eg
-if you are running multiple hledger\-web instances.
+Similarly, use \f[C]--port\f[R] to set a TCP port other than 5000, eg if
+you are running multiple hledger-web instances.
.PP
-You can use \f[C]\-\-base\-url\f[] to change the protocol, hostname,
-port and path that appear in hyperlinks, useful eg for integrating
-hledger\-web within a larger website.
-The default is \f[C]http://HOST:PORT/\f[] using the server\[aq]s
-configured host address and TCP port (or \f[C]http://HOST\f[] if PORT is
-80).
+You can use \f[C]--base-url\f[R] to change the protocol, hostname, port
+and path that appear in hyperlinks, useful eg for integrating
+hledger-web within a larger website.
+The default is \f[C]http://HOST:PORT/\f[R] using the server\[aq]s
+configured host address and TCP port (or \f[C]http://HOST\f[R] if PORT
+is 80).
.PP
-With \f[C]\-\-file\-url\f[] you can set a different base url for static
-files, eg for better caching or cookie\-less serving on high performance
+With \f[C]--file-url\f[R] you can set a different base url for static
+files, eg for better caching or cookie-less serving on high performance
websites.
.SH PERMISSIONS
.PP
-By default, hledger\-web allows anyone who can reach it to view the
+By default, hledger-web allows anyone who can reach it to view the
journal and to add new transactions, but not to change existing data.
.PP
You can restrict who can reach it by
.IP \[bu] 2
-setting the IP address it listens on (see \f[C]\-\-host\f[] above).
+setting the IP address it listens on (see \f[C]--host\f[R] above).
By default it listens on 127.0.0.1, accessible to all users on the local
machine.
.IP \[bu] 2
@@ -302,56 +236,62 @@ custom firewall rules
.PP
You can restrict what the users who reach it can do, by
.IP \[bu] 2
-using the \f[C]\-\-capabilities=CAP[,CAP..]\f[] flag when you start it,
+using the \f[C]--capabilities=CAP[,CAP..]\f[R] flag when you start it,
enabling one or more of the following capabilities.
-The default value is \f[C]view,add\f[]:
+The default value is \f[C]view,add\f[R]:
.RS 2
.IP \[bu] 2
-\f[C]view\f[] \- allows viewing the journal file and all included files
+\f[C]view\f[R] - allows viewing the journal file and all included files
.IP \[bu] 2
-\f[C]add\f[] \- allows adding new transactions to the main journal file
+\f[C]add\f[R] - allows adding new transactions to the main journal file
.IP \[bu] 2
-\f[C]manage\f[] \- allows editing, uploading or downloading the main or
+\f[C]manage\f[R] - allows editing, uploading or downloading the main or
included files
.RE
.IP \[bu] 2
-using the \f[C]\-\-capabilities\-header=HTTPHEADER\f[] flag to specify a
+using the \f[C]--capabilities-header=HTTPHEADER\f[R] flag to specify a
HTTP header from which it will read capabilities to enable.
-hledger\-web on Sandstorm uses the X\-Sandstorm\-Permissions header to
+hledger-web on Sandstorm uses the X-Sandstorm-Permissions header to
integrate with Sandstorm\[aq]s permissions.
This is disabled by default.
.SH EDITING, UPLOADING, DOWNLOADING
.PP
-If you enable the \f[C]manage\f[] capability mentioned above, you\[aq]ll
-see a new "spanner" button to the right of the search form.
+If you enable the \f[C]manage\f[R] capability mentioned above,
+you\[aq]ll see a new \[dq]spanner\[dq] button to the right of the search
+form.
Clicking this will let you edit, upload, or download the journal file or
any files it includes.
.PP
Note, unlike any other hledger command, in this mode you (or any
visitor) can alter or wipe the data files.
.PP
-Normally whenever a file is changed in this way, hledger\-web saves a
+Normally whenever a file is changed in this way, hledger-web saves a
numbered backup (assuming file permissions allow it, the disk is not
-full, etc.) hledger\-web is not aware of version control systems,
+full, etc.) hledger-web is not aware of version control systems,
currently; if you use one, you\[aq]ll have to arrange to commit the
changes yourself (eg with a cron job or a file watcher like entr).
.PP
-Changes which would leave the journal file(s) unparseable or non\-valid
+Changes which would leave the journal file(s) unparseable or non-valid
(eg with failing balance assertions) are prevented.
(Probably.
-This needs re\-testing.)
+This needs re-testing.)
.SH RELOADING
.PP
-hledger\-web detects changes made to the files by other means (eg if you
-edit it directly, outside of hledger\-web), and it will show the new
-data when you reload the page or navigate to a new page.
-If a change makes a file unparseable, hledger\-web will display an error
+hledger-web detects changes made to the files by other means (eg if you
+edit it directly, outside of hledger-web), and it will show the new data
+when you reload the page or navigate to a new page.
+If a change makes a file unparseable, hledger-web will display an error
message until the file has been fixed.
+.PP
+(Note: if you are viewing files mounted from another machine, make sure
+that both machine clocks are roughly in step.)
.SH JSON API
.PP
-In addition to the web UI, hledger\-web provides some JSON API routes.
-These are similar to the API provided by the hledger\-api tool, but it
-may be convenient to have them in hledger\-web also.
+In addition to the web UI, hledger-web provides some API routes that
+serve JSON in response to GET requests.
+Currently these are same ones provided by the hledger-api tool, but
+hledger-web will likely receive more attention than hledger-api in
+future:
.IP
.nf
\f[C]
@@ -361,31 +301,69 @@ may be convenient to have them in hledger\-web also.
/commodities
/accounts
/accounttransactions/#AccountName
-\f[]
+\f[R]
+.fi
+.PP
+Also, you can append a new transaction to the journal by sending a PUT
+request to \f[C]/add\f[R] (hledger-web only).
+As with the web UI\[aq]s add form, hledger-web must be started with the
+\f[C]add\f[R] capability for this (enabled by default).
+.PP
+The payload should be a valid hledger transaction as JSON, similar to
+what you get from \f[C]/transactions\f[R] or
+\f[C]/accounttransactions\f[R].
+.PP
+Another way to generate test data is with the
+\f[C]readJsonFile\f[R]/\f[C]writeJsonFile\f[R] helpers in
+Hledger.Web.Json, which read or write any of hledger\[aq]s JSON-capable
+types from or to a file.
+Eg here we write the first transaction of a sample journal:
+.IP
+.nf
+\f[C]
+$ make ghci-web
+>>> :m +*Hledger.Web.Json
+>>> writeJsonFile \[dq]txn.json\[dq] (head $ jtxns samplejournal)
+>>> :q
+$ python -m json.tool <txn.json >txn.pretty.json # optional: make human-readable
+\f[R]
.fi
+.PP
+(sample output & discussion)
+.PP
+And here\[aq]s how to test adding that with curl:
+.IP
+.nf
+\f[C]
+$ curl -s http://127.0.0.1:5000/add -X PUT -H \[aq]Content-Type: application/json\[aq] --data-binary \[at]txn.pretty.json; echo
+\f[R]
+.fi
+.PP
+By default, both the server-side HTML UI and the JSON API are served.
+Running with \f[C]--serve-api\f[R] disables the former, useful if you
+only want to serve the API.
.SH ENVIRONMENT
.PP
-\f[B]LEDGER_FILE\f[] The journal file path when not specified with
-\f[C]\-f\f[].
-Default: \f[C]~/.hledger.journal\f[] (on windows, perhaps
-\f[C]C:/Users/USER/.hledger.journal\f[]).
+\f[B]LEDGER_FILE\f[R] The journal file path when not specified with
+\f[C]-f\f[R].
+Default: \f[C]\[ti]/.hledger.journal\f[R] (on windows, perhaps
+\f[C]C:/Users/USER/.hledger.journal\f[R]).
.SH FILES
.PP
Reads data from one or more files in hledger journal, timeclock,
-timedot, or CSV format specified with \f[C]\-f\f[], or
-\f[C]$LEDGER_FILE\f[], or \f[C]$HOME/.hledger.journal\f[] (on windows,
-perhaps \f[C]C:/Users/USER/.hledger.journal\f[]).
+timedot, or CSV format specified with \f[C]-f\f[R], or
+\f[C]$LEDGER_FILE\f[R], or \f[C]$HOME/.hledger.journal\f[R] (on windows,
+perhaps \f[C]C:/Users/USER/.hledger.journal\f[R]).
.SH BUGS
.PP
-The need to precede options with \f[C]\-\-\f[] when invoked from hledger
+The need to precede options with \f[C]--\f[R] when invoked from hledger
is awkward.
.PP
-\f[C]\-f\-\f[] doesn\[aq]t work (hledger\-web can\[aq]t read from
-stdin).
+\f[C]-f-\f[R] doesn\[aq]t work (hledger-web can\[aq]t read from stdin).
.PP
Query arguments and some hledger options are ignored.
.PP
-Does not work in text\-mode browsers.
+Does not work in text-mode browsers.
.PP
Does not work well on small screens.
diff --git a/hledger-web.cabal b/hledger-web.cabal
index 5e332c4..0510b75 100644
--- a/hledger-web.cabal
+++ b/hledger-web.cabal
@@ -1,13 +1,13 @@
cabal-version: 1.12
--- This file has been generated from package.yaml by hpack version 0.31.1.
+-- This file has been generated from package.yaml by hpack version 0.31.2.
--
-- see: https://github.com/sol/hpack
--
--- hash: 8eb80f6aa7e34e0b036d490c3c0f5994e3959f33045102d18cac6367c369c352
+-- hash: 0edf95448e05cc75fb3c049d3ecc20264e8869497966ac5eb8783ee803dbce35
name: hledger-web
-version: 1.14.1
+version: 1.15
synopsis: Web interface for the hledger accounting tool
description: This is hledger's web interface.
It provides a more user-friendly and collaborative UI than the
@@ -27,7 +27,7 @@ author: Simon Michael <simon@joyful.com>
maintainer: Simon Michael <simon@joyful.com>
license: GPL-3
license-file: LICENSE
-tested-with: GHC==7.10.3, GHC==8.0.2, GHC==8.2.2, GHC==8.4.3, GHC==8.6.3
+tested-with: GHC==7.10.3, GHC==8.0.2, GHC==8.2.2, GHC==8.4.3, GHC==8.6.5
build-type: Simple
extra-source-files:
CHANGES.md
@@ -151,7 +151,7 @@ library
hs-source-dirs:
./.
ghc-options: -Wall -fwarn-tabs
- cpp-options: -DVERSION="1.14.1"
+ cpp-options: -DVERSION="1.15"
build-depends:
Decimal
, aeson
@@ -169,8 +169,8 @@ library
, directory
, filepath
, hjsmin
- , hledger >=1.14.2 && <1.15
- , hledger-lib >=1.14.1 && <1.15
+ , hledger >=1.15 && <1.16
+ , hledger-lib >=1.15 && <1.16
, http-client
, http-conduit
, http-types
@@ -211,7 +211,7 @@ executable hledger-web
hs-source-dirs:
app
ghc-options: -Wall -fwarn-tabs
- cpp-options: -DVERSION="1.14.1"
+ cpp-options: -DVERSION="1.15"
build-depends:
base
, hledger-web
diff --git a/hledger-web.info b/hledger-web.info
index 4e977ec..5df1b32 100644
--- a/hledger-web.info
+++ b/hledger-web.info
@@ -3,7 +3,7 @@ This is hledger-web.info, produced by makeinfo version 6.5 from stdin.

File: hledger-web.info, Node: Top, Next: OPTIONS, Up: (dir)
-hledger-web(1) hledger-web 1.14
+hledger-web(1) hledger-web 1.15
*******************************
hledger-web is hledger's web interface. It starts a simple web
@@ -25,6 +25,7 @@ journal, timeclock, timedot, or CSV format specified with '-f', or
'$LEDGER_FILE', or '$HOME/.hledger.journal' (on windows, perhaps
'C:/Users/USER/.hledger.journal'). For more about this see hledger(1),
hledger_journal(5) etc.
+
* Menu:
* OPTIONS::
@@ -49,6 +50,10 @@ before options, as shown in the synopsis above.
'--serve'
serve and log requests, don't browse or auto-exit
+'--serve-api'
+
+ like -serve, but serve only the JSON web API, without the
+ server-side web UI
'--host=IPADDR'
listen on this IP address (default: 127.0.0.1)
@@ -126,7 +131,7 @@ before options, as shown in the synopsis above.
'-p --period=PERIODEXP'
set start date, end date, and/or reporting interval all at once
- using period expressions syntax (overrides the flags above)
+ using period expressions syntax
'--date2'
match the secondary date instead (see command help for other
@@ -192,7 +197,9 @@ also opens it in your default web browser if possible. In this mode the
web app will keep running for as long as you have it open in a browser
window, and will exit after two minutes of inactivity (no requests and
no browser windows viewing it). With '--serve', it just runs the web
-app without exiting, and logs requests to the console.
+app without exiting, and logs requests to the console. With
+'--serve-api', only the JSON web api (see below) is served, with the
+usual HTML server-side web UI disabled.
By default the server listens on IP address 127.0.0.1, accessible
only to local requests. You can use '--host' to change this, eg '--host
@@ -281,15 +288,19 @@ when you reload the page or navigate to a new page. If a change makes a
file unparseable, hledger-web will display an error message until the
file has been fixed.
+ (Note: if you are viewing files mounted from another machine, make
+sure that both machine clocks are roughly in step.)
+

File: hledger-web.info, Node: JSON API, Prev: RELOADING, Up: Top
5 JSON API
**********
-In addition to the web UI, hledger-web provides some JSON API routes.
-These are similar to the API provided by the hledger-api tool, but it
-may be convenient to have them in hledger-web also.
+In addition to the web UI, hledger-web provides some API routes that
+serve JSON in response to GET requests. Currently these are same ones
+provided by the hledger-api tool, but hledger-web will likely receive
+more attention than hledger-api in future:
/accountnames
/transactions
@@ -298,18 +309,47 @@ may be convenient to have them in hledger-web also.
/accounts
/accounttransactions/#AccountName
+ Also, you can append a new transaction to the journal by sending a
+PUT request to '/add' (hledger-web only). As with the web UI's add
+form, hledger-web must be started with the 'add' capability for this
+(enabled by default).
+
+ The payload should be a valid hledger transaction as JSON, similar to
+what you get from '/transactions' or '/accounttransactions'.
+
+ Another way to generate test data is with the
+'readJsonFile'/'writeJsonFile' helpers in Hledger.Web.Json, which read
+or write any of hledger's JSON-capable types from or to a file. Eg here
+we write the first transaction of a sample journal:
+
+$ make ghci-web
+>>> :m +*Hledger.Web.Json
+>>> writeJsonFile "txn.json" (head $ jtxns samplejournal)
+>>> :q
+$ python -m json.tool <txn.json >txn.pretty.json # optional: make human-readable
+
+ (sample output & discussion)
+
+ And here's how to test adding that with curl:
+
+$ curl -s http://127.0.0.1:5000/add -X PUT -H 'Content-Type: application/json' --data-binary @txn.pretty.json; echo
+
+ By default, both the server-side HTML UI and the JSON API are served.
+Running with '--serve-api' disables the former, useful if you only want
+to serve the API.
+

Tag Table:
Node: Top72
-Node: OPTIONS1354
-Ref: #options1459
-Node: PERMISSIONS6549
-Ref: #permissions6688
-Node: EDITING UPLOADING DOWNLOADING7900
-Ref: #editing-uploading-downloading8081
-Node: RELOADING8915
-Ref: #reloading9049
-Node: JSON API9359
-Ref: #json-api9453
+Node: OPTIONS1355
+Ref: #options1460
+Node: PERMISSIONS6739
+Ref: #permissions6878
+Node: EDITING UPLOADING DOWNLOADING8090
+Ref: #editing-uploading-downloading8271
+Node: RELOADING9105
+Ref: #reloading9239
+Node: JSON API9672
+Ref: #json-api9766

End Tag Table
diff --git a/hledger-web.txt b/hledger-web.txt
index 944073b..3144b4c 100644
--- a/hledger-web.txt
+++ b/hledger-web.txt
@@ -46,6 +46,10 @@ OPTIONS
--serve
serve and log requests, don't browse or auto-exit
+ --serve-api
+ like --serve, but serve only the JSON web API, without the
+ server-side web UI
+
--host=IPADDR
listen on this IP address (default: 127.0.0.1)
@@ -53,22 +57,22 @@ OPTIONS
listen on this TCP port (default: 5000)
--base-url=URL
- set the base url (default: http://IPADDR:PORT). You would
+ set the base url (default: http://IPADDR:PORT). You would
change this when sharing over the network, or integrating within
a larger website.
--file-url=URL
set the static files url (default: BASEURL/static). hledger-web
- normally serves static files itself, but if you wanted to serve
- them from another server for efficiency, you would set the url
+ normally serves static files itself, but if you wanted to serve
+ them from another server for efficiency, you would set the url
with this.
--capabilities=CAP[,CAP..]
- enable the view, add, and/or manage capabilities (default:
+ enable the view, add, and/or manage capabilities (default:
view,add)
--capabilities-header=HTTPHEADER
- read capabilities to enable from a HTTP header, like X-Sand-
+ read capabilities to enable from a HTTP header, like X-Sand-
storm-Permissions (default: disabled)
hledger input options:
@@ -78,7 +82,7 @@ OPTIONS
$LEDGER_FILE or $HOME/.hledger.journal)
--rules-file=RULESFILE
- Conversion rules file to use when reading CSV (default:
+ Conversion rules file to use when reading CSV (default:
FILE.rules)
--separator=CHAR
@@ -119,11 +123,11 @@ OPTIONS
multiperiod/multicolumn report by year
-p --period=PERIODEXP
- set start date, end date, and/or reporting interval all at once
- using period expressions syntax (overrides the flags above)
+ set start date, end date, and/or reporting interval all at once
+ using period expressions syntax
--date2
- match the secondary date instead (see command help for other
+ match the secondary date instead (see command help for other
effects)
-U --unmarked
@@ -142,21 +146,21 @@ OPTIONS
hide/aggregate accounts or postings more than NUM levels deep
-E --empty
- show items with zero amount, normally hidden (and vice-versa in
+ show items with zero amount, normally hidden (and vice-versa in
hledger-ui/hledger-web)
-B --cost
- convert amounts to their cost at transaction time (using the
+ convert amounts to their cost at transaction time (using the
transaction price, if any)
-V --value
- convert amounts to their market value on the report end date
+ convert amounts to their market value on the report end date
(using the most recent applicable market price, if any)
--auto apply automated posting rules to modify transactions.
--forecast
- apply periodic transaction rules to generate future transac-
+ apply periodic transaction rules to generate future transac-
tions, to 6 months from now or report end date.
When a reporting option appears more than once in the command line, the
@@ -176,39 +180,41 @@ OPTIONS
show debug output (levels 1-9, default: 1)
A @FILE argument will be expanded to the contents of FILE, which should
- contain one command line option/argument per line. (To prevent this,
+ contain one command line option/argument per line. (To prevent this,
insert a -- argument before.)
By default, hledger-web starts the web app in "transient mode" and also
opens it in your default web browser if possible. In this mode the web
app will keep running for as long as you have it open in a browser win-
- dow, and will exit after two minutes of inactivity (no requests and no
- browser windows viewing it). With --serve, it just runs the web app
- without exiting, and logs requests to the console.
+ dow, and will exit after two minutes of inactivity (no requests and no
+ browser windows viewing it). With --serve, it just runs the web app
+ without exiting, and logs requests to the console. With --serve-api,
+ only the JSON web api (see below) is served, with the usual HTML
+ server-side web UI disabled.
- By default the server listens on IP address 127.0.0.1, accessible only
- to local requests. You can use --host to change this, eg
- --host 0.0.0.0 to listen on all configured addresses.
+ By default the server listens on IP address 127.0.0.1, accessible only
+ to local requests. You can use --host to change this, eg --host
+ 0.0.0.0 to listen on all configured addresses.
- Similarly, use --port to set a TCP port other than 5000, eg if you are
+ Similarly, use --port to set a TCP port other than 5000, eg if you are
running multiple hledger-web instances.
- You can use --base-url to change the protocol, hostname, port and path
+ You can use --base-url to change the protocol, hostname, port and path
that appear in hyperlinks, useful eg for integrating hledger-web within
- a larger website. The default is http://HOST:PORT/ using the server's
+ a larger website. The default is http://HOST:PORT/ using the server's
configured host address and TCP port (or http://HOST if PORT is 80).
- With --file-url you can set a different base url for static files, eg
+ With --file-url you can set a different base url for static files, eg
for better caching or cookie-less serving on high performance websites.
PERMISSIONS
- By default, hledger-web allows anyone who can reach it to view the
+ By default, hledger-web allows anyone who can reach it to view the
journal and to add new transactions, but not to change existing data.
You can restrict who can reach it by
- o setting the IP address it listens on (see --host above). By default
- it listens on 127.0.0.1, accessible to all users on the local
+ o setting the IP address it listens on (see --host above). By default
+ it listens on 127.0.0.1, accessible to all users on the local
machine.
o putting it behind an authenticating proxy, using eg apache or nginx
@@ -218,51 +224,55 @@ PERMISSIONS
You can restrict what the users who reach it can do, by
o using the --capabilities=CAP[,CAP..] flag when you start it, enabling
- one or more of the following capabilities. The default value is
+ one or more of the following capabilities. The default value is
view,add:
o view - allows viewing the journal file and all included files
o add - allows adding new transactions to the main journal file
- o manage - allows editing, uploading or downloading the main or
+ o manage - allows editing, uploading or downloading the main or
included files
- o using the --capabilities-header=HTTPHEADER flag to specify a HTTP
- header from which it will read capabilities to enable. hledger-web
- on Sandstorm uses the X-Sandstorm-Permissions header to integrate
+ o using the --capabilities-header=HTTPHEADER flag to specify a HTTP
+ header from which it will read capabilities to enable. hledger-web
+ on Sandstorm uses the X-Sandstorm-Permissions header to integrate
with Sandstorm's permissions. This is disabled by default.
EDITING, UPLOADING, DOWNLOADING
- If you enable the manage capability mentioned above, you'll see a new
- "spanner" button to the right of the search form. Clicking this will
- let you edit, upload, or download the journal file or any files it
+ If you enable the manage capability mentioned above, you'll see a new
+ "spanner" button to the right of the search form. Clicking this will
+ let you edit, upload, or download the journal file or any files it
includes.
- Note, unlike any other hledger command, in this mode you (or any visi-
+ Note, unlike any other hledger command, in this mode you (or any visi-
tor) can alter or wipe the data files.
- Normally whenever a file is changed in this way, hledger-web saves a
- numbered backup (assuming file permissions allow it, the disk is not
- full, etc.) hledger-web is not aware of version control systems, cur-
- rently; if you use one, you'll have to arrange to commit the changes
+ Normally whenever a file is changed in this way, hledger-web saves a
+ numbered backup (assuming file permissions allow it, the disk is not
+ full, etc.) hledger-web is not aware of version control systems, cur-
+ rently; if you use one, you'll have to arrange to commit the changes
yourself (eg with a cron job or a file watcher like entr).
- Changes which would leave the journal file(s) unparseable or non-valid
- (eg with failing balance assertions) are prevented. (Probably. This
+ Changes which would leave the journal file(s) unparseable or non-valid
+ (eg with failing balance assertions) are prevented. (Probably. This
needs re-testing.)
RELOADING
hledger-web detects changes made to the files by other means (eg if you
- edit it directly, outside of hledger-web), and it will show the new
- data when you reload the page or navigate to a new page. If a change
- makes a file unparseable, hledger-web will display an error message
+ edit it directly, outside of hledger-web), and it will show the new
+ data when you reload the page or navigate to a new page. If a change
+ makes a file unparseable, hledger-web will display an error message
until the file has been fixed.
+ (Note: if you are viewing files mounted from another machine, make sure
+ that both machine clocks are roughly in step.)
+
JSON API
- In addition to the web UI, hledger-web provides some JSON API routes.
- These are similar to the API provided by the hledger-api tool, but it
- may be convenient to have them in hledger-web also.
+ In addition to the web UI, hledger-web provides some API routes that
+ serve JSON in response to GET requests. Currently these are same ones
+ provided by the hledger-api tool, but hledger-web will likely receive
+ more attention than hledger-api in future:
/accountnames
/transactions
@@ -271,6 +281,35 @@ JSON API
/accounts
/accounttransactions/#AccountName
+ Also, you can append a new transaction to the journal by sending a PUT
+ request to /add (hledger-web only). As with the web UI's add form,
+ hledger-web must be started with the add capability for this (enabled
+ by default).
+
+ The payload should be a valid hledger transaction as JSON, similar to
+ what you get from /transactions or /accounttransactions.
+
+ Another way to generate test data is with the readJsonFile/writeJson-
+ File helpers in Hledger.Web.Json, which read or write any of hledger's
+ JSON-capable types from or to a file. Eg here we write the first
+ transaction of a sample journal:
+
+ $ make ghci-web
+ >>> :m +*Hledger.Web.Json
+ >>> writeJsonFile "txn.json" (head $ jtxns samplejournal)
+ >>> :q
+ $ python -m json.tool <txn.json >txn.pretty.json # optional: make human-readable
+
+ (sample output & discussion)
+
+ And here's how to test adding that with curl:
+
+ $ curl -s http://127.0.0.1:5000/add -X PUT -H 'Content-Type: application/json' --data-binary @txn.pretty.json; echo
+
+ By default, both the server-side HTML UI and the JSON API are served.
+ Running with --serve-api disables the former, useful if you only want
+ to serve the API.
+
ENVIRONMENT
LEDGER_FILE The journal file path when not specified with -f. Default:
~/.hledger.journal (on windows, perhaps C:/Users/USER/.hledger.jour-
@@ -319,4 +358,4 @@ SEE ALSO
-hledger-web 1.14 March 2019 hledger-web(1)
+hledger-web 1.15 August 2019 hledger-web(1)
diff --git a/templates/register.hamlet b/templates/register.hamlet
index 54241ab..877d8db 100644
--- a/templates/register.hamlet
+++ b/templates/register.hamlet
@@ -18,7 +18,7 @@
#{balancelabel'}
<tbody>
- $forall (torig, tacct, split, acct, amt, bal) <- items
+ $forall (torig, tacct, split, _acct, amt, bal) <- items
<tr ##{tindex torig} title="#{showTransaction torig}" style="vertical-align:top;">
<td .date>
<a href="@{JournalR}#transaction-#{tindex torig}">
@@ -26,7 +26,9 @@
<td>
#{textElideRight 30 (tdescription tacct)}
<td .account>
- #{elideRight 40 acct}
+ $forall (Posting { paccount = acc }, (summName,comma)) <- otherTransAccounts torig
+ <a href="@?{acctlink acc}##{tindex torig}" title="#{acc}">
+ #{summName}</a>#{comma}
<td .amount style="text-align:right; white-space:nowrap;">
$if not split || not (isZeroMixedAmount amt)
^{mixedAmountAsHtml amt}