{-# LANGUAGE QuasiQuotes #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE CPP #-} {-# LANGUAGE FlexibleContexts #-} module Yesod.Form.Fields ( FormMessage (..) , defaultFormMessage , textField , passwordField , textareaField , hiddenField , intField , dayField , timeField , htmlField , emailField , searchField , selectField , AutoFocus , urlField , doubleField , parseDate , parseTime , Textarea (..) , radioField , boolField ) where import Yesod.Form.Types import Yesod.Widget import Yesod.Message (RenderMessage) import Yesod.Handler (GGHandler) import Text.Hamlet hiding (renderHtml) import Text.Blaze (ToHtml (..)) import Text.Cassius import Data.Time (Day, TimeOfDay(..)) import qualified Text.Email.Validate as Email import Network.URI (parseURI) import Database.Persist (PersistField) import Text.HTML.SanitizeXSS (sanitizeBalance) import Control.Monad (when, unless) import qualified Blaze.ByteString.Builder.Html.Utf8 as B import Blaze.ByteString.Builder (writeByteString, toLazyByteString) import Blaze.ByteString.Builder.Internal.Write (fromWriteList) import Text.Blaze.Renderer.String (renderHtml) import qualified Data.ByteString as S import qualified Data.ByteString.Lazy as L import Data.Text (Text, unpack, pack) import qualified Data.Text.Read import Data.Monoid (mappend) import Text.Hamlet.NonPoly (html) #if __GLASGOW_HASKELL__ >= 700 #define WHAMLET whamlet #define HAMLET hamlet #define CASSIUS cassius #define JULIUS julius #define HTML html #else #define WHAMLET $whamlet #define HAMLET $hamlet #define CASSIUS $cassius #define JULIUS $julius #define HTML $html #endif data FormMessage = MsgInvalidInteger Text | MsgInvalidNumber Text | MsgInvalidEntry Text | MsgInvalidUrl Text | MsgInvalidEmail Text | MsgInvalidTimeFormat | MsgInvalidHour Text | MsgInvalidMinute Text | MsgInvalidSecond Text | MsgInvalidDay | MsgCsrfWarning | MsgValueRequired | MsgInputNotFound Text | MsgSelectNone | MsgInvalidBool Text | MsgBoolYes | MsgBoolNo defaultFormMessage :: FormMessage -> Text defaultFormMessage (MsgInvalidInteger t) = "Invalid integer: " `mappend` t defaultFormMessage (MsgInvalidNumber t) = "Invalid number: " `mappend` t defaultFormMessage (MsgInvalidEntry t) = "Invalid entry: " `mappend` t defaultFormMessage MsgInvalidTimeFormat = "Invalid time, must be in HH:MM[:SS] format" defaultFormMessage MsgInvalidDay = "Invalid day, must be in YYYY-MM-DD format" defaultFormMessage (MsgInvalidUrl t) = "Invalid URL: " `mappend` t defaultFormMessage (MsgInvalidEmail t) = "Invalid e-mail address: " `mappend` t defaultFormMessage (MsgInvalidHour t) = "Invalid hour: " `mappend` t defaultFormMessage (MsgInvalidMinute t) = "Invalid minute: " `mappend` t defaultFormMessage (MsgInvalidSecond t) = "Invalid second: " `mappend` t defaultFormMessage MsgCsrfWarning = "As a protection against cross-site request forgery attacks, please confirm your form submission." defaultFormMessage MsgValueRequired = "Value is required" defaultFormMessage (MsgInputNotFound t) = "Input not found: " `mappend` t defaultFormMessage MsgSelectNone = "" defaultFormMessage (MsgInvalidBool t) = "Invalid boolean: " `mappend` t defaultFormMessage MsgBoolYes = "Yes" defaultFormMessage MsgBoolNo = "No" blank :: (Text -> Either msg a) -> Maybe Text -> Either msg (Maybe a) blank _ Nothing = Right Nothing blank _ (Just "") = Right Nothing blank f (Just t) = either Left (Right . Just) $ f t intField :: (Monad monad, Integral i) => Field (GGWidget master monad ()) FormMessage i intField = Field { fieldParse = blank $ \s -> case Data.Text.Read.signed Data.Text.Read.decimal s of Right (a, "") -> Right a _ -> Left $ MsgInvalidInteger s , fieldRender = pack . showI , fieldView = \theId name val isReq -> addHamlet [HAMLET|\ |] } where showI x = show (fromIntegral x :: Integer) doubleField :: Monad monad => Field (GGWidget master monad ()) FormMessage Double doubleField = Field { fieldParse = blank $ \s -> case Data.Text.Read.double s of Right (a, "") -> Right a _ -> Left $ MsgInvalidNumber s , fieldRender = pack . show , fieldView = \theId name val isReq -> addHamlet [HAMLET|\ |] } dayField :: Monad monad => Field (GGWidget master monad ()) FormMessage Day dayField = Field { fieldParse = blank $ parseDate . unpack , fieldRender = pack . show , fieldView = \theId name val isReq -> addHamlet [HAMLET|\ |] } timeField :: Monad monad => Field (GGWidget master monad ()) FormMessage TimeOfDay timeField = Field { fieldParse = blank $ parseTime . unpack , fieldRender = pack . show . roundFullSeconds , fieldView = \theId name val isReq -> addHamlet [HAMLET|\ |] } where roundFullSeconds tod = TimeOfDay (todHour tod) (todMin tod) fullSec where fullSec = fromInteger $ floor $ todSec tod htmlField :: Monad monad => Field (GGWidget master monad ()) FormMessage Html htmlField = Field { fieldParse = blank $ Right . preEscapedString . sanitizeBalance . unpack -- FIXME make changes to xss-sanitize , fieldRender = pack . renderHtml , fieldView = \theId name val _isReq -> addHamlet [HAMLET|\