1
|
|
|
"""Page CMS page_tags template tags""" |
2
|
|
|
from django import template |
3
|
|
|
from django.utils.safestring import SafeText |
4
|
|
|
from django.template import TemplateSyntaxError |
5
|
|
|
from django.conf import settings |
6
|
|
|
from django.utils.text import unescape_string_literal |
7
|
|
|
|
8
|
|
|
from pages import settings as pages_settings |
9
|
|
|
from pages.models import Content, Page |
10
|
|
|
from pages.placeholders import PlaceholderNode, ImagePlaceholderNode, FilePlaceholderNode |
11
|
|
|
from pages.placeholders import ContactPlaceholderNode, MarkdownPlaceholderNode |
12
|
|
|
from pages.placeholders import JsonPlaceholderNode, parse_placeholder |
13
|
|
|
from six.moves import urllib |
14
|
|
|
import six |
15
|
|
|
|
16
|
|
|
register = template.Library() |
17
|
|
|
|
18
|
|
|
|
19
|
|
|
def get_page_from_string_or_id(page_string, lang=None): |
20
|
|
|
"""Return a Page object from a slug or an id.""" |
21
|
|
|
if type(page_string) == int: |
22
|
|
|
return Page.objects.get(pk=int(page_string)) |
23
|
|
|
# if we have a string coming from some templates templates |
24
|
|
|
if (isinstance(page_string, SafeText) or |
25
|
|
|
isinstance(page_string, six.string_types)): |
26
|
|
|
if page_string.isdigit(): |
27
|
|
|
return Page.objects.get(pk=int(page_string)) |
28
|
|
|
return Page.objects.from_path(page_string, lang) |
29
|
|
|
# in any other case we return the input becasue it's probably |
30
|
|
|
# a Page object. |
31
|
|
|
return page_string |
32
|
|
|
|
33
|
|
|
def _get_content(context, page, content_type, lang, fallback=True): |
34
|
|
|
"""Helper function used by ``PlaceholderNode``.""" |
35
|
|
|
if not page: |
36
|
|
|
return '' |
37
|
|
|
|
38
|
|
|
if not lang and 'lang' in context: |
39
|
|
|
lang = context.get('lang', pages_settings.PAGE_DEFAULT_LANGUAGE) |
40
|
|
|
|
41
|
|
|
page = get_page_from_string_or_id(page, lang) |
42
|
|
|
|
43
|
|
|
if not page: |
44
|
|
|
return '' |
45
|
|
|
|
46
|
|
|
content = Content.objects.get_content(page, lang, content_type, fallback) |
47
|
|
|
return content |
48
|
|
|
|
49
|
|
|
"""Filters""" |
50
|
|
|
|
51
|
|
|
|
52
|
|
|
def has_content_in(page, language): |
53
|
|
|
"""Fitler that return ``True`` if the page has any content in a |
54
|
|
|
particular language. |
55
|
|
|
|
56
|
|
|
:param page: the current page |
57
|
|
|
:param language: the language you want to look at |
58
|
|
|
""" |
59
|
|
|
return Content.objects.filter(page=page, language=language).count() > 0 |
60
|
|
|
|
61
|
|
|
|
62
|
|
|
register.filter(has_content_in) |
63
|
|
|
|
64
|
|
|
|
65
|
|
|
"""Inclusion tags""" |
66
|
|
|
|
67
|
|
|
|
68
|
|
|
def pages_menu(context, page, url='/'): |
69
|
|
|
"""Render a nested list of all the descendents of the given page, |
70
|
|
|
including this page. |
71
|
|
|
|
72
|
|
|
:param page: the page where to start the menu from. |
73
|
|
|
:param url: not used anymore. |
74
|
|
|
""" |
75
|
|
|
lang = context.get('lang', pages_settings.PAGE_DEFAULT_LANGUAGE) |
76
|
|
|
page = get_page_from_string_or_id(page, lang) |
77
|
|
|
if page: |
78
|
|
|
children = page.get_children_for_frontend() |
79
|
|
|
context.update({'children': children, 'page': page}) |
80
|
|
|
return context |
81
|
|
|
|
82
|
|
|
|
83
|
|
|
pages_menu = register.inclusion_tag('pages/menu.html', |
84
|
|
|
takes_context=True)(pages_menu) |
85
|
|
|
|
86
|
|
|
|
87
|
|
|
def pages_sub_menu(context, page, url='/'): |
88
|
|
|
"""Get the root page of the given page and |
89
|
|
|
render a nested list of all root's children pages. |
90
|
|
|
Good for rendering a secondary menu. |
91
|
|
|
|
92
|
|
|
:param page: the page where to start the menu from. |
93
|
|
|
:param url: not used anymore. |
94
|
|
|
""" |
95
|
|
|
lang = context.get('lang', pages_settings.PAGE_DEFAULT_LANGUAGE) |
96
|
|
|
page = get_page_from_string_or_id(page, lang) |
97
|
|
|
if page: |
98
|
|
|
root = page.get_root() |
99
|
|
|
children = root.get_children_for_frontend() |
100
|
|
|
context.update({'children': children, 'page': page}) |
101
|
|
|
return context |
102
|
|
|
|
103
|
|
|
|
104
|
|
|
pages_sub_menu = register.inclusion_tag('pages/sub_menu.html', |
105
|
|
|
takes_context=True)(pages_sub_menu) |
106
|
|
|
|
107
|
|
|
|
108
|
|
|
def pages_siblings_menu(context, page, url='/'): |
109
|
|
|
"""Get the parent page of the given page and render a nested list of its |
110
|
|
|
child pages. Good for rendering a secondary menu. |
111
|
|
|
|
112
|
|
|
:param page: the page where to start the menu from. |
113
|
|
|
:param url: not used anymore. |
114
|
|
|
""" |
115
|
|
|
lang = context.get('lang', pages_settings.PAGE_DEFAULT_LANGUAGE) |
116
|
|
|
page = get_page_from_string_or_id(page, lang) |
117
|
|
|
if page: |
118
|
|
|
siblings = page.get_siblings() |
119
|
|
|
context.update({'children': siblings, 'page': page}) |
120
|
|
|
return context |
121
|
|
|
|
122
|
|
|
|
123
|
|
|
pages_siblings_menu = register.inclusion_tag('pages/sub_menu.html', |
124
|
|
|
takes_context=True)(pages_siblings_menu) |
125
|
|
|
|
126
|
|
|
|
127
|
|
|
def pages_admin_menu(context, page): |
128
|
|
|
"""Render the admin table of pages.""" |
129
|
|
|
request = context.get('request', None) |
130
|
|
|
|
131
|
|
|
expanded = False |
132
|
|
|
if request and "tree_expanded" in request.COOKIES: |
133
|
|
|
cookie_string = urllib.parse.unquote(request.COOKIES['tree_expanded']) |
134
|
|
|
if cookie_string: |
135
|
|
|
ids = [int(id) for id in |
136
|
|
|
urllib.parse.unquote(request.COOKIES['tree_expanded']).split(',')] |
137
|
|
|
if page.id in ids: |
138
|
|
|
expanded = True |
139
|
|
|
context.update({'expanded': expanded, 'page': page}) |
140
|
|
|
return context |
141
|
|
|
|
142
|
|
|
|
143
|
|
|
pages_admin_menu = register.inclusion_tag( |
144
|
|
|
'admin/pages/page/menu.html', takes_context=True |
145
|
|
|
)(pages_admin_menu) |
146
|
|
|
|
147
|
|
|
|
148
|
|
|
def show_content(context, page, content_type, lang=None, fallback=True): |
149
|
|
|
"""Display a content type from a page. |
150
|
|
|
|
151
|
|
|
Example:: |
152
|
|
|
|
153
|
|
|
{% show_content page_object "title" %} |
154
|
|
|
|
155
|
|
|
You can also use the slug of a page:: |
156
|
|
|
|
157
|
|
|
{% show_content "my-page-slug" "title" %} |
158
|
|
|
|
159
|
|
|
Or even the id of a page:: |
160
|
|
|
|
161
|
|
|
{% show_content 10 "title" %} |
162
|
|
|
|
163
|
|
|
:param page: the page object, slug or id |
164
|
|
|
:param content_type: content_type used by a placeholder |
165
|
|
|
:param lang: the wanted language |
166
|
|
|
(default None, use the request object to know) |
167
|
|
|
:param fallback: use fallback content from other language |
168
|
|
|
""" |
169
|
|
|
return {'content': _get_content( |
170
|
|
|
context, page, content_type, lang, fallback) |
171
|
|
|
} |
172
|
|
|
|
173
|
|
|
|
174
|
|
|
show_content = register.inclusion_tag('pages/content.html', |
175
|
|
|
takes_context=True)(show_content) |
176
|
|
|
|
177
|
|
|
|
178
|
|
|
def show_absolute_url(context, page, lang=None): |
179
|
|
|
""" |
180
|
|
|
Show the url of a page in the right language |
181
|
|
|
|
182
|
|
|
Example :: |
183
|
|
|
|
184
|
|
|
{% show_absolute_url page_object %} |
185
|
|
|
|
186
|
|
|
You can also use the slug of a page:: |
187
|
|
|
|
188
|
|
|
{% show_absolute_url "my-page-slug" %} |
189
|
|
|
|
190
|
|
|
Keyword arguments: |
191
|
|
|
:param page: the page object, slug or id |
192
|
|
|
:param lang: the wanted language \ |
193
|
|
|
(defaults to `settings.PAGE_DEFAULT_LANGUAGE`) |
194
|
|
|
""" |
195
|
|
|
if not lang: |
196
|
|
|
lang = context.get('lang', pages_settings.PAGE_DEFAULT_LANGUAGE) |
197
|
|
|
page = get_page_from_string_or_id(page, lang) |
198
|
|
|
if not page: |
199
|
|
|
return {'content': ''} |
200
|
|
|
url = page.get_url_path(language=lang) |
201
|
|
|
if url: |
202
|
|
|
return {'content': url} |
203
|
|
|
return {'content': ''} |
204
|
|
|
show_absolute_url = register.inclusion_tag('pages/content.html', |
205
|
|
|
takes_context=True)(show_absolute_url) |
206
|
|
|
|
207
|
|
|
|
208
|
|
|
def show_revisions(context, page, content_type, lang=None): |
209
|
|
|
"""Render the last 10 revisions of a page content with a list using |
210
|
|
|
the ``pages/revisions.html`` template""" |
211
|
|
|
if (not pages_settings.PAGE_CONTENT_REVISION or |
212
|
|
|
content_type in pages_settings.PAGE_CONTENT_REVISION_EXCLUDE_LIST): |
213
|
|
|
return {'revisions': None} |
214
|
|
|
revisions = Content.objects.filter(page=page, language=lang, |
215
|
|
|
type=content_type).order_by('-creation_date') |
216
|
|
|
if len(revisions) < 2: |
217
|
|
|
return {'revisions': None} |
218
|
|
|
return {'revisions': revisions[0:10]} |
219
|
|
|
show_revisions = register.inclusion_tag('pages/revisions.html', |
220
|
|
|
takes_context=True)(show_revisions) |
221
|
|
|
|
222
|
|
|
|
223
|
|
|
def pages_dynamic_tree_menu(context, page, url='/'): |
224
|
|
|
""" |
225
|
|
|
Render a "dynamic" tree menu, with all nodes expanded which are either |
226
|
|
|
ancestors or the current page itself. |
227
|
|
|
|
228
|
|
|
Override ``pages/dynamic_tree_menu.html`` if you want to change the |
229
|
|
|
design. |
230
|
|
|
|
231
|
|
|
:param page: the current page |
232
|
|
|
:param url: not used anymore |
233
|
|
|
""" |
234
|
|
|
lang = context.get('lang', pages_settings.PAGE_DEFAULT_LANGUAGE) |
235
|
|
|
page = get_page_from_string_or_id(page, lang) |
236
|
|
|
children = None |
237
|
|
|
if page and 'current_page' in context: |
238
|
|
|
current_page = context['current_page'] |
239
|
|
|
# if this node is expanded, we also have to render its children |
240
|
|
|
# a node is expanded if it is the current node or one of its ancestors |
241
|
|
|
if(page.tree_id == current_page.tree_id and |
242
|
|
|
page.lft <= current_page.lft and |
243
|
|
|
page.rght >= current_page.rght): |
244
|
|
|
children = page.get_children_for_frontend() |
245
|
|
|
context.update({'children': children, 'page': page}) |
246
|
|
|
return context |
247
|
|
|
pages_dynamic_tree_menu = register.inclusion_tag( |
248
|
|
|
'pages/dynamic_tree_menu.html', |
249
|
|
|
takes_context=True |
250
|
|
|
)(pages_dynamic_tree_menu) |
251
|
|
|
|
252
|
|
|
|
253
|
|
|
def pages_breadcrumb(context, page, url='/'): |
254
|
|
|
""" |
255
|
|
|
Render a breadcrumb like menu. |
256
|
|
|
|
257
|
|
|
Override ``pages/breadcrumb.html`` if you want to change the |
258
|
|
|
design. |
259
|
|
|
|
260
|
|
|
:param page: the current page |
261
|
|
|
:param url: not used anymore |
262
|
|
|
""" |
263
|
|
|
lang = context.get('lang', pages_settings.PAGE_DEFAULT_LANGUAGE) |
264
|
|
|
page = get_page_from_string_or_id(page, lang) |
265
|
|
|
pages_navigation = None |
266
|
|
|
if page: |
267
|
|
|
pages_navigation = page.get_ancestors() |
268
|
|
|
context.update({'pages_navigation': pages_navigation, 'page': page}) |
269
|
|
|
return context |
270
|
|
|
pages_breadcrumb = register.inclusion_tag( |
271
|
|
|
'pages/breadcrumb.html', |
272
|
|
|
takes_context=True |
273
|
|
|
)(pages_breadcrumb) |
274
|
|
|
|
275
|
|
|
|
276
|
|
|
"""Tags""" |
277
|
|
|
|
278
|
|
|
class GetPageNode(template.Node): |
279
|
|
|
"""get_page Node""" |
280
|
|
|
def __init__(self, page_filter, varname): |
281
|
|
|
self.page_filter = page_filter |
282
|
|
|
self.varname = varname |
283
|
|
|
|
284
|
|
|
def render(self, context): |
285
|
|
|
page_or_id = self.page_filter.resolve(context) |
286
|
|
|
page = get_page_from_string_or_id(page_or_id) |
287
|
|
|
context[self.varname] = page |
288
|
|
|
return '' |
289
|
|
|
|
290
|
|
|
|
291
|
|
|
def do_get_page(parser, token): |
292
|
|
|
"""Retrieve a page and insert into the template's context. |
293
|
|
|
|
294
|
|
|
Example:: |
295
|
|
|
|
296
|
|
|
{% get_page "news" as news_page %} |
297
|
|
|
|
298
|
|
|
:param page: the page object, slug or id |
299
|
|
|
:param name: name of the context variable to store the page in |
300
|
|
|
""" |
301
|
|
|
bits = token.split_contents() |
302
|
|
|
if 4 != len(bits): |
303
|
|
|
raise TemplateSyntaxError('%r expects 4 arguments' % bits[0]) |
304
|
|
|
if bits[-2] != 'as': |
305
|
|
|
raise TemplateSyntaxError( |
306
|
|
|
'%r expects "as" as the second argument' % bits[0]) |
307
|
|
|
page_filter = parser.compile_filter(bits[1]) |
308
|
|
|
varname = bits[-1] |
309
|
|
|
return GetPageNode(page_filter, varname) |
310
|
|
|
do_get_page = register.tag('get_page', do_get_page) |
311
|
|
|
|
312
|
|
|
|
313
|
|
|
class GetContentNode(template.Node): |
314
|
|
|
"""Get content node""" |
315
|
|
|
def __init__(self, page, content_type, varname, lang, lang_filter): |
316
|
|
|
self.page = page |
317
|
|
|
self.content_type = content_type |
318
|
|
|
self.varname = varname |
319
|
|
|
self.lang = lang |
320
|
|
|
self.lang_filter = lang_filter |
321
|
|
|
|
322
|
|
|
def render(self, context): |
323
|
|
|
if self.lang_filter: |
324
|
|
|
self.lang = self.lang_filter.resolve(context) |
325
|
|
|
context[self.varname] = _get_content( |
326
|
|
|
context, |
327
|
|
|
self.page.resolve(context), |
328
|
|
|
self.content_type.resolve(context), |
329
|
|
|
self.lang |
330
|
|
|
) |
331
|
|
|
return '' |
332
|
|
|
|
333
|
|
|
|
334
|
|
|
def do_get_content(parser, token): |
335
|
|
|
"""Retrieve a Content object and insert it into the template's context. |
336
|
|
|
|
337
|
|
|
Example:: |
338
|
|
|
|
339
|
|
|
{% get_content page_object "title" as content %} |
340
|
|
|
|
341
|
|
|
You can also use the slug of a page:: |
342
|
|
|
|
343
|
|
|
{% get_content "my-page-slug" "title" as content %} |
344
|
|
|
|
345
|
|
|
Syntax:: |
346
|
|
|
|
347
|
|
|
{% get_content page type [lang] as name %} |
348
|
|
|
|
349
|
|
|
:param page: the page object, slug or id |
350
|
|
|
:param type: content_type used by a placeholder |
351
|
|
|
:param name: name of the context variable to store the content in |
352
|
|
|
:param lang: the wanted language |
353
|
|
|
""" |
354
|
|
|
bits = token.split_contents() |
355
|
|
|
if not 5 <= len(bits) <= 6: |
356
|
|
|
raise TemplateSyntaxError('%r expects 4 or 5 arguments' % bits[0]) |
357
|
|
|
if bits[-2] != 'as': |
358
|
|
|
raise TemplateSyntaxError( |
359
|
|
|
'%r expects "as" as the second last argument' % bits[0]) |
360
|
|
|
page = parser.compile_filter(bits[1]) |
361
|
|
|
content_type = parser.compile_filter(bits[2]) |
362
|
|
|
varname = bits[-1] |
363
|
|
|
lang = None |
364
|
|
|
lang_filter = None |
365
|
|
|
if len(bits) == 6: |
366
|
|
|
lang = bits[3] |
367
|
|
|
else: |
368
|
|
|
lang_filter = parser.compile_filter("lang") |
369
|
|
|
return GetContentNode(page, content_type, varname, lang, lang_filter) |
370
|
|
|
do_get_content = register.tag('get_content', do_get_content) |
371
|
|
|
|
372
|
|
|
|
373
|
|
|
class LoadPagesNode(template.Node): |
374
|
|
|
"""Load page node.""" |
375
|
|
|
def render(self, context): |
376
|
|
|
if 'pages_navigation' not in context: |
377
|
|
|
pages = Page.objects.navigation().order_by("tree_id") |
378
|
|
|
context.update({'pages_navigation': pages}) |
379
|
|
|
if 'current_page' not in context: |
380
|
|
|
context.update({'current_page': None}) |
381
|
|
|
return '' |
382
|
|
|
|
383
|
|
|
|
384
|
|
|
def do_load_pages(parser, token): |
385
|
|
|
"""Load the navigation pages, lang, and current_page variables into the |
386
|
|
|
current context. |
387
|
|
|
|
388
|
|
|
Example:: |
389
|
|
|
|
390
|
|
|
<ul> |
391
|
|
|
{% load_pages %} |
392
|
|
|
{% for page in pages_navigation %} |
393
|
|
|
{% pages_menu page %} |
394
|
|
|
{% endfor %} |
395
|
|
|
</ul> |
396
|
|
|
""" |
397
|
|
|
return LoadPagesNode() |
398
|
|
|
|
399
|
|
|
|
400
|
|
|
do_load_pages = register.tag('load_pages', do_load_pages) |
401
|
|
|
|
402
|
|
|
|
403
|
|
|
from pages.utils import get_placeholders |
404
|
|
|
from django.forms.widgets import Media |
405
|
|
|
from django import forms |
406
|
|
|
from django.template.loader import get_template |
407
|
|
|
|
408
|
|
|
|
409
|
|
|
class LoadEditNode(template.Node): |
410
|
|
|
"""Load edit node.""" |
411
|
|
|
|
412
|
|
|
def render(self, context): |
413
|
|
|
request = context.get('request') |
414
|
|
|
if not request.user.is_staff: |
415
|
|
|
return '' |
416
|
|
|
template_name = context.get('template_name') |
417
|
|
|
placeholders = get_placeholders(template_name) |
418
|
|
|
page = context.get('current_page') |
419
|
|
|
lang = context.get('lang', pages_settings.PAGE_DEFAULT_LANGUAGE) |
420
|
|
|
form = forms.Form() |
421
|
|
|
for p in placeholders: |
422
|
|
|
field = p.get_field(page, lang, initial=p.get_content_from_context(context)) |
423
|
|
|
form.fields[p.name] = field |
424
|
|
|
|
425
|
|
|
template = get_template('pages/inline-edit.html') |
426
|
|
|
with context.push(): |
427
|
|
|
context['form'] = form |
428
|
|
|
content = template.render(context) |
429
|
|
|
|
430
|
|
|
return content |
431
|
|
|
|
432
|
|
|
|
433
|
|
|
def do_load_edit(parser, token): |
434
|
|
|
""" |
435
|
|
|
""" |
436
|
|
|
return LoadEditNode() |
437
|
|
|
|
438
|
|
|
|
439
|
|
|
do_load_edit = register.tag('load_edit', do_load_edit) |
440
|
|
|
|
441
|
|
|
|
442
|
|
|
class LoadEditMediaNode(template.Node): |
443
|
|
|
"""Load edit node.""" |
444
|
|
|
|
445
|
|
|
def render(self, context): |
446
|
|
|
request = context.get('request') |
447
|
|
|
if not request.user.is_staff: |
448
|
|
|
return '' |
449
|
|
|
template_name = context.get('template_name') |
450
|
|
|
placeholders = get_placeholders(template_name) |
451
|
|
|
page = context.get('current_page') |
452
|
|
|
lang = context.get('lang', pages_settings.PAGE_DEFAULT_LANGUAGE) |
453
|
|
|
form = forms.Form() |
454
|
|
|
for p in placeholders: |
455
|
|
|
field = p.get_field(page, lang) |
456
|
|
|
form.fields[p.name] = field |
457
|
|
|
|
458
|
|
|
link = '<link href="{}" type="text/css" media="all" rel="stylesheet" />'.format( |
459
|
|
|
'/static/pages/css/inline-edit.css' |
460
|
|
|
) |
461
|
|
|
|
462
|
|
|
return unicode(form.media) + link |
463
|
|
|
|
464
|
|
|
|
465
|
|
|
def do_load_edit_media(parser, token): |
466
|
|
|
""" |
467
|
|
|
""" |
468
|
|
|
return LoadEditMediaNode() |
469
|
|
|
|
470
|
|
|
|
471
|
|
|
do_load_edit = register.tag('load_edit_media', do_load_edit_media) |
472
|
|
|
|
473
|
|
|
|
474
|
|
|
def do_placeholder(parser, token): |
475
|
|
|
""" |
476
|
|
|
Method that parse the placeholder template tag. |
477
|
|
|
|
478
|
|
|
Syntax:: |
479
|
|
|
|
480
|
|
|
{% placeholder <name> [on <page>] [with <widget>] \ |
481
|
|
|
[parsed] [as <varname>] %} |
482
|
|
|
|
483
|
|
|
Example usage:: |
484
|
|
|
|
485
|
|
|
{% placeholder about %} |
486
|
|
|
{% placeholder body with TextArea as body_text %} |
487
|
|
|
{% placeholder welcome with TextArea parsed as welcome_text %} |
488
|
|
|
{% placeholder teaser on next_page with TextArea parsed %} |
489
|
|
|
""" |
490
|
|
|
name, params = parse_placeholder(parser, token) |
491
|
|
|
return PlaceholderNode(name, **params) |
492
|
|
|
register.tag('placeholder', do_placeholder) |
493
|
|
|
|
494
|
|
|
def do_markdownlaceholder(parser, token): |
495
|
|
|
""" |
496
|
|
|
Method that parse the markdownplaceholder template tag. |
497
|
|
|
""" |
498
|
|
|
name, params = parse_placeholder(parser, token) |
499
|
|
|
return MarkdownPlaceholderNode(name, **params) |
500
|
|
|
register.tag('markdownplaceholder', do_markdownlaceholder) |
501
|
|
|
|
502
|
|
|
def do_imageplaceholder(parser, token): |
503
|
|
|
""" |
504
|
|
|
Method that parse the imageplaceholder template tag. |
505
|
|
|
""" |
506
|
|
|
name, params = parse_placeholder(parser, token) |
507
|
|
|
return ImagePlaceholderNode(name, **params) |
508
|
|
|
register.tag('imageplaceholder', do_imageplaceholder) |
509
|
|
|
|
510
|
|
|
def do_fileplaceholder(parser, token): |
511
|
|
|
""" |
512
|
|
|
Method that parse the fileplaceholder template tag. |
513
|
|
|
""" |
514
|
|
|
name, params = parse_placeholder(parser, token) |
515
|
|
|
return FilePlaceholderNode(name, **params) |
516
|
|
|
register.tag('fileplaceholder', do_fileplaceholder) |
517
|
|
|
|
518
|
|
|
def do_contactplaceholder(parser, token): |
519
|
|
|
""" |
520
|
|
|
Method that parse the contactplaceholder template tag. |
521
|
|
|
""" |
522
|
|
|
name, params = parse_placeholder(parser, token) |
523
|
|
|
return ContactPlaceholderNode(name, **params) |
524
|
|
|
register.tag('contactplaceholder', do_contactplaceholder) |
525
|
|
|
|
526
|
|
|
|
527
|
|
|
def do_jsonplaceholder(parser, token): |
528
|
|
|
""" |
529
|
|
|
Method that parse the contactplaceholder template tag. |
530
|
|
|
""" |
531
|
|
|
name, params = parse_placeholder(parser, token) |
532
|
|
|
return JsonPlaceholderNode(name, **params) |
533
|
|
|
register.tag('jsonplaceholder', do_jsonplaceholder) |
534
|
|
|
|
535
|
|
|
|
536
|
|
|
def language_content_up_to_date(page, language): |
537
|
|
|
"""Tell if all the page content has been updated since the last |
538
|
|
|
change of the official version (settings.LANGUAGE_CODE) |
539
|
|
|
|
540
|
|
|
This is approximated by comparing the last modified date of any |
541
|
|
|
content in the page, not comparing each content block to its |
542
|
|
|
corresponding official language version. That allows users to |
543
|
|
|
easily make "do nothing" changes to any content block when no |
544
|
|
|
change is required for a language. |
545
|
|
|
""" |
546
|
|
|
lang_code = getattr(settings, 'LANGUAGE_CODE', None) |
547
|
|
|
if lang_code == language: |
548
|
|
|
# official version is always "up to date" |
549
|
|
|
return True |
550
|
|
|
# get the last modified date for the official version |
551
|
|
|
last_modified = Content.objects.filter(language=lang_code, |
552
|
|
|
page=page).order_by('-creation_date') |
553
|
|
|
if not last_modified: |
554
|
|
|
# no official version |
555
|
|
|
return True |
556
|
|
|
lang_modified = Content.objects.filter(language=language, |
557
|
|
|
page=page).order_by('-creation_date')[0].creation_date |
558
|
|
|
return lang_modified > last_modified[0].creation_date |
559
|
|
|
register.filter(language_content_up_to_date) |
560
|
|
|
|
561
|
|
|
|
562
|
|
|
@register.assignment_tag |
563
|
|
|
def get_pages_with_tag(tag): |
564
|
|
|
""" |
565
|
|
|
Return Pages with given tag |
566
|
|
|
|
567
|
|
|
Syntax:: |
568
|
|
|
|
569
|
|
|
{% get_pages_with_tag <tag name> as <varname> %} |
570
|
|
|
|
571
|
|
|
Example use:: |
572
|
|
|
{% get_pages_with_tag "footer" as pages %} |
573
|
|
|
""" |
574
|
|
|
return Page.objects.filter(tags__name__in=[tag]) |
575
|
|
|
|
576
|
|
|
|
577
|
|
|
def do_page_has_content(parser, token): |
578
|
|
|
""" |
579
|
|
|
Conditional tag that only renders its nodes if the page |
580
|
|
|
has content for a particular content type. By default the |
581
|
|
|
current page is used. |
582
|
|
|
|
583
|
|
|
Syntax:: |
584
|
|
|
|
585
|
|
|
{% page_has_content <content_type> [<page var name>] %} |
586
|
|
|
... |
587
|
|
|
{%_end page_has_content %} |
588
|
|
|
|
589
|
|
|
Example use:: |
590
|
|
|
|
591
|
|
|
{% page_has_content 'header-image' %} |
592
|
|
|
<img src="{{ MEDIA_URL }}{% imageplaceholder 'header-image' %}"> |
593
|
|
|
{% end_page_has_content %} |
594
|
|
|
|
595
|
|
|
""" |
596
|
|
|
nodelist = parser.parse(('end_page_has_content',)) |
597
|
|
|
parser.delete_first_token() |
598
|
|
|
args = token.split_contents() |
599
|
|
|
try: |
600
|
|
|
content_type = unescape_string_literal(args[1]) |
601
|
|
|
except IndexError: |
602
|
|
|
raise template.TemplateSyntaxError( |
603
|
|
|
"%r tag requires the argument content_type" % args[0] |
604
|
|
|
) |
605
|
|
|
if len(args) > 2: |
606
|
|
|
page = args[2] |
607
|
|
|
else: |
608
|
|
|
page = None |
609
|
|
|
return PageHasContentNode(page, content_type, nodelist) |
610
|
|
|
register.tag('page_has_content', do_page_has_content) |
611
|
|
|
|
612
|
|
|
class PageHasContentNode(template.Node): |
613
|
|
|
|
614
|
|
|
def __init__(self, page, content_type, nodelist): |
615
|
|
|
self.page = page or 'current_page' |
616
|
|
|
self.content_type = content_type |
617
|
|
|
self.nodelist = nodelist |
618
|
|
|
|
619
|
|
|
def render(self, context): |
620
|
|
|
page = context.get(self.page) |
621
|
|
|
if not page: |
622
|
|
|
return '' |
623
|
|
|
content = page.get_content(context.get('lang', None), self.content_type) |
624
|
|
|
if(content): |
625
|
|
|
output = self.nodelist.render(context) |
626
|
|
|
return output |
627
|
|
|
return '' |
628
|
|
|
|
629
|
|
|
|