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