6 from django.conf import settings
7 from django.core.exceptions import PermissionDenied
8 from django.core.files.storage import DefaultStorage
9 from django.core.urlresolvers import reverse_lazy
10 from django.http import HttpResponse, Http404, HttpResponseRedirect
11 from django.core.urlresolvers import reverse, reverse_lazy
12 from django.shortcuts import redirect
13 from django.utils.translation import ugettext as _, ugettext_lazy
15 from django.views.decorators.csrf import csrf_exempt
16 from django.views.generic.base import View, TemplateView, RedirectView
17 from django.views.generic.edit import CreateView, UpdateView, DeleteView
18 from django.views.generic.list import ListView
19 from django.views.generic.detail import DetailView
21 from django.contrib import messages
25 from .models import Emission, Episode, Diffusion, Category, Schedule, \
26 SoundFile, NewsItem, NewsCategory, Absence, \
28 from .forms import EmissionForm, EpisodeForm, EpisodeNewForm, ScheduleForm, \
29 DiffusionForm, SoundFileForm, NewsItemForm, \
30 SoundFileEditForm, AbsenceForm, PlaylistElementForm
33 __all__ = ['EmissionListView', 'EmissionDetailView', 'EmissionCreateView',
34 'EmissionUpdateView', 'EmissionDeleteView',
35 'EpisodeCreateView', 'EpisodeDetailView', 'EpisodeUpdateView',
36 'EpisodeDeleteView', 'EmissionAddScheduleView',
37 'ScheduleDeleteView', 'CategoryListView', 'DaysView',
38 'UploadView', 'EpisodeAddSoundFileView',
39 'EpisodeAddDiffusionView', 'DiffusionDeleteView',
40 'EmissionNewsItemAdd', 'NewsItemDetailView',
41 'NewsItemUpdateView', 'CategoryNewsItemAddView',
42 'NewsItemDeleteView', 'NewsItemAddView',
43 'SoundFileDeleteView', 'SoundFileUpdateView',
44 'EmissionAddAbsenceView', 'AbsenceDeleteView',
45 'EmissionOpenChatView', 'EmissionCloseChatView',
47 'EpisodeRegieUpdateOrderView',
48 'EpisodeRegieMarks', 'EpisodeRegieDeleteElementView',
49 'EpisodeStartView', 'EpisodeStopView',
53 SUCCESS_MESSAGE = ugettext_lazy('Your changes will appear online in a few minutes.')
56 class EmissionListView(ListView):
59 def get_queryset(self):
60 return Emission.objects.order_by('title')
63 class CategoryListView(ListView):
66 def get_queryset(self):
67 return Category.objects.order_by('title')
70 class EmissionDetailView(DetailView):
73 def get_context_data(self, **kwargs):
74 context = super(EmissionDetailView, self).get_context_data(**kwargs)
75 context['add_schedule_form'] = ScheduleForm(initial={'emission': self.object})
76 context['add_absence_form'] = AbsenceForm(
77 initial={'emission': self.object, 'user': self.request.user})
78 context['schedules'] = Schedule.objects.select_related().filter(
79 emission=self.object).order_by('datetime')
81 # get all episodes, with an additional attribute to get the date of
82 # their first diffusion
83 context['episodes'] = \
84 Episode.objects.select_related().filter(emission=self.object
86 'first_diffusion': 'emissions_diffusion.datetime',
88 select_params=(False, True),
89 where=['''datetime = (SELECT MIN(datetime)
90 FROM emissions_diffusion
91 WHERE episode_id = emissions_episode.id)'''],
92 tables=['emissions_diffusion'],
93 ).order_by('-first_diffusion').distinct()
95 # get all related soundfiles in a single query
97 for soundfile in SoundFile.objects.filter(podcastable=True,
98 fragment=False, episode__emission=self.object):
99 soundfiles[soundfile.episode_id] = soundfile
101 Episode.set_prefetched_soundfiles(soundfiles)
103 context['newsitems'] = self.object.get_sorted_newsitems()
104 context['absences'] = self.object.absence_set.select_related(
105 ).filter(datetime__gte=datetime.date.today()).order_by('datetime')
107 if not 'all' in self.request.GET:
108 context['episodes'] = context['episodes'][:10]
109 if context['absences']:
110 context['newsitems'] = context['newsitems'][:5]
112 context['newsitems'] = context['newsitems'][:10]
115 context['can_manage'] = self.request.user.can_manage(self.object)
116 except AttributeError:
122 class EmissionCreateView(CreateView):
123 form_class = EmissionForm
126 success_url = reverse_lazy('emission-list')
128 def get_form(self, *args, **kwargs):
129 if not self.request.user.has_perm('emissions.add_emission'):
130 raise PermissionDenied()
131 return super(EmissionCreateView, self).get_form(*args, **kwargs)
133 def get_success_url(self):
134 messages.success(self.request, SUCCESS_MESSAGE)
135 return super(EmissionCreateView, self).get_success_url()
138 class EmissionUpdateView(UpdateView):
139 form_class = EmissionForm
142 def get_form(self, *args, **kwargs):
143 if not self.request.user.can_manage(self.object):
144 raise PermissionDenied()
145 return super(EmissionUpdateView, self).get_form(*args, **kwargs)
147 def get_success_url(self):
148 messages.success(self.request, SUCCESS_MESSAGE)
149 return super(EmissionUpdateView, self).get_success_url()
152 class EmissionDeleteView(DeleteView):
154 success_url = reverse_lazy('home')
156 def get_form(self, *args, **kwargs):
157 if not self.request.user.can_manage(self.object):
158 raise PermissionDenied()
159 return super(EmissionDeleteView, self).get_form(*args, **kwargs)
161 def get_success_url(self):
162 messages.success(self.request, SUCCESS_MESSAGE)
163 return super(EmissionDeleteView, self).get_success_url()
166 class EpisodeCreateView(CreateView):
168 form_class = EpisodeNewForm
170 def get_form(self, *args, **kwargs):
171 emission = Emission.objects.get(slug=self.kwargs.get('emission_slug'))
172 if not self.request.user.can_manage(emission):
173 raise PermissionDenied()
174 return super(EpisodeCreateView, self).get_form(*args, **kwargs)
176 def get_initial(self):
177 initial = super(EpisodeCreateView, self).get_initial()
178 initial['emission'] = Emission.objects.get(slug=self.kwargs.get('emission_slug'))
179 initial['duration'] = initial['emission'].duration
180 initial['first_diffusion'] = initial['emission'].get_next_planned_date()
181 for i, schedule in enumerate(
182 Schedule.objects.filter(emission=initial['emission'],
183 rerun=True).order_by('datetime')):
184 rerun_date = schedule.get_next_planned_date(initial['first_diffusion'])
186 initial['second_diffusion'] = rerun_date
188 initial['third_diffusion'] = rerun_date
190 initial['fourth_diffusion'] = rerun_date
193 def get_context_data(self, **kwargs):
194 context = super(EpisodeCreateView, self).get_context_data(**kwargs)
195 context['emission'] = Emission.objects.get(slug=self.kwargs.get('emission_slug'))
198 def get_success_url(self):
199 messages.success(self.request, SUCCESS_MESSAGE)
200 return self.object.get_absolute_url()
203 class EmissionEpisodeMixin(object):
204 def get_queryset(self):
205 return self.model.objects.filter(emission__slug=self.kwargs['emission_slug'])
208 class EpisodeDetailView(EmissionEpisodeMixin, DetailView):
211 def get_context_data(self, **kwargs):
212 context = super(EpisodeDetailView, self).get_context_data(**kwargs)
213 context['diffusions'] = Diffusion.objects.filter(episode=self.object.id)
214 context['soundfiles'] = SoundFile.objects.filter(episode=self.object.id)
217 context['can_manage'] = self.request.user.can_manage(self.object.emission)
218 except AttributeError:
224 class EpisodeUpdateView(EmissionEpisodeMixin, UpdateView):
225 form_class = EpisodeForm
228 def get_form(self, *args, **kwargs):
229 if not self.request.user.can_manage(self.object.emission):
230 raise PermissionDenied()
231 return super(EpisodeUpdateView, self).get_form(*args, **kwargs)
233 def get_success_url(self):
234 messages.success(self.request, SUCCESS_MESSAGE)
235 return super(EpisodeUpdateView, self).get_success_url()
238 class EpisodeDeleteView(EmissionEpisodeMixin, DeleteView):
241 def get_form(self, *args, **kwargs):
242 if not self.request.user.can_manage(self.object.emission):
243 raise PermissionDenied()
244 return super(EpisodeDeleteView, self).get_form(*args, **kwargs)
246 def get_success_url(self):
247 messages.success(self.request, SUCCESS_MESSAGE)
248 return reverse('emission-view', kwargs={
249 'slug': self.object.emission.slug})
252 class EmissionAddScheduleView(CreateView):
253 form_class = ScheduleForm
256 def get_form(self, *args, **kwargs):
257 if not self.request.user.has_perm('emissions.add_schedule'):
258 raise PermissionDenied()
259 return super(EmissionAddScheduleView, self).get_form(*args, **kwargs)
261 def get_success_url(self):
262 messages.success(self.request, SUCCESS_MESSAGE)
263 return self.object.emission.get_absolute_url()
266 class ScheduleDeleteView(RedirectView):
267 def get_redirect_url(self, emission_slug, pk):
268 if not self.request.user.has_perm('emissions.delete_schedule'):
269 raise PermissionDenied()
270 Schedule.objects.filter(id=pk).delete()
271 messages.success(self.request, SUCCESS_MESSAGE)
272 return reverse('emission-view', kwargs={'slug': str(emission_slug)})
275 class EmissionAddAbsenceView(CreateView):
276 form_class = AbsenceForm
279 def get_initial(self):
280 initial = super(EmissionAddAbsenceView, self).get_initial()
281 initial['emission'] = Emission.objects.get(slug=self.kwargs.get('slug'))
282 initial['user'] = self.request.user
285 def get_success_url(self):
286 messages.success(self.request, SUCCESS_MESSAGE)
287 return self.object.emission.get_absolute_url()
289 def get_form(self, *args, **kwargs):
290 emission = Emission.objects.get(slug=self.kwargs.get('slug'))
291 if not self.request.user.can_manage(emission):
292 raise PermissionDenied()
293 return super(EmissionAddAbsenceView, self).get_form(*args, **kwargs)
296 class AbsenceDeleteView(RedirectView):
297 def get_redirect_url(self, emission_slug, pk):
298 if not self.request.user.has_perm('emissions.delete_absence'):
299 raise PermissionDenied()
300 Absence.objects.filter(id=pk).delete()
301 messages.success(self.request, SUCCESS_MESSAGE)
302 return reverse('emission-view', kwargs={'slug': str(emission_slug)})
305 class DaysView(TemplateView):
306 template_name = 'emissions/days.html'
308 def get_context_data(self, **kwargs):
309 context = super(DaysView, self).get_context_data(**kwargs)
310 schedules = Schedule.objects.all().order_by('datetime')
313 days.append({'schedules': [x for x in schedules if x.is_on_weekday(day+1)],
314 'datetime': datetime.datetime(2007, 1, day+1)})
315 context['days'] = days
318 class JSONResponse(HttpResponse):
319 """JSON response class."""
320 def __init__(self, obj='', json_opts={}, mimetype='application/json', *args, **kwargs):
321 content = json.dumps(obj, **json_opts)
322 super(JSONResponse,self).__init__(content, mimetype, *args, **kwargs)
325 class UploadView(View):
327 def response_mimetype(self, request):
328 if 'application/json' in request.META['HTTP_ACCEPT']:
329 return 'application/json'
334 def post(self, request, transaction_id):
335 storage = DefaultStorage()
336 max_filename_length = 256
337 url = reverse('upload', kwargs={'transaction_id': transaction_id})
338 if request.FILES is None:
339 response = JSONResponse({}, {}, self.response_mimetype(request))
340 response['Content-Disposition'] = 'inline; filename=files.json'
343 for uploaded_file in request.FILES.values():
344 path = os.path.join('upload', str(transaction_id), uploaded_file.name)
345 filename = storage.save(path, uploaded_file)
346 url = '%s%s' % (url, os.path.basename(filename))
347 data.append({'name': uploaded_file.name, 'size': uploaded_file.size, 'url': url})
348 response = JSONResponse(data, {}, self.response_mimetype(request))
349 response['Content-Disposition'] = 'inline; filename=files.json'
353 class EpisodeAddSoundFileView(CreateView):
354 form_class = SoundFileForm
357 def get_initial(self):
359 'episode': Episode.objects.get(
360 slug=self.kwargs.get('slug'),
361 emission__slug=self.kwargs.get('emission_slug')),
362 'title': _('Record'),
364 context['license'] = context['episode'].emission.default_license
367 def get_form(self, *args, **kwargs):
368 emission = Emission.objects.get(slug=self.kwargs.get('emission_slug'))
369 if not self.request.user.can_manage(emission):
370 raise PermissionDenied()
371 return super(EpisodeAddSoundFileView, self).get_form(*args, **kwargs)
373 def get_success_url(self):
374 messages.success(self.request, SUCCESS_MESSAGE)
375 return self.object.episode.get_absolute_url()
378 class EpisodeAddDiffusionView(CreateView):
379 form_class = DiffusionForm
382 def get_initial(self):
384 'episode': Episode.objects.get(
385 slug=self.kwargs.get('slug'),
386 emission__slug=self.kwargs.get('emission_slug')),
389 def get_form(self, *args, **kwargs):
390 if not self.request.user.has_perm('emissions.add_diffusion'):
391 raise PermissionDenied()
392 episode = Episode.objects.get(slug=self.kwargs.get('slug'),
393 emission__slug=self.kwargs.get('emission_slug'))
394 if not self.request.user.can_manage(episode):
395 raise PermissionDenied()
396 return super(EpisodeAddDiffusionView, self).get_form(*args, **kwargs)
398 def get_success_url(self):
399 messages.success(self.request, SUCCESS_MESSAGE)
400 return self.object.episode.get_absolute_url()
403 class DiffusionDeleteView(RedirectView):
404 def get_redirect_url(self, emission_slug, slug, pk):
405 if not self.request.user.has_perm('emissions.delete_diffusion'):
406 raise PermissionDenied()
407 episode = Episode.objects.get(slug=slug, emission__slug=emission_slug)
408 if not self.request.user.can_manage(episode):
409 raise PermissionDenied()
410 Diffusion.objects.filter(id=pk).delete()
411 messages.success(self.request, SUCCESS_MESSAGE)
412 return reverse('episode-view', kwargs={'emission_slug': str(emission_slug),
416 class FacetedSearchView(haystack.views.FacetedSearchView):
417 def extra_context(self):
418 context = super(FacetedSearchView, self).extra_context()
419 context['selected_categories'] = [
420 x.split(':', 1)[1] for x in self.request.GET.getlist('selected_facets')
421 if x.startswith('categories_exact')]
422 context['selected_tags'] = [
423 x.split(':', 1)[1] for x in self.request.GET.getlist('selected_facets')
424 if x.startswith('tags_exact')]
428 class EmissionNewsItemAdd(CreateView):
430 form_class = NewsItemForm
432 def get_form(self, *args, **kwargs):
433 emission = Emission.objects.get(slug=self.kwargs.get('emission_slug'))
434 if not self.request.user.can_manage(emission):
435 raise PermissionDenied()
436 return super(EmissionNewsItemAdd, self).get_form(*args, **kwargs)
438 def get_initial(self):
439 initial = super(EmissionNewsItemAdd, self).get_initial()
440 initial['emission'] = Emission.objects.get(slug=self.kwargs.get('emission_slug'))
441 initial['date'] = datetime.datetime.today()
442 initial['user'] = self.request.user
445 def get_context_data(self, **kwargs):
446 context = super(EmissionNewsItemAdd, self).get_context_data(**kwargs)
447 context['emission'] = Emission.objects.get(slug=self.kwargs.get('emission_slug'))
450 def get_success_url(self):
451 messages.success(self.request, SUCCESS_MESSAGE)
452 return self.object.get_absolute_url()
455 class NewsItemDetailView(DetailView):
458 def get_context_data(self, **kwargs):
459 context = super(NewsItemDetailView, self).get_context_data(**kwargs)
460 context['can_manage'] = self.request.user.can_manage(self.object)
464 class NewsItemUpdateView(UpdateView):
465 form_class = NewsItemForm
468 def get_form(self, *args, **kwargs):
469 if not self.request.user.can_manage(self.object):
470 raise PermissionDenied()
471 return super(NewsItemUpdateView, self).get_form(*args, **kwargs)
473 def get_initial(self):
474 initial = super(NewsItemUpdateView, self).get_initial()
475 initial['user'] = self.request.user
478 def get_success_url(self):
479 messages.success(self.request, SUCCESS_MESSAGE)
480 return super(NewsItemUpdateView, self).get_success_url()
483 class CategoryNewsItemAddView(CreateView):
485 form_class = NewsItemForm
487 def get_form(self, *args, **kwargs):
488 if not self.request.user.has_perm('emissions.add_newsitem'):
489 raise PermissionDenied()
490 return super(CategoryNewsItemAddView, self).get_form(*args, **kwargs)
492 def get_initial(self):
493 initial = super(CategoryNewsItemAddView, self).get_initial()
494 initial['category'] = NewsCategory.objects.get(slug=self.kwargs.get('slug'))
495 initial['date'] = datetime.datetime.today()
496 initial['user'] = self.request.user
499 def get_success_url(self):
500 messages.success(self.request, SUCCESS_MESSAGE)
501 return self.object.get_absolute_url()
504 class NewsItemAddView(CreateView):
506 form_class = NewsItemForm
508 def get_form(self, *args, **kwargs):
509 if not self.request.user.has_perm('emissions.add_newsitem'):
510 raise PermissionDenied()
511 return super(NewsItemAddView, self).get_form(*args, **kwargs)
513 def get_initial(self):
514 initial = super(NewsItemAddView, self).get_initial()
515 initial['date'] = datetime.datetime.today()
516 initial['user'] = self.request.user
519 def get_success_url(self):
520 messages.success(self.request, SUCCESS_MESSAGE)
521 return self.object.get_absolute_url()
524 class NewsItemDeleteView(DeleteView):
526 success_url = reverse_lazy('home')
528 def get_form(self, *args, **kwargs):
529 if not self.request.user.can_manage(self.object):
530 raise PermissionDenied()
531 return super(NewsItemDeleteView, self).get_form(*args, **kwargs)
533 def get_success_url(self):
534 messages.success(self.request, SUCCESS_MESSAGE)
535 return super(NewsItemDeleteView, self).get_success_url()
538 class SoundFileDeleteView(DeleteView):
541 def get_form(self, *args, **kwargs):
542 if not self.request.user.can_manage(self.object):
543 raise PermissionDenied()
544 return super(SoundFileDeleteView, self).get_form(*args, **kwargs)
546 def get_success_url(self):
547 messages.success(self.request, SUCCESS_MESSAGE)
548 return reverse('episode-view', kwargs={
549 'emission_slug': self.object.episode.emission.slug,
550 'slug': self.object.episode.slug})
553 class SoundFileUpdateView(UpdateView):
554 template_name_suffix = '_form_update'
555 form_class = SoundFileEditForm
558 def get_form(self, *args, **kwargs):
559 if not self.request.user.can_manage(self.object.episode.emission):
560 raise PermissionDenied()
561 return super(SoundFileUpdateView, self).get_form(*args, **kwargs)
563 def get_success_url(self):
564 messages.success(self.request, SUCCESS_MESSAGE)
568 class EmissionOpenChatView(DetailView):
571 def render_to_response(self, context):
572 self.object.chat_open = datetime.datetime.now()
574 return redirect(self.object.get_absolute_url())
577 class EmissionCloseChatView(DetailView):
580 def render_to_response(self, context):
581 self.object.chat_open = None
583 return redirect(self.object.get_absolute_url())
586 class EpisodeRegieView(EmissionEpisodeMixin, DetailView):
588 template_name = 'emissions/episode_regie.html'
590 def get_object(self, queryset=None):
592 return Episode.objects.get(slug=self.kwargs.get('slug'),
593 emission__slug=self.kwargs.get('emission_slug'))
594 except Episode.DoesNotExist:
597 def post(self, request, *args, **kwargs):
598 episode = self.get_object()
599 form = PlaylistElementForm(request.POST, request.FILES)
600 if not form.is_valid():
601 messages.error(request, _('Error adding file'))
602 return HttpResponseRedirect('.')
603 i = PlaylistElement.objects.filter(episode=episode).count()
604 playlist_element = PlaylistElement(
606 title=os.path.basename(form.cleaned_data['sound'].name),
607 sound=form.cleaned_data['sound'],
609 playlist_element.save()
610 messages.info(request, _('File uploaded successfully.'))
611 return HttpResponseRedirect('.')
613 def get_context_data(self, **kwargs):
614 context = super(EpisodeRegieView, self).get_context_data(**kwargs)
615 context['playlist'] = PlaylistElement.objects.filter(episode=self.object.id)
616 context['start_time'] = ''
617 context['end_time'] = ''
618 if self.object.effective_start:
619 context['ready'] = 'ready'
620 context['start_time'] = int(time.mktime(self.object.effective_start.timetuple())*1000
621 + self.object.effective_start.microsecond/1000)
622 if self.object.effective_end:
623 context['end_time'] = int(time.mktime(self.object.effective_end.timetuple())*1000
624 + self.object.effective_end.microsecond/1000)
625 context['download_url'] = self.object.get_pige_download_url()
628 context['can_manage'] = self.request.user.can_manage(self.object)
629 except AttributeError:
632 context['upload_file_form'] = PlaylistElementForm(initial={'episode': self.object})
637 class EpisodeRegieUpdateOrderView(View):
638 def get(self, request, *args, **kwargs):
639 new_order = request.GET.getlist('new-order[]')
640 for element in PlaylistElement.objects.filter(id__in=new_order):
641 element.order = new_order.index(str(element.id)) + 1
643 return HttpResponse('ok')
646 class EpisodeRegieDeleteElementView(EpisodeRegieView):
647 def get(self, request, *args, **kwargs):
648 element_id = request.GET.get('id')
649 element = PlaylistElement.objects.get(id=element_id)
650 if not self.request.user.can_manage(element.episode):
651 return JSONResponse({'err': 1})
653 return HttpResponseRedirect('..')
656 class EpisodeRegieMarks(EpisodeRegieView):
657 def get(self, request, *args, **kwargs):
658 episode = self.get_object()
659 start = self.request.GET['start']
660 end = self.request.GET['end']
662 episode.effective_start = datetime.datetime.fromtimestamp(float(start)/1000)
664 episode.effective_start = None
666 episode.effective_end = datetime.datetime.fromtimestamp(float(end)/1000)
668 episode.effective_end = None
672 obj['pige_download_url'] = episode.get_pige_download_url()
673 return JSONResponse(obj)
676 class EpisodeStartView(EmissionEpisodeMixin, RedirectView):
677 def get_redirect_url(self, emission_slug, slug):
678 episode = Episode.objects.get(slug=slug, emission__slug=emission_slug)
679 episode.effective_start = datetime.datetime.now()
681 messages.success(self.request, _('Started recording'))
682 return reverse('episode-view',
683 kwargs={'emission_slug': emission_slug,
687 class EpisodeStopView(EmissionEpisodeMixin, RedirectView):
688 def get_redirect_url(self, emission_slug, slug):
689 episode = Episode.objects.get(slug=slug, emission__slug=emission_slug)
690 episode.effective_end = datetime.datetime.now()
692 messages.success(self.request, _('Stopped recording'))
693 return reverse('episode-view',
694 kwargs={'emission_slug': emission_slug,