Let publishers configure episode slugs
[mygpo.git] / mygpo / publisher / views.py
blob380baef26146796914d27d15a9d7d345e6c7fdf5
1 from functools import wraps
2 import urllib
4 from django.shortcuts import render
5 from django.http import HttpResponse, HttpResponseRedirect, \
6 HttpResponseForbidden, Http404
7 from django.core.cache import cache
8 from django.views.decorators.cache import never_cache, cache_control
9 from django.views.decorators.vary import vary_on_cookie
10 from django.core.urlresolvers import reverse
11 from django.utils.translation import ugettext as _
12 from django.contrib import messages
14 from mygpo.core.proxy import proxy_object
15 from mygpo.publisher.auth import require_publisher, is_publisher
16 from mygpo.publisher.forms import SearchPodcastForm
17 from mygpo.publisher.utils import listener_data, episode_listener_data, \
18 check_publisher_permission, subscriber_data
19 from mygpo.web.heatmap import EpisodeHeatmap
20 from mygpo.web.views.episode import oldid_decorator, slug_id_decorator
21 from mygpo.web.views.podcast import \
22 slug_id_decorator as podcast_slug_id_decorator, \
23 oldid_decorator as podcast_oldid_decorator
24 from mygpo.web.utils import get_podcast_link_target, normalize_twitter, \
25 get_episode_link_target
26 from django.contrib.sites.models import RequestSite
27 from mygpo.data.tasks import update_podcasts
28 from mygpo.decorators import requires_token, allowed_methods
29 from mygpo.users.models import User
30 from mygpo.db.couchdb.episode import episodes_for_podcast, episodes_for_slug, \
31 set_episode_slug, remove_episode_slug
32 from mygpo.db.couchdb.podcast import podcast_by_id, podcasts_by_id, \
33 podcast_for_url, podcastgroup_for_slug_id, podcastgroup_for_oldid, \
34 podcastgroup_by_id, update_additional_data
35 from mygpo.db.couchdb.episode_state import episode_listener_counts
38 @vary_on_cookie
39 @cache_control(private=True)
40 def home(request):
41 if is_publisher(request.user):
42 podcasts = podcasts_by_id(request.user.published_objects)
43 site = RequestSite(request)
44 update_token = request.user.get_token('publisher_update_token')
45 form = SearchPodcastForm()
46 return render(request, 'publisher/home.html', {
47 'update_token': update_token,
48 'podcasts': podcasts,
49 'form': form,
50 'site': site,
53 else:
54 site = RequestSite(request)
55 return render(request, 'publisher/info.html', {
56 'site': site
60 @vary_on_cookie
61 @cache_control(private=True)
62 @require_publisher
63 def search_podcast(request):
64 form = SearchPodcastForm(request.POST)
65 if form.is_valid():
66 url = form.cleaned_data['url']
68 podcast = podcast_for_url(url)
69 if not podcast:
70 raise Http404
72 url = get_podcast_link_target(podcast, 'podcast-publisher-detail')
73 else:
74 url = reverse('publisher')
76 return HttpResponseRedirect(url)
79 @vary_on_cookie
80 @cache_control(private=True)
81 @require_publisher
82 @allowed_methods(['GET', 'POST'])
83 def podcast(request, podcast):
85 if not check_publisher_permission(request.user, podcast):
86 return HttpResponseForbidden()
88 timeline_data = listener_data([podcast])
89 subscription_data = subscriber_data([podcast])[-20:]
91 if podcast.group:
92 group = podcastgroup_by_id(podcast.group)
93 else:
94 group = None
96 update_token = request.user.publisher_update_token
98 heatmap = EpisodeHeatmap(podcast.get_id())
100 site = RequestSite(request)
101 feedurl_quoted = urllib.quote(podcast.url)
103 return render(request, 'publisher/podcast.html', {
104 'site': site,
105 'podcast': podcast,
106 'group': group,
107 'form': None,
108 'timeline_data': timeline_data,
109 'subscriber_data': subscription_data,
110 'update_token': update_token,
111 'heatmap': heatmap,
112 'feedurl_quoted': feedurl_quoted,
116 @vary_on_cookie
117 @cache_control(private=True)
118 @require_publisher
119 def group(request, group):
121 podcasts = group.podcasts
123 # users need to have publisher access for at least one of the group's podcasts
124 if not any([check_publisher_permission(request.user, p) for p in podcasts]):
125 return HttpResponseForbidden()
127 timeline_data = listener_data(podcasts)
128 subscription_data = list(subscriber_data(podcasts))[-20:]
130 return render(request, 'publisher/group.html', {
131 'group': group,
132 'timeline_data': timeline_data,
133 'subscriber_data': subscription_data,
137 @vary_on_cookie
138 @cache_control(private=True)
139 @require_publisher
140 def update_podcast(request, podcast):
142 if not check_publisher_permission(request.user, podcast):
143 return HttpResponseForbidden()
145 update_podcasts.delay([podcast.url])
146 messages.success(request, _('The update has been scheduled. It might take some time until the results are visible.'))
148 url = get_podcast_link_target(podcast, 'podcast-publisher-detail')
149 return HttpResponseRedirect(url)
152 @vary_on_cookie
153 @cache_control(private=True)
154 @require_publisher
155 def save_podcast(request, podcast):
156 twitter = normalize_twitter(request.POST.get('twitter', ''))
157 update_additional_data(podcast, twitter)
158 messages.success(request, _('Data updated'))
159 url = get_podcast_link_target(podcast, 'podcast-publisher-detail')
160 return HttpResponseRedirect(url)
164 @never_cache
165 @require_publisher
166 def new_update_token(request, username):
167 request.user.create_new_token('publisher_update_token')
168 request.user.save()
169 messages.success(request, _('Publisher token updated'))
170 return HttpResponseRedirect(reverse('publisher'))
173 @never_cache
174 @requires_token(token_name='publisher_update_token')
175 def update_published_podcasts(request, username):
176 user = User.get_user(username)
177 if not user:
178 raise Http404
180 published_podcasts = podcasts_by_id(user.published_objects)
181 update_podcasts.delay([podcast.url for podcast in published_podcasts])
182 return HttpResponse('Updated:\n' + '\n'.join([p.url for p in published_podcasts]), mimetype='text/plain')
185 @vary_on_cookie
186 @cache_control(private=True)
187 @require_publisher
188 def episodes(request, podcast):
190 if not check_publisher_permission(request.user, podcast):
191 return HttpResponseForbidden()
193 episodes = episodes_for_podcast(podcast, descending=True)
194 listeners = dict(episode_listener_counts(podcast))
196 max_listeners = max(listeners.values() + [0])
198 def annotate_episode(episode):
199 listener_count = listeners.get(episode._id, None)
200 return proxy_object(episode, listeners=listener_count)
202 episodes = map(annotate_episode, episodes)
204 return render(request, 'publisher/episodes.html', {
205 'podcast': podcast,
206 'episodes': episodes,
207 'max_listeners': max_listeners
211 @require_publisher
212 @vary_on_cookie
213 @cache_control(private=True)
214 @allowed_methods(['GET', 'POST'])
215 def episode(request, episode):
217 site = RequestSite(request)
218 podcast = podcast_by_id(episode.podcast)
220 if not check_publisher_permission(request.user, podcast):
221 return HttpResponseForbidden()
223 if request.method == 'POST':
224 form = None #EpisodeForm(request.POST, instance=e)
225 #if form.is_valid():
226 # form.save()
228 elif request.method == 'GET':
229 form = None #EpisodeForm(instance=e)
231 timeline_data = list(episode_listener_data(episode))
233 heatmap = EpisodeHeatmap(episode.podcast, episode._id,
234 duration=episode.duration)
236 return render(request, 'publisher/episode.html', {
237 'is_secure': request.is_secure(),
238 'domain': site.domain,
239 'episode': episode,
240 'podcast': podcast,
241 'form': form,
242 'timeline_data': timeline_data,
243 'heatmap': heatmap,
247 @require_publisher
248 @never_cache
249 @allowed_methods(['POST'])
250 def update_episode_slug(request, episode):
251 """ sets a new "main" slug, and moves the existing to the merged slugs """
253 new_slug = request.POST.get('slug')
254 podcast = podcast_by_id(episode.podcast)
256 if new_slug:
257 # remove the new slug from other episodes (of the same podcast)
258 other_episodes = episodes_for_slug(podcast.get_id(), new_slug)
260 for other_episode in other_episodes:
262 if other_episode == episode:
263 continue
265 remove_episode_slug(other_episode, new_slug)
266 messages.warning(request,
267 _(u'Removed slug {slug} from {episode}'.format(
268 slug=new_slug, episode=other_episode.title))
271 set_episode_slug(episode, new_slug)
273 # TODO: we should use better cache invalidation
274 cache.clear()
276 return HttpResponseRedirect(
277 get_episode_link_target(episode, podcast, 'episode-publisher-detail')
281 @vary_on_cookie
282 @cache_control(private=True)
283 def link(request):
284 current_site = RequestSite(request)
285 return render(request, 'link.html', {
286 'url': current_site
290 @vary_on_cookie
291 @cache_control(private=True)
292 def advertise(request):
293 site = RequestSite(request)
294 return render(request, 'publisher/advertise.html', {
295 'site': site
299 def group_slug_id_decorator(f):
300 @wraps(f)
301 def _decorator(request, slug_id, *args, **kwargs):
302 group = podcastgroup_for_slug_id(slug_id)
304 if podcast is None:
305 raise Http404
307 return f(request, group, *args, **kwargs)
309 return _decorator
312 def group_oldid_decorator(f):
313 @wraps(f)
314 def _decorator(request, pid, *args, **kwargs):
315 try:
316 pid = int(pid)
317 except (TypeError, ValueError):
318 raise Http404
320 group = podcastgroup_for_oldid(pid)
322 if not podcast:
323 raise Http404
325 return f(request, group, *args, **kwargs)
327 return _decorator
331 episode_oldid = oldid_decorator(episode)
332 podcast_oldid = podcast_oldid_decorator(podcast)
333 update_podcast_oldid = podcast_oldid_decorator(update_podcast)
334 save_podcast_oldid = podcast_oldid_decorator(save_podcast)
335 episodes_oldid = podcast_oldid_decorator(episodes)
336 group_oldid = group_oldid_decorator(group)
338 episode_slug_id = slug_id_decorator(episode)
339 update_episode_slug_slug_id = slug_id_decorator(update_episode_slug)
340 podcast_slug_id = podcast_slug_id_decorator(podcast)
341 episodes_slug_id = podcast_slug_id_decorator(episodes)
342 update_podcast_slug_id = podcast_slug_id_decorator(update_podcast)
343 save_podcast_slug_id = podcast_slug_id_decorator(save_podcast)
344 group_slug_id = group_slug_id_decorator(group)