{-# LANGUAGE UndecidableInstances , TypeSynonymInstances , MultiParamTypeClasses , DeriveDataTypeable , FlexibleInstances #-} module MFlow.Hack( module MFlow.Cookies ,module MFlow ,hackMessageFlow) where import Data.Typeable import Hack import Control.Concurrent.MVar(modifyMVar_, readMVar) import Control.Monad(when) import Data.ByteString.Lazy.Char8 as B(pack, unpack, length, ByteString) import Control.Concurrent(ThreadId(..)) import System.IO.Unsafe import Control.Concurrent.MVar import Control.Exception import qualified Data.Map as M import Data.TCache import Control.Workflow.Text import MFlow import MFlow.Cookies import MFlow.Hack.Response import Data.Monoid --(!>)= flip trace assocList ->> k = getParam1 assocList k instance Processable Env where pwfname env= if null sc then "noscript" else sc where sc= tail $ pathInfo env -- takeWhile (/= '?') $ queryString env puser env = case lookup "cookieuser" $ http env of Nothing -> "nouser" Just user -> user pind env= case lookup "flow" $ http env of Nothing -> error ": No FlowID" Just fl -> fl getParams env= http env -- getServer env= serverName env -- getPath env= pathInfo env -- getPort env= serverPort env data Flow= Flow !Int deriving (Read, Show, Typeable) instance Indexable Flow where key _= "Flow" newFlow= do fl <- atomically $ do withSTMResources [Flow undefined] $ \m -> let (add, ret)= case m of [Just (Flow n)] -> ([Flow $ n+1],n) [Nothing] -> ([Flow 1],0) in resources{toAdd=add, toReturn= ret} return $ show fl --------------------------------------------- instance ConvertTo String TResp where convert = TResp . pack instance ConvertTo ByteString TResp where convert = TResp --instance ConvertTo Response TResp where -- convert= TResp -- instance ConvertTo Error TResp where convert (Error e)= TResp $ pack e instance ToResponse v =>ConvertTo (HttpData v) TResp where convert= TRespR -- --instance Typeable (HSP XML) where -- typeOf = \_ -> mkTyConApp (mkTyCon "HSP") [mkTyConApp ( mkTyCon "XML") []] -- -- -- --instance ConvertTo (HSP XML) TResp where -- convert xml=unsafePerformIO $ do -- (_,html) <- evalHSP Nothing xml -- return . TResp . pack $ renderAsHTML html -- -- -- --instance Typeable X.Html where -- typeOf = \_ -> mkTyConApp (mkTyCon "Text.XHtml.Transitional.Html") [] -- -- -- -- -- --instance ConvertTo X.Html TResp where -- convert = TResp . pack . X.showHtml -- webScheduler :: -- (Typeable c, ToResponse c) => Env -> ProcList -> IO (TResp, ThreadId) webScheduler = msgScheduler eitherToError (Right x)= x eitherToError (Left e)= error $ "eitherToError " ++ show e --theDir= unsafePerformIO getCurrentDirectory wFMiddleware :: (Env -> Bool) -> (Env-> IO Response) -> (Env -> IO Response) wFMiddleware filter f = \ env -> if filter env then hackWorkflow env else f env -- | An instance of the abstract "MFlow" scheduler to the Hack interface -- it accept the list of processes being scheduled and return a hack handler -- -- Example: -- -- @main= do -- -- putStrLn $ options messageFlows -- 'run' 80 $ 'hackMessageFlow' messageFlows -- where -- messageFlows= [(\"main\", 'runFlow' flowname ) -- ,(\"hello\", 'stateless' statelesproc) -- ,(\"trans\", 'transient' $ runflow transientflow] -- options msgs= \"in the browser choose\\n\\n\" ++ -- concat [ "http:\/\/server\/"++ i ++ "\n" | (i,_) \<- msgs] -- @ hackMessageFlow :: ProcList -> (Env -> IO Response) hackMessageFlow messageFlows = unsafePerformIO (addMessageFlows messageFlows) `seq` wFMiddleware (\env -> (pwfname env) `elem` paths) (\env -> defaultResponse $ "usage: http//server/" ++ show paths ++ "?verb") where (paths,_)= unzip messageFlows splitPath ""= ("","","") splitPath str= let strr= reverse str (ext, rest)= span (/= '.') strr (mod, path)= span(/='/') $ tail rest in (tail $ reverse path, reverse mod, reverse ext) hackWorkflow :: Env -> IO Response hackWorkflow req1= do let httpreq1= http req1 -- !> (show req1) let cookies= getCookies httpreq1 (flow , retcookies) <- case lookup "flow" cookies of Just fl -> return (fl, []) Nothing -> do fl <- newFlow return (fl, [("flow", fl, "/",Nothing)]) {- putStateCookie req1 cookies let retcookies= case getStateCookie req1 of Nothing -> retcookies1 Just ck -> ck:retcookies1 -} let [(input, str)]= case ( requestMethod req1, lookup "Content-Type" httpreq1 ) of (POST,Just "application/x-www-form-urlencoded") -> urlDecode . unpack . hackInput $ req1 (GET, _) -> urlDecode . queryString $ req1 _ -> [([("","")],"")] let req = case retcookies of [] -> req1{http= input ++ cookies ++ http req1} -- !> "REQ" _ -> req1{http= ("flow", flow): input ++ cookies ++ http req1} -- !> "REQ" wfs <- getMessageFlows (resp',th) <- webScheduler req wfs let resp''= toResponse resp' let headers1= case retcookies of [] -> headers resp''; _ -> ctype : cookieHeaders retcookies let resp = resp''{status=200, headers= headers1 {-,("Content-Length",show $ B.length x) -}} return resp ------persistent state in cookies (not tested) tvresources :: MVar (Maybe ( M.Map string string)) tvresources= unsafePerformIO $ newMVar Nothing statCookieName= "stat" putStateCookie req cookies= case lookup statCookieName cookies of Nothing -> return () Just (statCookieName, str , "/", _) -> modifyMVar_ tvresources $ \mmap -> case mmap of Just map -> return $ Just $ M.insert (keyResource req) str map Nothing -> return $ Just $ M.fromList [((keyResource req), str) ] getStateCookie req= do mr<- readMVar tvresources case mr of Nothing -> return Nothing Just map -> case M.lookup (keyResource req) map of Nothing -> return Nothing Just str -> do swapMVar tvresources Nothing return $ Just (statCookieName, str , "/") {- persistInCookies= setPersist PersistStat{readStat=readResource, writeStat=writeResource, deleteStat=deleteResource} where writeResource stat= modifyMVar_ tvresources $ \mmap -> case mmap of Just map-> return $ Just $ M.insert (keyResource stat) (serialize stat) map Nothing -> return $ Just $ M.fromList [((keyResource stat), (serialize stat)) ] readResource stat= do mstr <- withMVar tvresources $ \mmap -> case mmap of Just map -> return $ M.lookup (keyResource stat) map Nothing -> return Nothing case mstr of Nothing -> return Nothing Just str -> return $ deserialize str deleteResource stat= modifyMVar_ tvresources $ \mmap-> case mmap of Just map -> return $ Just $ M.delete (keyResource stat) map Nothing -> return $ Nothing -} -- | Users -- --instance Display String TResp where -- column vs= concat ["" ++ v ++"" | v <- vs] -- row vs= concat ["" ++ v ++ "" | v <- vs] -- hsep = "$nbsp;" -- vsep = "
" -- input n t v f= "" -- textarea name text= "" -- -- option name list msel= "" -- where -- selected msel n= if Just n == msel then " \"selected\" " else "" -- -- option1 name list msel= "" -- where -- selected msel n= if Just n == msel then " \"selected\" " else "" -- -- -- -- formAction action form = "
"++ form ++ input "cancel" "button" "cancel" False++ input "submit" "submit" "submit" False ++ "
" -- fromString s= s -- bold x= "" ++ x ++ "" -- fs x= "

" ++ x ++ "

" -- ts x= "

" ++ x ++ "

" -- style st content = " "++ content ++"" -- -- table title head rows= -- ""++ -- (if not . null $ title then "" else "") ++ -- (if not . null $ head then ""++concat [""| h <- head] ++ "" else "")++ -- (concat ["" ++ concat[""| v <- row] ++ "" | row <- rows]) ++ -- "
" ++ title ++ "
"++ h ++ "
"++ v ++ "
" -- -- ---- textarea name value= ""++value++ "" ---- option1 name options option= ---- "< input type=\"radio\" checked=\""++option++"\" name=\""++name++"\" value="approbal" -- -- -- link (Verb v) str = ""++ str ++"" {- instance Monoid (HSP XML) where mempty = mappend x y= <% x %> <% y %> instance Display (HSP XML) TResp where column vs= <% v %> | v <- vs] %>
<% [
row vs= <% [ <% v %> | v <- vs] %> table title caption rows= {- <% when (not . null $ title) -} -- %> {-<% when (not . null $ caption)-} <%[| cap <- caption] %> -- %> <%[ <% [| v <- row] %> | row <- rows] %>
<% title %>
<%cap %>
<% v %>
hsep =   style st content = <% content %> vsep =
fromString s = <% s %> -- option (Verb v) view1= <% view1 %> input typ name value= -}