diff --git a/interface/migrations/0017_auto_20200914_0943.py b/interface/migrations/0017_auto_20200914_0943.py new file mode 100644 index 0000000..bb2e88a --- /dev/null +++ b/interface/migrations/0017_auto_20200914_0943.py @@ -0,0 +1,25 @@ +# Generated by Django 3.1.1 on 2020-09-13 23:43 + +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('interface', '0016_auto_20200910_2025'), + ] + + operations = [ + migrations.AddField( + model_name='ensemble', + name='admins', + field=models.ManyToManyField(to=settings.AUTH_USER_MODEL), + ), + migrations.AlterField( + model_name='submission', + name='instrument', + field=models.CharField(max_length=100, verbose_name='Instrument / Voice'), + ), + ] diff --git a/interface/migrations/0018_auto_20200914_1009.py b/interface/migrations/0018_auto_20200914_1009.py new file mode 100644 index 0000000..cf7a09e --- /dev/null +++ b/interface/migrations/0018_auto_20200914_1009.py @@ -0,0 +1,25 @@ +# Generated by Django 3.1.1 on 2020-09-14 00:09 + +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('interface', '0017_auto_20200914_0943'), + ] + + operations = [ + migrations.AddField( + model_name='resource', + name='visible', + field=models.BooleanField(default=True), + ), + migrations.AlterField( + model_name='ensemble', + name='admins', + field=models.ManyToManyField(related_name='ensembles', to=settings.AUTH_USER_MODEL), + ), + ] diff --git a/interface/models.py b/interface/models.py index cf67033..e2cba05 100644 --- a/interface/models.py +++ b/interface/models.py @@ -28,6 +28,7 @@ class Ensemble(models.Model): name = models.CharField(max_length=100) code = models.CharField(max_length=9, default=generate_code) passphrase = models.CharField(max_length=100) + admins = models.ManyToManyField('auth.User', related_name='ensembles') def active_projects(self): return self.projects.filter(active=True) @@ -62,6 +63,7 @@ class Resource(models.Model): description = models.TextField(blank=True) key = models.CharField(max_length=255, blank=True) media_type = models.CharField(max_length=10, choices=MEDIA_TYPES, default='*') + visible = models.BooleanField(default=True) def key_template(self): return "{}/${{filename}}".format(slugify(self.name)) diff --git a/interface/templates/base.html b/interface/templates/base.html index 1fe19ea..bff9eb0 100644 --- a/interface/templates/base.html +++ b/interface/templates/base.html @@ -29,12 +29,9 @@ Ensembles {% endif %} + {% if request.is_admin %} - {% if request.user.is_authenticated %} - {% endif %} diff --git a/interface/templates/interface/manage.html b/interface/templates/interface/manage.html new file mode 100644 index 0000000..011a4ea --- /dev/null +++ b/interface/templates/interface/manage.html @@ -0,0 +1,20 @@ +{% extends "base.html" %} + +{% block content %} +
+

Manage {{ ensemble.name }}

+

+ Joining code for participants:
+ {{ ensemble_url }} +

+

+ Sorry, not much you can do here yet. +

+ +
+ Logged in as {{ request.user }} [Logout] +

+
+{% endblock %} \ No newline at end of file diff --git a/interface/templates/interface/project_detail.html b/interface/templates/interface/project_detail.html index 64011bb..f2373db 100644 --- a/interface/templates/interface/project_detail.html +++ b/interface/templates/interface/project_detail.html @@ -11,7 +11,7 @@ {{ submission.date|timesince }} ago {{ submission.name }} ({{ submission.instrument }}) - {% if request.user.is_authenticated %} + {% if request.is_admin %}   diff --git a/interface/templates/interface/register.html b/interface/templates/interface/register.html index d4dcade..a61fdc3 100644 --- a/interface/templates/interface/register.html +++ b/interface/templates/interface/register.html @@ -1,6 +1,9 @@ {% extends "base.html" %} {% block content %} +{% if not request.user.is_authenticated %} + +{% endif %}
{% if current %}
diff --git a/interface/templates/interface/resource_list.html b/interface/templates/interface/resource_list.html index 800d023..508c3ee 100644 --- a/interface/templates/interface/resource_list.html +++ b/interface/templates/interface/resource_list.html @@ -14,7 +14,7 @@ Download {% endif %} - {% if request.user.is_authenticated %} + {% if request.is_admin %} Upload @@ -28,7 +28,7 @@ {% endwith %} {% endfor %}
- {% if request.user.is_authenticated %} + {% if request.is_admin %}
Add new
diff --git a/interface/templates/registration/login.html b/interface/templates/registration/login.html new file mode 100644 index 0000000..a30ffa0 --- /dev/null +++ b/interface/templates/registration/login.html @@ -0,0 +1,11 @@ +{% extends "base.html" %} + +{% block content %} +
+ {% csrf_token %} + {{ form }} +
+ +
+
+{% endblock %} \ No newline at end of file diff --git a/interface/urls.py b/interface/urls.py index b47621a..7203ffe 100644 --- a/interface/urls.py +++ b/interface/urls.py @@ -1,10 +1,16 @@ from django.urls import path +from django.contrib.auth import views as auth_views from . import views urlpatterns = [ - path('', views.EnsembleDetailView.as_view(), name='ensemble_detail'), + + path('login', auth_views.LoginView.as_view(), name='login'), + path('logout', views.logout, name='logout'), path('register', views.register, name="register"), + path('manage', views.ManageView.as_view(), name="manage"), + + path('', views.EnsembleDetailView.as_view(), name='ensemble_detail'), path('projects/', views.ProjectDetailView.as_view(), name="project_detail"), path('projects//page/', views.WikiView.as_view(), name="wiki"), diff --git a/interface/views.py b/interface/views.py index d3b1de0..2fc6237 100644 --- a/interface/views.py +++ b/interface/views.py @@ -6,6 +6,7 @@ from django.views.generic.edit import CreateView 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 @@ -19,14 +20,26 @@ 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): @@ -134,6 +147,24 @@ def register(request): return render(request, 'interface/register.html', {'form': form, 'current': current}) +def on_login(sender, **kwargs): + request = kwargs['request'] + registered = request.session.get('registered', {}) + for e in kwargs['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): @@ -188,7 +219,7 @@ class SubmissionDetailView(ProjectMixin, S3CompleteMixin, DetailView): def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) - if self.request.user.is_authenticated: + if self.request.is_admin: context['download'] = self.object.presigned_url() return context @@ -219,7 +250,7 @@ class ResourceCreateView(ProjectMixin, CreateView): def form_valid(self, form): - if not self.request.user.is_authenticated: + if not self.request.is_admin: raise SuspiciousOperation("Must be logged in to create resources") self.object = form.save(commit=False) @@ -250,3 +281,16 @@ class ResourceCompleteView(S3CompleteMixin, SingleObjectMixin, RedirectView): class ResourceListView(ProjectMixin, ListView): model = models.Resource + + def get_queryset(self): + return super().get_queryset().filter(visible=True) + +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 \ No newline at end of file diff --git a/polyphonic/settings_default.py b/polyphonic/settings_default.py index e96084e..b2b4f8d 100644 --- a/polyphonic/settings_default.py +++ b/polyphonic/settings_default.py @@ -99,7 +99,7 @@ AUTH_PASSWORD_VALIDATORS = [ 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', }, ] - +LOGIN_REDIRECT_URL = "/" # Internationalization # https://docs.djangoproject.com/en/3.1/topics/i18n/