Check for duplicate collection slugs and make them unique. Add unique constraint...
[mediagoblin.git] / mediagoblin / tools / template.py
1 # GNU MediaGoblin -- federated, autonomous media hosting
2 # Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS.
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
17 from math import ceil
18
19 import jinja2
20 from jinja2.ext import Extension
21 from jinja2.nodes import Include, Const
22
23 from babel.localedata import exists
24 from werkzeug.urls import url_quote_plus
25
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 get_gettext_translation
31 from mediagoblin.tools.pluginapi import get_hook_templates
32 from mediagoblin.tools.timesince import timesince
33 from mediagoblin.meddleware.csrf import render_csrf_form_token
34
35
36
37 SETUP_JINJA_ENVS = {}
38
39
40 def get_jinja_env(template_loader, locale):
41 """
42 Set up the Jinja environment,
43
44 (In the future we may have another system for providing theming;
45 for now this is good enough.)
46 """
47 mg_globals.thread_scope.translations = get_gettext_translation(locale)
48
49 # If we have a jinja environment set up with this locale, just
50 # return that one.
51 if locale in SETUP_JINJA_ENVS:
52 return SETUP_JINJA_ENVS[locale]
53
54 # jinja2.StrictUndefined will give exceptions on references
55 # to undefined/unknown variables in templates.
56 template_env = jinja2.Environment(
57 loader=template_loader, autoescape=True,
58 undefined=jinja2.StrictUndefined,
59 extensions=[
60 'jinja2.ext.i18n', 'jinja2.ext.autoescape',
61 TemplateHookExtension])
62
63 template_env.install_gettext_callables(
64 mg_globals.thread_scope.translations.ugettext,
65 mg_globals.thread_scope.translations.ungettext)
66
67 # All templates will know how to ...
68 # ... fetch all waiting messages and remove them from the queue
69 # ... construct a grid of thumbnails or other media
70 # ... have access to the global and app config
71 template_env.globals['fetch_messages'] = messages.fetch_messages
72 template_env.globals['app_config'] = mg_globals.app_config
73 template_env.globals['global_config'] = mg_globals.global_config
74 template_env.globals['version'] = _version.__version__
75
76 template_env.filters['urlencode'] = url_quote_plus
77
78 # add human readable fuzzy date time
79 template_env.globals['timesince'] = timesince
80
81 # allow for hooking up plugin templates
82 template_env.globals['get_hook_templates'] = get_hook_templates
83
84 if exists(locale):
85 SETUP_JINJA_ENVS[locale] = template_env
86
87 return template_env
88
89
90 # We'll store context information here when doing unit tests
91 TEMPLATE_TEST_CONTEXT = {}
92
93
94 def render_template(request, template_path, context):
95 """
96 Render a template with context.
97
98 Always inserts the request into the context, so you don't have to.
99 Also stores the context if we're doing unit tests. Helpful!
100 """
101 template = request.template_env.get_template(
102 template_path)
103 context['request'] = request
104 rendered_csrf_token = render_csrf_form_token(request)
105 if rendered_csrf_token is not None:
106 context['csrf_token'] = render_csrf_form_token(request)
107 rendered = template.render(context)
108
109 if common.TESTS_ENABLED:
110 TEMPLATE_TEST_CONTEXT[template_path] = context
111
112 return rendered
113
114
115 def clear_test_template_context():
116 global TEMPLATE_TEST_CONTEXT
117 TEMPLATE_TEST_CONTEXT = {}
118
119
120 class TemplateHookExtension(Extension):
121 """
122 Easily loop through a bunch of templates from a template hook.
123
124 Use:
125 {% template_hook("comment_extras") %}
126
127 ... will include all templates hooked into the comment_extras section.
128 """
129
130 tags = set(["template_hook"])
131
132 def parse(self, parser):
133 includes = []
134 expr = parser.parse_expression()
135 lineno = expr.lineno
136 hook_name = expr.args[0].value
137
138 for template_name in get_hook_templates(hook_name):
139 includes.append(
140 parser.parse_import_context(
141 Include(Const(template_name), True, False, lineno=lineno),
142 True))
143
144 return includes