2022-11-28 14:11:39 +11:00

334 lines
11 KiB
Python

from django.shortcuts import render, get_object_or_404, redirect, resolve_url
from django.views.generic import TemplateView, RedirectView
from django.views.generic.detail import DetailView
from django.views.generic.list import ListView
from django.views.generic.edit import CreateView, UpdateView
from django.core.exceptions import SuspiciousOperation
from django.contrib import auth
from markdown2 import markdown
from . import models, forms
import logging
logger = logging.getLogger(__name__)
#from django.core.signing import Signer
#signer = Signer()
#def signed_url(name, **kwargs):
# url = resolve_url(name, **kwargs)
# sig = signer.sign(url)
# return sig.replace(":", "?auth=")
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 request.is_admin:
if request.ensemble_id is None:
return redirect('ensemble_list')
return super().dispatch(request, *args, **kwargs)
# if 'auth' in request.GET:
# sig = signer.sign(request.path)
# if sig[len(request.path)+1:] == request.GET['auth']:
# logger.info("Allowing auth key")
# request.is_admin = True
# return super().dispatch(request, *args, **kwargs)
# else:
# raise SuspiciousOperation("Bad auth code")
if request.user.is_authenticated:
try:
request.user.ensembles.get(pk=request.ensemble_id)
request.is_admin = True
except models.Ensemble.DoesNotExist:
pass
if not request.ensemble_id:
return redirect('register')
if self.admin_required and not request.is_admin:
return redirect('login')
return super().dispatch(request, *args, **kwargs)
@property
def ensemble(self):
return models.Ensemble.objects.get(pk=self.request.ensemble_id)
#def get_context_data(self, **kwargs):
# context = super().get_context_data(**kwargs)
# context['ensemble'] = self.ensemble
# return context
class ProjectMixin(EnsembleMixin):
def get_project(self):
if not hasattr(self, '_project'):
if self.request.is_admin: # can access any ensemble
self._project = get_object_or_404(models.Project, pk=self.kwargs['project'])
else:
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()
context['modules'] = context['project'].modules.values_list('name', flat=True)
return context
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('-', '')
logger.debug("Registering with code %s", code)
# check if already joined
if code in registered or request.user.is_superuser:
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.append(ensemble.code)
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)
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.append(e.code)
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 EnsembleForgetView(EnsembleMixin, RedirectView):
def get_redirect_url(self, *args, **kwargs):
registered = self.request.session.setdefault('registered', [])
ensemble = models.Ensemble.objects.get(pk=self.kwargs['pk'])
try:
registered.remove(ensemble.code)
self.request.session['registered'] = registered
except KeyError:
pass
if self.request.ensemble_id == ensemble.pk:
del(self.request.session['ensemble'])
return resolve_url('ensemble_detail')
class EnsembleListView(ListView):
def get_queryset(self):
registered = self.request.session.get('registered', [])
if self.request.user.is_superuser:
current = models.Ensemble.objects.all()
else:
current = models.Ensemble.objects.filter(code__in=registered)
return current
#def get_context_data(self, **kwargs):
# context = super().get_context_data(**kwargs)
# context['ensemble_id'] = self.request.session.get('ensemble')
# return context
class EnsembleProjectListView(EnsembleMixin, DetailView):
template_name = 'interface/ensemble_project_list.html'
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)
def get_context_data(self, **kwargs):
data = super().get_context_data(**kwargs)
if self.request.is_admin:
data['ensemble_url'] = self.request.build_absolute_uri('/?code={0}'.format(self.ensemble.ensemble_code()))
return data
class EnsembleDetailView(DetailView):
model = models.Ensemble
class ProjectDetailView(ProjectMixin, DetailView):
def get_object(self):
return self.get_project()
class ProjectCreateView(EnsembleMixin, CreateView):
model = models.Project
template_name = "interface/default_form.html"
title = "Add a new project"
form_class = forms.ProjectForm
def form_valid(self, form):
self.object = form.save(commit=False)
self.object.ensemble_id = self.request.ensemble_id
self.object.owner = self.request.user
self.object.save()
return redirect('project_detail', project=self.object.pk)
class ProjectUpdateView(EnsembleMixin, UpdateView):
model = models.Project
template_name = "interface/default_form.html"
#fields = ['name', 'description', 'event_date', 'enable_library', 'enable_submissions', 'active']
pk_url_kwarg = 'project'
form_class = forms.ProjectForm
def get_success_url(self):
return resolve_url('project_detail', project=self.kwargs['project'])
#class ProjectMakefileView(EnsembleMixin, DetailView):
# template_name = 'interface/project_submissions.mk'
# content_type = 'text/plain'
#
# def get_queryset(self):
# if self.request.is_admin:
# return models.Project.objects.all()
#
# return models.Project.objects.filter(ensemble=self.request.ensemble_id)
#
# def get_context_data(self, **kwargs):
# data = super().get_context_data(**kwargs)
#
# data['submissions'] = []
# data['targets'] = []
# for s in self.object.submissions:
# name = s.short_name
# data['targets'].append(name)
# data['submissions'].append({
# 'url': self.request.build_absolute_uri(signed_url('submission_download', project=self.kwargs['pk'], pk=s.pk)),
# 'name': name,
# })
#
# return data
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']
def form_valid(self, form):
self.object = form.save(commit=False)
self.object.project = self.get_project()
self.object.save()
return redirect('wiki', project=self.object.project_id, pk=self.object.pk)
class WikiEditView(ProjectMixin, UpdateView):
admin_required = True
model = models.WikiPage
fields = ['title', 'markdown']
class ResourceCreateView(ProjectMixin, CreateView):
model = models.Resource
form_class = forms.ResourceForm
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(ProjectMixin, UpdateView):
admin_required = True
model = models.Resource
fields = ['file']
template_name = 'interface/default_form.html'
def get_success_url(self):
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