]> git.0d.be Git - panikweb.git/blob - panikweb/paniktags/templatetags/paniktags.py
f0826e67dfb078aa5d0caa0af475aad13842fdc7
[panikweb.git] / panikweb / paniktags / templatetags / paniktags.py
1 import email.utils
2 import datetime
3 import json
4 import re
5 import time
6 import uuid
7
8 from django import template
9 from django.conf import settings
10 from django.urls import reverse
11 from django.db.models.query import QuerySet
12 from django.utils.encoding import force_text
13 from django.utils.http import quote
14 from django.utils.six.moves.urllib import parse as urlparse
15
16 from datetime import datetime, timedelta
17
18 from taggit.models import Tag
19
20 from emissions.models import Emission, Episode, NewsItem, SoundFile, Focus
21 from emissions.utils import period_program
22
23 from panikweb import utils
24 from panikweb import search
25
26 register = template.Library()
27
28
29 @register.filter(name='zip')
30 def zip_lists(a, b):
31     return zip(a, b)
32
33
34 @register.inclusion_tag('includes/audio.html', takes_context=True)
35 def audio(context, sound=None, embed=False, display_fragment_name=False):
36     return {
37         'episode': context.get('episode'),
38         'sound': sound,
39         'display_fragment_name': display_fragment_name,
40         'embed': embed,
41     }
42
43
44 @register.inclusion_tag('listen/nav.html', takes_context=True)
45 def listen_nav(context, date=None, klass=None):
46     return {
47         'class': klass,
48         'categories': context.get('categories'),
49     }
50
51
52 @register.inclusion_tag('news/nav.html', takes_context=True)
53 def news_nav(context, date=None, klass=None):
54     return {
55         'class': klass,
56         'newsitem': context.get('newsitem'),
57         'categories': context.get('categories'),
58         'news': context.get('news'),
59         'search_query': context.get('search_query'),
60     }
61
62
63 @register.inclusion_tag('emissions/nav.html', takes_context=True)
64 def emission_nav(context, date=None, klass=None):
65     return {
66         'class': klass,
67         'categories': context.get('categories'),
68         'episodes': context.get('episodes'),
69         'emission': context.get('emission'),
70         'episode': context.get('episode'),
71         'search_query': context.get('search_query'),
72     }
73
74
75 @register.inclusion_tag('episodes/inline.html', takes_context=True)
76 def episode_inline(context, date=None, model=None, klass=None):
77     return {
78         'class': klass,
79         'episode': context.get('episode'),
80         'date': date,
81     }
82
83
84 @register.inclusion_tag('episodes/resume.html', takes_context=True)
85 def episode_resume(context, date=None, model=None, klass=None):
86     return {
87         'model': model,
88         'class': klass,
89         'episode': context.get('episode'),
90         'date': date,
91     }
92
93
94 @register.inclusion_tag('episodes/detail.html', takes_context=True)
95 def episode_detail(context, date=None):
96     soundfiles = SoundFile.objects.select_related().filter(
97         fragment=True, podcastable=True, episode=context.get('episode')
98     )
99     return {
100         'episode': context.get('episode'),
101         'emission': context.get('emission'),
102         'diffusions': context.get('diffusions'),
103         'soundfiles': soundfiles,
104         'date': date,
105         'topik_pages': context.get('topik_pages'),
106     }
107
108
109 @register.inclusion_tag('emissions/detail.html', takes_context=True)
110 def emission_detail(context, date=None):
111     return {
112         'emission': context.get('emission'),
113         'schedules': context.get('schedules'),
114     }
115
116
117 @register.inclusion_tag('emissions/resume.html', takes_context=True)
118 def emission_resume(context, date=None):
119     return {
120         'emission': context.get('emission'),
121         'schedules': context.get('schedules'),
122     }
123
124
125 @register.inclusion_tag('emissions/inline.html', takes_context=True)
126 def emission_inline(context, date=None):
127     return {
128         'emission': context.get('emission'),
129         'schedules': context.get('schedules'),
130     }
131
132
133 @register.inclusion_tag('soundfiles/resume.html')
134 def soundfile_resume(soundfile, date=None):
135     return {'soundfile': soundfile, 'date': date}
136
137
138 @register.inclusion_tag('includes/player.html', takes_context=True)
139 def player(context):
140     return {
141         'unique': uuid.uuid4(),
142         'radio_stream_urls': settings.RADIO_STREAM_URLS,
143         'soundfiles': context.get('soundfiles'),
144     }
145
146
147 @register.inclusion_tag('includes/metaNav.html', takes_context=True)
148 def metanav(context):
149     try:
150         request_path = context['request'].path
151     except KeyError:
152         # a context without 'requests' may happen when rendering error pages
153         request_path = 'xxx'
154     paths = {
155         '/actus/': 'News',
156         '/sons/': 'Listen',
157         '/podcasts/': 'Listen',
158         '/topiks/': 'Topiks',
159         '/emissions': 'Emissions',
160         '/grille': 'Emissions',
161         '/programme/': 'Emissions',
162         '/recherche/': 'Search',
163         '/la-radio/': 'About',
164     }
165     section = ''
166     if request_path == '/':
167         section = 'Home'
168     else:
169         for path, section_name in paths.items():
170             if request_path.startswith(path):
171                 section = section_name
172                 break
173     return {
174         'LANGUAGE_CODE': context.get('LANGUAGE_CODE'),
175         'sectionName': section,
176     }
177
178
179 @register.inclusion_tag('includes/week.html')
180 def weekview(year=None, week=None):
181     year = year if year else datetime.today().isocalendar()[0]
182     week = week if week else datetime.today().isocalendar()[1]
183
184     date = utils.tofirstdayinisoweek(year, week)
185     date = datetime(*date.timetuple()[:3])
186
187     program = period_program(date, date + timedelta(days=7))
188     days = []
189     for day in range(7):
190         days.append(
191             {
192                 'cells': [x for x in program if x.is_on_weekday(day + 1)],
193                 'datetime': date + timedelta(days=day),
194             }
195         )
196
197     return {
198         'days': days,
199         'week': week,
200         'year': year,
201         'now': datetime.now(),
202     }
203
204
205 @register.inclusion_tag('includes/week-nav.html')
206 def weeknav(year=None, week=None, weekday=None):
207     year = year if year else datetime.today().isocalendar()[0]
208     week = week if week else datetime.today().isocalendar()[1]
209     weekday = weekday if weekday is not None else datetime.today().weekday()
210
211     date = utils.tofirstdayinisoweek(year, week)
212     date = datetime(*date.timetuple()[:3])
213
214     days = []
215     for day in range(7):
216         days.append({'datetime': date + timedelta(days=day)})
217
218     previous_week = date - timedelta(days=7)
219     previous_week_year, previous_week_no = previous_week.isocalendar()[:2]
220
221     next_week = date + timedelta(days=7)
222     next_week_year, next_week_no = next_week.isocalendar()[:2]
223
224     return {
225         'days': days,
226         'weekday': weekday,
227         'week': week,
228         'year': year,
229         'previous_week_year': previous_week_year,
230         'previous_week_no': previous_week_no,
231         'next_week_year': next_week_year,
232         'next_week_no': next_week_no,
233     }
234
235
236 @register.inclusion_tag('news/inline.html', takes_context=True)
237 def news_inline(context, klass=None, logo=None):
238     return {'content': context.get('content'), 'class': klass, 'logo': logo}
239
240
241 @register.inclusion_tag('news/roll.html')
242 def newsroll():
243     return {
244         'news': Focus.objects.filter(current=True)
245         .select_related('emission', 'newsitem', 'soundfile', 'episode', 'newsitem__category')
246         .order_by('?')[: settings.HOME_FOCUS_COUNT]
247     }
248
249
250 @register.filter
251 def jsonify(object):
252     if isinstance(object, QuerySet):
253         return serialize('json', object)
254     return json.dumps(object)
255
256
257 @register.filter
258 def strreplace(string, args):
259     find = args.split(',')[0]
260     replace = args.split(',')[1]
261     return string.replace(find, replace)
262
263
264 @register.filter
265 def replace(string, args):
266     search = args.split(args[0])[1]
267     replace = args.split(args[0])[2]
268
269     return re.sub(search, replace, string)
270
271
272 def remove_facet(facet_id, url, facet):
273     scheme, netloc, path, query, fragment = list(urlparse.urlsplit(str(url)))
274     facet = '%s_exact:%s' % (facet_id, facet)
275     query_string = urlparse.parse_qsl(query)
276     query_string = [x for x in query_string if not (x[0] == 'selected_facets' and x[1] == facet)]
277     query = '&'.join(['%s=%s' % x for x in query_string])
278     url = urlparse.urlunsplit([scheme, netloc, path, query, None])
279     return re.sub(r'&page=\d+', '', url)
280
281
282 @register.filter
283 def remove_tag_facet(url, facet):
284     return remove_facet('tags', url, facet)
285
286
287 @register.filter
288 def remove_category_facet(url, facet):
289     return remove_facet('categories', url, facet)
290
291
292 @register.filter
293 def remove_news_category_facet(url, facet):
294     return remove_facet('news_categories', url, facet)
295
296
297 @register.filter
298 def remove_format_facet(url, facet):
299     return remove_facet('format', url, facet)
300
301
302 def append_facet(facet_id, url, facet):
303     facet = quote(facet, safe='')
304     if not '?' in url:
305         url = url + '?'
306     return re.sub(r'&page=\d+', '', url + '&selected_facets=%s_exact:%s' % (facet_id, facet))
307
308
309 @register.filter
310 def append_tag_facet(url, facet):
311     return append_facet('tags', url, facet)
312
313
314 @register.filter
315 def append_category_facet(url, facet):
316     return append_facet('categories', url, facet)
317
318
319 @register.filter
320 def append_news_category_facet(url, facet):
321     return append_facet('news_categories', url, facet)
322
323
324 @register.filter
325 def append_format_facet(url, facet):
326     return append_facet('format', url, facet)
327
328
329 @register.tag
330 def search_result_template(parser, token):
331     try:
332         tag_name, result_str = token.split_contents()
333     except ValueError:
334         raise template.TemplateSyntaxError("%r tag requires exactly one argument" % token.contents.split()[0])
335     return FormatSearchResultNode(result_str)
336
337
338 class FormatSearchResultNode(template.Node):
339     def __init__(self, result_str):
340         self.result_var = template.Variable(result_str)
341
342     def render(self, context):
343         result = self.result_var.resolve(context)
344         dir_mapping = {'newsitem': 'news', 'emission': 'emissions', 'episode': 'episodes'}
345         t = template.loader.get_template('%s/search_result.html' % dir_mapping.get(result.model_name))
346         return t.render({'result': result})
347
348
349 @register.inclusion_tag('includes/piwik.html')
350 def piwik():
351     return {'enabled': settings.ENABLE_PIWIK}
352
353
354 @register.filter
355 def rfc822(datetime):
356     if datetime is None:
357         return ''
358     return email.utils.formatdate(time.mktime(datetime.timetuple()))
359
360
361 @register.inclusion_tag('includes/related.html', takes_context=False)
362 def related_objects(object):
363     sqs = search.MoreLikeThisSearchQuerySet().models(Emission, Episode, NewsItem)
364     return {'more_like_this': sqs.more_like_this(object)[:12]}
365
366
367 @register.simple_tag
368 def random_emissions(count=6):
369     return Emission.objects.filter(archived=False).order_by('?')[:count]
370
371
372 @register.simple_tag
373 def get_tags():
374     return Tag.objects.exclude(taggit_taggeditem_items__isnull=True)
375
376
377 @register.inclusion_tag('includes/topik.html', takes_context=True)
378 def topik(context, topik):
379     return {'page': topik}
380
381
382 @register.filter
383 def get_focus_url(object):
384     if object.newsitem:
385         return reverse('newsitem-view', kwargs={'slug': object.newsitem.slug})
386     if object.emission:
387         return reverse('emission-view', kwargs={'slug': object.emission.slug})
388     if object.episode:
389         return reverse(
390             'episode-view',
391             kwargs={'slug': object.episode.slug, 'emission_slug': object.episode.emission.slug},
392         )
393     if object.soundfile:
394         return reverse(
395             'episode-view',
396             kwargs={
397                 'slug': object.soundfile.episode.slug,
398                 'emission_slug': object.soundfile.episode.emission.slug,
399             },
400         )
401     if object.page:
402         return object.page.get_online_url()
403     return ''
404
405
406 @register.filter
407 def facet_tag(tag):
408     return tag.name.lower()
409
410
411 @register.filter
412 def set_absolute_urls(text):
413     text = text.replace('src="/', 'src="%s' % settings.WEBSITE_BASE_URL)
414     text = text.replace('href="/', 'href="%s' % settings.WEBSITE_BASE_URL)
415     return text
416
417
418 @register.filter
419 def as_absolute_url(url):
420     if url.startswith('/'):
421         url = settings.WEBSITE_BASE_URL + url.lstrip('/')
422     return url
423
424
425 @register.filter
426 def xml_illegal_fix(text):
427     # django.utils.xmlutils.UnserializableContentError: Control characters are not supported in XML 1.0
428     for i in range(0x20):  # remove control characters
429         char = chr(i)
430         if char in ('\t', '\r', '\n'):
431             # only allow tab, carriage return and line feed.
432             continue
433         text = text.replace(char, '')
434     # fffe and ffff are also invalid characters
435     return text.replace('\ufffe', '').replace('\uffff', '')
436
437
438 @register.filter
439 def hr_split(text):
440     return re.split(r'<hr\s*/?>', text)