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',
52 SUCCESS_MESSAGE = ugettext_lazy('Your changes will appear online in a few minutes.')
55 class EmissionListView(ListView):
58 def get_queryset(self):
59 return Emission.objects.order_by('title')
62 class CategoryListView(ListView):
65 def get_queryset(self):
66 return Category.objects.order_by('title')
69 class EmissionDetailView(DetailView):
72 def get_context_data(self, **kwargs):
73 context = super(EmissionDetailView, self).get_context_data(**kwargs)
74 context['add_schedule_form'] = ScheduleForm(initial={'emission': self.object})
75 context['add_absence_form'] = AbsenceForm(
76 initial={'emission': self.object, 'user': self.request.user})
77 context['schedules'] = Schedule.objects.select_related().filter(
78 emission=self.object).order_by('datetime')
80 # get all episodes, with an additional attribute to get the date of
81 # their first diffusion
82 context['episodes'] = \
83 Episode.objects.select_related().filter(emission=self.object
85 'first_diffusion': 'emissions_diffusion.datetime',
87 select_params=(False, True),
88 where=['''datetime = (SELECT MIN(datetime)
89 FROM emissions_diffusion
90 WHERE episode_id = emissions_episode.id)'''],
91 tables=['emissions_diffusion'],
92 ).order_by('-first_diffusion').distinct()
94 # get all related soundfiles in a single query
96 for soundfile in SoundFile.objects.filter(podcastable=True,
97 fragment=False, episode__emission=self.object):
98 soundfiles[soundfile.episode_id] = soundfile
100 Episode.set_prefetched_soundfiles(soundfiles)
102 context['newsitems'] = self.object.get_sorted_newsitems()
103 context['absences'] = self.object.absence_set.select_related(
104 ).filter(datetime__gte=datetime.date.today()).order_by('datetime')
106 if not 'all' in self.request.GET:
107 context['episodes'] = context['episodes'][:10]
108 if context['absences']:
109 context['newsitems'] = context['newsitems'][:5]
111 context['newsitems'] = context['newsitems'][:10]
114 context['can_manage'] = self.request.user.can_manage(self.object)
115 except AttributeError:
121 class EmissionCreateView(CreateView):
122 form_class = EmissionForm
125 success_url = reverse_lazy('emission-list')
127 def get_form(self, *args, **kwargs):
128 if not self.request.user.has_perm('emissions.add_emission'):
129 raise PermissionDenied()
130 return super(EmissionCreateView, self).get_form(*args, **kwargs)
132 def get_success_url(self):
133 messages.success(self.request, SUCCESS_MESSAGE)
134 return super(EmissionCreateView, self).get_success_url()
137 class EmissionUpdateView(UpdateView):
138 form_class = EmissionForm
141 def get_form(self, *args, **kwargs):
142 if not self.request.user.can_manage(self.object):
143 raise PermissionDenied()
144 return super(EmissionUpdateView, self).get_form(*args, **kwargs)
146 def get_success_url(self):
147 messages.success(self.request, SUCCESS_MESSAGE)
148 return super(EmissionUpdateView, self).get_success_url()
151 class EmissionDeleteView(DeleteView):
153 success_url = reverse_lazy('home')
155 def get_form(self, *args, **kwargs):
156 if not self.request.user.can_manage(self.object):
157 raise PermissionDenied()
158 return super(EmissionDeleteView, self).get_form(*args, **kwargs)
160 def get_success_url(self):
161 messages.success(self.request, SUCCESS_MESSAGE)
162 return super(EmissionDeleteView, self).get_success_url()
165 class EpisodeCreateView(CreateView):
167 form_class = EpisodeNewForm
169 def get_form(self, *args, **kwargs):
170 emission = Emission.objects.get(slug=self.kwargs.get('emission_slug'))
171 if not self.request.user.can_manage(emission):
172 raise PermissionDenied()
173 return super(EpisodeCreateView, self).get_form(*args, **kwargs)
175 def get_initial(self):
176 initial = super(EpisodeCreateView, self).get_initial()
177 initial['emission'] = Emission.objects.get(slug=self.kwargs.get('emission_slug'))
178 initial['duration'] = initial['emission'].duration
179 initial['first_diffusion'] = initial['emission'].get_next_planned_date()
180 for i, schedule in enumerate(
181 Schedule.objects.filter(emission=initial['emission'],
182 rerun=True).order_by('datetime')):
183 rerun_date = schedule.get_next_planned_date(initial['first_diffusion'])
185 initial['second_diffusion'] = rerun_date
187 initial['third_diffusion'] = rerun_date
189 initial['fourth_diffusion'] = rerun_date
192 def get_context_data(self, **kwargs):
193 context = super(EpisodeCreateView, self).get_context_data(**kwargs)
194 context['emission'] = Emission.objects.get(slug=self.kwargs.get('emission_slug'))
197 def get_success_url(self):
198 messages.success(self.request, SUCCESS_MESSAGE)
199 return self.object.get_absolute_url()
202 class EpisodeDetailView(DetailView):
205 def get_context_data(self, **kwargs):
206 context = super(EpisodeDetailView, self).get_context_data(**kwargs)
207 context['diffusions'] = Diffusion.objects.filter(episode=self.object.id)
208 context['soundfiles'] = SoundFile.objects.filter(episode=self.object.id)
209 context['add_soundfile_form'] = SoundFileForm(initial={
210 'episode': self.object, 'title': _('Record')})
211 context['add_diffusion_form'] = DiffusionForm(initial={'episode': self.object})
214 context['can_manage'] = self.request.user.can_manage(self.object.emission)
215 except AttributeError:
221 class EpisodeUpdateView(UpdateView):
222 form_class = EpisodeForm
225 def get_form(self, *args, **kwargs):
226 if not self.request.user.can_manage(self.object.emission):
227 raise PermissionDenied()
228 return super(EpisodeUpdateView, self).get_form(*args, **kwargs)
230 def get_success_url(self):
231 messages.success(self.request, SUCCESS_MESSAGE)
232 return super(EpisodeUpdateView, self).get_success_url()
235 class EpisodeDeleteView(DeleteView):
238 def get_form(self, *args, **kwargs):
239 if not self.request.user.can_manage(self.object.emission):
240 raise PermissionDenied()
241 return super(EpisodeDeleteView, self).get_form(*args, **kwargs)
243 def get_success_url(self):
244 messages.success(self.request, SUCCESS_MESSAGE)
245 return reverse('emission-view', kwargs={
246 'slug': self.object.emission.slug})
249 class EmissionAddScheduleView(CreateView):
250 form_class = ScheduleForm
253 def get_form(self, *args, **kwargs):
254 if not self.request.user.has_perm('emissions.add_schedule'):
255 raise PermissionDenied()
256 return super(EmissionAddScheduleView, self).get_form(*args, **kwargs)
258 def get_success_url(self):
259 messages.success(self.request, SUCCESS_MESSAGE)
260 return self.object.emission.get_absolute_url()
263 class ScheduleDeleteView(RedirectView):
264 def get_redirect_url(self, emission_slug, pk):
265 if not self.request.user.has_perm('emissions.delete_schedule'):
266 raise PermissionDenied()
267 Schedule.objects.filter(id=pk).delete()
268 messages.success(self.request, SUCCESS_MESSAGE)
269 return reverse('emission-view', kwargs={'slug': str(emission_slug)})
272 class EmissionAddAbsenceView(CreateView):
273 form_class = AbsenceForm
276 def get_initial(self):
277 initial = super(EmissionAddAbsenceView, self).get_initial()
278 initial['emission'] = Emission.objects.get(slug=self.kwargs.get('slug'))
279 initial['user'] = self.request.user
282 def get_success_url(self):
283 messages.success(self.request, SUCCESS_MESSAGE)
284 return self.object.emission.get_absolute_url()
286 def get_form(self, *args, **kwargs):
287 emission = Emission.objects.get(slug=self.kwargs.get('slug'))
288 if not self.request.user.can_manage(emission):
289 raise PermissionDenied()
290 return super(EmissionAddAbsenceView, self).get_form(*args, **kwargs)
293 class AbsenceDeleteView(RedirectView):
294 def get_redirect_url(self, emission_slug, pk):
295 if not self.request.user.has_perm('emissions.delete_absence'):
296 raise PermissionDenied()
297 Absence.objects.filter(id=pk).delete()
298 messages.success(self.request, SUCCESS_MESSAGE)
299 return reverse('emission-view', kwargs={'slug': str(emission_slug)})
302 class DaysView(TemplateView):
303 template_name = 'emissions/days.html'
305 def get_context_data(self, **kwargs):
306 context = super(DaysView, self).get_context_data(**kwargs)
307 schedules = Schedule.objects.all().order_by('datetime')
310 days.append({'schedules': [x for x in schedules if x.is_on_weekday(day+1)],
311 'datetime': datetime.datetime(2007, 1, day+1)})
312 context['days'] = days
315 class JSONResponse(HttpResponse):
316 """JSON response class."""
317 def __init__(self, obj='', json_opts={}, mimetype='application/json', *args, **kwargs):
318 content = json.dumps(obj, **json_opts)
319 super(JSONResponse,self).__init__(content, mimetype, *args, **kwargs)
322 class UploadView(View):
324 def response_mimetype(self, request):
325 if 'application/json' in request.META['HTTP_ACCEPT']:
326 return 'application/json'
331 def post(self, request, transaction_id):
332 storage = DefaultStorage()
333 max_filename_length = 256
334 url = reverse('upload', kwargs={'transaction_id': transaction_id})
335 if request.FILES is None:
336 response = JSONResponse({}, {}, self.response_mimetype(request))
337 response['Content-Disposition'] = 'inline; filename=files.json'
340 for uploaded_file in request.FILES.values():
341 path = os.path.join('upload', str(transaction_id), uploaded_file.name)
342 filename = storage.save(path, uploaded_file)
343 url = '%s%s' % (url, os.path.basename(filename))
344 data.append({'name': uploaded_file.name, 'size': uploaded_file.size, 'url': url})
345 response = JSONResponse(data, {}, self.response_mimetype(request))
346 response['Content-Disposition'] = 'inline; filename=files.json'
350 class EpisodeAddSoundFileView(CreateView):
351 form_class = SoundFileForm
354 def get_form(self, *args, **kwargs):
355 emission = Emission.objects.get(slug=self.kwargs.get('emission_slug'))
356 if not self.request.user.can_manage(emission):
357 raise PermissionDenied()
358 return super(EpisodeAddSoundFileView, self).get_form(*args, **kwargs)
360 def get_success_url(self):
361 messages.success(self.request, SUCCESS_MESSAGE)
362 return self.object.episode.get_absolute_url()
365 class EpisodeAddDiffusionView(CreateView):
366 form_class = DiffusionForm
369 def get_form(self, *args, **kwargs):
370 if not self.request.user.has_perm('emissions.add_diffusion'):
371 raise PermissionDenied()
372 episode = Episode.objects.get(slug=self.kwargs.get('slug'),
373 emission__slug=self.kwargs.get('emission_slug'))
374 if not self.request.user.can_manage(episode):
375 raise PermissionDenied()
376 return super(EpisodeAddDiffusionView, self).get_form(*args, **kwargs)
378 def get_success_url(self):
379 messages.success(self.request, SUCCESS_MESSAGE)
380 return self.object.episode.get_absolute_url()
383 class DiffusionDeleteView(RedirectView):
384 def get_redirect_url(self, emission_slug, slug, pk):
385 if not self.request.user.has_perm('emissions.delete_diffusion'):
386 raise PermissionDenied()
387 episode = Episode.objects.get(slug=slug, emission__slug=emission_slug)
388 if not self.request.user.can_manage(episode):
389 raise PermissionDenied()
390 Diffusion.objects.filter(id=pk).delete()
391 messages.success(self.request, SUCCESS_MESSAGE)
392 return reverse('episode-view', kwargs={'emission_slug': str(emission_slug),
396 class FacetedSearchView(haystack.views.FacetedSearchView):
397 def extra_context(self):
398 context = super(FacetedSearchView, self).extra_context()
399 context['selected_categories'] = [
400 x.split(':', 1)[1] for x in self.request.GET.getlist('selected_facets')
401 if x.startswith('categories_exact')]
402 context['selected_tags'] = [
403 x.split(':', 1)[1] for x in self.request.GET.getlist('selected_facets')
404 if x.startswith('tags_exact')]
408 class EmissionNewsItemAdd(CreateView):
410 form_class = NewsItemForm
412 def get_form(self, *args, **kwargs):
413 emission = Emission.objects.get(slug=self.kwargs.get('emission_slug'))
414 if not self.request.user.can_manage(emission):
415 raise PermissionDenied()
416 return super(EmissionNewsItemAdd, self).get_form(*args, **kwargs)
418 def get_initial(self):
419 initial = super(EmissionNewsItemAdd, self).get_initial()
420 initial['emission'] = Emission.objects.get(slug=self.kwargs.get('emission_slug'))
421 initial['date'] = datetime.datetime.today()
422 initial['user'] = self.request.user
425 def get_context_data(self, **kwargs):
426 context = super(EmissionNewsItemAdd, self).get_context_data(**kwargs)
427 context['emission'] = Emission.objects.get(slug=self.kwargs.get('emission_slug'))
430 def get_success_url(self):
431 messages.success(self.request, SUCCESS_MESSAGE)
432 return self.object.get_absolute_url()
435 class NewsItemDetailView(DetailView):
438 def get_context_data(self, **kwargs):
439 context = super(NewsItemDetailView, self).get_context_data(**kwargs)
440 context['can_manage'] = self.request.user.can_manage(self.object)
444 class NewsItemUpdateView(UpdateView):
445 form_class = NewsItemForm
448 def get_form(self, *args, **kwargs):
449 if not self.request.user.can_manage(self.object):
450 raise PermissionDenied()
451 return super(NewsItemUpdateView, self).get_form(*args, **kwargs)
453 def get_initial(self):
454 initial = super(NewsItemUpdateView, self).get_initial()
455 initial['user'] = self.request.user
458 def get_success_url(self):
459 messages.success(self.request, SUCCESS_MESSAGE)
460 return super(NewsItemUpdateView, self).get_success_url()
463 class CategoryNewsItemAddView(CreateView):
465 form_class = NewsItemForm
467 def get_form(self, *args, **kwargs):
468 if not self.request.user.has_perm('emissions.add_newsitem'):
469 raise PermissionDenied()
470 return super(CategoryNewsItemAddView, self).get_form(*args, **kwargs)
472 def get_initial(self):
473 initial = super(CategoryNewsItemAddView, self).get_initial()
474 initial['category'] = NewsCategory.objects.get(slug=self.kwargs.get('slug'))
475 initial['date'] = datetime.datetime.today()
476 initial['user'] = self.request.user
479 def get_success_url(self):
480 messages.success(self.request, SUCCESS_MESSAGE)
481 return self.object.get_absolute_url()
484 class NewsItemAddView(CreateView):
486 form_class = NewsItemForm
488 def get_form(self, *args, **kwargs):
489 if not self.request.user.has_perm('emissions.add_newsitem'):
490 raise PermissionDenied()
491 return super(NewsItemAddView, self).get_form(*args, **kwargs)
493 def get_initial(self):
494 initial = super(NewsItemAddView, self).get_initial()
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 NewsItemDeleteView(DeleteView):
506 success_url = reverse_lazy('home')
508 def get_form(self, *args, **kwargs):
509 if not self.request.user.can_manage(self.object):
510 raise PermissionDenied()
511 return super(NewsItemDeleteView, self).get_form(*args, **kwargs)
513 def get_success_url(self):
514 messages.success(self.request, SUCCESS_MESSAGE)
515 return super(NewsItemDeleteView, self).get_success_url()
518 class SoundFileDeleteView(DeleteView):
521 def get_form(self, *args, **kwargs):
522 if not self.request.user.can_manage(self.object):
523 raise PermissionDenied()
524 return super(SoundFileDeleteView, self).get_form(*args, **kwargs)
526 def get_success_url(self):
527 messages.success(self.request, SUCCESS_MESSAGE)
528 return reverse('episode-view', kwargs={
529 'emission_slug': self.object.episode.emission.slug,
530 'slug': self.object.episode.slug})
533 class SoundFileUpdateView(UpdateView):
534 template_name_suffix = '_form_update'
535 form_class = SoundFileEditForm
538 def get_form(self, *args, **kwargs):
539 if not self.request.user.can_manage(self.object.episode.emission):
540 raise PermissionDenied()
541 return super(SoundFileUpdateView, self).get_form(*args, **kwargs)
543 def get_success_url(self):
544 messages.success(self.request, SUCCESS_MESSAGE)
548 class EmissionOpenChatView(DetailView):
551 def render_to_response(self, context):
552 self.object.chat_open = datetime.datetime.now()
554 return redirect(self.object.get_absolute_url())
557 class EmissionCloseChatView(DetailView):
560 def render_to_response(self, context):
561 self.object.chat_open = None
563 return redirect(self.object.get_absolute_url())
566 class EpisodeRegieView(DetailView):
568 template_name = 'emissions/episode_regie.html'
570 def get_object(self, queryset=None):
572 return Episode.objects.get(slug=self.kwargs.get('slug'),
573 emission__slug=self.kwargs.get('emission_slug'))
574 except Episode.DoesNotExist:
577 def post(self, request, *args, **kwargs):
578 episode = self.get_object()
579 form = PlaylistElementForm(request.POST, request.FILES)
580 if not form.is_valid():
581 messages.error(request, _('Error adding file'))
582 return HttpResponseRedirect('.')
583 i = PlaylistElement.objects.filter(episode=episode).count()
584 playlist_element = PlaylistElement(
586 title=os.path.basename(form.cleaned_data['sound'].name),
587 sound=form.cleaned_data['sound'],
589 playlist_element.save()
590 messages.info(request, _('File uploaded successfully.'))
591 return HttpResponseRedirect('.')
593 def get_context_data(self, **kwargs):
594 context = super(EpisodeRegieView, self).get_context_data(**kwargs)
595 context['playlist'] = PlaylistElement.objects.filter(episode=self.object.id)
596 context['start_time'] = ''
597 context['end_time'] = ''
598 if self.object.effective_start:
599 context['ready'] = 'ready'
600 context['start_time'] = int(time.mktime(self.object.effective_start.timetuple())*1000
601 + self.object.effective_start.microsecond/1000)
602 if self.object.effective_end:
603 context['end_time'] = int(time.mktime(self.object.effective_end.timetuple())*1000
604 + self.object.effective_end.microsecond/1000)
605 context['download_url'] = self.object.get_pige_download_url()
608 context['can_manage'] = self.request.user.can_manage(self.object)
609 except AttributeError:
612 context['upload_file_form'] = PlaylistElementForm(initial={'episode': self.object})
616 class EpisodeRegieUpdateOrderView(View):
617 def get(self, request, *args, **kwargs):
618 new_order = request.GET.getlist('new-order[]')
619 for element in PlaylistElement.objects.filter(id__in=new_order):
620 element.order = new_order.index(str(element.id)) + 1
622 return HttpResponse('ok')
625 class EpisodeRegieMarks(EpisodeRegieView):
626 def get(self, request, *args, **kwargs):
627 episode = self.get_object()
628 start = self.request.GET['start']
629 end = self.request.GET['end']
631 episode.effective_start = datetime.datetime.fromtimestamp(float(start)/1000)
633 episode.effective_start = None
635 episode.effective_end = datetime.datetime.fromtimestamp(float(end)/1000)
637 episode.effective_end = None
641 obj['pige_download_url'] = episode.get_pige_download_url()
642 return JSONResponse(obj)