Added copyright notice.
[toast/alpenzoo.git] / django_template_backend_genshi / __init__.py
1 # Copyright (c) 2015 Gregor Herrmann, Philipp Spitzer.
2 # All rights reserved.
3 #
4 # Redistribution and use in source and binary forms, with or without modification,
5 # are permitted provided that the following conditions are met:
6 #
7 # 1. Redistributions of source code must retain the above copyright notice,
8 # this list of conditions and the following disclaimer.
9 #
10 # 2. Redistributions in binary form must reproduce the above copyright
11 # notice, this list of conditions and the following disclaimer in the
12 # documentation and/or other materials provided with the distribution.
13 #
14 # 3. The names of the authors may be used
15 # to endorse or promote products derived from this software without
16 # specific prior written permission.
17 #
18 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
19 # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
22 # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
25 # ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 """Genshi Engine for Django 1.8
29
30 The following context variable are already pre-configured and usable:
31     'static'     # staticfiles_storage.url
32     'url'        # reverse
33     'request'    # request
34     'csrf_input' # csrf_input_lazy(request)
35     'csrf_token' # csrf_token_lazy(request)
36
37
38 Usage/configuration:
39     In your settings.py, change the TEMPLATE section to the following:
40
41     TEMPLATES = [
42         {
43             'BACKEND': 'django_template_backend_genshi.GenshiEngine',
44             'DIRS': [],
45             'APP_DIRS': True,
46             'OPTIONS': {
47                 'auto_reload': True,
48                 'app_dirname': 'templates',
49                 'serialization': 'xhtml',
50             },
51         },
52     ]
53
54
55 Questions with answers:
56     How do I add additional context variables or functions that are available to every template?
57
58         Define a receiver function of the signal template_render and add context variables:
59
60         from django.dispatch import receiver
61         from django_template_backend_genshi import template_render
62
63         @receiver(template_render)
64         def add_render_globals(sender, **kwargs):
65             genshi_context = kwargs['genshi_context']
66             request = genshi_context['request']
67             genshi_context['mynewvar'] = len(request)
68 """
69
70 from django.template.backends.utils import csrf_input_lazy, csrf_token_lazy
71 import django.template.backends.base
72 import django.dispatch
73 from django.core.urlresolvers import reverse
74 from django.contrib.staticfiles.storage import staticfiles_storage
75 import genshi.template
76 import genshi.template.loader
77 import genshi.template.base
78
79
80 class GenshiEngine(django.template.backends.base.BaseEngine):
81     app_dirname = 'genshi'
82
83     def __init__(self, params):
84         params = params.copy()
85         options = params.pop('OPTIONS').copy()
86         self.app_dirname = options.get('app_dirname', self.app_dirname)
87         super(GenshiEngine, self).__init__(params)
88         loader = genshi.template.TemplateLoader(self.template_dirs, auto_reload=options['auto_reload'])
89         self.loader = loader
90         self.serialization = options.get('serialization', 'xhtml')
91
92     def from_string(self, template_code):
93         try:
94             template = genshi.template.MarkupTemplate(template_code)
95             return GenshiTemplateWrapper(template, self.serialization)
96         except genshi.template.base.TemplateSyntaxError as exc:
97             raise django.template.TemplateSyntaxError(exc.args)
98
99     def get_template(self, template_name):
100         try:
101             template = self.loader.load(template_name)
102             return GenshiTemplateWrapper(template, self.serialization)
103         except genshi.template.loader.TemplateNotFound as exc:
104             raise django.template.TemplateDoesNotExist(exc.args)
105         except genshi.template.base.TemplateSyntaxError as exc:
106             raise django.template.TemplateSyntaxError(exc.args)
107
108
109 # signal emitted just before the template is rendered by GenshiTemplateWrapper to have an opportunity to add
110 # context variables. genshi_context is the Genshi context (genshi.template.base.Context()) already filles with
111 # 'static' and 'url' and - if it's a request - 'request', 'csrf_input' and 'csrf_token'.
112 template_render = django.dispatch.Signal(providing_args=["genshi_context"])
113
114
115 class GenshiTemplateWrapper(object):
116     def __init__(self, template, serialization):
117         self.template = template
118         self.serialization = serialization
119
120     def render(self, context=None, request=None):
121         genshi_context = genshi.template.base.Context()
122         genshi_context['static'] = staticfiles_storage.url
123         genshi_context['url'] = reverse
124         if request is not None:
125             genshi_context['request'] = request
126             genshi_context['csrf_input'] = csrf_input_lazy(request)
127             genshi_context['csrf_token'] = csrf_token_lazy(request)
128         if context is not None:
129             genshi_context.push(context)
130         template_render.send(self, genshi_context=genshi_context)
131         stream = self.template.generate(genshi_context)
132         # this might raise a genshi.template.eval.UndefinedError (derived from genshi.template.base.TemplateRuntimeError)
133         return stream.render(self.serialization)