370 lines
12 KiB
Python

from django.shortcuts import render, get_object_or_404, redirect, resolve_url
from django.views.generic import TemplateView, View, RedirectView
from django.views.generic.detail import DetailView, SingleObjectMixin
from django.views.generic.list import ListView
from django.views.generic.edit import CreateView, UpdateView, FormView
from django.views.generic.base import ContextMixin
from django.http import HttpResponseRedirect
from django.core.exceptions import SuspiciousOperation
from django.contrib import auth
from markdown2 import markdown
from datetime import datetime
from urllib.parse import urlparse, urlencode
import os.path
from . import models, forms
from base64 import b64decode
import logging
logger = logging.getLogger(__name__)
class EnsembleMixin(object):
admin_required = False
def dispatch(self, request, *args, **kwargs):
request.ensemble_id = request.session.get('ensemble')
request.is_admin = request.user.is_superuser
if not request.ensemble_id:
return redirect('register')
if not request.is_admin and request.user.is_authenticated:
try:
request.user.ensembles.get(pk=request.ensemble_id)
request.is_admin = True
except models.Ensemble.DoesNotExist:
pass
if self.admin_required and not request.is_admin:
return redirect('login')
return super().dispatch(request, *args, **kwargs)
class ProjectMixin(EnsembleMixin):
def get_project(self):
if not hasattr(self, '_project'):
self._project = get_object_or_404(models.Project,
pk=self.kwargs['project'], ensemble=self.request.ensemble_id)
return self._project
def get_queryset(self):
return super().get_queryset().filter(project=self.get_project())
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['project'] = self.get_project()
return context
class S3UploadMixin(ProjectMixin):
accept_files = ''
def get_accept_files(self):
return self.accept_files
def get_cancel_url(self):
return self.cancel_url
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
success_url = self.request.build_absolute_uri(self.get_success_url())
key_template = self.object.key_template()
project = self.get_project()
context['upload'] = project.presigned_post(key_template,
fields={'success_action_redirect': success_url},
conditions=[["starts-with", "$success_action_redirect", ""]])
context['ajax_upload'] = project.presigned_post(key_template)
context['success_url'] = success_url
context['cancel_url'] = self.get_cancel_url()
context['accept_files'] = self.accept_files
return context
class S3CompleteView(SingleObjectMixin, RedirectView):
def complete(self, key):
self.object.key = key
self.object.save()
def get(self, request, *args, **kwargs):
self.object = self.get_object()
if 'key' in request.GET:
self.complete(request.GET['key'])
elif 'location' in request.GET:
uri = urlparse(request.GET['location'])
bucket, key = uri.path[1:].split('/', 1)
if bucket != models.BUCKET:
key = uri.path[1:]
self.complete(key)
else:
raise KeyError("No key or location found")
return super().get(request, *args, **kwargs)
def register(request):
if 'clear' in request.GET:
request.session.clear()
request.ensemble_id = request.session.get('ensemble')
registered = request.session.setdefault('registered', {})
code = request.GET.get('code', '').replace('-', '')
# check if already joined
if code in registered:
request.session['ensemble'] = registered[code]
return redirect('ensemble_detail')
if request.user.is_superuser and code:
request.session['ensemble'] = models.Ensemble.objects.get(code=code).pk
return redirect('ensemble_detail')
if request.method == "POST":
form = forms.CodeForm(request.POST)
if form.is_valid():
data = form.cleaned_data
try:
ensemble = models.Ensemble.objects.get(code=data['code'].replace('-', ''))
if ensemble.passphrase.lower() == data['passphrase'].lower():
request.session['ensemble'] = ensemble.pk
registered[ensemble.code] = ensemble.pk
return redirect('ensemble_detail')
except models.Ensemble.DoesNotExist:
form.add_error(None, "Incorrect code or passphrase")
else:
form = forms.CodeForm(initial=request.GET)
if request.user.is_superuser:
current = models.Ensemble.objects.all()
else:
current = models.Ensemble.objects.filter(pk__in=registered.values())
return render(request, 'interface/register.html', {'form': form, 'current': current})
def on_login(sender, **kwargs):
user = kwargs['user']
request = kwargs['request']
registered = request.session.get('registered', {})
for e in user.ensembles.all():
if not e.code in registered:
registered[e.code] = e.pk
request.session['registered'] = registered
auth.signals.user_logged_in.connect(on_login)
def logout(request):
ensemble = request.session.get('ensemble')
registered = request.session.get('registered', {})
auth.logout(request)
request.session['ensemble'] = ensemble
request.session['registered'] = registered
return redirect('/')
class EnsembleDetailView(EnsembleMixin, DetailView):
def dispatch(self, request, *args, **kwargs):
# capture provided urls
if 'code' in request.GET:
return redirect('/register?code={0}'.format(request.GET['code']))
return super().dispatch(request, *args, **kwargs)
def get_object(self):
return models.Ensemble.objects.get(pk=self.request.ensemble_id)
class ProjectDetailView(EnsembleMixin, DetailView):
def get_queryset(self):
return models.Project.objects.filter(ensemble=self.request.ensemble_id)
class WikiView(ProjectMixin, DetailView):
template_name = 'interface/wiki.html'
model = models.WikiPage
def get_context_data(self, **kwargs):
data = super().get_context_data(**kwargs)
data['wiki_html'] = markdown(self.object.markdown)
return data
class WikiCreateView(ProjectMixin, CreateView):
admin_required = True
model = models.WikiPage
fields = ['title', 'markdown']
class WikiEditView(ProjectMixin, UpdateView):
admin_required = True
model = models.WikiPage
fields = ['title', 'markdown']
class SubmissionCreateView(ProjectMixin, FormView):
#model = models.Submission
#fields = ['name', 'instrument', 'url', 'notes']
form_class = forms.SubmissionForm
template_name = "interface/submission_create.html"
def form_valid(self, form):
self.object = form.save(commit=False)
self.object.project = self.get_project()
self.object.save()
self.request.session['name'] = self.object.name
self.request.session['instrument'] = self.object.instrument
if form.cleaned_data['method'] == 'link':
return redirect('submission_link', project=self.object.project.pk, pk=self.object.pk)
return redirect('submission_upload', project=self.object.project.pk, pk=self.object.pk)
def get_initial(self):
return { k: self.request.session.get(k) for k in ('name', 'instrument') }
class SubmissionCompleteView(ProjectMixin, S3CompleteView):
model = models.Submission
def complete(self, key):
self.object.url = key
self.object.private = False
self.object.complete = True
self.object.save()
def get_redirect_url(self, **kwargs):
return resolve_url('submission_detail', **self.kwargs)
class SubmissionDetailView(ProjectMixin, DetailView):
model = models.Submission
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['can_download'] = self.request.is_admin
return context
class SubmissionPreview(ProjectMixin, DetailView):
model = models.Submission
template_name = 'interface/submission_preview.html'
admin_required = True
class SubmissionUploadView(S3UploadMixin, DetailView):
template_name = 'interface/s3_upload.html'
model = models.Submission
accept_files = "video/*"
def get_success_url(self):
return resolve_url('submission_complete', **self.kwargs)
def get_cancel_url(self):
return resolve_url('submission_cancel', **self.kwargs)
class SubmissionLinkView(ProjectMixin, UpdateView):
model = models.Submission
template_name = 'interface/submission_link.html'
fields = ['url']
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['cancel_url'] = self.get_cancel_url()
return context
def get_success_url(self):
return resolve_url('submission_detail', **self.kwargs)
def get_cancel_url(self):
return resolve_url('submission_cancel', **self.kwargs)
def form_valid(self, form):
self.object = form.save(commit=False)
self.object.complete = True
self.object.private = True
self.object.save()
return redirect(self.get_success_url())
class SubmissionCancelView(ProjectMixin, SingleObjectMixin, View):
model = models.Submission
def get(self, request, *args, **kwargs):
self.object = self.get_object()
self.object.delete()
return redirect('project_detail', pk=kwargs['project'])
class SubmissionListView(ProjectMixin, ListView):
model = models.Submission
admin_required = True
def get_queryset(self):
return super().get_queryset().filter(complete=True).order_by('-pk')
class ResourceCreateView(ProjectMixin, CreateView):
model = models.Resource
fields = ['name', 'media_type', 'description']
template_name = 'interface/project_form.html'
title = "Add a new resource"
admin_required = True
def form_valid(self, form):
self.object = form.save(commit=False)
self.object.project = self.get_project()
self.object.save()
return redirect('resource_upload', project=self.object.project_id, pk=self.object.pk)
class ResourceUploadView(S3UploadMixin, DetailView):
model = models.Resource
template_name = 'interface/s3_upload.html'
def get_accept_files(self):
return self.object.accept()
def get_success_url(self):
return resolve_url('resource_complete', **self.kwargs)
def get_cancel_url(self):
return resolve_url('resource_list', project=self.kwargs['project'])
class ResourceCompleteView(ProjectMixin, S3CompleteView):
model = models.Resource
def get_redirect_url(self, **kwargs):
return resolve_url('resource_list', project=self.kwargs['project'])
class ResourceListView(ProjectMixin, ListView):
model = models.Resource
def get_queryset(self):
qs = super().get_queryset()
if not self.request.is_admin:
qs = qs.filter(visible=True)
return qs
class ResourceEditView(ProjectMixin, UpdateView):
admin_required = True
model = models.Resource
fields = ['name', 'description', 'visible']
template_name = 'interface/default_form.html'
def get_success_url(self):
return resolve_url('resource_list', project=self.kwargs['project'])
class ManageView(EnsembleMixin, TemplateView):
template_name = 'interface/manage.html'
admin_required = True
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['ensemble'] = models.Ensemble.objects.get(pk=self.request.ensemble_id)
context['ensemble_url'] = self.request.build_absolute_uri('/?code={0}'.format(context['ensemble'].ensemble_code()))
return context