Support for files in forms
This commit is contained in:
parent
53f7837cff
commit
f49c16c3ba
@ -61,7 +61,8 @@ import Data.Char (isLower, isUpper)
|
|||||||
|
|
||||||
import Data.Serialize
|
import Data.Serialize
|
||||||
import qualified Data.Serialize as Ser
|
import qualified Data.Serialize as Ser
|
||||||
import Network.Wai.Parse
|
import Network.Wai.Parse hiding (FileInfo)
|
||||||
|
import qualified Network.Wai.Parse as NWP
|
||||||
|
|
||||||
#if TEST
|
#if TEST
|
||||||
import Test.Framework (testGroup, Test)
|
import Test.Framework (testGroup, Test)
|
||||||
@ -122,6 +123,7 @@ typeHelper =
|
|||||||
go s@(x:_)
|
go s@(x:_)
|
||||||
| isLower x = VarT $ mkName s
|
| isLower x = VarT $ mkName s
|
||||||
| otherwise = ConT $ mkName s
|
| otherwise = ConT $ mkName s
|
||||||
|
go [] = error "typeHelper: empty string to go"
|
||||||
|
|
||||||
mkYesodGeneral :: String -- ^ argument name
|
mkYesodGeneral :: String -- ^ argument name
|
||||||
-> [String] -- ^ parameters for site argument
|
-> [String] -- ^ parameters for site argument
|
||||||
@ -336,8 +338,8 @@ parseWaiRequest env session' = do
|
|||||||
rbHelper :: W.Request -> IO RequestBodyContents
|
rbHelper :: W.Request -> IO RequestBodyContents
|
||||||
rbHelper = fmap (fix1 *** map fix2) . parseRequestBody lbsSink where
|
rbHelper = fmap (fix1 *** map fix2) . parseRequestBody lbsSink where
|
||||||
fix1 = map (S.toString *** S.toString)
|
fix1 = map (S.toString *** S.toString)
|
||||||
fix2 (x, FileInfo a b c) =
|
fix2 (x, NWP.FileInfo a b c) =
|
||||||
(S.toString x, FileInfo a b c)
|
(S.toString x, FileInfo (S.toString a) (S.toString b) c)
|
||||||
|
|
||||||
-- | Produces a \"compute on demand\" value. The computation will be run once
|
-- | Produces a \"compute on demand\" value. The computation will be run once
|
||||||
-- it is requested, and then the result will be stored. This will happen only
|
-- it is requested, and then the result will be stored. This will happen only
|
||||||
|
|||||||
@ -71,34 +71,36 @@ instance Applicative FormResult where
|
|||||||
_ <*> _ = FormMissing
|
_ <*> _ = FormMissing
|
||||||
|
|
||||||
newtype Form sub y a = Form
|
newtype Form sub y a = Form
|
||||||
{ deform :: Env -> Incr (GHandler sub y) (FormResult a, Hamlet (Routes y))
|
{ deform :: Env -> FileEnv -> Incr (GHandler sub y) (FormResult a, Hamlet (Routes y))
|
||||||
}
|
}
|
||||||
type Formlet sub y a = Maybe a -> Form sub y a
|
type Formlet sub y a = Maybe a -> Form sub y a
|
||||||
|
|
||||||
type Env = [(String, String)]
|
type Env = [(String, String)]
|
||||||
|
type FileEnv = [(String, FileInfo)]
|
||||||
|
|
||||||
instance Functor (Form sub url) where
|
instance Functor (Form sub url) where
|
||||||
fmap f (Form g) = Form $ \env -> liftM (first $ fmap f) (g env)
|
fmap f (Form g) = Form $ \env fe -> liftM (first $ fmap f) (g env fe)
|
||||||
|
|
||||||
instance Applicative (Form sub url) where
|
instance Applicative (Form sub url) where
|
||||||
pure a = Form $ const $ return (pure a, mempty)
|
pure a = Form $ const $ const $ return (pure a, mempty)
|
||||||
(Form f) <*> (Form g) = Form $ \env -> do
|
(Form f) <*> (Form g) = Form $ \env fe -> do
|
||||||
(f1, f2) <- f env
|
(f1, f2) <- f env fe
|
||||||
(g1, g2) <- g env
|
(g1, g2) <- g env fe
|
||||||
return (f1 <*> g1, f2 `mappend` g2)
|
return (f1 <*> g1, f2 `mappend` g2)
|
||||||
|
|
||||||
runFormGeneric :: Env
|
runFormGeneric :: Env
|
||||||
|
-> FileEnv
|
||||||
-> Form sub y a
|
-> Form sub y a
|
||||||
-> GHandler sub y (FormResult a, Hamlet (Routes y))
|
-> GHandler sub y (FormResult a, Hamlet (Routes y))
|
||||||
runFormGeneric env f = evalStateT (deform f env) 1
|
runFormGeneric env fe f = evalStateT (deform f env fe) 1
|
||||||
|
|
||||||
-- | Run a form against POST parameters.
|
-- | Run a form against POST parameters.
|
||||||
runFormPost :: Form sub y a
|
runFormPost :: Form sub y a
|
||||||
-> GHandler sub y (FormResult a, Hamlet (Routes y))
|
-> GHandler sub y (FormResult a, Hamlet (Routes y))
|
||||||
runFormPost f = do
|
runFormPost f = do
|
||||||
rr <- getRequest
|
rr <- getRequest
|
||||||
(pp, _) <- liftIO $ reqRequestBody rr
|
(pp, files) <- liftIO $ reqRequestBody rr
|
||||||
runFormGeneric pp f
|
runFormGeneric pp files f
|
||||||
|
|
||||||
-- | Run a form against POST parameters, disregarding the resulting HTML and
|
-- | Run a form against POST parameters, disregarding the resulting HTML and
|
||||||
-- returning an error response on invalid input.
|
-- returning an error response on invalid input.
|
||||||
@ -120,7 +122,7 @@ runFormGet :: Form sub y a
|
|||||||
-> GHandler sub y (FormResult a, Hamlet (Routes y))
|
-> GHandler sub y (FormResult a, Hamlet (Routes y))
|
||||||
runFormGet f = do
|
runFormGet f = do
|
||||||
gs <- reqGetParams `fmap` getRequest
|
gs <- reqGetParams `fmap` getRequest
|
||||||
runFormGeneric gs f
|
runFormGeneric gs [] f
|
||||||
|
|
||||||
type Incr = StateT Int
|
type Incr = StateT Int
|
||||||
|
|
||||||
@ -134,7 +136,7 @@ incr = do
|
|||||||
input :: (String -> String -> Hamlet (Routes y))
|
input :: (String -> String -> Hamlet (Routes y))
|
||||||
-> Maybe String
|
-> Maybe String
|
||||||
-> Form sub y String
|
-> Form sub y String
|
||||||
input mkXml val = Form $ \env -> do
|
input mkXml val = Form $ \env _ -> do
|
||||||
i <- incr
|
i <- incr
|
||||||
let i' = show i
|
let i' = show i
|
||||||
let param = lookup i' env
|
let param = lookup i' env
|
||||||
@ -142,7 +144,7 @@ input mkXml val = Form $ \env -> do
|
|||||||
return (maybe FormMissing FormSuccess param, xml)
|
return (maybe FormMissing FormSuccess param, xml)
|
||||||
|
|
||||||
check :: Form sub url a -> (a -> Either [String] b) -> Form sub url b
|
check :: Form sub url a -> (a -> Either [String] b) -> Form sub url b
|
||||||
check (Form form) f = Form $ \env -> liftM (first go) (form env)
|
check (Form form) f = Form $ \env fe -> liftM (first go) (form env fe)
|
||||||
where
|
where
|
||||||
go FormMissing = FormMissing
|
go FormMissing = FormMissing
|
||||||
go (FormFailure x) = FormFailure x
|
go (FormFailure x) = FormFailure x
|
||||||
@ -169,7 +171,7 @@ sealRow label getVal val =
|
|||||||
|
|
||||||
sealForm :: ([String] -> Hamlet (Routes y) -> Hamlet (Routes y))
|
sealForm :: ([String] -> Hamlet (Routes y) -> Hamlet (Routes y))
|
||||||
-> Form sub y a -> Form sub y a
|
-> Form sub y a -> Form sub y a
|
||||||
sealForm wrapper (Form form) = Form $ \env -> liftM go (form env)
|
sealForm wrapper (Form form) = Form $ \env fe -> liftM go (form env fe)
|
||||||
where
|
where
|
||||||
go (res, xml) = (res, wrapper (toList res) xml)
|
go (res, xml) = (res, wrapper (toList res) xml)
|
||||||
toList (FormFailure errs) = errs
|
toList (FormFailure errs) = errs
|
||||||
@ -181,22 +183,22 @@ sealFormlet wrapper formlet initVal = sealForm wrapper $ formlet initVal
|
|||||||
|
|
||||||
-------- Prebuilt
|
-------- Prebuilt
|
||||||
optionalField :: String -> Form sub master (Maybe String)
|
optionalField :: String -> Form sub master (Maybe String)
|
||||||
optionalField n = Form $ \env ->
|
optionalField n = Form $ \env _ ->
|
||||||
return (FormSuccess $ lookup n env, mempty) -- FIXME
|
return (FormSuccess $ lookup n env, mempty) -- FIXME
|
||||||
|
|
||||||
requiredField :: String -> Form sub master String
|
requiredField :: String -> Form sub master String
|
||||||
requiredField n = Form $ \env ->
|
requiredField n = Form $ \env _ ->
|
||||||
return (maybe FormMissing FormSuccess $ lookup n env, mempty) -- FIXME
|
return (maybe FormMissing FormSuccess $ lookup n env, mempty) -- FIXME
|
||||||
|
|
||||||
notEmptyField :: String -> Form sub master String
|
notEmptyField :: String -> Form sub master String
|
||||||
notEmptyField n = Form $ \env -> return
|
notEmptyField n = Form $ \env _ -> return
|
||||||
(case lookup n env of
|
(case lookup n env of
|
||||||
Nothing -> FormMissing
|
Nothing -> FormMissing
|
||||||
Just "" -> FormFailure [n ++ ": You must provide a non-empty string"]
|
Just "" -> FormFailure [n ++ ": You must provide a non-empty string"]
|
||||||
Just x -> FormSuccess x, mempty) -- FIXME
|
Just x -> FormSuccess x, mempty) -- FIXME
|
||||||
|
|
||||||
boolField :: String -> Form sub master Bool
|
boolField :: String -> Form sub master Bool
|
||||||
boolField n = Form $ \env -> return
|
boolField n = Form $ \env _ -> return
|
||||||
(FormSuccess $ isJust $ lookup n env, mempty) -- FIXME
|
(FormSuccess $ isJust $ lookup n env, mempty) -- FIXME
|
||||||
|
|
||||||
class Formable a where
|
class Formable a where
|
||||||
@ -299,7 +301,7 @@ instance Formable (Maybe Int64) where
|
|||||||
[] -> Left ["Invalid integer"]
|
[] -> Left ["Invalid integer"]
|
||||||
|
|
||||||
instance Formable Bool where
|
instance Formable Bool where
|
||||||
formable x = Form $ \env -> do
|
formable x = Form $ \env _ -> do
|
||||||
i <- incr
|
i <- incr
|
||||||
let i' = show i
|
let i' = show i
|
||||||
let param = lookup i' env
|
let param = lookup i' env
|
||||||
|
|||||||
@ -46,7 +46,6 @@ import qualified Network.Wai as W
|
|||||||
import qualified Data.ByteString.Lazy as BL
|
import qualified Data.ByteString.Lazy as BL
|
||||||
import "transformers" Control.Monad.IO.Class
|
import "transformers" Control.Monad.IO.Class
|
||||||
import Control.Monad (liftM)
|
import Control.Monad (liftM)
|
||||||
import Network.Wai.Parse
|
|
||||||
import Control.Monad.Instances () -- I'm missing the instance Monad ((->) r
|
import Control.Monad.Instances () -- I'm missing the instance Monad ((->) r
|
||||||
import Data.Maybe (listToMaybe)
|
import Data.Maybe (listToMaybe)
|
||||||
|
|
||||||
@ -85,9 +84,16 @@ waiRequest = reqWaiRequest `liftM` getRequest
|
|||||||
-- | A tuple containing both the POST parameters and submitted files.
|
-- | A tuple containing both the POST parameters and submitted files.
|
||||||
type RequestBodyContents =
|
type RequestBodyContents =
|
||||||
( [(ParamName, ParamValue)]
|
( [(ParamName, ParamValue)]
|
||||||
, [(ParamName, FileInfo BL.ByteString)]
|
, [(ParamName, FileInfo)]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
data FileInfo = FileInfo
|
||||||
|
{ fileName :: String
|
||||||
|
, fileContentType :: String
|
||||||
|
, fileContent :: BL.ByteString
|
||||||
|
}
|
||||||
|
deriving (Eq, Show)
|
||||||
|
|
||||||
-- | The parsed request information.
|
-- | The parsed request information.
|
||||||
data Request = Request
|
data Request = Request
|
||||||
{ reqGetParams :: [(ParamName, ParamValue)]
|
{ reqGetParams :: [(ParamName, ParamValue)]
|
||||||
@ -134,13 +140,13 @@ lookupPostParam = liftM listToMaybe . lookupPostParams
|
|||||||
-- | Lookup for POSTed files.
|
-- | Lookup for POSTed files.
|
||||||
lookupFile :: (MonadIO m, RequestReader m)
|
lookupFile :: (MonadIO m, RequestReader m)
|
||||||
=> ParamName
|
=> ParamName
|
||||||
-> m (Maybe (FileInfo BL.ByteString))
|
-> m (Maybe FileInfo)
|
||||||
lookupFile = liftM listToMaybe . lookupFiles
|
lookupFile = liftM listToMaybe . lookupFiles
|
||||||
|
|
||||||
-- | Lookup for POSTed files.
|
-- | Lookup for POSTed files.
|
||||||
lookupFiles :: (MonadIO m, RequestReader m)
|
lookupFiles :: (MonadIO m, RequestReader m)
|
||||||
=> ParamName
|
=> ParamName
|
||||||
-> m [FileInfo BL.ByteString]
|
-> m [FileInfo]
|
||||||
lookupFiles pn = do
|
lookupFiles pn = do
|
||||||
rr <- getRequest
|
rr <- getRequest
|
||||||
(_, files) <- liftIO $ reqRequestBody rr
|
(_, files) <- liftIO $ reqRequestBody rr
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user