Initial commit.
[django-ssify.git] / ssify / templatetags / ssify.py
1 # -*- coding: utf-8 -*-
2 # This file is part of Wolnelektury, licensed under GNU Affero GPLv3 or later.
3 # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
4 #
5 from __future__ import absolute_import
6 from django.conf import settings
7 from django.core.urlresolvers import NoReverseMatch, reverse, resolve
8 from django.middleware.csrf import get_token, rotate_token, _sanitize_token
9 from django import template
10 from django.utils.translation import get_language
11 from ssify.decorators import ssi_variable
12 from ssify.variables import SsiVariable
13
14
15 register = template.Library()
16
17
18 @register.simple_tag(takes_context=True)
19 def ssi_include(context, name_, **kwargs):
20     """
21     Inserts an SSI include statement for an URL.
22
23     Works similarly to {% url %}, but only use keyword arguments are
24     supported.
25
26     In addition to just outputting the SSI include statement, it
27     remembers any request-info the included piece declares as needed.
28
29     """
30     b_kwargs = {'lang': get_language()}
31     subst = {}
32     num = 0
33     for k, value in kwargs.items():
34         if isinstance(value, SsiVariable):
35             numstr = '%04d' % num
36             b_kwargs[k] = numstr
37             subst[numstr] = value
38             num += 1
39         else:
40             b_kwargs[k] = value
41
42     try:
43         url = reverse(name_, kwargs=b_kwargs)
44     except NoReverseMatch:
45         b_kwargs.pop('lang')
46         url = reverse(name_, kwargs=b_kwargs)
47     view = resolve(url).func
48
49     for numstr, orig in subst.items():
50         url = url.replace(numstr, orig.as_var())
51     request = context['request']
52
53     # Remember the SSI vars the included view says it needs.
54     get_ssi_vars = getattr(view, 'get_ssi_vars', None)
55     if get_ssi_vars:
56         pass_vars = get_ssi_vars(**kwargs)
57         for var in pass_vars:
58             if not isinstance(var, SsiVariable):
59                 var = SsiVariable(*var)
60             request.ssi_vars_needed[var.name] = var
61
62     # Output the SSI include.
63     return "<!--#include file='%s'-->" % url
64
65
66 @ssi_variable(register, vary=('Cookie',))
67 def get_csrf_token(request):
68     """
69     As CsrfViewMiddleware.process_view is never for a cached response,
70     and we still need to provide a request-specific CSRF token as
71     request-info ssi variable, we must make sure here that the
72     CSRF token is in request.META['CSRF_COOKIE'].
73
74     """
75     token = get_token(request)
76     if token:
77         # CSRF token is already in place, just return it.
78         return token
79
80     # Mimicking CsrfViewMiddleware.process_view.
81     try:
82         token = _sanitize_token(request.COOKIES[settings.CSRF_COOKIE_NAME])
83         request.META['CSRF_COOKIE'] = token
84     except KeyError:
85         # Create new CSRF token.
86         rotate_token(request)
87         token = get_token(request)
88
89     return token
90
91
92 @register.inclusion_tag('ssify/csrf_token.html', takes_context=True)
93 def csrf_token(context):
94     return {'request': context['request']}