diff --git a/Yesod/Form.hs b/Yesod/Form.hs index 8db1f2ef..237ddeb1 100644 --- a/Yesod/Form.hs +++ b/Yesod/Form.hs @@ -4,6 +4,7 @@ {-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE CPP #-} +{-# LANGUAGE OverloadedStrings #-} -- | Parse forms (and query strings). module Yesod.Form ( -- * Data types @@ -63,6 +64,8 @@ import Data.Char (toUpper, isUpper) import Control.Arrow ((&&&)) import Data.List (group, sort) import Data.Monoid (mempty) +import Data.Text (Text) +import Text.Blaze (toHtml) #if __GLASGOW_HASKELL__ >= 700 #define HAMLET hamlet @@ -88,7 +91,7 @@ fieldsToTable = mapFormXml $ mapM_ go $maybe err <- fiErrors fi #{err} |] - clazz fi = if fiRequired fi then "required" else "optional" + clazz fi = if fiRequired fi then "required" else "optional" :: Text -- | Display the label, tooltip, input code and errors in a single div. fieldsToDivs :: FormField sub y a -> Form sub y a @@ -102,7 +105,7 @@ fieldsToDivs = mapFormXml $ mapM_ go $maybe err <- fiErrors fi
#{err} |] - clazz fi = if fiRequired fi then "required" else "optional" + clazz fi = if fiRequired fi then "required" else "optional" :: Text -- | Run a form against POST parameters, without CSRF protection. runFormPostNoNonce :: GForm s m xml a -> GHandler s m (FormResult a, xml, Enctype) @@ -133,7 +136,7 @@ runFormPost f = do |] -nonceName :: String +nonceName :: Text nonceName = "_nonce" -- | Run a form against POST parameters. Please note that this does not provide @@ -258,7 +261,7 @@ mkToForm = just <- [|pure|] nothing <- [|Nothing|] let just' = just `AppE` ConE (mkName $ entityName t) - string' <- [|string|] + string' <- [|toHtml|] ftt <- [|fieldsToTable|] ffs' <- [|FormFieldSettings|] let stm "" = nothing @@ -306,6 +309,6 @@ toLabel (x:rest) = toUpper x : go rest | isUpper c = ' ' : c : go cs | otherwise = c : go cs -formFailures :: FormResult a -> Maybe [String] +formFailures :: FormResult a -> Maybe [Text] formFailures (FormFailure x) = Just x formFailures _ = Nothing diff --git a/Yesod/Form/Class.hs b/Yesod/Form/Class.hs index 290b15d7..d6af8096 100644 --- a/Yesod/Form/Class.hs +++ b/Yesod/Form/Class.hs @@ -12,16 +12,24 @@ import Yesod.Form.Core import Yesod.Form.Profiles (Textarea) import Data.Int (Int64) import Data.Time (Day, TimeOfDay) +import Data.Text (Text) class ToForm a y where toForm :: Formlet sub y a class ToFormField a y where toFormField :: FormFieldSettings -> FormletField sub y a +{- FIXME instance ToFormField String y where toFormField = stringField instance ToFormField (Maybe String) y where toFormField = maybeStringField +-} + +instance ToFormField Text y where + toFormField = stringField +instance ToFormField (Maybe Text) y where + toFormField = maybeStringField instance ToFormField Int y where toFormField = intField diff --git a/Yesod/Form/Core.hs b/Yesod/Form/Core.hs index b779b4a6..9b08fff9 100644 --- a/Yesod/Form/Core.hs +++ b/Yesod/Form/Core.hs @@ -52,6 +52,13 @@ import Text.Hamlet import Text.Blaze (ToHtml (..)) import Data.String import Control.Monad (join) +import Data.Text (Text, pack) +import qualified Data.Text as T +import Prelude hiding ((++)) +import Data.Monoid (Monoid (mappend)) + +(++) :: Monoid a => a -> a -> a +(++) = mappend -- | A form can produce three different results: there was no data available, -- the data was invalid, or there was a successful parse. @@ -59,7 +66,7 @@ import Control.Monad (join) -- The 'Applicative' instance will concatenate the failure messages in two -- 'FormResult's. data FormResult a = FormMissing - | FormFailure [String] + | FormFailure [Text] | FormSuccess a deriving Show instance Functor FormResult where @@ -92,7 +99,7 @@ instance Monoid Enctype where data Ints = IntCons Int Ints | IntSingle Int instance Show Ints where show (IntSingle i) = show i - show (IntCons i is) = show i ++ '-' : show is + show (IntCons i is) = show i ++ ('-' : show is) incrInts :: Ints -> Ints incrInts (IntSingle i) = IntSingle $ i + 1 @@ -113,16 +120,16 @@ type FormInner s m = GHandler s m ))) -type Env = [(String, String)] -type FileEnv = [(String, FileInfo)] +type Env = [(Text, Text)] +type FileEnv = [(Text, FileInfo)] -- | Get a unique identifier. -newFormIdent :: Monad m => StateT Ints m String +newFormIdent :: Monad m => StateT Ints m Text newFormIdent = do i <- get let i' = incrInts i put i' - return $ 'f' : show i' + return $ pack $ 'f' : show i' deeperFormIdent :: Monad m => StateT Ints m () deeperFormIdent = do @@ -172,12 +179,12 @@ requiredFieldHelper (FieldProfile parse render mkWidget) ffs orig = toForm $ do Left e -> (FormFailure [e], x) Right y -> (FormSuccess y, x) let fi = FieldInfo - { fiLabel = string label + { fiLabel = toHtml label , fiTooltip = tooltip , fiIdent = theId , fiInput = mkWidget theId name val True , fiErrors = case res of - FormFailure [x] -> Just $ string x + FormFailure [x] -> Just $ toHtml x _ -> Nothing , fiRequired = True } @@ -261,12 +268,12 @@ optionalFieldHelper (FieldProfile parse render mkWidget) ffs orig' = toForm $ do Left e -> (FormFailure [e], x) Right y -> (FormSuccess $ Just y, x) let fi = FieldInfo - { fiLabel = string label + { fiLabel = toHtml label , fiTooltip = tooltip , fiIdent = theId , fiInput = mkWidget theId name val False , fiErrors = case res of - FormFailure x -> Just $ string $ unlines x + FormFailure x -> Just $ toHtml $ T.unlines x _ -> Nothing , fiRequired = False } @@ -290,29 +297,29 @@ mapFormXml f (GForm g) = GForm $ do data FieldInfo sub y = FieldInfo { fiLabel :: Html , fiTooltip :: Html - , fiIdent :: String + , fiIdent :: Text , fiInput :: GWidget sub y () , fiErrors :: Maybe Html , fiRequired :: Bool } data FormFieldSettings = FormFieldSettings - { ffsLabel :: String + { ffsLabel :: Text , ffsTooltip :: Html - , ffsId :: Maybe String - , ffsName :: Maybe String + , ffsId :: Maybe Text + , ffsName :: Maybe Text } instance IsString FormFieldSettings where - fromString s = FormFieldSettings s mempty Nothing Nothing + fromString s = FormFieldSettings (pack s) mempty Nothing Nothing -- | A generic definition of a form field that can be used for generating both -- required and optional fields. See 'requiredFieldHelper and -- 'optionalFieldHelper'. data FieldProfile sub y a = FieldProfile - { fpParse :: String -> Either String a - , fpRender :: a -> String + { fpParse :: Text -> Either Text a + , fpRender :: a -> Text -- | ID, name, value, required - , fpWidget :: String -> String -> String -> Bool -> GWidget sub y () + , fpWidget :: Text -> Text -> Text -> Bool -> GWidget sub y () } type Form sub y = GForm sub y (GWidget sub y ()) @@ -338,7 +345,7 @@ checkForm f (GForm form) = GForm $ do -- -- Unlike 'checkForm', the validation error will appear in the generated HTML -- of the form. -checkField :: (a -> Either String b) -> FormField s m a -> FormField s m b +checkField :: (a -> Either Text b) -> FormField s m a -> FormField s m b checkField f (GForm form) = GForm $ do (res, xml, enc) <- form let (res', merr) = @@ -355,7 +362,7 @@ checkField f (GForm form) = GForm $ do Just err -> flip map xml $ \fi -> fi { fiErrors = Just $ case fiErrors fi of - Nothing -> string err + Nothing -> toHtml err Just x -> x } return (res', xml', enc) diff --git a/Yesod/Form/Fields.hs b/Yesod/Form/Fields.hs index ad46681f..ccf8bfef 100644 --- a/Yesod/Form/Fields.hs +++ b/Yesod/Form/Fields.hs @@ -2,6 +2,7 @@ {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE CPP #-} +{-# LANGUAGE OverloadedStrings #-} module Yesod.Form.Fields ( -- * Fields -- ** Required @@ -62,6 +63,9 @@ import Text.Hamlet import Data.Monoid import Control.Monad (join) import Data.Maybe (fromMaybe, isNothing) +import Data.Text (Text, pack, unpack) +import qualified Data.Text as T +import Text.Blaze (toHtml) #if __GLASGOW_HASKELL__ >= 700 #define HAMLET hamlet @@ -69,28 +73,28 @@ import Data.Maybe (fromMaybe, isNothing) #define HAMLET $hamlet #endif -stringField :: (IsForm f, FormType f ~ String) - => FormFieldSettings -> Maybe String -> f +stringField :: (IsForm f, FormType f ~ Text) + => FormFieldSettings -> Maybe Text -> f stringField = requiredFieldHelper stringFieldProfile -maybeStringField :: (IsForm f, FormType f ~ Maybe String) - => FormFieldSettings -> Maybe (Maybe String) -> f +maybeStringField :: (IsForm f, FormType f ~ Maybe Text) + => FormFieldSettings -> Maybe (Maybe Text) -> f maybeStringField = optionalFieldHelper stringFieldProfile -passwordField :: (IsForm f, FormType f ~ String) - => FormFieldSettings -> Maybe String -> f +passwordField :: (IsForm f, FormType f ~ Text) + => FormFieldSettings -> Maybe Text -> f passwordField = requiredFieldHelper passwordFieldProfile -maybePasswordField :: (IsForm f, FormType f ~ Maybe String) - => FormFieldSettings -> Maybe (Maybe String) -> f +maybePasswordField :: (IsForm f, FormType f ~ Maybe Text) + => FormFieldSettings -> Maybe (Maybe Text) -> f maybePasswordField = optionalFieldHelper passwordFieldProfile -intInput :: Integral i => String -> FormInput sub master i +intInput :: Integral i => Text -> FormInput sub master i intInput n = mapFormXml fieldsToInput $ requiredFieldHelper intFieldProfile (nameSettings n) Nothing -maybeIntInput :: Integral i => String -> FormInput sub master (Maybe i) +maybeIntInput :: Integral i => Text -> FormInput sub master (Maybe i) maybeIntInput n = mapFormXml fieldsToInput $ optionalFieldHelper intFieldProfile (nameSettings n) Nothing @@ -144,14 +148,14 @@ boolField ffs orig = toForm $ do Just "false" -> (FormSuccess False, False) Just _ -> (FormSuccess True, True) let fi = FieldInfo - { fiLabel = string label + { fiLabel = toHtml label , fiTooltip = tooltip , fiIdent = theId , fiInput = [HAMLET| |] , fiErrors = case res of - FormFailure [x] -> Just $ string x + FormFailure [x] -> Just $ toHtml x _ -> Nothing , fiRequired = True } @@ -181,7 +185,7 @@ selectField pairs ffs initial = toForm $ do Nothing -> FormMissing Just "none" -> FormFailure ["Field is required"] Just x -> - case reads x of + case reads $ unpack x of (x', _):_ -> case lookup x' pairs' of Nothing -> FormFailure ["Invalid entry"] @@ -203,19 +207,19 @@ selectField pairs ffs initial = toForm $ do