1 {-# LANGUAGE GeneralizedNewtypeDeriving #-} 2 3 {-| 4 5 This module contains the core definitions for the Heist template system. 6 7 The Heist template system is based on HTML and XML. It allows you to build 8 custom HTML and XML based markup languages. With Heist you can define your 9 own domain-specific HTML and XML tags implemented with Haskell and use them 10 in your templates. 11 12 The most important concept in Heist is the 'Splice'. Splices can be thought 13 of as functions that transform a node into a list of nodes. Heist then 14 substitutes the resulting list of nodes into your template in place of the 15 input node. 'Splice' is implemented as a type synonym @type Splice m = 16 HeistT m [Node]@, and 'HeistT' has a function 'getParamNode' 17 that lets you get the input node. 18 19 Suppose you have a place on your page where you want to display a link with 20 the text \"Logout username\" if the user is currently logged in or a link to 21 the login page if no user is logged in. Assume you have a function 22 @getUser :: MyAppMonad (Maybe Text)@ that gets the current user. 23 You can implement this functionality with a 'Splice' as follows: 24 25 > import Blaze.ByteString.Builder 26 > import Data.ByteString.Char8 (ByteString) 27 > import qualified Data.ByteString.Char8 as B 28 > import Data.Text (Text) 29 > import qualified Data.Text as T 30 > import qualified Text.XmlHtml as X 31 > 32 > import Text.Templating.Heist 33 > 34 > link :: Text -> Text -> X.Node 35 > link target text = X.Element "a" [("href", target)] [X.TextNode text] 36 > 37 > loginLink :: X.Node 38 > loginLink = link "/login" "Login" 39 > 40 > logoutLink :: Text -> X.Node 41 > logoutLink user = link "/logout" (T.append "Logout " user) 42 > 43 > loginLogoutSplice :: Splice MyAppMonad 44 > loginLogoutSplice = do 45 > user <- lift getUser 46 > return [maybe loginLink logoutLink user] 47 > 48 49 Next, you need to bind that splice to a tag. Heist stores information 50 about splices and templates in the 'TemplateState' data structure. The 51 following code demonstrates how this splice would be used. 52 53 > mySplices = [ ("loginLogout", loginLogoutSplice) ] 54 > 55 > main = do 56 > ets <- loadTemplates "templates" $ 57 > bindSplices mySplices (emptyTemplateState "templates") 58 > let ts = either error id ets 59 > t <- runMyAppMonad $ renderTemplate ts "index" 60 > print $ maybe "Page not found" (toByteString . fst) t 61 62 Here we build up our 'TemplateState' by starting with emptyTemplateState and 63 applying bindSplice for all the splices we want to add. Then we pass this 64 to loadTemplates our final 'TemplateState' wrapped in an Either to handle 65 errors. Then we use this 'TemplateState' to render our templates. 66 67 -} 68 69 module Text.Templating.Heist 70 ( 71 -- * Types 72 Template 73 , MIMEType 74 , Splice 75 , TemplateMonad 76 , HeistT 77 , TemplateState 78 79 -- * Functions and declarations on TemplateState values 80 , addTemplate 81 , addXMLTemplate 82 , emptyTemplateState 83 , bindSplice 84 , bindSplices 85 , lookupSplice 86 , setTemplates 87 , loadTemplates 88 , hasTemplate 89 , addTemplatePathPrefix 90 91 -- * Hook functions 92 -- $hookDoc 93 , addOnLoadHook 94 , addPreRunHook 95 , addPostRunHook 96 97 -- * HeistT functions 98 , stopRecursion 99 , getParamNode 100 , runNodeList 101 , getContext 102 , getTemplateFilePath 103 104 , localParamNode 105 , getsTS 106 , getTS 107 , putTS 108 , modifyTS 109 , restoreTS 110 , localTS 111 112 -- * Functions for running splices and templates 113 , evalTemplate 114 , callTemplate 115 , renderTemplate 116 , renderWithArgs 117 , bindStrings 118 , bindString 119 120 -- * Functions for creating splices 121 , textSplice 122 , runChildren 123 , runChildrenWith 124 , runChildrenWithTrans 125 , runChildrenWithTemplates 126 , runChildrenWithText 127 , mapSplices 128 129 -- * Misc functions 130 , getDoc 131 , getXMLDoc 132 , bindStaticTag 133 134 ) where 135 136 import Control.Monad.Trans 137 import qualified Data.Map as Map 138 import Text.Templating.Heist.Internal 139 import Text.Templating.Heist.Splices 140 import Text.Templating.Heist.Types 141 142 143 ------------------------------------------------------------------------------ 144 -- | The default set of built-in splices. 145 defaultSpliceMap :: MonadIO m => SpliceMap m 146 defaultSpliceMap = Map.fromList 147 [(applyTag, applyImpl) 148 ,(bindTag, bindImpl) 149 ,(ignoreTag, ignoreImpl) 150 ,(markdownTag, markdownSplice) 151 ] 152 153 154 ------------------------------------------------------------------------------ 155 -- | An empty template state, with Heist's default splices (@\<apply\>@, 156 -- @\<bind\>@, @\<ignore\>@, and @\<markdown\>@) mapped. The static tag is 157 -- not mapped here because it must be mapped manually in your application. 158 emptyTemplateState :: MonadIO m => TemplateState m 159 emptyTemplateState = 160 TemplateState (defaultSpliceMap) Map.empty True [] 0 161 return return return [] Nothing 162 163 164 -- $hookDoc 165 -- Heist hooks allow you to modify templates when they are loaded and before 166 -- and after they are run. Every time you call one of the addAbcHook 167 -- functions the hook is added to onto the processing pipeline. The hooks 168 -- processes the template in the order that they were added to the 169 -- TemplateState. 170 -- 171 -- The pre-run and post-run hooks are run before and after every template is 172 -- run/rendered. You should be careful what code you put in these hooks 173 -- because it can significantly affect the performance of your site. 174