htmlField and fixed auth

This commit is contained in:
Michael Snoyman 2010-07-02 07:18:39 +03:00
parent be3235a0b2
commit f2e71a6c00
4 changed files with 73 additions and 21 deletions

View File

@ -8,18 +8,26 @@
-- | Parse forms (and query strings). -- | Parse forms (and query strings).
module Yesod.Form module Yesod.Form
( -- * Data types ( -- * Data types
Form (..) GForm (..)
, Form
, FormField
, FormResult (..) , FormResult (..)
-- * Unwrapping functions -- * Unwrapping functions
, runFormGet , runFormGet
, runFormPost , runFormPost
, runFormGet' , runFormGet'
, runFormPost' , runFormPost'
-- * Type classes
, IsForm (..)
, IsFormField (..)
-- * Pre-built fields
, requiredField , requiredField
, stringField , stringField
, intField , intField
, dayField , dayField
, boolField , boolField
, htmlField
, stringInput
, fieldsToTable , fieldsToTable
{- FIXME {- FIXME
-- * Create your own formlets -- * Create your own formlets
@ -60,7 +68,7 @@ import Data.Char (isAlphaNum, toUpper, isUpper)
import Data.Maybe (isJust) import Data.Maybe (isJust)
import Web.Routes.Quasi (SinglePiece) import Web.Routes.Quasi (SinglePiece)
import Data.Int (Int64) import Data.Int (Int64)
import qualified Data.ByteString.Lazy.UTF8 import qualified Data.ByteString.Lazy.UTF8 as U
import Yesod.Widget import Yesod.Widget
data FormResult a = FormMissing data FormResult a = FormMissing
@ -81,8 +89,8 @@ instance Applicative FormResult where
data Enctype = UrlEncoded | Multipart data Enctype = UrlEncoded | Multipart
instance Show Enctype where instance Show Enctype where
show UrlEncoded = "urlencoded" show UrlEncoded = "application/x-www-form-urlencoded"
show Multipart = "multipart/mimetype" -- FIXME show Multipart = "multipart/form-data"
instance Monoid Enctype where instance Monoid Enctype where
mempty = UrlEncoded mempty = UrlEncoded
mappend UrlEncoded UrlEncoded = UrlEncoded mappend UrlEncoded UrlEncoded = UrlEncoded
@ -133,6 +141,11 @@ fieldsToTable = mapM_ go
$fiErrors.fi$ $fiErrors.fi$
|] |]
class IsForm a where
toForm :: Maybe a -> Form sub y a
class IsFormField a where
toFormField :: Html () -> Html () -> Maybe a -> FormField sub y a
requiredField :: FieldProfile sub y a requiredField :: FieldProfile sub y a
-> Html () -> Html () -> Maybe a -> FormField sub y a -> Html () -> Html () -> Maybe a -> FormField sub y a
requiredField (FieldProfile parse render mkXml w) label tooltip orig = requiredField (FieldProfile parse render mkXml w) label tooltip orig =
@ -177,6 +190,8 @@ stringField = FieldProfile
|] |]
, fpWidget = \_name -> return () , fpWidget = \_name -> return ()
} }
instance IsFormField String where
toFormField = requiredField stringField
intField :: FieldProfile sub y Int intField :: FieldProfile sub y Int
intField = FieldProfile intField = FieldProfile
@ -187,6 +202,8 @@ intField = FieldProfile
|] |]
, fpWidget = \_name -> return () , fpWidget = \_name -> return ()
} }
instance IsFormField Int where
toFormField = requiredField intField
dayField :: FieldProfile sub y Day dayField :: FieldProfile sub y Day
dayField = FieldProfile dayField = FieldProfile
@ -202,6 +219,8 @@ dayField = FieldProfile
addStylesheetRemote "http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.1/themes/cupertino/jquery-ui.css" addStylesheetRemote "http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.1/themes/cupertino/jquery-ui.css"
addHead [$hamlet|%script $$(function(){$$("#$string.name$").datepicker({dateFormat:'yy-mm-dd'})})|] addHead [$hamlet|%script $$(function(){$$("#$string.name$").datepicker({dateFormat:'yy-mm-dd'})})|]
} }
instance IsFormField Day where
toFormField = requiredField dayField
boolField :: Html () -> Html () -> Maybe Bool -> FormField sub y Bool boolField :: Html () -> Html () -> Maybe Bool -> FormField sub y Bool
boolField label tooltip orig = GForm $ \env _ -> do boolField label tooltip orig = GForm $ \env _ -> do
@ -224,6 +243,23 @@ boolField label tooltip orig = GForm $ \env _ -> do
_ -> string "" _ -> string ""
} }
return (res, [fi], UrlEncoded) return (res, [fi], UrlEncoded)
instance IsFormField Bool where
toFormField = boolField
htmlField :: FieldProfile sub y (Html ())
htmlField = FieldProfile
{ fpParse = Right . preEscapedString
, fpRender = U.toString . renderHtml
, fpHamlet = \name val isReq -> [$hamlet|
%textarea#$name$!name=$name$ $val$
|]
, fpWidget = \name -> do
addScriptRemote "http://js.nicedit.com/nicEdit-latest.js"
addHead [$hamlet|%script bkLib.onDomLoaded(function(){new nicEditor({fullPanel:true}).panelInstance("$string.name$")})|]
addStyle [$hamlet|\#$string.name${min-width:400px;min-height:300px}|]
}
instance IsFormField (Html ()) where
toFormField = requiredField htmlField
readMay :: Read a => String -> Maybe a readMay :: Read a => String -> Maybe a
readMay s = case reads s of readMay s = case reads s of
@ -232,6 +268,17 @@ readMay s = case reads s of
--------------------- End prebuilt forms --------------------- End prebuilt forms
--------------------- Begin prebuilt inputs
stringInput :: String -> Form sub master String
stringInput n = GForm $ \env _ -> return
(case lookup n env of
Nothing -> FormMissing
Just "" -> FormFailure [n ++ ": You must provide a non-empty string"]
Just x -> FormSuccess x, mempty, UrlEncoded)
--------------------- End prebuilt inputs
incr :: Monad m => StateT Int m String incr :: Monad m => StateT Int m String
incr = do incr = do
i <- get i <- get
@ -324,7 +371,7 @@ instance Formable (Maybe String) where
instance Formable (Html ()) where instance Formable (Html ()) where
formable = fmap preEscapedString formable = fmap preEscapedString
. input go . input go
. fmap (Data.ByteString.Lazy.UTF8.toString . renderHtml) . fmap (U.toString . renderHtml)
where where
go name val = [$hamlet|%textarea!name=$string.name$ $string.val$|] go name val = [$hamlet|%textarea!name=$string.name$ $string.val$|]

View File

@ -182,7 +182,7 @@ $maybe message msg
getOpenIdForward :: GHandler Auth master () getOpenIdForward :: GHandler Auth master ()
getOpenIdForward = do getOpenIdForward = do
testOpenId testOpenId
oid <- runFormGet' $ requiredField "openid" oid <- runFormGet' $ stringInput "openid"
render <- getUrlRender render <- getUrlRender
toMaster <- getRouteToMaster toMaster <- getRouteToMaster
let complete = render $ toMaster OpenIdComplete let complete = render $ toMaster OpenIdComplete
@ -302,7 +302,7 @@ getEmailRegisterR = do
postEmailRegisterR :: YesodAuth master => GHandler Auth master RepHtml postEmailRegisterR :: YesodAuth master => GHandler Auth master RepHtml
postEmailRegisterR = do postEmailRegisterR = do
ae <- getAuthEmailSettings ae <- getAuthEmailSettings
email <- runFormPost' $ notEmptyField "email" -- FIXME checkEmail email <- runFormPost' $ stringInput "email" -- FIXME checkEmail
y <- getYesod y <- getYesod
mecreds <- liftIO $ getEmailCreds ae email mecreds <- liftIO $ getEmailCreds ae email
(lid, verKey) <- (lid, verKey) <-
@ -367,8 +367,8 @@ postEmailLoginR :: YesodAuth master => GHandler Auth master ()
postEmailLoginR = do postEmailLoginR = do
ae <- getAuthEmailSettings ae <- getAuthEmailSettings
(email, pass) <- runFormPost' $ (,) (email, pass) <- runFormPost' $ (,)
<$> notEmptyField "email" -- FIXME valid e-mail? <$> stringInput "email" -- FIXME valid e-mail?
<*> requiredField "password" <*> stringInput "password"
y <- getYesod y <- getYesod
mecreds <- liftIO $ getEmailCreds ae email mecreds <- liftIO $ getEmailCreds ae email
let mlid = let mlid =
@ -420,8 +420,8 @@ postEmailPasswordR :: YesodAuth master => GHandler Auth master ()
postEmailPasswordR = do postEmailPasswordR = do
ae <- getAuthEmailSettings ae <- getAuthEmailSettings
(new, confirm) <- runFormPost' $ (,) (new, confirm) <- runFormPost' $ (,)
<$> notEmptyField "new" <$> stringInput "new"
<*> notEmptyField "confirm" <*> stringInput "confirm"
toMaster <- getRouteToMaster toMaster <- getRouteToMaster
when (new /= confirm) $ do when (new /= confirm) $ do
setMessage $ string "Passwords did not match, please try again" setMessage $ string "Passwords did not match, please try again"
@ -495,7 +495,7 @@ getFacebookR = do
render <- getUrlRender render <- getUrlRender
tm <- getRouteToMaster tm <- getRouteToMaster
let fb = Facebook.Facebook cid secret $ render $ tm FacebookR let fb = Facebook.Facebook cid secret $ render $ tm FacebookR
code <- runFormGet' $ requiredField "code" code <- runFormGet' $ stringInput "code"
at <- liftIO $ Facebook.getAccessToken fb code at <- liftIO $ Facebook.getAccessToken fb code
so <- liftIO $ Facebook.getGraphData at "me" so <- liftIO $ Facebook.getGraphData at "me"
let c = fromMaybe (error "Invalid response from Facebook") $ do let c = fromMaybe (error "Invalid response from Facebook") $ do

View File

@ -19,7 +19,7 @@ import Text.Hamlet
import Yesod.Form import Yesod.Form
import Data.Monoid (mempty) import Data.Monoid (mempty)
class Formable a => Item a where class IsForm a => Item a where
itemTitle :: a -> String itemTitle :: a -> String
data Crud master item = Crud data Crud master item = Crud
@ -133,7 +133,7 @@ crudHelper
-> GHandler (Crud master a) master RepHtml -> GHandler (Crud master a) master RepHtml
crudHelper title me isPost = do crudHelper title me isPost = do
crud <- getYesodSub crud <- getYesodSub
(errs, form) <- runFormPost $ formable $ fmap snd me (errs, form, enctype) <- runFormPost $ toForm $ fmap snd me
toMaster <- getRouteToMaster toMaster <- getRouteToMaster
case (isPost, errs) of case (isPost, errs) of
(True, FormSuccess a) -> do (True, FormSuccess a) -> do
@ -146,21 +146,21 @@ crudHelper title me isPost = do
$ toSinglePiece eid $ toSinglePiece eid
_ -> return () _ -> return ()
applyLayoutW $ do applyLayoutW $ do
wrapWidget (wrapForm toMaster) form wrapWidget (wrapForm toMaster enctype) form
setTitle $ string title setTitle $ string title
where where
wrapForm toMaster form = [$hamlet| wrapForm toMaster enctype form = [$hamlet|
%p %p
%a!href=@toMaster.CrudListR@ Return to list %a!href=@toMaster.CrudListR@ Return to list
%h1 $string.title$ %h1 $string.title$
%form!method=post %form!method=post!enctype=$string.show.enctype$
%table %table
^form^ ^form^
%tr %tr
%td!colspan=2 %td!colspan=2
%input!type=submit %input!type=submit
$maybe me e $maybe me e
\ \ $
%a!href=@toMaster.CrudDeleteR.toSinglePiece.fst.e@ Delete %a!href=@toMaster.CrudDeleteR.toSinglePiece.fst.e@ Delete
|] |]

View File

@ -34,12 +34,17 @@ getRootR = applyLayoutW $ wrapWidget wrapper $ do
addHead [$hamlet|%meta!keywords=haskell|] addHead [$hamlet|%meta!keywords=haskell|]
handleFormR = do handleFormR = do
(res, form, enctype) <- runFormPost $ (,,,,) (res, form, enctype) <- runFormPost $ (,,,,,)
<$> requiredField stringField (string "My Field") (string "Some tooltip info") Nothing <$> requiredField stringField (string "My Field") (string "Some tooltip info") Nothing
<*> requiredField stringField (string "Another field") (string "") (Just "some default text") <*> requiredField stringField (string "Another field") (string "") (Just "some default text")
<*> requiredField intField (string "A number field") (string "some nums") (Just 5) <*> requiredField intField (string "A number field") (string "some nums") (Just 5)
<*> requiredField dayField (string "A day field") (string "") Nothing <*> requiredField dayField (string "A day field") (string "") Nothing
<*> boolField (string "A checkbox") (string "") (Just False) <*> boolField (string "A checkbox") (string "") (Just False)
<*> requiredField htmlField (string "HTML") (string "")
(Just $ string "You can put rich text here")
let mhtml = case res of
FormSuccess (_, _, _, _, _, x) -> Just x
_ -> Nothing
applyLayoutW $ do applyLayoutW $ do
addStyle [$hamlet|\.tooltip{color:#666;font-style:italic}|] addStyle [$hamlet|\.tooltip{color:#666;font-style:italic}|]
flip wrapWidget (fieldsToTable form) $ \h -> [$hamlet| flip wrapWidget (fieldsToTable form) $ \h -> [$hamlet|
@ -49,8 +54,8 @@ handleFormR = do
%tr %tr
%td!colspan=2 %td!colspan=2
%input!type=submit %input!type=submit
%h3 $maybe mhtml html
Result: $string.show.res$ $html$
|] |]
setTitle $ string "Form" setTitle $ string "Form"