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
,
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
, \
39 logger
= logging
.getLogger(__name__
)
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
)
52 episodes
= episode_list(podcast
, request
.user
, limit
=num_episodes
)
55 max_listeners
= max([e
.listeners
for e
in episodes
] + [0])
61 episodes
= episodes
[1:]
65 rel_podcasts
= group
.podcast_set
.exclude(pk
=podcast
.pk
)
69 tags
= get_tags(podcast
, user
)
70 has_tagged
= any(t
['is_own'] for t
in tags
)
72 if user
.is_authenticated():
73 subscribed_devices
= Client
.objects
.filter(
74 subscription__user
=user
,
75 subscription__podcast
=podcast
,
78 subscribe_targets
= get_subscribe_targets(podcast
, user
)
80 has_history
= HistoryEntry
.objects
.filter(user
=user
, podcast
=podcast
)\
82 can_flattr
= (user
.profile
.settings
.get_wksetting(FLATTR_TOKEN
) and
87 subscribed_devices
= []
88 subscribe_targets
= []
91 is_publisher
= check_publisher_permission(user
, podcast
)
93 episodes_total
= podcast
.episode_count
or 0
94 num_pages
= episodes_total
/ num_episodes
95 page_list
= get_page_list(1, num_pages
, 1, 15)
97 return render(request
, 'podcast.html', {
99 'has_tagged': has_tagged
,
101 'has_history': has_history
,
103 'devices': subscribed_devices
,
104 'related_podcasts': rel_podcasts
,
105 'can_subscribe': len(subscribe_targets
) > 0,
106 'subscribe_targets': subscribe_targets
,
108 'episodes': episodes
,
109 'max_listeners': max_listeners
,
110 'can_flattr': can_flattr
,
111 'is_publisher': is_publisher
,
112 'page_list': page_list
,
117 def get_tags(podcast
, user
, max_tags
=50):
118 """ Returns all tags that user sees for the given podcast
120 The tag list is a list of dicts in the form of {'tag': 'tech', 'is_own':
121 True}. "is_own" indicates if the tag was created by the given user. """
124 for tag
in podcast
.tags
.all():
127 tags
[t
] = {'tag': t
, 'is_own': False}
130 tags
[t
]['is_own'] = True
135 def episode_list(podcast
, user
, offset
=0, limit
=None):
136 """ Returns a list of episodes """
137 episodes
= Episode
.objects
.filter(podcast
=podcast
)\
138 .order_by('-order', '-released')
139 episodes
= list(episodes
.prefetch_related('slugs')[offset
:offset
+limit
])
143 def all_episodes(request
, podcast
, page_size
=20):
145 # Make sure page request is an int. If not, deliver first page.
147 page
= int(request
.GET
.get('page', '1'))
153 episodes
= episode_list(podcast
, user
, (page
-1) * page_size
, page_size
)
154 episodes_total
= podcast
.episode_count
or 0
155 num_pages
= episodes_total
/ page_size
156 page_list
= get_page_list(1, num_pages
, page
, 15)
158 max_listeners
= max([e
.listeners
for e
in episodes
] + [0])
160 is_publisher
= check_publisher_permission(user
, podcast
)
162 return render(request
, 'episodes.html', {
164 'episodes': episodes
,
165 'max_listeners': max_listeners
,
166 'page_list': page_list
,
167 'current_page': page
,
168 'is_publisher': is_publisher
,
174 def add_tag(request
, podcast
):
176 tag_str
= request
.GET
.get('tag', '')
178 return HttpResponseBadRequest()
182 tags
= tag_str
.split(',')
183 tags
= map(unicode.strip
, tags
)
185 ContentType
.objects
.get_for_model(podcast
)
188 Tag
.objects
.get_or_create(
192 content_type
=ContentType
.objects
.get_for_model(podcast
),
193 object_id
=podcast
.id,
196 if request
.GET
.get('next', '') == 'mytags':
197 return HttpResponseRedirect('/tags/')
199 return HttpResponseRedirect(get_podcast_link_target(podcast
))
204 def remove_tag(request
, podcast
):
206 tag_str
= request
.GET
.get('tag', '')
208 return HttpResponseBadRequest()
212 tags
= tag_str
.split(',')
213 tags
= map(unicode.strip
, tags
)
215 ContentType
.objects
.get_for_model(podcast
)
221 content_type
=ContentType
.objects
.get_for_model(podcast
),
222 object_id
=podcast
.id,
225 if request
.GET
.get('next', '') == 'mytags':
226 return HttpResponseRedirect('/tags/')
228 return HttpResponseRedirect(get_podcast_link_target(podcast
))
233 @allowed_methods(['GET', 'POST'])
234 def subscribe(request
, podcast
):
236 if request
.method
== 'POST':
238 # multiple UIDs from the /podcast/<slug>/subscribe
239 device_uids
= [k
for (k
,v
) in request
.POST
.items() if k
==v
]
241 # single UID from /podcast/<slug>
242 if 'targets' in request
.POST
:
243 devices
= request
.POST
.get('targets')
244 devices
= devices
.split(',')
245 device_uids
.extend(devices
)
247 for uid
in device_uids
:
249 device
= request
.user
.client_set
.get(uid
=uid
)
250 subscribe_podcast(podcast
, request
.user
, device
)
252 except Client
.DoesNotExist
as e
:
253 messages
.error(request
, str(e
))
255 return HttpResponseRedirect(get_podcast_link_target(podcast
))
257 targets
= get_subscribe_targets(podcast
, request
.user
)
259 return render(request
, 'subscribe.html', {
267 @allowed_methods(['POST'])
268 def subscribe_all(request
, podcast
):
269 """ subscribe all of the user's devices to the podcast """
271 subscribe_podcast_all(podcast
, user
)
272 return HttpResponseRedirect(get_podcast_link_target(podcast
))
277 def unsubscribe(request
, podcast
, device_uid
):
279 return_to
= request
.GET
.get('return_to', None)
282 raise Http404('Wrong URL')
286 device
= user
.client_set
.get(uid
=device_uid
)
288 except Client
.DoesNotExist
as e
:
289 messages
.error(request
, str(e
))
290 return HttpResponseRedirect(return_to
)
293 unsubscribe_podcast(podcast
, user
, device
)
294 except SubscriptionException
as e
:
295 logger
.exception('Web: %(username)s: could not unsubscribe from podcast %(podcast_url)s on device %(device_id)s' %
296 {'username': request
.user
.username
, 'podcast_url': podcast
.url
, 'device_id': device
.id})
298 return HttpResponseRedirect(return_to
)
303 @allowed_methods(['POST'])
304 def unsubscribe_all(request
, podcast
):
305 """ unsubscribe all of the user's devices from the podcast """
307 unsubscribe_podcast_all(podcast
, user
)
308 return HttpResponseRedirect(get_podcast_link_target(podcast
))
313 def subscribe_url(request
):
314 url
= request
.GET
.get('url', None)
317 raise Http404('http://my.gpodder.org/subscribe?url=http://www.example.com/podcast.xml')
319 url
= normalize_feed_url(url
)
322 raise Http404('Please specify a valid url')
324 podcast
= Podcast
.objects
.get_or_create_for_url(url
)
326 return HttpResponseRedirect(get_podcast_link_target(podcast
, 'subscribe'))
330 @allowed_methods(['POST'])
331 def set_public(request
, podcast
, public
):
332 settings
, created
= UserSettings
.objects
.get_or_create(
334 content_type
=ContentType
.objects
.get_for_model(podcast
),
335 object_id
=podcast
.pk
,
337 settings
.set_wksetting(PUBLIC_SUB_PODCAST
, public
)
339 return HttpResponseRedirect(get_podcast_link_target(podcast
))
344 def flattr_podcast(request
, podcast
):
345 """ Flattrs a podcast, records an event and redirects to the podcast """
348 site
= RequestSite(request
)
349 now
= datetime
.utcnow()
351 # do flattring via the tasks queue, but wait for the result
352 task
= flattr_thing
.delay(user
, podcast
.get_id(), site
.domain
,
353 request
.is_secure(), 'Podcast')
354 success
, msg
= task
.get()
357 messages
.success(request
, _("Flattr\'d"))
360 messages
.error(request
, msg
)
362 return HttpResponseRedirect(get_podcast_link_target(podcast
))
365 # To make all view accessible via either IDs or Slugs
366 # a decorator queries the podcast and passes the Id on to the
369 def slug_decorator(f
):
371 def _decorator(request
, slug
, *args
, **kwargs
):
374 podcast
= Podcast
.objects
.filter(
376 slugs__content_type
=ContentType
.objects
.get_for_model(Podcast
),
378 podcast
= podcast
.prefetch_related('slugs', 'urls').get()
379 except Podcast
.DoesNotExist
:
382 # redirect when a non-cannonical slug is used
383 if slug
!= podcast
.slug
:
384 return HttpResponseRedirect(get_podcast_link_target(podcast
))
386 return f(request
, podcast
, *args
, **kwargs
)
393 def _decorator(request
, podcast_id
, *args
, **kwargs
):
396 podcast
= Podcast
.objects
.filter(id=podcast_id
)
397 podcast
= podcast
.prefetch_related('slugs', 'urls').get()
399 # if the podcast has a slug, redirect to its canonical URL
401 return HttpResponseRedirect(get_podcast_link_target(podcast
))
403 return f(request
, podcast
, *args
, **kwargs
)
405 except Podcast
.DoesNotExist
:
406 podcast
= get_object_or_404(Podcast
, merged_uuids__uuid
=podcast_id
)
407 return HttpResponseRedirect(get_podcast_link_target(podcast
))
412 show_slug
= slug_decorator(show
)
413 subscribe_slug
= slug_decorator(subscribe
)
414 subscribe_all_slug
= slug_decorator(subscribe_all
)
415 unsubscribe_slug
= slug_decorator(unsubscribe
)
416 unsubscribe_all_slug
= slug_decorator(unsubscribe_all
)
417 add_tag_slug
= slug_decorator(add_tag
)
418 remove_tag_slug
= slug_decorator(remove_tag
)
419 set_public_slug
= slug_decorator(set_public
)
420 all_episodes_slug
= slug_decorator(all_episodes
)
421 flattr_podcast_slug
= slug_decorator(flattr_podcast
)
424 show_id
= id_decorator(show
)
425 subscribe_id
= id_decorator(subscribe
)
426 subscribe_all_id
= id_decorator(subscribe_all
)
427 unsubscribe_id
= id_decorator(unsubscribe
)
428 unsubscribe_all_id
= id_decorator(unsubscribe_all
)
429 add_tag_id
= id_decorator(add_tag
)
430 remove_tag_id
= id_decorator(remove_tag
)
431 set_public_id
= id_decorator(set_public
)
432 all_episodes_id
= id_decorator(all_episodes
)
433 flattr_podcast_id
= id_decorator(flattr_podcast
)