Fix Python 2 / 3 incompatabilities
[mygpo.git] / mygpo / web / views / podcast.py
blob9f33a6fc86a567b13b5578b894cfa7f0b861408f
1 from functools import wraps, partial
2 from datetime import datetime
4 from django.core.urlresolvers import reverse
5 from django.http import HttpResponseBadRequest, HttpResponseRedirect, Http404
6 from django.shortcuts import render
7 from django.contrib.auth.decorators import login_required
8 from django.contrib.sites.models import RequestSite
9 from django.utils.translation import ugettext as _
10 from django.contrib import messages
11 from django.views.decorators.vary import vary_on_cookie
12 from django.views.decorators.cache import never_cache, cache_control
13 from django.shortcuts import get_object_or_404
14 from django.contrib.contenttypes.models import ContentType
16 from mygpo.podcasts.models import Podcast, PodcastGroup, Episode, Tag
17 from mygpo.users.models import SubscriptionException
18 from mygpo.subscriptions.models import Subscription
19 from mygpo.subscriptions import (
20 subscribe as subscribe_podcast,
21 unsubscribe as unsubscribe_podcast,
22 subscribe_all as subscribe_podcast_all,
23 unsubscribe_all as unsubscribe_podcast_all,
24 get_subscribe_targets
26 from mygpo.history.models import HistoryEntry
27 from mygpo.core.tasks import flattr_thing
28 from mygpo.utils import normalize_feed_url
29 from mygpo.users.settings import PUBLIC_SUB_PODCAST, FLATTR_TOKEN
30 from mygpo.publisher.utils import check_publisher_permission
31 from mygpo.usersettings.models import UserSettings
32 from mygpo.users.models import Client
33 from mygpo.web.forms import SyncForm
34 from mygpo.decorators import allowed_methods
35 from mygpo.web.utils import get_podcast_link_target, get_page_list, \
36 check_restrictions
38 import logging
39 logger = logging.getLogger(__name__)
42 @vary_on_cookie
43 @cache_control(private=True)
44 @allowed_methods(['GET'])
45 def show(request, podcast):
46 """ Shows a podcast detail page """
48 podcast = check_restrictions(podcast)
50 current_site = RequestSite(request)
51 num_episodes = 20
52 episodes = episode_list(podcast, request.user, limit=num_episodes)
53 user = request.user
55 listeners = list(filter(None, [e.listeners for e in episodes]))
56 max_listeners = max(listeners + [0])
58 episode = None
60 if episodes:
61 episode = episodes[0]
62 episodes = episodes[1:]
64 if podcast.group:
65 group = podcast.group
66 rel_podcasts = group.podcast_set.exclude(pk=podcast.pk)
67 else:
68 rel_podcasts = []
70 tags = get_tags(podcast, user)
71 has_tagged = any(t['is_own'] for t in tags)
73 if user.is_authenticated():
74 subscribed_devices = Client.objects.filter(
75 subscription__user=user,
76 subscription__podcast=podcast,
79 subscribe_targets = get_subscribe_targets(podcast, user)
81 has_history = HistoryEntry.objects.filter(user=user, podcast=podcast)\
82 .exists()
83 can_flattr = (user.profile.settings.get_wksetting(FLATTR_TOKEN) and
84 podcast.flattr_url)
86 else:
87 has_history = False
88 subscribed_devices = []
89 subscribe_targets = []
90 can_flattr = False
92 is_publisher = check_publisher_permission(user, podcast)
94 episodes_total = podcast.episode_count or 0
95 num_pages = episodes_total / num_episodes
96 page_list = get_page_list(1, num_pages, 1, 15)
98 return render(request, 'podcast.html', {
99 'tags': tags,
100 'has_tagged': has_tagged,
101 'url': current_site,
102 'has_history': has_history,
103 'podcast': podcast,
104 'devices': subscribed_devices,
105 'related_podcasts': rel_podcasts,
106 'can_subscribe': len(subscribe_targets) > 0,
107 'subscribe_targets': subscribe_targets,
108 'episode': episode,
109 'episodes': episodes,
110 'max_listeners': max_listeners,
111 'can_flattr': can_flattr,
112 'is_publisher': is_publisher,
113 'page_list': page_list,
114 'current_page': 1,
118 def get_tags(podcast, user, max_tags=50):
119 """ Returns all tags that user sees for the given podcast
121 The tag list is a list of dicts in the form of {'tag': 'tech', 'is_own':
122 True}. "is_own" indicates if the tag was created by the given user. """
123 tags = {}
125 for tag in podcast.tags.all():
126 t = tag.tag.lower()
127 if not t in tags:
128 tags[t] = {'tag': t, 'is_own': False}
130 if tag.user == user:
131 tags[t]['is_own'] = True
133 return list(tags.values())
136 def episode_list(podcast, user, offset=0, limit=20):
137 """ Returns a list of episodes """
138 # fast pagination by using Episode.order instead of offset/limit
139 page_start = podcast.max_episode_order - offset
140 page_end = page_start - limit
141 return Episode.objects.filter(podcast=podcast,
142 order__lte=page_start,
143 order__gt=page_end)\
144 .prefetch_related('slugs')\
145 .order_by('-order')
148 def all_episodes(request, podcast, page_size=20):
150 # Make sure page request is an int. If not, deliver first page.
151 try:
152 page = int(request.GET.get('page', '1'))
153 except ValueError:
154 page = 1
156 user = request.user
158 episodes = episode_list(podcast, user, (page-1) * page_size, page_size)
159 episodes_total = podcast.episode_count or 0
160 num_pages = episodes_total / page_size
161 page_list = get_page_list(1, num_pages, page, 15)
163 max_listeners = max([e.listeners for e in episodes] + [0])
165 is_publisher = check_publisher_permission(user, podcast)
167 return render(request, 'episodes.html', {
168 'podcast': podcast,
169 'episodes': episodes,
170 'max_listeners': max_listeners,
171 'page_list': page_list,
172 'current_page': page,
173 'is_publisher': is_publisher,
177 @never_cache
178 @login_required
179 def add_tag(request, podcast):
181 tag_str = request.GET.get('tag', '')
182 if not tag_str:
183 return HttpResponseBadRequest()
185 user = request.user
187 tags = tag_str.split(',')
188 tags = list(map(str.strip, tags))
190 ContentType.objects.get_for_model(podcast)
192 for tag in tags:
193 Tag.objects.get_or_create(
194 tag=tag,
195 source=Tag.USER,
196 user=user,
197 content_type=ContentType.objects.get_for_model(podcast),
198 object_id=podcast.id,
201 if request.GET.get('next', '') == 'mytags':
202 return HttpResponseRedirect('/tags/')
204 return HttpResponseRedirect(get_podcast_link_target(podcast))
207 @never_cache
208 @login_required
209 def remove_tag(request, podcast):
211 tag_str = request.GET.get('tag', '')
212 if not tag_str:
213 return HttpResponseBadRequest()
215 user = request.user
217 tags = tag_str.split(',')
218 tags = list(map(str.strip, tags))
220 ContentType.objects.get_for_model(podcast)
222 Tag.objects.filter(
223 tag__in=tags,
224 source=Tag.USER,
225 user=user,
226 content_type=ContentType.objects.get_for_model(podcast),
227 object_id=podcast.id,
228 ).delete()
230 if request.GET.get('next', '') == 'mytags':
231 return HttpResponseRedirect('/tags/')
233 return HttpResponseRedirect(get_podcast_link_target(podcast))
236 @never_cache
237 @login_required
238 @allowed_methods(['GET', 'POST'])
239 def subscribe(request, podcast):
241 if request.method == 'POST':
243 # multiple UIDs from the /podcast/<slug>/subscribe
244 device_uids = [k for (k,v) in list(request.POST.items()) if k==v]
246 # single UID from /podcast/<slug>
247 if 'targets' in request.POST:
248 devices = request.POST.get('targets')
249 devices = devices.split(',')
250 device_uids.extend(devices)
252 for uid in device_uids:
253 try:
254 device = request.user.client_set.get(uid=uid)
255 subscribe_podcast(podcast, request.user, device)
257 except Client.DoesNotExist as e:
258 messages.error(request, str(e))
260 return HttpResponseRedirect(get_podcast_link_target(podcast))
262 targets = get_subscribe_targets(podcast, request.user)
264 return render(request, 'subscribe.html', {
265 'targets': targets,
266 'podcast': podcast,
270 @never_cache
271 @login_required
272 @allowed_methods(['POST'])
273 def subscribe_all(request, podcast):
274 """ subscribe all of the user's devices to the podcast """
275 user = request.user
276 subscribe_podcast_all(podcast, user)
277 return HttpResponseRedirect(get_podcast_link_target(podcast))
280 @never_cache
281 @login_required
282 def unsubscribe(request, podcast, device_uid):
284 return_to = request.GET.get('return_to', None)
286 if not return_to:
287 raise Http404('Wrong URL')
289 user = request.user
290 try:
291 device = user.client_set.get(uid=device_uid)
293 except Client.DoesNotExist as e:
294 messages.error(request, str(e))
295 return HttpResponseRedirect(return_to)
297 try:
298 unsubscribe_podcast(podcast, user, device)
299 except SubscriptionException as e:
300 logger.exception('Web: %(username)s: could not unsubscribe from podcast %(podcast_url)s on device %(device_id)s' %
301 {'username': request.user.username, 'podcast_url': podcast.url, 'device_id': device.id})
303 return HttpResponseRedirect(return_to)
306 @never_cache
307 @login_required
308 @allowed_methods(['POST'])
309 def unsubscribe_all(request, podcast):
310 """ unsubscribe all of the user's devices from the podcast """
311 user = request.user
312 unsubscribe_podcast_all(podcast, user)
313 return HttpResponseRedirect(get_podcast_link_target(podcast))
316 @never_cache
317 @login_required
318 def subscribe_url(request):
319 url = request.GET.get('url', None)
321 if not url:
322 raise Http404('http://my.gpodder.org/subscribe?url=http://www.example.com/podcast.xml')
324 url = normalize_feed_url(url)
326 if not url:
327 raise Http404('Please specify a valid url')
329 podcast = Podcast.objects.get_or_create_for_url(url)
331 return HttpResponseRedirect(get_podcast_link_target(podcast, 'subscribe'))
334 @never_cache
335 @allowed_methods(['POST'])
336 def set_public(request, podcast, public):
337 settings, created = UserSettings.objects.get_or_create(
338 user=request.user,
339 content_type=ContentType.objects.get_for_model(podcast),
340 object_id=podcast.pk,
342 settings.set_wksetting(PUBLIC_SUB_PODCAST, public)
343 settings.save()
344 return HttpResponseRedirect(get_podcast_link_target(podcast))
347 @never_cache
348 @login_required
349 def flattr_podcast(request, podcast):
350 """ Flattrs a podcast, records an event and redirects to the podcast """
352 user = request.user
353 site = RequestSite(request)
354 now = datetime.utcnow()
356 # do flattring via the tasks queue, but wait for the result
357 task = flattr_thing.delay(user, podcast.get_id(), site.domain,
358 request.is_secure(), 'Podcast')
359 success, msg = task.get()
361 if success:
362 messages.success(request, _("Flattr\'d"))
364 else:
365 messages.error(request, msg)
367 return HttpResponseRedirect(get_podcast_link_target(podcast))
370 # To make all view accessible via either IDs or Slugs
371 # a decorator queries the podcast and passes the Id on to the
372 # regular views
374 def slug_decorator(f):
375 @wraps(f)
376 def _decorator(request, slug, *args, **kwargs):
378 try:
379 podcast = Podcast.objects.filter(
380 slugs__slug=slug,
381 slugs__content_type=ContentType.objects.get_for_model(Podcast),
383 podcast = podcast.prefetch_related('slugs', 'urls').get()
384 except Podcast.DoesNotExist:
385 raise Http404
387 # redirect when a non-cannonical slug is used
388 if slug != podcast.slug:
389 return HttpResponseRedirect(get_podcast_link_target(podcast))
391 return f(request, podcast, *args, **kwargs)
393 return _decorator
396 def id_decorator(f):
397 @wraps(f)
398 def _decorator(request, podcast_id, *args, **kwargs):
400 try:
401 podcast = Podcast.objects.filter(id=podcast_id)
402 podcast = podcast.prefetch_related('slugs', 'urls').get()
404 # if the podcast has a slug, redirect to its canonical URL
405 if podcast.slug:
406 return HttpResponseRedirect(get_podcast_link_target(podcast))
408 return f(request, podcast, *args, **kwargs)
410 except Podcast.DoesNotExist:
411 podcast = get_object_or_404(Podcast, merged_uuids__uuid=podcast_id)
412 return HttpResponseRedirect(get_podcast_link_target(podcast))
414 return _decorator
417 show_slug = slug_decorator(show)
418 subscribe_slug = slug_decorator(subscribe)
419 subscribe_all_slug = slug_decorator(subscribe_all)
420 unsubscribe_slug = slug_decorator(unsubscribe)
421 unsubscribe_all_slug = slug_decorator(unsubscribe_all)
422 add_tag_slug = slug_decorator(add_tag)
423 remove_tag_slug = slug_decorator(remove_tag)
424 set_public_slug = slug_decorator(set_public)
425 all_episodes_slug = slug_decorator(all_episodes)
426 flattr_podcast_slug = slug_decorator(flattr_podcast)
429 show_id = id_decorator(show)
430 subscribe_id = id_decorator(subscribe)
431 subscribe_all_id = id_decorator(subscribe_all)
432 unsubscribe_id = id_decorator(unsubscribe)
433 unsubscribe_all_id = id_decorator(unsubscribe_all)
434 add_tag_id = id_decorator(add_tag)
435 remove_tag_id = id_decorator(remove_tag)
436 set_public_id = id_decorator(set_public)
437 all_episodes_id = id_decorator(all_episodes)
438 flattr_podcast_id = id_decorator(flattr_podcast)