From 70e69f73cb4fbb12ec775926534a809c4a2bb746 Mon Sep 17 00:00:00 2001
From: Adam Kelly
Date: Wed, 3 Jun 2020 17:38:30 +0100
Subject: [PATCH] Add manage invites page.
---
fedireads/forms.py | 37 +++++++++++++++++++-
fedireads/migrations/0044_siteinvite_user.py | 21 +++++++++++
fedireads/models/site.py | 10 ++++--
fedireads/templates/layout.html | 3 +-
fedireads/templates/manage_invites.html | 32 +++++++++++++++++
fedireads/urls.py | 3 ++
fedireads/view_actions.py | 12 +++++++
fedireads/views.py | 8 +++++
8 files changed, 122 insertions(+), 4 deletions(-)
create mode 100644 fedireads/migrations/0044_siteinvite_user.py
create mode 100644 fedireads/templates/manage_invites.html
diff --git a/fedireads/forms.py b/fedireads/forms.py
index c7805c4c..4df32fc6 100644
--- a/fedireads/forms.py
+++ b/fedireads/forms.py
@@ -1,5 +1,8 @@
''' usin django model forms '''
-from django.forms import ModelForm, PasswordInput, HiddenInput
+import datetime
+
+from django.core.exceptions import ValidationError
+from django.forms import ModelForm, PasswordInput, widgets
from django import forms
from fedireads import models
@@ -115,3 +118,35 @@ class EditionForm(ModelForm):
class ImportForm(forms.Form):
csv_file = forms.FileField()
+
+class ExpiryWidget(widgets.Select):
+ def value_from_datadict(self, data, files, name):
+ selected_string = super().value_from_datadict(data, files, name)
+
+ if selected_string == 'day':
+ interval = datetime.timedelta(days=1)
+ elif selected_string == 'week':
+ interval = datetime.timedelta(days=7)
+ elif selected_string == 'month':
+ interval = datetime.timedelta(days=31) # Close enough?
+ elif selected_string == 'forever':
+ return None
+ else:
+ return selected_string # "This will raise
+
+ return datetime.datetime.now() + interval
+
+class CreateInviteForm(ModelForm):
+ class Meta:
+ model = models.SiteInvite
+ exclude = ['code', 'user', 'times_used']
+ widgets = {
+ 'expiry': ExpiryWidget(choices=[
+ ('day', 'One Day'),
+ ('week', 'One Week'),
+ ('month', 'One Month'),
+ ('forever', 'Does Not Expire')]),
+ 'use_limit': widgets.Select(
+ choices=[(i, "%d uses" % (i,)) for i in [1, 5, 10, 25, 50, 100]]
+ + [(None, 'Unlimited')])
+ }
diff --git a/fedireads/migrations/0044_siteinvite_user.py b/fedireads/migrations/0044_siteinvite_user.py
new file mode 100644
index 00000000..8a332129
--- /dev/null
+++ b/fedireads/migrations/0044_siteinvite_user.py
@@ -0,0 +1,21 @@
+# Generated by Django 3.0.3 on 2020-06-02 15:46
+
+from django.conf import settings
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('fedireads', '0043_siteinvite'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='siteinvite',
+ name='user',
+ field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL),
+ preserve_default=False,
+ ),
+ ]
diff --git a/fedireads/models/site.py b/fedireads/models/site.py
index 11639845..301202dc 100644
--- a/fedireads/models/site.py
+++ b/fedireads/models/site.py
@@ -1,10 +1,11 @@
import base64
-import datetime
from Crypto import Random
from django.db import models
+from django.utils import timezone
from fedireads.settings import DOMAIN
+from .user import User
class SiteSettings(models.Model):
name = models.CharField(default=DOMAIN, max_length=100)
@@ -31,8 +32,13 @@ class SiteInvite(models.Model):
expiry = models.DateTimeField(blank=True, null=True)
use_limit = models.IntegerField(blank=True, null=True)
times_used = models.IntegerField(default=0)
+ user = models.ForeignKey(User, on_delete=models.CASCADE)
def valid(self):
return (
- (self.expiry is None or self.expiry > datetime.datetime.now()) and
+ (self.expiry is None or self.expiry > timezone.now()) and
(self.use_limit is None or self.times_used < self.use_limit))
+
+ @property
+ def link(self):
+ return "https://{}/invite/{}".format(DOMAIN, self.code)
diff --git a/fedireads/templates/layout.html b/fedireads/templates/layout.html
index 40affbd7..027fa02d 100644
--- a/fedireads/templates/layout.html
+++ b/fedireads/templates/layout.html
@@ -62,7 +62,8 @@
diff --git a/fedireads/templates/manage_invites.html b/fedireads/templates/manage_invites.html
new file mode 100644
index 00000000..ee62ed6a
--- /dev/null
+++ b/fedireads/templates/manage_invites.html
@@ -0,0 +1,32 @@
+{% extends 'layout.html' %}
+{% load humanize %}
+{% block content %}
+
+
+
Invites
+
+
+ Link |
+ Expires |
+ Max uses |
+ Times used |
+
+ {% for invite in invites %}
+
+ {{ invite.link }} |
+ {{ invite.expiry|naturaltime }} |
+ {{ invite.use_limit }} |
+ {{ invite.times_used }} |
+
+ {% endfor %}
+
+
Generate New Invite
+
+
+
+
+{% endblock %}
diff --git a/fedireads/urls.py b/fedireads/urls.py
index 1723ae92..b8fb83e4 100644
--- a/fedireads/urls.py
+++ b/fedireads/urls.py
@@ -36,6 +36,7 @@ urlpatterns = [
re_path(r'^login/?$', views.login_page),
re_path(r'^about/?$', views.about_page),
re_path(r'^invite/(?P[A-Za-z0-9]+)/?$', views.invite_page),
+ re_path(r'^manage_invites/?$', views.manage_invites),
path('', views.home),
re_path(r'^(?Phome|local|federated)/?$', views.home_tab),
@@ -104,4 +105,6 @@ urlpatterns = [
re_path(r'^clear-notifications/?$', actions.clear_notifications),
+ re_path(r'^create_invite/?$', actions.create_invite),
+
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
diff --git a/fedireads/view_actions.py b/fedireads/view_actions.py
index 6b7c2ac2..db35269c 100644
--- a/fedireads/view_actions.py
+++ b/fedireads/view_actions.py
@@ -431,3 +431,15 @@ def import_data(request):
goodreads_import.start_import(job)
return redirect('/import_status/%d' % (job.id,))
return HttpResponseBadRequest()
+
+@login_required
+def create_invite(request):
+ form = forms.CreateInviteForm(request.POST)
+ if not form.is_valid():
+ return HttpResponseBadRequest("ERRORS : %s" % (form.errors,))
+
+ invite = form.save(commit=False)
+ invite.user = request.user
+ invite.save()
+
+ return redirect('/manage_invites')
diff --git a/fedireads/views.py b/fedireads/views.py
index af9452ef..32834842 100644
--- a/fedireads/views.py
+++ b/fedireads/views.py
@@ -238,6 +238,14 @@ def invite_page(request, code):
}
return TemplateResponse(request, 'invite.html', data)
+@login_required
+def manage_invites(request):
+ data = {
+ 'invites': models.SiteInvite.objects.filter(user=request.user),
+ 'form': forms.CreateInviteForm(),
+ }
+ return TemplateResponse(request, 'manage_invites.html', data)
+
@login_required
def notifications_page(request):