-- Hoogle documentation, generated by Haddock -- See Hoogle, http://www.haskell.org/hoogle/ -- | stateful, RESTful web framework -- @package MFlow @version 0.4.5.13 module MFlow.Cookies type CookieT = (ByteString, ByteString, ByteString, Maybe ByteString) data Cookie UnEncryptedCookie :: CookieT -> Cookie EncryptedCookie :: CookieT -> Cookie ParanoidCookie :: CookieT -> Cookie contentHtml :: (ByteString, ByteString) cookieuser :: String cookieHeaders :: IsString t => [Cookie] -> [(t, ByteString)] getCookies :: (IsString a, Eq a) => [(a, ByteString)] -> [(ByteString, ByteString)] paranoidEncryptCookie :: CookieT -> IO Cookie paranoidDecryptCookie :: CookieT -> IO Cookie encryptCookie :: CookieT -> IO Cookie decryptCookie :: Cookie -> IO Cookie instance Eq Cookie instance Read Cookie instance Show Cookie -- | Non monadic low level primitives that implement the MFlow application -- server. See MFlow.Form for the higher level interface that you -- may use. -- -- It implements a scheduler of Processable messages that are -- served according to the source identification and the verb invoked. -- The scheduler executes the appropriate workflow (using the workflow -- package). The workflow will send additional messages to the source and -- wait for the responses. The dialog is identified by a Token, -- which is associated to the Flow. The computation state is -- optionally logged, on timeout the process is killed, when invoked -- again, the execution state is recovered as if no interruption took -- place. -- -- There is no assumption 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 -- composable widgets and an applicative combinator with a higher-level -- communication interface. -- -- MFlow.Forms.XHtml is an instantiation for the Text.XHtml format -- -- MFlow.Forms.HSP is an instantiation for the Haskell Server -- Pages format -- -- MFlow.Forms.Blaze.Html is an instantiation for blaze-html. Use -- this instead of XHtml and HSP, which are for backwards compat reasons. -- -- There are some *.All packages that contain a mix of these -- instantiations. For example, MFlow.Wai.Blaze.Html.All includes -- most of all necessary for using MFlow with Wai -- http://hackage.haskell.org/package/wai and Blaze-html -- http://hackage.haskell.org/package/blaze-html -- -- 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 which -- provides an higher level interface. -- -- Fragment based streaming: sendFragment are provided only at -- this level. -- -- stateless and transient server processes are also -- possible. stateless are request-response processes. While -- transient processes do not persist after timeout, they restart -- anew after a timeout or a crash. module MFlow type Flow = Token -> Workflow IO () type Params = [(String, String)] data HttpData HttpData :: [(ByteString, ByteString)] -> [Cookie] -> ByteString -> HttpData Error :: ByteString -> HttpData class Processable a where pwfname s = head $ pwfPath s pwfname :: Processable a => a -> String pwfPath :: Processable a => a -> [String] puser :: Processable a => a -> String pind :: Processable a => a -> String getParams :: Processable a => a -> Params -- | A Token identifies a Flow that handle messages. The -- scheduler composes a Token with every Processable -- message that arrives and sends the message to the appropriate -- Flow. data Token Token :: String -> String -> String -> [String] -> Params -> MVar Bool -> MVar Req -> MVar Resp -> Token twfname :: Token -> String tuser :: Token -> String tind :: Token -> String tpath :: Token -> [String] tenv :: Token -> Params tblock :: Token -> MVar Bool tsendq :: Token -> MVar Req trecq :: 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 (Maybe Req) flushResponse :: Token -> IO (Maybe Resp) receive :: Typeable a => Token -> IO a receiveReq :: Token -> IO Req receiveReqTimeout :: Int -> Integer -> Token -> IO Req -- | Send a complete response. send :: Token -> HttpData -> IO() send :: Token -> HttpData -> IO () sendFlush :: Token -> HttpData -> IO () -- | Send a response fragment, useful for streaming. The last packet must -- be sent trough send. sendFragment :: Token -> HttpData -> IO () -- | Deprecated: use "send" to end a fragmented response instead sendEndFragment :: Token -> HttpData -> IO () sendToMF :: (Processable a, Typeable * a) => Token -> a -> IO () -- | Set the flow to be executed when the URL has no path. The home page. -- -- By default it is "noscript". Although it is changed by -- runNavigation to it's own flow name. setNoScript :: String -> IO () -- | Add a list of flows to be scheduled. Each entry in the list is a pair -- (path, flow) addMessageFlows :: [(String, Token -> Workflow IO ())] -> IO () -- | Return the list of the scheduler. getMessageFlows :: IO (WorkflowList IO Token ()) delMessageFlow :: String -> IO () -- | Executes a monadic computation that are send and receive messages, but -- does not store it's state in permanent storage. The process once -- stopped, will restart anew transient :: (Token -> IO ()) -> Flow -- | Executes a simple request-response computation that receive the params -- and return a response. -- -- It is used with addMessageFlows -- -- There is a higher level version wstateless in -- MFLow.Forms stateless :: (Params -> IO HttpData) -> Flow -- | The anonymous user. anonymous :: [Char] noScript :: String -- | The handler of the error log. hlog :: Handle -- | Set the 404 "not found" response. -- -- The parameter is as follows: (Bool Either if the user is Administrator -- or not -> String The error string -> HttpData) The response. See -- defNotFoundResponse code for an example setNotFoundResponse :: (Bool -> String -> ByteString) -> IO () getNotFoundResponse :: IO (Bool -> [Char] -> 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)] addAttrs :: ByteString -> Attribs -> ByteString -- | Register a user with the auth method. userRegister :: MonadIO m => UserStr -> PasswdStr -> m (Maybe String) -- | Set the Administrator user and password. It must be defined in Main, -- before any configuration parameter is read and before the execution of -- any flow. setAdminUser :: MonadIO m => UserStr -> PasswdStr -> m () getAdminName :: String data Auth Auth :: (UserStr -> PasswdStr -> (IO (Maybe String))) -> (UserStr -> PasswdStr -> (IO (Maybe String))) -> Auth uregister :: Auth -> UserStr -> PasswdStr -> (IO (Maybe String)) uvalidate :: Auth -> UserStr -> PasswdStr -> (IO (Maybe String)) getAuthMethod :: IO Auth -- | Sets an authentication method, that includes the registration and -- validation calls. Both return Nothing if successful. Otherwise they -- return a text message explaining the failure. setAuthMethod :: Auth -> IO () config :: Map String String -- | Read a config variable from the config file "mflow.config". If it is -- not set, use the second parameter and add it to the configuration list -- so next time the administrator can change it in the configuration -- file. getConfig :: String -> String -> String -- | Set the path of the files in the web server. The links to the files -- are relative to it. The files 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. This -- enhances the performance in the context of heavy concurrency. It uses -- Memoization. The caching and uncaching follows the -- setPersist criteria setFilesPath :: MonadIO m => String -> m () addTokenToList :: Token -> IO () deleteTokenInList :: Token -> IO () -- | The scheduler creates a Token with every Processable -- message that arrives and sends the message to the appropriate flow, -- then waits for the response and returns it. -- -- This is the core of the application server. MFLow.Wai and -- MFlow.Hack use it msgScheduler :: (Typeable a, Processable a) => a -> IO (HttpData, ThreadId) serveFile :: [Char] -> IO HttpData mimeTable :: [([Char], ByteString)] newFlow :: IO ByteString type UserStr = String type PasswdStr = String data User User :: String -> String -> User userName :: User -> String upassword :: User -> String eUser :: User instance Typeable HttpData instance Typeable Req instance Typeable Token instance Typeable User instance Typeable Config instance Typeable Config0 instance Typeable NFlow instance Show HttpData instance Read User instance Show User instance Read Config instance Show Config instance Read Config0 instance Show Config0 instance Read NFlow instance Show NFlow instance Serializable NFlow instance Indexable NFlow instance Serializable Config instance Indexable Config instance Serializable User instance Indexable User instance Processable Req instance Processable Token instance Monoid HttpData instance Serializable Token instance Read Token instance Show Token instance Indexable Token module MFlow.Forms.Internals (!>) :: c -> String -> c data FailBack a BackPoint :: a -> FailBack a NoBack :: a -> FailBack a GoBack :: FailBack a iCanFailBack :: [Char] repeatPlease :: [Char] noFailBack :: [Char] newtype Sup m a Sup :: m (FailBack a) -> Sup m a runSup :: Sup m a -> m (FailBack a) class MonadState s m => Supervise s m where supBack = const $ return () supervise = id supBack :: Supervise s m => s -> m () supervise :: Supervise s m => m (FailBack a) -> m (FailBack a) fromFailBack :: FailBack t -> t toFailBack :: a -> FailBack a -- | the FlowM monad executes the page navigation. It perform Backtracking -- when necessary to synchronize when the user press the back button or -- when the user enter an arbitrary URL. The instruction pointer is moved -- to the right position within the procedure to handle the request. -- -- However this is transparent to the programmer, who codify in the style -- of a console application. newtype FlowM v m a FlowM :: FlowMM v m a -> FlowM v m a runFlowM :: FlowM v m a -> FlowMM v m a -- | Use this instead of return to return from a computation with ask -- statements -- -- 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 -> FlowM v m a liftSup :: Monad m => m a -> Sup m a type WState view m = StateT (MFlowState view) m type FlowMM view m = Sup (WState view m) data FormElm view a FormElm :: view -> (Maybe a) -> FormElm view a -- | View v m a is a widget (formlet) with formatting v -- running the monad m (usually IO) and which return a -- value of type a -- -- It has Applicative, Alternative and Monad -- instances. -- -- Things to know about these instances: -- -- If the View expression does not validate, ask will present the page -- again. -- -- Alternative instance: Both alternatives are executed. The rest -- is as usual -- -- Monad Instance: -- -- The rendering of each statement is added to the previous. If you want -- to avoid this, use wcallback -- -- The execution is stopped when the statement has a formlet-widget that -- does not validate and return an invalid response (So it will present -- the page again if no other widget in the expression validates). -- -- The monadic code is executed from the beginning each time the page is -- presented or refreshed -- -- use pageFlow if your page has more than one monadic -- computation with dynamic behavior -- -- use pageFlow to identify each subflow branch of a conditional -- -- For example: -- --
-- pageFlow "myid" $ do -- r <- formlet1 -- liftIO $ ioaction1 r -- s <- formlet2 -- liftIO $ ioaction2 s -- case s of -- True -> pageFlow "idtrue" $ do .... -- False -> paeFlow "idfalse" $ do ... -- ... ---- -- Here if formlet2 do not validate, ioaction2 is not -- executed. But if formLet1 validates and the page is refreshed -- two times (because formlet2 has failed, see above),then -- ioaction1 is executed two times. use cachedByKey if -- you want to avoid repeated IO executions. newtype View v m a View :: WState v m (FormElm v a) -> View v m a runView :: View v m a -> WState v m (FormElm v a) -- | It is a callback in the view monad. The callback rendering substitutes -- the widget rendering when the latter is validated, without affecting -- the rendering of other widgets. This allow the simultaneous execution -- of different behaviors in different widgets in the same page. The -- inspiration is the callback primitive in the Seaside Web Framework -- that allows similar functionality (See http://www.seaside.st) -- -- This is the visible difference with waction callbacks, which -- execute a a flow in the FlowM monad that takes complete control of the -- navigation, while wactions are executed within the same page. wcallback :: Monad m => View view m a -> (a -> View view m b) -> View view m b -- | Execute the widget in a monad and return the result in another. changeMonad :: (Monad m, Executable m') => View v m' a -> View v m a -- | Join two widgets in the same page the resulting widget, when -- asked with it, return a 2 tuple of their validation results -- if both return Noting, the widget return Nothing (invalid). -- -- it has a low infix priority: infixr 2 -- --
-- r <- ask widget1 <+> widget2 -- case r of (Just x, Nothing) -> .. --(<+>) :: (Monad m, FormInput view) => View view m a -> View view m b -> View view m (Maybe a, Maybe b) -- | Join two widgets in the same page the resulting widget, when -- asked with it, return a 2 tuple of their validation results -- if both return Noting, the widget return Nothing (invalid). -- -- it has a low infix priority: infixr 2 -- --
-- r <- ask widget1 <+> widget2 -- case r of (Just x, Nothing) -> .. --mix :: (Monad m, FormInput view) => View view m a -> View view m b -> View view m (Maybe a, Maybe b) -- | The first elem result (even if it is not validated) is discarded, and -- the second 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 happens in the case of -- *> . -- -- Here w's are widgets and r's are returned values -- -- (w1 <* w2) will return Just r1 only if w1 and w2 -- are validated -- -- (w1 <** w2) will return Just r1 even if w2 is not -- validated -- -- it has a low infix priority: infixr 1 (**>) :: (Functor m, Monad m, FormInput view) => View view m a -> View view m b -> View view m b valid :: Monad m => View view m a1 -> View view m a -- | 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 second element is displayed however, as in the -- case of <*. see the <** examples -- -- it has a low infix priority: infixr 1 (<**) :: (Functor m, Monad m, FormInput view) => View view m a -> View view m b -> View view m a -- | True if the flow is going back (as a result of the back button pressed -- in the web browser). Usually this check 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 below would execute the ask of the menu once. But the -- user will never have the possibility to see the menu again. 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 ---- -- However this is very specialized. Normally the back button detection -- is not necessary. In a persistent flow (with step) even this default -- entry option would be completely automatic, since the process would -- restart at the last page visited. goingBack :: MonadState (MFlowState view) m => m Bool -- | Will prevent the Suprack beyond the point where -- preventGoingBack is located. If the user press the back button -- beyond that point, the flow parameter is executed, usually it is an -- ask statement with a message. If the flow is not going back, it does -- nothing. It is a cut in Supracking -- -- It is useful when an undoable transaction has been commited. For -- example, after a payment. -- -- This example show a message when the user go back and press again to -- pay -- --
-- ask $ wlink () << b << "press here to pay 100000 $ " -- payIt -- preventGoingBack . ask $ b << "You paid 10000 $ one time" -- ++> wlink () << b << " Please press here to complete the proccess" -- ask $ wlink () << b << "OK, press here to go to the menu or press the back button to verify that you can not pay again" -- where -- payIt= liftIO $ print "paying" --preventGoingBack :: (Functor m, MonadIO m, FormInput v) => FlowM v m () -> FlowM v m () -- | executes the first computation when going forward and the second -- computation when backtracking. Depending on how the second computation -- finishes, the flow will resume forward or backward. onBacktrack :: Monad m => m a -> FlowM v m a -> FlowM v m a -- | less powerful version of onBacktrack: The second computation -- simply undo the effect of the first one, and the flow continues -- backward ever. It can be used as a rollback mechanism in the context -- of long running transactions. compensate :: Monad m => m a -> m a -> FlowM v m a type Lang = String data NeedForm HasForm :: NeedForm HasElems :: NeedForm NoElems :: NeedForm data MFlowState view MFlowState :: Int -> Bool -> Bool -> Bool -> Bool -> Lang -> Params -> NeedForm -> Bool -> Token -> Int -> Integer -> [Cookie] -> [(ByteString, ByteString)] -> (view -> view) -> Bool -> [Requirement] -> [WebRequirement] -> Map TypeRep Void -> Maybe (Map String Void) -> Int -> Bool -> [String] -> [String] -> String -> Bool -> Bool -> Bool -> [String] -> Bool -> MFlowState view mfSequence :: MFlowState view -> Int mfCached :: MFlowState view -> Bool newAsk :: MFlowState view -> Bool inSync :: MFlowState view -> Bool mfSomeNotValidates :: MFlowState view -> Bool mfLang :: MFlowState view -> Lang mfEnv :: MFlowState view -> Params needForm :: MFlowState view -> NeedForm mfFileUpload :: MFlowState view -> Bool mfToken :: MFlowState view -> Token mfkillTime :: MFlowState view -> Int mfSessionTime :: MFlowState view -> Integer mfCookies :: MFlowState view -> [Cookie] mfHttpHeaders :: MFlowState view -> [(ByteString, ByteString)] mfHeader :: MFlowState view -> view -> view mfDebug :: MFlowState view -> Bool mfRequirements :: MFlowState view -> [Requirement] mfInstalledScripts :: MFlowState view -> [WebRequirement] mfData :: MFlowState view -> Map TypeRep Void mfAjax :: MFlowState view -> Maybe (Map String Void) mfSeqCache :: MFlowState view -> Int notSyncInAction :: MFlowState view -> Bool mfPath :: MFlowState view -> [String] mfPagePath :: MFlowState view -> [String] mfPrefix :: MFlowState view -> String mfPageFlow :: MFlowState view -> Bool linkMatched :: MFlowState view -> Bool mfAutorefresh :: MFlowState view -> Bool mfTrace :: MFlowState view -> [String] mfClear :: MFlowState view -> Bool type Void = Char mFlowState0 :: FormInput view => MFlowState view -- | Set user-defined data in the context of the session. -- -- The data is indexed by type in a map. So the user can insert-retrieve -- different kinds of data in the session context. -- -- This example define addHistory and getHistory to -- maintain a Html log in the session of a Flow: -- --
-- newtype History = History ( Html) deriving Typeable -- setHistory html= setSessionData $ History html -- getHistory= getSessionData `onNothing` return (History mempty) >>= \(History h) -> return h -- addHistory html= do -- html' <- getHistory -- setHistory $ html' `mappend` html --setSessionData :: (Typeable a, MonadState (MFlowState view) m) => a -> m () delSessionData :: (MonadState (MFlowState view) m, Typeable * a) => a -> m () -- | Get the session data of the desired type if there is any. getSessionData :: (Typeable a, MonadState (MFlowState view) m) => m (Maybe a) -- | getSessionData specialized for the View monad. if Nothing, the monadic -- computation does not continue. getSData :: (Monad m, Typeable a, Monoid v) => View v m a -- | Return the session identifier getSessionId :: MonadState (MFlowState v) m => m String -- | Return the user language. Now it is fixed to "en" getLang :: MonadState (MFlowState view) m => m String getToken :: MonadState (MFlowState view) m => m Token getEnv :: MonadState (MFlowState view) m => m Params stdHeader :: t -> t -- | Set the header-footer that will enclose the widgets. It must be -- provided in the same formatting than them, although with normalization -- to ByteStrings any formatting can be used -- -- 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 :: MonadState (MFlowState view) m => (view -> view) -> m ()
-- | Return the current header
getHeader :: Monad m => FlowM view m (view -> view)
-- | Add another header embedded in the previous one
addHeader :: Monad m => (view -> view) -> FlowM view m ()
-- | Set an HTTP cookie
setCookie :: MonadState (MFlowState view) m => String -> String -> String -> Maybe Integer -> m ()
setParanoidCookie :: MonadState (MFlowState view) m => String -> String -> String -> Maybe Integer -> m ()
setEncryptedCookie :: MonadState (MFlowState view) m => String -> String -> String -> Maybe Integer -> m ()
setEncryptedCookie' :: (MonadState (MFlowState view) m, Show a, Functor f) => String -> String -> String -> f a -> ((ByteString, ByteString, ByteString, f ByteString) -> IO Cookie) -> m ()
-- | Set an HTTP Response header
setHttpHeader :: MonadState (MFlowState view) m => ByteString -> ByteString -> m ()
-- | Set 1) the timeout of the flow execution since the last user
-- interaction. Once passed, the flow executes from the beginning.
--
-- 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.
--
-- As the other state primitives, it can be run in the Flow and in the
-- View monad
--
-- transient flows restart anew. persistent flows (that use
-- step) restart at the last saved execution point, unless the
-- session time has expired for the user.
setTimeouts :: MonadState (MFlowState v) m => Int -> Integer -> m ()
getWFName :: MonadState (MFlowState view) m => m String
getCurrentUser :: MonadState (MFlowState view) m => m String
type Name = String
type Type = String
type Value = String
type Checked = Bool
type OnClick = Maybe String
normalize :: (Monad m, FormInput v) => View v m a -> View ByteString m a
-- | Minimal interface for defining the basic form and link elements. The
-- core of MFlow is agnostic about the rendering package used. Every
-- formatting (either HTML or not) used with MFlow must have an instance
-- of this class. See "MFlow.Forms.Blaze.Html for the instance for
-- blaze-html" MFlow.Forms.XHtml for the instance for
-- Text.XHtml and MFlow.Forms.HSP for the instance for Haskell
-- Server Pages.
class (Monoid view, Typeable view) => FormInput view where flink1 verb = flink verb (fromStr verb) foption1 val msel = foption val (fromStr val) msel
toByteString :: FormInput view => view -> ByteString
toHttpData :: FormInput view => view -> HttpData
fromStr :: FormInput view => String -> view
fromStrNoEncode :: FormInput view => String -> view
ftag :: FormInput view => String -> view -> view
inred :: FormInput view => view -> 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 -> Text -> view
fselect :: FormInput view => String -> view -> view
foption :: FormInput view => String -> view -> Bool -> view
foption1 :: FormInput view => String -> Bool -> view
formAction :: FormInput view => String -> String -> view -> view
attrs :: FormInput view => view -> Attribs -> view
-- | 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 programmer 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.Blaze.Html.All -- import Some.Time.Library -- addMessageFlows [(noscript, time)] -- main= run 80 waiMessageFlow -- time=do ask $ cachedWidget "time" 5 -- $ wlink () b << "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. -- -- NOTE: the rendering of cached widgets are shared by all users cachedWidget :: (MonadIO m, Typeable view, FormInput view, Typeable a, Executable m) => String -> Int -> View view Identity a -> View view m a -- | A shorter name for cachedWidget wcached :: (MonadIO m, Typeable view, FormInput view, Typeable a, Executable m) => String -> Int -> View view Identity a -> View view m a -- | Unlike cachedWidget, which cache the rendering but not the user -- response, wfreeze cache also the user response. This is -- useful for pseudo-widgets which just show information while the -- controls are in other non freezed widgets. A freezed widget ever -- return the first user response It is faster than cachedWidget. -- It is not restricted to the Identity monad. -- -- NOTE: the content of freezed widgets are shared by all users wfreeze :: (MonadIO m, Typeable view, FormInput view, Typeable a, Executable m) => String -> Int -> View view m a -> View view m a -- | Execute the Flow, in the FlowM view m monad. It is used as -- parameter of hackMessageFlow waiMessageFlow or -- addMessageFlows -- -- The flow is executed in a loop. When the flow is finished, it is -- started again -- --
-- main= do
-- addMessageFlows [("noscript",transient $ runFlow mainf)]
-- forkIO . run 80 $ waiMessageFlow
-- adminLoop
--
runFlow :: (FormInput view, MonadIO m) => FlowM view (Workflow m) () -> Token -> Workflow m ()
inRecovery :: Int
runFlowOnce :: (FormInput view, MonadIO m) => FlowM view (Workflow m) () -> Token -> Workflow m ()
runFlowOnce1 :: (FormInput v, Monad m) => FlowM v (WF Stat m) a -> Token -> WF Stat m (FailBack Token, MFlowState v)
startState :: FormInput view => Token -> MFlowState view
runFlowOnce2 :: Monad m => MFlowState v -> FlowM v (WF Stat m) a -> WF Stat m (FailBack Token, MFlowState v)
runFlowOnceReturn :: FormInput v => MFlowState v -> FlowM v m a -> Token -> m (FailBack a, MFlowState v)
-- | Run a persistent flow inside the current flow. It is identified by the
-- procedure and the string identifier. Unlike the normal flows, that run
-- within infinite loops, runFlowIn executes once. In subsequent
-- executions, the flow will get the intermediate responses from the log
-- and will return the result without asking again. This is useful for
-- asking once, storing in the log and subsequently retrieving user
-- defined configurations by means of persistent flows with web
-- formularies.
runFlowIn :: (MonadIO m, FormInput view) => String -> FlowM view (Workflow IO) b -> FlowM view m b
-- | To unlift a FlowM computation. useful for executing the configuration
-- generated by runFLowIn outside of the web flow (FlowM) monad
runFlowConf :: (FormInput view, MonadIO m) => FlowM view m a -> m a
-- | Run a transient Flow from the IO monad. runNav :: String -> FlowM
-- Html IO () -> IO () runNav ident f= exec1 ident $ runFlowOnce
-- (transientNav f) undefined
--
-- Clears the environment
clearEnv :: MonadState (MFlowState view) m => m ()
-- | clear the request paramenters and the rest path.
clearEnv' :: MonadState (MFlowState view) m => m ()
-- | Stores the result of the flow in a persistent log. When restarted, it
-- get the result from the log and it does not execute it again. When no
-- results are in the log, the computation is executed. It is equivalent
-- to step but in the FlowM monad.
step :: (Serialize a, Typeable view, FormInput view, MonadIO m, Typeable a) => FlowM view m a -> FlowM view (Workflow m) a
-- | To execute transient flows as if they were persistent, it can be used
-- instead of step, but it does log nothing. Thus, it is faster and
-- convenient when no session state must be stored beyond the lifespan of
-- the server process.
--
-- -- transient $ runFlow f === runFlow $ transientNav f --transientNav :: (Serialize a, Typeable view, FormInput view, Typeable a) => FlowM view IO a -> FlowM view (Workflow IO) a data ParamResult v a NoParam :: ParamResult v a NotValidated :: String -> v -> ParamResult v a Validated :: a -> ParamResult v a valToMaybe :: ParamResult t a -> Maybe a isValidated :: ParamResult t t1 -> Bool fromValidated :: ParamResult t t1 -> t1 getParam1 :: (Monad m, MonadState (MFlowState v) m, Typeable a, Read a, FormInput v) => String -> Params -> m (ParamResult v a) getRestParam :: (Read a, Typeable a, Monad m, Functor m, MonadState (MFlowState v) m, FormInput v) => m (Maybe a) -- | return the value of a post or get param in the form -- ?param=value¶m2=value2... getKeyValueParam :: (FormInput view, MonadState (MFlowState view) m, Typeable * a, Read a) => String -> m (Maybe a) readParam :: (Monad m, MonadState (MFlowState v) m, Typeable a, Read a, FormInput v) => String -> m (ParamResult v a) -- | Requirements are JavaScripts, Stylesheets or server processes (or any -- instance of the Requirement class) that are included in the Web -- page or in the server when a widget specifies this. requires -- is the procedure to be called with the list of requirements. Various -- widgets in the page can require the same element, MFlow will install -- it once. requires :: (Requirements a, MonadState (MFlowState view) m, Typeable * a, Show a) => [a] -> m () unfold :: WebRequirement -> [WebRequirement] data Requirement Requirement :: a -> Requirement class Requirements a installRequirements :: (Requirements a, MonadState (MFlowState view) m, MonadIO m, FormInput view) => [a] -> m view installAllRequirements :: (MonadIO m, FormInput view) => WState view m view loadjsfile :: [Char] -> [Char] loadScript :: [Char] loadCallback :: a -> [Char] -> [Char] loadcssfile :: [Char] -> [Char] loadcss :: [Char] -> [Char] data WebRequirement -- | Script URL and the list of scripts to be executed when loaded JScriptFile :: String -> [String] -> WebRequirement -- | a CSS file URL CSSFile :: String -> WebRequirement -- | a String with a CSS description CSS :: String -> WebRequirement -- | a string with a valid JavaScript JScript :: String -> WebRequirement -- | a server procedure ServerProc :: (String, Flow) -> WebRequirement installWebRequirements :: (MonadState (MFlowState view) m, MonadIO m, FormInput view) => [WebRequirement] -> m view strRequirement :: (MonadState (MFlowState view) m, MonadIO m) => WebRequirement -> m [Char] strRequirement' :: (MonadState (MFlowState view) m, MonadIO m) => WebRequirement -> m [Char] ajaxScript :: [Char] formPrefix :: (FormInput b, MonadState (MFlowState view1) m) => MFlowState view -> b -> Bool -> m b -- | Insert a form tag if the widget has form input fields. If not, it does -- nothing insertForm :: (FormInput view, Monad m) => View view m a -> View view m a controlForms :: (FormInput v, MonadState (MFlowState v) m) => MFlowState v -> MFlowState v -> v -> v -> m (v, Bool) currentPath :: MFlowState view -> [Char] -- | Generate a new string. Useful for creating tag identifiers and other -- attributes. -- -- If the page is refreshed, the identifiers generated are the same. genNewId :: MonadState (MFlowState view) m => m String -- | Get the next identifier that will be created by genNewId getNextId :: MonadState (MFlowState view) m => m String instance [overlap ok] Typeable FailBack instance [overlap ok] Typeable FormElm instance [overlap ok] Typeable WebRequirement instance [overlap ok] Typeable MFlowState instance [overlap ok] Typeable Requirement instance [overlap ok] Show a => Show (FailBack a) instance [overlap ok] Show NeedForm instance [overlap ok] (Read v, Read a) => Read (ParamResult v a) instance [overlap ok] (Show v, Show a) => Show (ParamResult v a) instance [overlap ok] Eq WebRequirement instance [overlap ok] Ord WebRequirement instance [overlap ok] Show WebRequirement instance [overlap ok] (Monad m, Functor m) => Applicative (FlowM v m) instance [overlap ok] (Monad m, Functor m) => Alternative (FlowM v m) instance [overlap ok] Monad m => Monad (FlowM v m) instance [overlap ok] MonadIO m => MonadIO (FlowM v m) instance [overlap ok] (Monad m, Functor m) => Functor (FlowM v m) instance [overlap ok] Monad m => MonadState (MFlowState v) (FlowM v m) instance [overlap ok] Requirements WebRequirement instance [overlap ok] Show (String, Flow) instance [overlap ok] Ord (String, Flow) instance [overlap ok] Eq (String, Flow) instance [overlap ok] Show Requirement instance [overlap ok] (FormInput v, Serialize a) => Serialize (a, MFlowState v) instance [overlap ok] (FormInput view, MonadIO m) => MonadIO (View view m) instance [overlap ok] (FormInput view, Monad m) => MonadState (MFlowState view) (View view m) instance [overlap ok] MonadTrans (FlowM view) instance [overlap ok] Monoid view => MonadTrans (View view) instance [overlap ok] (FormInput v, Monad m, Functor m, Monoid a) => Monoid (View v m a) instance [overlap ok] (FormInput view, Monad m) => Monad (View view m) instance [overlap ok] (FormInput view, Functor m, Monad m) => Alternative (View view m) instance [overlap ok] (Monoid view, Functor m, Monad m) => Applicative (View view m) instance [overlap ok] (Monad m, Functor m) => Functor (View view m) instance [overlap ok] Functor (FormElm view) instance [overlap ok] FormInput v => MonadLoc (View v IO) instance [overlap ok] MonadLoc (FlowM v IO) instance [overlap ok] Monad m => Supervise (MFlowState v) (WState v m) instance [overlap ok] (Monoid view, Serialize a) => Serialize (FormElm view a) instance [overlap ok] (Supervise s m, MonadState s m) => MonadState s (Sup m) instance [overlap ok] MonadTrans Sup instance [overlap ok] (Monad m, Functor m) => Functor (Sup m) instance [overlap ok] (Supervise s m, MonadIO m) => MonadIO (Sup m) instance [overlap ok] (Monad m, Applicative m) => Alternative (Sup m) instance [overlap ok] (Monad m, Applicative m) => Applicative (Sup m) instance [overlap ok] Supervise s m => Monad (Sup m) instance [overlap ok] Serialize a => Serialize (FailBack a) instance [overlap ok] Alternative FailBack instance [overlap ok] Applicative FailBack instance [overlap ok] Functor FailBack -- | Composable cache and HTTP header directives. Intended to permit each -- widget to express his caching needs to the whole page The page will -- compile them and choose the most strict ones Autorefreshed, push and -- witerate'd widgets do not inherit the page rules. they must specify -- their own. -- -- The composition rules are explained in the corresponding combinators. -- This is a work in progress module MFlow.Forms.Cache -- | To delete all previous directives resetCachePolicy :: (MonadState (MFlowState v) m, Monad m) => m () -- | Return the composition of the current directives. Used by the page -- internally setCachePolicy :: (MonadState (MFlowState v) m, Monad m) => m () -- | Add no-cache to the Cache-Control header directive. -- It deletes all expires and put max-age=0 -- -- It means that the widget need not to be cached noCache :: (MonadState (MFlowState v) m, MonadIO m) => m () -- | Add no-cache: string to the Cache-Control header -- directive -- -- It deletes the header string (sensible cookies for example) from the -- data stored in the cache noCache' :: (MonadState (MFlowState v) m, MonadIO m) => ByteString -> m () -- | Add no-store to the Cache-Control header directive. -- It deletes expires and put max-age: 0 -- -- Stronger kind of noCache. Not even store temporally noStore :: (MonadState (MFlowState v) m, MonadIO m) => m () -- | Add expires: string to the Cache-Control -- header directive. it deletes max-age Currently it takes the -- last one if many -- -- The page will be cached until this date expires :: (MonadState (MFlowState v) m, MonadIO m) => ByteString -> m () -- | Add max-age: seconds to the Cache-Control -- header directive. if there are more than one, it chooses the lower one -- -- The page will be stored in the cache for that amount of seconds maxAge :: (MonadState (MFlowState v) m, MonadIO m) => Int -> m () -- | Add private to the Cache-Control header directive. -- it delete public if any -- -- It means that the page that holds the widget must not be shared by -- other users. private :: (MonadState (MFlowState v) m, MonadIO m) => m () -- | Add public to the Cache-Control header directive. -- -- Means that the cache can share the page content with other users. public :: (MonadState (MFlowState v) m, MonadIO m) => m () -- | Add sMaxAge seconds to the Cache-Control -- header directive. if many, chooses the minimum -- -- Specify the time to hold the page for intermediate caches: for example -- proxies and CDNs. sMaxAge :: (MonadState (MFlowState v) m, MonadIO m) => Int -> m () -- | Add noTransform to the Cache-Control header -- directive. -- -- Tell CDNs that the content should not be transformed to save space and -- so on noTransform :: (MonadState (MFlowState v) m, MonadIO m) => m () -- | Add proxyRevalidate to the Cache-Control header -- directive. -- -- The same than mustRevalidate, for shared caches (proxies etc) proxyRevalidate :: (MonadState (MFlowState v) m, MonadIO m) => m () -- | Add etag string to the header directives. -- -- It is a resource identifier for the page that substitutes the URL -- identifier etag :: (MonadState (MFlowState v) m, MonadIO m) => ByteString -> m () -- | Add vary string to the header directives. -- -- Usually the page add this identifier to the URL string, that is the -- default identifier So the same page with different etags will be -- cached and server separately vary :: (MonadState (MFlowState v) m, MonadIO m) => ByteString -> m () instance Typeable CacheElem instance Show CacheElem instance Eq CacheElem instance Ord CacheElem -- | MFlow run stateful server processes. This version is the first -- stateful web framework that is as RESTful as a web framework can be. -- -- The routes are expressed as normal, monadic Haskell code in the FlowM -- monad. Local links point to alternative routes within this monadic -- computation just like a textual menu in a console application. Any GET -- page is directly reachable by means of a RESTful URL. -- -- At any moment the flow can respond to the back button or to any -- RESTful path that the user may paste in the navigation bar. If the -- procedure is waiting for another different page, the FlowM monad -- backtrack until the path partially match . From this position the -- execution goes forward until the rest of the path match. This way the -- statelessness is optional. However, it is possible to store a session -- state, which may backtrack or not when the navigation goes back and -- forth. It is up to the programmer. -- -- All the flow of requests and responses are coded by the programmer in -- a single procedure. Although single request-response flows are -- possible. Therefore, the code is more understandable. It is not -- continuation based. It uses a log for thread state persistence and -- backtracking for handling the back button. Back button state -- synchronization is supported out-of-the-box -- -- The MFlow architecture is scalable, since the state is serializable -- and small -- -- The processes are stopped and restarted by the application server on -- demand, including the execution state (if the Workflow monad is used). -- 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 applicative 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 blaze-html, HSP, Text.XHtml and ByteString , Hack and WAI but it -- can be extended to non Web based architectures. -- -- Bindings for hack, and HSP >= 0.8, are not compiled by Hackage, and -- do not appear, but are included in the package files. To use them, add -- then to the exported modules and execute cabal install -- -- It is designed for applications that can be run with no deployment -- with runghc in order to speed up the development process. see -- http://haskell-web.blogspot.com.es/2013/05/a-web-application-in-tweet.html -- -- 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 processes 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 procedure in the -- FlowM monad. Although 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 -- functions 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, -- callbacks 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. -- --
-- ask $ wform userloginform `validate` valdateProc `waction` loginProc `wmodify` hideIfLogged ---- --
-- pageFlow "myid" $ do -- r <- formlet1 -- liftIO $ ioaction1 r -- s <- formlet2 -- liftIO $ ioaction2 s -- case s of -- True -> pageFlow "idtrue" $ do .... -- False -> paeFlow "idfalse" $ do ... -- ... ---- -- Here if formlet2 do not validate, ioaction2 is not -- executed. But if formLet1 validates and the page is refreshed -- two times (because formlet2 has failed, see above),then -- ioaction1 is executed two times. use cachedByKey if -- you want to avoid repeated IO executions. newtype View v m a View :: WState v m (FormElm v a) -> View v m a runView :: View v m a -> WState v m (FormElm v a) data FormElm view a FormElm :: view -> (Maybe a) -> FormElm view a -- | Minimal interface for defining the basic form and link elements. The -- core of MFlow is agnostic about the rendering package used. Every -- formatting (either HTML or not) used with MFlow must have an instance -- of this class. See "MFlow.Forms.Blaze.Html for the instance for -- blaze-html" MFlow.Forms.XHtml for the instance for -- Text.XHtml and MFlow.Forms.HSP for the instance for Haskell -- Server Pages. class (Monoid view, Typeable view) => FormInput view where flink1 verb = flink verb (fromStr verb) foption1 val msel = foption val (fromStr val) msel toByteString :: FormInput view => view -> ByteString toHttpData :: FormInput view => view -> HttpData fromStr :: FormInput view => String -> view fromStrNoEncode :: FormInput view => String -> view ftag :: FormInput view => String -> view -> view inred :: FormInput view => view -> 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 -> Text -> view fselect :: FormInput view => String -> view -> view foption :: FormInput view => String -> view -> Bool -> view foption1 :: FormInput view => String -> Bool -> view formAction :: FormInput view => String -> String -> view -> view attrs :: FormInput view => view -> Attribs -> view data Auth Auth :: (UserStr -> PasswdStr -> (IO (Maybe String))) -> (UserStr -> PasswdStr -> (IO (Maybe String))) -> Auth uregister :: Auth -> UserStr -> PasswdStr -> (IO (Maybe String)) uvalidate :: Auth -> UserStr -> PasswdStr -> (IO (Maybe String)) -- | Register a user with the auth method. userRegister :: MonadIO m => UserStr -> PasswdStr -> m (Maybe String) -- | Sets an authentication method, that includes the registration and -- validation calls. Both return Nothing if successful. Otherwise they -- return a text message explaining the failure. setAuthMethod :: Auth -> IO () -- | Authentication against userRegistered users. to be used with -- validate userValidate :: (FormInput view, MonadIO m) => (UserStr, PasswdStr) -> m (Maybe view) -- | If the user is logged or is anonymous isLogged :: MonadState (MFlowState v) m => m Bool -- | Set the Administrator user and password. It must be defined in Main, -- before any configuration parameter is read and before the execution of -- any flow. setAdminUser :: MonadIO m => UserStr -> PasswdStr -> m () getAdminName :: String getCurrentUser :: MonadState (MFlowState view) m => m String -- | If not logged, perform login. otherwise return the user -- --
-- getUserSimple= getUser Nothing userFormLine --getUserSimple :: (FormInput view, Typeable view) => FlowM view IO 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, Typeable view) => Maybe String -> View view IO (Maybe (UserStr, PasswdStr), Maybe String) -> FlowM view IO 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 logging, a entry to repeat password necessary 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") -- <+> fromStr " password again" +> getPassword <* submitButton "register" --userFormLine :: (FormInput view, Functor m, Monad m) => View view m (Maybe (UserStr, PasswdStr), Maybe PasswdStr) -- | Example of user/password form (no validation) to be used with -- userWidget userLogin :: (FormInput view, Functor m, Monad m) => View view m (Maybe (UserStr, PasswdStr), Maybe String) logout :: (MonadState (MFlowState view) m, MonadIO m) => m () paranoidLogout :: (MonadState (MFlowState view) m, MonadIO m) => m () encryptedLogout :: (MonadState (MFlowState view) m, MonadIO m) => m () -- | 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. If this user was already logged, the widget return -- the user without asking. If the user press the register button, the -- new user-password is registered and the user logged. userWidget :: (MonadIO m, Functor m, FormInput view) => Maybe String -> View view m (Maybe (UserStr, PasswdStr), Maybe String) -> View view m String -- | Uses 4 different keys to encrypt the 4 parts of a MFlow cookie. paranoidUserWidget :: (FormInput view, MonadIO m, Functor m) => Maybe String -> View view m (Maybe (UserStr, PasswdStr), Maybe [Char]) -> View view m String -- | Uses a single key to encrypt the MFlow cookie. encryptedUserWidget :: (FormInput view, MonadIO m, Functor m) => Maybe String -> View view m (Maybe (UserStr, PasswdStr), Maybe [Char]) -> View view m String -- | change the user -- -- It is supposed that the user has been validated login :: (MonadState (MFlowState view) m, MonadIO m) => String -> m () paranoidLogin :: (MonadState (MFlowState view) m, MonadIO m) => String -> m () encryptedLogin :: (MonadState (MFlowState view) m, MonadIO m) => String -> m () userName :: User -> String ask :: FormInput view => View view IO a -> FlowM view IO a -- | A synonym of ask. -- -- Maybe more appropriate for pages with long interactions with the user -- while the result has little importance. page :: FormInput view => View view IO a -> FlowM view IO a -- | for compatibility with the same procedure in askt. This is the -- non testing version -- --
-- askt v w= ask w ---- -- hide one or the other askt :: FormInput v => (Int -> a) -> View v IO a -> FlowM v IO a -- | Run a transient Flow from the IO monad. runNav :: String -> FlowM -- Html IO () -> IO () runNav ident f= exec1 ident $ runFlowOnce -- (transientNav f) undefined -- -- Clears the environment clearEnv :: MonadState (MFlowState view) m => m () -- | clear the request paramenters and the rest path. clearEnv' :: MonadState (MFlowState view) m => m () -- | Creates a stateless flow (see stateless) whose behavior is -- defined as a widget. It is a higher level form of the latter wstateless :: (Typeable view, FormInput view) => View view IO () -> Flow -- | Prepares the state for a page flow. It add a prefix to every form -- element or link identifier for the formlets and also keep the state of -- the links clicked and form input entered within the widget. If the -- computation within the widget has branches if case -- etc, each branch must have its pageFlow with a distinct identifier. -- See -- http://haskell-web.blogspot.com.es/2013/06/the-promising-land-of-monadic-formlets.html pageFlow :: (Monad m, Functor m, FormInput view) => String -> View view m a -> View view m a -- | Display a text box and return a non empty 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, 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, 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) => Text -> View view m Text -- | 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, Functor 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, Eq 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, Eq 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 -- | encloses a set of Radio boxes. Return the option selected getRadio :: (Monad m, Functor m, FormInput view) => [String -> View view m (Radio a)] -> View view m a -- | Implement a radio button the parameter is the name of the radio group setRadio :: (FormInput view, MonadIO m, Read a, Typeable a, Eq a, Show a) => a -> String -> View view m (Radio a) -- | Implement a radio button that perform a submit when pressed. the -- parameter is the name of the radio group setRadioActive :: (FormInput view, MonadIO m, Read a, Typeable a, Eq a, Show a) => a -> String -> View view m (Radio a) wlabel :: (Monad m, FormInput view) => view -> View view m a -> View view m a getCheckBoxes :: (FormInput view, Monad m) => View view m CheckBoxes -> View view m [String] -- | Read the checkboxes dynamically created by JavaScript within the view -- parameter see for example selectAutocomplete in -- MFlow.Forms.Widgets genCheckBoxes :: (Monad m, FormInput view) => view -> View view m CheckBoxes -- | Display a text box and return the value entered if it is readable( -- Otherwise, fail the validation) setCheckBox :: (FormInput view, MonadIO m) => Bool -> String -> View view m CheckBoxes submitButton :: (FormInput view, Monad m) => String -> View view m String resetButton :: (FormInput view, Monad m) => String -> View view m () whidden :: (Monad m, FormInput v, Read a, Show a, Typeable a) => a -> View v m a -- | Creates a link to a the next step within the flow. A link can be -- composed with other widget elements. It can not be broken by its own -- definition. It points to the page that created it. wlink :: (Typeable a, Show a, MonadIO m, FormInput view) => a -> view -> View view m a -- | Creates an absolute link. While a wlink path depend on the page -- where it is located and ever points to the code of the page that had -- it inserted, an absLink point to the first page in the flow that -- inserted it. It is useful for creating a backtracking point in -- combination with retry -- --
-- page $ absLink "here" << p << "here link" -- page $ p << "second page" ++> wlink () << p << "click here" -- page $ p << "third page" ++> retry (absLink "here" << p << "will go back") -- page $ p << "fourth page" ++> wlink () << p << "will not reach here" ---- -- After navigating to the third page, when clicking in the link, will -- backtrack to the first, and will validate the first link as if the -- click where done in the first page. Then the second page would be -- displayed. -- -- In monadic widgets, it also backtrack to the statement where the -- absLink is located without the need of retry: -- --
-- page $ do -- absLink "here" << p << "here link" -- p << "second statement" ++> wlink () << p << "click here" -- p << "third statement" ++> (absLink "here" << p << "will present the first statement alone") -- p << "fourth statement" ++> wlink () << p << "will not reach here" ---- -- absLink x = wcached (show x) 0 . wlink x absLink :: (FormInput v, MonadIO m, Typeable * a, Show a) => a -> v -> View v m a -- | return the value of a post or get param in the form -- ?param=value¶m2=value2... getKeyValueParam :: (FormInput view, MonadState (MFlowState view) m, Typeable * a, Read a) => String -> m (Maybe a) -- | upload a file to a temporary file in the server -- -- The user can move, rename it etc. fileUpload :: (FormInput view, Monad m, Functor m) => View view m (String, String, String) getRestParam :: (Read a, Typeable a, Monad m, Functor m, MonadState (MFlowState v) m, FormInput v) => m (Maybe a) -- | When some user interface return some response to the server, but it is -- not produced by a form or a link, but for example by an script, -- returning convert this code into a widget. -- -- At runtime the parameter is read from the environment and validated. -- -- . The parameter is the visualization code, that accept a serialization -- function that generate the server invocation string, used by the -- visualization to return the value by means of an script, usually. returning :: (Typeable a, Read a, Show a, Monad m, FormInput view) => ((a -> String) -> view) -> View view m a -- | Wrap a widget with form element within a form-action element. Usually -- this is not necessary since this wrapping is done automatically by the -- View monad, unless there are more than one form in the page. wform :: (Monad m, FormInput view) => View view m b -> View view m b -- | Concat a list of widgets of the same type, return a the first -- validated result firstOf :: (FormInput view, Monad m, Functor m) => [View view m a] -> View view m a -- | from a list of widgets, it return the validated ones. manyOf :: (FormInput view, MonadIO m, Functor m) => [View view m a] -> View view m [a] -- | like manyOf, but does not validate if one or more of the widgets does -- not validate allOf :: (FormInput view, MonadIO m, IsString view, Functor m) => [View view m a] -> View view m [a] -- | Render raw view formatting. It is useful for displaying information. wraw :: Monad m => view -> View view m () -- | Render a Show-able value and return it wrender :: (Monad m, Functor m, Show a, FormInput view) => a -> View view m a notValid :: Monad m => view -> View view m a -- | 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 view)) -> View view m a -- | Empty widget that does not validate. May be used as "empty boxes" -- inside larger widgets. -- -- It returns a non valid value. noWidget :: (FormInput view, Monad m, Functor m) => View view m a -- | a synonym of noWidget that can be used in a monadic expression in the -- View monad does not continue stop :: (FormInput view, Monad m, Functor m) => View view m a -- | Actions are callbacks that are executed when a widget is validated. A -- action may be a complete flow in the flowM monad. It takes complete -- control of the navigation while it is executed. At the end it return -- the result to the caller and display the original calling page. It is -- useful when the widget is inside widget containers that may treat it -- as a black box. -- -- It returns a result that can be significant 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 -- | It is a callback in the view monad. The callback rendering substitutes -- the widget rendering when the latter is validated, without affecting -- the rendering of other widgets. This allow the simultaneous execution -- of different behaviors in different widgets in the same page. The -- inspiration is the callback primitive in the Seaside Web Framework -- that allows similar functionality (See http://www.seaside.st) -- -- This is the visible difference with waction callbacks, which -- execute a a flow in the FlowM monad that takes complete control of the -- navigation, while wactions are executed within the same page. wcallback :: Monad m => View view m a -> (a -> View view m b) -> View view m b -- | change the rendering and the return value of a page. This is -- superseded by page flows. 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 programmer 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.Blaze.Html.All -- import Some.Time.Library -- addMessageFlows [(noscript, time)] -- main= run 80 waiMessageFlow -- time=do ask $ cachedWidget "time" 5 -- $ wlink () b << "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. -- -- NOTE: the rendering of cached widgets are shared by all users cachedWidget :: (MonadIO m, Typeable view, FormInput view, Typeable a, Executable m) => String -> Int -> View view Identity a -> View view m a -- | A shorter name for cachedWidget wcached :: (MonadIO m, Typeable view, FormInput view, Typeable a, Executable m) => String -> Int -> View view Identity a -> View view m a -- | Unlike cachedWidget, which cache the rendering but not the user -- response, wfreeze cache also the user response. This is -- useful for pseudo-widgets which just show information while the -- controls are in other non freezed widgets. A freezed widget ever -- return the first user response It is faster than cachedWidget. -- It is not restricted to the Identity monad. -- -- NOTE: the content of freezed widgets are shared by all users wfreeze :: (MonadIO m, Typeable view, FormInput view, Typeable a, Executable m) => String -> Int -> View view m a -> View view m a -- | Join two widgets in the same page the resulting widget, when -- asked with it, return a 2 tuple of their validation results -- if both return Noting, the widget return Nothing (invalid). -- -- it has a low infix priority: infixr 2 -- --
-- r <- ask widget1 <+> widget2 -- case r of (Just x, Nothing) -> .. --(<+>) :: (Monad m, FormInput view) => 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. -- -- it has a infix priority infixr 5 (|*>) :: (MonadIO m, Functor m, FormInput view) => View view m r -> [View view m r'] -> View view m (Maybe r, Maybe r') -- | Put a widget before and after other. Useful for navigation links in a -- page that appears at toAdd and at the bottom of a page. (|+|) :: (Functor m, FormInput 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 second 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 happens in the case of -- *> . -- -- Here w's are widgets and r's are returned values -- -- (w1 <* w2) will return Just r1 only if w1 and w2 -- are validated -- -- (w1 <** w2) will return Just r1 even if w2 is not -- validated -- -- it has a low infix priority: infixr 1 (**>) :: (Functor m, Monad m, FormInput view) => 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 second element is displayed however, as in the -- case of <*. see the <** examples -- -- it has a low infix priority: infixr 1 (<**) :: (Functor m, Monad m, FormInput view) => View view m a -> View view m b -> 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 (>:>) :: (Monad m, Monoid v) => View v m a -> View v m [a] -> View v m [a] -- | Enclose Widgets within some formatting. view is intended to -- be instantiated to a particular format -- -- NOTE: It has a infix priority : infixr 5 less than the one of -- ++> and <++ of the operators, so use -- parentheses when appropriate, unless the we want to enclose all the -- widgets in the right side. Most of the type errors in the DSL are due -- to the low priority of this operator. -- -- This is a widget, which is a table with some links. it returns an Int -- --
-- import MFlow.Forms.Blaze.Html -- -- tableLinks :: View Html Int -- table ! At.style "border:1;width:20%;margin-left:auto;margin-right:auto" -- <<< caption << text "choose an item" -- ++> thead << tr << ( th << b << text "item" <> th << b << text "times chosen") -- ++> (tbody -- <<< tr ! rowspan "2" << td << linkHome -- ++> (tr <<< td <<< wlink IPhone (b << text "iphone") <++ td << ( b << text (fromString $ show ( cart V.! 0))) -- <|> tr <<< td <<< wlink IPod (b << text "ipad") <++ td << ( b << text (fromString $ show ( cart V.! 1))) -- <|> tr <<< td <<< wlink IPad (b << text "ipod") <++ td << ( b << text (fromString $ show ( cart V.! 2)))) -- ) --(<<<) :: (Monad m, Monoid view) => (view -> view) -> View view m a -> View view m a -- | Prepend formatting code to a widget -- --
-- bold << "enter name" ++> getString Nothing ---- -- It has a infix priority: infixr 6 higher than -- <<< and most other operators (++>) :: (Monad m, Monoid view) => view -> View view m a -> View view m a -- | Append formatting code to a widget -- --
-- getString "hi" <++ H1 << "hi there" ---- -- It has a infix priority: infixr 6 higher than -- <<< and most other operators. (<++) :: (Monad m, Monoid v) => View v m a -> v -> View v m a -- | Add attributes to the topmost tag of a widget -- -- It has a fixity infix 8 ( View view m a -> Attribs -> View view 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 -- | send raw bytestring data to the client. usable for -- -- example -- --
-- do ---- -- setHttpHeader Content-Type "text//plain" maxAge 36000 rawSend -- longdata rawSend :: (FormInput v, MonadIO m, Functor m) => ByteString -> View v m () -- | 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 normalize :: (Monad m, FormInput v) => View v m a -> View ByteString m a -- | Execute the Flow, in the FlowM view m monad. It is used as -- parameter of hackMessageFlow waiMessageFlow or -- addMessageFlows -- -- The flow is executed in a loop. When the flow is finished, it is -- started again -- --
-- main= do
-- addMessageFlows [("noscript",transient $ runFlow mainf)]
-- forkIO . run 80 $ waiMessageFlow
-- adminLoop
--
runFlow :: (FormInput view, MonadIO m) => FlowM view (Workflow m) () -> Token -> Workflow m ()
-- | To execute transient flows as if they were persistent, it can be used
-- instead of step, but it does log nothing. Thus, it is faster and
-- convenient when no session state must be stored beyond the lifespan of
-- the server process.
--
-- -- transient $ runFlow f === runFlow $ transientNav f --transientNav :: (Serialize a, Typeable view, FormInput view, Typeable a) => FlowM view IO a -> FlowM view (Workflow IO) a runFlowOnce :: (FormInput view, MonadIO m) => FlowM view (Workflow m) () -> Token -> Workflow m () -- | Run a persistent flow inside the current flow. It is identified by the -- procedure and the string identifier. Unlike the normal flows, that run -- within infinite loops, runFlowIn executes once. In subsequent -- executions, the flow will get the intermediate responses from the log -- and will return the result without asking again. This is useful for -- asking once, storing in the log and subsequently retrieving user -- defined configurations by means of persistent flows with web -- formularies. runFlowIn :: (MonadIO m, FormInput view) => String -> FlowM view (Workflow IO) b -> FlowM view m b -- | To unlift a FlowM computation. useful for executing the configuration -- generated by runFLowIn outside of the web flow (FlowM) monad runFlowConf :: (FormInput view, MonadIO m) => FlowM view m a -> m a -- | Stores the result of the flow in a persistent log. When restarted, it -- get the result from the log and it does not execute it again. When no -- results are in the log, the computation is executed. It is equivalent -- to step but in the FlowM monad. step :: (Serialize a, Typeable view, FormInput 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 check 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 below would execute the ask of the menu once. But the -- user will never have the possibility to see the menu again. 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 ---- -- However this is very specialized. Normally the back button detection -- is not necessary. In a persistent flow (with step) even this default -- entry option would be completely automatic, since the process would -- restart at the last page visited. goingBack :: MonadState (MFlowState view) m => m Bool -- | return the result if going forward -- -- If the process is backtracking, it does not validate, in order to -- continue the backtracking returnIfForward :: (Monad m, FormInput view, Functor m) => b -> View view m b -- | Use this instead of return to return from a computation with ask -- statements -- -- 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 -> FlowM v m a -- | Will prevent the Suprack beyond the point where -- preventGoingBack is located. If the user press the back button -- beyond that point, the flow parameter is executed, usually it is an -- ask statement with a message. If the flow is not going back, it does -- nothing. It is a cut in Supracking -- -- It is useful when an undoable transaction has been commited. For -- example, after a payment. -- -- This example show a message when the user go back and press again to -- pay -- --
-- ask $ wlink () << b << "press here to pay 100000 $ " -- payIt -- preventGoingBack . ask $ b << "You paid 10000 $ one time" -- ++> wlink () << b << " Please press here to complete the proccess" -- ask $ wlink () << b << "OK, press here to go to the menu or press the back button to verify that you can not pay again" -- where -- payIt= liftIO $ print "paying" --preventGoingBack :: (Functor m, MonadIO m, FormInput v) => FlowM v m () -> FlowM v m () -- | less powerful version of onBacktrack: The second computation -- simply undo the effect of the first one, and the flow continues -- backward ever. It can be used as a rollback mechanism in the context -- of long running transactions. compensate :: Monad m => m a -> m a -> FlowM v m a -- | executes the first computation when going forward and the second -- computation when backtracking. Depending on how the second computation -- finishes, the flow will resume forward or backward. onBacktrack :: Monad m => m a -> FlowM v m a -> FlowM v m a -- | forces backtracking if the widget validates, because a previous page -- handle this widget response . This is useful for recurrent cached -- widgets or absLinks that are present in multiple pages. For -- example in the case of menus or common options. The active elements of -- this widget must be cached with no timeout. retry :: Monad m => View v m a -> View v m () -- | Set an HTTP Response header setHttpHeader :: MonadState (MFlowState view) m => ByteString -> ByteString -> m () -- | Set the header-footer that will enclose the widgets. It must be -- provided in the same formatting than them, although with normalization -- to ByteStrings any formatting can be used -- -- 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 :: MonadState (MFlowState view) m => (view -> view) -> m ()
-- | Add another header embedded in the previous one
addHeader :: Monad m => (view -> view) -> FlowM view m ()
-- | Return the current header
getHeader :: Monad m => FlowM view m (view -> view)
-- | Set user-defined data in the context of the session.
--
-- The data is indexed by type in a map. So the user can insert-retrieve
-- different kinds of data in the session context.
--
-- This example define addHistory and getHistory to
-- maintain a Html log in the session of a Flow:
--
-- -- newtype History = History ( Html) deriving Typeable -- setHistory html= setSessionData $ History html -- getHistory= getSessionData `onNothing` return (History mempty) >>= \(History h) -> return h -- addHistory html= do -- html' <- getHistory -- setHistory $ html' `mappend` html --setSessionData :: (Typeable a, MonadState (MFlowState view) m) => a -> m () -- | Get the session data of the desired type if there is any. getSessionData :: (Typeable a, MonadState (MFlowState view) m) => m (Maybe a) -- | getSessionData specialized for the View monad. if Nothing, the monadic -- computation does not continue. getSData :: (Monad m, Typeable a, Monoid v) => View v m a delSessionData :: (MonadState (MFlowState view) m, Typeable * a) => a -> m () -- | Set 1) the timeout of the flow execution since the last user -- interaction. Once passed, the flow executes from the beginning. -- -- 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. -- -- As the other state primitives, it can be run in the Flow and in the -- View monad -- -- transient flows restart anew. persistent flows (that use -- step) restart at the last saved execution point, unless the -- session time has expired for the user. setTimeouts :: MonadState (MFlowState v) m => Int -> Integer -> m () -- | Set an HTTP cookie setCookie :: MonadState (MFlowState view) m => String -> String -> String -> Maybe Integer -> m () setParanoidCookie :: MonadState (MFlowState view) m => String -> String -> String -> Maybe Integer -> m () setEncryptedCookie :: MonadState (MFlowState view) m => String -> String -> String -> Maybe Integer -> m () -- | Install the server code and return the client code for an AJAX -- interaction. It is very lightweight, It does no t need jQuery. -- -- This example increases the value of a text box each time the box is -- clicked -- --
-- ask $ do
-- let elemval= "document.getElementById('text1').value"
-- ajaxc <- ajax $ \n -> return $ elemval <> "='" <> B.pack(show(read n +1)) <> "'"
-- b << text "click the box"
-- ++> getInt (Just 0) <! [("id","text1"),("onclick", ajaxc elemval)]
--
ajax :: (MonadIO m, FormInput v) => (String -> View v m ByteString) -> View v m (String -> String)
-- | Send the JavaScript expression, generated by the procedure parameter
-- as a ByteString, execute it in the browser and the result is returned
-- back
--
-- The ajaxSend invocation must be inside a ajax procedure or
-- else a No ajax session set error will be produced
ajaxSend :: (Read a, Monoid v, MonadIO m) => View v m ByteString -> View v m a
-- | Like ajaxSend but the result is ignored
ajaxSend_ :: (MonadIO m, Monoid v) => View v m ByteString -> View v m ()
class Requirements a
installRequirements :: (Requirements a, MonadState (MFlowState view) m, MonadIO m, FormInput view) => [a] -> m view
data WebRequirement
-- | Script URL and the list of scripts to be executed when loaded
JScriptFile :: String -> [String] -> WebRequirement
-- | a CSS file URL
CSSFile :: String -> WebRequirement
-- | a String with a CSS description
CSS :: String -> WebRequirement
-- | a string with a valid JavaScript
JScript :: String -> WebRequirement
-- | a server procedure
ServerProc :: (String, Flow) -> WebRequirement
-- | Requirements are JavaScripts, Stylesheets or server processes (or any
-- instance of the Requirement class) that are included in the Web
-- page or in the server when a widget specifies this. requires
-- is the procedure to be called with the list of requirements. Various
-- widgets in the page can require the same element, MFlow will install
-- it once.
requires :: (Requirements a, MonadState (MFlowState view) m, Typeable * a, Show a) => [a] -> m ()
-- | Return the session identifier
getSessionId :: MonadState (MFlowState v) m => m String
-- | Return the user language. Now it is fixed to "en"
getLang :: MonadState (MFlowState view) m => m String
-- | Generate a new string. Useful for creating tag identifiers and other
-- attributes.
--
-- If the page is refreshed, the identifiers generated are the same.
genNewId :: MonadState (MFlowState view) m => m String
-- | Get the next identifier that will be created by genNewId
getNextId :: MonadState (MFlowState view) m => m String
-- | Execute the widget in a monad and return the result in another.
changeMonad :: (Monad m, Executable m') => View v m' a -> View v m a
data FailBack a
fromFailBack :: FailBack t -> t
toFailBack :: a -> FailBack a
instance [incoherent] Typeable MFOption
instance [incoherent] Typeable AjaxSessionId
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] (FormInput view, Monad m, Functor m) => Monoid (View view m (MFOption a))
instance [incoherent] Monoid CheckBoxes
-- | Instantiation of the FormInput class for blaze-html
-- http://hackage.haskell.org/package/blaze-html
--
-- This package is included in MFlow.Wai.Blaze.Hml.All.
--
-- Use it to create applications with this kind of formatting.
module MFlow.Forms.Blaze.Html
-- | Used to insert html elements within a tag with the appropriate infix
-- priority for the other operators used in MFlow. Also it can be used
-- for adding markup to widgets with this signature such are wlink
-- ad setOption
(<<) :: ToMarkup a => (Markup -> t) -> a -> t
instance FormInput Html
module MFlow.Forms.Admin
-- | A small console interpreter with some commands:
--
-- -- r <- page $ autoRefresh $ ul <<< do -- li <<< wlink OptionA << "option A" -- ul <<< li <<< (wlink OptionA1 << "Option A1" <! noAutoRefresh) -- <|> li <<< (wlink OptionA2 << "Option A2" <! noAutoRefresh) -- <|>... -- maybe other content -- -- case r of -- OptionA1 -> pageA1 -- OptionA2 -> pageA2 ---- -- when option A is clicked, the two sub-options appear with -- autorefresh. Only the two lines are returned by the server using AJAX. -- but when Option A1-2 is pressed we want to present other pages, so we -- add the noAutorefresh attribute. -- -- NOTE: the noAutoRefresh attribute should be added to the a/ or -- form/ tags. noAutoRefresh :: [(String, String)] -- | does the same than autoRefresh but append the result of each -- request to the bottom of the widget -- -- all the comments and remarks of autoRefresh apply here appendUpdate :: (MonadIO m, FormInput v) => View v m a -> View v m a -- | does the same than autoRefresh but prepend the result of each -- request before the current widget content -- -- all the comments and remarks of autoRefresh apply here prependUpdate :: (MonadIO m, FormInput v) => View v m a -> View v m a -- | continuously execute a widget and update the content. The update -- method specify how the update is done. Html means a -- substitution of content. The second parameter is the delay for the -- next retry in case of disconnection, in milliseconds. -- -- It can be used to show data updates in the server. The widget is -- executed in a different process than the one of the rest of the page. -- Updates in the session context are not seen by the push widget. It has -- his own context. To communicate with the widget, use DBRef's or TVar -- and the STM semantics for waiting updates using retry. -- -- Widgets in a push can have links and forms, but since they are -- asynchronous, they can not return inputs. but they can modify the -- server state. push ever return an invalid response to the calling -- widget, so it never triggers the advance of the navigation. -- -- This example is a counter increased each second: -- --
-- pushIncrease= do -- tv <- liftIO $ newTVarIO 0 -- page $ push 0 Html $ do -- n <- atomic $ readTVar tv -- atomic $ writeTVar tv $ n + 1 -- liftIO $ threadDelay 1000000 -- b << (show n) ++> noWidget ---- -- This other simulates a console output that echoes what is entered in a -- text box below. It has two widgets: a push output in append mode and a -- text box input. The communication uses a TVar. The push widget wait -- for updates in the TVar. because the second widget uses autoRefresh, -- all happens in the same page. -- -- It is recommended to add a timeout to the push widget, like in the -- example: -- --
-- pushSample= do -- tv <- liftIO $ newTVarIO $ Just "init" -- page $ push Append 1000 (disp tv) <** input tv -- -- where -- disp tv= do -- setTimeouts 100 0 -- line <- tget tv -- p << line ++> noWidget -- -- input tv= autoRefresh $ do -- line <- getString Nothing <** submitButton "Enter" -- tput tv line -- -- tput tv x = atomic $ writeTVar tv ( Just x) !> "WRITE" -- -- tget tv= atomic $ do -- mr <- readTVar tv -- case mr of -- Nothing -> retry -- Just r -> do -- writeTVar tv Nothing -- return r --push :: FormInput v => UpdateMethod -> Int -> View v IO () -> View v IO () data UpdateMethod Append :: UpdateMethod Prepend :: UpdateMethod Html :: UpdateMethod -- | takes as argument a widget and delay the load until it is visible. The -- rendering to be shown during the load is the specified in the first -- parameter. The resulting lazy widget behaves programatically in the -- same way. -- -- It can lazily load recursively. It means that if the loaded widget has -- a lazy statement, it will be honored as well. -- -- Because a widget can contain arbitrary HTML, images or javascript, -- lazy can be used to lazy load anything. -- -- To load a image: -- -- lazy temprendering $ wraw ( img ! href imageurl) -- -- or -- -- lazy temprendering $ img ! href imageurl ++> noWidget lazy :: (FormInput v, Functor m, MonadIO m) => v -> View v m a -> View v m a -- | present the JQuery datePicker calendar to choose a date. The second -- parameter is the configuration. Use "()" by default. See -- http://jqueryui.com/datepicker/ datePicker :: (Monad m, FormInput v) => String -> Maybe String -> View v m (Int, Int, Int) -- | show the jQuery spinner widget. the first parameter is the -- configuration . Use "()" by default. See -- http://jqueryui.com/spinner getSpinner :: (MonadIO m, Read a, Show a, Typeable a, FormInput view) => String -> Maybe a -> View view m a -- | Present the JQuery auto-completion list, from a procedure defined by -- the programmer, to a text box. wautocomplete :: (Show a, MonadIO m, FormInput v) => Maybe String -> (String -> IO a) -> View v m String -- | present a jQuery dialog with a widget. When a button is pressed it -- return the result. The first parameter is the configuration. To make -- it modal, use "({modal: true})" see http://jqueryui.com/dialog/ -- for the available configurations. -- -- The enclosed widget will be wrapped within a form tag if the user do -- not encloses it using wform.f wdialog :: (Monad m, FormInput v) => String -> String -> View v m a -> View v m a -- | Present a user form if not logged in. Otherwise, the user name and a -- logout link is presented. The parameters and the behavior are the same -- as userWidget. Only the display is different userFormOrName :: (FormInput [t], FormInput t, MonadIO m, Functor m) => Maybe String -> View [t] m (Maybe (UserStr, PasswdStr), Maybe String) -> View [t] m String -- | Display a logout link if the user is logged. Nothing otherwise maybeLogout :: (MonadIO m, Functor m, FormInput v) => View v m () -- | If not logged, it present a page flow which asks for the user name, -- then the password if not logged -- -- If logged, it present the user name and a link to logout -- -- normally to be used with autoRefresh and pageFlow when used with other -- widgets. wlogin :: (MonadIO m, Functor m, FormInput v) => View v m () -- | Inside a tag, it add and delete widgets of the same type. When the -- form is submitted or a wlink is pressed, this widget return the list -- of validated widgets. the event for adding a new widget is attached , -- as a click event to the element of the page with the identifier -- wEditListAdd that the user will choose. -- -- This example add or delete editable text boxes, with two initial boxes -- with hi, how are you as values. Tt uses blaze-html: -- --
-- r <- ask $ addLink -- ++> br -- ++> (El.div `wEditList` getString1 $ ["hi", "how are you"]) "addid" -- <++ br -- <** submitButton "send" -- -- ask $ p << (show r ++ " returned") -- ++> wlink () (p << text " back to menu") -- mainmenu -- where -- addLink = a ! At.id "addid" -- ! href "#" -- $ text "add" -- delBox = input ! type_ "checkbox" -- ! checked "" -- ! onclick "this.parentNode.parentNode.removeChild(this.parentNode)" -- getString1 mx= El.div <<< delBox ++> getString mx <++ br --wEditList :: (Typeable a, Read a, FormInput view, Functor m, MonadIO m, Executable m, Typeable m, Typeable Identity) => (view -> view) -> (Maybe a -> View view Identity a) -> [a] -> String -> View view m [a] -- | A specialization of wutocompleteEdit which make appear each -- chosen option with a checkbox that deletes the element when unchecked. -- The result, when submitted, is the list of selected elements. wautocompleteList :: (Functor m, MonadIO m, Executable m, FormInput v, Typeable m, Typeable Identity) => String -> (String -> IO [String]) -> [String] -> View v m [String] -- | Produces a text box. It gives a auto-completion list to the textbox. -- When return is pressed in the textbox, the box content is used to -- create a widget of a kind defined by the user, which will be situated -- above of the textbox. When submitted, the result is the content of the -- created widgets (the validated ones). -- -- wautocompleteList is an specialization of this widget, where -- the widget parameter is fixed, with a checkbox that delete the element -- when unselected . This fixed widget is as such (using generic -- FormElem class tags): -- --
-- ftag "div" <<< ftag "input" mempty
-- `attrs` [("type","checkbox")
-- ,("checked","")
-- ,("onclick","this.parentNode.parentNode.removeChild(this.parentNode)")]
-- ++> ftag "span" (fromStr $ fromJust x )
-- ++> whidden( fromJust x)
--
wautocompleteEdit :: (Typeable a, MonadIO m, Functor m, Executable m, FormInput v, Typeable m, Typeable Identity) => String -> (String -> IO [String]) -> (Maybe String -> View v Identity a) -> [String] -> View v m [a]
-- | Deletes the list of edited widgets for a certain identifier and with
-- the type of the witness widget parameter
delEdited :: (Typeable v, Typeable a, MonadIO m, Typeable m1, MonadState (MFlowState view) m) => ByteString -> [View v m1 a] -> m ()
-- | Return the list of edited widgets (added by the active widgets) for a
-- given identifier
getEdited :: (Typeable v, Typeable a, Typeable m1, MonadState (MFlowState view) m) => ByteString -> m [View v m1 a]
setEdited :: (MonadState (MFlowState view) m, Typeable * a, Typeable * view1, Typeable (* -> *) m1) => ByteString -> [(String, View view1 m1 a)] -> m ()
-- | Return the JavaScript to be executed on the browser to prepend a
-- widget to the location identified by the selector (the bytestring
-- parameter), The selector must have the form of a jQuery expression .
-- It stores the added widgets in the edited list, that is accessed with
-- getEdited
--
-- The resulting string can be executed in the browser. ajax will
-- return the code to execute the complete ajax roundtrip. This code
-- returned by ajax must be in an event handler.
--
-- This example will insert a widget in the div when the element with
-- identifier clickelem is clicked. when the form is submitted,
-- the widget values are returned and the list of edited widgets are
-- deleted.
--
--
-- id1<- genNewId
-- let sel= "$('#" <> fromString id1 <> "')"
-- callAjax <- ajax . const $ prependWidget sel wn
-- let installevents= "$(document).ready(function(){\
-- \$('#clickelem').click(function(){"++callAjax "''"++"});})"
--
-- requires [JScriptFile jqueryScript [installevents] ]
-- ws <- getEdited sel
-- r <- (div <<< manyOf ws) <! [("id",id1)]
-- delEdited sel ws'
-- return r
--
prependWidget :: (Typeable a, MonadIO m, Executable m, FormInput v, Typeable Identity, Typeable m) => ByteString -> View v Identity a -> View v m ByteString
-- | Like prependWidget but append the widget instead of prepend.
appendWidget :: (Typeable a, MonadIO m, Executable m, FormInput v, Typeable Identity, Typeable m) => ByteString -> View v Identity a -> View v m ByteString
-- | Like prependWidget but set the entire content of the selector
-- instead of prepending an element
setWidget :: (Typeable a, MonadIO m, Executable m, FormInput v, Typeable Identity, Typeable m) => ByteString -> View v Identity a -> View v m ByteString
-- | a text field. Read the cached field value and present it without
-- edition.
tField :: (MonadIO m, Functor m, Executable m, FormInput v) => Key -> View v m ()
-- | A widget that display the content of an html, But if the user has
-- edition privileges, it permits to edit it in place. So the editor
-- could see the final appearance of what he writes.
--
-- When the user click the save, the content is saved and identified by
-- the key. Then, from now on, all the users will see the saved content
-- instead of the code content.
--
-- The content is saved in a file by default (texts in this
-- versions), but there is a configurable version (tFieldGen).
-- The content of the element and the formatting is cached in memory, so
-- the display is, theoretically, very fast.
tFieldEd :: (Functor m, MonadIO m, Executable m, FormInput v) => UserStr -> Key -> v -> View v m ()
-- | Creates a rich text editor around a text field or a text area widget.
-- This code:
--
--
-- page $ p "Insert the text"
-- ++> htmlEdit ["bold","italic"] ""
-- (getMultilineText "" <! [("rows","3"),("cols","80")]) <++ br
-- <** submitButton "enter"
--
--
-- Creates a rich text area with bold and italic buttons. The buttons are
-- the ones added in the nicEdit editor.
htmlEdit :: (Monad m, FormInput v) => [String] -> UserStr -> View v m a -> View v m a
-- | permits the edition of the rendering of a widget at run time. Once
-- saved, the new rendering becomes the new rendering of the widget for
-- all the users. You must keep the active elements of the template
--
-- the first parameter is the user that has permissions for edition. the
-- second is a key that identifies the template.
edTemplate :: (MonadIO m, FormInput v, Typeable a) => UserStr -> Key -> View v m a -> View v m a
-- | Present a widget via AJAX if it is within a witerate context.
-- In the first iteration it present the widget surrounded by a
-- placeholder. subsequent iterations will send just the javascript code
-- necessary for the refreshing of the placeholder.
dField :: (Monad m, FormInput view) => View view m b -> View view m b
-- | Does the same than template but without the edition facility
template :: (MonadIO m, FormInput v, Typeable a) => Key -> View v m a -> View v m a
-- | Permits to iterate the presentation of data and//or input fields and
-- widgets within a web page that does not change. The placeholders are
-- created with dField. Both are widget modifiers: The latter gets a
-- widget and create a placeholder in the page that is updated via ajax.
-- The content of the update is the rendering of the widget at each
-- iteration. The former gets a wider widget which contains dField
-- elements and permit the iteration. Whenever a link or a form within
-- the witerate widget is activated, the result is the placeholders
-- filled with the new html content. This content can be data, a input
-- field, a link or a widget. No navigation happens.
--
-- This permits even faster updates than autoRefresh. since the latter
-- refresh the whole widget and it does not permits modifications of the
-- layout at runtime.
--
-- When edTemplate or template is used on top of witerate, the result is
-- editable at runtime, and the span placeholders generated, that are
-- updated via ajax can be relocated within the layout of the template.
--
-- Additionally, contrary to some javascript frameworks, the pages
-- generated with this mechanism are searchable by web crawlers.
witerate :: (MonadIO m, Functor m, FormInput v) => View v m a -> View v m a
tfieldKey :: TField -> Key
-- | A multilanguage version of tFieldEd. For a field with key it
-- add a suffix with the two characters of the language used.
mFieldEd :: (FormInput v, MonadIO m, Executable m, Functor m) => UserStr -> [Char] -> v -> View v m ()
-- | A multilanguage version of tField
mField :: (FormInput v, MonadIO m, Executable m, Functor m) => [Char] -> View v m ()
-- | Insert a form tag if the widget has form input fields. If not, it does
-- nothing
insertForm :: (FormInput view, Monad m) => View view m a -> View view m a
readtField :: FormInput a => a -> String -> IO a
writetField :: FormInput view => String -> view -> IO ()
instance [overlap ok] Typeable Medit
instance [overlap ok] Typeable TField
instance [overlap ok] Typeable IteratedId
instance [overlap ok] Typeable Identity
instance [overlap ok] Read TField
instance [overlap ok] Show TField
instance [overlap ok] Show IteratedId
instance [overlap ok] Show UpdateMethod
instance [overlap ok] Serializable TField
instance [overlap ok] Indexable TField
module MFlow.Wai
waiMessageFlow :: Application
instance Processable Request
module MFlow.Wai.Blaze.Html.All
-- | Run a persistent flow. It uses getPortW to get the port The
-- first parameter is the first element in the URL path. It also set the
-- home page The port is read from the first parameter passed to the
-- executable. If no parameter, it is read from the PORT environment
-- variable. if this does not exist, the port 80 is used.
runNavigation :: String -> FlowM Html (Workflow IO) () -> IO ()
-- | Exactly the same as runNavigation, but with TLS added. Expects
-- certificate.pem and key.pem in project directory.
runSecureNavigation :: String -> FlowM Html (Workflow IO) () -> IO ()
runSecureNavigation' :: TLSSettings -> Settings -> String -> FlowM Html (Workflow IO) () -> IO ()
module MFlow.Forms.Test
class Generate a
generate :: Generate a => IO a
-- | Run a list of flows with a number of simultaneous threads
runTest :: [(Int, Flow)] -> IO ()
runTest1 :: (Token -> Workflow IO b) -> IO (Either WFErrors b)
-- | Inject substitutes an expression by other. It may be used to override
-- ask interaction with the user. It should bee used infix for greater
-- readability:
--
-- -- ask something `inject` const some other ---- -- The parameter passed is the test number if the flow has not been -- executed by runTest, inject return the original inject :: MonadIO m => m b -> (Int -> b) -> m b -- | A simulated ask that generate simulated user input of the type -- expected. -- -- It forces the web page rendering, since it is monadic and can contain -- side effects and load effects to be tested. -- -- it is a substitute of ask from MFlow.Forms for testing -- purposes. ask :: (Generate a, MonadIO m, Functor m, FormInput v, Typeable v) => View v m a -> FlowM v m a -- | Instead of generating a result like ask, the result is given as -- the first parameter so it does not need a Generate instance. -- -- It forces the web page rendering, since it is monadic so it can -- contain side effects and load effects to be tested. askt :: (MonadIO m, FormInput v) => (Int -> a) -> View v m a -> FlowM v m a userWidget :: (MonadIO m, Functor m, FormInput view) => Maybe String -> View view m (Maybe (String, String), Maybe String) -> View view m String getUser :: (FormInput view, Typeable view, Functor m, MonadIO m) => Maybe String -> View view m (Maybe (String, String), Maybe String) -> FlowM view m String getUserSimple :: (MonadIO m, FormInput view, Typeable view, Functor m) => FlowM view m String -- | a pure version of verifyM verify :: a -> (Bool, String) -> a instance [overlap ok] (Bounded a, Enum a) => Generate a instance [overlap ok] (Generate a, Generate b) => Generate (Maybe a, Maybe b) instance [overlap ok] (Generate a, Generate b) => Generate (a, b) instance [overlap ok] Generate Integer instance [overlap ok] Generate Int instance [overlap ok] Generate String instance [overlap ok] Generate a => Generate (Maybe a)