diff --git a/marietje/marietje/context_processors.py b/marietje/marietje/context_processors.py new file mode 100644 index 0000000..170dd78 --- /dev/null +++ b/marietje/marietje/context_processors.py @@ -0,0 +1,6 @@ +from django.conf import settings + + +def global_settings(request): + return {'issues_url': settings.ISSUES_URL, 'contact_email': settings.CONTACT_EMAIL, + 'merge_requests_url': settings.MERGE_REQUESTS_URL} diff --git a/marietje/marietje/settings.py b/marietje/marietje/settings.py index 577fa9e..402a1dd 100644 --- a/marietje/marietje/settings.py +++ b/marietje/marietje/settings.py @@ -66,6 +66,7 @@ TEMPLATES = [ 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', + 'marietje.context_processors.global_settings', ], }, }, @@ -79,8 +80,12 @@ WSGI_APPLICATION = 'marietje.wsgi.application' DATABASES = { 'default': { - 'ENGINE': 'django.db.backends.sqlite3', - 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), + 'ENGINE': 'django.db.backends.mysql', + 'NAME': 'marietje', + 'USER': 'marietje', + 'PASSWORD': 'UYmINKUDXIfY1qudVhbr5UhJ61kVwZPA', + 'HOST': 'localhost', + 'PORT': '3306', } } @@ -150,3 +155,11 @@ MAX_MINUTES_IN_A_ROW = 20 # Time range (dependent on timezone specified) when MAX_MINUTES_IN_A_ROW is in effect. LIMIT_ALWAYS = False LIMIT_HOURS = (12, 16) + +CONTACT_EMAIL = 'marietje@science.ru.nl' + +STATS_TOP_COUNT = 50 +STATS_REQUEST_IGNORE_USER_IDS = (51, 515) + +ISSUES_URL = 'https://gitlab.science.ru.nl/Marietje/MarietjeDjango/issues' +MERGE_REQUESTS_URL = 'https://gitlab.science.ru.nl/Marietje/MarietjeDjango/merge_requests' diff --git a/marietje/marietje/static/css/custom.css b/marietje/marietje/static/css/custom.css index 0009f9b..f97451c 100644 --- a/marietje/marietje/static/css/custom.css +++ b/marietje/marietje/static/css/custom.css @@ -17,3 +17,7 @@ #queue-time-header { cursor: pointer; } + +footer { + text-align: center; +} diff --git a/marietje/marietje/static/js/queue.js b/marietje/marietje/static/js/queue.js index 88db7ce..3fcc9e9 100644 --- a/marietje/marietje/static/js/queue.js +++ b/marietje/marietje/static/js/queue.js @@ -48,39 +48,39 @@ $(function () { $('#cancel-request').click(function () { hideRequestTable(); }); - + $('.pagenum').change(function(){ getSongs(); }); - + $('#search-all, #search-uploader').change(function(){ $('.pagenum').val(1); getSongs(); }); $('.pagesize').change(function(){ - Cookies.set('pagesize', $(this).val()); + Cookies.set('pagesize', $(this).val(), { expires: 365 }); $('.pagenum').val(1); getSongs(); }); - + $('button.prev').click(function(){ var pageNumSelect = $('.pagenum'); pageNumSelect.val(Math.max(parseInt(pageNumSelect.val()) - 1, 1)); getSongs(); }); - + $('button.next').click(function(){ var pageNumSelect = $('.pagenum'); pageNumSelect.val(Math.min(parseInt(pageNumSelect.val()) + 1, pageNumSelect.children('option:last-child').val())); getSongs(); }); - + $('button.first').click(function(){ $('.pagenum').val(1); getSongs(); }); - + $('button.last').click(function(){ var pageNumSelect = $('.pagenum'); pageNumSelect.val(pageNumSelect.children('option:last-child').val()); @@ -126,7 +126,7 @@ $(function () { $('#queue-time-header').click(function(){ showTimeToPlay = !showTimeToPlay; $('#queue-time-header').text(showTimeToPlay ? 'Plays In' : 'Plays At'); - Cookies.set('showtimetoplay', showTimeToPlay ? '1' : '0'); + Cookies.set('showtimetoplay', showTimeToPlay ? '1' : '0', { expires: 365 }); }); getSongs(); }); @@ -279,6 +279,14 @@ function getSongs() } pageNumSelect.val(result.current_page); $('.pagesize').val(result.per_page); + $('button.first').prop('disabled', result.current_page == 1); + $('button.prev').prop('disabled', result.current_page == 1); + $('button.next').prop('disabled', result.current_page == result.last_page); + $('button.last').prop('disabled', result.current_page == result.last_page); + refreshingSongs = false; + if(requestViewOpen) { + window.scrollTo(0, 0); + } }); } diff --git a/marietje/marietje/templates/base.html b/marietje/marietje/templates/base.html index 23bab7e..6426d87 100644 --- a/marietje/marietje/templates/base.html +++ b/marietje/marietje/templates/base.html @@ -7,7 +7,7 @@ Marietje 4.0 - {% block title %}{% endblock title %} - + @@ -59,7 +59,20 @@ {% endfor %} {% endfor %} {% endif %} + {% if messages %} + {% for message in messages %} +
+ + {{ message }} +
+ {% endfor %} + {% endif %} {% block content %}{% endblock content %} + diff --git a/marietje/marietje/views.py b/marietje/marietje/views.py index d7c5cef..235ff99 100644 --- a/marietje/marietje/views.py +++ b/marietje/marietje/views.py @@ -1,11 +1,15 @@ -import random, string -from django.shortcuts import render, redirect, get_object_or_404 +import random +import string + +from django.conf import settings +from django.contrib import messages from django.contrib.auth import get_user_model from django.core.mail import send_mail -from django.conf import settings +from django.shortcuts import render, redirect, get_object_or_404 from django.urls import reverse -from .forms import RegistrationForm, ResetPasswordForm + from marietje.utils import get_first_queue +from .forms import RegistrationForm, ResetPasswordForm def register(request): @@ -31,8 +35,9 @@ def register(request): 'Please confirm your account by following this link: ' + activation_link, settings.MAIL_FROM, [user.email], - fail_silently=True - ) + fail_silently=True) + messages.add_message(request, messages.INFO, 'Please check your email, ' + user.email + ', for activation.', + extra_tags='info') return redirect('login') @@ -42,6 +47,10 @@ def activate(request, user_id, token): if token == user.activation_token: user.activation_token = None user.save() + messages.add_message(request, messages.SUCCESS, 'Successfully activated.', extra_tags='success') + else: + messages.add_message(request, messages.ERROR, 'Activation failed. If the problem persists, please contact ' + + settings.CONTACT_EMAIL + '.', extra_tags='danger') return redirect('login') @@ -52,6 +61,7 @@ def forgotpassword(request): User = get_user_model() user = User.objects.filter(username=request.POST.get('email')).first() if user is None or user.activation_token: + messages.add_message(request, messages.ERROR, 'No (active) user found.', extra_tags='danger') return render(request, 'registration/forgotpassword.html') user.reset_token = ''.join(random.choice(string.ascii_letters + string.digits) for _ in range(32)) @@ -63,8 +73,9 @@ def forgotpassword(request): + reset_link + '\nIf you did not request to reset your password, you can ignore this email.', settings.MAIL_FROM, [user.email], - fail_silently=True - ) + fail_silently=True) + messages.add_message(request, messages.INFO, 'Please check your email, ' + user.email + + ', for resetting your password.', extra_tags='info') return redirect('login') @@ -72,6 +83,7 @@ def resetpassword(request, user_id, token): User = get_user_model() user = get_object_or_404(User, pk=user_id) if not user.reset_token or token != user.reset_token: + messages.add_message(request, messages.ERROR, 'Invalid password reset link.', extra_tags='danger') return redirect('login') if not request.POST: return render(request, 'registration/resetpassword.html', {'user_id': user.id, 'reset_token': token}) @@ -79,9 +91,11 @@ def resetpassword(request, user_id, token): form = ResetPasswordForm(request.POST) if not form.is_valid(): - return render(request, 'registration/resetpassword.html', {'user_id': user.id, 'reset_token': token, 'form': form}) + return render(request, 'registration/resetpassword.html', {'user_id': user.id, 'reset_token': token, + 'form': form}) user.reset_token = None user.set_password(form.cleaned_data['password1']) user.save() + messages.add_message(request, messages.SUCCESS, 'Your password has been reset.', extra_tags='success') return redirect('login') diff --git a/marietje/playerapi/views.py b/marietje/playerapi/views.py index f3f3786..d81f105 100644 --- a/marietje/playerapi/views.py +++ b/marietje/playerapi/views.py @@ -34,6 +34,10 @@ def play(request): queue = get_object_or_404(Queue, id=request.POST.get('queue')) queue.started_at = timezone.now() queue.save() + current_song = queue.current_song() + current_song.played_at = queue.started_at + current_song.save() + return JsonResponse({}) @@ -46,14 +50,6 @@ def next(request): player_song.save() return JsonResponse({}) -@csrf_exempt -@token_required -def next(request): - queue = get_object_or_404(Queue, id=request.POST.get('queue')) - player_song = queue.current_song() - player_song.state = 2 - player_song.save() - return JsonResponse({}) @csrf_exempt @token_required diff --git a/marietje/queues/migrations/0004_remove_playlistsong_order.py b/marietje/queues/migrations/0004_remove_playlistsong_order.py new file mode 100644 index 0000000..2033cbd --- /dev/null +++ b/marietje/queues/migrations/0004_remove_playlistsong_order.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.5 on 2017-09-23 22:59 +from __future__ import unicode_literals + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('queues', '0003_queuecommand'), + ] + + operations = [ + migrations.RemoveField( + model_name='playlistsong', + name='order', + ), + ] diff --git a/marietje/queues/migrations/0005_playlistsong_played_at.py b/marietje/queues/migrations/0005_playlistsong_played_at.py new file mode 100644 index 0000000..e51bf7d --- /dev/null +++ b/marietje/queues/migrations/0005_playlistsong_played_at.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.5 on 2017-09-26 13:56 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('queues', '0004_remove_playlistsong_order'), + ] + + operations = [ + migrations.AddField( + model_name='playlistsong', + name='played_at', + field=models.DateTimeField(blank=True, null=True), + ), + ] diff --git a/marietje/queues/models.py b/marietje/queues/models.py index 2d3c18a..a67ca89 100644 --- a/marietje/queues/models.py +++ b/marietje/queues/models.py @@ -29,6 +29,7 @@ class PlaylistSong(models.Model): blank=True, null=True ) + played_at = models.DateTimeField(blank=True, null=True) # 0: Queued. # 1: Playing. @@ -36,22 +37,20 @@ class PlaylistSong(models.Model): # 3: Cancelled. state = models.IntegerField(default=0) - order = models.IntegerField() - def move_up(self): - other_song = PlaylistSong.objects.filter(playlist=self.playlist, order__lt=self.order)\ - .order_by('-order').first() + other_song = PlaylistSong.objects.filter(playlist=self.playlist, id__lt=self.id)\ + .order_by('-id').first() self.switch_order(other_song) def move_down(self): - other_song = PlaylistSong.objects.filter(playlist=self.playlist, order__gt=self.order).order_by('order').first() + other_song = PlaylistSong.objects.filter(playlist=self.playlist, id__gt=self.id).order_by('id').first() self.switch_order(other_song) def switch_order(self, other_song): - old_order = self.order - self.order = other_song.order + old_id = self.id + self.id = other_song.id + other_song.id = old_id self.save() - other_song.order = old_order other_song.save() def __str__(self): @@ -91,7 +90,7 @@ class Queue(models.Model): self.songs = PlaylistSong.objects\ .filter(Q(playlist=self.playlist_id) | Q(playlist_id=self.random_playlist_id), Q(state=0) | Q(state=1))\ - .order_by('-state', 'playlist_id', 'order')\ + .order_by('-state', 'playlist_id', 'id')\ .select_related('song', 'user') return self.songs @@ -112,12 +111,10 @@ class Queue(models.Model): return songs[1:] def request(self, song, user): - playlist_songs = PlaylistSong.objects.filter(playlist=self.playlist, state=0).order_by('order') + playlist_songs = PlaylistSong.objects.filter(playlist=self.playlist, state=0).order_by('id') - order = 0 seconds_in_a_row = 0 for playlist_song in playlist_songs: - order = playlist_song.order if playlist_song.user != user: seconds_in_a_row = 0 else: @@ -129,27 +126,17 @@ class Queue(models.Model): and not user.is_superuser and settings.LIMIT_HOURS[0] <= now.hour < settings.LIMIT_HOURS[1]: return False - if order is None: - order = 0 - order += 1 - playlist_song = PlaylistSong(playlist=self.playlist, song=song, user=user, order=order) + playlist_song = PlaylistSong(playlist=self.playlist, song=song, user=user) playlist_song.save() return True def fill_random_queue(self): song_count = PlaylistSong.objects.filter(playlist_id=self.random_playlist_id, state=0).count() while song_count < 5: - order = PlaylistSong.objects.filter(playlist_id=self.random_playlist_id)\ - .aggregate(Max('order'))['order__max'] - if order is None: - order = 0 - order += 1 song = Song.objects.filter(deleted=False).order_by('?').first() if song is None: return - playlist_song = PlaylistSong(playlist=self.random_playlist, - song=song, - user=None, order=order) + playlist_song = PlaylistSong(playlist=self.random_playlist, song=song, user=None) playlist_song.save() song_count += 1 diff --git a/marietje/queues/templates/queues/queue.html b/marietje/queues/templates/queues/queue.html index f9ba994..238cea7 100644 --- a/marietje/queues/templates/queues/queue.html +++ b/marietje/queues/templates/queues/queue.html @@ -88,7 +88,7 @@ - +