Commit | Line | Data |
---|---|---|
ae3bc7fa | 1 | # GNU MediaGoblin -- federated, autonomous media hosting |
cf29e8a8 | 2 | # Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS. |
ae3bc7fa AW |
3 | # |
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. | |
8 | # | |
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. | |
13 | # | |
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/>. | |
16 | ||
927be5e8 | 17 | |
ae3bc7fa | 18 | import jinja2 |
927be5e8 CAW |
19 | from jinja2.ext import Extension |
20 | from jinja2.nodes import Include, Const | |
21 | ||
ae3bc7fa | 22 | from babel.localedata import exists |
620e4e1b SS |
23 | from werkzeug.urls import url_quote_plus |
24 | ||
ae3bc7fa AW |
25 | from mediagoblin import mg_globals |
26 | from mediagoblin import messages | |
2a0aed84 | 27 | from mediagoblin import _version |
ae3bc7fa | 28 | from mediagoblin.tools import common |
50cb5122 | 29 | from mediagoblin.tools.translate import set_thread_locale |
f7a5c7c7 | 30 | from mediagoblin.tools.pluginapi import get_hook_templates, hook_transform |
f1c3807d | 31 | from mediagoblin.tools.timesince import timesince |
ce5ae8da | 32 | from mediagoblin.meddleware.csrf import render_csrf_form_token |
ae3bc7fa | 33 | |
ee91c2b8 | 34 | |
ae3bc7fa AW |
35 | SETUP_JINJA_ENVS = {} |
36 | ||
ee91c2b8 | 37 | |
ae3bc7fa AW |
38 | def get_jinja_env(template_loader, locale): |
39 | """ | |
ee91c2b8 | 40 | Set up the Jinja environment, |
ae3bc7fa AW |
41 | |
42 | (In the future we may have another system for providing theming; | |
43 | for now this is good enough.) | |
44 | """ | |
50cb5122 | 45 | set_thread_locale(locale) |
ae3bc7fa AW |
46 | |
47 | # If we have a jinja environment set up with this locale, just | |
48 | # return that one. | |
04453ccf | 49 | if locale in SETUP_JINJA_ENVS: |
ae3bc7fa AW |
50 | return SETUP_JINJA_ENVS[locale] |
51 | ||
155d234d KD |
52 | # The default config does not require a [jinja2] block. |
53 | # You may create one if you wish to enable additional jinja2 extensions, | |
54 | # see example in config_spec.ini | |
55 | jinja2_config = mg_globals.global_config.get('jinja2', {}) | |
56 | local_exts = jinja2_config.get('extensions', []) | |
57 | ||
f1cdd278 E |
58 | # jinja2.StrictUndefined will give exceptions on references |
59 | # to undefined/unknown variables in templates. | |
ae3bc7fa AW |
60 | template_env = jinja2.Environment( |
61 | loader=template_loader, autoescape=True, | |
f1cdd278 | 62 | undefined=jinja2.StrictUndefined, |
927be5e8 CAW |
63 | extensions=[ |
64 | 'jinja2.ext.i18n', 'jinja2.ext.autoescape', | |
155d234d | 65 | TemplateHookExtension] + local_exts) |
ae3bc7fa AW |
66 | |
67 | template_env.install_gettext_callables( | |
c80982c7 JK |
68 | mg_globals.thread_scope.translations.ugettext, |
69 | mg_globals.thread_scope.translations.ungettext) | |
ae3bc7fa AW |
70 | |
71 | # All templates will know how to ... | |
72 | # ... fetch all waiting messages and remove them from the queue | |
73 | # ... construct a grid of thumbnails or other media | |
53bc3975 | 74 | # ... have access to the global and app config |
ae3bc7fa | 75 | template_env.globals['fetch_messages'] = messages.fetch_messages |
6950c6c7 CAW |
76 | template_env.globals['app_config'] = mg_globals.app_config |
77 | template_env.globals['global_config'] = mg_globals.global_config | |
2a0aed84 | 78 | template_env.globals['version'] = _version.__version__ |
744f1c83 | 79 | template_env.globals['auth'] = mg_globals.app.auth |
ae3bc7fa | 80 | |
620e4e1b SS |
81 | template_env.filters['urlencode'] = url_quote_plus |
82 | ||
f1c3807d J |
83 | # add human readable fuzzy date time |
84 | template_env.globals['timesince'] = timesince | |
85 | ||
a3f811a6 CAW |
86 | # allow for hooking up plugin templates |
87 | template_env.globals['get_hook_templates'] = get_hook_templates | |
88 | ||
f7a5c7c7 CAW |
89 | template_env.globals = hook_transform( |
90 | 'template_global_context', template_env.globals) | |
91 | ||
7949d88a CAW |
92 | #### THIS IS TEMPORARY, PLEASE FIX IT |
93 | ## Notifications stuff is not yet a plugin (and we're not sure it will be), | |
94 | ## but it needs to add stuff to the context. This is THE WRONG WAY TO DO IT | |
95 | from mediagoblin import notifications | |
96 | template_env.globals['get_notifications'] = notifications.get_notifications | |
29cd702e CAW |
97 | template_env.globals[ |
98 | 'get_notification_count'] = notifications.get_notification_count | |
7949d88a CAW |
99 | template_env.globals[ |
100 | 'get_comment_subscription'] = notifications.get_comment_subscription | |
101 | ||
ae3bc7fa AW |
102 | if exists(locale): |
103 | SETUP_JINJA_ENVS[locale] = template_env | |
104 | ||
105 | return template_env | |
106 | ||
ee91c2b8 | 107 | |
ae3bc7fa AW |
108 | # We'll store context information here when doing unit tests |
109 | TEMPLATE_TEST_CONTEXT = {} | |
110 | ||
111 | ||
112 | def render_template(request, template_path, context): | |
113 | """ | |
114 | Render a template with context. | |
115 | ||
116 | Always inserts the request into the context, so you don't have to. | |
117 | Also stores the context if we're doing unit tests. Helpful! | |
118 | """ | |
119 | template = request.template_env.get_template( | |
120 | template_path) | |
121 | context['request'] = request | |
71c6c432 E |
122 | rendered_csrf_token = render_csrf_form_token(request) |
123 | if rendered_csrf_token is not None: | |
124 | context['csrf_token'] = render_csrf_form_token(request) | |
38103094 CAW |
125 | |
126 | # allow plugins to do things to the context | |
f7a5c7c7 CAW |
127 | if request.controller_name: |
128 | context = hook_transform( | |
129 | (request.controller_name, template_path), | |
130 | context) | |
38103094 | 131 | |
1c6d2e87 CAW |
132 | # More evil: allow plugins to possibly do something to the context |
133 | # in every request ever with access to the request and other | |
134 | # variables. Note: this is slower than using | |
135 | # template_global_context | |
136 | context = hook_transform( | |
137 | 'template_context_prerender', context) | |
138 | ||
ae3bc7fa | 139 | rendered = template.render(context) |
ee91c2b8 CAW |
140 | |
141 | if common.TESTS_ENABLED: | |
ae3bc7fa AW |
142 | TEMPLATE_TEST_CONTEXT[template_path] = context |
143 | ||
144 | return rendered | |
145 | ||
146 | ||
147 | def clear_test_template_context(): | |
148 | global TEMPLATE_TEST_CONTEXT | |
149 | TEMPLATE_TEST_CONTEXT = {} | |
927be5e8 CAW |
150 | |
151 | ||
152 | class TemplateHookExtension(Extension): | |
153 | """ | |
154 | Easily loop through a bunch of templates from a template hook. | |
155 | ||
156 | Use: | |
157 | {% template_hook("comment_extras") %} | |
158 | ||
159 | ... will include all templates hooked into the comment_extras section. | |
160 | """ | |
161 | ||
8a4d0dbc | 162 | tags = set(["template_hook"]) |
927be5e8 CAW |
163 | |
164 | def parse(self, parser): | |
165 | includes = [] | |
166 | expr = parser.parse_expression() | |
167 | lineno = expr.lineno | |
168 | hook_name = expr.args[0].value | |
169 | ||
170 | for template_name in get_hook_templates(hook_name): | |
171 | includes.append( | |
172 | parser.parse_import_context( | |
173 | Include(Const(template_name), True, False, lineno=lineno), | |
174 | True)) | |
175 | ||
176 | return includes |