1try: 2 from urllib import quote_plus 3except ImportError: #pragma: no cover 4 from urllib.parse import quote_plus 5 6from tg.support.converters import asbool 7from markupsafe import Markup 8 9import tg 10from tg import predicates 11from tg.util import Bunch 12 13 14class MissingRendererError(Exception): 15 def __init__(self, template_engine): 16 Exception.__init__(self, 17 ("The renderer for '%(template_engine)s' templates is missing. " 18 "Try adding the following line in you app_cfg.py:\n" 19 "\"base_config.renderers.append('%(template_engine)s')\"") % dict( 20 template_engine=template_engine)) 21 self.template_engine = template_engine 22 23 24def _get_tg_vars(): 25 """Create a Bunch of variables that should be available in all templates. 26 27 These variables are: 28 29 WARNING: This function should not be called from outside of the render() 30 code. Please consider this function as private. 31 32 quote_plus 33 the urllib quote_plus function 34 url 35 the turbogears.url function for creating flexible URLs 36 identity 37 the current visitor's identity information 38 session 39 the current beaker.session if the session_filter.on it set 40 in the app.cfg configuration file. If it is not set then session 41 will be None. 42 locale 43 the default locale 44 inputs 45 input values from a form 46 errors 47 validation errors 48 request 49 the WebOb Request Object 50 config 51 the app's config object 52 auth_stack_enabled 53 A boolean that determines if the auth stack is present in the environment 54 predicates 55 The :mod:`tg.predicates` module. 56 57 """ 58 59 tgl = tg.request_local.context._current_obj() 60 req = tgl.request 61 conf = tgl.config 62 tmpl_context = tgl.tmpl_context 63 app_globals = tgl.app_globals 64 translator = tgl.translator 65 response = tgl.response 66 session = tgl.session 67 helpers = conf['helpers'] 68 69 try: 70 validation = req.validation 71 except AttributeError: 72 validation = {} 73 74 # TODO: Implement user_agent and other missing features. 75 tg_vars = Bunch( 76 config=tg.config, 77 flash_obj=tg.flash, 78 quote_plus=quote_plus, 79 url=tg.url, 80 # this will be None if no identity 81 identity = req.environ.get('repoze.who.identity'), 82 session = session, 83 locale = req.plain_languages, 84 errors = validation and validation.errors, 85 inputs = validation and validation.values, 86 request = req, 87 auth_stack_enabled = 'repoze.who.plugins' in req.environ, 88 predicates = predicates) 89 90 root_vars = Bunch( 91 c=tmpl_context, 92 tmpl_context=tmpl_context, 93 response=response, 94 request=req, 95 config=conf, 96 app_globals=app_globals, 97 g=app_globals, 98 session=session, 99 url=tg.url, 100 helpers=helpers, 101 h=helpers, 102 tg=tg_vars, 103 translator=translator, 104 ungettext=tg.i18n.ungettext, 105 _=tg.i18n.ugettext, 106 N_=tg.i18n.gettext_noop) 107 108 # If there is an identity, push it to the Pylons template context 109 tmpl_context.identity = tg_vars['identity'] 110 111 # Allow users to provide a callable that defines extra vars to be 112 # added to the template namespace 113 variable_provider = conf.get('variable_provider', None) 114 if variable_provider: 115 root_vars.update(variable_provider()) 116 return root_vars 117 118#Monkey patch pylons_globals for cases when pylons.templating is used 119#instead of tg.render to programmatically render templates. 120try: #pragma: no cover 121 import pylons 122 import pylons.templating 123 pylons.templating.pylons_globals = _get_tg_vars 124except ImportError: 125 pass 126# end monkeying around 127 128 129def render(template_vars, template_engine=None, template_name=None, **kwargs): 130 """Renders a specific template in current TurboGears context. 131 132 Permits to manually render any template like TurboGears would for 133 expositions. It also guarantees that the ``before_render_call`` and 134 ``after_render_call`` hooks are called in the process. 135 136 :param dict template_vars: This is the dictonary of variables that should 137 become available to the template. Template 138 vars can also include the ``tg_cache`` dictionary 139 which enables template caching. 140 :param str template_engine: This is the template engine name, same as 141 specified inside AppConfig.renderers. 142 :param str template_name: This is the template to render, can be specified 143 both as a path or using dotted notation if available. 144 145 TurboGears injects some additional variables in the template context, 146 those include: 147 148 - tg.config -> like tg.config in controllers 149 - tg.flash_obj -> the flash object, call ``render`` on it to display it. 150 - tg.quote_plus -> function to perform percentage escaping (%xx) 151 - tg.url -> like tg.url in controllers 152 - tg.identity -> like tg.request.identity in controllers 153 - tg.session -> like tg.session in controllers 154 - tg.locale -> Languages of the current request 155 - tg.errors -> Validation errors 156 - tg.inputs -> Values submitted for validation 157 - tg.request -> like tg.request in controllers 158 - tg.auth_stack_enabled -> if authentication is enabled or not 159 - tg.predicates -> like tg.predicates in controllers 160 161 - tmpl_context -> like tg.tmpl_context in controllers 162 - response -> like tg.response in controllers 163 - request -> like tg.request in controllers 164 - config -> like tg.config in controllers 165 - app_globals -> like tg.app_globals in controllers 166 - session -> like tg.session in controllers 167 - url -> like tg.url in controllers 168 - h -> Your application helpers 169 - translator -> The current gettext translator 170 - _ -> like tg.i18n.ugettext 171 172 Additional variables can be added to every template by a 173 ``variable_provider`` function inside the application 174 configuration. This function is expected to return 175 a ``dict`` with any variable that should be added 176 the default template variables. It can even replace 177 existing variables. 178 179 """ 180 config = tg.config._current_obj() 181 182 if template_engine is None: 183 template_engine = config['default_renderer'] 184 185 render_function = config['render_functions'].get(template_engine) 186 if render_function is None: 187 # engine is not present in the engine list, warn developer 188 raise MissingRendererError(template_engine) 189 190 if not template_vars: 191 template_vars = {} 192 193 caching_options = template_vars.get('tg_cache', {}) 194 kwargs['cache_key'] = caching_options.get('key') 195 kwargs['cache_expire'] = caching_options.get('expire') 196 kwargs['cache_type'] = caching_options.get('type') 197 198 tg.hooks.notify('before_render_call', (template_engine, template_name, template_vars, kwargs)) 199 200 tg_vars = template_vars 201 202 engines_without_vars = config['rendering_engines_without_vars'] 203 if template_engine not in engines_without_vars: 204 # Get the extra vars, and merge in the vars from the controller 205 tg_vars = _get_tg_vars() 206 tg_vars.update(template_vars) 207 208 kwargs['result'] = render_function(template_name, tg_vars, **kwargs) 209 210 tg.hooks.notify('after_render_call', (template_engine, template_name, template_vars, kwargs)) 211 return kwargs['result'] 212 213 214def cached_template(template_name, render_func, ns_options=(), 215 cache_key=None, cache_type=None, cache_expire=None, 216 **kwargs): 217 """Cache and render a template, took from Pylons 218 219 Cache a template to the namespace ``template_name``, along with a 220 specific key if provided. 221 222 Basic Options 223 224 ``template_name`` 225 Name of the template, which is used as the template namespace. 226 ``render_func`` 227 Function used to generate the template should it no longer be 228 valid or doesn't exist in the cache. 229 ``ns_options`` 230 Tuple of strings, that should correspond to keys likely to be 231 in the ``kwargs`` that should be used to construct the 232 namespace used for the cache. For example, if the template 233 language supports the 'fragment' option, the namespace should 234 include it so that the cached copy for a template is not the 235 same as the fragment version of it. 236 237 Caching options (uses Beaker caching middleware) 238 239 ``cache_key`` 240 Key to cache this copy of the template under. 241 ``cache_type`` 242 Valid options are ``dbm``, ``file``, ``memory``, ``database``, 243 or ``memcached``. 244 ``cache_expire`` 245 Time in seconds to cache this template with this ``cache_key`` 246 for. Or use 'never' to designate that the cache should never 247 expire. 248 249 The minimum key required to trigger caching is 250 ``cache_expire='never'`` which will cache the template forever 251 seconds with no key. 252 253 """ 254 # If one of them is not None then the user did set something 255 if cache_key is not None or cache_type is not None or cache_expire is not None: 256 get_cache_kw = {} 257 if cache_type is not None: 258 get_cache_kw['type'] = cache_type 259 260 if not cache_key: 261 cache_key = 'default' 262 if cache_expire == 'never': 263 cache_expire = None 264 265 namespace = template_name 266 for name in ns_options: 267 namespace += str(kwargs.get(name)) 268 269 cache = tg.cache.get_cache(namespace, **get_cache_kw) 270 content = cache.get_value(cache_key, createfunc=render_func, 271 expiretime=cache_expire) 272 return content 273 else: 274 return render_func() 275 276