tenants: add default_locale read only field, pre-hydrate in flows and read in autodetect as first choice
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
This commit is contained in:
parent
3344af72c2
commit
0a73e7ac9f
2
.vscode/settings.json
vendored
2
.vscode/settings.json
vendored
|
@ -24,7 +24,7 @@
|
|||
"*.akflow": "json"
|
||||
},
|
||||
"typescript.preferences.importModuleSpecifier": "non-relative",
|
||||
"typescript.preferences.importModuleSpecifierEnding": "js",
|
||||
"typescript.preferences.importModuleSpecifierEnding": "index",
|
||||
"typescript.tsdk": "./web/node_modules/typescript/lib",
|
||||
"typescript.enablePromptUseWorkspaceTsdk": true
|
||||
}
|
||||
|
|
|
@ -10,7 +10,9 @@
|
|||
<script>ShadyDOM = { force: !navigator.webdriver };</script>
|
||||
{% endif %}
|
||||
<script>
|
||||
window.authentik = {};
|
||||
window.authentik = {
|
||||
"locale": "{{ tenant.default_locale }}",
|
||||
};
|
||||
window.authentik.flow = {
|
||||
"layout": "{{ flow.layout }}",
|
||||
};
|
||||
|
|
|
@ -4,7 +4,7 @@ from typing import Any
|
|||
from drf_spectacular.utils import extend_schema
|
||||
from rest_framework.decorators import action
|
||||
from rest_framework.exceptions import ValidationError
|
||||
from rest_framework.fields import CharField, ListField
|
||||
from rest_framework.fields import CharField, ListField, ReadOnlyField
|
||||
from rest_framework.permissions import AllowAny
|
||||
from rest_framework.request import Request
|
||||
from rest_framework.response import Response
|
||||
|
@ -76,6 +76,8 @@ class CurrentTenantSerializer(PassiveSerializer):
|
|||
flow_unenrollment = CharField(source="flow_unenrollment.slug", required=False)
|
||||
flow_user_settings = CharField(source="flow_user_settings.slug", required=False)
|
||||
|
||||
default_locale = ReadOnlyField()
|
||||
|
||||
|
||||
class TenantViewSet(UsedByMixin, ModelViewSet):
|
||||
"""Tenant Viewset"""
|
||||
|
|
|
@ -65,6 +65,11 @@ class Tenant(models.Model):
|
|||
|
||||
attributes = models.JSONField(default=dict, blank=True)
|
||||
|
||||
@property
|
||||
def default_locale(self) -> str:
|
||||
"""Get default locale"""
|
||||
return self.attributes.get("settings", {}).get("locale", "")
|
||||
|
||||
def __str__(self) -> str:
|
||||
if self.default:
|
||||
return "Default tenant"
|
||||
|
|
|
@ -20658,10 +20658,14 @@ components:
|
|||
type: string
|
||||
flow_user_settings:
|
||||
type: string
|
||||
default_locale:
|
||||
type: string
|
||||
readOnly: true
|
||||
required:
|
||||
- branding_favicon
|
||||
- branding_logo
|
||||
- branding_title
|
||||
- default_locale
|
||||
- matched_domain
|
||||
- ui_footer_links
|
||||
DeniedActionEnum:
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { VERSION } from "@goauthentik/web/constants";
|
||||
import { MessageMiddleware } from "@goauthentik/web/elements/messages/Middleware";
|
||||
import { APIMiddleware } from "@goauthentik/web/elements/notifications/APIDrawer";
|
||||
import { activateLocale } from "@goauthentik/web/interfaces/locale";
|
||||
import { getCookie } from "@goauthentik/web/utils";
|
||||
|
||||
import {
|
||||
|
@ -34,28 +35,39 @@ export function config(): Promise<Config> {
|
|||
return globalConfigPromise;
|
||||
}
|
||||
|
||||
export function tenantSetFavicon(tenant: CurrentTenant) {
|
||||
/**
|
||||
* <link rel="icon" href="/static/dist/assets/icons/icon.png">
|
||||
* <link rel="shortcut icon" href="/static/dist/assets/icons/icon.png">
|
||||
*/
|
||||
const rels = ["icon", "shortcut icon"];
|
||||
rels.forEach((rel) => {
|
||||
let relIcon = document.head.querySelector<HTMLLinkElement>(`link[rel='${rel}']`);
|
||||
if (!relIcon) {
|
||||
relIcon = document.createElement("link");
|
||||
relIcon.rel = rel;
|
||||
document.getElementsByTagName("head")[0].appendChild(relIcon);
|
||||
}
|
||||
relIcon.href = tenant.brandingFavicon;
|
||||
});
|
||||
}
|
||||
|
||||
export function tenantSetLocale(tenant: CurrentTenant) {
|
||||
if (tenant.defaultLocale === "") {
|
||||
return;
|
||||
}
|
||||
console.debug("authentik/locale: setting locale from tenant default");
|
||||
activateLocale(tenant.defaultLocale);
|
||||
}
|
||||
|
||||
let globalTenantPromise: Promise<CurrentTenant>;
|
||||
export function tenant(): Promise<CurrentTenant> {
|
||||
if (!globalTenantPromise) {
|
||||
globalTenantPromise = new CoreApi(DEFAULT_CONFIG)
|
||||
.coreTenantsCurrentRetrieve()
|
||||
.then((tenant) => {
|
||||
/**
|
||||
* <link rel="icon" href="/static/dist/assets/icons/icon.png">
|
||||
* <link rel="shortcut icon" href="/static/dist/assets/icons/icon.png">
|
||||
*/
|
||||
const rels = ["icon", "shortcut icon"];
|
||||
rels.forEach((rel) => {
|
||||
let relIcon = document.head.querySelector<HTMLLinkElement>(
|
||||
`link[rel='${rel}']`,
|
||||
);
|
||||
if (!relIcon) {
|
||||
relIcon = document.createElement("link");
|
||||
relIcon.rel = rel;
|
||||
document.getElementsByTagName("head")[0].appendChild(relIcon);
|
||||
}
|
||||
relIcon.href = tenant.brandingFavicon;
|
||||
});
|
||||
tenantSetFavicon(tenant);
|
||||
tenantSetLocale(tenant);
|
||||
return tenant;
|
||||
});
|
||||
}
|
||||
|
|
14
web/src/api/Global.ts
Normal file
14
web/src/api/Global.ts
Normal file
|
@ -0,0 +1,14 @@
|
|||
export interface GlobalAuthentik {
|
||||
locale?: string;
|
||||
flow?: {
|
||||
layout: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface AuthentikWindow {
|
||||
authentik: GlobalAuthentik;
|
||||
}
|
||||
|
||||
export function globalAK(): GlobalAuthentik {
|
||||
return (window as unknown as AuthentikWindow).authentik;
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
import { DEFAULT_CONFIG, tenant } from "@goauthentik/web/api/Config";
|
||||
import { globalAK } from "@goauthentik/web/api/Global";
|
||||
import { configureSentry } from "@goauthentik/web/api/Sentry";
|
||||
import { WebsocketClient } from "@goauthentik/web/common/ws";
|
||||
import { EVENT_FLOW_ADVANCE, TITLE_DEFAULT } from "@goauthentik/web/constants";
|
||||
|
@ -46,14 +47,6 @@ import {
|
|||
|
||||
import { StageHost } from "./stages/base";
|
||||
|
||||
export interface FlowWindow extends Window {
|
||||
authentik: {
|
||||
flow: {
|
||||
layout: LayoutEnum;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
@customElement("ak-flow-executor")
|
||||
export class FlowExecutor extends LitElement implements StageHost {
|
||||
flowSlug?: string;
|
||||
|
@ -435,7 +428,7 @@ export class FlowExecutor extends LitElement implements StageHost {
|
|||
}
|
||||
|
||||
getLayout(): string {
|
||||
const prefilledFlow = (window as unknown as FlowWindow).authentik.flow.layout;
|
||||
const prefilledFlow = globalAK().flow?.layout || LayoutEnum.Stacked;
|
||||
if (this.challenge) {
|
||||
return this.challenge?.flowInfo?.layout || prefilledFlow;
|
||||
}
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import { globalAK } from "@goauthentik/web/api/Global";
|
||||
|
||||
import { Messages, i18n } from "@lingui/core";
|
||||
import { detect, fromNavigator, fromUrl } from "@lingui/detect-locale";
|
||||
import { t } from "@lingui/macro";
|
||||
|
@ -121,7 +123,14 @@ const DEFAULT_FALLBACK = () => "en";
|
|||
|
||||
export function autoDetectLanguage() {
|
||||
const detected =
|
||||
detect(fromUrl("locale"), fromNavigator(), DEFAULT_FALLBACK) || DEFAULT_FALLBACK();
|
||||
detect(
|
||||
() => {
|
||||
return globalAK().locale;
|
||||
},
|
||||
fromUrl("locale"),
|
||||
fromNavigator(),
|
||||
DEFAULT_FALLBACK,
|
||||
) || DEFAULT_FALLBACK();
|
||||
const locales = [detected];
|
||||
// For now we only care about the first locale part
|
||||
if (detected.includes("_")) {
|
||||
|
|
Reference in a new issue