Moves landing views into directory
This commit is contained in:
0
bookwyrm/views/landing/__init__.py
Normal file
0
bookwyrm/views/landing/__init__.py
Normal file
41
bookwyrm/views/landing/landing.py
Normal file
41
bookwyrm/views/landing/landing.py
Normal file
@ -0,0 +1,41 @@
|
||||
""" non-interactive pages """
|
||||
from django.template.response import TemplateResponse
|
||||
from django.views import View
|
||||
|
||||
from bookwyrm import forms
|
||||
from bookwyrm.views import helpers
|
||||
from bookwyrm.views.feed import Feed
|
||||
|
||||
|
||||
# pylint: disable= no-self-use
|
||||
class About(View):
|
||||
"""create invites"""
|
||||
|
||||
def get(self, request):
|
||||
"""more information about the instance"""
|
||||
return TemplateResponse(request, "landing/about.html")
|
||||
|
||||
|
||||
class Home(View):
|
||||
"""landing page or home feed depending on auth"""
|
||||
|
||||
def get(self, request):
|
||||
"""this is the same as the feed on the home tab"""
|
||||
if request.user.is_authenticated:
|
||||
feed_view = Feed.as_view()
|
||||
return feed_view(request, "home")
|
||||
landing_view = Landing.as_view()
|
||||
return landing_view(request)
|
||||
|
||||
|
||||
class Landing(View):
|
||||
"""preview of recently reviewed books"""
|
||||
|
||||
def get(self, request):
|
||||
"""tiled book activity page"""
|
||||
data = {
|
||||
"register_form": forms.RegisterForm(),
|
||||
"request_form": forms.InviteRequestForm(),
|
||||
"books": helpers.get_landing_books(),
|
||||
}
|
||||
return TemplateResponse(request, "landing/landing.html", data)
|
82
bookwyrm/views/landing/login.py
Normal file
82
bookwyrm/views/landing/login.py
Normal file
@ -0,0 +1,82 @@
|
||||
""" class views for login/register views """
|
||||
from django.contrib.auth import authenticate, login, logout
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.shortcuts import redirect
|
||||
from django.template.response import TemplateResponse
|
||||
from django.utils.decorators import method_decorator
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.views import View
|
||||
from django.views.decorators.csrf import csrf_exempt
|
||||
from django.views.decorators.debug import sensitive_variables, sensitive_post_parameters
|
||||
|
||||
from bookwyrm import forms, models
|
||||
from bookwyrm.settings import DOMAIN
|
||||
from bookwyrm.views.helpers import set_language
|
||||
|
||||
|
||||
# pylint: disable=no-self-use
|
||||
@method_decorator(csrf_exempt, name="dispatch")
|
||||
class Login(View):
|
||||
"""authenticate an existing user"""
|
||||
|
||||
def get(self, request, confirmed=None):
|
||||
"""login page"""
|
||||
if request.user.is_authenticated:
|
||||
return redirect("/")
|
||||
# send user to the login page
|
||||
data = {
|
||||
"show_confirmed_email": confirmed,
|
||||
"login_form": forms.LoginForm(),
|
||||
"register_form": forms.RegisterForm(),
|
||||
}
|
||||
return TemplateResponse(request, "landing/login.html", data)
|
||||
|
||||
@sensitive_variables("password")
|
||||
@method_decorator(sensitive_post_parameters("password"))
|
||||
def post(self, request):
|
||||
"""authentication action"""
|
||||
if request.user.is_authenticated:
|
||||
return redirect("/")
|
||||
login_form = forms.LoginForm(request.POST)
|
||||
|
||||
localname = login_form.data["localname"]
|
||||
if "@" in localname: # looks like an email address to me
|
||||
try:
|
||||
username = models.User.objects.get(email=localname).username
|
||||
except models.User.DoesNotExist: # maybe it's a full username?
|
||||
username = localname
|
||||
else:
|
||||
username = f"{localname}@{DOMAIN}"
|
||||
password = login_form.data["password"]
|
||||
|
||||
# perform authentication
|
||||
user = authenticate(request, username=username, password=password)
|
||||
if user is not None:
|
||||
# successful login
|
||||
login(request, user)
|
||||
user.update_active_date()
|
||||
if request.POST.get("first_login"):
|
||||
return set_language(user, redirect("get-started-profile"))
|
||||
return set_language(user, redirect(request.GET.get("next", "/")))
|
||||
|
||||
# maybe the user is pending email confirmation
|
||||
if models.User.objects.filter(
|
||||
username=username, is_active=False, deactivation_reason="pending"
|
||||
).exists():
|
||||
return redirect("confirm-email")
|
||||
|
||||
# login errors
|
||||
login_form.non_field_errors = _("Username or password are incorrect")
|
||||
register_form = forms.RegisterForm()
|
||||
data = {"login_form": login_form, "register_form": register_form}
|
||||
return TemplateResponse(request, "landing/login.html", data)
|
||||
|
||||
|
||||
@method_decorator(login_required, name="dispatch")
|
||||
class Logout(View):
|
||||
"""log out"""
|
||||
|
||||
def get(self, request):
|
||||
"""done with this place! outa here!"""
|
||||
logout(request)
|
||||
return redirect("/")
|
84
bookwyrm/views/landing/password.py
Normal file
84
bookwyrm/views/landing/password.py
Normal file
@ -0,0 +1,84 @@
|
||||
""" class views for password management """
|
||||
from django.contrib.auth import login
|
||||
from django.core.exceptions import PermissionDenied
|
||||
from django.shortcuts import redirect
|
||||
from django.template.response import TemplateResponse
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.views import View
|
||||
|
||||
from bookwyrm import models
|
||||
from bookwyrm.emailing import password_reset_email
|
||||
|
||||
|
||||
# pylint: disable= no-self-use
|
||||
class PasswordResetRequest(View):
|
||||
"""forgot password flow"""
|
||||
|
||||
def get(self, request):
|
||||
"""password reset page"""
|
||||
return TemplateResponse(
|
||||
request,
|
||||
"landing/password_reset_request.html",
|
||||
)
|
||||
|
||||
def post(self, request):
|
||||
"""create a password reset token"""
|
||||
email = request.POST.get("email")
|
||||
try:
|
||||
user = models.User.viewer_aware_objects(request.user).get(
|
||||
email=email, email__isnull=False
|
||||
)
|
||||
except models.User.DoesNotExist:
|
||||
data = {"error": _("No user with that email address was found.")}
|
||||
return TemplateResponse(
|
||||
request, "landing/password_reset_request.html", data
|
||||
)
|
||||
|
||||
# remove any existing password reset cods for this user
|
||||
models.PasswordReset.objects.filter(user=user).all().delete()
|
||||
|
||||
# create a new reset code
|
||||
code = models.PasswordReset.objects.create(user=user)
|
||||
password_reset_email(code)
|
||||
data = {"message": _(f"A password reset link was sent to {email}")}
|
||||
return TemplateResponse(request, "landing/password_reset_request.html", data)
|
||||
|
||||
|
||||
class PasswordReset(View):
|
||||
"""set new password"""
|
||||
|
||||
def get(self, request, code):
|
||||
"""endpoint for sending invites"""
|
||||
if request.user.is_authenticated:
|
||||
return redirect("/")
|
||||
try:
|
||||
reset_code = models.PasswordReset.objects.get(code=code)
|
||||
if not reset_code.valid():
|
||||
raise PermissionDenied()
|
||||
except models.PasswordReset.DoesNotExist:
|
||||
raise PermissionDenied()
|
||||
|
||||
return TemplateResponse(request, "landing/password_reset.html", {"code": code})
|
||||
|
||||
def post(self, request, code):
|
||||
"""allow a user to change their password through an emailed token"""
|
||||
try:
|
||||
reset_code = models.PasswordReset.objects.get(code=code)
|
||||
except models.PasswordReset.DoesNotExist:
|
||||
data = {"errors": ["Invalid password reset link"]}
|
||||
return TemplateResponse(request, "landing/password_reset.html", data)
|
||||
|
||||
user = reset_code.user
|
||||
|
||||
new_password = request.POST.get("password")
|
||||
confirm_password = request.POST.get("confirm-password")
|
||||
|
||||
if new_password != confirm_password:
|
||||
data = {"errors": ["Passwords do not match"]}
|
||||
return TemplateResponse(request, "landing/password_reset.html", data)
|
||||
|
||||
user.set_password(new_password)
|
||||
user.save(broadcast=False, update_fields=["password"])
|
||||
login(request, user)
|
||||
reset_code.delete()
|
||||
return redirect("/")
|
145
bookwyrm/views/landing/register.py
Normal file
145
bookwyrm/views/landing/register.py
Normal file
@ -0,0 +1,145 @@
|
||||
""" class views for login/register views """
|
||||
from django.contrib.auth import login
|
||||
from django.core.exceptions import PermissionDenied
|
||||
from django.shortcuts import get_object_or_404, redirect
|
||||
from django.template.response import TemplateResponse
|
||||
from django.utils.decorators import method_decorator
|
||||
from django.views import View
|
||||
from django.views.decorators.http import require_POST
|
||||
from django.views.decorators.debug import sensitive_variables, sensitive_post_parameters
|
||||
|
||||
from bookwyrm import emailing, forms, models
|
||||
from bookwyrm.settings import DOMAIN
|
||||
|
||||
|
||||
# pylint: disable=no-self-use
|
||||
class Register(View):
|
||||
"""register a user"""
|
||||
|
||||
def get(self, request): # pylint: disable=unused-argument
|
||||
"""whether or not you're logged in, just go to the home view"""
|
||||
return redirect("/")
|
||||
|
||||
@sensitive_variables("password")
|
||||
@method_decorator(sensitive_post_parameters("password"))
|
||||
def post(self, request):
|
||||
"""join the server"""
|
||||
settings = models.SiteSettings.get()
|
||||
if not settings.allow_registration:
|
||||
invite_code = request.POST.get("invite_code")
|
||||
|
||||
if not invite_code:
|
||||
raise PermissionDenied()
|
||||
|
||||
invite = get_object_or_404(models.SiteInvite, code=invite_code)
|
||||
if not invite.valid():
|
||||
raise PermissionDenied()
|
||||
else:
|
||||
invite = None
|
||||
|
||||
form = forms.RegisterForm(request.POST)
|
||||
errors = False
|
||||
if not form.is_valid():
|
||||
errors = True
|
||||
|
||||
localname = form.data["localname"].strip()
|
||||
email = form.data["email"]
|
||||
password = form.data["password"]
|
||||
|
||||
# make sure the email isn't blocked as spam
|
||||
email_domain = email.split("@")[-1]
|
||||
if models.EmailBlocklist.objects.filter(domain=email_domain).exists():
|
||||
# treat this like a successful registration, but don't do anything
|
||||
return redirect("confirm-email")
|
||||
|
||||
# check localname and email uniqueness
|
||||
if models.User.objects.filter(localname=localname).first():
|
||||
form.errors["localname"] = ["User with this username already exists"]
|
||||
errors = True
|
||||
|
||||
if errors:
|
||||
data = {
|
||||
"login_form": forms.LoginForm(),
|
||||
"register_form": form,
|
||||
"invite": invite,
|
||||
"valid": invite.valid() if invite else True,
|
||||
}
|
||||
if invite:
|
||||
return TemplateResponse(request, "landing/invite.html", data)
|
||||
return TemplateResponse(request, "landing/login.html", data)
|
||||
|
||||
username = f"{localname}@{DOMAIN}"
|
||||
user = models.User.objects.create_user(
|
||||
username,
|
||||
email,
|
||||
password,
|
||||
localname=localname,
|
||||
local=True,
|
||||
deactivation_reason="pending" if settings.require_confirm_email else None,
|
||||
is_active=not settings.require_confirm_email,
|
||||
)
|
||||
if invite:
|
||||
invite.times_used += 1
|
||||
invite.invitees.add(user)
|
||||
invite.save()
|
||||
|
||||
if settings.require_confirm_email:
|
||||
emailing.email_confirmation_email(user)
|
||||
return redirect("confirm-email")
|
||||
|
||||
login(request, user)
|
||||
return redirect("get-started-profile")
|
||||
|
||||
|
||||
class ConfirmEmailCode(View):
|
||||
"""confirm email address"""
|
||||
|
||||
def get(self, request, code): # pylint: disable=unused-argument
|
||||
"""you got the code! good work"""
|
||||
settings = models.SiteSettings.get()
|
||||
if request.user.is_authenticated or not settings.require_confirm_email:
|
||||
return redirect("/")
|
||||
|
||||
# look up the user associated with this code
|
||||
try:
|
||||
user = models.User.objects.get(confirmation_code=code)
|
||||
except models.User.DoesNotExist:
|
||||
return TemplateResponse(
|
||||
request, "confirm_email/confirm_email.html", {"valid": False}
|
||||
)
|
||||
# update the user
|
||||
user.is_active = True
|
||||
user.deactivation_reason = None
|
||||
user.save(broadcast=False, update_fields=["is_active", "deactivation_reason"])
|
||||
# direct the user to log in
|
||||
return redirect("login", confirmed="confirmed")
|
||||
|
||||
|
||||
class ConfirmEmail(View):
|
||||
"""enter code to confirm email address"""
|
||||
|
||||
def get(self, request): # pylint: disable=unused-argument
|
||||
"""you need a code! keep looking"""
|
||||
settings = models.SiteSettings.get()
|
||||
if request.user.is_authenticated or not settings.require_confirm_email:
|
||||
return redirect("/")
|
||||
|
||||
return TemplateResponse(
|
||||
request, "confirm_email/confirm_email.html", {"valid": True}
|
||||
)
|
||||
|
||||
def post(self, request):
|
||||
"""same as clicking the link"""
|
||||
code = request.POST.get("code")
|
||||
return ConfirmEmailCode().get(request, code)
|
||||
|
||||
|
||||
@require_POST
|
||||
def resend_link(request):
|
||||
"""resend confirmation link"""
|
||||
email = request.POST.get("email")
|
||||
user = get_object_or_404(models.User, email=email)
|
||||
emailing.email_confirmation_email(user)
|
||||
return TemplateResponse(
|
||||
request, "confirm_email/confirm_email.html", {"valid": True}
|
||||
)
|
Reference in New Issue
Block a user