imprevu-happstack-0.1.0: src/Imprevu/Happstack/Forms.hs
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ExtendedDefaultRules #-}
module Imprevu.Happstack.Forms where
import Control.Monad.Extra (mapMaybeM)
import Control.Concurrent.STM
import Control.Monad.State
import Data.String
import Data.Text (Text, unpack)
import Data.Typeable
import Imprevu
import Imprevu.Evaluation
import Imprevu.Happstack.Types
import Happstack.Server as HS (Response, Method (..), methodM, seeOther, toResponse, ServerPartT)
import Text.Blaze.Html5 (ToMarkup, Html, toHtml, td, toValue, tr, (!), input)
import qualified Text.Blaze.Html5 as H (form, label)
import Text.Blaze.Html5.Attributes as A (id, type_, name, value, checked, for, action, method, enctype)
import Text.Reform (eitherForm, viewForm, (<++), (++>), ErrorInputType, Form, FormError (..), FormInput)
import Text.Reform.Blaze.String (inputCheckboxes, label, textarea)
import qualified Text.Reform.Blaze.String as RB
import Text.Reform.Happstack (environment)
import qualified Text.Reform.Generalized as G
import Debug.NoTrace
default (Integer, Double, Data.Text.Text)
type BackLink = EventNumber -> Input -> Text
viewInput :: ClientNumber -> BackLink -> EventNumber -> [SomeSignal] -> ServerPartT IO (Maybe Html)
viewInput cn bl en ss = do
ds <- mapMaybeM (viewInput' cn bl en) ss
return $ if null ds
then Nothing
else Just $ sequence_ ds
viewInput _ _ _ _ = return Nothing
viewInput' :: ClientNumber -> BackLink -> EventNumber -> SomeSignal -> ServerPartT IO (Maybe Html)
viewInput' me backlink en (SomeSignal (Signal s)) = do
traceM $ "viewInput' " ++ (show s)
case (cast s) of
Just is@(Input i cn) | me == cn -> do
lf <- viewForm "user" $ inputForm' i
let link = backlink en is
traceM $ "viewInput' backlink=" ++ (unpack link)
return $ Just $ tr $ td $ do
--fromString title
fromString " "
blazeForm lf link ! A.id "InputForm"
_ -> return Nothing
inputForm' :: InputField -> ImpForm InputData
inputForm' (Radio s choices) = RadioData <$> RB.label s ++> (inputRadio' choices (== 0)) <++ RB.label (" " :: String)
inputForm' (Text s) = TextData <$> RB.label s ++> (RB.inputText "") <++ label (" " :: String)
inputForm' (TextArea s) = TextAreaData <$> RB.label s ++> (textarea 50 5 "") <++ label (" " :: String)
inputForm' (Button s) = pure ButtonData <$> RB.label s
inputForm' (Checkbox s choices) = CheckboxData <$> RB.label s ++> (inputCheckboxes choices $ const False) <++ label (" " :: String)
-- | a form result has been sent
newInput :: Input -> EventNumber -> TVar s -> UpdateSession s -> Text -> ServerPartT IO Response
newInput is@(Input i _) en tv updateSession bl = toResponse <$> do
methodM POST
r <- eitherForm environment "user" (inputForm' i)
case r of
(Right id) -> liftIO $ updateSession tv is id en
(Left _) -> liftIO $ putStrLn "cannot retrieve form data"
seeOther bl "Redirecting..."
-- | Create a group of radio elements without BR between elements
reformInputRadio' :: (Functor m, Monad m, FormError error, ErrorInputType error ~ input, FormInput input, ToMarkup lbl) =>
[(a, lbl)] -- ^ value, label, initially checked
-> (a -> Bool) -- ^ isDefault
-> Form m input error Html () a
reformInputRadio' choices isDefault =
G.inputChoice isDefault choices mkRadios
where
mkRadios nm choices' = mconcat $ concatMap (mkRadio nm) choices'
mkRadio nm (i, val, lbl, checked) =
[ (if checked then (! A.checked "checked") else Prelude.id) $ input ! A.type_ "radio" ! A.id (toValue i) ! A.name (toValue nm) ! A.value (toValue val)
, " ", H.label ! A.for (toValue i) $ toHtml lbl]
blazeForm :: Html -> Text -> Html
blazeForm html link =
H.form ! A.action (toValue link)
! A.method "POST"
! A.enctype "multipart/form-data" $
do html
input ! A.type_ "submit" ! A.value "Submit"
-- | Create a group of radio elements without BR between elements
inputRadio' :: (Functor m, Monad m, FormError error, ErrorInputType error ~ input, FormInput input, ToMarkup lbl) =>
[(a, lbl)] -- ^ value, label, initially checked
-> (a -> Bool) -- ^ isDefault
-> Form m input error Html () a
inputRadio' choices isDefault =
G.inputChoice isDefault choices mkRadios
where
mkRadios nm choices' = mconcat $ concatMap (mkRadio nm) choices'
mkRadio nm (i, val, lbl, checked) =
[ (if checked then (! A.checked "checked") else Prelude.id) $ input ! A.type_ "radio" ! A.id (toValue i) ! A.name (toValue nm) ! A.value (toValue val)
, " ", H.label ! A.for (toValue i) $ toHtml lbl]