2 | {% if flatblock.header %}
3 |
{{ flatblock.header }}
4 | {% endif %}
5 |
{{ flatblock.content|safe }}
6 |
7 |
--------------------------------------------------------------------------------
/flatblocks/templatetags/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zerok/django-flatblocks/a5b93d3b61d525b02099bd4fce6a3c38ec41c02e/flatblocks/templatetags/__init__.py
--------------------------------------------------------------------------------
/flatblocks/templatetags/flatblock_tags.py:
--------------------------------------------------------------------------------
1 | """
2 | This module offers one templatetag called "flatblock" which allows you to
3 | easily embed small text-snippets (like for example the help section of a page)
4 | into a template.
5 |
6 | It accepts 2 parameter:
7 |
8 | slug
9 | The slug/key of the text (for example 'contact_help'). There are two
10 | ways you can pass the slug to the templatetag: (1) by its name or
11 | (2) as a variable.
12 |
13 | If you want to pass it by name, you have to use quotes on it.
14 | Otherwise just use the variable name.
15 |
16 | cache_time
17 | The number of seconds that text should get cached after it has been
18 | fetched from the database.
19 |
20 | This field is optional and defaults to no caching (0).
21 |
22 | To use Django's default caching length use None.
23 |
24 | Example::
25 |
26 | {% load flatblock_tags %}
27 |
28 | ...
29 |
30 | {% flatblock 'contact_help' %}
31 | {% flatblock name_in_variable %}
32 |
33 | The 'flatblock' template tag acts like an inclusiontag and operates on the
34 | ``flatblock/flatblock.html`` template file, which gets (besides the global
35 | context) also the ``flatblock`` variable passed.
36 |
37 | Compared to the original implementation this includes not only the block's
38 | content but the whole object inclusing title, slug and id. This way you
39 | can easily for example offer administrative operations (like editing)
40 | within that template.
41 |
42 | """
43 |
44 | from django import template
45 | from django.template import loader
46 | from django.db import models
47 | from django.core.cache import cache
48 |
49 | from flatblocks import settings
50 |
51 | import logging
52 |
53 |
54 | register = template.Library()
55 | logger = logging.getLogger(__name__)
56 |
57 | FlatBlock = models.get_model('flatblocks', 'flatblock')
58 |
59 |
60 | class BasicFlatBlockWrapper(object):
61 | def prepare(self, parser, token):
62 | """
63 | The parser checks for following tag-configurations::
64 |
65 | {% flatblock {block} %}
66 | {% flatblock {block} {timeout} %}
67 | {% flatblock {block} using {tpl_name} %}
68 | {% flatblock {block} {timeout} using {tpl_name} %}
69 | {% flatblock {block} evaluated %}
70 | {% flatblock {block} evaluated using {tpl_name} %}
71 | {% flatblock {block} {timeout} evaluated %}
72 | {% flatblock {block} {timeout} evaluated using {tpl_name} %}
73 | """
74 | tokens = token.split_contents()
75 | self.is_variable = False
76 | self.tpl_is_variable = False
77 | self.slug = None
78 | self.cache_time = 0
79 | self.tpl_name = None
80 | self.evaluated = False
81 | tag_name, self.slug, args = tokens[0], tokens[1], tokens[2:]
82 | num_args = len(args)
83 | if num_args == 0:
84 | # Only the block name was specified
85 | pass
86 | elif num_args == 1:
87 | # block and timeout
88 | if args[0] == 'evaluated':
89 | self.evaluated = True
90 | else:
91 | self.cache_time = args[0]
92 | elif num_args == 2:
93 | if args[0] != 'using':
94 | # block, timeout, "evaluated"
95 | if args[1] != 'evaluated':
96 | raise template.TemplateSyntaxError("{0!r} tag with two "
97 | "arguments has to "
98 | "include the cache "
99 | "timeout and the "
100 | "evaluated flag".format(
101 | tag_name))
102 | self.cache_time = args[0]
103 | self.evaluated = True
104 | else:
105 | # block, "using", tpl_name
106 | self.tpl_name = args[1]
107 | elif num_args == 3:
108 | # block, timeout|"evaluated", "using", tpl_name
109 | if args[0] == 'evaluated':
110 | self.evaluated = True
111 | else:
112 | self.cache_time = args[0]
113 | self.tpl_name = args[2]
114 | elif num_args == 4:
115 | self.cache_time = args[0]
116 | self.evaluated = True
117 | self.tpl_name = args[3]
118 | else:
119 | raise template.TemplateSyntaxError("{0!r} tag should have between "
120 | "1 and 5 arguments".format(
121 | tokens[0]))
122 | # Check to see if the slug is properly double/single quoted
123 | if not (self.slug[0] == self.slug[-1] and self.slug[0] in ('"', "'")):
124 | self.is_variable = True
125 | else:
126 | self.slug = self.slug[1:-1]
127 | # Clean up the template name
128 | if self.tpl_name is not None:
129 | if not(self.tpl_name[0] == self.tpl_name[-1] and
130 | self.tpl_name[0] in ('"', "'")):
131 | self.tpl_is_variable = True
132 | else:
133 | self.tpl_name = self.tpl_name[1:-1]
134 | if self.cache_time is not None and self.cache_time != 'None':
135 | self.cache_time = int(self.cache_time)
136 |
137 | def __call__(self, parser, token):
138 | self.prepare(parser, token)
139 | return FlatBlockNode(self.slug, self.is_variable, self.cache_time,
140 | template_name=self.tpl_name,
141 | tpl_is_variable=self.tpl_is_variable,
142 | evaluated=self.evaluated)
143 |
144 |
145 | class PlainFlatBlockWrapper(BasicFlatBlockWrapper):
146 | def __call__(self, parser, token):
147 | self.prepare(parser, token)
148 | return FlatBlockNode(self.slug, self.is_variable, self.cache_time,
149 | False, evaluated=self.evaluated)
150 |
151 |
152 | do_get_flatblock = BasicFlatBlockWrapper()
153 | do_plain_flatblock = PlainFlatBlockWrapper()
154 |
155 |
156 | class FlatBlockNode(template.Node):
157 | def __init__(self, slug, is_variable, cache_time=0, with_template=True,
158 | template_name=None, tpl_is_variable=False, evaluated=False):
159 | if template_name is None:
160 | self.template_name = 'flatblocks/flatblock.html'
161 | else:
162 | if tpl_is_variable:
163 | self.template_name = template.Variable(template_name)
164 | else:
165 | self.template_name = template_name
166 | self.slug = slug
167 | self.is_variable = is_variable
168 | self.cache_time = cache_time
169 | self.with_template = with_template
170 | self.evaluated = evaluated
171 |
172 | def render(self, context):
173 | if self.is_variable:
174 | real_slug = template.Variable(self.slug).resolve(context)
175 | else:
176 | real_slug = self.slug
177 | if isinstance(self.template_name, template.Variable):
178 | real_template = self.template_name.resolve(context)
179 | else:
180 | real_template = self.template_name
181 | # Eventually we want to pass the whole context to the template so that
182 | # users have the maximum of flexibility of what to do in there.
183 | if self.with_template:
184 | new_ctx = template.Context({})
185 | new_ctx.update(context)
186 | try:
187 | flatblock = None
188 | if self.cache_time != 0:
189 | cache_key = settings.CACHE_PREFIX + real_slug
190 | flatblock = cache.get(cache_key)
191 | if flatblock is None:
192 |
193 | # if flatblock's slug is hard-coded in template then it is
194 | # safe and convenient to auto-create block if it doesn't exist.
195 | # This behaviour can be configured using the
196 | # FLATBLOCKS_AUTOCREATE_STATIC_BLOCKS setting
197 | if self.is_variable or not settings.AUTOCREATE_STATIC_BLOCKS:
198 | flatblock = FlatBlock.objects.get(slug=real_slug)
199 | else:
200 | flatblock, _ = FlatBlock.objects.get_or_create(
201 | slug=real_slug,
202 | defaults={'content': real_slug}
203 | )
204 | if self.cache_time != 0:
205 | if self.cache_time is None or self.cache_time == 'None':
206 | logger.debug("Caching {0} for the cache's default "
207 | "timeout".format(real_slug))
208 | cache.set(cache_key, flatblock)
209 | else:
210 | logger.debug("Caching {0} for {1} seconds".format(
211 | real_slug, str(self.cache_time)))
212 | cache.set(cache_key, flatblock, int(self.cache_time))
213 | else:
214 | logger.debug("Don't cache %s" % (real_slug,))
215 |
216 | if self.evaluated:
217 | flatblock.raw_content = flatblock.content
218 | flatblock.raw_header = flatblock.header
219 | flatblock.content = self._evaluate(flatblock.content, context)
220 | flatblock.header = self._evaluate(flatblock.header, context)
221 |
222 | if self.with_template:
223 | tmpl = loader.get_template(real_template)
224 | new_ctx.update({'flatblock': flatblock})
225 | return tmpl.render(new_ctx)
226 | else:
227 | return flatblock.content
228 | except FlatBlock.DoesNotExist:
229 | return ''
230 |
231 | def _evaluate(self, content, context):
232 | return template.Template(content).render(context)
233 |
234 | register.tag('flatblock', do_get_flatblock)
235 | register.tag('plain_flatblock', do_plain_flatblock)
236 |
--------------------------------------------------------------------------------
/flatblocks/tests.py:
--------------------------------------------------------------------------------
1 | from django import template
2 | from django.test import TestCase
3 | from django.core.cache import cache
4 | from django.contrib.auth.models import User
5 | from django import db
6 |
7 | from flatblocks.models import FlatBlock
8 | from flatblocks.templatetags.flatblock_tags import do_get_flatblock
9 | from flatblocks import settings
10 |
11 |
12 | class BasicTests(TestCase):
13 | urls = 'flatblocks.urls'
14 |
15 | def setUp(self):
16 | self.testblock = FlatBlock.objects.create(slug='block',
17 | header='HEADER',
18 | content='CONTENT'
19 | )
20 | self.admin = User.objects.create_superuser('admin', 'admin@localhost',
21 | 'adminpwd')
22 |
23 | def testURLConf(self):
24 | # We have to support two different APIs here (1.1 and 1.2)
25 | def get_tmpl(resp):
26 | if hasattr(resp, 'templates'):
27 | return resp.templates[0]
28 | else:
29 | if isinstance(resp.template, list):
30 | return resp.template[0]
31 | return resp.template
32 | self.assertEqual(get_tmpl(self.client.get('/edit/1/')).name,
33 | 'admin/login.html')
34 | self.client.login(username='admin', password='adminpwd')
35 | self.assertEqual(get_tmpl(self.client.get('/edit/1/')).name,
36 | 'flatblocks/edit.html')
37 |
38 | def testCacheReset(self):
39 | """
40 | Tests if FlatBlock.save() resets the cache.
41 | """
42 | tpl = template.Template(
43 | '{% load flatblock_tags %}{% flatblock "block" 60 %}')
44 | tpl.render(template.Context({}))
45 | name = '%sblock' % settings.CACHE_PREFIX
46 | self.assertNotEqual(None, cache.get(name))
47 | block = FlatBlock.objects.get(slug='block')
48 | block.header = 'UPDATED'
49 | block.save()
50 | self.assertEqual(None, cache.get(name))
51 |
52 | def testSaveForceUpdate(self):
53 | block = FlatBlock(slug='missing')
54 | with self.assertRaises(ValueError):
55 | block.save(force_update=True)
56 |
57 | def testSaveForceInsert(self):
58 | block = FlatBlock.objects.get(slug='block')
59 | with self.assertRaises(db.IntegrityError):
60 | block.save(force_insert=True)
61 |
62 | def testCacheRemoval(self):
63 | """
64 | If a block is deleted it should also be removed from the cache.
65 | """
66 | block = FlatBlock(slug="test", content="CONTENT")
67 | block.save()
68 | tpl = template.Template(
69 | '{% load flatblock_tags %}{% flatblock "test" 100 %}')
70 | # We fill the cache by rendering the block
71 | tpl.render(template.Context({}))
72 | cache_key = "%stest" % settings.CACHE_PREFIX
73 | self.assertNotEqual(None, cache.get(cache_key))
74 | block.delete()
75 | self.assertEqual(None, cache.get(cache_key))
76 |
77 |
78 | class TagTests(TestCase):
79 | def setUp(self):
80 | self.testblock = FlatBlock.objects.create(slug='block',
81 | header='HEADER',
82 | content='CONTENT'
83 | )
84 |
85 | def testLoadingTaglib(self):
86 | """Tests if the taglib defined in this app can be loaded"""
87 | tpl = template.Template('{% load flatblock_tags %}')
88 | tpl.render(template.Context({}))
89 |
90 | def testExistingPlain(self):
91 | tpl = template.Template(
92 | '{% load flatblock_tags %}{% plain_flatblock "block" %}')
93 | self.assertEqual('CONTENT', tpl.render(template.Context({})).strip())
94 |
95 | def testExistingTemplate(self):
96 | expected = """