[API] handle missing URLs in episode action upload
[mygpo.git] / mygpo / publisher / views.py
blobbb7706c8b78c042ccbb42953e1dbb39f7ec4908c
1 from functools import wraps
2 import urllib.request, urllib.parse, urllib.error
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
13 from django.contrib.auth import get_user_model
14 from django.contrib.contenttypes.models import ContentType
15 from django.shortcuts import get_object_or_404
17 from mygpo.podcasts.models import PodcastGroup, Podcast, Episode
18 from mygpo.publisher.auth import require_publisher, is_publisher
19 from mygpo.publisher.forms import SearchPodcastForm
20 from mygpo.publisher.utils import listener_data, episode_listener_data, \
21 check_publisher_permission, subscriber_data
22 from mygpo.web.heatmap import EpisodeHeatmap
23 from mygpo.web.views.episode import (slug_decorator as episode_slug_decorator,
24 id_decorator as episode_id_decorator)
25 from mygpo.web.views.podcast import (slug_decorator as podcast_slug_decorator,
26 id_decorator as podcast_id_decorator)
27 from mygpo.web.utils import get_podcast_link_target, normalize_twitter, \
28 get_episode_link_target
29 from django.contrib.sites.requests import RequestSite
30 from mygpo.data.tasks import update_podcasts
31 from mygpo.decorators import requires_token, allowed_methods
32 from mygpo.pubsub.models import HubSubscription
35 @vary_on_cookie
36 @cache_control(private=True)
37 def home(request):
38 if is_publisher(request.user):
39 podcasts = Podcast.objects.filter(publishedpodcast__publisher=request.user)\
40 .prefetch_related('slugs')
41 site = RequestSite(request)
42 update_token = request.user.profile.get_token('publisher_update_token')
43 form = SearchPodcastForm()
44 return render(request, 'publisher/home.html', {
45 'update_token': update_token,
46 'podcasts': podcasts,
47 'form': form,
48 'site': site,
51 else:
52 site = RequestSite(request)
53 return render(request, 'publisher/info.html', {
54 'site': site
58 @vary_on_cookie
59 @cache_control(private=True)
60 @require_publisher
61 def search_podcast(request):
62 form = SearchPodcastForm(request.POST)
63 if form.is_valid():
64 podcast_url = form.cleaned_data['url']
65 podcast = get_objet_or_404(Podcast, urls__url=podcast_url)
66 url = get_podcast_link_target(podcast, 'podcast-publisher-detail')
67 else:
68 url = reverse('publisher')
70 return HttpResponseRedirect(url)
73 @vary_on_cookie
74 @cache_control(private=True)
75 @require_publisher
76 @allowed_methods(['GET', 'POST'])
77 def podcast(request, podcast):
79 if not check_publisher_permission(request.user, podcast):
80 return HttpResponseForbidden()
82 timeline_data = listener_data([podcast])
83 subscription_data = subscriber_data([podcast])[-20:]
85 update_token = request.user.profile.get_token('publisher_update_token')
87 heatmap = EpisodeHeatmap(podcast)
89 try:
90 pubsubscription = HubSubscription.objects.get(topic_url=podcast.url)
91 except HubSubscription.DoesNotExist:
92 pubsubscription = None
94 site = RequestSite(request)
95 feedurl_quoted = urllib.parse.quote(podcast.url)
97 return render(request, 'publisher/podcast.html', {
98 'site': site,
99 'podcast': podcast,
100 'group': podcast.group,
101 'form': None,
102 'timeline_data': timeline_data,
103 'subscriber_data': subscription_data,
104 'update_token': update_token,
105 'heatmap': heatmap,
106 'feedurl_quoted': feedurl_quoted,
107 'pubsubscription': pubsubscription,
111 @vary_on_cookie
112 @cache_control(private=True)
113 @require_publisher
114 def group(request, group):
116 podcasts = group.podcasts
118 # users need to have publisher access for at least one of the group's podcasts
119 if not any([check_publisher_permission(request.user, p) for p in podcasts]):
120 return HttpResponseForbidden()
122 timeline_data = listener_data(podcasts)
123 subscription_data = list(subscriber_data(podcasts))[-20:]
125 return render(request, 'publisher/group.html', {
126 'group': group,
127 'timeline_data': timeline_data,
128 'subscriber_data': subscription_data,
132 @vary_on_cookie
133 @cache_control(private=True)
134 @require_publisher
135 def update_podcast(request, podcast):
137 if not check_publisher_permission(request.user, podcast):
138 return HttpResponseForbidden()
140 update_podcasts.delay([podcast.url])
141 messages.success(request, _('The update has been scheduled. It might take some time until the results are visible.'))
143 url = get_podcast_link_target(podcast, 'podcast-publisher-detail')
144 return HttpResponseRedirect(url)
147 @vary_on_cookie
148 @cache_control(private=True)
149 @require_publisher
150 def save_podcast(request, podcast):
151 twitter = normalize_twitter(request.POST.get('twitter', ''))
152 podcast.twitter = twitter
153 podcast.save()
154 messages.success(request, _('Data updated'))
155 url = get_podcast_link_target(podcast, 'podcast-publisher-detail')
156 return HttpResponseRedirect(url)
160 @never_cache
161 @require_publisher
162 def new_update_token(request, username):
163 request.user.profile.create_new_token('publisher_update_token')
164 request.user.profile.save()
165 messages.success(request, _('Publisher token updated'))
166 return HttpResponseRedirect(reverse('publisher'))
169 @never_cache
170 @requires_token(token_name='publisher_update_token')
171 def update_published_podcasts(request, username):
172 User = get_user_model()
173 user = get_object_or_404(User, username=username)
174 published_podcasts = [pp.podcast for pp in user.publishedpodcast_set.all()]
175 update_podcasts.delay([podcast.url for podcast in published_podcasts])
176 return HttpResponse('Updated:\n' + '\n'.join([p.url for p in published_podcasts]), content_type='text/plain')
179 @vary_on_cookie
180 @cache_control(private=True)
181 @require_publisher
182 def episodes(request, podcast):
184 if not check_publisher_permission(request.user, podcast):
185 return HttpResponseForbidden()
187 episodes = Episode.objects.filter(podcast=podcast).select_related('podcast').prefetch_related('slugs', 'podcast__slugs')
189 listeners = map(None, (e.listeners for e in episodes))
190 max_listeners = max(listeners, default=0)
192 return render(request, 'publisher/episodes.html', {
193 'podcast': podcast,
194 'episodes': episodes,
195 'max_listeners': max_listeners
199 @require_publisher
200 @vary_on_cookie
201 @cache_control(private=True)
202 @allowed_methods(['GET', 'POST'])
203 def episode(request, episode):
205 site = RequestSite(request)
206 podcast = episode.podcast
208 if not check_publisher_permission(request.user, podcast):
209 return HttpResponseForbidden()
211 if request.method == 'POST':
212 form = None # EpisodeForm(request.POST, instance=e)
213 # if form.is_valid():
214 # form.save()
216 elif request.method == 'GET':
217 form = None # EpisodeForm(instance=e)
219 timeline_data = list(episode_listener_data(episode))
221 heatmap = EpisodeHeatmap(episode.podcast, episode, duration=episode.duration)
223 return render(request, 'publisher/episode.html', {
224 'is_secure': request.is_secure(),
225 'domain': site.domain,
226 'episode': episode,
227 'podcast': podcast,
228 'form': form,
229 'timeline_data': timeline_data,
230 'heatmap': heatmap,
234 @require_publisher
235 @never_cache
236 @allowed_methods(['POST'])
237 def update_episode_slug(request, episode):
238 """ sets a new "main" slug, and moves the existing to the merged slugs """
240 new_slug = request.POST.get('slug')
241 podcast = episode.podcast
243 if new_slug:
244 # remove the new slug from other episodes (of the same podcast)
245 other_episodes = Episode.objects.filter(
246 podcast=podcast,
247 slugs__slug=new_slug,
248 slugs__content_type=ContentType.objects.get_for_model(Episode),
251 for other_episode in other_episodes:
253 if other_episode == episode:
254 continue
256 other_episode.remove_slug(new_slug)
257 messages.warning(request,
258 _('Removed slug {slug} from {episode}'.format(
259 slug=new_slug, episode=other_episode.title))
262 episode.set_slug(new_slug)
264 # TODO: we should use better cache invalidation
265 cache.clear()
267 return HttpResponseRedirect(
268 get_episode_link_target(episode, podcast, 'episode-publisher-detail')
272 @vary_on_cookie
273 @cache_control(private=True)
274 def link(request):
275 current_site = RequestSite(request)
276 return render(request, 'link.html', {
277 'url': current_site
281 @vary_on_cookie
282 @cache_control(private=True)
283 def advertise(request):
284 site = RequestSite(request)
285 return render(request, 'publisher/advertise.html', {
286 'site': site
290 def group_id_decorator(f):
291 @wraps(f)
292 def _decorator(request, pg_slug, *args, **kwargs):
293 group = get_object_or_404(PodcastGroup, pk=slug_id)
294 return f(request, group, *args, **kwargs)
296 return _decorator
299 episode_slug = episode_slug_decorator(episode)
300 update_episode_slug_slug = episode_slug_decorator(update_episode_slug)
301 podcast_slug = podcast_slug_decorator(podcast)
302 episodes_slug = podcast_slug_decorator(episodes)
303 update_podcast_slug = podcast_slug_decorator(update_podcast)
304 save_podcast_slug = podcast_slug_decorator(save_podcast)
306 episode_id = episode_id_decorator(episode)
307 update_episode_slug_id = episode_id_decorator(update_episode_slug)
308 podcast_id = podcast_id_decorator(podcast)
309 episodes_id = podcast_id_decorator(episodes)
310 update_podcast_id = podcast_id_decorator(update_podcast)
311 save_podcast_id = podcast_id_decorator(save_podcast)
313 group_slug = group_id_decorator(group)
314 group_id = group_id_decorator(group)