-- Hoogle documentation, generated by Haddock -- See Hoogle, http://www.haskell.org/hoogle/ -- | Web application server with stateful, type safe user interactions and widget combinators. -- -- Simple application server with stateful request-response flows, -- persistent and transparent session handling. server process -- management, combinators for the definition of widgets and formlets -- that can be mixed freely with HTML formatting and produce statically -- typed web applications. Adopt and extend the best formlet/applicative -- Haskell traditions Console and window oriented apps are possible. -- -- MFlow (MessageFlow) was created initially as the user interface for -- the Workflow package Currently is an alpha version. It has only basic -- authentication but I plan to inprove it for serious applications. -- -- It includes Application Server features such is resource an process -- management and automatic recovery -- -- Resource management: The user can define process and session timeout. -- The process is automatically rerun after timeout if a new request -- arrive with transparent recovery of state, at the point of the -- interrupted dialog even after server crash. -- -- The backend operation relies on the Workflow package -- (http://hackage.haskell.org/package/Workflow/index), this gives -- transparent sessión persistence and recovery, all of this is supported -- by TCache (http://hackage.haskell.org/package/TCache/index), -- that gives backend-independent transactions and can be used directly -- by the programmer. Persistence in files for session and data out of -- the box enables very fast prototyping. -- -- All the plumbing is hidden to the programmer, There are no methods for -- session management, database query, recovery and so on. All of this is -- transparent So the surface exposed to the programmer is minimal. -- -- Includes generalized formlets that permits the mix of active widgets -- in the same page while remaining statically typed and, thus the -- programs can verify correctness at compilation time. -- -- Includes combinators for seamless inclusion of these widgets within -- user defined HTML formatting. Bindings for Text.XHtml. The widget -- generation may be easy for user with familiarity with -- formlets/digestive functors and Text.XHtml formatting. -- -- Currently it has bindings for the Hack interface -- -- Streaming facilities. -- -- To do: -- -- Bindings for HSP -- -- Clustering -- -- Other bindigs for Hack alternatives @package MFlow @version 0.0.4 -- | This module defines an integrated way to interact with the user. -- ask is a single method of user interaction. it send user -- interfaces and return statically typed responses. The user interface -- definitions are based on the formLets interface -- -- But additionally, unlike formLets in its current form, it permits the -- definition of widgets. A widget is data that, when renderized and -- interact with the user, return data, just like a formlet, but it hasn -- to be an HTML form. it can contain JavaScript, or additional Html -- decoration or it can use Ajax istead of form post for the interaction. -- There is an example of widget defined (Selection) -- -- widgets (and formlets) can be combined in a sigle Html page. Here is a -- ready-to-run example that combines a Widget (Selection) and a HTML -- decorated formLet in the same page. -- --
--   import MFlow.Hack.XHtml.All
--   
--   import Data.Typeable
--   import Control.Monad.Trans
--   import qualified Data.Vector as V
--   
--   main= do
--   
--   putStrLn $ options messageFlows
--      run 80 $ hackMessageFlow messageFlows
--      where
--      messageFlows=  [("main",  runFlow mainProds )
--                     ,("hello", stateless hello)]
--      options msgs= "in the browser choose\n\n" ++
--        concat [ http://server/++ i ++ n | (i,_) <- msgs]
--   
--   --an stateless procedure, as an example
--   hello :: Env -> IO String
--   hello env =  return  "hello, this is a stateless response"
--   
--   data Prod= Prod{pname :: String, pprice :: Int} deriving (Typeable,Read,Show)
--   
--   -- formLets can have Html formatting. Additional operators <++ <+> <<< ++> to XHtml formatting
--   
--   instance FormLet Prod IO Html where
--      digest mp= table <<< (
--         Prod <$> tr <<< (td << "enter the name"  <++ td <<< getString (pname <$> mp))
--              <*> tr <<< (td << "enter the price" <++ td <<< getInt ( pprice <$> mp)))
--   
--   -- Here an example of predefined widget (Selection) that return an Int, combined in the same
--   -- page with the fromLet for the introduction of a product.
--   -- The result of the user interaction is Either one or the other value
--   
--   shopProds :: V.Vector Int -> [Prod]
--             -> View Html IO  (Either Int Prod)
--   shopProds cart products=
--   
--   p << "----------------Shopping List--------------"
--     <++
--     widget(Selection{
--          stitle = bold << "choose an item",
--          sheader= [ bold << "item"   , bold << "price", bold << "times chosen"],
--          sbody= [([toHtml pname, toHtml $ show pprice, toHtml $ show $ cart V.! i],i )
--                 | (Prod{..},i ) <- zip products [1..]]})
--   
--   <+>
--     p << "--------------Add a new product ---------------"
--     <++
--     table <<< (tr <<< td ! [valign "top"]
--                             <<< widget (Form (Nothing :: Maybe Prod) )
--                ++>
--                tr << td ! [align "center"]
--                             << hotlink  "hello"
--                                         (bold << "Hello World"))
--   
--   -- the header
--   
--   appheader user forms= thehtml
--            << body << dlist << (concatHtml
--               [dterm <<("Hi "++ user)
--               ,dterm << "This example contains two forms enclosed within user defined HTML formatting"
--               ,dterm << "The first one is defined as a Widget, the second is a formlet formatted within a table"
--               ,dterm << "both are defined using an extension of the FormLets concept"
--               ,dterm << "the form results are statically typed"
--               ,dterm << "The state is implicitly logged. No explicit handling of state"
--               ,dterm << "The program logic is written as a procedure. Not    in request-response form. But request response is possible"
--               ,dterm << "lifespan of the serving process and the execution state defined by the programmer"
--               ,dterm << "user state is  automatically recovered after cold re-start"
--               ,dterm << "transient, non persistent states possible."
--               ])
--               +++ forms
--   
--   -- Here the procedure. It ask for either entering a new product
--   -- or to "buy" one of the entered products.
--   -- There is a timeout of ten minutes before the process is stopped
--   -- THERE IS A timeout of one day for the whole state so after this, the
--   -- user will see the list erased.
--   -- The state is user specific.
--   
--   --mainProds ::  FlowM Html (Workflow IO) ()
--   mainProds   = do
--      setTimeouts (10*60) (24*60*60)
--      setHeader $ w -> bold << "Please enter userpassword (pepepepe)" +++ br +++ w
--   
--   setHeader  $ appheader user
--      mainProds1 [] $ V.fromList [0]
--      where
--      mainProds1  prods cart=  do
--        mr <- step . ask  $ shopProds  cart prods
--        case mr of
--         Right prod -> mainProds1  (prod:prods) (V.snoc cart 0)
--         Left i   -> do
--            let newCart= cart V. [(i, cart V.! i + 1 )]
--            mainProds1 prods newCart
--   
module MFlow.Forms class (Functor m, MonadIO m) => Widget a b m view | a -> view widget :: Widget a b m view => a -> View view m b class (Functor m, MonadIO m) => FormLet a m view digest :: FormLet a m view => Maybe a -> View view m a -- | Launchable widgets create user requests. For example whatever piece -- containing a Form tag, a link with an embeeded Ajax invocation etc. -- -- A FormLet for an input field can not be an instance of Launchable, for -- example to invoke it with ask, make the widget an instance of -- Launchable class Widget a b m view => Launchable a b m view type View view m a = FormT view (FlowM view m) a -- | Minimal interface for defining the abstract basic form combinators -- defined in this module. see MFlow.Forms.XHtml for the instance -- for Text.XHtml format class FormInput view where flink1 verb = flink verb (fromString verb) foption1 name list msel = foption name (zip list list) msel inred :: FormInput view => view -> view ftable :: FormInput view => view -> [view] -> [[view]] -> view fromString :: FormInput view => String -> view flink :: FormInput view => String -> view -> view flink1 :: FormInput view => String -> view finput :: FormInput view => Name -> Type -> Value -> Checked -> OnClick -> view ftextarea :: FormInput view => String -> String -> view foption :: FormInput view => String -> [(String, String)] -> Maybe String -> view foption1 :: FormInput view => String -> [String] -> Maybe String -> view fformAction :: FormInput view => String -> view -> view newtype FormT view m a FormT :: (Params -> m (FormElm view a)) -> FormT view m a runFormT :: FormT view m a -> Params -> m (FormElm view a) data FormElm view a FormElm :: [view] -> (Maybe a) -> FormElm view a newtype Form a Form :: a -> Form a data Selection a view Selection :: view -> [view] -> [([view], a)] -> Selection a view stitle :: Selection a view -> view sheader :: Selection a view -> [view] sbody :: Selection a view -> [([view], a)] -- | register an user/password combination userRegister :: String -> String -> IO () -- | authentication against userRegistered users. to be used with -- validate userAuthenticate :: MonadIO m => User -> m (Maybe String) data User -- | Very basic user authentication. The user is stored in a cookie. it -- looks for the cookie. If no cookie, it ask to the user for a -- userRegistered user-password combination. It return a reference -- to the user. getUser :: (FormInput view, Monoid view, Typeable view, ConvertTo (HttpData view) display, Typeable display, MonadIO m, Functor m) => FlowM view m String -- | it is the way to interact with the user. It takes a combination of -- launchable objects and return the user result in the FlowM -- monad ask :: (Launchable a b m view, FormInput view, Monoid view, Typeable view, ConvertTo (HttpData view) display, Typeable display) => a -> FlowM view m b getString :: (FormInput view, Monad m) => Maybe String -> View view m String getInt :: (FormInput view, Functor m, MonadIO m) => Maybe Int -> View view m Int getInteger :: (FormInput view, Functor m, MonadIO m) => Maybe Integer -> View view m Integer getMultilineText :: (FormInput view, Monad m) => Maybe [Char] -> View view m String getBool :: (FormInput view, Monad m) => Maybe String -> String -> String -> View view m Bool getOption :: (FormInput view, Monad m) => Maybe String -> [(String, String)] -> View view m String getPassword :: (FormInput view, Monad m) => View view m String -- | Validates a form or widget result against a validating procedure -- -- getOdd= getInt Nothing validate (x-> return $ if mod x 2==0 -- then Nothing else Just only odd number please) validate :: (FormInput view, Functor m, MonadIO m) => View view m a -> (a -> m (Maybe String)) -> View view m a -- | join two widgets in the same pages the resulting widget, when -- asked with it, returns a either one or the other mix :: (FormInput view, Monad m) => View view m a' -> View view m b' -> View view m (Either a' b') -- | encloses instances of Widget or FormLet in formating -- view is intended to be instantiated to a particular format see -- MFlow.Forms.XHtml for usage examples wrap :: (Monad m, FormInput view, Monoid view) => (view -> view) -> View view m a -> View view m a -- | append formatting to Widget or FormLet instances view is -- intended to be instantiated to a particular format see -- MFlow.Forms.XHtml for usage examples addToForm :: (Monad m, FormInput view, Monoid view) => View view m a -> view -> View view m a type FlowM view = StateT (MFlowState view) runFlow :: (FormInput view, Monoid view, Monad m) => FlowM view m () -> Token -> m () step :: (Serialize a, MonadIO m, Typeable a) => FlowM view m a -> FlowM view (Workflow m) a setHeader :: Monad m => (view -> view) -> FlowM view m () setTimeouts :: Monad m => Int -> Integer -> FlowM view m () -- | set an HTTP cookie setCookie :: Monad m => String -> String -> String -> Maybe String -> FlowM view m () instance [incoherent] Typeable User instance [incoherent] Read User instance [incoherent] Show User instance [incoherent] (MonadIO m, Functor m, FormInput view) => FormLet Bool m view instance [incoherent] (FormInput view, FormLet a m view, FormLet b m view, FormLet c m view) => FormLet (a, b, c) m view instance [incoherent] (FormInput view, FormLet a m view, FormLet b m view) => FormLet (a, b) m view instance [incoherent] FormLet a m view => Widget (Maybe a) a m view instance [incoherent] (Monad m, Functor m) => Monad (FormT view m) instance [incoherent] (Functor m, Monad m) => Applicative (FormT view m) instance [incoherent] Functor m => Functor (FormT view m) instance [incoherent] Functor (FormElm view) instance [incoherent] (FormInput view, Monoid view, Widget a b m view) => Widget (Form a) b m view instance [incoherent] (FormInput view, Monoid view, Widget a b m view) => Launchable (Form a) b m view instance [incoherent] (MonadIO m, Functor m, FormInput view, Read a, Show a, Eq a, Typeable a) => Widget (Selection a view) a m view instance [incoherent] (MonadIO m, Functor m, FormInput view, Typeable a, Show a, Read a, Eq a) => Launchable (Selection a view) a m view instance [incoherent] (MonadIO m, Functor m) => Launchable (View view m a) a m view instance [incoherent] (MonadIO m, Functor m) => Widget (View view m a) a m view instance [incoherent] (MonadIO m, Functor m, FormInput view) => FormLet User m view instance [incoherent] Indexable User module MFlow.Forms.XHtml -- | prepend Html formatting to a widget -- --
--   bold << hi there
--     <++
--     widget widget1
--   
(<++) :: Monad m => Html -> View Html m a -> View Html m a -- | join two widgets in the same pages the resulting widget, when -- asked with it, returns a either one or the other -- --
--   r - ask widget widget1 <+ widget widget2
--   
(<+>) :: Monad m => View Html m a' -> View Html m b' -> View Html m (Either a' b') -- | append Html to a widget -- --
--   widget widget1
--    ++>
--    H1 << hi there
--   
(++>) :: Monad m => View Html m a -> Html -> View Html m a -- | encloses a widget in Html formatting -- --
--   table <<< (
--         tr <<< (td << widget widget1)
--         tr <<< (td << widget widget2))
--   
(<<<) :: Monad m => (Html -> Html) -> View Html m a -> View Html m a instance FormInput Html instance Typeable Html module MFlow.Hack type Cookie = (String, String, String, Maybe String) ctype :: ([Char], [Char]) getCookies :: [([Char], [Char])] -> [([Char], [Char])] cookieHeaders :: [Cookie] -> [([Char], String)] urlDecode :: String -> [([(String, String)], String)] type Params = [(String, String)] getParam1 :: (Typeable a, Read a) => String -> Params -> Maybe a data Req Req :: a -> Req data Resp Fragm :: a -> Resp EndFragm :: a -> Resp Resp :: a -> Resp type Workflow (m :: * -> *) = WF Stat m data HttpData a HttpData :: [Cookie] -> a -> HttpData a class Processable a pwfname :: Processable a => a -> String puser :: Processable a => a -> String pind :: Processable a => a -> String getParams :: Processable a => a -> Params class ConvertTo a b | a -> b convert :: ConvertTo a b => a -> b data Token Token :: String -> String -> String -> TChan Req -> TChan Resp -> Token twfname :: Token -> String tuser :: Token -> String tind :: Token -> String q :: Token -> TChan Req qr :: Token -> TChan Resp getToken :: Processable a => a -> IO Token data Error Error :: String -> Error -- | List of (wfname, workflow) pairs, to be scheduled depending on the -- message's pwfname type ProcList = WorkflowList IO Token () flushRec :: Token -> IO () receive :: (Processable a, Typeable a) => Token -> IO a receiveReq :: Token -> IO Req receiveReqTimeout :: Int -> Integer -> Token -> IO Req -- | send a complete response send :: (Typeable a, Typeable b, ConvertTo a b) => Token -> a -> IO () sendFlush :: (Typeable b, Typeable a, ConvertTo a b) => Token -> a -> IO () -- | send a response fragment. Useful for streaming. the last packet must -- sent trough send sendFragment :: (Typeable a, Typeable b, Monoid b, ConvertTo a b) => Token -> a -> IO () sendEndFragment :: (Typeable a, Typeable b, Monoid b, ConvertTo a b) => Token -> a -> IO () msgScheduler :: (Processable a, Typeable a, ConvertTo Error c, Typeable c) => a -> ProcList -> IO (c, ThreadId) addMessageFlows :: [(String, Token -> Workflow IO ())] -> IO () getMessageFlows :: IO ProcList -- | to add a monadic computation that send and receive messages, but does -- not store its state in permanent storage. transient :: (Token -> IO ()) -> (Token -> Workflow IO ()) stateless :: (Typeable a, Processable a, Typeable b, ConvertTo b c, Typeable c) => (a -> IO b) -> (Token -> Workflow IO ()) -- | An instance of the abstract MFlow scheduler to the Hack -- interface it accept the list of processes being scheduled and return a -- hack handler -- -- Example: -- --
--   main= do
--   
--   putStrLn $ options messageFlows
--      run 80 $ hackMessageFlow messageFlows
--      where
--      messageFlows=  [("main",  runFlow flowname )
--                     ,("hello", stateless statelesproc)
--                     ,("trans", transient $ runflow transientflow]
--      options msgs= "in the browser choose\n\n" ++
--        concat [ http://server/++ i ++ n | (i,_) <- msgs]
--   
hackMessageFlow :: ProcList -> (Env -> IO Response) instance Typeable Flow instance Read Flow instance Show Flow instance ToResponse v => ConvertTo (HttpData v) TResp instance ConvertTo Error TResp instance ConvertTo ByteString TResp instance ConvertTo String TResp instance Indexable Flow instance Processable Env module MFlow.Hack.XHtml instance ToResponse Html module MFlow.Hack.XHtml.All