web: show all add form errors as form errors

Don't allow internal server errors during form validation.
This commit is contained in:
Simon Michael 2015-02-17 22:22:24 +00:00
parent d3453c66c1
commit 9351f10b81

View File

@ -10,12 +10,10 @@ import Control.Applicative
import Data.Either (lefts,rights) import Data.Either (lefts,rights)
import Data.List (sort) import Data.List (sort)
import qualified Data.List as L (head) -- qualified keeps dev & prod builds warning-free import qualified Data.List as L (head) -- qualified keeps dev & prod builds warning-free
-- import Data.Maybe import Data.Text (append, pack, unpack)
import Data.Text (unpack)
import qualified Data.Text as T import qualified Data.Text as T
-- import Data.Time.Calendar import Data.Time.Calendar
import Text.Parsec (digit, eof, many1, string, runParser) import Text.Parsec (digit, eof, many1, string, runParser)
-- import Yesod.Form.Jquery
import Hledger.Utils import Hledger.Utils
import Hledger.Data hiding (num) import Hledger.Data hiding (num)
@ -26,50 +24,57 @@ import Hledger.Cli hiding (num)
-- Part of the data required from the add form. -- Part of the data required from the add form.
-- Don't know how to handle the variable posting fields with yesod-form yet. -- Don't know how to handle the variable posting fields with yesod-form yet.
data AddForm = AddForm data AddForm = AddForm
{ addFormJournalFile :: Maybe Text -- FilePath { addFormDate :: Day
, addFormDate :: Maybe Text -- Day
, addFormDescription :: Maybe Text -- String , addFormDescription :: Maybe Text -- String
-- , addFormPostings :: [(AccountName, String)] -- , addFormPostings :: [(AccountName, String)]
, addFormJournalFile :: Maybe Text -- FilePath
} }
deriving Show deriving Show
postAddForm :: Handler Html postAddForm :: Handler Html
postAddForm = do postAddForm = do
VD{..} <- getViewData
let showErrors errs = do let showErrors errs = do
-- error $ show errs -- XXX uncomment to prevent redirect, for debugging -- error $ show errs -- XXX uncomment to prevent redirect for debugging
setMessage [shamlet| setMessage [shamlet|
Error:<br> Errors:<br>
$forall e<-errs $forall e<-errs
\#{e}<br> \#{e}<br>
|] |]
-- 1. process the fixed fields with yesod-form
VD{..} <- getViewData
let
validateJournalFile :: Text -> Either FormMessage Text
validateJournalFile f
| unpack f `elem` journalFilePaths j = Right f
| otherwise = Left $ MsgInvalidEntry $ pack "the selected journal file \"" `append` f `append` "\"is unknown"
validateDate :: Text -> Handler (Either FormMessage Day)
validateDate s = return $
case fixSmartDateStrEither' today $ strip $ unpack s of
Right d -> Right d
Left _ -> Left $ MsgInvalidEntry $ pack "could not parse date \"" `append` s `append` pack "\":" -- ++ show e)
formresult <- runInputPostResult $ AddForm formresult <- runInputPostResult $ AddForm
<$> iopt textField "journal" <$> ireq (checkMMap validateDate (pack . show) textField) "date"
<*> iopt textField "date"
-- (jqueryDayField def
-- {
-- jdsChangeYear = True -- give a year dropdown
-- , jdsYearRange = "1900:-5" -- 1900 till five years ago
-- }) "date"
<*> iopt textField "description" <*> iopt textField "description"
<*> iopt (check validateJournalFile textField) "journal"
case formresult of case formresult of
FormMissing -> showErrors ["there is no form data"::String] FormMissing -> showErrors ["there is no form data"::String]
FormFailure errs -> showErrors errs FormFailure errs -> showErrors errs
FormSuccess formdata -> do FormSuccess dat -> do
let AddForm{ let AddForm{
addFormJournalFile=mjournalfile addFormDate =date
,addFormDate =mdate
,addFormDescription=mdesc ,addFormDescription=mdesc
} = formdata ,addFormJournalFile=mjournalfile
date = parsedate $ fixSmartDateStr today $ maybe "today" (strip . unpack) mdate } = dat
desc = maybe "" unpack mdesc desc = maybe "" unpack mdesc
journalfile = maybe journalfile = maybe (journalFilePath j) unpack mjournalfile
(journalFilePath j)
(\f' -> let f = unpack f' in -- 2. the fixed fields look good; now process the posting fields adhocly,
if f `elem` journalFilePaths j -- getting either errors or a balanced transaction
then f
else error $ "the selected journal file is unknown: " ++ f)
mjournalfile
(params,_) <- runRequestBody (params,_) <- runRequestBody
let numberedParams s = let numberedParams s =
@ -93,22 +98,23 @@ postAddForm = do
(amts', amtErrs) = (rights eamts, map show $ lefts eamts) (amts', amtErrs) = (rights eamts, map show $ lefts eamts)
amts | length amts' == num = amts' amts | length amts' == num = amts'
| otherwise = amts' ++ [missingamt] | otherwise = amts' ++ [missingamt]
-- if no errors so far, generate a transaction and balance it or get the error.
errs = if not (null paramErrs) then paramErrs else (acctErrs ++ amtErrs) errs = if not (null paramErrs) then paramErrs else (acctErrs ++ amtErrs)
et | not $ null errs = Left errs etxn | not $ null errs = Left errs
| otherwise = either (\e -> Left [L.head $ lines e]) Right | otherwise = either (\e -> Left [L.head $ lines e]) Right
(balanceTransaction Nothing $ nulltransaction { (balanceTransaction Nothing $ nulltransaction {
tdate=date tdate=date
,tdescription=desc ,tdescription=desc
,tpostings=[nullposting{paccount=acct, pamount=Mixed [amt]} | (acct,amt) <- zip accts amts] ,tpostings=[nullposting{paccount=acct, pamount=Mixed [amt]} | (acct,amt) <- zip accts amts]
}) })
-- display errors or add transaction case etxn of
case et of
Left errs -> showErrors errs Left errs -> showErrors errs
Right t -> do Right t -> do
let t' = txnTieKnot t -- XXX move into balanceTransaction -- 3. all fields look good and form a balanced transaction; append it to the file
liftIO $ do ensureJournalFileExists journalfile liftIO $ do ensureJournalFileExists journalfile
appendToJournalFileOrStdout journalfile $ showTransaction t' appendToJournalFileOrStdout journalfile $
showTransaction $
txnTieKnot -- XXX move into balanceTransaction
t
setMessage [shamlet|<span>Transaction added.|] setMessage [shamlet|<span>Transaction added.|]
redirect (JournalR) -- , [("add","1")]) redirect (JournalR) -- , [("add","1")])