Remove <br/> from safe string indicating that CSRF cooking is missing.
[larjonas-mediagoblin.git] / mediagoblin / tools / template.py
blobf2619808438aa46c29e00fb5bd82b676a51cf0d0
1 # GNU MediaGoblin -- federated, autonomous media hosting
2 # Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS.
4 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License as published by
6 # the Free Software Foundation, either version 3 of the License, or
7 # (at your option) any later version.
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU Affero General Public License for more details.
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
17 import six
19 import jinja2
20 from jinja2.ext import Extension
21 from jinja2.nodes import Include, Const
23 from babel.localedata import exists
24 from werkzeug.urls import url_quote_plus
26 from mediagoblin import mg_globals
27 from mediagoblin import messages
28 from mediagoblin import _version
29 from mediagoblin.tools import common
30 from mediagoblin.tools.translate import is_rtl
31 from mediagoblin.tools.translate import set_thread_locale
32 from mediagoblin.tools.pluginapi import get_hook_templates, hook_transform
33 from mediagoblin.tools.timesince import timesince
34 from mediagoblin.meddleware.csrf import render_csrf_form_token
36 SETUP_JINJA_ENVS = {}
39 def get_jinja_env(app, template_loader, locale):
40 """
41 Set up the Jinja environment,
43 (In the future we may have another system for providing theming;
44 for now this is good enough.)
45 """
46 set_thread_locale(locale)
48 # If we have a jinja environment set up with this locale, just
49 # return that one.
50 if locale in SETUP_JINJA_ENVS:
51 return SETUP_JINJA_ENVS[locale]
53 # The default config does not require a [jinja2] block.
54 # You may create one if you wish to enable additional jinja2 extensions,
55 # see example in config_spec.ini
56 jinja2_config = app.global_config.get('jinja2', {})
57 local_exts = jinja2_config.get('extensions', [])
59 # jinja2.StrictUndefined will give exceptions on references
60 # to undefined/unknown variables in templates.
61 template_env = jinja2.Environment(
62 loader=template_loader, autoescape=True,
63 undefined=jinja2.StrictUndefined,
64 extensions=[
65 'jinja2.ext.i18n', 'jinja2.ext.autoescape',
66 TemplateHookExtension] + local_exts)
68 if six.PY2:
69 template_env.install_gettext_callables(mg_globals.thread_scope.translations.ugettext,
70 mg_globals.thread_scope.translations.ungettext)
71 else:
72 template_env.install_gettext_callables(mg_globals.thread_scope.translations.gettext,
73 mg_globals.thread_scope.translations.ngettext)
75 # All templates will know how to ...
76 # ... fetch all waiting messages and remove them from the queue
77 # ... construct a grid of thumbnails or other media
78 # ... have access to the global and app config
79 # ... determine if the language is rtl or ltr
80 template_env.globals['fetch_messages'] = messages.fetch_messages
81 template_env.globals['app_config'] = app.app_config
82 template_env.globals['global_config'] = app.global_config
83 template_env.globals['version'] = _version.__version__
84 template_env.globals['auth'] = app.auth
85 template_env.globals['is_rtl'] = is_rtl(locale)
86 template_env.filters['urlencode'] = url_quote_plus
88 # add human readable fuzzy date time
89 template_env.globals['timesince'] = timesince
91 # allow for hooking up plugin templates
92 template_env.globals['get_hook_templates'] = get_hook_templates
94 template_env.globals = hook_transform(
95 'template_global_context', template_env.globals)
97 #### THIS IS TEMPORARY, PLEASE FIX IT
98 ## Notifications stuff is not yet a plugin (and we're not sure it will be),
99 ## but it needs to add stuff to the context. This is THE WRONG WAY TO DO IT
100 from mediagoblin import notifications
101 template_env.globals['get_notifications'] = notifications.get_notifications
102 template_env.globals[
103 'get_notification_count'] = notifications.get_notification_count
104 template_env.globals[
105 'get_comment_subscription'] = notifications.get_comment_subscription
107 if exists(locale):
108 SETUP_JINJA_ENVS[locale] = template_env
110 return template_env
113 # We'll store context information here when doing unit tests
114 TEMPLATE_TEST_CONTEXT = {}
117 def render_template(request, template_path, context):
119 Render a template with context.
121 Always inserts the request into the context, so you don't have to.
122 Also stores the context if we're doing unit tests. Helpful!
124 template = request.template_env.get_template(
125 template_path)
126 context['request'] = request
127 rendered_csrf_token = render_csrf_form_token(request)
128 if rendered_csrf_token is not None:
129 context['csrf_token'] = render_csrf_form_token(request)
131 # allow plugins to do things to the context
132 if request.controller_name:
133 context = hook_transform(
134 (request.controller_name, template_path),
135 context)
137 # More evil: allow plugins to possibly do something to the context
138 # in every request ever with access to the request and other
139 # variables. Note: this is slower than using
140 # template_global_context
141 context = hook_transform(
142 'template_context_prerender', context)
144 rendered = template.render(context)
146 if common.TESTS_ENABLED:
147 TEMPLATE_TEST_CONTEXT[template_path] = context
149 return rendered
152 def clear_test_template_context():
153 global TEMPLATE_TEST_CONTEXT
154 TEMPLATE_TEST_CONTEXT = {}
157 class TemplateHookExtension(Extension):
159 Easily loop through a bunch of templates from a template hook.
161 Use:
162 {% template_hook("comment_extras") %}
164 ... will include all templates hooked into the comment_extras section.
167 tags = set(["template_hook"])
169 def parse(self, parser):
170 includes = []
171 expr = parser.parse_expression()
172 lineno = expr.lineno
173 hook_name = expr.args[0].value
175 for template_name in get_hook_templates(hook_name):
176 includes.append(
177 parser.parse_import_context(
178 Include(Const(template_name), True, False, lineno=lineno),
179 True))
181 return includes