MFlow-0.2.0.8: continuation-based Web framework without continuations.

Safe HaskellNone

MFlow.Forms

Contents

Description

This module implement stateful processes (flows) that are optionally persistent. This means that they automatically store and recover his execution state. They are executed by the MFlow app server. defined in the MFlow module.

These processses interact with the user trough user interfaces made of widgets (see below) that return back statically typed responses to the calling process. Because flows are stateful, not request-response, the code is more understandable, because all the flow of request and responses is coded by the programmer in a single function. Allthoug single request-response flows and callbacks are possible.

This module is abstract with respect to the formatting (here referred with the type variable view) . For an instantiation for Text.XHtml import MFlow.Forms.XHtml, MFlow.Hack.XHtml.All or MFlow.Wai.XHtml.All . To use Haskell Server Pages import MFlow.Forms.HSP. However the functionalities are documented here.

ask is the only method for user interaction. It run in the MFlow view m monad, with m the monad chosen by the user, usually IO. It send user interfaces (in the View view m monad) and return statically typed responses. The user interface definitions are based on a extension of formLets (http://www.haskell.org/haskellwiki/Formlets) with the addition of caching, links, formatting, attributes, extra combinators, callbaks and modifiers. The interaction with the user is stateful. In the same computation there may be many request-response interactions, in the same way than in the case of a console applications.

  • APPLICATION SERVER

Therefore, session and state management is simple and transparent: it is in the haskell structures in the scope of the computation. transient (normal) procedures have no persistent session state and stateless procedures accept a single request and return a single response.

step is a lifting monad transformer that permit persistent server procedures that remember the execution state even after system shutdowns by using the package workflow (http://hackage.haskell.org/package/Workflow) internally. This state management is transparent. There is no programer interface for session management.

The programmer set the process timeout and the session timeout with setTimeouts. If the procedure has been stopped due to the process timeout or due to a system shutdowm, the procedure restart in the last state when a request for this procedure arrives (if the procedure uses the step monad transformer)

  • WIDGETS

The correctness of the web responses is assured by the use of formLets. But unlike formLets in its current form, it permits the definition of widgets. A widget is a combination of formLets and links within its own formatting template, all in the same definition in the same source file, in plain declarative Haskell style.

The formatting is abstract. It has to implement the FormInput class. There are instances for Text.XHtml (MFlow.Forms.XHtml), Haskell Server Pages (MFlow.Forms.HSP) and ByteString. So widgets can use any formatting that is instance of FormInput. It is possible to use more than one format in the same widget.

Links defined with wlink are treated the same way than forms. They are type safe and return values to the same flow of execution. It is posssible to combine links and forms in the same widget by using applicative combinators but also additional applicative combinators like <+> !*> , |*|. Widgets are also monoids, so they can be combined as such.

  • NEW IN THIS RELEASE
WAI interface
Now MFlow works with Snap and other WAI developments. Include MFlow.Wai or MFlow.Wai.Blaze.Html.All to use it.
blaze-html support
see http://hackage.haskell.org/package/blaze-html import MFlow.Forms.Blaze.Html or MFlow.Wai.Blaze.Html.All to use Blaze-Html
AJAX
Now an ajax procedures (defined with ajax can perform many interactions with the browser widgets, instead of a single request-response (see ajaxSend).
Active widgets
MFlow.Forms.Widgets contains active widgets that interact with the server via Ajax and dynamically control other widgets: wEditList, autocomplete autocompleteEdit and others.
Requirements
a widget can specify javaScript files, JavasScript online scipts, CSS files, online CSS and server processes and any other instance of the Requrement class. See requires and WebRequirements
content-management
for templating and online edition of the content template. See tFieldEd tFieldGen and tField
multilanguage
see mField and mFieldEd
URLs to internal states
if the web navigation is trough GET forms or links, an URL can express a direct path to the n-th step of a flow, So this URL can be shared with other users. Just like in the case of an ordinary stateless application.
  • NEW IN PREVIOUS RELEASE:
Back Button
This is probably the first implementation in any language where the navigation can be expressed procedurally and still it works well with the back button, thanks to monad magic. (See http://haskell-web.blogspot.com.es/2012/03//failback-monad.html)
Cached widgets
with cachedWidget it is possible to cache the rendering of a widget as a ByteString (maintaining type safety) , the caching can be permanent or for a certain time. this is very useful for complex widgets that present information. Specially if the widget content comes from a database and it is shared by all users.
Callbacks
waction add a callback to a widget. It is executed when its input is validated. The callback may initate a flow of interactions with the user or simply executes an internal computation. Callbacks are necessary for the creation of abstract container widgets that may not know the behaviour of its content. with callbacks, the widget manages its content as black boxes.
Modifiers
wmodify change the visualization and result returned by the widget. For example it may hide a login form and substitute it by the username if already logged.

Example:

 ask $ wform userloginform `validate` valdateProc `waction` loginProc `wmodify` hideIfLogged
attributes for formLet elements
to add atributes to widgets. See the <! opèrator
ByteString normalization and hetereogeneous formatting
For caching the rendering of widgets at the ByteString level, and to permit many formatring styles in the same page, there are operators that combine different formats which are converted to ByteStrings. For example the header and footer may be coded in XML, while the formlets may be formatted using Text.XHtml.
File Server
With file caching. See MFlow.FileServer

Synopsis

Basic definitions

data FlowM v m a Source

Instances

MonadTrans (FlowM view) 
Monad m => MonadState (MFlowState v) (FlowM v m) 
Monad m => Monad (FlowM v m) 
MonadIO m => MonadIO (FlowM v m) 

newtype View v m a Source

Constructors

View 

Fields

runView :: WState v m (FormElm v a)
 

Instances

MonadTrans (View view) 
Monad m => MonadState (MFlowState view) (View view m) 
Monad m => Monad (View view m) 
(Monad m, Functor m) => Functor (View view m) 
(Functor m, Monad m) => Applicative (View view m) 
(Functor m, Monad m) => Alternative (View view m) 
MonadIO m => MonadIO (View view m) 
(Monad m, Functor m, Monoid a) => Monoid (View v m a) 
Monad m => ADDATTRS (View Html m a) 

data FormElm view a Source

Constructors

FormElm [view] (Maybe a) 

Instances

class (Monoid view, Typeable view) => FormInput view whereSource

Minimal interface for defining the basic form combinators in a concrete rendering. defined in this module. see MFlow.Forms.XHtml for the instance for Text.XHtml and MFlow.Forms.HSP for an instance form Haskell Server Pages.

Methods

toByteString :: view -> ByteStringSource

toHttpData :: view -> HttpDataSource

fromStr :: String -> viewSource

fromStrNoEncode :: String -> viewSource

ftag :: String -> view -> viewSource

inred :: view -> viewSource

flink :: String -> view -> viewSource

flink1 :: String -> viewSource

finput :: Name -> Type -> Value -> Checked -> OnClick -> viewSource

ftextarea :: String -> String -> viewSource

fselect :: String -> view -> viewSource

foption :: String -> view -> Bool -> viewSource

foption1 :: String -> Bool -> viewSource

formAction :: String -> view -> viewSource

attrs :: view -> Attribs -> viewSource

Users

userRegister :: MonadIO m => String -> String -> m (DBRef User)Source

Register an user/password

userValidate :: (FormInput view, MonadIO m) => (UserStr, PasswdStr) -> m (Maybe view)Source

Authentication against userRegistered users. to be used with validate

isLogged :: MonadState (MFlowState v) m => m BoolSource

Wether the user is logged or is anonymous

setAdminUser :: MonadIO m => UserStr -> PasswdStr -> m ()Source

getAdminName :: MonadIO m => m UserStrSource

getUserSimple :: (FormInput view, Typeable view) => FlowM view IO StringSource

If not logged, perform login. otherwise return the user

getUserSimple= getUser Nothing userFormLine

getUser :: (FormInput view, Typeable view) => Maybe String -> View view IO (Maybe (UserStr, PasswdStr), Maybe String) -> FlowM view IO StringSource

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

userFormLine :: (FormInput view, Functor m, Monad m) => View view m (Maybe (UserStr, PasswdStr), Maybe PasswdStr)Source

Is an example of login/register validation form needed by userWidget. In this case the form field appears in a single line. it shows, in sequence, entries for the username, password, a button for loging, a entry to repeat password necesary for registering and a button for registering. The user can build its own user login/validation forms by modifying this example

 userFormLine=
     (User <$> getString (Just "enter user") <*> getPassword <+> submitButton "login")
     <+> fromStr "  password again" +> getPassword <* submitButton "register"

userLogin :: (FormInput view, Functor m, Monad m) => View view m (Maybe (UserStr, PasswdStr), Maybe String)Source

Example of user/password form (no validation) to be used with userWidget

logout :: (MonadIO m, MonadState (MFlowState view) m) => m ()Source

logout. The user is resetted to the anonymous user

userWidget :: (MonadIO m, Functor m, FormInput view) => Maybe String -> View view m (Maybe (UserStr, PasswdStr), Maybe String) -> View view m StringSource

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.

getLang :: MonadState (MFlowState view) m => m StringSource

Return the user language. Now it is fixed to en

login :: (MonadIO m, MonadState (MFlowState view) m) => String -> m ()Source

change the user

It is supposed that the user has been validated

User interaction

ask :: FormInput view => View view IO a -> FlowM view IO aSource

It is the way to interact with the user. It takes a widget and return the user result.

If the widget is not validated (return Nothing), the page is presented again

If the environment has the parameters being looked at, as a result of a previous interaction, it will not ask to the user. To force asking in any case, put an clearEnv statement before

askt :: FormInput v => (Int -> a) -> View v IO a -> FlowM v IO aSource

for compatibility with the same procedure in askt. This is the non testing version

 askt v w= ask w

hide one or the other

clearEnv :: MonadState (MFlowState view) m => m ()Source

Clears the environment

wstateless :: (Typeable view, FormInput view) => View view IO a -> FlowSource

Creates a stateless flow (see stateless) whose behaviour is defined as a widget. It is a higuer level form of the latter

transfer :: MonadIO m => String -> FlowM v m ()Source

transfer control to another flow.

formLets

They usually produce the HTML form elements (depending on the FormInput instance used) It is possible to modify their attributes with the <! operator. They are combined with applicative ombinators and some additional ones formatting can be added with the formatting combinators. modifiers change their presentation and behaviour

getString :: (FormInput view, Monad m) => Maybe String -> View view m StringSource

Display a text box and return a String

getInt :: (FormInput view, MonadIO m) => Maybe Int -> View view m IntSource

Display a text box and return a Int (if the value entered is not an Int, fails the validation)

getInteger :: (FormInput view, MonadIO m) => Maybe Integer -> View view m IntegerSource

Display a text box and return an Integer (if the value entered is not an Integer, fails the validation)

getTextBox :: (FormInput view, Monad m, Typeable a, Show a, Read a) => Maybe a -> View view m aSource

getMultilineText :: (FormInput view, Monad m) => String -> View view m StringSource

Display a multiline text box and return its content

getBool :: (FormInput view, Monad m) => Bool -> String -> String -> View view m BoolSource

Display a dropdown box with the two values (second (true) and third parameter(false)) . With the value of the first parameter selected.

getSelect :: (FormInput view, Monad m, Typeable a, Read a) => View view m (MFOption a) -> View view m aSource

Display a dropdown box with the options in the first parameter is optionally selected . It returns the selected option.

setOption :: (Monad m, Show a, Typeable a, FormInput view) => a -> view -> View view m (MFOption a)Source

Set the option for getSelect. Options are concatenated with <|>

setSelectedOption :: (Monad m, Show a, Typeable a, FormInput view) => a -> view -> View view m (MFOption a)Source

Set the selected option for getSelect. Options are concatenated with <|>

getPassword :: (FormInput view, Monad m) => View view m StringSource

Display a password box

getRadio :: (Monad m, Functor m, FormInput view) => [String -> View view m Radio] -> View view m StringSource

setRadio :: (FormInput view, MonadIO m) => String -> String -> View view m RadioSource

Implement a radio button the parameter is the name of the radio group

setRadioActive :: (FormInput view, MonadIO m) => String -> String -> View view m RadioSource

Implement a radio button that perform a submit when pressed. the parameter is the name of the radio group

getCheckBoxes :: (FormInput view, Monad m) => View view m CheckBoxes -> View view m [String]Source

genCheckBoxes :: (Monad m, FormInput view) => view -> View view m CheckBoxesSource

Read the checkboxes dinamically created by JavaScript within the view parameter see for example selectAutocomplete in MFlow.Forms.Widgets

setCheckBox :: (FormInput view, MonadIO m) => Bool -> String -> View view m CheckBoxesSource

Display a text box and return the value entered if it is readable( Otherwise, fail the validation)

resetButton :: (FormInput view, Monad m) => String -> View view m ()Source

whidden :: (Monad m, FormInput v, Read a, Show a, Typeable a) => a -> View v m aSource

wlink :: (Typeable a, Read a, Show a, MonadIO m, Functor m, FormInput view) => a -> view -> View view m aSource

Creates a link wiget. A link can be composed with other widget elements,

returning :: (Typeable a, Read a, Show a, Monad m, FormInput view) => ((a -> String) -> view) -> View view m aSource

When some user interface int return some response to the server, but it is not produced by a form or a link, but for example by an script, returning notify the type checker.

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 a link or a window.location statement in javasCript

wform :: (Monad m, FormInput view) => View view m b -> View view m bSource

Wrap a widget of form element within a form-action element.

firstOf :: (Monoid view, Monad m, Functor m) => [View view m a] -> View view m aSource

Concat a list of widgets of the same type, return a the first validated result

manyOf :: (FormInput view, MonadIO m, Functor m) => [View view m a] -> View view m [a]Source

from a list of widgets, it return the validated ones.

wraw :: Monad m => view -> View view m ()Source

Render raw view formatting. It is useful for displaying information

wrender :: (Monad m, Functor m, Show a, FormInput view) => a -> View view m aSource

Render a Show-able value and return it

FormLet modifiers

validate :: (FormInput view, Monad m) => View view m a -> (a -> WState view m (Maybe view)) -> View view m aSource

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)

noWidget :: (FormInput view, Monad m) => View view m aSource

Empty widget that return Nothing. May be used as "empty boxes" inside larger widgets

waction :: (FormInput view, Monad m) => View view m a -> (a -> FlowM view m b) -> View view m bSource

Actions are callbacks that are executed when a widget is validated. It is useful when the widget is inside widget containers that know nothing about his content.

It returns a result that can be significative or, else, be ignored with <** and **>. An action may or may not initiate his own dialog with the user via ask

wmodify :: (Monad m, FormInput v) => View v m a -> ([v] -> Maybe a -> WState v m ([v], Maybe b)) -> View v m bSource

Caching widgets

cachedWidgetSource

Arguments

:: (MonadIO m, Typeable view, FormInput view, Typeable a, Executable m) 
=> String

The key of the cached object for the retrieval

-> Int

Timeout of the caching. Zero means sessionwide

-> View view Identity a

The cached widget, in the Identity monad

-> View view m a

The cached result

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.XHtm.All
 import Some.Time.Library
 addMessageFlows [(noscript, time)]
 main= run 80 waiMessageFlow
 time=do  ask $ cachedWidget "time" 5
            $ wlink () bold << "the time is " ++ show (execute giveTheTime) ++ " click here"
          time

this pseudocode would update the time every 5 seconds. The execution of the IO computation giveTheTime must be executed inside the cached widget to avoid unnecesary IO executions.

NOTE: cached widgets are shared by all users

wcachedSource

Arguments

:: (MonadIO m, Typeable view, FormInput view, Typeable a, Executable m) 
=> String

The key of the cached object for the retrieval

-> Int

Timeout of the caching. Zero means sessionwide

-> View view Identity a

The cached widget, in the Identity monad

-> View view m a

The cached result

A shorter name for cachedWidget

wfreezeSource

Arguments

:: (MonadIO m, Typeable view, FormInput view, Typeable a, Executable m) 
=> String

The key of the cached object for the retrieval

-> Int

Timeout of the caching. Zero means sessionwide

-> View view m a

The cached widget

-> View view m a

The cached result

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: cached widgets are shared by all users

Widget combinators

(<+>) :: Monad m => View view m a -> View view m b -> View view m (Maybe a, Maybe b)Source

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) -> ..

(|*>) :: (MonadIO m, Functor m, Monoid view) => View view m r -> [View view m r'] -> View view m (Maybe r, Maybe r')Source

Intersperse a widget in a list of widgets. the results is a 2-tuple of both types.

it has a infix priority infixr 5

(|+|) :: (Functor m, Monoid view, MonadIO m) => View view m r -> View view m r' -> View view m (Maybe r, Maybe r')Source

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, Monad m) => View view m a -> View view m b -> View view m bSource

The first elem result (even if it is not validated) is discarded, and the secod is returned . This contrast with the applicative operator *> which fails the whole validation if the validation of the first elem fails.

The first element is displayed however, as 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) => View view m a -> View view m b -> View view m aSource

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

(<|>) :: Alternative f => forall a. f a -> f a -> f a

An associative binary operation

(<*) :: Applicative f => forall a b. f a -> f b -> f a

Sequence actions, discarding the value of the second argument.

(<$>) :: Functor f => (a -> b) -> f a -> f b

An infix synonym for fmap.

(<*>) :: Applicative f => forall a b. f (a -> b) -> f a -> f b

Sequential application.

(>:>) :: Monad m => View v m a -> View v m [a] -> View v m [a]Source

Normalized (convert to ByteString) widget combinators

These dot operators are indentical to the non dot operators, with the addition of the conversion of the arguments to lazy byteStrings

The purpose is to combine heterogeneous formats into byteString-formatted widgets that can be cached with cachedWidget

(.<+>.) :: (Monad m, FormInput v, FormInput v1) => View v m a -> View v1 m b -> View ByteString m (Maybe a, Maybe b)Source

 (.<+>.) x y = normalize x <+> normalize y

(.|*>.) :: (Functor m, MonadIO m, FormInput v, FormInput v1) => View v m r -> [View v1 m r'] -> View ByteString m (Maybe r, Maybe r')Source

 (.|*>.) x y = normalize x |*> map normalize y

(.|+|.) :: (Functor m, MonadIO m, FormInput v, FormInput v1) => View v m r -> View v1 m r' -> View ByteString m (Maybe r, Maybe r')Source

 (.|+|.) x y = normalize x |+| normalize y

(.**>.) :: (Monad m, Functor m, FormInput v, FormInput v1) => View v m a -> View v1 m b -> View ByteString m bSource

 (.**>.) x y = normalize x **> normalize y

(.<**.) :: (Monad m, Functor m, FormInput v, FormInput v1) => View v m a -> View v1 m b -> View ByteString m aSource

 (.<**.) x y = normalize x <** normalize y

(.<|>.) :: (Monad m, Functor m, FormInput v, FormInput v1) => View v m a -> View v1 m a -> View ByteString m aSource

 (.<|>.) x y= normalize x <|> normalize y

Formatting combinators

(<<<) :: (Monad m, Monoid view) => (view -> view) -> View view m a -> View view m aSource

Enclose Widgets within some formating. view is intended to be instantiated to a particular format

This is a widget, which is a table with some links. it returns an Int

it has a infix priority : infixr 5 less than ++> so use parenthesis when appropriate

 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 => View v m a -> v -> View v m aSource

Useful for the creation of pages using two or more views. For example HSP and Html. Because both have ConvertTo instances to ByteString, then it is possible to mix them via normalize:

 normalize widget  <+> normalize widget'

is equivalent to

 widget .<+>. widget'

Append formatting code to a widget

 getString hi <++ H1 << hi there

It has a infix prority: infixr 6 higuer that <<< and most other operators

(++>) :: (Monad m, Monoid view) => view -> View view m a -> View view m aSource

Prepend formatting code to a widget

bold "enter name" ++ getString Nothing

It has a infix prority: infixr 6 higuer that <<< and most other operators

(<!) :: (Monad m, FormInput view) => View view m a -> Attribs -> View view m aSource

Add attributes to the topmost tag of a widget

it has a fixity infix 8

Normalized (convert to ByteString) formatting combinators

Some combinators that convert the formatting of their arguments to lazy byteString

(.<<.) :: FormInput view => (ByteString -> ByteString) -> view -> ByteStringSource

 (.<<.) w x = w $ toByteString x

(.<++.) :: (Monad m, FormInput v, FormInput v') => View v m a -> v' -> View ByteString m aSource

 (.<++.) x v= normalize x <++ toByteString v

(.++>.) :: (Monad m, FormInput v, FormInput v') => v -> View v' m a -> View ByteString m aSource

 (.++>.) v x= toByteString v ++> normalize x

ByteString tags

btag :: String -> Attribs -> ByteString -> ByteStringSource

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.

bhtml :: Attribs -> ByteString -> ByteStringSource

 bhtml ats v= btag "html" ats v

bbody :: Attribs -> ByteString -> ByteStringSource

 bbody ats v= btag "body" ats v

Normalization

flatten :: Flatten (Maybe tree) list => tree -> listSource

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)

Running the flow monad

runFlow :: (FormInput view, Monad m) => FlowM view m () -> Token -> m ()Source

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

runFlowOnce :: (FormInput view, Monad m) => FlowM view m () -> Token -> m ()Source

runFlowIn :: (MonadIO m, FormInput view) => String -> FlowM view (Workflow IO) b -> FlowM view m bSource

Run a persistent flow inside the current flow. It is identified by the procedure and the string identifier. unlike the normal flows, that are infinite loops, runFlowIn executes a finite flow once executed, in subsequent executions the flow will return the stored result without asking again. This is useful for askingstoringretrieving user defined configurations.

step :: (Serialize a, Typeable view, FormInput view, MonadIO m, Typeable a) => FlowM view m a -> FlowM view (Workflow m) aSource

goingBack :: MonadState (MFlowState view) m => m BoolSource

True if the flow is going back (as a result of the back button pressed in the web browser). Usually this chech is nos necessary unless conditional code make it necessary

menu= do
       mop <- getGoStraighTo
       case mop of
        Just goop -> goop
        Nothing -> do
               r <- ask option1 <|> option2
               case r of
                op1 -> setGoStraighTo (Just goop1) >> goop1
                op2 -> setGoStraighTo (Just goop2) >> goop2

This pseudocode 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 restar at the last page visited. No setting is necessary.

breturn :: Monad m => a -> FlowM v m aSource

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.

preventGoingBack :: (Functor m, MonadIO m, FormInput v) => FlowM v m () -> FlowM v m ()Source

Will prevent the backtrack 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 backtracking

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"

Setting parameters

setHeader :: MonadState (MFlowState view) m => (view -> view) -> m ()Source

Set the header-footer that will enclose the widgets. It must be provided in the same formatting than them, altrough 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

setSessionData :: (Typeable a, MonadState (MFlowState view) m) => a -> m ()Source

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

getSessionData :: (Typeable a, MonadState (MFlowState view) m) => m (Maybe a)Source

Get the session data of the desired type if there is any.

getHeader :: Monad m => FlowM view m (view -> view)Source

Return the current header

setTimeouts :: Monad m => Int -> Integer -> FlowM view m ()Source

Set 1) the timeout of the flow execution since the last user interaction. Once passed, the flow executes from the begining. 2). In persistent flows it set the session state timeout for the flow, that is persistent. If the flow is not persistent, it has no effect.

transient flows restart anew. persistent flows (that use step) restart at the las saved execution point, unless the session time has expired for the user.

Cookies

setCookieSource

Arguments

:: MonadState (MFlowState view) m 
=> String

name

-> String

value

-> String

path

-> Maybe Integer

Max-Age in seconds. Nothing for a session cookie

-> m () 

Set an HTTP cookie

Ajax

ajaxSource

Arguments

:: MonadIO m 
=> (String -> View v m ByteString)

user defined procedure, executed in the server.Receives the value of the javascript expression and must return another javascript expression that will be executed in the web browser

-> View v m (String -> String)

returns a function that accept a javascript expression and return a javascript event handler expression that invoques the ajax server procedure

Install the server code and return the client code for an AJAX interaction.

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)]

ajaxSend :: (Read a, MonadIO m) => View v m ByteString -> View v m aSource

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_ :: MonadIO m => View v m ByteString -> View v m ()Source

Like ajaxSend but the result is ignored

Requirements

class Requirements a whereSource

Methods

installRequirements :: (Monad m, FormInput view) => [a] -> m viewSource

data WebRequirement Source

Constructors

JScriptFile String [String]

Script URL and the list of scripts to be executed when loaded

CSSFile String

a CSS file URL

CSS String

a String with a CSS description

JScript String

a string with a valid JavaScript

ServerProc (String, Flow)

a server procedure

requires :: (Typeable a, MonadState (MFlowState view) m, Requirements a) => [a] -> m ()Source

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. Varios widgets in the page can require the same element, MFlow will install it once.

Utility

genNewId :: MonadState (MFlowState view) m => m StringSource

Generate a new string. Useful for creating tag identifiers and other attributes

changeMonad :: (Monad m, Executable m1) => View v m1 a -> View v m aSource

Execute the widget in a monad and return the result in another.

The monster of the deep

data MFlowState view Source

Instances