1 # -*- coding: utf-8 -*-
2 # This file is part of django-ssify, licensed under GNU Affero GPLv3 or later.
3 # Copyright © Fundacja Nowoczesna Polska. See README.md for more information.
6 Defines decorators for use in ssify-enabled projects.
8 from __future__ import unicode_literals
10 from inspect import getargspec
12 from django.template.base import parse_bits
13 from django.utils.translation import get_language, activate
14 from .store import cache_include
15 from . import exceptions
16 from .variables import SsiVariable
19 def ssi_included(view=None, use_lang=True, get_ssi_vars=None):
21 Marks a view to be used as a snippet to be included with SSI.
23 If use_lang is True (which is default), the URL pattern for such
24 a view must provide a keyword argument named 'lang' for language.
25 SSI included views don't use language or content negotiation, so
26 everything they need to know has to be included in the URL.
28 get_ssi_vars should be a callable which takes the view's arguments
29 and returns the names of SSI variables it uses.
33 @functools.wraps(view)
34 def new_view(request, *args, **kwargs):
37 lang = kwargs.pop('lang')
39 raise exceptions.NoLangFieldError(request)
40 current_lang = get_language()
42 request.LANGUAGE_CODE = lang
43 response = view(request, *args, **kwargs)
45 activate(current_lang)
46 if response.status_code == 200:
47 # We don't want this view to be cached in
48 # UpdateCacheMiddleware. We'll just cache the contents
49 # ourselves, and point the webserver to use this cache.
50 request._cache_update_cache = False
52 def _check_included_vars(response):
53 used_vars = request.ssi_vars_needed
55 # Remove the ssi vars that should be provided
56 # by the including view.
57 pass_vars = get_ssi_vars(*args, **kwargs)
60 if not isinstance(var, SsiVariable):
61 var = SsiVariable(*var)
63 del used_vars[var.name]
66 exceptions.UnusedSsiVarsWarning(
69 raise exceptions.UndeclaredSsiVarsError(
71 request.ssi_vars_needed = {}
73 # Don't use default django response caching for this view,
74 # just save the contents instead.
75 cache_include(request.path, response.content)
77 if hasattr(response, 'render') and callable(response.render):
78 response.add_post_render_callback(_check_included_vars)
80 _check_included_vars(response)
84 # Remember get_ssi_vars so that in can be computed from args/kwargs
86 new_view.get_ssi_vars = get_ssi_vars
88 return dec(view) if view else dec
91 def ssi_variable(register, vary=None, name=None):
93 Creates a template tag representing an SSI variable from a function.
95 The function must take 'request' as its first argument.
96 It may take other arguments, which should be provided when using
104 function_name = (name or
105 getattr(func, '_decorated_function', func).__name__)
106 lib_name = func.__module__.rsplit('.', 1)[-1]
107 tagpath = "%s.%s" % (lib_name, function_name)
108 # Make sure the function takes request parameter.
109 params, varargs, varkw, defaults = getargspec(func)
110 assert params and params[0] == 'request', '%s is decorated with '\
111 'request_info_tag, so it must take `request` for '\
112 'its first argument.' % (tagpath)
114 @register.tag(name=function_name)
115 def _ssi_var_tag(parser, token):
117 Creates a SSI variable reference for a request-dependent info.
122 {% ri_tag args... as variable %}
125 {% ssi_include 'some-snippet' variable %}
131 bits = token.split_contents()[1:]
133 # Is it the 'as' form?
134 if len(bits) >= 2 and bits[-2] == 'as':
140 # Parse the arguments like Django's generic tags do.
141 args, kwargs = parse_bits(parser, bits,
142 ['context'] + params[1:], varargs, varkw,
143 defaults, takes_context=True,
145 return SsiVariableNode(tagpath, args, kwargs, vary, asvar)
146 _ssi_var_tag.get_value = func
153 from .variables import SsiVariableNode