packages feed

hledger-web 1.16.2 → 1.17

raw patch · 17 files changed

+384/−285 lines, 17 filesdep +extradep +networkdep +unix-compatdep ~basedep ~hledgerdep ~hledger-libPVP ok

version bump matches the API change (PVP)

Dependencies added: extra, network, unix-compat

Dependency ranges changed: base, hledger, hledger-lib, wai-handler-launch

API changes (from Hackage documentation)

- Hledger.Web.Import: askUnliftIO :: MonadUnliftIO m => m (UnliftIO m)
- Hledger.Web.Json: instance Data.Aeson.Types.FromJSON.FromJSON (Data.Decimal.DecimalRaw GHC.Integer.Type.Integer)
- Hledger.Web.Json: instance Data.Aeson.Types.FromJSON.FromJSON Hledger.Data.Types.Account
- Hledger.Web.Json: instance Data.Aeson.Types.FromJSON.FromJSON Hledger.Data.Types.AccountDeclarationInfo
- Hledger.Web.Json: instance Data.Aeson.Types.FromJSON.FromJSON Hledger.Data.Types.Amount
- Hledger.Web.Json: instance Data.Aeson.Types.FromJSON.FromJSON Hledger.Data.Types.AmountPrice
- Hledger.Web.Json: instance Data.Aeson.Types.FromJSON.FromJSON Hledger.Data.Types.AmountStyle
- Hledger.Web.Json: instance Data.Aeson.Types.FromJSON.FromJSON Hledger.Data.Types.BalanceAssertion
- Hledger.Web.Json: instance Data.Aeson.Types.FromJSON.FromJSON Hledger.Data.Types.DigitGroupStyle
- Hledger.Web.Json: instance Data.Aeson.Types.FromJSON.FromJSON Hledger.Data.Types.GenericSourcePos
- Hledger.Web.Json: instance Data.Aeson.Types.FromJSON.FromJSON Hledger.Data.Types.MarketPrice
- Hledger.Web.Json: instance Data.Aeson.Types.FromJSON.FromJSON Hledger.Data.Types.MixedAmount
- Hledger.Web.Json: instance Data.Aeson.Types.FromJSON.FromJSON Hledger.Data.Types.Posting
- Hledger.Web.Json: instance Data.Aeson.Types.FromJSON.FromJSON Hledger.Data.Types.PostingType
- Hledger.Web.Json: instance Data.Aeson.Types.FromJSON.FromJSON Hledger.Data.Types.Side
- Hledger.Web.Json: instance Data.Aeson.Types.FromJSON.FromJSON Hledger.Data.Types.Status
- Hledger.Web.Json: instance Data.Aeson.Types.FromJSON.FromJSON Hledger.Data.Types.Transaction
- Hledger.Web.Json: instance Data.Aeson.Types.ToJSON.ToJSON Data.Decimal.Decimal
- Hledger.Web.Json: instance Data.Aeson.Types.ToJSON.ToJSON Hledger.Data.Types.Account
- Hledger.Web.Json: instance Data.Aeson.Types.ToJSON.ToJSON Hledger.Data.Types.Amount
- Hledger.Web.Json: instance Data.Aeson.Types.ToJSON.ToJSON Hledger.Data.Types.AmountPrice
- Hledger.Web.Json: instance Data.Aeson.Types.ToJSON.ToJSON Hledger.Data.Types.AmountStyle
- Hledger.Web.Json: instance Data.Aeson.Types.ToJSON.ToJSON Hledger.Data.Types.BalanceAssertion
- Hledger.Web.Json: instance Data.Aeson.Types.ToJSON.ToJSON Hledger.Data.Types.DigitGroupStyle
- Hledger.Web.Json: instance Data.Aeson.Types.ToJSON.ToJSON Hledger.Data.Types.GenericSourcePos
- Hledger.Web.Json: instance Data.Aeson.Types.ToJSON.ToJSON Hledger.Data.Types.MarketPrice
- Hledger.Web.Json: instance Data.Aeson.Types.ToJSON.ToJSON Hledger.Data.Types.MixedAmount
- Hledger.Web.Json: instance Data.Aeson.Types.ToJSON.ToJSON Hledger.Data.Types.Posting
- Hledger.Web.Json: instance Data.Aeson.Types.ToJSON.ToJSON Hledger.Data.Types.PostingType
- Hledger.Web.Json: instance Data.Aeson.Types.ToJSON.ToJSON Hledger.Data.Types.Side
- Hledger.Web.Json: instance Data.Aeson.Types.ToJSON.ToJSON Hledger.Data.Types.Status
- Hledger.Web.Json: instance Data.Aeson.Types.ToJSON.ToJSON Hledger.Data.Types.Transaction
- Hledger.Web.Json: instance GHC.Generics.Generic (Data.Decimal.DecimalRaw a)
- Hledger.Web.Json: readJsonFile :: FromJSON a => FilePath -> IO a
- Hledger.Web.Json: writeJsonFile :: ToJSON a => FilePath -> a -> IO ()
- Hledger.Web.Widget.Common: writeValidJournal :: MonadHandler m => FilePath -> Text -> m (Either String ())
+ Hledger.Web.Import: fieldError :: Text -> Text -> Text -> Text
+ Hledger.Web.WebOptions: [socket_] :: WebOpts -> Maybe String
+ Hledger.Web.Widget.Common: writeJournalTextIfValidAndChanged :: MonadHandler m => FilePath -> Text -> m (Either String ())
- Hledger.Web.WebOptions: WebOpts :: Bool -> Bool -> Maybe String -> String -> Int -> String -> Maybe String -> [Capability] -> Maybe (CI ByteString) -> CliOpts -> WebOpts
+ Hledger.Web.WebOptions: WebOpts :: Bool -> Bool -> Maybe String -> String -> Int -> String -> Maybe String -> [Capability] -> Maybe (CI ByteString) -> CliOpts -> Maybe String -> WebOpts

Files

CHANGES.md view
@@ -1,6 +1,40 @@ User-visible changes in hledger-web. See also the hledger changelog. +# 1.17 2020-03-01++- Fonts have been improved on certain platforms. (David Zhang)++- IPv6 is supported (Amarandus) (#1145)++- The --host option can now take a local hostname (Amarandus) (#1145)++- New --socket option to run hledger-web over an AF_UNIX socket file. (Carl Richard Theodor Schneider)+  This allows running multiple instances of hledger-web on the same+  system without having to manually choose a port for each instance,+  which is helpful for running individual instances for multiple+  users. In this scenario, the socket path is predictable, as it can+  be derived from the username.++- The edit and upload forms now normalise line endings, avoiding parse+  errors (#1194). Summary of current behaviour:++  - hledger add and import commands will append with (at least some)+    unix line endings, possibly causing the file to have mixed line+    endings++  - hledger-web edit and upload forms will write the file with+    the current system's native line endings, ie changing all+    line endings if the file previously used foreign line endings.++- Numbers in JSON output now provide a floating point Number+  representation as well as our native Decimal object representation,+  since the later can sometimes contain 255-digit integers. The+  floating point numbers can have up to 10 decimal digits (and an+  unbounded number of integer digits.)+  Experimental, suggestions needed. (#1195)++ # 1.16.2 2020-01-14  - add support for megaparsec 8 (#1175)@@ -9,9 +43,9 @@  # 1.16.1 2019-12-03 -- Drop old json dependency (#1190)+- Drop unnecessary json (#1190), mtl-compat dependencies -- Drop unnecessary mtl-compat dependency+- use hledger 1.16.1, fixing GHC 8.0/8.2 build  # 1.16 2019-12-01 
Hledger/Web/Handler/AddR.hs view
@@ -19,7 +19,6 @@ import Hledger import Hledger.Cli.Commands.Add (appendToJournalFileOrStdout, journalAddTransaction) import Hledger.Web.Import-import Hledger.Web.Json () import Hledger.Web.WebOptions (WebOpts(..)) import Hledger.Web.Widget.AddForm (addForm) 
Hledger/Web/Handler/EditR.hs view
@@ -12,7 +12,7 @@  import Hledger.Web.Import import Hledger.Web.Widget.Common-       (fromFormSuccess, helplink, journalFile404, writeValidJournal)+       (fromFormSuccess, helplink, journalFile404, writeJournalTextIfValidAndChanged)  editForm :: FilePath -> Text -> Markup -> MForm Handler (FormResult Text, Widget) editForm f txt =@@ -35,8 +35,8 @@    (f', txt) <- journalFile404 f j   ((res, view), enctype) <- runFormPost (editForm f' txt)-  text <- fromFormSuccess (showForm view enctype) res-  writeValidJournal f text >>= \case+  newtxt <- fromFormSuccess (showForm view enctype) res+  writeJournalTextIfValidAndChanged f newtxt >>= \case     Left e -> do       setMessage $ "Failed to load journal: " <> toHtml e       showForm view enctype
Hledger/Web/Handler/MiscR.hs view
@@ -25,7 +25,6 @@ import Yesod.Default.Handlers (getFaviconR, getRobotsR)  import Hledger-import Hledger.Web.Json () import Hledger.Web.Import import Hledger.Web.Widget.Common (journalFile404) 
Hledger/Web/Handler/UploadR.hs view
@@ -15,7 +15,7 @@ import qualified Data.Text.Encoding as TE  import Hledger.Web.Import-import Hledger.Web.Widget.Common (fromFormSuccess, journalFile404, writeValidJournal)+import Hledger.Web.Widget.Common (fromFormSuccess, journalFile404, writeJournalTextIfValidAndChanged)  uploadForm :: FilePath -> Markup -> MForm Handler (FormResult FileInfo, Widget) uploadForm f =@@ -44,15 +44,15 @@    -- Try to parse as UTF-8   -- XXX Unfortunate - how to parse as system locale?-  text <- case TE.decodeUtf8' lbs of+  newtxt <- case TE.decodeUtf8' lbs of     Left e -> do       setMessage $         "Encoding error: '" <> toHtml (show e) <> "'. " <>         "If your file is not UTF-8 encoded, try the 'edit form', " <>         "where the transcoding should be handled by the browser."       showForm view enctype-    Right text -> return text-  writeValidJournal f text >>= \case+    Right newtxt -> return newtxt+  writeJournalTextIfValidAndChanged f newtxt >>= \case     Left e -> do       setMessage $ "Failed to load journal: " <> toHtml e       showForm view enctype
− Hledger/Web/Json.hs
@@ -1,167 +0,0 @@-{-# OPTIONS_GHC -fno-warn-orphans #-}----{-# LANGUAGE CPP                 #-}---{-# LANGUAGE DataKinds           #-}---{-# LANGUAGE DeriveAnyClass      #-}-{-# LANGUAGE DeriveGeneric       #-}---{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE FlexibleInstances   #-}---{-# LANGUAGE NamedFieldPuns #-}---{-# LANGUAGE OverloadedStrings   #-}-{-# LANGUAGE OverloadedStrings #-}---{-# LANGUAGE PolyKinds           #-}---{-# LANGUAGE QuasiQuotes         #-}---{-# LANGUAGE QuasiQuotes #-}---{-# LANGUAGE Rank2Types #-}---{-# LANGUAGE RankNTypes #-}-{-# LANGUAGE RecordWildCards #-}---{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE StandaloneDeriving #-}---{-# LANGUAGE TemplateHaskell       #-}---{-# LANGUAGE TypeFamilies        #-}---{-# LANGUAGE TypeOperators       #-}--module Hledger.Web.Json (-  -- * Instances-  -- * Utilities-   readJsonFile-  ,writeJsonFile-) where--import           Data.Aeson---import           Data.Aeson.TH-import qualified Data.ByteString.Lazy as BL-import           Data.Decimal-import           Data.Maybe-import           GHC.Generics (Generic)--import           Hledger.Data---- JSON instances. Should they be in hledger-lib Types.hs ?---- To JSON--instance ToJSON Status-instance ToJSON GenericSourcePos-instance ToJSON Decimal-instance ToJSON Amount-instance ToJSON AmountStyle-instance ToJSON Side-instance ToJSON DigitGroupStyle-instance ToJSON MixedAmount-instance ToJSON BalanceAssertion-instance ToJSON AmountPrice-instance ToJSON MarketPrice-instance ToJSON PostingType--instance ToJSON Posting where-  toJSON Posting{..} = object-    ["pdate"             .= toJSON pdate-    ,"pdate2"            .= toJSON pdate2-    ,"pstatus"           .= toJSON pstatus-    ,"paccount"          .= toJSON paccount-    ,"pamount"           .= toJSON pamount-    ,"pcomment"          .= toJSON pcomment-    ,"ptype"             .= toJSON ptype-    ,"ptags"             .= toJSON ptags-    ,"pbalanceassertion" .= toJSON pbalanceassertion-    -- 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)-    ]--instance ToJSON Transaction--instance ToJSON Account where-  toJSON a = object-    ["aname"        .= toJSON (aname a)-    ,"aebalance"    .= toJSON (aebalance a)-    ,"aibalance"    .= toJSON (aibalance a)-    ,"anumpostings" .= toJSON (anumpostings a)-    ,"aboring"      .= toJSON (aboring a)-    -- 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.-    ,"asubs_"       .= toJSON (map aname $ asubs a)-    -- The actual subaccounts (and their subs..), making a (probably highly redundant) tree-    -- ,"asubs"        .= toJSON (asubs a)-    -- Omit the actual subaccounts-    ,"asubs"        .= toJSON ([]::[Account])-    ]---- From JSON--instance FromJSON Status-instance FromJSON GenericSourcePos-instance FromJSON Amount-instance FromJSON AmountStyle-instance FromJSON Side-instance FromJSON DigitGroupStyle-instance FromJSON MixedAmount-instance FromJSON BalanceAssertion-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--- 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 =-----  A.withScientific "Decimal" (return . right . eitherFromRational . toRational)------ https://github.com/bos/aeson/issues/474--- http://hackage.haskell.org/package/aeson-1.4.2.0/docs/Data-Aeson-TH.html--- $(deriveFromJSON defaultOptions ''Decimal) -- doesn't work--- $(deriveFromJSON defaultOptions ''DecimalRaw)  -- works; requires TH, but gives better parse error messages------ https://github.com/PaulJohnson/Haskell-Decimal/issues/6---deriving instance Generic Decimal---instance FromJSON Decimal-deriving instance Generic (DecimalRaw a)-instance FromJSON (DecimalRaw Integer)------ @simonmichael, I think the code in your first comment should work if it compiles—though “work” doesn’t mean you can parse a JSON number directly into a `Decimal` using the generic instance, as you’ve discovered.------Error messages with these extensions are always rather cryptic, but I’d prefer them to Template Haskell. Typically you’ll want to start by getting a generic `ToJSON` instance working, then use that to figure out what the `FromJSON` instance expects to parse: for a correct instance, `encode` and `decode` should give you an isomorphism between your type and a subset of `Bytestring` (up to the `Maybe` wrapper that `decode` returns).------I don’t have time to test it right now, but I think it will also work without `DeriveAnyClass`, just using `DeriveGeneric` and `StandAloneDeriving`. It should also work to use the [`genericParseJSON`](http://hackage.haskell.org/package/aeson/docs/Data-Aeson.html#v:genericParseJSON) function to implement the class explicitly, something like this:------{-# LANGUAGE DeriveGeneric #-}---{-# LANGUAGE StandAloneDeriving #-}---import GHC.Generics---import Data.Aeson---deriving instance Generic Decimal---instance FromJSON Decimal where---  parseJSON = genericParseJSON defaultOptions------And of course you can avoid `StandAloneDeriving` entirely if you’re willing to wrap `Decimal` in your own `newtype`.----- Utilities---- | Read a json from a file and decode/parse it as the target type, if we can.--- Example:--- >>> readJsonFile "in.json" :: IO MixedAmount-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)-  case fromJSON v :: FromJSON a => Result a of-    Error e   -> error e-    Success t -> return t---- | Write some to-JSON-convertible haskell value to a json file, if we can.--- Example:--- >>> writeJsonFile "out.json" nullmixedamt-writeJsonFile :: ToJSON a => FilePath -> a -> IO ()-writeJsonFile f v = BL.writeFile f (encode $ toJSON v)
Hledger/Web/Main.hs view
@@ -9,15 +9,19 @@  module Hledger.Web.Main where +import Control.Exception (bracket) import Control.Monad (when) import Data.String (fromString) import qualified Data.Text as T+import Network.Socket import Network.Wai (Application)-import Network.Wai.Handler.Warp (runSettings, defaultSettings, setHost, setPort)-import Network.Wai.Handler.Launch (runHostPortUrl)+import Network.Wai.Handler.Warp (runSettings, runSettingsSocket, defaultSettings, setHost, setPort)+import Network.Wai.Handler.Launch (runHostPortFullUrl) import Prelude hiding (putStrLn)-import System.Exit (exitSuccess)+import System.Directory (removeFile)+import System.Exit (exitSuccess, exitFailure) import System.IO (hFlush, stdout)+import System.PosixCompat.Files (getFileStatus, isSocket) import Text.Printf (printf) import Yesod.Default.Config import Yesod.Default.Main (defaultDevelApp)@@ -76,11 +80,31 @@       putStrLn "Press ctrl-c to quit"       hFlush stdout       let warpsettings = setHost (fromString h) (setPort p defaultSettings)-      Network.Wai.Handler.Warp.runSettings warpsettings app+      case socket_ opts of+        Just s -> do+          if isUnixDomainSocketAvailable then+            bracket+              (do+                  sock <- socket AF_UNIX Stream 0+                  setSocketOption sock ReuseAddr 1+                  bind sock $ SockAddrUnix s+                  listen sock maxListenQueue+                  return sock+              )+              (\_ -> do+                  sockstat <-  getFileStatus s+                  when (isSocket sockstat) $ removeFile s+              )+              (\sock -> Network.Wai.Handler.Warp.runSettingsSocket warpsettings sock app)+            else do+              putStrLn "Unix domain sockets are not available on your operating system"+              putStrLn "Please try again without --socket"+              exitFailure+        Nothing -> Network.Wai.Handler.Warp.runSettings warpsettings app     else do       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+      Network.Wai.Handler.Launch.runHostPortFullUrl h p u app 
Hledger/Web/Settings.hs view
@@ -45,7 +45,10 @@  defbaseurl :: String -> Int -> String defbaseurl host port =-  "http://" ++ host ++ if port /= 80 then ":" ++ show port else ""+  if ':' `elem` host then+    "http://[" ++ host ++ "]" ++ if port /= 80 then ":" ++ show port else ""+  else+    "http://" ++ host ++ if port /= 80 then ":" ++ show port else ""  -- Static setting below. Changing these requires a recompile 
Hledger/Web/WebOptions.hs view
@@ -44,6 +44,11 @@       "ORIGIN"       ("allow cross-origin requests from the specified origin; setting ORIGIN to \"*\" allows requests from any origin")   , flagReq+      ["socket"]+      (\s opts -> Right $ setopt "socket" s opts)+      "SOCKET"+      "use the given socket instead of the given IP and port (implies --serve)"+  , flagReq       ["host"]       (\s opts -> Right $ setopt "host" s opts)       "IPADDR"@@ -110,10 +115,11 @@   , capabilities_ :: [Capability]   , capabilitiesHeader_ :: Maybe (CI ByteString)   , cliopts_ :: CliOpts+  , socket_ :: Maybe String   } deriving (Show)  defwebopts :: WebOpts-defwebopts = WebOpts def def Nothing def def def def [CapView, CapAdd] Nothing def+defwebopts = WebOpts def def Nothing def def def def [CapView, CapAdd] Nothing def Nothing  instance Default WebOpts where def = defwebopts @@ -131,9 +137,12 @@           Left e -> error' ("Unknown capability: " ++ T.unpack e)           Right [] -> [CapView, CapAdd]           Right xs -> xs+        sock = stripTrailingSlash <$> maybestringopt "socket" rawopts     return       defwebopts-      { serve_ = boolopt "serve" rawopts+      { serve_ = case sock of+          Just _ -> True+          Nothing -> boolopt "serve" rawopts       , serve_api_ = boolopt "serve-api" rawopts       , cors_ = maybestringopt "cors" rawopts       , host_ = h@@ -143,16 +152,13 @@       , capabilities_ = caps       , capabilitiesHeader_ = mk . BC.pack <$> maybestringopt "capabilities-header" rawopts       , cliopts_ = cliopts+      , socket_ = sock       }   where     stripTrailingSlash = reverse . dropWhile (== '/') . reverse -- yesod don't like it  checkWebOpts :: WebOpts -> WebOpts-checkWebOpts wopts = do-  let h = host_ wopts-  if any (`notElem` (".0123456789" :: String)) h-    then usageError $ "--host requires an IP address, not " ++ show h-    else wopts+checkWebOpts = id  getHledgerWebOpts :: IO WebOpts getHledgerWebOpts = do
Hledger/Web/Widget/AddForm.hs view
@@ -13,7 +13,8 @@  import Control.Monad.State.Strict (evalStateT) import Data.Bifunctor (first)-import Data.List (dropWhileEnd, intercalate, nub, sort, unfoldr)+import Data.List (dropWhileEnd, intercalate, unfoldr)+import Data.List.Extra (nubSort) import Data.Maybe (isJust) #if !(MIN_VERSION_base(4,13,0)) import Data.Semigroup ((<>))@@ -71,7 +72,7 @@   let (postRes, displayRows) = validatePostings acctRes amtRes    -- bindings used in add-form.hamlet-  let descriptions = sort $ nub $ tdescription <$> jtxns j+  let descriptions = nubSort $ tdescription <$> jtxns j       journals = fst <$> jfiles j    pure (validateTransaction dateRes descRes postRes, $(widgetFile "add-form"))
Hledger/Web/Widget/Common.hs view
@@ -12,7 +12,7 @@   , helplink   , mixedAmountAsHtml   , fromFormSuccess-  , writeValidJournal+  , writeJournalTextIfValidAndChanged   , journalFile404   ) where @@ -50,14 +50,28 @@ fromFormSuccess h (FormFailure _) = h fromFormSuccess _ (FormSuccess a) = pure a -writeValidJournal :: MonadHandler m => FilePath -> Text -> m (Either String ())-writeValidJournal f txt =-  liftIO (readJournal def (Just f) txt) >>= \case+-- | A helper for postEditR/postUploadR: check that the given text+-- parses as a Journal, and if so, write it to the given file, if the+-- text has changed. Or, return any error message encountered.+--+-- As a convenience for data received from web forms, which does not+-- have normalised line endings, line endings will be normalised (to \n)+-- before parsing.+--+-- The file will be written (if changed) with the current system's native+-- line endings (see writeFileWithBackupIfChanged).+--+writeJournalTextIfValidAndChanged :: MonadHandler m => FilePath -> Text -> m (Either String ())+writeJournalTextIfValidAndChanged f t = do+  -- Ensure unix line endings, since both readJournal (cf+  -- formatdirectivep, #1194) writeFileWithBackupIfChanged require them.+  -- XXX klunky. Any equivalent of "hSetNewlineMode h universalNewlineMode" for form posts ?+  let t' = T.pack $ regexReplace "\r" "" $ T.unpack t+  liftIO (readJournal def (Just f) t') >>= \case     Left e -> return (Left e)     Right _ -> do-      _ <- liftIO (writeFileWithBackupIfChanged f txt)+      _ <- liftIO (writeFileWithBackupIfChanged f t')       return (Right ())-  -- | Link to a topic in the manual. helplink :: Text -> Text -> HtmlUrl r
hledger-web.1 view
@@ -1,5 +1,5 @@ -.TH "hledger-web" "1" "January 2020" "hledger-web 1.16.2" "hledger User Manuals"+.TH "hledger-web" "1" "March 2020" "hledger-web 1.17" "hledger User Manuals"   @@ -15,9 +15,9 @@ \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-file format.+hledger is a reliable, cross-platform set of programs 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.@@ -64,6 +64,13 @@ \f[B]\f[CB]--port=PORT\f[B]\f[R] listen on this TCP port (default: 5000) .TP+\f[B]\f[CB]--socket=SOCKETFILE\f[B]\f[R]+use a unix domain socket file to listen for requests instead of a TCP+socket.+Implies \f[C]--serve\f[R].+It can only be used if the operating system can provide this type of+socket.+.TP \f[B]\f[CB]--base-url=URL\f[B]\f[R] set the base url (default: http://IPADDR:PORT). You would change this when sharing over the network, or integrating@@ -105,7 +112,8 @@ use some other field or tag for the account name .TP \f[B]\f[CB]-I --ignore-assertions\f[B]\f[R]-ignore any failing balance assertions+disable balance assertion checks (note: does not disable balance+assignments) .PP hledger reporting options: .TP@@ -168,8 +176,9 @@ apply automated posting rules to modify transactions. .TP \f[B]\f[CB]--forecast\f[B]\f[R]-apply periodic transaction rules to generate future transactions, to 6-months from now or report end date.+generate future transactions from periodic transaction rules, for the+next 6 months or till report end date.+In hledger-ui, also make ordinary future transactions visible. .PP When a reporting option appears more than once in the command line, the last one takes precedence.@@ -209,6 +218,26 @@ 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+Both of these options are ignored when \f[C]--socket\f[R] is used.+In this case, it creates an \f[C]AF_UNIX\f[R] socket file at the+supplied path and uses that for communication.+This is an alternative way of running multiple hledger-web instances+behind a reverse proxy that handles authentication for different users.+The path can be derived in a predictable way, eg by using the username+within the path.+As an example, \f[C]nginx\f[R] as reverse proxy can use the variabel+\f[C]$remote_user\f[R] to derive a path from the username used in a HTTP+basic authentication.+The following \f[C]proxy_pass\f[R] directive allows access to all+\f[C]hledger-web\f[R] instances that created a socket in+\f[C]/tmp/hledger/\f[R]:+.IP+.nf+\f[C]+  proxy_pass http://unix:/tmp/hledger/${remote_user}.socket;+\f[R]+.fi+.PP 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.@@ -314,8 +343,8 @@ .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.+Hledger.Web.Json, which can write or read most of hledger\[aq]s data+types to or from a file. Eg here we write the first transaction of a sample journal: .IP .nf@@ -347,6 +376,27 @@ \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]).+.PP+A typical value is \f[C]\[ti]/DIR/YYYY.journal\f[R], where DIR is a+version-controlled finance directory and YYYY is the current year.+Or \f[C]\[ti]/DIR/current.journal\f[R], where current.journal is a+symbolic link to YYYY.journal.+.PP+On Mac computers, you can set this and other environment variables in a+more thorough way that also affects applications started from the GUI+(say, an Emacs dock icon).+Eg on MacOS Catalina I have a \f[C]\[ti]/.MacOSX/environment.plist\f[R]+file containing+.IP+.nf+\f[C]+{+  \[dq]LEDGER_FILE\[dq] : \[dq]\[ti]/finance/current.journal\[dq]+}+\f[R]+.fi+.PP+To see the effect you may need to \f[C]killall Dock\f[R], or reboot. .SH FILES .PP Reads data from one or more files in hledger journal, timeclock,
hledger-web.cabal view
@@ -4,10 +4,10 @@ -- -- see: https://github.com/sol/hpack ----- hash: 4c599d719d293cac5bb59f0e672d9aaf251a5a548a7010c5b8155f557bd56895+-- hash: 450997bd04bd661f348425755f2045c35d11a0765dd32552b961d62d05aa458b  name:           hledger-web-version:        1.16.2+version:        1.17 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 @@ maintainer:     Simon Michael <simon@joyful.com> license:        GPL-3 license-file:   LICENSE-tested-with:    GHC==8.0.2, GHC==8.2.2, GHC==8.4.3, GHC==8.6.5, GHC==8.8.1+tested-with:    GHC==8.2.2, GHC==8.4.3, GHC==8.6.5, GHC==8.8.2, GHC==8.10 build-type:     Simple extra-source-files:     CHANGES.md@@ -139,7 +139,6 @@       Hledger.Web.Handler.RegisterR       Hledger.Web.Handler.UploadR       Hledger.Web.Import-      Hledger.Web.Json       Hledger.Web.Main       Hledger.Web.Settings       Hledger.Web.Settings.StaticFiles@@ -151,11 +150,11 @@   hs-source-dirs:       ./.   ghc-options: -Wall -fwarn-tabs -Wcompat -Wincomplete-uni-patterns -Wincomplete-record-updates -Wredundant-constraints-  cpp-options: -DVERSION="1.16.2"+  cpp-options: -DVERSION="1.17"   build-depends:       Decimal     , aeson-    , base >=4.9 && <4.14+    , base >=4.9 && <4.15     , blaze-html     , blaze-markup     , bytestring@@ -167,26 +166,29 @@     , containers     , data-default     , directory+    , extra >=1.6.3     , filepath     , hjsmin-    , hledger >=1.16.2 && <1.17-    , hledger-lib >=1.16.2 && <1.17+    , hledger >=1.17 && <1.18+    , hledger-lib >=1.17 && <1.18     , http-client     , http-conduit     , http-types     , megaparsec >=7.0.0 && <8.1     , mtl >=2.2.1+    , network     , semigroups     , shakespeare >=2.0.2.2     , template-haskell     , text >=1.2     , time >=1.5     , transformers+    , unix-compat     , utf8-string     , wai     , wai-cors     , wai-extra-    , wai-handler-launch >=1.3+    , wai-handler-launch >=3.0.3     , warp     , yaml     , yesod >=1.4 && <1.7@@ -206,7 +208,7 @@   hs-source-dirs:       app   ghc-options: -Wall -fwarn-tabs -Wcompat -Wincomplete-uni-patterns -Wincomplete-record-updates -Wredundant-constraints-  cpp-options: -DVERSION="1.16.2"+  cpp-options: -DVERSION="1.17"   build-depends:       base     , hledger-web
hledger-web.info view
@@ -3,10 +3,20 @@  File: hledger-web.info,  Node: Top,  Next: OPTIONS,  Up: (dir) -hledger-web(1) hledger-web 1.16.2-*********************************+hledger-web(1) hledger-web 1.17+******************************* -hledger-web is hledger's web interface.  It starts a simple web+hledger-web - web interface for the hledger accounting tool++   'hledger-web [OPTIONS]'+'hledger web -- [OPTIONS]'++   hledger is a reliable, cross-platform set of programs 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).++   hledger-web is hledger'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 interface, showing@@ -33,6 +43,9 @@ * EDITING UPLOADING DOWNLOADING:: * RELOADING:: * JSON API::+* ENVIRONMENT::+* FILES::+* BUGS::   File: hledger-web.info,  Node: OPTIONS,  Next: PERMISSIONS,  Prev: Top,  Up: Top@@ -60,6 +73,11 @@ '--port=PORT'       listen on this TCP port (default: 5000)+'--socket=SOCKETFILE'++     use a unix domain socket file to listen for requests instead of a+     TCP socket.  Implies '--serve'.  It can only be used if the+     operating system can provide this type of socket. '--base-url=URL'       set the base url (default: http://IPADDR:PORT). You would change@@ -103,7 +121,8 @@      use some other field or tag for the account name '-I --ignore-assertions' -     ignore any failing balance assertions+     disable balance assertion checks (note: does not disable balance+     assignments)     hledger reporting options: @@ -168,8 +187,9 @@      apply automated posting rules to modify transactions. '--forecast' -     apply periodic transaction rules to generate future transactions,-     to 6 months from now or report end date.+     generate future transactions from periodic transaction rules, for+     the next 6 months or till report end date.  In hledger-ui, also+     make ordinary future transactions visible.     When a reporting option appears more than once in the command line, the last one takes precedence.@@ -208,6 +228,19 @@    Similarly, use '--port' to set a TCP port other than 5000, eg if you are running multiple hledger-web instances. +   Both of these options are ignored when '--socket' is used.  In this+case, it creates an 'AF_UNIX' socket file at the supplied path and uses+that for communication.  This is an alternative way of running multiple+hledger-web instances behind a reverse proxy that handles authentication+for different users.  The path can be derived in a predictable way, eg+by using the username within the path.  As an example, 'nginx' as+reverse proxy can use the variabel '$remote_user' to derive a path from+the username used in a HTTP basic authentication.  The following+'proxy_pass' directive allows access to all 'hledger-web' instances that+created a socket in '/tmp/hledger/':++  proxy_pass http://unix:/tmp/hledger/${remote_user}.socket;+    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@@ -292,7 +325,7 @@ sure that both machine clocks are roughly in step.)  -File: hledger-web.info,  Node: JSON API,  Prev: RELOADING,  Up: Top+File: hledger-web.info,  Node: JSON API,  Next: ENVIRONMENT,  Prev: RELOADING,  Up: Top  5 JSON API **********@@ -317,8 +350,8 @@ 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+'readJsonFile'/'writeJsonFile' helpers in Hledger.Web.Json, which can+write or read most of hledger's data types to or from a file.  Eg here we write the first transaction of a sample journal:  $ make ghci-web@@ -338,18 +371,78 @@ to serve the API.  +File: hledger-web.info,  Node: ENVIRONMENT,  Next: FILES,  Prev: JSON API,  Up: Top++6 ENVIRONMENT+*************++*LEDGER_FILE* The journal file path when not specified with '-f'.+Default: '~/.hledger.journal' (on windows, perhaps+'C:/Users/USER/.hledger.journal').++   A typical value is '~/DIR/YYYY.journal', where DIR is a+version-controlled finance directory and YYYY is the current year.  Or+'~/DIR/current.journal', where current.journal is a symbolic link to+YYYY.journal.++   On Mac computers, you can set this and other environment variables in+a more thorough way that also affects applications started from the GUI+(say, an Emacs dock icon).  Eg on MacOS Catalina I have a+'~/.MacOSX/environment.plist' file containing++{+  "LEDGER_FILE" : "~/finance/current.journal"+}++   To see the effect you may need to 'killall Dock', or reboot.+++File: hledger-web.info,  Node: FILES,  Next: BUGS,  Prev: ENVIRONMENT,  Up: Top++7 FILES+*******++Reads data from one or more files in hledger journal, timeclock,+timedot, or CSV format specified with '-f', or '$LEDGER_FILE', or+'$HOME/.hledger.journal' (on windows, perhaps+'C:/Users/USER/.hledger.journal').+++File: hledger-web.info,  Node: BUGS,  Prev: FILES,  Up: Top++8 BUGS+******++The need to precede options with '--' when invoked from hledger is+awkward.++   '-f-' doesn't work (hledger-web can't read from stdin).++   Query arguments and some hledger options are ignored.++   Does not work in text-mode browsers.++   Does not work well on small screens.++ Tag Table: Node: Top72-Node: OPTIONS1359-Ref: #options1464-Node: PERMISSIONS6743-Ref: #permissions6882-Node: EDITING UPLOADING DOWNLOADING8094-Ref: #editing-uploading-downloading8275-Node: RELOADING9109-Ref: #reloading9243-Node: JSON API9676-Ref: #json-api9770+Node: OPTIONS1746+Ref: #options1851+Node: PERMISSIONS8195+Ref: #permissions8334+Node: EDITING UPLOADING DOWNLOADING9546+Ref: #editing-uploading-downloading9727+Node: RELOADING10561+Ref: #reloading10695+Node: JSON API11128+Ref: #json-api11242+Node: ENVIRONMENT12680+Ref: #environment12796+Node: FILES13529+Ref: #files13629+Node: BUGS13842+Ref: #bugs13920  End Tag Table 
hledger-web.txt view
@@ -11,10 +11,10 @@        hledger web -- [OPTIONS]  DESCRIPTION-       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).+       hledger  is  a  reliable,  cross-platform  set of programs 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).         hledger-web is hledger's web interface.  It starts a simple web  appli-        cation for browsing and adding transactions, and optionally opens it in@@ -56,6 +56,11 @@        --port=PORT               listen on this TCP port (default: 5000) +       --socket=SOCKETFILE+              use  a unix domain socket file to listen for requests instead of+              a TCP socket.  Implies --serve.  It can only be used if the  op-+              erating system can provide this type of socket.+        --base-url=URL               set  the  base  url  (default:  http://IPADDR:PORT).   You would               change this when sharing over the network, or integrating within@@ -97,7 +102,8 @@               use some other field or tag for the account name         -I --ignore-assertions-              ignore any failing balance assertions+              disable balance assertion checks (note: does not disable balance+              assignments)         hledger reporting options: @@ -123,7 +129,7 @@               multiperiod/multicolumn report by year         -p --period=PERIODEXP-              set start date, end date, and/or reporting interval all at  once+              set  start date, end date, and/or reporting interval all at once               using period expressions syntax         --date2@@ -146,22 +152,23 @@               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--              tions, to 6 months from now or report end date.+              generate future transactions from  periodic  transaction  rules,+              for  the  next 6 months or till report end date.  In hledger-ui,+              also make ordinary future transactions visible.         When a reporting option appears more than once in the command line, the        last one takes precedence.@@ -199,22 +206,35 @@        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+       Both of these options are ignored when --socket is used.  In this case,+       it creates an AF_UNIX socket file at the supplied path  and  uses  that+       for  communication.   This  is  an  alternative way of running multiple+       hledger-web instances behind a reverse proxy that  handles  authentica-+       tion  for  different  users.   The path can be derived in a predictable+       way, eg by using the username within the path.  As an example, nginx as+       reverse  proxy  can use the variabel $remote_user to derive a path from+       the username used  in  a  HTTP  basic  authentication.   The  following+       proxy_pass  directive  allows  access to all hledger-web instances that+       created a socket in /tmp/hledger/:++                proxy_pass http://unix:/tmp/hledger/${remote_user}.socket;++       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 ma-+       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  ma-          chine.         o putting it behind an authenticating proxy, using eg apache or nginx@@ -224,44 +244,44 @@        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  in-+         o manage  -  allows editing, uploading or downloading the main or in-            cluded 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  in-+       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 in-        cludes. -       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+       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 un-        til the file has been fixed. @@ -269,8 +289,8 @@        that both machine clocks are roughly in step.)  JSON API-       In  addition  to  the web UI, hledger-web provides some API routes that-       serve JSON in  response  to  GET  requests.   (And  when  started  with+       In addition to the web UI, hledger-web provides some  API  routes  that+       serve  JSON  in  response  to  GET  requests.   (And  when started with        --serve-api, it provides only these routes.):                /accountnames@@ -280,17 +300,17 @@               /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+       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+       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+       Another way to generate test data is with  the  readJsonFile/writeJson-+       File  helpers  in  Hledger.Web.Json,  which  can  write or read most of+       hledger's data types to or from a file.  Eg here  we  write  the  first        transaction of a sample journal:                $ make ghci-web@@ -305,15 +325,30 @@                $ 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+       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-+       ~/.hledger.journal (on  windows,  perhaps  C:/Users/USER/.hledger.jour-        nal). +       A  typical  value  is  ~/DIR/YYYY.journal,  where DIR is a version-con-+       trolled finance directory and YYYY is the current year.  Or  ~/DIR/cur-+       rent.journal, where current.journal is a symbolic link to YYYY.journal.++       On Mac computers, you can set this and other environment variables in a+       more thorough way that also affects applications started from  the  GUI+       (say, an Emacs dock icon).  Eg on MacOS Catalina I have a ~/.MacOSX/en-+       vironment.plist file containing++              {+                "LEDGER_FILE" : "~/finance/current.journal"+              }++       To see the effect you may need to killall Dock, or reboot.+ FILES        Reads data from one or more files in hledger journal, timeclock,  time-        dot,   or   CSV   format   specified   with  -f,  or  $LEDGER_FILE,  or@@ -357,4 +392,4 @@   -hledger-web 1.16.2               January 2020                   hledger-web(1)+hledger-web 1.17                  March 2020                    hledger-web(1)
static/hledger.css view
@@ -18,6 +18,12 @@ }  /*------------------------------------------------------------------------------------------*/+/* 2. fonts */+body {+  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Helvetica, Arial, sans-serif;+}++/*------------------------------------------------------------------------------------------*/ /* 4. typeahead styles */  .tt-hint {
static/js/jquery.flot.js view
@@ -517,7 +517,7 @@                 colors: ["#edc240", "#afd8f8", "#cb4b4b", "#4da74d", "#9440ed"],                 legend: {                     show: true,-                    noColumns: 1, // number of colums in legend table+                    noColumns: 1, // number of columns in legend table                     labelFormatter: null, // fn: string -> string                     labelBoxBorderColor: "#ccc", // border color for the little label boxes                     container: null, // container (as jQuery object) to put legend in, null means default on top of graph