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
16 from datetime import datetime, timedelta
18 from taggit.models import Tag
20 from emissions.models import Emission, Episode, NewsItem, SoundFile, Focus
21 from emissions.utils import period_program
23 from panikweb import utils
24 from panikweb import search
26 register = template.Library()
29 @register.filter(name='zip')
34 @register.inclusion_tag('includes/audio.html', takes_context=True)
35 def audio(context, sound=None, embed=False, display_fragment_name=False):
37 'episode': context.get('episode'),
39 'display_fragment_name': display_fragment_name,
44 @register.inclusion_tag('listen/nav.html', takes_context=True)
45 def listen_nav(context, date=None, klass=None):
48 'categories': context.get('categories'),
52 @register.inclusion_tag('news/nav.html', takes_context=True)
53 def news_nav(context, date=None, klass=None):
56 'newsitem': context.get('newsitem'),
57 'categories': context.get('categories'),
58 'news': context.get('news'),
59 'search_query': context.get('search_query'),
63 @register.inclusion_tag('emissions/nav.html', takes_context=True)
64 def emission_nav(context, date=None, klass=None):
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'),
75 @register.inclusion_tag('episodes/inline.html', takes_context=True)
76 def episode_inline(context, date=None, model=None, klass=None):
79 'episode': context.get('episode'),
84 @register.inclusion_tag('episodes/resume.html', takes_context=True)
85 def episode_resume(context, date=None, model=None, klass=None):
89 'episode': context.get('episode'),
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')
100 'episode': context.get('episode'),
101 'emission': context.get('emission'),
102 'diffusions': context.get('diffusions'),
103 'soundfiles': soundfiles,
105 'topik_pages': context.get('topik_pages'),
109 @register.inclusion_tag('emissions/detail.html', takes_context=True)
110 def emission_detail(context, date=None):
112 'emission': context.get('emission'),
113 'schedules': context.get('schedules'),
117 @register.inclusion_tag('emissions/resume.html', takes_context=True)
118 def emission_resume(context, date=None):
120 'emission': context.get('emission'),
121 'schedules': context.get('schedules'),
125 @register.inclusion_tag('emissions/inline.html', takes_context=True)
126 def emission_inline(context, date=None):
128 'emission': context.get('emission'),
129 'schedules': context.get('schedules'),
133 @register.inclusion_tag('soundfiles/resume.html')
134 def soundfile_resume(soundfile, date=None):
135 return {'soundfile': soundfile, 'date': date}
138 @register.inclusion_tag('includes/player.html', takes_context=True)
141 'unique': uuid.uuid4(),
142 'radio_stream_urls': settings.RADIO_STREAM_URLS,
143 'soundfiles': context.get('soundfiles'),
147 @register.inclusion_tag('includes/metaNav.html', takes_context=True)
148 def metanav(context):
150 request_path = context['request'].path
152 # a context without 'requests' may happen when rendering error pages
157 '/podcasts/': 'Listen',
158 '/topiks/': 'Topiks',
159 '/emissions': 'Emissions',
160 '/grille': 'Emissions',
161 '/programme/': 'Emissions',
162 '/recherche/': 'Search',
163 '/la-radio/': 'About',
166 if request_path == '/':
169 for path, section_name in paths.items():
170 if request_path.startswith(path):
171 section = section_name
174 'LANGUAGE_CODE': context.get('LANGUAGE_CODE'),
175 'sectionName': section,
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]
184 date = utils.tofirstdayinisoweek(year, week)
185 date = datetime(*date.timetuple()[:3])
187 program = period_program(date, date + timedelta(days=7))
192 'cells': [x for x in program if x.is_on_weekday(day + 1)],
193 'datetime': date + timedelta(days=day),
201 'now': datetime.now(),
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()
211 date = utils.tofirstdayinisoweek(year, week)
212 date = datetime(*date.timetuple()[:3])
216 days.append({'datetime': date + timedelta(days=day)})
218 previous_week = date - timedelta(days=7)
219 previous_week_year, previous_week_no = previous_week.isocalendar()[:2]
221 next_week = date + timedelta(days=7)
222 next_week_year, next_week_no = next_week.isocalendar()[:2]
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,
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}
241 @register.inclusion_tag('news/roll.html')
244 'news': Focus.objects.filter(current=True)
245 .select_related('emission', 'newsitem', 'soundfile', 'episode', 'newsitem__category')
246 .order_by('?')[: settings.HOME_FOCUS_COUNT]
252 if isinstance(object, QuerySet):
253 return serialize('json', object)
254 return json.dumps(object)
258 def strreplace(string, args):
259 find = args.split(',')[0]
260 replace = args.split(',')[1]
261 return string.replace(find, replace)
265 def replace(string, args):
266 search = args.split(args[0])[1]
267 replace = args.split(args[0])[2]
269 return re.sub(search, replace, string)
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)
283 def remove_tag_facet(url, facet):
284 return remove_facet('tags', url, facet)
288 def remove_category_facet(url, facet):
289 return remove_facet('categories', url, facet)
293 def remove_news_category_facet(url, facet):
294 return remove_facet('news_categories', url, facet)
298 def remove_format_facet(url, facet):
299 return remove_facet('format', url, facet)
302 def append_facet(facet_id, url, facet):
303 facet = quote(facet, safe='')
306 return re.sub(r'&page=\d+', '', url + '&selected_facets=%s_exact:%s' % (facet_id, facet))
310 def append_tag_facet(url, facet):
311 return append_facet('tags', url, facet)
315 def append_category_facet(url, facet):
316 return append_facet('categories', url, facet)
320 def append_news_category_facet(url, facet):
321 return append_facet('news_categories', url, facet)
325 def append_format_facet(url, facet):
326 return append_facet('format', url, facet)
330 def search_result_template(parser, token):
332 tag_name, result_str = token.split_contents()
334 raise template.TemplateSyntaxError("%r tag requires exactly one argument" % token.contents.split()[0])
335 return FormatSearchResultNode(result_str)
338 class FormatSearchResultNode(template.Node):
339 def __init__(self, result_str):
340 self.result_var = template.Variable(result_str)
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})
349 @register.inclusion_tag('includes/piwik.html')
351 return {'enabled': settings.ENABLE_PIWIK}
355 def rfc822(datetime):
358 return email.utils.formatdate(time.mktime(datetime.timetuple()))
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]}
368 def random_emissions(count=6):
369 return Emission.objects.filter(archived=False).order_by('?')[:count]
374 return Tag.objects.exclude(taggit_taggeditem_items__isnull=True)
377 @register.inclusion_tag('includes/topik.html', takes_context=True)
378 def topik(context, topik):
379 return {'page': topik}
383 def get_focus_url(object):
385 return reverse('newsitem-view', kwargs={'slug': object.newsitem.slug})
387 return reverse('emission-view', kwargs={'slug': object.emission.slug})
391 kwargs={'slug': object.episode.slug, 'emission_slug': object.episode.emission.slug},
397 'slug': object.soundfile.episode.slug,
398 'emission_slug': object.soundfile.episode.emission.slug,
402 return object.page.get_online_url()
408 return tag.name.lower()
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)
419 def as_absolute_url(url):
420 if url.startswith('/'):
421 url = settings.WEBSITE_BASE_URL + url.lstrip('/')
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
430 if char in ('\t', '\r', '\n'):
431 # only allow tab, carriage return and line feed.
433 text = text.replace(char, '')
434 # fffe and ffff are also invalid characters
435 return text.replace('\ufffe', '').replace('\uffff', '')
440 return re.split(r'<hr\s*/?>', text)