Add manage invites page.

This commit is contained in:
Adam Kelly 2020-06-03 17:38:30 +01:00
parent 48f7fd34a7
commit 70e69f73cb
8 changed files with 122 additions and 4 deletions

View file

@ -1,5 +1,8 @@
''' usin django model forms ''' ''' 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 django import forms
from fedireads import models from fedireads import models
@ -115,3 +118,35 @@ class EditionForm(ModelForm):
class ImportForm(forms.Form): class ImportForm(forms.Form):
csv_file = forms.FileField() 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')])
}

View file

@ -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,
),
]

View file

@ -1,10 +1,11 @@
import base64 import base64
import datetime
from Crypto import Random from Crypto import Random
from django.db import models from django.db import models
from django.utils import timezone
from fedireads.settings import DOMAIN from fedireads.settings import DOMAIN
from .user import User
class SiteSettings(models.Model): class SiteSettings(models.Model):
name = models.CharField(default=DOMAIN, max_length=100) name = models.CharField(default=DOMAIN, max_length=100)
@ -31,8 +32,13 @@ class SiteInvite(models.Model):
expiry = models.DateTimeField(blank=True, null=True) expiry = models.DateTimeField(blank=True, null=True)
use_limit = models.IntegerField(blank=True, null=True) use_limit = models.IntegerField(blank=True, null=True)
times_used = models.IntegerField(default=0) times_used = models.IntegerField(default=0)
user = models.ForeignKey(User, on_delete=models.CASCADE)
def valid(self): def valid(self):
return ( 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)) (self.use_limit is None or self.times_used < self.use_limit))
@property
def link(self):
return "https://{}/invite/{}".format(DOMAIN, self.code)

View file

@ -62,7 +62,8 @@
<ul class="pulldown"> <ul class="pulldown">
<li><a href="/user/{{ request.user }}">Your profile</a></li> <li><a href="/user/{{ request.user }}">Your profile</a></li>
<li><a href="/user-edit/">Settings</a></li> <li><a href="/user-edit/">Settings</a></li>
<li><a href="/import">Import Books</a><li> <li><a href="/import">Import Books</a></li>
<li><a href="/manage_invites/">Invites</a></li>
<li><a href="/logout/">Log out</a></li> <li><a href="/logout/">Log out</a></li>
</ul> </ul>
</p> </p>

View file

@ -0,0 +1,32 @@
{% extends 'layout.html' %}
{% load humanize %}
{% block content %}
<div class="content-container">
<div class="manage-invites">
<h2>Invites</h2>
<table>
<tr>
<th>Link</th>
<th>Expires</th>
<th>Max uses</th>
<th>Times used</th>
</tr>
{% for invite in invites %}
<tr>
<td><a href="{{ invite.link }}">{{ invite.link }}</td>
<td>{{ invite.expiry|naturaltime }}</td>
<td>{{ invite.use_limit }}</td>
<td>{{ invite.times_used }}</td>
</tr>
{% endfor %}
</table>
<h2>Generate New Invite</h2>
<form name="avatar" action="/create_invite/" method="post">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Create Invite</button>
</form>
</div>
</div>
{% endblock %}

View file

@ -36,6 +36,7 @@ urlpatterns = [
re_path(r'^login/?$', views.login_page), re_path(r'^login/?$', views.login_page),
re_path(r'^about/?$', views.about_page), re_path(r'^about/?$', views.about_page),
re_path(r'^invite/(?P<code>[A-Za-z0-9]+)/?$', views.invite_page), re_path(r'^invite/(?P<code>[A-Za-z0-9]+)/?$', views.invite_page),
re_path(r'^manage_invites/?$', views.manage_invites),
path('', views.home), path('', views.home),
re_path(r'^(?P<tab>home|local|federated)/?$', views.home_tab), re_path(r'^(?P<tab>home|local|federated)/?$', views.home_tab),
@ -104,4 +105,6 @@ urlpatterns = [
re_path(r'^clear-notifications/?$', actions.clear_notifications), 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) ] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

View file

@ -431,3 +431,15 @@ def import_data(request):
goodreads_import.start_import(job) goodreads_import.start_import(job)
return redirect('/import_status/%d' % (job.id,)) return redirect('/import_status/%d' % (job.id,))
return HttpResponseBadRequest() 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')

View file

@ -238,6 +238,14 @@ def invite_page(request, code):
} }
return TemplateResponse(request, 'invite.html', data) 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 @login_required
def notifications_page(request): def notifications_page(request):