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
).all().by_released()
138 episodes
= list(episodes
.prefetch_related('slugs')[offset
:offset
+limit
])
142 def all_episodes(request
, podcast
, page_size
=20):
144 # Make sure page request is an int. If not, deliver first page.
146 page
= int(request
.GET
.get('page', '1'))
152 episodes
= episode_list(podcast
, user
, (page
-1) * page_size
, page_size
)
153 episodes_total
= podcast
.episode_count
or 0
154 num_pages
= episodes_total
/ page_size
155 page_list
= get_page_list(1, num_pages
, page
, 15)
157 max_listeners
= max([e
.listeners
for e
in episodes
] + [0])
159 is_publisher
= check_publisher_permission(user
, podcast
)
161 return render(request
, 'episodes.html', {
163 'episodes': episodes
,
164 'max_listeners': max_listeners
,
165 'page_list': page_list
,
166 'current_page': page
,
167 'is_publisher': is_publisher
,
173 def add_tag(request
, podcast
):
175 tag_str
= request
.GET
.get('tag', '')
177 return HttpResponseBadRequest()
181 tags
= tag_str
.split(',')
182 tags
= map(unicode.strip
, tags
)
184 ContentType
.objects
.get_for_model(podcast
)
187 Tag
.objects
.get_or_create(
191 content_type
=ContentType
.objects
.get_for_model(podcast
),
192 object_id
=podcast
.id,
195 if request
.GET
.get('next', '') == 'mytags':
196 return HttpResponseRedirect('/tags/')
198 return HttpResponseRedirect(get_podcast_link_target(podcast
))
203 def remove_tag(request
, podcast
):
205 tag_str
= request
.GET
.get('tag', '')
207 return HttpResponseBadRequest()
211 tags
= tag_str
.split(',')
212 tags
= map(unicode.strip
, tags
)
214 ContentType
.objects
.get_for_model(podcast
)
220 content_type
=ContentType
.objects
.get_for_model(podcast
),
221 object_id
=podcast
.id,
224 if request
.GET
.get('next', '') == 'mytags':
225 return HttpResponseRedirect('/tags/')
227 return HttpResponseRedirect(get_podcast_link_target(podcast
))
232 @allowed_methods(['GET', 'POST'])
233 def subscribe(request
, podcast
):
235 if request
.method
== 'POST':
237 # multiple UIDs from the /podcast/<slug>/subscribe
238 device_uids
= [k
for (k
,v
) in request
.POST
.items() if k
==v
]
240 # single UID from /podcast/<slug>
241 if 'targets' in request
.POST
:
242 devices
= request
.POST
.get('targets')
243 devices
= devices
.split(',')
244 device_uids
.extend(devices
)
246 for uid
in device_uids
:
248 device
= request
.user
.client_set
.get(uid
=uid
)
249 subscribe_podcast(podcast
, request
.user
, device
)
251 except Client
.DoesNotExist
as e
:
252 messages
.error(request
, str(e
))
254 return HttpResponseRedirect(get_podcast_link_target(podcast
))
256 targets
= get_subscribe_targets(podcast
, request
.user
)
258 return render(request
, 'subscribe.html', {
266 @allowed_methods(['POST'])
267 def subscribe_all(request
, podcast
):
268 """ subscribe all of the user's devices to the podcast """
270 subscribe_podcast_all(podcast
, user
)
271 return HttpResponseRedirect(get_podcast_link_target(podcast
))
276 def unsubscribe(request
, podcast
, device_uid
):
278 return_to
= request
.GET
.get('return_to', None)
281 raise Http404('Wrong URL')
285 device
= user
.client_set
.get(uid
=device_uid
)
287 except Client
.DoesNotExist
as e
:
288 messages
.error(request
, str(e
))
289 return HttpResponseRedirect(return_to
)
292 unsubscribe_podcast(podcast
, user
, device
)
293 except SubscriptionException
as e
:
294 logger
.exception('Web: %(username)s: could not unsubscribe from podcast %(podcast_url)s on device %(device_id)s' %
295 {'username': request
.user
.username
, 'podcast_url': podcast
.url
, 'device_id': device
.id})
297 return HttpResponseRedirect(return_to
)
302 @allowed_methods(['POST'])
303 def unsubscribe_all(request
, podcast
):
304 """ unsubscribe all of the user's devices from the podcast """
306 unsubscribe_podcast_all(podcast
, user
)
307 return HttpResponseRedirect(get_podcast_link_target(podcast
))
312 def subscribe_url(request
):
313 url
= request
.GET
.get('url', None)
316 raise Http404('http://my.gpodder.org/subscribe?url=http://www.example.com/podcast.xml')
318 url
= normalize_feed_url(url
)
321 raise Http404('Please specify a valid url')
323 podcast
= Podcast
.objects
.get_or_create_for_url(url
)
325 return HttpResponseRedirect(get_podcast_link_target(podcast
, 'subscribe'))
329 @allowed_methods(['POST'])
330 def set_public(request
, podcast
, public
):
331 settings
, created
= UserSettings
.objects
.get_or_create(
333 content_type
=ContentType
.objects
.get_for_model(podcast
),
334 object_id
=podcast
.pk
,
336 settings
.set_wksetting(PUBLIC_SUB_PODCAST
, public
)
338 return HttpResponseRedirect(get_podcast_link_target(podcast
))
343 def flattr_podcast(request
, podcast
):
344 """ Flattrs a podcast, records an event and redirects to the podcast """
347 site
= RequestSite(request
)
348 now
= datetime
.utcnow()
350 # do flattring via the tasks queue, but wait for the result
351 task
= flattr_thing
.delay(user
, podcast
.get_id(), site
.domain
,
352 request
.is_secure(), 'Podcast')
353 success
, msg
= task
.get()
356 messages
.success(request
, _("Flattr\'d"))
359 messages
.error(request
, msg
)
361 return HttpResponseRedirect(get_podcast_link_target(podcast
))
364 # To make all view accessible via either IDs or Slugs
365 # a decorator queries the podcast and passes the Id on to the
368 def slug_decorator(f
):
370 def _decorator(request
, slug
, *args
, **kwargs
):
373 podcast
= Podcast
.objects
.filter(
375 slugs__content_type
=ContentType
.objects
.get_for_model(Podcast
),
377 podcast
= podcast
.prefetch_related('slugs', 'urls').get()
378 except Podcast
.DoesNotExist
:
381 # redirect when a non-cannonical slug is used
382 if slug
!= podcast
.slug
:
383 return HttpResponseRedirect(get_podcast_link_target(podcast
))
385 return f(request
, podcast
, *args
, **kwargs
)
392 def _decorator(request
, podcast_id
, *args
, **kwargs
):
395 podcast
= Podcast
.objects
.filter(id=podcast_id
)
396 podcast
= podcast
.prefetch_related('slugs', 'urls').get()
398 # if the podcast has a slug, redirect to its canonical URL
400 return HttpResponseRedirect(get_podcast_link_target(podcast
))
402 return f(request
, podcast
, *args
, **kwargs
)
404 except Podcast
.DoesNotExist
:
405 podcast
= get_object_or_404(Podcast
, merged_uuids__uuid
=podcast_id
)
406 return HttpResponseRedirect(get_podcast_link_target(podcast
))
411 show_slug
= slug_decorator(show
)
412 subscribe_slug
= slug_decorator(subscribe
)
413 subscribe_all_slug
= slug_decorator(subscribe_all
)
414 unsubscribe_slug
= slug_decorator(unsubscribe
)
415 unsubscribe_all_slug
= slug_decorator(unsubscribe_all
)
416 add_tag_slug
= slug_decorator(add_tag
)
417 remove_tag_slug
= slug_decorator(remove_tag
)
418 set_public_slug
= slug_decorator(set_public
)
419 all_episodes_slug
= slug_decorator(all_episodes
)
420 flattr_podcast_slug
= slug_decorator(flattr_podcast
)
423 show_id
= id_decorator(show
)
424 subscribe_id
= id_decorator(subscribe
)
425 subscribe_all_id
= id_decorator(subscribe_all
)
426 unsubscribe_id
= id_decorator(unsubscribe
)
427 unsubscribe_all_id
= id_decorator(unsubscribe_all
)
428 add_tag_id
= id_decorator(add_tag
)
429 remove_tag_id
= id_decorator(remove_tag
)
430 set_public_id
= id_decorator(set_public
)
431 all_episodes_id
= id_decorator(all_episodes
)
432 flattr_podcast_id
= id_decorator(flattr_podcast
)