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/>.
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
.translate
import get_locale_from_request
33 from mediagoblin
.tools
.pluginapi
import get_hook_templates
, hook_transform
34 from mediagoblin
.tools
.timesince
import timesince
35 from mediagoblin
.meddleware
.csrf
import render_csrf_form_token
40 def get_jinja_env(template_loader
, locale
):
42 Set up the Jinja environment,
44 (In the future we may have another system for providing theming;
45 for now this is good enough.)
47 set_thread_locale(locale
)
49 # If we have a jinja environment set up with this locale, just
51 if locale
in SETUP_JINJA_ENVS
:
52 return SETUP_JINJA_ENVS
[locale
]
54 # The default config does not require a [jinja2] block.
55 # You may create one if you wish to enable additional jinja2 extensions,
56 # see example in config_spec.ini
57 jinja2_config
= mg_globals
.global_config
.get('jinja2', {})
58 local_exts
= jinja2_config
.get('extensions', [])
60 # jinja2.StrictUndefined will give exceptions on references
61 # to undefined/unknown variables in templates.
62 template_env
= jinja2
.Environment(
63 loader
=template_loader
, autoescape
=True,
64 undefined
=jinja2
.StrictUndefined
,
66 'jinja2.ext.i18n', 'jinja2.ext.autoescape',
67 TemplateHookExtension
] + local_exts
)
70 template_env
.install_gettext_callables(mg_globals
.thread_scope
.translations
.ugettext
,
71 mg_globals
.thread_scope
.translations
.ungettext
)
73 template_env
.install_gettext_callables(mg_globals
.thread_scope
.translations
.gettext
,
74 mg_globals
.thread_scope
.translations
.ngettext
)
76 # All templates will know how to ...
77 # ... fetch all waiting messages and remove them from the queue
78 # ... construct a grid of thumbnails or other media
79 # ... have access to the global and app config
80 # ... determine if the language is rtl or ltr
81 template_env
.globals['fetch_messages'] = messages
.fetch_messages
82 template_env
.globals['app_config'] = mg_globals
.app_config
83 template_env
.globals['global_config'] = mg_globals
.global_config
84 template_env
.globals['version'] = _version
.__version
__
85 template_env
.globals['auth'] = mg_globals
.app
.auth
86 template_env
.globals['is_rtl'] = is_rtl(locale
)
87 template_env
.filters
['urlencode'] = url_quote_plus
89 # add human readable fuzzy date time
90 template_env
.globals['timesince'] = timesince
92 # allow for hooking up plugin templates
93 template_env
.globals['get_hook_templates'] = get_hook_templates
95 template_env
.globals = hook_transform(
96 'template_global_context', template_env
.globals)
98 #### THIS IS TEMPORARY, PLEASE FIX IT
99 ## Notifications stuff is not yet a plugin (and we're not sure it will be),
100 ## but it needs to add stuff to the context. This is THE WRONG WAY TO DO IT
101 from mediagoblin
import notifications
102 template_env
.globals['get_notifications'] = notifications
.get_notifications
103 template_env
.globals[
104 'get_notification_count'] = notifications
.get_notification_count
105 template_env
.globals[
106 'get_comment_subscription'] = notifications
.get_comment_subscription
109 SETUP_JINJA_ENVS
[locale
] = template_env
114 # We'll store context information here when doing unit tests
115 TEMPLATE_TEST_CONTEXT
= {}
118 def render_template(request
, template_path
, context
):
120 Render a template with context.
122 Always inserts the request into the context, so you don't have to.
123 Also stores the context if we're doing unit tests. Helpful!
125 template
= request
.template_env
.get_template(
127 context
['request'] = request
128 rendered_csrf_token
= render_csrf_form_token(request
)
129 if rendered_csrf_token
is not None:
130 context
['csrf_token'] = render_csrf_form_token(request
)
132 # allow plugins to do things to the context
133 if request
.controller_name
:
134 context
= hook_transform(
135 (request
.controller_name
, template_path
),
138 # More evil: allow plugins to possibly do something to the context
139 # in every request ever with access to the request and other
140 # variables. Note: this is slower than using
141 # template_global_context
142 context
= hook_transform(
143 'template_context_prerender', context
)
145 rendered
= template
.render(context
)
147 if common
.TESTS_ENABLED
:
148 TEMPLATE_TEST_CONTEXT
[template_path
] = context
153 def clear_test_template_context():
154 global TEMPLATE_TEST_CONTEXT
155 TEMPLATE_TEST_CONTEXT
= {}
158 class TemplateHookExtension(Extension
):
160 Easily loop through a bunch of templates from a template hook.
163 {% template_hook("comment_extras") %}
165 ... will include all templates hooked into the comment_extras section.
168 tags
= set(["template_hook"])
170 def parse(self
, parser
):
172 expr
= parser
.parse_expression()
174 hook_name
= expr
.args
[0].value
176 for template_name
in get_hook_templates(hook_name
):
178 parser
.parse_import_context(
179 Include(Const(template_name
), True, False, lineno
=lineno
),