-- Hoogle documentation, generated by Haddock -- See Hoogle, http://www.haskell.org/hoogle/ -- | Web app server for stateful processes with safe, composable user interfaces. -- -- A Web framework with some unique features thanks to the power of the -- Haskell language. MFlow run stateful server processes; All the flow of -- request and responses are coded by the programmer in a single -- function. Allthoug single request-response flows and callbacks are -- possible. Therefore, the code is more understandable. -- -- These processes are stopped and restarted by the application server on -- demand, including its execution state. Therefore session management is -- automatic. State consistence and transactions are given by the TCache -- package. -- -- The processes interact trough widgets, that are an extension of -- formlets with additional combinators, formatting, link management, -- callbacks, modifiers, caching, byteString conversion and AJAX. All is -- coded in pure haskell. -- -- The interfaces and communications are abstract, but there are bindings -- for HSP, Text.XHtml and byteString , Hack and WAI but it can be -- extended to non Web based architectures. -- -- This release add transparent back button management, cached widgets, -- callbacks, modifiers, heterogeneous formatting, AJAX, and WAI -- integration. -- -- It is designed for applications that can be run with no deployment -- with runghc in order to speed up the development process. -- -- See MFlow.Forms for details -- -- Altroug still it is experimental, it is being used in at least one -- future commercial project. So I have te commitment to continue its -- development. There are many examples in the documentation and in the -- package. -- -- Due to compilation problems in Hackage, this release has the bindings -- for Hack and HSP not exposed, but they can be found in the source -- files. -- -- To do: -- -- @package MFlow @version 0.1.5.2 module MFlow.Cookies type Cookie = (String, String, String, Maybe String) ctype :: (String, String) urlDecode :: String -> [(String, String)] cookieuser :: String cookieHeaders :: IsString t => [Cookie] -> [(t, String)] getCookies :: (Eq a, IsString a) => [(a, [Char])] -> [([Char], [Char])] -- | Non monadic low level support stuff for the MFlow application server. -- (See MFlow.Form for the higher level interfaces) it implements -- an scheduler of queued Processable messages that are served -- according with the source identification and the verb invoked. The -- scheduler executed the appropriate workflow (using the workflow -- package). The workflow may send additional messages to the source, -- identified by a Token . The computation state is optionally -- logged and recovered. -- -- The message communication is trough polimorphic, monoidal queues. -- There is no asumption about message codification, so instantiations of -- this scheduler for different infrastructures is possible, including -- non-Web based ones as long as they support or emulate cookies. -- -- MFlow.Hack is an instantiation for the Hack interface in a Web -- context. -- -- MFlow.Wai is a instantiation for the WAI interface. -- -- MFlow.Forms implements a monadic type safe interface with -- composabe widgets and a higher comunication interface. -- -- MFlow.Forms.XHtml is an instantiation for the Text.XHtml format -- -- MFlow.Forms.HSP is an instantiation for the Haskell Server -- Pages format -- -- In order to manage resources, there are primitives that kill the -- process and its state after a timeout. -- -- All these details are hidden in the monad of MFlow.Forms that -- provides an higher level interface. -- -- Fragment based streaming sendFragment sendEndFragment -- are provided only at this level. -- -- stateless and transient server processeses are also -- possible. stateless are request-response with no intermediate -- messaging dialog. transient processes have no persistent state, -- so they restart anew after a timeout or a crash. module MFlow type Params = [(String, String)] type Workflow (m :: * -> *) = WF Stat m data HttpData HttpData :: Params -> [Cookie] -> ByteString -> HttpData Error :: WFErrors -> ByteString -> HttpData class Processable a pwfname :: Processable a => a -> String puser :: Processable a => a -> String pind :: Processable a => a -> String getParams :: Processable a => a -> Params class ToHttpData a toHttpData :: ToHttpData a => a -> HttpData -- | a Token identifies a flow that handle messages. The scheduler compose -- a Token with every Processable message that arrives and send -- the mesage to the appropriate flow. data Token Token :: String -> String -> String -> MVar Req -> MVar Resp -> Token twfname :: Token -> String tuser :: Token -> String tind :: Token -> String q :: Token -> MVar Req qr :: Token -> MVar Resp -- | List of (wfname, workflow) pairs, to be scheduled depending on the -- message's pwfname type ProcList = WorkflowList IO Token () flushRec :: Token -> IO () receive :: Typeable a => Token -> IO a receiveReq :: Token -> IO Req receiveReqTimeout :: Int -> Integer -> Token -> IO Req -- | send a complete response send :: ToHttpData a => Token -> a -> IO () sendFlush :: ToHttpData a => Token -> a -> IO () -- | send a response fragment. Useful for streaming. the last packet must -- sent trough send sendFragment :: ToHttpData a => Token -> a -> IO () sendEndFragment :: ToHttpData a => Token -> a -> IO () -- | add a list of flows to be scheduled. Each entry in the list is a pair -- (path, flow) addMessageFlows :: [([Char], Token -> Workflow IO ())] -> IO () -- | return the list of the scheduler getMessageFlows :: IO (Map String (Token -> Workflow IO ())) -- | Executes a monadic computation that send and receive messages, but -- does not store its state in permanent storage. The process once -- stopped, will restart anew transient :: (Token -> IO ()) -> (Token -> Workflow IO ()) -- | executes a simple monadic computation that receive the params and -- return a response -- -- It is used with addMessageFlows hackMessageFlow or -- waiMessageFlow stateless :: ToHttpData b => (Params -> IO b) -> (Token -> Workflow IO ()) -- | The anonymous user anonymous :: [Char] -- | It is the path of the root flow noScript :: [Char] -- | The handler of the error log hlog :: Handle -- | set the 404 not found response setNotFoundResponse :: MonadIO m => (ByteString -> ByteString) -> m () getNotFoundResponse :: ByteString -> ByteString -- | Writes a XML tag in a ByteString. It is the most basic form of -- formatting. For more sophisticated formatting , use -- MFlow.Forms.XHtml or MFlow.Forms.HSP. btag :: String -> Attribs -> ByteString -> ByteString -- |
--   bhtml ats v= btag "html" ats v
--   
bhtml :: Attribs -> ByteString -> ByteString -- |
--   bbody ats v= btag "body" ats v
--   
bbody :: Attribs -> ByteString -> ByteString type Attribs = [(String, String)] addTokenToList :: Token -> IO () deleteTokenInList :: Token -> IO () -- | The scheduler creates a Token with every Processable message -- that arrives and send the mesage to the appropriate flow, get the -- response and return it. msgScheduler :: (Typeable a, Processable a) => a -> IO (HttpData, ThreadId) instance Typeable HttpData instance Typeable Req instance Typeable Token instance Show HttpData instance ToHttpData String instance Serializable Token instance Read Token instance Show Token instance Indexable Token instance Processable Req instance Monoid HttpData instance ToHttpData ByteString instance ToHttpData HttpData -- | This module implement stateful processes (flows) that are optionally -- persistent. This means that they automatically store and recover his -- execution state. They are executed by the MFlow app server. defined in -- the MFlow module. -- -- These processses interact with the user trough user interfaces made of -- widgets (see below) that return back statically typed responses to the -- calling process. Because flows are stateful, not request-response, the -- code is more understandable, because all the flow of request and -- responses is coded by the programmer in a single function. Allthoug -- single request-response flows and callbacks are possible. -- -- This module is abstract with respect to the formatting (here referred -- with the type variable view) . For an instantiation for -- Text.XHtml import MFlow.Forms.XHtml, -- MFlow.Hack.XHtml.All or MFlow.Wai.XHtml.All . To use -- Haskell Server Pages import MFlow.Forms.HSP. However the -- functionalities are documented here. -- -- ask is the only method for user interaction. It run in the -- MFlow view m monad, with m the monad chosen by the -- user, usually IO. It send user interfaces (in the View view m -- monad) and return statically typed responses. The user interface -- definitions are based on a extension of formLets -- (http://www.haskell.org/haskellwiki/Formlets) with the addition -- of caching, links, formatting, attributes, extra combinators, callbaks -- and modifiers. The interaction with the user is stateful. In the same -- computation there may be many request-response interactions, in the -- same way than in the case of a console applications. -- -- -- -- Therefore, session and state management is simple and transparent: it -- is in the haskell structures in the scope of the computation. -- transient (normal) procedures have no persistent session state -- and stateless procedures accept a single request and return a -- single response. -- -- step is a lifting monad transformer that permit persistent -- server procedures that remember the execution state even after system -- shutdowns by using the package workflow -- (http://hackage.haskell.org/package/Workflow) internally. This -- state management is transparent. There is no programer interface for -- session management. -- -- The programmer set the process timeout and the session timeout with -- setTimeouts. If the procedure has been stopped due to the -- process timeout or due to a system shutdowm, the procedure restart in -- the last state when a request for this procedure arrives (if the -- procedure uses the step monad transformer) -- -- -- -- The correctness of the web responses is assured by the use of -- formLets. But unlike formLets in its current form, it permits the -- definition of widgets. A widget is a combination of formLets and -- links within its own formatting template, all in the same -- definition in the same source file, in plain declarative Haskell -- style. -- -- The formatting is abstract. It has to implement the FormInput -- class. There are instances for Text.XHtml (MFlow.Forms.XHtml), -- Haskell Server Pages (MFlow.Forms.HSP) and ByteString. So -- widgets can use any formatting that is instance of FormInput. It is -- possible to use more than one format in the same widget. -- -- Links defined with wlink are treated the same way than forms. -- They are type safe and return values to the same flow of execution. It -- is posssible to combine links and forms in the same widget by using -- applicative combinators but also additional applicative combinators -- like <+> !*> , |*|. -- -- -- -- -- -- Example: -- --
--   ask $ wform userloginform `validate` valdateProc `waction` loginProc `wmodify` hideIfLogged
--   
-- -- -- --
--   userFormLine=
--          (User <$> getString (Just enter user)                  <! [(size,5)]
--                <*> getPassword                                    <! [(size,5)]
--                <+> submitButton login)
--                <+> fromString   password again ++> getPassword  <! [(size,5)]
--                <*  submitButton register
--   
-- -- -- -- This is a complete example, that can be run with runghc, which show -- some of these features: -- --
--   
--   module Main where
--   import MFlow.Wai.XHtml.All
--   import Data.TCache
--   import Control.Monad.Trans
--   import Data.Typeable
--   import Control.Concurrent
--   import Control.Exception as E
--   import qualified Data.ByteString.Char8 as SB
--   import qualified Data.Vector as V
--   import Data.Maybe
--   
--   
--   
--   data Ops= Ints | Strings | Actions | Ajax | Shop deriving(Typeable,Read, Show)
--   
--   main= do
--      setFilesPath ""
--      addFileServerWF
--      addMessageFlows [(""  ,transient $ runFlow mainf)
--                      ,("shop"    ,runFlow shopCart)]
--      forkIO $ run 80 waiMessageFlow
--      adminLoop
--   
--   
--   
--   stdheader c= p << "you can press the back button to go to the menu"+++ c
--   mainf=   do
--          setHeader stdheader
--          r <- ask $   wlink Ints (bold << "increase an Int")
--                  <|>  br ++> wlink Strings (bold << "increase a String")
--                  <|>  br ++> wlink Actions (bold << "Example of a string widget with an action")
--                  <|>  br ++> wlink Ajax (bold << "Simple AJAX example")
--                  <++ (br +++ linkShop) -- this is an ordinary XHtml link
--   
--          case r of
--            Ints ->  clickn 0
--            Strings ->  clicks "1"
--            Actions ->  actions 1
--            Ajax    ->  ajaxsample
--   
--          mainf
--       where
--       linkShop= toHtml $ hotlink  "shop" << "shopping"
--   
--   clickn (n :: Int)= do
--      setHeader stdheader
--      r <- ask $  wlink "menu" (p << "menu")
--              |+| getInt (Just n) <* submitButton "submit"
--      case r of
--       (Just _,_) -> breturn ()
--       (_, Just n') -> clickn $ n'+1
--   
--   
--   clicks s= do
--      setHeader stdheader
--      s' <- ask $ (getString (Just s)
--                <* submitButton "submit")
--                `validate` (\s -> return $ if length s   > 5 then Just "length must be < 5" else Nothing )
--      clicks $ s'++ "1"
--   
--   
--   ajaxheader html= thehtml << ajaxHead << p << "click the box" +++ html
--   
--   ajaxsample= do
--      setHeader ajaxheader
--      ajaxc <- ajaxCommand    "document.getElementById('text1').value"
--                              (\n ->  return $ "document.getElementById('text1').value='"++show(read  n +1)++"'")
--      ask $ (getInt (Just 0) <! [("id","text1"),("onclick", ajaxc)])
--      breturn()
--   
--   actions n=do
--     ask $ wlink () (p << "exit from action")
--        <**((getInt (Just (n+1)) <** submitButton "submit" ) `waction` actions )
--     breturn ()
--   
--   -- A persistent flow  (uses step). The process is killed after 10 seconds of inactivity
--   -- but it is restarted automatically. if you restart the program, it remember the shopping cart
--   -- defines a table with links enclosed that return ints and a link to the menu, that abandon this flow.
--   shopCart  = do
--      setTimeouts 10 0
--      shopCart1 (V.fromList [0,0,0:: Int])
--      where
--      shopCart1 cart=  do
--        i <- step . ask $
--                table ! [border 1,thestyle "width:20%;margin-left:auto;margin-right:auto"]
--                <<< caption << "choose an item"
--                ++> thead << tr << concatHtml[ th << bold << "item", th << bold << "times chosen"]
--                ++> (tbody
--                     <<<  tr ! [rowspan 2] << td << linkHome
--                     ++> (tr <<< td <<< wlink  0 (bold <<"iphone") <++  td << ( bold << show ( cart V.! 0))
--                     <|>  tr <<< td <<< wlink  1 (bold <<"ipad")   <++  td << ( bold << show ( cart V.! 1))
--                     <|>  tr <<< td <<< wlink  2 (bold <<"ipod")   <++  td << ( bold << show ( cart V.! 2)))
--                     <++  tr << td << linkHome
--                     )
--   
--        let newCart= cart V.// [(i, cart V.! i + 1 )]
--        shopCart1 newCart
--       where
--       linkHome= (toHtml $ hotlink  noScript << bold << "home")
--   
module MFlow.Forms -- | A FormLet instance class (Functor m, MonadIO m) => FormLet a m view digest :: FormLet a m view => Maybe a -> View view m a type FlowM view m = BackT (WState view m) data View v m a -- | Minimal interface for defining the basic form combinators in a -- concrete rendering. defined in this module. see -- MFlow.Forms.XHtml for the instance for Text.XHtml and -- MFlow.Forms.HSP for an instance form Haskell Server Pages. class Monoid view => FormInput view where flink1 verb = flink verb (fromString verb) foption1 val msel = foption val (fromString val) msel inred :: FormInput 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 fselect :: FormInput view => String -> view -> view foption :: FormInput view => String -> view -> Bool -> view foption1 :: FormInput view => String -> Bool -> view formAction :: FormInput view => String -> view -> view addAttributes :: FormInput view => view -> Attribs -> view -- | Register an user/password userRegister :: MonadIO m => String -> String -> m (DBRef User) -- | Authentication against userRegistered users. to be used with -- validate userValidate :: MonadIO m => (UserStr, PasswdStr) -> m (Maybe String) -- | Wether the user is logged or is anonymous isLogged :: MonadState (MFlowState v) m => m Bool data User setAdminUser :: MonadIO m => UserStr -> PasswdStr -> m () getAdminName :: MonadIO m => m UserStr getCurrentUser :: MonadState (MFlowState view) m => m String -- | If not logged, perform login. otherwise return the user -- --
--   getUserSimple= getUser Nothing userFormLine
--   
getUserSimple :: (FormInput view, Monoid view, Typeable view, ToHttpData view, MonadIO m, Functor m) => FlowM view m String -- | 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. The user-password -- combination is only asked if the user has not logged already -- otherwise, the stored username is returned. -- --
--   getUser mu form= ask $ userWidget mu form
--   
getUser :: (FormInput view, Monoid view, Typeable view, ToHttpData view, MonadIO m, Functor m) => Maybe String -> View view m (Maybe (Maybe (UserStr, PasswdStr), Maybe String), Maybe String) -> FlowM view m String -- | Is an example of login/register validation form needed by -- userWidget. In this case the form field appears in a single -- line. it shows, in sequence, entries for the username, password, a -- button for loging, a entry to repeat password necesary for registering -- and a button for registering. The user can build its own user -- login/validation forms by modifying this example -- --
--   userFormLine=
--       (User <$> getString (Just "enter user") <*> getPassword <+> submitButton "login")
--       <+> fromString "  password again" +> getPassword <* submitButton "register"
--   
userFormLine :: (FormInput view, Monoid view, Functor m, Monad m) => View view m (Maybe (Maybe (UserStr, PasswdStr), Maybe String), Maybe String) -- | Example of user/password form (no validation) to be used with -- userWidget userLogin :: (FormInput view, Monoid view, Functor m, Monad m) => View view m (Maybe (Maybe (UserStr, PasswdStr), Maybe String), Maybe String) -- | It creates a widget for user login/registering. If a user name is -- specified in the first parameter, it is forced to login/password as -- this specific user. Otherwise, if the user is already logged, the -- widget does not appear If the user press the register button, the -- user/password is registered and the user userWidget :: (MonadIO m, Functor m, FormInput view, Monoid view) => Maybe String -> View view m (Maybe (Maybe (UserStr, PasswdStr), Maybe String), Maybe String) -> View view m String -- | It is the way to interact with the user. It takes a widget and return -- the user result If the environment has the result, ask don't ask to -- the user. To force asking in any case, put an clearEnv -- statement before in the FlowM monad ask :: (ToHttpData view, FormInput view, Monoid view, MonadIO m, Typeable view) => View view m b -> FlowM view m b -- | Clears the environment clearEnv :: MonadState (MFlowState view) m => m () -- | display a text box and return a String getString :: (FormInput view, Monad m) => Maybe String -> View view m String -- | display a text box and return a Int (if the value entered is not an -- Int, fails the validation) getInt :: (FormInput view, Functor m, MonadIO m) => Maybe Int -> View view m Int -- | display a text box and return an Integer (if the value entered is not -- an Integer, fails the validation) getInteger :: (FormInput view, Functor m, MonadIO m) => Maybe Integer -> View view m Integer getTextBox :: (FormInput view, Monad m, Typeable a, Show a, Read a) => Maybe a -> View view m a -- | display a multiline text box and return its content getMultilineText :: (FormInput view, Monad m) => String -> View view m String -- | display a dropdown box with the two values (second (true) and third -- parameter(false)) . With the value of the first parameter selected. getBool :: (FormInput view, Monad m) => Bool -> String -> String -> View view m Bool -- | display a dropdown box with the options in the first parameter is -- optionally selected . It returns the selected option. getSelect :: (FormInput view, Monad m, Typeable a, Read a) => View view m (MFOption a) -> View view m a -- | set the option for getSelect. Options are concatenated with -- <|> setOption :: (Monad m, Show a, Typeable a, FormInput view) => a -> view -> View view m (MFOption a) -- | set the selected option for getSelect. Options are concatenated with -- <|> setSelectedOption :: (Monad m, Show a, Typeable a, FormInput view) => a -> view -> View view m (MFOption a) -- | display a password box getPassword :: (FormInput view, Monad m) => View view m String -- | implement a radio button the parameter is the name of the radio group getRadio :: (FormInput view, Functor m, MonadIO m) => String -> String -> View view m String -- | implement a radio button that perform a submit when pressed. the -- parameter is the name of the radio group getRadioActive :: (FormInput view, Functor m, MonadIO m) => String -> String -> View view m String -- | display a text box and return the value entered if it is readable( -- Otherwise, fail the validation) getCheckBox :: (FormInput view, Functor m, MonadIO m) => String -> Bool -> View view m String submitButton :: (FormInput view, Monad m) => String -> View view m String resetButton :: (FormInput view, Monad m) => String -> View view m () -- | creates a link wiget. A link can be composed with other widget -- elements, wlink :: (Typeable a, Read a, Show a, MonadIO m, Functor m, FormInput view) => a -> view -> View view m a -- | wrap a widget of form element within a form-action element. wform :: (Monad m, FormInput view, Monoid view) => View view m b -> View view m b getCurrentName :: MonadState (MFlowState view) m => 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 numbers, please)
--   
validate :: (FormInput view, Monad m) => View view m a -> (a -> WState view m (Maybe String)) -> View view m a -- | empty widget that return Nothing. May be used as "empty boxes" inside -- larger widgets noWidget :: (FormInput view, Monad m) => View view m a -- | render the Show instance of the parameter and return it. It is useful -- for displaying information wrender :: (Monad m, Show a, FormInput view) => a -> View view m a -- | Actions are callbacks that are executed when a widget is validated. It -- is useful when the widget is inside widget containers that know -- nothing about his content. It returns a result that can be -- significative or, else, be ignored with <** and -- **>. An action may or may not initiate his own dialog with -- the user via ask waction :: (FormInput view, Monad m) => View view m a -> (a -> FlowM view m b) -> View view m b -- | A modifier get the result and the rendering of a widget and change -- them. -- -- This modifier, when logged, changes a login-password-register widget -- with a display username. -- --
--   userFormOrName= userWidget Nothing userFormLine `wmodify` f
--      where
--      f _ justu@(Just u)  =  return ([fromString u], justu) -- user validated, display and return user
--      f felem Nothing = do
--        us <-  getCurrentUser
--        if us == anonymous
--              then return (felem, Nothing)                    -- user not logged, present the form
--              else return([fromString us],  Just us)        -- already logged, display and return user
--   
wmodify :: (Monad m, FormInput v) => View v m a -> ([v] -> Maybe a -> WState v m ([v], Maybe b)) -> View v m b -- | Cached widgets operate with widgets in the Identity monad, but they -- may perform IO using the execute instance of the monad m, which is -- usually the IO monad. execute basically "sanctifies" the use of -- unsafePerformIO for a transient purpose such is caching. This is -- defined in Data.TCache.Memoization. The user can create his own -- instance for his monad. -- -- With cachedWidget it is possible to cache the rendering of a -- widget as a ByteString (maintaining type safety) , permanently or for -- a certain time. this is very useful for complex widgets that present -- information. Specially it they must access to databases. -- --
--   import Mflow.Wai.XHtm.All
--   import Some.Time.Library
--   main= run 80 waiMessageFlow [(noscript, time)]
--   time=do  ask $ cachedWidget "time" 5
--              $ wlink () bold << "the time is " ++ show (execute giveTheTime) ++ " click here"
--            time
--   
-- -- this pseudocode would update the time every 5 seconds. The execution -- of the IO computation giveTheTime must be executed inside the cached -- widget to avoid unnecesary IO executions. cachedWidget :: (Show a, MonadIO m, Typeable view, Monoid view, FormInput view, Typeable a, Functor m, Executable m) => String -> Int -> View view Identity a -> View view m a -- | Join two widgets in the same page the resulting widget, when -- asked with it, returns a either one or the other -- --
--   r <- ask widget widget1 <+> widget widget2
--   case r of (Just x, Nothing) -> ..
--   
(<+>) :: Monad m => View view m a -> View view m b -> View view m (Maybe a, Maybe b) -- | intersperse a widget in a list of widgets. the results is a 2-tuple of -- both types (|*>) :: (MonadIO m, Functor m, Monoid view) => View view m r -> [View view m r'] -> View view m (Maybe r, Maybe r') -- | Put a widget above and below other. Useful for navigation links in a -- page. (|+|) :: (Functor m, Monoid view, MonadIO m) => View view m r -> View view m r' -> View view m (Maybe r, Maybe r') -- | The first elem result (even if it is not validated) is discarded, and -- the secod is returned . This contrast with the applicative operator -- *> which fails the whole validation if the validation of the -- first elem fails. . The first element is displayed however, as in the -- case of *> (**>) :: (Functor m, Monad m) => View view m a -> View view m b -> View view m b -- | The second elem result (even if it is not validated) is discarded, and -- the first is returned . This contrast with the applicative operator -- <* which fails the whole validation if the validation of the -- second elem fails. . The first element is displayed however, as in the -- case of <* (<**) :: (Functor m, Monad m) => View view m a -> View view m b -> View view m a -- | Concat a list of widgets of the same type, to return a single result wconcat :: (Monoid view, MonadIO m, Functor m) => [View view m a] -> View view m a -- | An associative binary operation (<|>) :: Alternative f => forall a. f a -> f a -> f a -- | Sequence actions, discarding the value of the second argument. (<*) :: Applicative f => forall a b. f a -> f b -> f a -- | An infix synonym for fmap. (<$>) :: Functor f => (a -> b) -> f a -> f b -- | Sequential application. (<*>) :: Applicative f => forall a b. f (a -> b) -> f a -> f b -- |
--   (.<+>.) x y = normalize x <+> normalize y
--   
(.<+>.) :: (Monad m, ToByteString v, ToByteString v1) => View v m a -> View v1 m b -> View ByteString m (Maybe a, Maybe b) -- |
--   (.|*>.) x y = normalize x |*> map normalize y
--   
(.|*>.) :: (Functor m, MonadIO m, ToByteString v, ToByteString v1) => View v m r -> [View v1 m r'] -> View ByteString m (Maybe r, Maybe r') -- |
--   (.|+|.) x y = normalize x |+| normalize y
--   
(.|+|.) :: (Functor m, MonadIO m, ToByteString v, ToByteString v1) => View v m r -> View v1 m r' -> View ByteString m (Maybe r, Maybe r') -- |
--   (.**>.) x y = normalize x **> normalize y
--   
(.**>.) :: (Monad m, Functor m, ToByteString v, ToByteString v1) => View v m a -> View v1 m b -> View ByteString m b -- |
--   (.<**.) x y = normalize x <** normalize y
--   
(.<**.) :: (Monad m, Functor m, ToByteString v, ToByteString v1) => View v m a -> View v1 m b -> View ByteString m a -- |
--   (.<|>.) x y= normalize x <|> normalize y
--   
(.<|>.) :: (Monad m, Functor m, ToByteString v, ToByteString v1) => View v m a -> View v1 m a -> View ByteString m a -- | Enclose Widgets in some formating. view is intended to be -- instantiated to a particular format -- -- This is a widget, which is table with some links. it returns an Int -- --
--   import MFlow.Forms.XHtml
--   
--   tableLinks :: View Html Int
--   tableLinks= table ! [border 1,thestyle "width:20%;margin-left:auto;margin-right:auto"]
--                <<< caption << "choose an item"
--                ++> thead << tr << concatHtml[ th << bold << "item", th << bold << "times chosen"]
--                ++> (tbody
--                     <<< (tr <<< td <<< wlink  0 (bold <<"iphone") <++  td << ( bold << "One")
--                     <|>  tr <<< td <<< wlink  1 (bold <<"ipad")   <++  td << ( bold << "Two")
--                     <|>  tr <<< td <<< wlink  2 (bold <<"ipod")   <++  td << ( bold << "Three"))
--                     )
--   
(<<<) :: (Monad m, Monoid view) => (view -> view) -> View view m a -> View view m a -- | Append formatting code to a widget -- --
--   getString hi <++ H1 << hi there
--   
(<++) :: Monad m => View v m a -> v -> View v m a -- | Prepend formatting code to a widget -- --
--   bold "enter name" ++ getString Nothing
--   
(++>) :: (Monad m, Monoid view) => view -> View view m a -> View view m a -- | add attributes to the form element if the view has more than one -- element, it is applied to the first ( View view m a -> Attribs -> View view m a -- |
--   (.<<.) w x = w $ toByteString x
--   
(.<<.) :: ToByteString view => (ByteString -> ByteString) -> view -> ByteString -- |
--   (.<++.) x v= normalize x <++ toByteString v
--   
(.<++.) :: (Monad m, ToByteString v, ToByteString v') => View v m a -> v' -> View ByteString m a -- |
--   (.++>.) v x= toByteString v ++> normalize x
--   
(.++>.) :: (Monad m, ToByteString v, ToByteString v') => v -> View v' m a -> View ByteString m a -- | Writes a XML tag in a ByteString. It is the most basic form of -- formatting. For more sophisticated formatting , use -- MFlow.Forms.XHtml or MFlow.Forms.HSP. btag :: String -> Attribs -> ByteString -> ByteString -- |
--   bhtml ats v= btag "html" ats v
--   
bhtml :: Attribs -> ByteString -> ByteString -- |
--   bbody ats v= btag "body" ats v
--   
bbody :: Attribs -> ByteString -> ByteString -- | Flatten a binary tree of tuples of Maybe results produced by the -- <+> operator into a single tuple with the same elements in the -- same order. This is useful for easing matching. For example: -- --
--   res <- ask $ wlink1 <+> wlink2 wform <+> wlink3 <+> wlink4
--   
-- -- res has type: -- --
--   Maybe (Maybe (Maybe (Maybe (Maybe a,Maybe b),Maybe c),Maybe d),Maybe e)
--   
-- -- but flatten res has type: -- --
--   (Maybe a, Maybe b, Maybe c, Maybe d, Maybe e)
--   
flatten :: Flatten (Maybe tree) list => tree -> list -- | Useful for the creation of pages using two or more views. For example -- HSP and Html. Because both have ConvertTo instances -- to ByteString, then it is possible to mix them via normalize: -- --
--   normalize widget  <+> normalize widget'
--   
-- -- is equivalent to -- --
--   widget .<+>. widget'
--   
normalize :: (Monad m, ToByteString v) => View v m a -> View ByteString m a class ToByteString a toByteString :: ToByteString a => a -> ByteString -- | Execute the FlowM view m monad. It is used as parameter of -- hackMessageFlow waiMessageFlow or -- addMessageFlows -- --
--   main= do
--      forkIO $ run 80 $ waiMessageFlow  [("noscript",transient $ runFlow mainf)]
--      adminLoop
--   
runFlow :: (FormInput view, Monoid view, Monad m) => FlowM view m () -> Token -> m () step :: (Serialize a, Typeable view, FormInput view, Monoid view, MonadIO m, Typeable a) => FlowM view m a -> FlowM view (Workflow m) a -- | True if the flow is going back (as a result of the back button pressed -- in the web browser). Usually this chech is nos necessary unless -- conditional code make it necessary -- --
--   menu= do
--          mop <- getGoStraighTo
--          case mop of
--           Just goop -> goop
--           Nothing -> do
--                  r <- ask option1 <|> option2
--                  case r of
--                   op1 -> setGoStraighTo (Just goop1) >> goop1
--                   op2 -> setGoStraighTo (Just goop2) >> goop2
--   
-- -- This pseudocode would execute the ask of the menu once. But if the -- user press the back button he will see again the menu. To let him -- choose other option, the code has to be change to -- --
--   menu= do
--          mop <- getGoStraighTo
--          back <- goingBack
--          case (mop,back) of
--           (Just goop,False) -> goop
--           _ -> do
--                  r <- ask option1 <|> option2
--                  case r of
--                   op1 -> setGoStraighTo (Just goop1) >> goop1
--                   op2 -> setGoStraighTo (Just goop2) >> goop2
--   
goingBack :: MonadState (MFlowState view) m => m Bool -- | Use this instead of return to return from a computation with an ask -- statement -- -- This way when the user press the back button, the computation will -- execute back, to the returned code, according with the user -- navigation. breturn :: Monad m => a -> BackT m a -- | Set the header-footer that will enclose the widgets. It must be -- provided in the same formatting than them, altrough with normalization -- to byteStrings can be used any formatting -- -- This header uses XML trough Haskell Server Pages -- (http://hackage.haskell.org/package/hsp) -- --
--   setHeader $ c ->
--              <html>
--                   <head>
--                        <title>  my title </title>
--                        <meta name= "Keywords" content= "sci-fi" />)
--                   </head>
--                    <body style= "margin-left:5%;margin-right:5%">
--                         <% c %>
--                    </body>
--              </html>
--   
-- -- This header uses Text.XHtml -- --
--   setHeader $ c ->
--             thehtml
--                 << (header
--                     << (thetitle << title +++
--                         meta ! [name "Keywords",content "sci-fi"])) +++
--                    body ! [style "margin-left:5%;margin-right:5%"] c
--   
-- -- This header uses both. It uses byteString tags -- --
--   setHeader $ c ->
--            bhtml [] $
--                 btag head [] $
--                       (toByteString (thetitle << title) append
--                       toByteString name= \"Keywords\" content= \"sci-fi\" /) append
--                    bbody [("style", "margin-left:5%;margin-right:5%")] c
--   
setHeader :: Monad m => (view -> view) -> FlowM view m () -- | return the current header getHeader :: Monad m => FlowM view m (view -> view) -- | Set 1) the timeout of the flow execution since the last user -- interaction. Once passed, the flow executes from the begining. 2). In -- persistent flows it set the session state timeout for the flow, that -- is persistent. If the flow is not persistent, it has no effect. -- -- transient flows restart anew. persistent flows (that use -- step) restart at the las saved execution point, unless the -- session time has expired for the user. setTimeouts :: Monad m => Int -> Integer -> FlowM view m () -- | Set an HTTP cookie setCookie :: MonadState (MFlowState view) m => String -> String -> String -> Maybe Integer -> m () data MFlowState view getNewName :: MonadState (MFlowState view) m => m String instance [incoherent] Typeable User instance [incoherent] Typeable1 FailBack instance [incoherent] Typeable2 FormElm instance [incoherent] Typeable1 MFlowState instance [incoherent] Typeable Config instance [incoherent] Read User instance [incoherent] Show User instance [incoherent] Show a => Show (FailBack a) instance [incoherent] Read Config instance [incoherent] Show Config instance [incoherent] FormInput ByteString instance [incoherent] Flatten (Tuple6 a b c d e f) (Maybe a, Maybe b, Maybe c, Maybe d, Maybe e, Maybe f) instance [incoherent] Flatten (Tuple5 a b c d e) (Maybe a, Maybe b, Maybe c, Maybe d, Maybe e) instance [incoherent] Flatten (Tuple4 a b c d) (Maybe a, Maybe b, Maybe c, Maybe d) instance [incoherent] Flatten (Tuple3 a b c) (Maybe a, Maybe b, Maybe c) instance [incoherent] Flatten (Tuple2 a b) (Maybe a, Maybe b) instance [incoherent] ToByteString ByteString instance [incoherent] ToByteString a => ToHttpData a 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] (MonadIO m, Functor m) => MonadIO (View view m) instance [incoherent] (Functor m, Monad m) => MonadState (MFlowState view) (View view m) instance [incoherent] MonadTrans (View view) instance [incoherent] (Monad m, Functor m) => Monad (View view m) instance [incoherent] (Functor m, Monad m) => Alternative (View view m) instance [incoherent] (Functor m, Monad m) => Applicative (View view m) instance [incoherent] (Monad m, Functor m) => Functor (View view m) instance [incoherent] Functor (FormElm view) instance [incoherent] (FormInput v, Monoid v, Serialize a) => Serialize (a, MFlowState v) instance [incoherent] MonadState s m => MonadState s (BackT m) instance [incoherent] MonadTrans BackT instance [incoherent] (Monad m, Functor m) => Functor (BackT m) instance [incoherent] MonadIO m => MonadIO (BackT m) instance [incoherent] Monad m => Monad (BackT m) instance [incoherent] Serialize a => Serialize (FailBack a) instance [incoherent] Indexable Config instance [incoherent] Indexable User instance [incoherent] Serialize a => Serializable a -- | Instances of FormInput for the XHtml module of the xhtml -- package module MFlow.Forms.XHtml instance Typeable Html instance FormInput Html instance ToByteString Html instance Monad m => ADDATTRS (View Html m a) module MFlow.Forms.Admin -- | A small console interpreter with some commands: -- -- -- -- on exception, for example Control-c, it sync and exits. It must be -- used as the last statement of the main procedure. adminLoop :: IO () -- | Install the admin flow in the list of flows handled by -- HackMessageFlow this gives access to an administrator page. -- It is necessary to create an admin user with setAdminUser. -- -- The administration page is reached with the path -- adminserv addAdminWF :: IO () -- | A file server for frequently accessed files, such are static web pages -- and image decorations, icons etc that are cached (memoized) according -- with the Data.TCache policies in the program space. This avoid -- the blocking of the efficient GHC threads by frequent IO calls.So it -- enhances the performance in the context of heavy concurrence. It uses -- Memoization. The caching-uncaching follows the -- setPersist criteria. module MFlow.FileServer -- | Is the flow to be added to the list in order to stream any file from -- the filesystem for example, images -- -- This app includes the fileServe flow: -- --
--   addFileServerWF
--   run 80 $ hackMessageFlow  messageFlows
--   adminLoop
--   where
--   messageFlows=  [(noscript , transient $ runFlow showStories)
--                  ,(admin    , transient $ runFlow admin)
--                  ,(mail     , transient $ runFlow mail)]
--   
-- -- | Add the fileServer to the list of server flows addFileServerWF :: IO () -- | Creates the url of file path. To be used in ordinary links to files. -- in Text.XHtml, a image would be embeded as -- --
--   image ![src $ linkFile imagepath]
--   
-- -- in HSP: -- --
--   <img src=(linkFile imagepath)\>
--   
-- -- Given the relative path of a file, it return the content of the -- href element in a html link linkFile :: String -> String -- | Set the path of the files in the web server. The links to the files -- are relative to it setFilesPath :: String -> IO () -- | A very simple (but effective) support for AJAX. The value of a -- javaScript variable is sent to the server. The server must return a -- valid sequence of javaScript statements that are evaluated in the -- client. -- -- This example increase the value, from 0 on, in a text box trough AJAX: -- --
--   import Text.XHtml
--   import MFlow.Forms
--   import MFlow.Forms.Ajax
--   ajaxsample= do
--     ajaxheader html= thehtml << ajaxHead << html
--     setHeader ajaxheader
--     ajaxc <- ajaxCommand "document.getElementById('text1').value"
--                             (n ->  return $ "document.getElementById('text1').value='"++show(read n +1)++"'")
--     ask $ (getInt (Just 0) <! [("id","text1"),("onclick",ajaxc)])
--     breturn()
--   
module MFlow.Forms.Ajax -- | Install the server code and return the client code for an AJAX -- interaction. ajaxCommand :: (MonadIO m, MonadState (MFlowState view) m) => String -> (String -> IO String) -> m (String) -- | ajaxHead must be used instead of header when using -- ajax(see example). -- -- Although it produces code form Text.XHtml rendering (package -- xhtml), it can be converted to byteString, so that any rendering can -- be used trough normalization . see setHeader ajaxHead :: Html -> Html module MFlow.Wai.Response class ToResponse a toResponse :: ToResponse a => a -> Response data TResp TRempty :: TResp TRespR :: a -> TResp TResp :: a -> TResp ctype1 :: [(CI ByteString, ByteString)] mkParams :: [(String, String)] -> [(CI ByteString, ByteString)] mkparam :: (String, String) -> (CI ByteString, ByteString) instance Typeable TResp instance ToResponse HttpData instance ToResponse String instance ToResponse ByteString instance ToResponse Response instance ToResponse TResp instance Monoid TResp module MFlow.Wai waiMessageFlow :: Request -> ResourceT IO Response instance Typeable Flow instance Read Flow instance Show Flow instance Indexable Flow instance Serializable Flow instance Processable Request module MFlow.Wai.XHtml.All