----------------------------------------------------------------------------- -- | -- Module : Templates.Templates -- Maintainer : bartek@skrivapa.se -- Stability : development -- Portability : portable -- -- This is main templating module. It provides basic interface for -- generation templates (RenderTemplate) with 'renderTemplate' -- function. We also provide types for templates and few functions for -- loading and testing -- -- HOW TO USE TEMPLATING SYSTEM -- -- 1) There is a folder called templates, All templates files (*.st) are there. Each files contains many templates -- definitions, and there must be line starting with # between each two templates. -- Template definition has form 'nameOfTemplate=bodyOfTemplate'. -- -- 2) Template body is just String Template and you should be able to find more -- info at http://www.haskell.org/haskellwiki/HStringTemplate -- -- 3) All templates are in a global scope. Watch out for conflicting names. -- On dev computers they are loaded on every request, so one can change them without stoping server. -- -- 4) To generate a template in haskell call renderTemplate. -- First param is a set of all templates. Usually you can get it from 'Context'. -- Next is a name of template that You wan't to render. -- Last one is some for of list of params. -- As a result you get IO String. -- If template will fail You will get error info inside. But this is only for syntax errors. -- If You will forget a param there will be info in log, and template set param value to something empty. -- -- -- FIELDS -- -- Current policy is to use fields. You can find usage of -- [(String,String)] as params and also composition of setAttribute -- functions in a code. This are old concepts and will be droped at -- some point. -- -- How to user fields: -- - there is one function ('field') that sets one field -- - fields form a monad so you can use do notation for setting many fields -- - value of a field can be almoust everything (String, Int, Maybe, List, Map, types that are instances of Data and Typeable etc) -- - IO wrapped values and fields can be also a values of a field. -- -- Example -- -- > userView tempates user = -- > renderTemplate templates "userView" $ do -- > userFields -- > -- > userFields user = do -- > field "name" $ username user -- > field "company" $ usercompany user -- > field "documents" $ map (documentFields) getUserDocumentsFromDB -- > -- > documentFields document = do -- > field "id" $ documentid document -- > field "title" $ documenttitle document -- -- -- Why we want to use fields -- - They force reuse. We write documentFields, and reuse it every time we want to pass document info to template. -- - Fields can be extended. If I want to have extended info about user I use 'userFields' to set basic info and -- then add advanced fields -- - No need to first bind from IO, then pass to template -- - They support advanced structures like lists and maybe's -- -- -- Some extra info: -- In templates use maybe. You can use 'if' in template body to check for Nothing -- Always change ByteString to String. We have a problems with encoding, so please watch for this. -- -- Please also see example.hs for a running example ----------------------------------------------------------------------------- module Text.StringTemplates.Templates ( Fields , runFields , TemplatesMonad(..) , renderTemplate , renderTemplate_ , renderTemplateI , TemplatesT(..) , runTemplatesT , renderHelper ) where import Control.Monad.Trans.Maybe import Text.StringTemplate.Base hiding (ToSElem, toSElem, render) import Text.StringTemplates.TemplatesLoader import Text.StringTemplates.Fields import Control.Applicative import Control.Monad.Reader import Control.Monad.Identity import Control.Monad.Error -- | simple reader monad class that provides access to templates class (Functor m, Monad m) => TemplatesMonad m where getTemplates :: m Templates -- ^ get templates (for text templates default column name is used) getTextTemplatesByColumn :: String -> m Templates -- ^ get templates (for text templates specified column name is used) instance TemplatesMonad m => TemplatesMonad (MaybeT m) where getTemplates = lift getTemplates getTextTemplatesByColumn = lift . getTextTemplatesByColumn instance (TemplatesMonad m , Error e) => TemplatesMonad (ErrorT e m) where getTemplates = lift getTemplates getTextTemplatesByColumn = lift . getTextTemplatesByColumn -- | renders a template by name renderTemplate :: TemplatesMonad m => String -- ^ template name -> Fields m () -- ^ template params -> m String renderTemplate name fields = do ts <- getTemplates renderHelper ts name fields -- | renders a template by name (params function cannot use side effects) renderTemplateI :: TemplatesMonad m => String -- ^ template name -> Fields Identity () -- ^ template params -> m String renderTemplateI name fields = do ts <- getTemplates return $ renderTemplateMain ts name ([]::[(String, String)]) (setManyAttrib $ runIdentity $ runFields fields) -- | renders a template by name without any params renderTemplate_ :: TemplatesMonad m => String -- ^ template name -> m String renderTemplate_ name = renderTemplate name $ return () renderHelper :: Monad m => Templates -> String -> Fields m () -> m String renderHelper ts name fields = do attrs <- runFields fields return $ renderTemplateMain ts name ([]::[(String, String)]) (setManyAttrib attrs) -- | Simple implementation of TemplatesMonad newtype TemplatesT m a = TemplatesT { unTT :: ReaderT (String, GlobalTemplates) m a } deriving (Applicative, Functor, Monad, MonadIO, MonadTrans) runTemplatesT :: (Functor m, Monad m) => (String, GlobalTemplates) -- ^ (default column name, global templates) -> TemplatesT m a -> m a runTemplatesT ts action = runReaderT (unTT action) ts instance (Functor m, Monad m) => TemplatesMonad (TemplatesT m) where getTemplates = TemplatesT $ do (column, ts) <- ask return $ localizedVersion column ts getTextTemplatesByColumn column = TemplatesT $ do (_, ts) <- ask return $ localizedVersion column ts