Request invite flow

This commit is contained in:
Mouse Reeve 2021-03-20 19:14:41 -07:00
parent 4d07cb1eb5
commit d15396eb26
8 changed files with 81 additions and 16 deletions

View file

@ -3,6 +3,7 @@ import datetime
from collections import defaultdict from collections import defaultdict
from django import forms from django import forms
from django.core.exceptions import ValidationError
from django.forms import ModelForm, PasswordInput, widgets from django.forms import ModelForm, PasswordInput, widgets
from django.forms.widgets import Textarea from django.forms.widgets import Textarea
from django.utils import timezone from django.utils import timezone
@ -203,6 +204,13 @@ class ExpiryWidget(widgets.Select):
class InviteRequestForm(CustomForm): 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: class Meta:
model = models.InviteRequest model = models.InviteRequest
fields = ["email"] fields = ["email"]

View file

@ -45,9 +45,33 @@
<form name="register" method="post" action="/register"> <form name="register" method="post" action="/register">
{% include 'snippets/register_form.html' %} {% include 'snippets/register_form.html' %}
</form> </form>
{% else %} {% else %}
<h2 class="title">{% trans "This instance is closed" %}</h2> <h2 class="title">{% trans "This instance is closed" %}</h2>
<p>{{ site.registration_closed_text | safe}}</p> <p>{{ site.registration_closed_text | safe}}</p>
{% if site.allow_invite_requests %}
{% if request_received %}
<p>
{% trans "Thank you! Your request has been received." %}
</p>
{% else %}
<h3>{% trans "Request an Invitation" %}</h3>
<form name="invite-request" action="{% url 'invite-request' %}" method="post">
{% csrf_token %}
<div class="block">
<label for="id_request_email" class="label">{% trans "Email address:" %}</label>
<input type="email" name="email" maxlength="255" class="input" required="" id="id_request_email">
{% for error in request_form.email.errors %}
<p class="help is-danger">{{ error | escape }}</p>
{% endfor %}
</div>
<button type="submit" class="button is-link">{% trans "Submit" %}</button>
</form>
{% endif %}
{% endif %}
{% endif %} {% endif %}
</div> </div>
{% else %} {% else %}

View file

@ -79,6 +79,10 @@
<label class="label" for="id_allow_registration">{% trans "Allow registration:" %} <label class="label" for="id_allow_registration">{% trans "Allow registration:" %}
{{ site_form.allow_registration }} {{ site_form.allow_registration }}
</div> </div>
<div class="control">
<label class="label" for="id_allow_invite_requests">{% trans "Allow invite requests:" %}
{{ site_form.allow_invite_requests }}
</div>
<div class="control"> <div class="control">
<label class="label" for="id_registration_closed_text">{% trans "Registration closed text:" %}</label> <label class="label" for="id_registration_closed_text">{% trans "Registration closed text:" %}</label>
{{ site_form.registration_closed_text }} {{ site_form.registration_closed_text }}

View file

@ -54,6 +54,9 @@ urlpatterns = [
re_path( re_path(
r"^settings/invites/?$", views.ManageInvites.as_view(), name="settings-invites" r"^settings/invites/?$", views.ManageInvites.as_view(), name="settings-invites"
), ),
re_path(
r"^invite-request/?$", views.InviteRequest.as_view(), name="invite-request"
),
re_path(r"^invite/(?P<code>[A-Za-z0-9]+)/?$", views.Invite.as_view()), re_path(r"^invite/(?P<code>[A-Za-z0-9]+)/?$", views.Invite.as_view()),
# moderation # moderation
re_path(r"^settings/reports/?$", views.Reports.as_view(), name="settings-reports"), re_path(r"^settings/reports/?$", views.Reports.as_view(), name="settings-reports"),

View file

@ -13,7 +13,7 @@ from .goal import Goal, hide_goal
from .import_data import Import, ImportStatus from .import_data import Import, ImportStatus
from .inbox import Inbox from .inbox import Inbox
from .interaction import Favorite, Unfavorite, Boost, Unboost from .interaction import Favorite, Unfavorite, Boost, Unboost
from .invite import ManageInvites, Invite from .invite import ManageInvites, Invite, InviteRequest
from .isbn import Isbn from .isbn import Isbn
from .landing import About, Home, Discover from .landing import About, Home, Discover
from .list import Lists, List, Curate, UserLists from .list import Lists, List, Curate, UserLists

View file

@ -2,7 +2,7 @@
import re import re
from requests import HTTPError from requests import HTTPError
from django.core.exceptions import FieldError from django.core.exceptions import FieldError
from django.db.models import Q from django.db.models import Max, Q
from bookwyrm import activitypub, models from bookwyrm import activitypub, models
from bookwyrm.connectors import ConnectorException, get_data from bookwyrm.connectors import ConnectorException, get_data
@ -216,3 +216,20 @@ def is_blocked(viewer, user):
if viewer.is_authenticated and viewer in user.blocks.all(): if viewer.is_authenticated and viewer in user.blocks.all():
return True return True
return False return False
def get_discover_books():
""" list of books for the discover page """
return list(
set(
models.Edition.objects.filter(
review__published_date__isnull=False,
review__deleted=False,
review__user__local=True,
review__privacy__in=["public", "unlisted"],
)
.exclude(cover__exact="")
.annotate(Max("review__published_date"))
.order_by("-review__published_date__max")[:6]
)
)

View file

@ -9,6 +9,7 @@ from django.views import View
from bookwyrm import forms, models from bookwyrm import forms, models
from bookwyrm.settings import PAGE_LENGTH from bookwyrm.settings import PAGE_LENGTH
from . import helpers
# pylint: disable= no-self-use # pylint: disable= no-self-use
@ -77,3 +78,22 @@ class Invite(View):
return TemplateResponse(request, "invite.html", data) return TemplateResponse(request, "invite.html", data)
# post handling is in views.authentication.Register # post handling is in views.authentication.Register
class InviteRequest(View):
""" prospective users sign up here """
def post(self, request):
""" create a request """
form = forms.InviteRequestForm(request.POST)
received = False
if form.is_valid():
received = True
form.save()
data = {
"request_form": form,
"request_received": received,
"books": helpers.get_discover_books(),
}
return TemplateResponse(request, "discover/discover.html", data)

View file

@ -1,10 +1,10 @@
""" non-interactive pages """ """ non-interactive pages """
from django.db.models import Max
from django.template.response import TemplateResponse from django.template.response import TemplateResponse
from django.views import View from django.views import View
from bookwyrm import forms, models from bookwyrm import forms, models
from .feed import Feed from .feed import Feed
from . import helpers
# pylint: disable= no-self-use # pylint: disable= no-self-use
@ -33,20 +33,9 @@ class Discover(View):
def get(self, request): def get(self, request):
""" tiled book activity page """ """ tiled book activity page """
books = (
models.Edition.objects.filter(
review__published_date__isnull=False,
review__deleted=False,
review__user__local=True,
review__privacy__in=["public", "unlisted"],
)
.exclude(cover__exact="")
.annotate(Max("review__published_date"))
.order_by("-review__published_date__max")[:6]
)
data = { data = {
"register_form": forms.RegisterForm(), "register_form": forms.RegisterForm(),
"books": list(set(books)), "request_form": forms.InviteRequestForm(),
"books": helpers.get_discover_books(),
} }
return TemplateResponse(request, "discover/discover.html", data) return TemplateResponse(request, "discover/discover.html", data)