Initial commit
This commit is contained in:
431
env/lib/python3.10/site-packages/wagtail/admin/views/account.py
vendored
Normal file
431
env/lib/python3.10/site-packages/wagtail/admin/views/account.py
vendored
Normal file
@@ -0,0 +1,431 @@
|
||||
from collections import OrderedDict
|
||||
from functools import cached_property
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib import messages
|
||||
from django.contrib.auth import update_session_auth_hash
|
||||
from django.contrib.auth import views as auth_views
|
||||
from django.db import transaction
|
||||
from django.forms import Media
|
||||
from django.http import Http404
|
||||
from django.shortcuts import redirect
|
||||
from django.template.loader import render_to_string
|
||||
from django.template.response import TemplateResponse
|
||||
from django.urls import reverse, reverse_lazy
|
||||
from django.utils.decorators import method_decorator
|
||||
from django.utils.translation import gettext as _
|
||||
from django.utils.translation import gettext_lazy, override
|
||||
from django.views.decorators.debug import sensitive_post_parameters
|
||||
from django.views.generic.base import TemplateView
|
||||
|
||||
from wagtail import hooks
|
||||
from wagtail.admin.forms.account import (
|
||||
AvatarPreferencesForm,
|
||||
LocalePreferencesForm,
|
||||
NameEmailForm,
|
||||
NotificationPreferencesForm,
|
||||
ThemePreferencesForm,
|
||||
)
|
||||
from wagtail.admin.forms.auth import LoginForm, PasswordChangeForm, PasswordResetForm
|
||||
from wagtail.admin.localization import (
|
||||
get_available_admin_languages,
|
||||
get_available_admin_time_zones,
|
||||
)
|
||||
from wagtail.admin.views.generic import WagtailAdminTemplateMixin
|
||||
from wagtail.log_actions import log
|
||||
from wagtail.users.models import UserProfile
|
||||
from wagtail.utils.loading import get_custom_form
|
||||
|
||||
|
||||
def get_user_login_form():
|
||||
form_setting = "WAGTAILADMIN_USER_LOGIN_FORM"
|
||||
if hasattr(settings, form_setting):
|
||||
return get_custom_form(form_setting)
|
||||
else:
|
||||
return LoginForm
|
||||
|
||||
|
||||
def get_password_reset_form():
|
||||
form_setting = "WAGTAILADMIN_USER_PASSWORD_RESET_FORM"
|
||||
if hasattr(settings, form_setting):
|
||||
return get_custom_form(form_setting)
|
||||
else:
|
||||
return PasswordResetForm
|
||||
|
||||
|
||||
# Helper functions to check password management settings to enable/disable views as appropriate.
|
||||
# These are functions rather than class-level constants so that they can be overridden in tests
|
||||
# by override_settings
|
||||
|
||||
|
||||
def password_management_enabled():
|
||||
return getattr(settings, "WAGTAIL_PASSWORD_MANAGEMENT_ENABLED", True)
|
||||
|
||||
|
||||
def email_management_enabled():
|
||||
return getattr(settings, "WAGTAIL_EMAIL_MANAGEMENT_ENABLED", True)
|
||||
|
||||
|
||||
def password_reset_enabled():
|
||||
return getattr(
|
||||
settings, "WAGTAIL_PASSWORD_RESET_ENABLED", password_management_enabled()
|
||||
)
|
||||
|
||||
|
||||
# Tabs
|
||||
|
||||
|
||||
class SettingsTab:
|
||||
def __init__(self, name, title, order=0):
|
||||
self.name = name
|
||||
self.title = title
|
||||
self.order = order
|
||||
|
||||
|
||||
profile_tab = SettingsTab("profile", gettext_lazy("Profile"), order=100)
|
||||
notifications_tab = SettingsTab(
|
||||
"notifications", gettext_lazy("Notifications"), order=200
|
||||
)
|
||||
|
||||
|
||||
# Panels
|
||||
|
||||
|
||||
class BaseSettingsPanel:
|
||||
name = ""
|
||||
title = ""
|
||||
tab = profile_tab
|
||||
help_text = None
|
||||
template_name = "wagtailadmin/account/settings_panels/base.html"
|
||||
form_class = None
|
||||
form_object = "user"
|
||||
|
||||
def __init__(self, request, user, profile):
|
||||
self.request = request
|
||||
self.user = user
|
||||
self.profile = profile
|
||||
|
||||
def is_active(self):
|
||||
"""
|
||||
Returns True to display the panel.
|
||||
"""
|
||||
return True
|
||||
|
||||
def get_form(self):
|
||||
"""
|
||||
Returns an initialised form.
|
||||
"""
|
||||
kwargs = {
|
||||
"instance": self.profile if self.form_object == "profile" else self.user,
|
||||
"prefix": self.name,
|
||||
}
|
||||
|
||||
if self.request.method == "POST":
|
||||
return self.form_class(self.request.POST, self.request.FILES, **kwargs)
|
||||
else:
|
||||
return self.form_class(**kwargs)
|
||||
|
||||
def get_context_data(self):
|
||||
"""
|
||||
Returns the template context to use when rendering the template.
|
||||
"""
|
||||
return {"form": self.get_form()}
|
||||
|
||||
def render(self):
|
||||
"""
|
||||
Renders the panel using the template specified in .template_name and context from .get_context_data()
|
||||
"""
|
||||
return render_to_string(
|
||||
self.template_name, self.get_context_data(), request=self.request
|
||||
)
|
||||
|
||||
|
||||
class NameEmailSettingsPanel(BaseSettingsPanel):
|
||||
name = "name_email"
|
||||
order = 100
|
||||
form_class = NameEmailForm
|
||||
|
||||
@cached_property
|
||||
def title(self):
|
||||
if email_management_enabled():
|
||||
return _("Name and Email")
|
||||
return _("Name")
|
||||
|
||||
|
||||
class AvatarSettingsPanel(BaseSettingsPanel):
|
||||
name = "avatar"
|
||||
title = gettext_lazy("Profile picture")
|
||||
order = 300
|
||||
template_name = "wagtailadmin/account/settings_panels/avatar.html"
|
||||
form_class = AvatarPreferencesForm
|
||||
form_object = "profile"
|
||||
|
||||
|
||||
class NotificationsSettingsPanel(BaseSettingsPanel):
|
||||
name = "notifications"
|
||||
title = gettext_lazy("Notifications")
|
||||
tab = notifications_tab
|
||||
order = 100
|
||||
form_class = NotificationPreferencesForm
|
||||
form_object = "profile"
|
||||
|
||||
def is_active(self):
|
||||
# Hide the panel if there are no notification preferences
|
||||
return bool(self.get_form().fields)
|
||||
|
||||
|
||||
class LocaleSettingsPanel(BaseSettingsPanel):
|
||||
name = "locale"
|
||||
title = gettext_lazy("Locale")
|
||||
order = 400
|
||||
form_class = LocalePreferencesForm
|
||||
form_object = "profile"
|
||||
|
||||
def is_active(self):
|
||||
return (
|
||||
len(get_available_admin_languages()) > 1
|
||||
or len(get_available_admin_time_zones()) > 1
|
||||
)
|
||||
|
||||
|
||||
class ThemeSettingsPanel(BaseSettingsPanel):
|
||||
name = "theme"
|
||||
title = gettext_lazy("Theme preferences")
|
||||
order = 450
|
||||
form_class = ThemePreferencesForm
|
||||
form_object = "profile"
|
||||
|
||||
|
||||
class ChangePasswordPanel(BaseSettingsPanel):
|
||||
name = "password"
|
||||
title = gettext_lazy("Password")
|
||||
order = 500
|
||||
form_class = PasswordChangeForm
|
||||
|
||||
def is_active(self):
|
||||
return password_management_enabled() and self.user.has_usable_password()
|
||||
|
||||
def get_form(self):
|
||||
# Note: don't bind the form unless a field is specified
|
||||
# This prevents the validation error from displaying if the user wishes to ignore this
|
||||
bind_form = False
|
||||
if self.request.method == "POST":
|
||||
bind_form = any(
|
||||
[
|
||||
self.request.POST.get(self.name + "-new_password1"),
|
||||
self.request.POST.get(self.name + "-new_password2"),
|
||||
]
|
||||
)
|
||||
|
||||
if bind_form:
|
||||
return self.form_class(self.user, self.request.POST, prefix=self.name)
|
||||
else:
|
||||
return self.form_class(self.user, prefix=self.name)
|
||||
|
||||
|
||||
# Views
|
||||
|
||||
|
||||
@method_decorator(sensitive_post_parameters(), name="post")
|
||||
class AccountView(WagtailAdminTemplateMixin, TemplateView):
|
||||
template_name = "wagtailadmin/account/account.html"
|
||||
page_title = gettext_lazy("Account")
|
||||
header_icon = "user"
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
panels = self.get_panels()
|
||||
context["panels_by_tab"] = self.get_panels_by_tab(panels)
|
||||
context["menu_items"] = self.get_menu_items()
|
||||
context["media"] = self.get_media(panels)
|
||||
context["user"] = self.request.user
|
||||
return context
|
||||
|
||||
def get_panels(self):
|
||||
request = self.request
|
||||
user = self.request.user
|
||||
profile = UserProfile.get_for_user(user)
|
||||
# Panels
|
||||
panels = [
|
||||
NameEmailSettingsPanel(request, user, profile),
|
||||
AvatarSettingsPanel(request, user, profile),
|
||||
NotificationsSettingsPanel(request, user, profile),
|
||||
LocaleSettingsPanel(request, user, profile),
|
||||
ThemeSettingsPanel(request, user, profile),
|
||||
ChangePasswordPanel(request, user, profile),
|
||||
]
|
||||
for fn in hooks.get_hooks("register_account_settings_panel"):
|
||||
panel = fn(request, user, profile)
|
||||
if panel and panel.is_active():
|
||||
panels.append(panel)
|
||||
|
||||
panels = [panel for panel in panels if panel.is_active()]
|
||||
return panels
|
||||
|
||||
def get_panels_by_tab(self, panels):
|
||||
# Get tabs and order them
|
||||
tabs = list({panel.tab for panel in panels})
|
||||
tabs.sort(key=lambda tab: tab.order)
|
||||
|
||||
# Get dict of tabs to ordered panels
|
||||
panels_by_tab = OrderedDict([(tab, []) for tab in tabs])
|
||||
for panel in panels:
|
||||
panels_by_tab[panel.tab].append(panel)
|
||||
for tab, tab_panels in panels_by_tab.items():
|
||||
tab_panels.sort(key=lambda panel: panel.order)
|
||||
return panels_by_tab
|
||||
|
||||
def get_menu_items(self):
|
||||
# Menu items
|
||||
menu_items = []
|
||||
for fn in hooks.get_hooks("register_account_menu_item"):
|
||||
item = fn(self.request)
|
||||
if item:
|
||||
menu_items.append(item)
|
||||
return menu_items
|
||||
|
||||
def get_media(self, panels):
|
||||
panel_forms = [panel.get_form() for panel in panels]
|
||||
|
||||
media = Media()
|
||||
for form in panel_forms:
|
||||
media += form.media
|
||||
return media
|
||||
|
||||
def post(self, request):
|
||||
panel_forms = [panel.get_form() for panel in self.get_panels()]
|
||||
user = self.request.user
|
||||
profile = UserProfile.get_for_user(user)
|
||||
|
||||
if all(form.is_valid() or not form.is_bound for form in panel_forms):
|
||||
with transaction.atomic():
|
||||
for form in panel_forms:
|
||||
if form.is_bound:
|
||||
form.save()
|
||||
|
||||
log(user, "wagtail.edit")
|
||||
|
||||
# Prevent a password change from logging this user out
|
||||
update_session_auth_hash(request, user)
|
||||
|
||||
# Override the language when creating the success message
|
||||
# If the user has changed their language in this request, the message should
|
||||
# be in the new language, not the existing one
|
||||
with override(profile.get_preferred_language()):
|
||||
messages.success(
|
||||
request, _("Your account settings have been changed successfully!")
|
||||
)
|
||||
|
||||
return redirect("wagtailadmin_account")
|
||||
|
||||
return TemplateResponse(request, self.template_name, self.get_context_data())
|
||||
|
||||
|
||||
class PasswordResetEnabledViewMixin:
|
||||
"""
|
||||
Class based view mixin that disables the view if password reset is disabled by one of the following settings:
|
||||
- WAGTAIL_PASSWORD_RESET_ENABLED
|
||||
- WAGTAIL_PASSWORD_MANAGEMENT_ENABLED
|
||||
"""
|
||||
|
||||
def dispatch(self, *args, **kwargs):
|
||||
if not password_reset_enabled():
|
||||
raise Http404
|
||||
|
||||
return super().dispatch(*args, **kwargs)
|
||||
|
||||
|
||||
class PasswordResetView(PasswordResetEnabledViewMixin, auth_views.PasswordResetView):
|
||||
template_name = "wagtailadmin/account/password_reset/form.html"
|
||||
email_template_name = "wagtailadmin/account/password_reset/email.txt"
|
||||
subject_template_name = "wagtailadmin/account/password_reset/email_subject.txt"
|
||||
success_url = reverse_lazy("wagtailadmin_password_reset_done")
|
||||
|
||||
def get_form_class(self):
|
||||
return get_password_reset_form()
|
||||
|
||||
|
||||
class PasswordResetDoneView(
|
||||
PasswordResetEnabledViewMixin, auth_views.PasswordResetDoneView
|
||||
):
|
||||
template_name = "wagtailadmin/account/password_reset/done.html"
|
||||
|
||||
|
||||
class PasswordResetConfirmView(
|
||||
PasswordResetEnabledViewMixin, auth_views.PasswordResetConfirmView
|
||||
):
|
||||
template_name = "wagtailadmin/account/password_reset/confirm.html"
|
||||
success_url = reverse_lazy("wagtailadmin_password_reset_complete")
|
||||
|
||||
|
||||
class PasswordResetCompleteView(
|
||||
PasswordResetEnabledViewMixin, auth_views.PasswordResetCompleteView
|
||||
):
|
||||
template_name = "wagtailadmin/account/password_reset/complete.html"
|
||||
|
||||
|
||||
class LoginView(auth_views.LoginView):
|
||||
template_name = "wagtailadmin/login.html"
|
||||
|
||||
def get_success_url(self):
|
||||
return self.get_redirect_url() or reverse("wagtailadmin_home")
|
||||
|
||||
def get(self, *args, **kwargs):
|
||||
# If user is already logged in, redirect them to the dashboard
|
||||
if self.request.user.is_authenticated and self.request.user.has_perm(
|
||||
"wagtailadmin.access_admin"
|
||||
):
|
||||
return redirect(self.get_success_url())
|
||||
|
||||
return super().get(*args, **kwargs)
|
||||
|
||||
def get_form_class(self):
|
||||
return get_user_login_form()
|
||||
|
||||
def form_valid(self, form):
|
||||
response = super().form_valid(form)
|
||||
|
||||
remember = form.cleaned_data.get("remember")
|
||||
if remember:
|
||||
self.request.session.set_expiry(settings.SESSION_COOKIE_AGE)
|
||||
else:
|
||||
self.request.session.set_expiry(0)
|
||||
|
||||
return response
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
|
||||
context["show_password_reset"] = password_reset_enabled()
|
||||
|
||||
from django.contrib.auth import get_user_model
|
||||
|
||||
User = get_user_model()
|
||||
context["username_field"] = User._meta.get_field(
|
||||
User.USERNAME_FIELD
|
||||
).verbose_name
|
||||
|
||||
return context
|
||||
|
||||
|
||||
class LogoutView(auth_views.LogoutView):
|
||||
next_page = "wagtailadmin_login"
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
response = super().dispatch(request, *args, **kwargs)
|
||||
|
||||
messages.success(self.request, _("You have been successfully logged out."))
|
||||
# By default, logging out will generate a fresh sessionid cookie. We want to use the
|
||||
# absence of sessionid as an indication that front-end pages are being viewed by a
|
||||
# non-logged-in user and are therefore cacheable, so we forcibly delete the cookie here.
|
||||
response.delete_cookie(
|
||||
settings.SESSION_COOKIE_NAME,
|
||||
domain=settings.SESSION_COOKIE_DOMAIN,
|
||||
path=settings.SESSION_COOKIE_PATH,
|
||||
)
|
||||
|
||||
# HACK: pretend that the session hasn't been modified, so that SessionMiddleware
|
||||
# won't override the above and write a new cookie.
|
||||
self.request.session.modified = False
|
||||
|
||||
return response
|
||||
Reference in New Issue
Block a user