Apply custom theme to login page also.

And improved theme login for frontend.
This commit is contained in:
Antoine Nguyen
2025-09-15 13:50:59 +02:00
parent bcb4050c6c
commit 6afa2d15ac
7 changed files with 116 additions and 75 deletions
+48
View File
@@ -0,0 +1,48 @@
import { ref } from 'vue'
import { useTheme } from 'vuetify'
import themeApi from '@/api/theme'
export function useModoboaTheme() {
const vTheme = useTheme()
const theme = ref(null)
const enableTheme = async () => {
const response = await themeApi.getTheme()
theme.value = response.data
if (
theme.value.theme_primary_color !==
vTheme.themes.value.light.colors.primary
) {
vTheme.themes.value.light.colors.primary = theme.value.theme_primary_color
}
if (
theme.value.theme_primary_color_light !==
vTheme.themes.value.light.colors['primary-lighten-1']
) {
vTheme.themes.value.light.colors['primary-lighten-1'] =
theme.value.theme_primary_color_light
}
if (
theme.value.theme_primary_color_dark !==
vTheme.themes.value.light.colors['primary-darken-1']
) {
vTheme.themes.value.light.colors['primary-darken-1'] =
theme.value.theme_primary_color_dark
}
if (
theme.value.theme_secondary_color !==
vTheme.themes.value.light.colors.secondary
) {
vTheme.themes.value.light.colors.secondary =
theme.value.theme_secondary_color
}
if (
theme.value.theme_label_color !== vTheme.themes.value.light.colors.label
) {
vTheme.themes.value.light.colors.label = theme.value.theme_label_color
}
}
return { theme, enableTheme }
}
@@ -26,17 +26,16 @@
<script setup>
import { ref, computed } from 'vue'
import { useGettext } from 'vue3-gettext'
import { useTheme } from 'vuetify'
import { useAuthStore, useBusStore } from '@/stores'
import { useModoboaTheme } from '@/composables/theme'
import ConnectedView from './ConnectedView.vue'
import NavBar from '@/components/shared/NavBar.vue'
import TopMenu from '@/components/shared/TopMenu.vue'
import themeApi from '@/api/theme'
const authStore = useAuthStore()
const busStore = useBusStore()
const { $gettext } = useGettext()
const theme = useTheme()
const { enableTheme } = useModoboaTheme()
const props = defineProps({
color: {
@@ -69,41 +68,7 @@ busStore.$onAction(({ name, after }) => {
}, true)
if (props.loadTheme) {
themeApi.getTheme().then((response) => {
if (
response.data.theme_primary_color !==
theme.themes.value.light.colors.primary
) {
theme.themes.value.light.colors.primary =
response.data.theme_primary_color
}
if (
response.data.theme_primary_color_light !==
theme.themes.value.light.colors['primary-lighten-1']
) {
theme.themes.value.light.colors['primary-lighten-1'] =
response.data.theme_primary_color_light
}
if (
response.data.theme_primary_color_dark !==
theme.themes.value.light.colors['primary-darken-1']
) {
theme.themes.value.light.colors['primary-darken-1'] =
response.data.theme_primary_color_dark
}
if (
response.data.theme_secondary_color !==
theme.themes.value.light.colors.secondary
) {
theme.themes.value.light.colors.secondary =
response.data.theme_secondary_color
}
if (
response.data.theme_label_color !== theme.themes.value.light.colors.label
) {
theme.themes.value.light.colors.label = response.data.theme_label_color
}
})
await enableTheme()
}
</script>
+12 -8
View File
@@ -1,30 +1,34 @@
<script setup>
import { onMounted } from 'vue'
import { useAuthStore } from '@/stores'
import { useModoboaTheme } from '@/composables/theme'
const props = defineProps({
redirectUrl: String,
})
const authStore = useAuthStore()
const { enableTheme } = useModoboaTheme()
onMounted(() => {
authStore.completeLogin(props.redirectUrl)
})
await enableTheme()
</script>
<template>
<v-container class="fill-height" fluid>
<v-row align="center" justify="center">
<v-col cols="auto" class="text-center">
<h1 class="ma-5">{{ $gettext('Attempting to log you in.') }}</h1>
<v-progress-circular
color="primary"
indeterminate
:size="128"
:width="12"
align-self="center"
></v-progress-circular>
<h1 class="ma-5">{{ $gettext('Attempting to log you in.') }}</h1>
<v-progress-circular
color="primary"
indeterminate
:size="128"
:width="12"
align-self="center"
></v-progress-circular>
</v-col>
</v-row>
</v-container>
+27 -8
View File
@@ -37,6 +37,23 @@ from .. import signals
logger = logging.getLogger("modoboa.auth")
class ModoboaThemeMixin:
def dispatch(self, request, *args, **kwargs):
self.parameters = dict(param_tools.get_global_parameters(app="core"))
return super().dispatch(request, *args, **kwargs)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context.update(
{
"theme_primary_color": self.parameters["theme_primary_color"],
"theme_primary_color_dark": self.parameters["theme_primary_color_dark"],
}
)
return context
class LoginViewMixin:
@cached_property
def logger(self):
@@ -74,7 +91,7 @@ class LoginViewMixin:
return response
class LoginView(LoginViewMixin, auth_views.LoginView):
class LoginView(ModoboaThemeMixin, LoginViewMixin, auth_views.LoginView):
"""Login view with 2FA support."""
form_class = forms.AuthenticationForm
@@ -86,13 +103,15 @@ class LoginView(LoginViewMixin, auth_views.LoginView):
sender="login", location="loginpage"
)
announcements = [announcement[1] for announcement in announcements]
context.update({"announcements": announcements})
context.update(
{
"announcements": announcements,
}
)
return context
def check_password_hash(self, user, form):
condition = user.is_local and param_tools.get_global_parameter(
"update_scheme", raise_exception=False
)
condition = user.is_local and self.parameters.get("update_scheme")
if not condition:
return
# check if password scheme is correct
@@ -157,7 +176,7 @@ def dologout(request):
return HttpResponseRedirect(reverse("core:login"))
class PasswordResetView(auth_views.PasswordResetView):
class PasswordResetView(ModoboaThemeMixin, auth_views.PasswordResetView):
"""Custom view to override form."""
form_class = forms.PasswordResetForm
@@ -202,7 +221,7 @@ class PasswordResetView(auth_views.PasswordResetView):
return HttpResponseRedirect(reverse("password_reset_confirm_code"))
class VerifySMSCodeView(generic.FormView):
class VerifySMSCodeView(ModoboaThemeMixin, generic.FormView):
"""View to verify a code received by SMS."""
form_class = forms.VerifySMSCodeForm
@@ -254,7 +273,7 @@ class ResendSMSCodeView(generic.View):
return JsonResponse({"status": "ok"})
class TwoFactorCodeVerifyView(LoginViewMixin, generic.FormView):
class TwoFactorCodeVerifyView(ModoboaThemeMixin, LoginViewMixin, generic.FormView):
"""View to verify a 2FA code after login."""
form_class = forms.Verify2FACodeForm
+7 -20
View File
@@ -23,11 +23,6 @@ body {
padding: 20px;
}
.left-column {
background-color: #046bf8;
text-align: center;
}
.right-column {
background-color: #ffffff;
display: flex;
@@ -115,29 +110,14 @@ h2 {
transition: background-color 0.3s;
}
.btn-primary {
background-color: #007bff;
color: white;
}
.btn:hover {
background-color: #c9c9c9;
}
.btn-primary:hover {
background-color: #0056b3;
}
.footer {
margin-top: 20px;
}
.footer a {
color: #007bff;
text-decoration: none;
font-size: 14px;
}
.footer a:hover {
text-decoration: underline;
}
@@ -155,6 +135,13 @@ h2 {
margin-top: 5px;
}
.annoucement {
background-color: #e6e6e6;
padding: 20px;
border-radius: 5px;
text-align: left;
}
@media (max-width: 768px) {
.container {
flex-direction: column;
+18
View File
@@ -8,6 +8,24 @@
<title>{% trans "Welcome to Modoboa" %}</title>
<link rel="icon" type="image/x-icon" href="{% static 'css/favicon.ico' %}" />
<link rel="stylesheet" type="text/css" href="{% static 'css/offline.css' %}" />
<style>
.left-column {
background-color: {{ theme_primary_color }};
text-align: center;
}
.btn-primary {
background-color: {{ theme_primary_color }};
color: white;
}
.btn-primary:hover {
background-color: {{ theme_primary_color_dark }};
}
.footer a {
color: #{{ theme_primary_color }};
text-decoration: none;
font-size: 14px;
}
</style>
{% block extra_css %}{% endblock %}
</head>
<body>
+1 -1
View File
@@ -38,7 +38,7 @@
{% block sub_content %}
{% for announcement in announcements %}
<div>{{ announcement|safe }}</div>
<div class="annoucement">{{ announcement|safe }}</div>
{% endfor %}
{% endblock %}