From 4d07cb1eb56d011ab4040621f27bb6fac7e954e3 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Sat, 20 Mar 2021 18:23:59 -0700 Subject: [PATCH 01/11] Adds invite request model --- bookwyrm/forms.py | 6 ++ .../migrations/0056_auto_20210321_0123.py | 58 +++++++++++++++++++ bookwyrm/models/__init__.py | 2 +- bookwyrm/models/site.py | 20 ++++++- 4 files changed, 84 insertions(+), 2 deletions(-) create mode 100644 bookwyrm/migrations/0056_auto_20210321_0123.py diff --git a/bookwyrm/forms.py b/bookwyrm/forms.py index d723ebdbf..0a69dbe09 100644 --- a/bookwyrm/forms.py +++ b/bookwyrm/forms.py @@ -202,6 +202,12 @@ class ExpiryWidget(widgets.Select): return timezone.now() + interval +class InviteRequestForm(CustomForm): + class Meta: + model = models.InviteRequest + fields = ["email"] + + class CreateInviteForm(CustomForm): class Meta: model = models.SiteInvite diff --git a/bookwyrm/migrations/0056_auto_20210321_0123.py b/bookwyrm/migrations/0056_auto_20210321_0123.py new file mode 100644 index 000000000..f36e4b12d --- /dev/null +++ b/bookwyrm/migrations/0056_auto_20210321_0123.py @@ -0,0 +1,58 @@ +# Generated by Django 3.1.6 on 2021-03-21 01:23 + +import bookwyrm.models.fields +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ("bookwyrm", "0055_auto_20210321_0101"), + ] + + operations = [ + migrations.AddField( + model_name="sitesettings", + name="allow_invite_requests", + field=models.BooleanField(default=True), + ), + migrations.CreateModel( + name="InviteRequest", + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("created_date", models.DateTimeField(auto_now_add=True)), + ("updated_date", models.DateTimeField(auto_now=True)), + ( + "remote_id", + bookwyrm.models.fields.RemoteIdField( + max_length=255, + null=True, + validators=[bookwyrm.models.fields.validate_remote_id], + ), + ), + ("email", models.EmailField(max_length=255, unique=True)), + ("invite_sent", models.BooleanField(default=False)), + ( + "invite", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to="bookwyrm.siteinvite", + ), + ), + ], + options={ + "abstract": False, + }, + ), + ] diff --git a/bookwyrm/models/__init__.py b/bookwyrm/models/__init__.py index 326a673e1..35e32c2cf 100644 --- a/bookwyrm/models/__init__.py +++ b/bookwyrm/models/__init__.py @@ -26,7 +26,7 @@ from .federated_server import FederatedServer from .import_job import ImportJob, ImportItem -from .site import SiteSettings, SiteInvite, PasswordReset +from .site import SiteSettings, SiteInvite, PasswordReset, InviteRequest cls_members = inspect.getmembers(sys.modules[__name__], inspect.isclass) activity_models = { diff --git a/bookwyrm/models/site.py b/bookwyrm/models/site.py index 7fde6781e..2722de174 100644 --- a/bookwyrm/models/site.py +++ b/bookwyrm/models/site.py @@ -3,10 +3,11 @@ import base64 import datetime from Crypto import Random -from django.db import models +from django.db import models, IntegrityError from django.utils import timezone from bookwyrm.settings import DOMAIN +from .base_model import BookWyrmModel from .user import User @@ -24,6 +25,7 @@ class SiteSettings(models.Model): code_of_conduct = models.TextField(default="Add a code of conduct here.") privacy_policy = models.TextField(default="Add a privacy policy here.") allow_registration = models.BooleanField(default=True) + allow_invite_requests = models.BooleanField(default=True) logo = models.ImageField(upload_to="logos/", null=True, blank=True) logo_small = models.ImageField(upload_to="logos/", null=True, blank=True) favicon = models.ImageField(upload_to="logos/", null=True, blank=True) @@ -69,6 +71,22 @@ class SiteInvite(models.Model): return "https://{}/invite/{}".format(DOMAIN, self.code) +class InviteRequest(BookWyrmModel): + """ prospective users can request an invite """ + + email = models.EmailField(max_length=255, unique=True) + invite = models.ForeignKey( + SiteInvite, on_delete=models.SET_NULL, null=True, blank=True + ) + invite_sent = models.BooleanField(default=False) + + def save(self, *args, **kwargs): + """ don't create a request for a registered email """ + if User.objects.filter(email=self.email).exists(): + raise IntegrityError() + super().save(*args, **kwargs) + + def get_passowrd_reset_expiry(): """ give people a limited time to use the link """ now = timezone.now() From d15396eb26c1352bc713bc603ad1057b24be6eee Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Sat, 20 Mar 2021 19:14:41 -0700 Subject: [PATCH 02/11] Request invite flow --- bookwyrm/forms.py | 8 +++++++ .../templates/discover/landing_layout.html | 24 +++++++++++++++++++ bookwyrm/templates/settings/site.html | 4 ++++ bookwyrm/urls.py | 3 +++ bookwyrm/views/__init__.py | 2 +- bookwyrm/views/helpers.py | 19 ++++++++++++++- bookwyrm/views/invite.py | 20 ++++++++++++++++ bookwyrm/views/landing.py | 17 +++---------- 8 files changed, 81 insertions(+), 16 deletions(-) diff --git a/bookwyrm/forms.py b/bookwyrm/forms.py index 0a69dbe09..d330211c1 100644 --- a/bookwyrm/forms.py +++ b/bookwyrm/forms.py @@ -3,6 +3,7 @@ import datetime from collections import defaultdict from django import forms +from django.core.exceptions import ValidationError from django.forms import ModelForm, PasswordInput, widgets from django.forms.widgets import Textarea from django.utils import timezone @@ -203,6 +204,13 @@ class ExpiryWidget(widgets.Select): class InviteRequestForm(CustomForm): + def clean(self): + """ make sure the email isn't in use by a registered user """ + cleaned_data = super().clean() + email = cleaned_data.get("email") + if email and models.User.objects.filter(email=email).exists(): + self.add_error("email", _("A user with this email already exists.")) + class Meta: model = models.InviteRequest fields = ["email"] diff --git a/bookwyrm/templates/discover/landing_layout.html b/bookwyrm/templates/discover/landing_layout.html index 5cfa1fd39..8e507531e 100644 --- a/bookwyrm/templates/discover/landing_layout.html +++ b/bookwyrm/templates/discover/landing_layout.html @@ -45,9 +45,33 @@
{% include 'snippets/register_form.html' %}
+ {% else %} +

{% trans "This instance is closed" %}

{{ site.registration_closed_text | safe}}

+ + {% if site.allow_invite_requests %} + {% if request_received %} +

+ {% trans "Thank you! Your request has been received." %} +

+ {% else %} +

{% trans "Request an Invitation" %}

+
+ {% csrf_token %} +
+ + + {% for error in request_form.email.errors %} +

{{ error | escape }}

+ {% endfor %} +
+ +
+ {% endif %} + {% endif %} + {% endif %} {% else %} diff --git a/bookwyrm/templates/settings/site.html b/bookwyrm/templates/settings/site.html index 27be0c9b2..28009e1a8 100644 --- a/bookwyrm/templates/settings/site.html +++ b/bookwyrm/templates/settings/site.html @@ -79,6 +79,10 @@