248 lines
7.2 KiB
Python
248 lines
7.2 KiB
Python
# -*- coding: utf-8 -*-
|
|
from __future__ import annotations
|
|
|
|
from django.contrib import messages
|
|
from django.contrib.auth import logout as auth_logout
|
|
from django.contrib.auth.views import LoginView as BaseLoginView
|
|
from django.core.exceptions import PermissionDenied
|
|
import django.db
|
|
from django.http import Http404, HttpRequest, HttpResponse
|
|
from django.shortcuts import redirect, render
|
|
from django.urls import reverse
|
|
from django.utils.http import url_has_allowed_host_and_scheme
|
|
from django.utils.translation import gettext_lazy as _
|
|
from django.views.generic import FormView, RedirectView
|
|
|
|
from hotpocket_backend.apps.accounts.decorators import account_required
|
|
from hotpocket_backend.apps.accounts.mixins import AccountRequiredMixin
|
|
from hotpocket_backend.apps.core.conf import settings as django_settings
|
|
from hotpocket_backend.apps.ui.forms.accounts import (
|
|
FederatedPasswordForm,
|
|
FederatedProfileForm,
|
|
LoginForm,
|
|
PasswordForm,
|
|
ProfileForm,
|
|
SettingsForm,
|
|
)
|
|
|
|
|
|
@account_required
|
|
def index(request: HttpRequest) -> HttpResponse:
|
|
return redirect(reverse('ui.accounts.settings'))
|
|
|
|
|
|
@account_required
|
|
def browse(request: HttpRequest) -> HttpResponse:
|
|
raise Http404()
|
|
|
|
|
|
class LoginView(BaseLoginView):
|
|
template_name = 'ui/accounts/login.html'
|
|
form_class = LoginForm
|
|
|
|
def get(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
|
|
request.session['post_login_next_url'] = request.GET.get('next', None)
|
|
request.session.save()
|
|
|
|
return super().get(request, *args, **kwargs)
|
|
|
|
def get_success_url(self) -> str:
|
|
return reverse('ui.accounts.post_login')
|
|
|
|
|
|
class PostLoginView(RedirectView):
|
|
def get_redirect_url(self, *args, **kwargs) -> str:
|
|
next_url = self.request.session.pop('post_login_next_url', None)
|
|
self.request.session.save()
|
|
|
|
allowed_hosts = None
|
|
if len(django_settings.ALLOWED_HOSTS) > 0:
|
|
allowed_hosts = set(filter(
|
|
lambda value: value != '*',
|
|
django_settings.ALLOWED_HOSTS,
|
|
))
|
|
|
|
if next_url is not None:
|
|
next_url_is_safe = url_has_allowed_host_and_scheme(
|
|
url=next_url,
|
|
allowed_hosts=allowed_hosts,
|
|
require_https=self.request.is_secure(),
|
|
)
|
|
|
|
if next_url_is_safe is False:
|
|
next_url = None
|
|
|
|
return next_url or reverse('ui.index.index')
|
|
|
|
def get(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
|
|
if request.user.is_anonymous is True:
|
|
raise PermissionDenied('NOPE')
|
|
|
|
return super().get(request, *args, **kwargs)
|
|
|
|
|
|
def logout(request: HttpRequest) -> HttpResponse:
|
|
if request.user.is_authenticated is True:
|
|
auth_logout(request)
|
|
|
|
return render(request, 'ui/accounts/logout.html')
|
|
|
|
|
|
@account_required
|
|
def settings(request: HttpRequest) -> HttpResponse:
|
|
return redirect(reverse('ui.accounts.settings.profile'))
|
|
|
|
|
|
class BaseSettingsView(AccountRequiredMixin, FormView):
|
|
template_name = 'ui/accounts/settings.html'
|
|
|
|
@property
|
|
def is_federated(self) -> bool:
|
|
return all((
|
|
self.request.user.is_anonymous is False,
|
|
self.request.user.has_usable_password() is False,
|
|
))
|
|
|
|
def get_context_data(self, **kwargs) -> dict:
|
|
result = super().get_context_data(**kwargs)
|
|
|
|
result.update({
|
|
'is_federated': self.is_federated,
|
|
})
|
|
|
|
return result
|
|
|
|
|
|
class ProfileView(BaseSettingsView):
|
|
def get_form_class(self) -> type[ProfileForm]:
|
|
if self.is_federated is True:
|
|
return FederatedProfileForm
|
|
|
|
return ProfileForm
|
|
|
|
def get_initial(self) -> dict:
|
|
result = super().get_initial()
|
|
|
|
result.update({
|
|
'username': self.request.user.username,
|
|
'first_name': self.request.user.first_name,
|
|
'last_name': self.request.user.last_name,
|
|
'email': self.request.user.email,
|
|
})
|
|
|
|
return result
|
|
|
|
def get_context_data(self, **kwargs) -> dict:
|
|
result = super().get_context_data(**kwargs)
|
|
|
|
result.update({
|
|
'title': _('Profile'),
|
|
'active_tab': 'profile',
|
|
})
|
|
|
|
return result
|
|
|
|
def form_valid(self, form: ProfileForm) -> HttpResponse:
|
|
assert self.is_federated is False, (
|
|
'Refuse to save profile of a federated account: '
|
|
f'account=`{self.request.user}`'
|
|
)
|
|
|
|
with django.db.transaction.atomic():
|
|
self.request.user.first_name = form.cleaned_data['first_name']
|
|
self.request.user.last_name = form.cleaned_data['last_name']
|
|
self.request.user.email = form.cleaned_data['email']
|
|
self.request.user.save()
|
|
|
|
messages.add_message(
|
|
self.request,
|
|
messages.SUCCESS,
|
|
message=_('Your profile has been been updated!'),
|
|
)
|
|
|
|
return super().form_valid(form)
|
|
|
|
def get_success_url(self) -> str:
|
|
return reverse('ui.accounts.settings.profile')
|
|
|
|
|
|
class PasswordView(BaseSettingsView):
|
|
def get_form_class(self) -> type[PasswordForm]:
|
|
if self.is_federated is True:
|
|
return FederatedPasswordForm
|
|
|
|
return PasswordForm
|
|
|
|
def get_form(self,
|
|
form_class: type[PasswordForm] | None = None,
|
|
) -> PasswordForm:
|
|
form_class = form_class or self.get_form_class()
|
|
return form_class(self.request.user, **self.get_form_kwargs())
|
|
|
|
def get_context_data(self, **kwargs) -> dict:
|
|
result = super().get_context_data(**kwargs)
|
|
|
|
result.update({
|
|
'title': _('Password'),
|
|
'active_tab': 'password',
|
|
})
|
|
|
|
return result
|
|
|
|
def form_valid(self, form: PasswordForm) -> HttpResponse:
|
|
assert self.is_federated is False, (
|
|
'Refuse to change password of a federated account: '
|
|
f'account=`{self.request.user}`'
|
|
)
|
|
|
|
with django.db.transaction.atomic():
|
|
form.set_password_and_save(
|
|
self.request.user,
|
|
password_field_name='new_password1',
|
|
)
|
|
|
|
messages.add_message(
|
|
self.request,
|
|
messages.SUCCESS,
|
|
message=_('Your password has been changed!'),
|
|
)
|
|
|
|
return super().form_valid(form)
|
|
|
|
def get_success_url(self) -> str:
|
|
return reverse('ui.accounts.settings.password')
|
|
|
|
|
|
class SettingsView(BaseSettingsView):
|
|
form_class = SettingsForm
|
|
|
|
def get_context_data(self, **kwargs) -> dict:
|
|
result = super().get_context_data(**kwargs)
|
|
|
|
result.update({
|
|
'title': _('Settings'),
|
|
'active_tab': 'settings',
|
|
'is_federated': False,
|
|
})
|
|
|
|
return result
|
|
|
|
def get_initial(self) -> dict:
|
|
return self.request.user.settings
|
|
|
|
def form_valid(self, form: PasswordForm) -> HttpResponse:
|
|
with django.db.transaction.atomic():
|
|
self.request.user.raw_settings = form.cleaned_data
|
|
self.request.user.save()
|
|
|
|
messages.add_message(
|
|
self.request,
|
|
messages.SUCCESS,
|
|
message=_('Your settings have been updated!'),
|
|
)
|
|
|
|
return super().form_valid(form)
|
|
|
|
def get_success_url(self) -> str:
|
|
return reverse('ui.accounts.settings.settings')
|