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.core.signing import Signer from django.contrib import auth from markdown2 import markdown from . import models, forms import logging logger = logging.getLogger(__name__) 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('-', '') print("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