Compare commits
26 Commits
v10.0.0-rc
...
v10.0.0-rc
Author | SHA1 | Date | |
---|---|---|---|
13dc47533c | |||
0091a888bc | |||
724b585004 | |||
c0d127e4f4 | |||
951c202fd0 | |||
a578b86715 | |||
b6b384854e | |||
dac937060d | |||
c628183773 | |||
eaacaa6966 | |||
9a09e280c9 | |||
70ac07d861 | |||
dabe372360 | |||
d8e3fdeb14 | |||
a147084458 | |||
b25e171412 | |||
60aaa03202 | |||
3392ab8385 | |||
f172b94467 | |||
ca549fe8d8 | |||
0b6f56a774 | |||
e5bcff12cb | |||
2754900f7a | |||
54f43d3331 | |||
4900200c06 | |||
6d82a74db4 |
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "keycloakify",
|
||||
"version": "10.0.0-rc.109",
|
||||
"version": "10.0.0-rc.118",
|
||||
"description": "Create Keycloak themes using React",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { containerName } from "../src/bin/shared/constants";
|
||||
import { CONTAINER_NAME } from "../src/bin/shared/constants";
|
||||
import child_process from "child_process";
|
||||
import { SemVer } from "../src/bin/tools/SemVer";
|
||||
import { join as pathJoin, relative as pathRelative } from "path";
|
||||
@ -14,7 +14,7 @@ import { is } from "tsafe/is";
|
||||
const child = child_process.spawn(
|
||||
"docker",
|
||||
[
|
||||
...["exec", containerName],
|
||||
...["exec", CONTAINER_NAME],
|
||||
...["/opt/keycloak/bin/kc.sh", "export"],
|
||||
...["--dir", "/tmp"],
|
||||
...["--realm", "myrealm"],
|
||||
@ -62,7 +62,7 @@ import { is } from "tsafe/is";
|
||||
|
||||
const keycloakMajorVersionNumber = SemVer.parse(
|
||||
child_process
|
||||
.execSync(`docker inspect --format '{{.Config.Image}}' ${containerName}`)
|
||||
.execSync(`docker inspect --format '{{.Config.Image}}' ${CONTAINER_NAME}`)
|
||||
.toString("utf8")
|
||||
.trim()
|
||||
.split(":")[1]
|
||||
@ -80,7 +80,7 @@ import { is } from "tsafe/is";
|
||||
)
|
||||
);
|
||||
|
||||
run(`docker cp ${containerName}:/tmp/myrealm-realm.json ${targetFilePath}`);
|
||||
run(`docker cp ${CONTAINER_NAME}:/tmp/myrealm-realm.json ${targetFilePath}`);
|
||||
|
||||
console.log(`${chalk.green(`✓ Exported realm to`)} ${chalk.bold(targetFilePath)}`);
|
||||
})();
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { basenameOfTheKeycloakifyResourcesDir } from "keycloakify/bin/shared/constants";
|
||||
import { BASENAME_OF_KEYCLOAKIFY_RESOURCES_DIR } from "keycloakify/bin/shared/constants";
|
||||
import { assert } from "tsafe/assert";
|
||||
|
||||
/**
|
||||
@ -17,5 +17,5 @@ export const PUBLIC_URL = (() => {
|
||||
return process.env.PUBLIC_URL;
|
||||
}
|
||||
|
||||
return `${kcContext.url.resourcesPath}/${basenameOfTheKeycloakifyResourcesDir}`;
|
||||
return `${kcContext.url.resourcesPath}/${BASENAME_OF_KEYCLOAKIFY_RESOURCES_DIR}`;
|
||||
})();
|
||||
|
@ -1,10 +1,10 @@
|
||||
import "keycloakify/tools/Object.fromEntries";
|
||||
import { resources_common, keycloak_resources } from "keycloakify/bin/shared/constants";
|
||||
import { RESOURCES_COMMON, KEYCLOAK_RESOURCES } from "keycloakify/bin/shared/constants";
|
||||
import { id } from "tsafe/id";
|
||||
import type { KcContext } from "./KcContext";
|
||||
import { BASE_URL } from "keycloakify/lib/BASE_URL";
|
||||
|
||||
const resourcesPath = `${BASE_URL}${keycloak_resources}/account/resources`;
|
||||
const resourcesPath = `${BASE_URL}${KEYCLOAK_RESOURCES}/account/resources`;
|
||||
|
||||
export const kcContextCommonMock: KcContext.Common = {
|
||||
themeVersion: "0.0.0",
|
||||
@ -13,7 +13,7 @@ export const kcContextCommonMock: KcContext.Common = {
|
||||
themeName: "my-theme-name",
|
||||
url: {
|
||||
resourcesPath,
|
||||
resourcesCommonPath: `${resourcesPath}/${resources_common}`,
|
||||
resourcesCommonPath: `${resourcesPath}/${RESOURCES_COMMON}`,
|
||||
resourceUrl: "#",
|
||||
accountUrl: "#",
|
||||
applicationsUrl: "#",
|
||||
|
@ -3,7 +3,7 @@ import { assert } from "tsafe/assert";
|
||||
import messages_defaultSet_fallbackLanguage from "./messages_defaultSet/en";
|
||||
import { fetchMessages_defaultSet } from "./messages_defaultSet";
|
||||
import type { KcContext } from "../KcContext";
|
||||
import { fallbackLanguageTag } from "keycloakify/bin/shared/constants";
|
||||
import { FALLBACK_LANGUAGE_TAG } from "keycloakify/bin/shared/constants";
|
||||
import { id } from "tsafe/id";
|
||||
|
||||
export type KcContextLike = {
|
||||
@ -104,7 +104,7 @@ export function createGetI18n<MessageKey_themeDefined extends string = never>(me
|
||||
}
|
||||
|
||||
const partialI18n: Pick<I18n, "currentLanguageTag" | "getChangeLocaleUrl" | "labelBySupportedLanguageTag"> = {
|
||||
currentLanguageTag: kcContext.locale?.currentLanguageTag ?? fallbackLanguageTag,
|
||||
currentLanguageTag: kcContext.locale?.currentLanguageTag ?? FALLBACK_LANGUAGE_TAG,
|
||||
getChangeLocaleUrl: newLanguageTag => {
|
||||
const { locale } = kcContext;
|
||||
|
||||
@ -122,7 +122,7 @@ export function createGetI18n<MessageKey_themeDefined extends string = never>(me
|
||||
const { createI18nTranslationFunctions } = createI18nTranslationFunctionsFactory<MessageKey_themeDefined>({
|
||||
messages_themeDefined:
|
||||
messagesByLanguageTag_themeDefined[partialI18n.currentLanguageTag] ??
|
||||
messagesByLanguageTag_themeDefined[fallbackLanguageTag] ??
|
||||
messagesByLanguageTag_themeDefined[FALLBACK_LANGUAGE_TAG] ??
|
||||
(() => {
|
||||
const firstLanguageTag = Object.keys(messagesByLanguageTag_themeDefined)[0];
|
||||
if (firstLanguageTag === undefined) {
|
||||
@ -133,7 +133,7 @@ export function createGetI18n<MessageKey_themeDefined extends string = never>(me
|
||||
messages_fromKcServer: kcContext["x-keycloakify"].messages
|
||||
});
|
||||
|
||||
const isCurrentLanguageFallbackLanguage = partialI18n.currentLanguageTag === fallbackLanguageTag;
|
||||
const isCurrentLanguageFallbackLanguage = partialI18n.currentLanguageTag === FALLBACK_LANGUAGE_TAG;
|
||||
|
||||
const result: Result = {
|
||||
i18n: {
|
||||
|
@ -3,7 +3,7 @@ import { createGetI18n, type GenericI18n_noJsx, type KcContextLike, type Message
|
||||
import { GenericI18n } from "./GenericI18n";
|
||||
import { Reflect } from "tsafe/Reflect";
|
||||
|
||||
export function createUseI18n<MessageKey_themeDefined extends string = never>(messageBundle: {
|
||||
export function createUseI18n<MessageKey_themeDefined extends string = never>(messagesByLanguageTag: {
|
||||
[languageTag: string]: { [key in MessageKey_themeDefined]: string };
|
||||
}) {
|
||||
type MessageKey = MessageKey_defaultSet | MessageKey_themeDefined;
|
||||
@ -13,10 +13,11 @@ export function createUseI18n<MessageKey_themeDefined extends string = never>(me
|
||||
const { withJsx } = (() => {
|
||||
const cache = new WeakMap<GenericI18n_noJsx<MessageKey>, GenericI18n<MessageKey>>();
|
||||
|
||||
function renderHtmlString(htmlString: string): JSX.Element {
|
||||
function renderHtmlString(params: { htmlString: string; msgKey: string }): JSX.Element {
|
||||
const { htmlString, msgKey } = params;
|
||||
return (
|
||||
<div
|
||||
style={{ display: "inline-block" }}
|
||||
data-kc-msg={msgKey}
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: htmlString
|
||||
}}
|
||||
@ -37,8 +38,8 @@ export function createUseI18n<MessageKey_themeDefined extends string = never>(me
|
||||
|
||||
const i18n: I18n = {
|
||||
...i18n_noJsx,
|
||||
msg: (...args) => renderHtmlString(i18n_noJsx.msgStr(...args)),
|
||||
advancedMsg: (...args) => renderHtmlString(i18n_noJsx.advancedMsgStr(...args))
|
||||
msg: (msgKey, ...args) => renderHtmlString({ htmlString: i18n_noJsx.msgStr(msgKey, ...args), msgKey }),
|
||||
advancedMsg: (msgKey, ...args) => renderHtmlString({ htmlString: i18n_noJsx.advancedMsgStr(msgKey, ...args), msgKey })
|
||||
};
|
||||
|
||||
cache.set(i18n_noJsx, i18n);
|
||||
@ -49,7 +50,20 @@ export function createUseI18n<MessageKey_themeDefined extends string = never>(me
|
||||
return { withJsx };
|
||||
})();
|
||||
|
||||
const { getI18n } = createGetI18n(messageBundle);
|
||||
add_style: {
|
||||
const attributeName = "data-kc-i18n";
|
||||
|
||||
// Check if already exists in head
|
||||
if (document.querySelector(`style[${attributeName}]`) !== null) {
|
||||
break add_style;
|
||||
}
|
||||
|
||||
const styleElement = document.createElement("style");
|
||||
styleElement.attributes.setNamedItem(document.createAttribute(attributeName));
|
||||
(styleElement.textContent = `[data-kc-msg] { display: inline-block; }`), document.head.prepend(styleElement);
|
||||
}
|
||||
|
||||
const { getI18n } = createGetI18n(messagesByLanguageTag);
|
||||
|
||||
function useI18n(params: { kcContext: KcContextLike }): { i18n: I18n } {
|
||||
const { kcContext } = params;
|
||||
|
@ -1,11 +1,11 @@
|
||||
import { getThisCodebaseRootDirPath } from "./tools/getThisCodebaseRootDirPath";
|
||||
import cliSelect from "cli-select";
|
||||
import {
|
||||
loginThemePageIds,
|
||||
accountThemePageIds,
|
||||
LOGIN_THEME_PAGE_IDS,
|
||||
ACCOUNT_THEME_PAGE_IDS,
|
||||
type LoginThemePageId,
|
||||
type AccountThemePageId,
|
||||
themeTypes,
|
||||
THEME_TYPES,
|
||||
type ThemeType
|
||||
} from "./shared/constants";
|
||||
import { capitalize } from "tsafe/capitalize";
|
||||
@ -27,7 +27,7 @@ export async function command(params: { cliCommandOptions: CliCommandOptions })
|
||||
console.log(chalk.cyan("Theme type:"));
|
||||
|
||||
const { value: themeType } = await cliSelect<ThemeType>({
|
||||
values: [...themeTypes]
|
||||
values: [...THEME_TYPES]
|
||||
}).catch(() => {
|
||||
process.exit(-1);
|
||||
});
|
||||
@ -40,9 +40,9 @@ export async function command(params: { cliCommandOptions: CliCommandOptions })
|
||||
values: (() => {
|
||||
switch (themeType) {
|
||||
case "login":
|
||||
return [...loginThemePageIds];
|
||||
return [...LOGIN_THEME_PAGE_IDS];
|
||||
case "account":
|
||||
return [...accountThemePageIds];
|
||||
return [...ACCOUNT_THEME_PAGE_IDS];
|
||||
}
|
||||
assert<Equals<typeof themeType, never>>(false);
|
||||
})()
|
||||
|
@ -3,11 +3,11 @@
|
||||
import { getThisCodebaseRootDirPath } from "./tools/getThisCodebaseRootDirPath";
|
||||
import cliSelect from "cli-select";
|
||||
import {
|
||||
loginThemePageIds,
|
||||
accountThemePageIds,
|
||||
LOGIN_THEME_PAGE_IDS,
|
||||
ACCOUNT_THEME_PAGE_IDS,
|
||||
type LoginThemePageId,
|
||||
type AccountThemePageId,
|
||||
themeTypes,
|
||||
THEME_TYPES,
|
||||
type ThemeType
|
||||
} from "./shared/constants";
|
||||
import { capitalize } from "tsafe/capitalize";
|
||||
@ -29,7 +29,7 @@ export async function command(params: { cliCommandOptions: CliCommandOptions })
|
||||
console.log(chalk.cyan("Theme type:"));
|
||||
|
||||
const { value: themeType } = await cliSelect<ThemeType>({
|
||||
values: [...themeTypes]
|
||||
values: [...THEME_TYPES]
|
||||
}).catch(() => {
|
||||
process.exit(-1);
|
||||
});
|
||||
@ -54,10 +54,10 @@ export async function command(params: { cliCommandOptions: CliCommandOptions })
|
||||
return [
|
||||
templateValue,
|
||||
userProfileFormFieldsValue,
|
||||
...loginThemePageIds
|
||||
...LOGIN_THEME_PAGE_IDS
|
||||
];
|
||||
case "account":
|
||||
return [templateValue, ...accountThemePageIds];
|
||||
return [templateValue, ...ACCOUNT_THEME_PAGE_IDS];
|
||||
}
|
||||
assert<Equals<typeof themeType, never>>(false);
|
||||
})()
|
||||
|
@ -7,7 +7,7 @@ import { join as pathJoin, dirname as pathDirname } from "path";
|
||||
import { transformCodebase } from "../../tools/transformCodebase";
|
||||
import type { BuildContext } from "../../shared/buildContext";
|
||||
import * as fs from "fs/promises";
|
||||
import { accountV1ThemeName } from "../../shared/constants";
|
||||
import { ACCOUNT_V1_THEME_NAME } from "../../shared/constants";
|
||||
import {
|
||||
generatePom,
|
||||
BuildContextLike as BuildContextLike_generatePom
|
||||
@ -24,6 +24,7 @@ export type BuildContextLike = BuildContextLike_generatePom & {
|
||||
artifactId: string;
|
||||
themeVersion: string;
|
||||
cacheDirPath: string;
|
||||
recordIsImplementedByThemeType: BuildContext["recordIsImplementedByThemeType"];
|
||||
};
|
||||
|
||||
assert<BuildContext extends BuildContextLike ? true : false>();
|
||||
@ -45,20 +46,21 @@ export async function buildJar(params: {
|
||||
buildContext
|
||||
} = params;
|
||||
|
||||
const keycloakifyBuildTmpDirPath = pathJoin(
|
||||
const keycloakifyBuildCacheDirPath = pathJoin(
|
||||
buildContext.cacheDirPath,
|
||||
"maven",
|
||||
jarFileBasename.replace(".jar", "")
|
||||
);
|
||||
|
||||
rmSync(keycloakifyBuildTmpDirPath, { recursive: true, force: true });
|
||||
|
||||
const tmpResourcesDirPath = pathJoin(
|
||||
keycloakifyBuildTmpDirPath,
|
||||
keycloakifyBuildCacheDirPath,
|
||||
"src",
|
||||
"main",
|
||||
"resources"
|
||||
);
|
||||
|
||||
rmSync(tmpResourcesDirPath, { recursive: true, force: true });
|
||||
|
||||
transformCodebase({
|
||||
srcDirPath: resourcesDirPath,
|
||||
destDirPath: tmpResourcesDirPath,
|
||||
@ -73,7 +75,7 @@ export async function buildJar(params: {
|
||||
|
||||
if (
|
||||
isInside({
|
||||
dirPath: pathJoin("theme", accountV1ThemeName),
|
||||
dirPath: pathJoin("theme", ACCOUNT_V1_THEME_NAME),
|
||||
filePath: fileRelativePath
|
||||
})
|
||||
) {
|
||||
@ -89,7 +91,7 @@ export async function buildJar(params: {
|
||||
sourceCode
|
||||
.toString("utf8")
|
||||
.replace(
|
||||
`parent=${accountV1ThemeName}`,
|
||||
`parent=${ACCOUNT_V1_THEME_NAME}`,
|
||||
"parent=keycloak"
|
||||
),
|
||||
"utf8"
|
||||
@ -124,7 +126,7 @@ export async function buildJar(params: {
|
||||
assert(metaInfKeycloakTheme !== undefined);
|
||||
|
||||
metaInfKeycloakTheme.themes = metaInfKeycloakTheme.themes.filter(
|
||||
({ name }) => name !== accountV1ThemeName
|
||||
({ name }) => name !== ACCOUNT_V1_THEME_NAME
|
||||
);
|
||||
|
||||
return metaInfKeycloakTheme;
|
||||
@ -133,6 +135,10 @@ export async function buildJar(params: {
|
||||
}
|
||||
|
||||
route_legacy_pages: {
|
||||
if (!buildContext.recordIsImplementedByThemeType.login) {
|
||||
break route_legacy_pages;
|
||||
}
|
||||
|
||||
// NOTE: If there's no account theme there is no special target for keycloak 24 and up so we create
|
||||
// the pages anyway. If there is an account pages, since we know that account-v1 is only support keycloak
|
||||
// 24 in version 0.4 and up, we can safely break the route for legacy pages.
|
||||
@ -155,10 +161,7 @@ export async function buildJar(params: {
|
||||
(["register.ftl", "login-update-profile.ftl"] as const).forEach(pageId =>
|
||||
buildContext.themeNames.map(themeName => {
|
||||
const ftlFilePath = pathJoin(
|
||||
keycloakifyBuildTmpDirPath,
|
||||
"src",
|
||||
"main",
|
||||
"resources",
|
||||
tmpResourcesDirPath,
|
||||
"theme",
|
||||
themeName,
|
||||
"login",
|
||||
@ -167,7 +170,7 @@ export async function buildJar(params: {
|
||||
|
||||
const ftlFileContent = readFileSync(ftlFilePath).toString("utf8");
|
||||
|
||||
const realPageId = (() => {
|
||||
const ftlFileBasename = (() => {
|
||||
switch (pageId) {
|
||||
case "register.ftl":
|
||||
return "register-user-profile.ftl";
|
||||
@ -178,14 +181,14 @@ export async function buildJar(params: {
|
||||
})();
|
||||
|
||||
const modifiedFtlFileContent = ftlFileContent.replace(
|
||||
`kcContext.pageId = "\${pageId}";`,
|
||||
`kcContext.pageId = "${pageId}"; kcContext.realPageId = "${realPageId}";`
|
||||
`"ftlTemplateFileName": "${pageId}"`,
|
||||
`"ftlTemplateFileName": "${ftlFileBasename}"`
|
||||
);
|
||||
|
||||
assert(modifiedFtlFileContent !== ftlFileContent);
|
||||
|
||||
fs.writeFile(
|
||||
pathJoin(pathDirname(ftlFilePath), realPageId),
|
||||
pathJoin(pathDirname(ftlFilePath), ftlFileBasename),
|
||||
Buffer.from(modifiedFtlFileContent, "utf8")
|
||||
);
|
||||
})
|
||||
@ -200,15 +203,15 @@ export async function buildJar(params: {
|
||||
});
|
||||
|
||||
await fs.writeFile(
|
||||
pathJoin(keycloakifyBuildTmpDirPath, "pom.xml"),
|
||||
pathJoin(keycloakifyBuildCacheDirPath, "pom.xml"),
|
||||
Buffer.from(pomFileCode, "utf8")
|
||||
);
|
||||
}
|
||||
|
||||
await new Promise<void>((resolve, reject) =>
|
||||
child_process.exec(
|
||||
`mvn clean install -Dmaven.repo.local="${pathJoin(keycloakifyBuildTmpDirPath, ".m2")}"`,
|
||||
{ cwd: keycloakifyBuildTmpDirPath },
|
||||
`mvn install -Dmaven.repo.local="${pathJoin(keycloakifyBuildCacheDirPath, ".m2")}"`,
|
||||
{ cwd: keycloakifyBuildCacheDirPath },
|
||||
error => {
|
||||
if (error !== null) {
|
||||
console.error(
|
||||
@ -233,12 +236,10 @@ export async function buildJar(params: {
|
||||
|
||||
await fs.rename(
|
||||
pathJoin(
|
||||
keycloakifyBuildTmpDirPath,
|
||||
keycloakifyBuildCacheDirPath,
|
||||
"target",
|
||||
`${buildContext.artifactId}-${buildContext.themeVersion}.jar`
|
||||
),
|
||||
pathJoin(buildContext.keycloakifyBuildDirPath, jarFileBasename)
|
||||
);
|
||||
|
||||
rmSync(keycloakifyBuildTmpDirPath, { recursive: true });
|
||||
}
|
||||
|
@ -13,8 +13,8 @@ import type { BuildContext } from "../../shared/buildContext";
|
||||
import { assert } from "tsafe/assert";
|
||||
import {
|
||||
type ThemeType,
|
||||
basenameOfTheKeycloakifyResourcesDir,
|
||||
resources_common
|
||||
BASENAME_OF_KEYCLOAKIFY_RESOURCES_DIR,
|
||||
RESOURCES_COMMON
|
||||
} from "../../shared/constants";
|
||||
import { getThisCodebaseRootDirPath } from "../../tools/getThisCodebaseRootDirPath";
|
||||
|
||||
@ -34,7 +34,6 @@ export function generateFtlFilesCodeFactory(params: {
|
||||
keycloakifyVersion: string;
|
||||
themeType: ThemeType;
|
||||
fieldNames: string[];
|
||||
isAccountV3: boolean;
|
||||
}) {
|
||||
const {
|
||||
themeName,
|
||||
@ -42,8 +41,7 @@ export function generateFtlFilesCodeFactory(params: {
|
||||
buildContext,
|
||||
keycloakifyVersion,
|
||||
themeType,
|
||||
fieldNames,
|
||||
isAccountV3
|
||||
fieldNames
|
||||
} = params;
|
||||
|
||||
const $ = cheerio.load(indexHtmlCode);
|
||||
@ -70,8 +68,7 @@ export function generateFtlFilesCodeFactory(params: {
|
||||
const { fixedCssCode } = replaceImportsInCssCode({
|
||||
cssCode,
|
||||
cssFileRelativeDirPath: undefined,
|
||||
buildContext,
|
||||
isAccountV3
|
||||
buildContext
|
||||
});
|
||||
|
||||
$(element).text(fixedCssCode);
|
||||
@ -96,7 +93,7 @@ export function generateFtlFilesCodeFactory(params: {
|
||||
new RegExp(
|
||||
`^${(buildContext.urlPathname ?? "/").replace(/\//g, "\\/")}`
|
||||
),
|
||||
`\${${!isAccountV3 ? "url.resourcesPath" : "resourceUrl"}}/${basenameOfTheKeycloakifyResourcesDir}/`
|
||||
`\${xKeycloakify.resourcesPath}/${BASENAME_OF_KEYCLOAKIFY_RESOURCES_DIR}/`
|
||||
)
|
||||
);
|
||||
})
|
||||
@ -116,19 +113,17 @@ export function generateFtlFilesCodeFactory(params: {
|
||||
)
|
||||
)
|
||||
.toString("utf8")
|
||||
.replace("{{themeType}}", themeType)
|
||||
.replace("{{themeName}}", themeName)
|
||||
.replace("{{keycloakifyVersion}}", keycloakifyVersion)
|
||||
.replace("{{themeVersion}}", buildContext.themeVersion)
|
||||
.replace("{{fieldNames}}", fieldNames.map(name => `"${name}"`).join(", "))
|
||||
.replace("{{RESOURCES_COMMON}}", RESOURCES_COMMON)
|
||||
.replace(
|
||||
"FIELD_NAMES_eKsIY4ZsZ4xeM",
|
||||
fieldNames.map(name => `"${name}"`).join(", ")
|
||||
)
|
||||
.replace("KEYCLOAKIFY_VERSION_xEdKd3xEdr", keycloakifyVersion)
|
||||
.replace("KEYCLOAKIFY_THEME_VERSION_sIgKd3xEdr3dx", buildContext.themeVersion)
|
||||
.replace("KEYCLOAKIFY_THEME_TYPE_dExKd3xEdr", themeType)
|
||||
.replace("KEYCLOAKIFY_THEME_NAME_cXxKd3xEer", themeName)
|
||||
.replace("RESOURCES_COMMON_cLsLsMrtDkpVv", resources_common)
|
||||
.replace(
|
||||
"USER_DEFINED_EXCLUSIONS_eKsaY4ZsZ4eMr2",
|
||||
"{{userDefinedExclusions}}",
|
||||
buildContext.kcContextExclusionsFtlCode ?? ""
|
||||
);
|
||||
|
||||
const ftlObjectToJsCodeDeclaringAnObjectPlaceholder =
|
||||
'{ "x": "vIdLqMeOed9sdLdIdOxdK0d" }';
|
||||
|
||||
@ -173,7 +168,8 @@ export function generateFtlFilesCodeFactory(params: {
|
||||
Object.entries({
|
||||
[ftlObjectToJsCodeDeclaringAnObjectPlaceholder]:
|
||||
kcContextDeclarationTemplateFtl,
|
||||
PAGE_ID_xIgLsPgGId9D8e: pageId
|
||||
"{{pageId}}": pageId,
|
||||
"{{ftlTemplateFileName}}": pageId
|
||||
}).map(
|
||||
([searchValue, replaceValue]) =>
|
||||
(ftlCode = ftlCode.replace(searchValue, replaceValue))
|
||||
|
@ -1,22 +1,48 @@
|
||||
<#assign pageId="PAGE_ID_xIgLsPgGId9D8e">
|
||||
<#assign themeType="KEYCLOAKIFY_THEME_TYPE_dExKd3xEdr">
|
||||
<#assign xKeycloakifyMessages = {}>
|
||||
<#assign debugMessage="">
|
||||
<#assign xKeycloakify={
|
||||
"messages": {},
|
||||
"pageId": "{{pageId}}",
|
||||
"ftlTemplateFileName": "{{ftlTemplateFileName}}",
|
||||
"themeType": "{{themeType}}",
|
||||
"themeName": "{{themeName}}",
|
||||
"keycloakifyVersion": "{{keycloakifyVersion}}",
|
||||
"themeVersion": "{{themeVersion}}",
|
||||
"resourcesPath": ""
|
||||
}>
|
||||
|
||||
const kcContext = ${ftl_object_to_js_code_declaring_an_object(.data_model, [])?no_esc};
|
||||
<#if url?? && url?is_hash && url.resourcesPath?? && url.resourcesPath?is_string>
|
||||
<#assign xKeycloakify = xKeycloakify + { "resourcesPath": url.resourcesPath }>
|
||||
</#if>
|
||||
<#if resourceUrl?? && resourceUrl?is_string>
|
||||
<#assign xKeycloakify = xKeycloakify + { "resourcesPath": resourceUrl }>
|
||||
</#if>
|
||||
|
||||
const kcContext = ${toJsDeclarationString(.data_model, [])?no_esc};
|
||||
kcContext.keycloakifyVersion = "${xKeycloakify.keycloakifyVersion}";
|
||||
kcContext.themeVersion = "${xKeycloakify.themeVersion}";
|
||||
kcContext.themeType = "${xKeycloakify.themeType}";
|
||||
kcContext.themeName = "${xKeycloakify.themeName}";
|
||||
kcContext.pageId = "${xKeycloakify.pageId}";
|
||||
kcContext.ftlTemplateFileName = "${xKeycloakify.ftlTemplateFileName}";
|
||||
|
||||
<@addNonAutomaticallyGatherableMessagesToXKeycloakifyMessages />
|
||||
|
||||
console.log(`${debugMessage}`);
|
||||
|
||||
kcContext["x-keycloakify"] = {};
|
||||
|
||||
kcContext["x-keycloakify"].resourcesPath = "${xKeycloakify.resourcesPath}";
|
||||
|
||||
{
|
||||
var messages = {};
|
||||
<#list xKeycloakifyMessages as key, resolvedMsg>
|
||||
messages["${key}"] = decodeHtmlEntities("${resolvedMsg?js_string}");
|
||||
</#list>
|
||||
kcContext["x-keycloakify"].messages = messages;
|
||||
var messages = {};
|
||||
<#list xKeycloakify.messages as key, resolvedMsg>
|
||||
messages["${key}"] = decodeHtmlEntities("${resolvedMsg?js_string}");
|
||||
</#list>
|
||||
kcContext["x-keycloakify"].messages = messages;
|
||||
}
|
||||
|
||||
if(
|
||||
kcContext.url instanceof Object &&
|
||||
typeof kcContext.url.resourcesPath === "string"
|
||||
){
|
||||
kcContext.url.resourcesCommonPath = kcContext.url.resourcesPath + "/{{RESOURCES_COMMON}}";
|
||||
}
|
||||
|
||||
if( kcContext.messagesPerField ){
|
||||
@ -44,23 +70,6 @@ if( kcContext.messagesPerField ){
|
||||
}
|
||||
};
|
||||
}
|
||||
kcContext.keycloakifyVersion = "KEYCLOAKIFY_VERSION_xEdKd3xEdr";
|
||||
kcContext.themeVersion = "KEYCLOAKIFY_THEME_VERSION_sIgKd3xEdr3dx";
|
||||
kcContext.themeType = "${themeType}";
|
||||
kcContext.themeName = "KEYCLOAKIFY_THEME_NAME_cXxKd3xEer";
|
||||
kcContext.pageId = "${pageId}";
|
||||
if( kcContext.url && kcContext.url.resourcesPath ){
|
||||
kcContext.url.resourcesCommonPath = kcContext.url.resourcesPath + "/" + "RESOURCES_COMMON_cLsLsMrtDkpVv";
|
||||
}
|
||||
if( kcContext.resourceUrl && !kcContext.url ){
|
||||
Object.defineProperty(kcContext, "url", {
|
||||
value: {
|
||||
resourcesPath: kcContext.resourceUrl
|
||||
},
|
||||
enumerable: false
|
||||
});
|
||||
}
|
||||
|
||||
attributes_to_attributesByName: {
|
||||
if( !kcContext.profile ){
|
||||
break attributes_to_attributesByName;
|
||||
@ -86,451 +95,453 @@ function decodeHtmlEntities(htmlStr){
|
||||
return element.value;
|
||||
}
|
||||
|
||||
<#function ftl_object_to_js_code_declaring_an_object object path>
|
||||
<#function toJsDeclarationString object path>
|
||||
<#local isHash = -1>
|
||||
<#attempt>
|
||||
<#local isHash = object?is_hash || object?is_hash_ex>
|
||||
<#recover>
|
||||
<#return "ABORT: Can't evaluate if " + path?join(".") + " is a hash">
|
||||
</#attempt>
|
||||
|
||||
<#if isHash>
|
||||
<#if path?size gt 10>
|
||||
<#return "ABORT: Too many recursive calls, path: " + path?join(".")>
|
||||
</#if>
|
||||
<#local keys = -1>
|
||||
|
||||
<#local isHash = "">
|
||||
<#attempt>
|
||||
<#local isHash = object?is_hash || object?is_hash_ex>
|
||||
<#local keys = object?keys>
|
||||
<#recover>
|
||||
<#return "ABORT: Can't evaluate if " + path?join(".") + " is hash">
|
||||
<#return "ABORT: We can't list keys on object">
|
||||
</#attempt>
|
||||
|
||||
<#if isHash>
|
||||
<#local outSeq = []>
|
||||
|
||||
<#if path?size gt 10>
|
||||
<#return "ABORT: Too many recursive calls, path: " + path?join(".")>
|
||||
<#list keys as key>
|
||||
<#if ["class","declaredConstructors","superclass","declaringClass" ]?seq_contains(key) >
|
||||
<#continue>
|
||||
</#if>
|
||||
|
||||
<#local keys = "">
|
||||
<#if (
|
||||
areSamePath(path, ["url"]) &&
|
||||
["loginUpdatePasswordUrl", "loginUpdateProfileUrl", "loginUsernameReminderUrl", "loginUpdateTotpUrl"]?seq_contains(key)
|
||||
) || (
|
||||
key == "updateProfileCtx" &&
|
||||
areSamePath(path, [])
|
||||
) || (
|
||||
<#-- https://github.com/keycloakify/keycloakify/pull/65#issuecomment-991896344 (reports with saml-post-form.ftl) -->
|
||||
<#-- https://github.com/keycloakify/keycloakify/issues/91#issue-1212319466 (reports with error.ftl and Kc18) -->
|
||||
<#-- https://github.com/keycloakify/keycloakify/issues/109#issuecomment-1134610163 -->
|
||||
<#-- https://github.com/keycloakify/keycloakify/issues/357 -->
|
||||
<#-- https://github.com/keycloakify/keycloakify/discussions/406#discussioncomment-7514787 -->
|
||||
key == "loginAction" &&
|
||||
areSamePath(path, ["url"]) &&
|
||||
["saml-post-form.ftl", "error.ftl", "info.ftl", "login-oauth-grant.ftl", "logout-confirm.ftl", "login-oauth2-device-verify-user-code.ftl"]?seq_contains(xKeycloakify.pageId) &&
|
||||
!(auth?has_content && auth.showTryAnotherWayLink())
|
||||
) || (
|
||||
<#-- https://github.com/keycloakify/keycloakify/issues/362 -->
|
||||
["secretData", "value"]?seq_contains(key) &&
|
||||
areSamePath(path, [ "totp", "otpCredentials", "*" ])
|
||||
) || (
|
||||
["contextData", "idpConfig", "idp", "authenticationSession"]?seq_contains(key) &&
|
||||
areSamePath(path, ["brokerContext"]) &&
|
||||
["login-idp-link-confirm.ftl", "login-idp-link-email.ftl" ]?seq_contains(xKeycloakify.pageId)
|
||||
) || (
|
||||
key == "identityProviderBrokerCtx" &&
|
||||
areSamePath(path, []) &&
|
||||
["login-idp-link-confirm.ftl", "login-idp-link-email.ftl" ]?seq_contains(xKeycloakify.pageId)
|
||||
) || (
|
||||
["masterAdminClient", "delegateForUpdate", "defaultRole"]?seq_contains(key) &&
|
||||
areSamePath(path, ["realm"])
|
||||
) || (
|
||||
xKeycloakify.pageId == "error.ftl" &&
|
||||
areSamePath(path, ["realm"]) &&
|
||||
!["name", "displayName", "displayNameHtml", "internationalizationEnabled", "registrationEmailAsUsername" ]?seq_contains(key)
|
||||
) || (
|
||||
xKeycloakify.pageId == "applications.ftl" &&
|
||||
(
|
||||
key == "realm" ||
|
||||
key == "container"
|
||||
) &&
|
||||
isSubpath(path, ["applications", "applications"])
|
||||
) || (
|
||||
key == "delegateForUpdate" &&
|
||||
areSamePath(path, ["user"])
|
||||
) || (
|
||||
<#-- Security audit forwarded by Garth (Gmail) -->
|
||||
key == "saml.signing.private.key" &&
|
||||
areSamePath(path, ["client", "attributes"])
|
||||
) || (
|
||||
<#-- See: https://github.com/keycloakify/keycloakify/issues/534 -->
|
||||
key == "password" &&
|
||||
areSamePath(path, ["login"])
|
||||
) || (
|
||||
<#-- Remove realmAttributes added by https://github.com/jcputney/keycloak-theme-additional-info-extension for peace of mind. -->
|
||||
key == "realmAttributes" &&
|
||||
areSamePath(path, [])
|
||||
) || (
|
||||
<#-- attributesByName adds a lot of noise to the output and is not needed, we already have profile.attributes -->
|
||||
key == "attributesByName" &&
|
||||
areSamePath(path, ["profile"])
|
||||
) || (
|
||||
<#-- We already have the attributes in profile speedup the rendering by filtering it out from the register object -->
|
||||
(key == "attributes" || key == "attributesByName") &&
|
||||
areSamePath(path, ["register"])
|
||||
) || (
|
||||
areSamePath(path, ["properties"]) &&
|
||||
(
|
||||
key?starts_with("kc") ||
|
||||
key == "locales" ||
|
||||
key == "import" ||
|
||||
key == "parent" ||
|
||||
key == "meta" ||
|
||||
key == "stylesCommon" ||
|
||||
key == "styles" ||
|
||||
key == "accountResourceProvider"
|
||||
)
|
||||
) || (
|
||||
key == "execution" &&
|
||||
areSamePath(path, [])
|
||||
) || (
|
||||
key == "entity" &&
|
||||
areSamePath(path, ["user"])
|
||||
) || (
|
||||
key == "attributes" &&
|
||||
areSamePath(path, ["realm"])
|
||||
)
|
||||
>
|
||||
<#-- <#local outSeq += ["/*" + path?join(".") + "." + key + " excluded*/"]> -->
|
||||
<#continue>
|
||||
</#if>
|
||||
|
||||
<#-- https://github.com/keycloakify/keycloakify/discussions/406 -->
|
||||
<#if (
|
||||
key == "attemptedUsername" &&
|
||||
areSamePath(path, ["auth"]) &&
|
||||
[
|
||||
"register.ftl", "terms.ftl", "info.ftl", "login.ftl",
|
||||
"login-update-password.ftl", "login-oauth2-device-verify-user-code.ftl"
|
||||
]?seq_contains(xKeycloakify.pageId)
|
||||
)>
|
||||
<#attempt>
|
||||
<#-- https://github.com/keycloak/keycloak/blob/3a2bf0c04bcde185e497aaa32d0bb7ab7520cf4a/themes/src/main/resources/theme/base/login/template.ftl#L63 -->
|
||||
<#if !(auth?has_content && auth.showUsername() && !auth.showResetCredentials())>
|
||||
<#local outSeq += ["/*" + path?join(".") + "." + key + " excluded*/"]>
|
||||
<#continue>
|
||||
</#if>
|
||||
<#recover>
|
||||
<#local outSeq += ["/*Accessing attemptedUsername throwed an exception */"]>
|
||||
</#attempt>
|
||||
</#if>
|
||||
|
||||
{{userDefinedExclusions}}
|
||||
|
||||
<#attempt>
|
||||
<#local keys = object?keys>
|
||||
<#if !object[key]??>
|
||||
<#continue>
|
||||
</#if>
|
||||
<#recover>
|
||||
<#return "ABORT: We can't list keys on this object">
|
||||
<#local outSeq += ["/*Couldn't test if '" + key + "' is available on this object*/"]>
|
||||
<#continue>
|
||||
</#attempt>
|
||||
|
||||
<#local out_seq = []>
|
||||
<#local propertyValue = -1>
|
||||
|
||||
<#list keys as key>
|
||||
<#attempt>
|
||||
<#local propertyValue = object[key]>
|
||||
<#recover>
|
||||
<#local outSeq += ["/*Couldn't dereference '" + key + "' on this object*/"]>
|
||||
<#continue>
|
||||
</#attempt>
|
||||
|
||||
<#if ["class","declaredConstructors","superclass","declaringClass" ]?seq_contains(key) >
|
||||
<#continue>
|
||||
<#local recOut = toJsDeclarationString(propertyValue, path + [ key ])>
|
||||
|
||||
<#if recOut?starts_with("ABORT:")>
|
||||
|
||||
<#local errorMessage = recOut?remove_beginning("ABORT:")>
|
||||
|
||||
<#if errorMessage != " It's a method" >
|
||||
<#local outSeq += ["/*" + key + ": " + errorMessage + "*/"]>
|
||||
</#if>
|
||||
|
||||
<#if
|
||||
(
|
||||
["loginUpdatePasswordUrl", "loginUpdateProfileUrl", "loginUsernameReminderUrl", "loginUpdateTotpUrl"]?seq_contains(key) &&
|
||||
are_same_path(path, ["url"])
|
||||
) || (
|
||||
key == "updateProfileCtx" &&
|
||||
are_same_path(path, [])
|
||||
) || (
|
||||
<#-- https://github.com/keycloakify/keycloakify/pull/65#issuecomment-991896344 (reports with saml-post-form.ftl) -->
|
||||
<#-- https://github.com/keycloakify/keycloakify/issues/91#issue-1212319466 (reports with error.ftl and Kc18) -->
|
||||
<#-- https://github.com/keycloakify/keycloakify/issues/109#issuecomment-1134610163 -->
|
||||
<#-- https://github.com/keycloakify/keycloakify/issues/357 -->
|
||||
<#-- https://github.com/keycloakify/keycloakify/discussions/406#discussioncomment-7514787 -->
|
||||
key == "loginAction" &&
|
||||
are_same_path(path, ["url"]) &&
|
||||
["saml-post-form.ftl", "error.ftl", "info.ftl", "login-oauth-grant.ftl", "logout-confirm.ftl", "login-oauth2-device-verify-user-code.ftl"]?seq_contains(pageId) &&
|
||||
!(auth?has_content && auth.showTryAnotherWayLink())
|
||||
) || (
|
||||
<#-- https://github.com/keycloakify/keycloakify/issues/362 -->
|
||||
["secretData", "value"]?seq_contains(key) &&
|
||||
are_same_path(path, [ "totp", "otpCredentials", "*" ])
|
||||
) || (
|
||||
["contextData", "idpConfig", "idp", "authenticationSession"]?seq_contains(key) &&
|
||||
are_same_path(path, ["brokerContext"]) &&
|
||||
["login-idp-link-confirm.ftl", "login-idp-link-email.ftl" ]?seq_contains(pageId)
|
||||
) || (
|
||||
key == "identityProviderBrokerCtx" &&
|
||||
are_same_path(path, []) &&
|
||||
["login-idp-link-confirm.ftl", "login-idp-link-email.ftl" ]?seq_contains(pageId)
|
||||
) || (
|
||||
["masterAdminClient", "delegateForUpdate", "defaultRole"]?seq_contains(key) &&
|
||||
are_same_path(path, ["realm"])
|
||||
) || (
|
||||
"error.ftl" == pageId &&
|
||||
are_same_path(path, ["realm"]) &&
|
||||
!["name", "displayName", "displayNameHtml", "internationalizationEnabled", "registrationEmailAsUsername" ]?seq_contains(key)
|
||||
) || (
|
||||
"applications.ftl" == pageId &&
|
||||
(
|
||||
key == "realm" ||
|
||||
key == "container"
|
||||
) &&
|
||||
is_subpath(path, ["applications", "applications"])
|
||||
) || (
|
||||
key == "delegateForUpdate" &&
|
||||
are_same_path(path, ["user"])
|
||||
) || (
|
||||
<#-- Security audit forwarded by Garth (Gmail) -->
|
||||
key == "saml.signing.private.key" &&
|
||||
are_same_path(path, ["client", "attributes"])
|
||||
) || (
|
||||
<#-- See: https://github.com/keycloakify/keycloakify/issues/534 -->
|
||||
key == "password" &&
|
||||
are_same_path(path, ["login"])
|
||||
) || (
|
||||
<#-- Remove realmAttributes added by https://github.com/jcputney/keycloak-theme-additional-info-extension for peace of mind. -->
|
||||
key == "realmAttributes" &&
|
||||
are_same_path(path, [])
|
||||
) || (
|
||||
<#-- attributesByName adds a lot of noise to the output and is not needed, we already have profile.attributes -->
|
||||
key == "attributesByName" &&
|
||||
are_same_path(path, ["profile"])
|
||||
) || (
|
||||
<#-- We already have the attributes in profile speedup the rendering by filtering it out from the register object -->
|
||||
(key == "attributes" || key == "attributesByName") &&
|
||||
are_same_path(path, ["register"])
|
||||
) || (
|
||||
are_same_path(path, ["properties"]) &&
|
||||
(
|
||||
key?starts_with("kc") ||
|
||||
key == "locales" ||
|
||||
key == "import" ||
|
||||
key == "parent" ||
|
||||
key == "meta" ||
|
||||
key == "stylesCommon" ||
|
||||
key == "styles" ||
|
||||
key == "accountResourceProvider"
|
||||
)
|
||||
) || (
|
||||
key == "execution" &&
|
||||
are_same_path(path, [])
|
||||
) || (
|
||||
key == "entity" &&
|
||||
are_same_path(path, ["user"])
|
||||
)
|
||||
>
|
||||
<#-- <#local out_seq += ["/*" + path?join(".") + "." + key + " excluded*/"]> -->
|
||||
<#continue>
|
||||
</#if>
|
||||
|
||||
<#-- https://github.com/keycloakify/keycloakify/discussions/406 -->
|
||||
<#if (
|
||||
["register.ftl", "register-user-profile.ftl", "terms.ftl", "info.ftl", "login.ftl", "login-update-password.ftl", "login-oauth2-device-verify-user-code.ftl"]?seq_contains(pageId) &&
|
||||
key == "attemptedUsername" && are_same_path(path, ["auth"])
|
||||
)>
|
||||
<#attempt>
|
||||
<#-- https://github.com/keycloak/keycloak/blob/3a2bf0c04bcde185e497aaa32d0bb7ab7520cf4a/themes/src/main/resources/theme/base/login/template.ftl#L63 -->
|
||||
<#if !(auth?has_content && auth.showUsername() && !auth.showResetCredentials())>
|
||||
<#local out_seq += ["/*" + path?join(".") + "." + key + " excluded*/"]>
|
||||
<#continue>
|
||||
</#if>
|
||||
<#recover>
|
||||
<#local out_seq += ["/*Accessing attemptedUsername throwed an exception */"]>
|
||||
</#attempt>
|
||||
</#if>
|
||||
<#continue>
|
||||
</#if>
|
||||
|
||||
USER_DEFINED_EXCLUSIONS_eKsaY4ZsZ4eMr2
|
||||
|
||||
<#local outSeq += ['"' + key + '": ' + recOut + ","]>
|
||||
|
||||
</#list>
|
||||
|
||||
<#return (["{"] + outSeq?map(str -> ""?right_pad(4 * (path?size + 1)) + str) + [ ""?right_pad(4 * path?size) + "}"])?join("\n")>
|
||||
|
||||
</#if>
|
||||
|
||||
<#local isMethod = -1>
|
||||
<#attempt>
|
||||
<#local isMethod = object?is_method>
|
||||
<#recover>
|
||||
<#return "ABORT: Can't test if it'sa method.">
|
||||
</#attempt>
|
||||
|
||||
<#if isMethod>
|
||||
|
||||
<#if areSamePath(path, ["auth", "showUsername"])>
|
||||
<#attempt>
|
||||
<#return auth.showUsername()?c>
|
||||
<#recover>
|
||||
<#return "ABORT: Couldn't evaluate auth.showUsername()">
|
||||
</#attempt>
|
||||
</#if>
|
||||
|
||||
<#if areSamePath(path, ["auth", "showResetCredentials"])>
|
||||
<#attempt>
|
||||
<#return auth.showResetCredentials()?c>
|
||||
<#recover>
|
||||
<#return "ABORT: Couldn't evaluate auth.showResetCredentials()">
|
||||
</#attempt>
|
||||
</#if>
|
||||
|
||||
<#if areSamePath(path, ["auth", "showTryAnotherWayLink"])>
|
||||
<#attempt>
|
||||
<#return auth.showTryAnotherWayLink()?c>
|
||||
<#recover>
|
||||
<#return "ABORT: Couldn't evaluate auth.showTryAnotherWayLink()">
|
||||
</#attempt>
|
||||
</#if>
|
||||
|
||||
<#if areSamePath(path, ["url", "getLogoutUrl"])>
|
||||
<#local returnValue = -1>
|
||||
<#attempt>
|
||||
<#local returnValue = url.getLogoutUrl()>
|
||||
<#recover>
|
||||
<#return "ABORT: Couldn't evaluate url.getLogoutUrl()">
|
||||
</#attempt>
|
||||
<#return 'function(){ return "' + returnValue + '"; }'>
|
||||
</#if>
|
||||
|
||||
<#if areSamePath(path, ["totp", "policy", "getAlgorithmKey"])>
|
||||
<#local returnValue = "error">
|
||||
<#if mode?? && mode = "manual">
|
||||
<#attempt>
|
||||
<#if !object[key]??>
|
||||
<#local returnValue = totp.policy.getAlgorithmKey()>
|
||||
<#recover>
|
||||
<#return "ABORT: Couldn't evaluate totp.policy.getAlgorithmKey()">
|
||||
</#attempt>
|
||||
</#if>
|
||||
<#return 'function(){ return "' + returnValue + '"; }'>
|
||||
</#if>
|
||||
|
||||
<#assign fieldNames = [{{fieldNames}}]>
|
||||
<#if profile?? && profile.attributes??>
|
||||
<#list profile.attributes as attribute>
|
||||
<#if fieldNames?seq_contains(attribute.name)>
|
||||
<#continue>
|
||||
</#if>
|
||||
<#assign fieldNames += [attribute.name]>
|
||||
</#list>
|
||||
</#if>
|
||||
|
||||
<#if areSamePath(path, ["messagesPerField", "get"])>
|
||||
|
||||
<#local jsFunctionCode = "function (fieldName) { ">
|
||||
|
||||
<#list fieldNames as fieldName>
|
||||
|
||||
<#-- See: https://github.com/keycloakify/keycloakify/issues/217 -->
|
||||
<#if xKeycloakify.pageId == "login.ftl" >
|
||||
|
||||
<#if fieldName == "username">
|
||||
|
||||
<#local jsFunctionCode += "if(fieldName === 'username' || fieldName === 'password' ){ ">
|
||||
|
||||
<#if messagesPerField.exists('username') || messagesPerField.exists('password')>
|
||||
<#local jsFunctionCode += "return kcContext.message && kcContext.message.summary ? kcContext.message.summary : 'error'; ">
|
||||
<#else>
|
||||
<#local jsFunctionCode += "return ''; ">
|
||||
</#if>
|
||||
|
||||
<#local jsFunctionCode += "} ">
|
||||
|
||||
<#continue>
|
||||
</#if>
|
||||
<#recover>
|
||||
<#local out_seq += ["/*Couldn't test if '" + key + "' is available on this object*/"]>
|
||||
<#continue>
|
||||
</#attempt>
|
||||
|
||||
<#local propertyValue = "">
|
||||
|
||||
<#attempt>
|
||||
<#local propertyValue = object[key]>
|
||||
<#recover>
|
||||
<#local out_seq += ["/*Couldn't dereference '" + key + "' on this object*/"]>
|
||||
<#continue>
|
||||
</#attempt>
|
||||
|
||||
<#local rec_out = ftl_object_to_js_code_declaring_an_object(propertyValue, path + [ key ])>
|
||||
|
||||
<#if rec_out?starts_with("ABORT:")>
|
||||
|
||||
<#local errorMessage = rec_out?remove_beginning("ABORT:")>
|
||||
|
||||
<#if errorMessage != " It's a method" >
|
||||
<#local out_seq += ["/*" + key + ": " + errorMessage + "*/"]>
|
||||
<#if fieldName == "password">
|
||||
<#continue>
|
||||
</#if>
|
||||
|
||||
<#continue>
|
||||
</#if>
|
||||
|
||||
<#local out_seq += ['"' + key + '": ' + rec_out + ","]>
|
||||
<#local jsFunctionCode += "if(fieldName === '" + fieldName + "'){ ">
|
||||
|
||||
<#if messagesPerField.exists('${fieldName}')>
|
||||
<#local jsFunctionCode += 'return decodeHtmlEntities("' + messagesPerField.get('${fieldName}')?js_string + '"); '>
|
||||
<#else>
|
||||
<#local jsFunctionCode += "return ''; ">
|
||||
</#if>
|
||||
|
||||
<#local jsFunctionCode += "} ">
|
||||
|
||||
</#list>
|
||||
|
||||
<#return (["{"] + out_seq?map(str -> ""?right_pad(4 * (path?size + 1)) + str) + [ ""?right_pad(4 * path?size) + "}"])?join("\n")>
|
||||
<#local jsFunctionCode += "}">
|
||||
|
||||
<#return jsFunctionCode>
|
||||
|
||||
</#if>
|
||||
|
||||
<#local isMethod = "">
|
||||
<#attempt>
|
||||
<#local isMethod = object?is_method>
|
||||
<#recover>
|
||||
<#return "ABORT: Can't test if it'sa method.">
|
||||
</#attempt>
|
||||
<#if areSamePath(path, ["messagesPerField", "existsError"])>
|
||||
|
||||
<#if isMethod>
|
||||
<#local jsFunctionCode = "function (fieldName) { ">
|
||||
|
||||
<#if are_same_path(path, ["auth", "showUsername"])>
|
||||
<#attempt>
|
||||
<#return auth.showUsername()?c>
|
||||
<#recover>
|
||||
<#return "ABORT: Couldn't evaluate auth.showUsername()">
|
||||
</#attempt>
|
||||
</#if>
|
||||
<#list fieldNames as fieldName>
|
||||
|
||||
<#if are_same_path(path, ["auth", "showResetCredentials"])>
|
||||
<#attempt>
|
||||
<#return auth.showResetCredentials()?c>
|
||||
<#recover>
|
||||
<#return "ABORT: Couldn't evaluate auth.showResetCredentials()">
|
||||
</#attempt>
|
||||
</#if>
|
||||
<#-- See: https://github.com/keycloakify/keycloakify/issues/217 -->
|
||||
<#if xKeycloakify.pageId == "login.ftl" >
|
||||
<#if fieldName == "username">
|
||||
|
||||
<#if are_same_path(path, ["auth", "showTryAnotherWayLink"])>
|
||||
<#attempt>
|
||||
<#return auth.showTryAnotherWayLink()?c>
|
||||
<#recover>
|
||||
<#return "ABORT: Couldn't evaluate auth.showTryAnotherWayLink()">
|
||||
</#attempt>
|
||||
</#if>
|
||||
<#local jsFunctionCode += "if(fieldName === 'username' || fieldName === 'password' ){ ">
|
||||
|
||||
<#if are_same_path(path, ["url", "getLogoutUrl"])>
|
||||
<#local returnValue = "">
|
||||
<#attempt>
|
||||
<#local returnValue = url.getLogoutUrl()>
|
||||
<#recover>
|
||||
<#return "ABORT: Couldn't evaluate url.getLogoutUrl()">
|
||||
</#attempt>
|
||||
<#return 'function(){ return "' + returnValue + '"; }'>
|
||||
</#if>
|
||||
<#if messagesPerField.existsError('username') || messagesPerField.existsError('password')>
|
||||
<#local jsFunctionCode += "return true; ">
|
||||
<#else>
|
||||
<#local jsFunctionCode += "return false; ">
|
||||
</#if>
|
||||
|
||||
<#if are_same_path(path, ["totp", "policy", "getAlgorithmKey"])>
|
||||
<#local returnValue = "error">
|
||||
<#if mode?? && mode = "manual">
|
||||
<#attempt>
|
||||
<#local returnValue = totp.policy.getAlgorithmKey()>
|
||||
<#recover>
|
||||
<#return "ABORT: Couldn't evaluate totp.policy.getAlgorithmKey()">
|
||||
</#attempt>
|
||||
</#if>
|
||||
<#return 'function(){ return "' + returnValue + '"; }'>
|
||||
</#if>
|
||||
<#local jsFunctionCode += "} ">
|
||||
|
||||
<#assign fieldNames = [ FIELD_NAMES_eKsIY4ZsZ4xeM ]>
|
||||
<#if profile?? && profile.attributes??>
|
||||
<#list profile.attributes as attribute>
|
||||
<#if fieldNames?seq_contains(attribute.name)>
|
||||
<#continue>
|
||||
</#if>
|
||||
<#assign fieldNames += [attribute.name]>
|
||||
</#list>
|
||||
</#if>
|
||||
|
||||
<#if are_same_path(path, ["messagesPerField", "get"])>
|
||||
|
||||
<#local jsFunctionCode = "function (fieldName) { ">
|
||||
|
||||
<#list fieldNames as fieldName>
|
||||
|
||||
<#-- See: https://github.com/keycloakify/keycloakify/issues/217 -->
|
||||
<#if pageId == "login.ftl" >
|
||||
|
||||
<#if fieldName == "username">
|
||||
|
||||
<#local jsFunctionCode += "if(fieldName === 'username' || fieldName === 'password' ){ ">
|
||||
|
||||
<#if messagesPerField.exists('username') || messagesPerField.exists('password')>
|
||||
<#local jsFunctionCode += "return kcContext.message && kcContext.message.summary ? kcContext.message.summary : 'error'; ">
|
||||
<#else>
|
||||
<#local jsFunctionCode += "return ''; ">
|
||||
</#if>
|
||||
|
||||
<#local jsFunctionCode += "} ">
|
||||
|
||||
<#continue>
|
||||
</#if>
|
||||
|
||||
<#if fieldName == "password">
|
||||
<#continue>
|
||||
</#if>
|
||||
|
||||
<#if fieldName == "password">
|
||||
<#continue>
|
||||
</#if>
|
||||
|
||||
<#local jsFunctionCode += "if(fieldName === '" + fieldName + "'){ ">
|
||||
|
||||
<#if messagesPerField.exists('${fieldName}')>
|
||||
<#local jsFunctionCode += 'return decodeHtmlEntities("' + messagesPerField.get('${fieldName}')?js_string + '"); '>
|
||||
<#else>
|
||||
<#local jsFunctionCode += "return ''; ">
|
||||
</#if>
|
||||
|
||||
<#local jsFunctionCode += "} ">
|
||||
|
||||
</#list>
|
||||
|
||||
<#local jsFunctionCode += "}">
|
||||
|
||||
<#return jsFunctionCode>
|
||||
|
||||
</#if>
|
||||
|
||||
<#if are_same_path(path, ["messagesPerField", "existsError"])>
|
||||
|
||||
<#local jsFunctionCode = "function (fieldName) { ">
|
||||
|
||||
<#list fieldNames as fieldName>
|
||||
|
||||
<#-- See: https://github.com/keycloakify/keycloakify/issues/217 -->
|
||||
<#if pageId == "login.ftl" >
|
||||
<#if fieldName == "username">
|
||||
|
||||
<#local jsFunctionCode += "if(fieldName === 'username' || fieldName === 'password' ){ ">
|
||||
|
||||
<#if messagesPerField.existsError('username') || messagesPerField.existsError('password')>
|
||||
<#local jsFunctionCode += "return true; ">
|
||||
<#else>
|
||||
<#local jsFunctionCode += "return false; ">
|
||||
</#if>
|
||||
|
||||
<#local jsFunctionCode += "} ">
|
||||
|
||||
<#continue>
|
||||
</#if>
|
||||
|
||||
<#if fieldName == "password">
|
||||
<#continue>
|
||||
</#if>
|
||||
</#if>
|
||||
|
||||
<#local jsFunctionCode += "if(fieldName === '" + fieldName + "' ){ ">
|
||||
|
||||
<#if messagesPerField.existsError('${fieldName}')>
|
||||
<#local jsFunctionCode += 'return true; '>
|
||||
<#else>
|
||||
<#local jsFunctionCode += "return false; ">
|
||||
</#if>
|
||||
|
||||
<#local jsFunctionCode += "}">
|
||||
|
||||
</#list>
|
||||
|
||||
<#local jsFunctionCode += "}">
|
||||
|
||||
<#return jsFunctionCode>
|
||||
|
||||
</#if>
|
||||
|
||||
<#if themeType == "account" && are_same_path(path, ["realm", "isInternationalizationEnabled"])>
|
||||
<#attempt>
|
||||
<#return realm.isInternationalizationEnabled()?c>
|
||||
<#recover>
|
||||
<#return "ABORT: Couldn't evaluate realm.isInternationalizationEnabled()">
|
||||
</#attempt>
|
||||
</#if>
|
||||
|
||||
<#return "ABORT: It's a method">
|
||||
</#if>
|
||||
|
||||
<#local isBoolean = "">
|
||||
<#attempt>
|
||||
<#local isBoolean = object?is_boolean>
|
||||
<#recover>
|
||||
<#return "ABORT: Can't test if it's a boolean">
|
||||
</#attempt>
|
||||
|
||||
<#if isBoolean>
|
||||
<#return object?c>
|
||||
</#if>
|
||||
|
||||
<#local isEnumerable = "">
|
||||
<#attempt>
|
||||
<#local isEnumerable = object?is_enumerable>
|
||||
<#recover>
|
||||
<#return "ABORT: Can't test if it's an enumerable">
|
||||
</#attempt>
|
||||
|
||||
|
||||
<#if isEnumerable>
|
||||
|
||||
<#local out_seq = []>
|
||||
|
||||
<#local i = 0>
|
||||
|
||||
<#list object as array_item>
|
||||
|
||||
<#if !array_item??>
|
||||
<#local out_seq += ["null,"]>
|
||||
<#continue>
|
||||
</#if>
|
||||
|
||||
<#local rec_out = ftl_object_to_js_code_declaring_an_object(array_item, path + [ i ])>
|
||||
<#local jsFunctionCode += "if(fieldName === '" + fieldName + "' ){ ">
|
||||
|
||||
<#local i = i + 1>
|
||||
|
||||
<#if rec_out?starts_with("ABORT:")>
|
||||
|
||||
<#local errorMessage = rec_out?remove_beginning("ABORT:")>
|
||||
|
||||
<#if errorMessage != " It's a method" >
|
||||
<#local out_seq += ["/*" + i?string + ": " + errorMessage + "*/"]>
|
||||
</#if>
|
||||
|
||||
<#continue>
|
||||
<#if messagesPerField.existsError('${fieldName}')>
|
||||
<#local jsFunctionCode += 'return true; '>
|
||||
<#else>
|
||||
<#local jsFunctionCode += "return false; ">
|
||||
</#if>
|
||||
|
||||
<#local out_seq += [rec_out + ","]>
|
||||
<#local jsFunctionCode += "}">
|
||||
|
||||
</#list>
|
||||
|
||||
<#return (["["] + out_seq?map(str -> ""?right_pad(4 * (path?size + 1)) + str) + [ ""?right_pad(4 * path?size) + "]"])?join("\n")>
|
||||
<#local jsFunctionCode += "}">
|
||||
|
||||
<#return jsFunctionCode>
|
||||
|
||||
</#if>
|
||||
|
||||
<#local isDate = "">
|
||||
<#attempt>
|
||||
<#local isDate = object?is_date_like>
|
||||
<#recover>
|
||||
<#return "ABORT: Can't test if it's a date">
|
||||
</#attempt>
|
||||
|
||||
<#if isDate>
|
||||
<#return '"' + object?datetime?iso_utc + '"'>
|
||||
<#if xKeycloakify.themeType == "account" && areSamePath(path, ["realm", "isInternationalizationEnabled"])>
|
||||
<#attempt>
|
||||
<#return realm.isInternationalizationEnabled()?c>
|
||||
<#recover>
|
||||
<#return "ABORT: Couldn't evaluate realm.isInternationalizationEnabled()">
|
||||
</#attempt>
|
||||
</#if>
|
||||
|
||||
<#local isNumber = "">
|
||||
<#attempt>
|
||||
<#local isNumber = object?is_number>
|
||||
<#recover>
|
||||
<#return "ABORT: Can't test if it's a number">
|
||||
</#attempt>
|
||||
<#return "ABORT: It's a method">
|
||||
</#if>
|
||||
|
||||
<#if isNumber>
|
||||
<#return object?c>
|
||||
</#if>
|
||||
<#local isBoolean = -1>
|
||||
<#attempt>
|
||||
<#local isBoolean = object?is_boolean>
|
||||
<#recover>
|
||||
<#return "ABORT: Can't test if it's a boolean">
|
||||
</#attempt>
|
||||
|
||||
<#local isString = "">
|
||||
<#attempt>
|
||||
<#local isString = object?is_string>
|
||||
<#recover>
|
||||
<#return "ABORT: Can't test if it's a string">
|
||||
</#attempt>
|
||||
<#if isBoolean>
|
||||
<#return object?c>
|
||||
</#if>
|
||||
|
||||
<#if isString>
|
||||
<@addToXKeycloakifyMessagesIfMessageKey str=object />
|
||||
</#if>
|
||||
<#local isEnumerable = -1>
|
||||
<#attempt>
|
||||
<#local isEnumerable = object?is_enumerable>
|
||||
<#recover>
|
||||
<#return "ABORT: Can't test if it's an enumerable">
|
||||
</#attempt>
|
||||
|
||||
<#attempt>
|
||||
<#return '"' + object?js_string + '"'>;
|
||||
<#recover>
|
||||
</#attempt>
|
||||
|
||||
<#return "ABORT: Couldn't convert into string non hash, non method, non boolean, non number, non enumerable object">
|
||||
<#if isEnumerable>
|
||||
|
||||
<#local outSeq = []>
|
||||
|
||||
<#local i = 0>
|
||||
|
||||
<#list object as array_item>
|
||||
|
||||
<#if !array_item??>
|
||||
<#local outSeq += ["null,"]>
|
||||
<#continue>
|
||||
</#if>
|
||||
|
||||
<#local recOut = toJsDeclarationString(array_item, path + [ i ])>
|
||||
|
||||
<#local i = i + 1>
|
||||
|
||||
<#if recOut?starts_with("ABORT:")>
|
||||
|
||||
<#local errorMessage = recOut?remove_beginning("ABORT:")>
|
||||
|
||||
<#if errorMessage != " It's a method" >
|
||||
<#local outSeq += ["/*" + i?string + ": " + errorMessage + "*/"]>
|
||||
</#if>
|
||||
|
||||
<#continue>
|
||||
</#if>
|
||||
|
||||
<#local outSeq += [recOut + ","]>
|
||||
|
||||
</#list>
|
||||
|
||||
<#return (["["] + outSeq?map(str -> ""?right_pad(4 * (path?size + 1)) + str) + [ ""?right_pad(4 * path?size) + "]"])?join("\n")>
|
||||
|
||||
</#if>
|
||||
|
||||
<#local isDate = -1>
|
||||
<#attempt>
|
||||
<#local isDate = object?is_date_like>
|
||||
<#recover>
|
||||
<#return "ABORT: Can't test if it's a date">
|
||||
</#attempt>
|
||||
|
||||
<#if isDate>
|
||||
<#return '"' + object?datetime?iso_utc + '"'>
|
||||
</#if>
|
||||
|
||||
<#local isNumber = -1>
|
||||
<#attempt>
|
||||
<#local isNumber = object?is_number>
|
||||
<#recover>
|
||||
<#return "ABORT: Can't test if it's a number">
|
||||
</#attempt>
|
||||
|
||||
<#if isNumber>
|
||||
<#return object?c>
|
||||
</#if>
|
||||
|
||||
<#local isString = -1>
|
||||
<#attempt>
|
||||
<#local isString = object?is_string>
|
||||
<#recover>
|
||||
<#return "ABORT: Can't test if it's a string">
|
||||
</#attempt>
|
||||
|
||||
<#if isString>
|
||||
<@addToXKeycloakifyMessagesIfMessageKey str=object />
|
||||
</#if>
|
||||
|
||||
<#attempt>
|
||||
<#return '"' + object?js_string + '"'>;
|
||||
<#recover>
|
||||
</#attempt>
|
||||
|
||||
<#return "ABORT: Couldn't convert into string non hash, non method, non boolean, non number, non enumerable object">
|
||||
|
||||
</#function>
|
||||
<#function is_subpath path searchedPath>
|
||||
<#function isSubpath path searchedPath>
|
||||
|
||||
<#if path?size < searchedPath?size>
|
||||
<#return false>
|
||||
@ -570,8 +581,8 @@ function decodeHtmlEntities(htmlStr){
|
||||
|
||||
</#function>
|
||||
|
||||
<#function are_same_path path searchedPath>
|
||||
<#return path?size == searchedPath?size && is_subpath(path, searchedPath)>
|
||||
<#function areSamePath path searchedPath>
|
||||
<#return path?size == searchedPath?size && isSubpath(path, searchedPath)>
|
||||
</#function>
|
||||
|
||||
<#macro addToXKeycloakifyMessagesIfMessageKey str>
|
||||
@ -592,15 +603,17 @@ function decodeHtmlEntities(htmlStr){
|
||||
<#if resolvedMsg==key>
|
||||
<#return>
|
||||
</#if>
|
||||
<#assign xKeycloakifyMessages = xKeycloakifyMessages + { "${key}": resolvedMsg }>
|
||||
<#local messages=xKeycloakify.messages>
|
||||
<#local messages = messages + { key: resolvedMsg }>
|
||||
<#assign xKeycloakify = xKeycloakify + { "messages": messages }>
|
||||
</#macro>
|
||||
|
||||
<#function removeBrackets str>
|
||||
<#if str?starts_with("${") && str?ends_with("}")>
|
||||
<#return str[2..(str?length-2)]>
|
||||
<#else>
|
||||
<#return str>
|
||||
</#if>
|
||||
<#if str?starts_with("${") && str?ends_with("}")>
|
||||
<#return str[2..(str?length-2)]>
|
||||
<#else>
|
||||
<#return str>
|
||||
</#if>
|
||||
</#function>
|
||||
|
||||
<#macro addNonAutomaticallyGatherableMessagesToXKeycloakifyMessages>
|
||||
@ -628,7 +641,7 @@ function decodeHtmlEntities(htmlStr){
|
||||
</#list>
|
||||
</#list>
|
||||
</#if>
|
||||
<#if pageId == "terms.ftl" || termsAcceptanceRequired?? && termsAcceptanceRequired>
|
||||
<#if xKeycloakify.pageId == "terms.ftl" || termsAcceptanceRequired?? && termsAcceptanceRequired>
|
||||
<@addToXKeycloakifyMessagesIfMessageKey str="termsText" />
|
||||
</#if>
|
||||
<#if requiredActions?? && requiredActions?is_enumerable>
|
||||
@ -640,5 +653,3 @@ function decodeHtmlEntities(htmlStr){
|
||||
</#list>
|
||||
</#if>
|
||||
</#macro>
|
||||
|
||||
|
||||
|
@ -3,9 +3,9 @@ import { join as pathJoin } from "path";
|
||||
import { assert } from "tsafe/assert";
|
||||
import type { BuildContext } from "../../shared/buildContext";
|
||||
import {
|
||||
resources_common,
|
||||
lastKeycloakVersionWithAccountV1,
|
||||
accountV1ThemeName
|
||||
RESOURCES_COMMON,
|
||||
LAST_KEYCLOAK_VERSION_WITH_ACCOUNT_V1,
|
||||
ACCOUNT_V1_THEME_NAME
|
||||
} from "../../shared/constants";
|
||||
import {
|
||||
downloadKeycloakDefaultTheme,
|
||||
@ -24,14 +24,14 @@ export async function bringInAccountV1(params: {
|
||||
const { resourcesDirPath, buildContext } = params;
|
||||
|
||||
const { defaultThemeDirPath } = await downloadKeycloakDefaultTheme({
|
||||
keycloakVersion: lastKeycloakVersionWithAccountV1,
|
||||
keycloakVersion: LAST_KEYCLOAK_VERSION_WITH_ACCOUNT_V1,
|
||||
buildContext
|
||||
});
|
||||
|
||||
const accountV1DirPath = pathJoin(
|
||||
resourcesDirPath,
|
||||
"theme",
|
||||
accountV1ThemeName,
|
||||
ACCOUNT_V1_THEME_NAME,
|
||||
"account"
|
||||
);
|
||||
|
||||
@ -47,7 +47,7 @@ export async function bringInAccountV1(params: {
|
||||
|
||||
transformCodebase({
|
||||
srcDirPath: pathJoin(defaultThemeDirPath, "keycloak", "common", "resources"),
|
||||
destDirPath: pathJoin(accountV1DirPath, "resources", resources_common)
|
||||
destDirPath: pathJoin(accountV1DirPath, "resources", RESOURCES_COMMON)
|
||||
});
|
||||
|
||||
fs.writeFileSync(
|
||||
@ -69,7 +69,7 @@ export async function bringInAccountV1(params: {
|
||||
"patternfly-additions.min.css"
|
||||
].map(
|
||||
fileBasename =>
|
||||
`${resources_common}/node_modules/patternfly/dist/css/${fileBasename}`
|
||||
`${RESOURCES_COMMON}/node_modules/patternfly/dist/css/${fileBasename}`
|
||||
)
|
||||
].join(" "),
|
||||
"",
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { type ThemeType, fallbackLanguageTag } from "../../shared/constants";
|
||||
import { type ThemeType, FALLBACK_LANGUAGE_TAG } from "../../shared/constants";
|
||||
import { crawl } from "../../tools/crawl";
|
||||
import { join as pathJoin } from "path";
|
||||
import { symToStr } from "tsafe/symToStr";
|
||||
@ -168,7 +168,7 @@ export function generateMessageProperties(params: {
|
||||
...(messageBundle === undefined
|
||||
? {}
|
||||
: messageBundle[languageTag] ??
|
||||
messageBundle[fallbackLanguageTag] ??
|
||||
messageBundle[FALLBACK_LANGUAGE_TAG] ??
|
||||
messageBundle[Object.keys(messageBundle)[0]] ??
|
||||
{})
|
||||
}
|
||||
|
@ -15,12 +15,12 @@ import {
|
||||
} from "../generateFtl";
|
||||
import {
|
||||
type ThemeType,
|
||||
lastKeycloakVersionWithAccountV1,
|
||||
keycloak_resources,
|
||||
accountV1ThemeName,
|
||||
basenameOfTheKeycloakifyResourcesDir,
|
||||
loginThemePageIds,
|
||||
accountThemePageIds
|
||||
LAST_KEYCLOAK_VERSION_WITH_ACCOUNT_V1,
|
||||
KEYCLOAK_RESOURCES,
|
||||
ACCOUNT_V1_THEME_NAME,
|
||||
BASENAME_OF_KEYCLOAKIFY_RESOURCES_DIR,
|
||||
LOGIN_THEME_PAGE_IDS,
|
||||
ACCOUNT_THEME_PAGE_IDS
|
||||
} from "../../shared/constants";
|
||||
import type { BuildContext } from "../../shared/buildContext";
|
||||
import { assert, type Equals } from "tsafe/assert";
|
||||
@ -85,7 +85,7 @@ export async function generateResourcesForMainTheme(params: {
|
||||
const destDirPath = pathJoin(
|
||||
themeTypeDirPath,
|
||||
"resources",
|
||||
basenameOfTheKeycloakifyResourcesDir
|
||||
BASENAME_OF_KEYCLOAKIFY_RESOURCES_DIR
|
||||
);
|
||||
|
||||
// NOTE: Prevent accumulation of files in the assets dir, as names are hashed they pile up.
|
||||
@ -103,7 +103,7 @@ export async function generateResourcesForMainTheme(params: {
|
||||
themeType: "login"
|
||||
}),
|
||||
"resources",
|
||||
basenameOfTheKeycloakifyResourcesDir
|
||||
BASENAME_OF_KEYCLOAKIFY_RESOURCES_DIR
|
||||
),
|
||||
destDirPath
|
||||
});
|
||||
@ -114,7 +114,7 @@ export async function generateResourcesForMainTheme(params: {
|
||||
{
|
||||
const dirPath = pathJoin(
|
||||
buildContext.projectBuildDirPath,
|
||||
keycloak_resources
|
||||
KEYCLOAK_RESOURCES
|
||||
);
|
||||
|
||||
if (fs.existsSync(dirPath)) {
|
||||
@ -122,7 +122,7 @@ export async function generateResourcesForMainTheme(params: {
|
||||
|
||||
throw new Error(
|
||||
[
|
||||
`Keycloakify build error: The ${keycloak_resources} directory shouldn't exist in your build directory.`,
|
||||
`Keycloakify build error: The ${KEYCLOAK_RESOURCES} directory shouldn't exist in your build directory.`,
|
||||
`(${pathRelative(process.cwd(), dirPath)}).\n`,
|
||||
`Theses assets are only required for local development with Storybook.",
|
||||
"Please remove this directory as an additional step of your command.\n`,
|
||||
@ -140,8 +140,7 @@ export async function generateResourcesForMainTheme(params: {
|
||||
const { fixedCssCode } = replaceImportsInCssCode({
|
||||
cssCode: sourceCode.toString("utf8"),
|
||||
cssFileRelativeDirPath: pathDirname(fileRelativePath),
|
||||
buildContext,
|
||||
isAccountV3
|
||||
buildContext
|
||||
});
|
||||
|
||||
return {
|
||||
@ -176,17 +175,16 @@ export async function generateResourcesForMainTheme(params: {
|
||||
fieldNames: readFieldNameUsage({
|
||||
themeSrcDirPath: buildContext.themeSrcDirPath,
|
||||
themeType
|
||||
}),
|
||||
isAccountV3
|
||||
})
|
||||
});
|
||||
|
||||
[
|
||||
...(() => {
|
||||
switch (themeType) {
|
||||
case "login":
|
||||
return loginThemePageIds;
|
||||
return LOGIN_THEME_PAGE_IDS;
|
||||
case "account":
|
||||
return isAccountV3 ? ["index.ftl"] : accountThemePageIds;
|
||||
return isAccountV3 ? ["index.ftl"] : ACCOUNT_THEME_PAGE_IDS;
|
||||
}
|
||||
})(),
|
||||
...(isAccountV3
|
||||
@ -240,7 +238,7 @@ export async function generateResourcesForMainTheme(params: {
|
||||
keycloakVersion: (() => {
|
||||
switch (themeType) {
|
||||
case "account":
|
||||
return lastKeycloakVersionWithAccountV1;
|
||||
return LAST_KEYCLOAK_VERSION_WITH_ACCOUNT_V1;
|
||||
case "login":
|
||||
return buildContext.loginThemeResourcesFromKeycloakVersion;
|
||||
}
|
||||
@ -258,7 +256,7 @@ export async function generateResourcesForMainTheme(params: {
|
||||
`parent=${(() => {
|
||||
switch (themeType) {
|
||||
case "account":
|
||||
return isAccountV3 ? "base" : accountV1ThemeName;
|
||||
return isAccountV3 ? "base" : ACCOUNT_V1_THEME_NAME;
|
||||
case "login":
|
||||
return "keycloak";
|
||||
}
|
||||
@ -349,7 +347,7 @@ export async function generateResourcesForMainTheme(params: {
|
||||
|
||||
if (buildContext.recordIsImplementedByThemeType.account) {
|
||||
metaInfKeycloakThemes.themes.push({
|
||||
name: accountV1ThemeName,
|
||||
name: ACCOUNT_V1_THEME_NAME,
|
||||
types: ["account"]
|
||||
});
|
||||
}
|
||||
|
@ -31,8 +31,8 @@ export function generateResourcesForThemeVariant(params: {
|
||||
Buffer.from(sourceCode)
|
||||
.toString("utf-8")
|
||||
.replace(
|
||||
`kcContext.themeName = "${themeName}";`,
|
||||
`kcContext.themeName = "${themeVariantName}";`
|
||||
`"themeName": "${themeName}"`,
|
||||
`"themeName": "${themeVariantName}"`
|
||||
),
|
||||
"utf8"
|
||||
);
|
||||
|
@ -5,8 +5,8 @@ import * as fs from "fs";
|
||||
import { join as pathJoin } from "path";
|
||||
import {
|
||||
type ThemeType,
|
||||
accountThemePageIds,
|
||||
loginThemePageIds
|
||||
ACCOUNT_THEME_PAGE_IDS,
|
||||
LOGIN_THEME_PAGE_IDS
|
||||
} from "../../shared/constants";
|
||||
|
||||
export function readExtraPagesNames(params: {
|
||||
@ -41,9 +41,9 @@ export function readExtraPagesNames(params: {
|
||||
return extraPages.reduce(...removeDuplicates<string>()).filter(pageId => {
|
||||
switch (themeType) {
|
||||
case "account":
|
||||
return !id<readonly string[]>(accountThemePageIds).includes(pageId);
|
||||
return !id<readonly string[]>(ACCOUNT_THEME_PAGE_IDS).includes(pageId);
|
||||
case "login":
|
||||
return !id<readonly string[]>(loginThemePageIds).includes(pageId);
|
||||
return !id<readonly string[]>(LOGIN_THEME_PAGE_IDS).includes(pageId);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ import { join as pathJoin, relative as pathRelative, sep as pathSep } from "path
|
||||
import * as child_process from "child_process";
|
||||
import * as fs from "fs";
|
||||
import { getBuildContext } from "../shared/buildContext";
|
||||
import { vitePluginSubScriptEnvNames } from "../shared/constants";
|
||||
import { VITE_PLUGIN_SUB_SCRIPTS_ENV_NAMES } from "../shared/constants";
|
||||
import { buildJars } from "./buildJars";
|
||||
import type { CliCommandOptions } from "../main";
|
||||
import chalk from "chalk";
|
||||
@ -93,10 +93,12 @@ export async function command(params: { cliCommandOptions: CliCommandOptions })
|
||||
cwd: buildContext.projectDirPath,
|
||||
env: {
|
||||
...process.env,
|
||||
[vitePluginSubScriptEnvNames.runPostBuildScript]: JSON.stringify({
|
||||
resourcesDirPath,
|
||||
buildContext
|
||||
})
|
||||
[VITE_PLUGIN_SUB_SCRIPTS_ENV_NAMES.RUN_POST_BUILD_SCRIPT]: JSON.stringify(
|
||||
{
|
||||
resourcesDirPath,
|
||||
buildContext
|
||||
}
|
||||
)
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
import type { BuildContext } from "../../shared/buildContext";
|
||||
import { basenameOfTheKeycloakifyResourcesDir } from "../../shared/constants";
|
||||
import { BASENAME_OF_KEYCLOAKIFY_RESOURCES_DIR } from "../../shared/constants";
|
||||
import { assert } from "tsafe/assert";
|
||||
import { posix } from "path";
|
||||
|
||||
@ -12,42 +12,55 @@ assert<BuildContext extends BuildContextLike ? true : false>();
|
||||
export function replaceImportsInCssCode(params: {
|
||||
cssCode: string;
|
||||
cssFileRelativeDirPath: string | undefined;
|
||||
isAccountV3: boolean;
|
||||
buildContext: BuildContextLike;
|
||||
}): {
|
||||
fixedCssCode: string;
|
||||
} {
|
||||
const { cssCode, cssFileRelativeDirPath, buildContext, isAccountV3 } = params;
|
||||
const { cssCode, cssFileRelativeDirPath, buildContext } = params;
|
||||
|
||||
const fixedCssCode = cssCode.replace(
|
||||
/url\(["']?(\/[^/][^)"']+)["']?\)/g,
|
||||
(match, assetFileAbsoluteUrlPathname) => {
|
||||
if (buildContext.urlPathname !== undefined) {
|
||||
if (!assetFileAbsoluteUrlPathname.startsWith(buildContext.urlPathname)) {
|
||||
// NOTE: Should never happen
|
||||
return match;
|
||||
let fixedCssCode = cssCode;
|
||||
|
||||
[
|
||||
/url\("(\/[^/][^"]+)"\)/g,
|
||||
/url\('(\/[^/][^']+)'\)/g,
|
||||
/url\((\/[^/][^)]+)\)/g
|
||||
].forEach(
|
||||
regex =>
|
||||
(fixedCssCode = fixedCssCode.replace(
|
||||
regex,
|
||||
(match, assetFileAbsoluteUrlPathname) => {
|
||||
if (buildContext.urlPathname !== undefined) {
|
||||
if (
|
||||
!assetFileAbsoluteUrlPathname.startsWith(
|
||||
buildContext.urlPathname
|
||||
)
|
||||
) {
|
||||
// NOTE: Should never happen
|
||||
return match;
|
||||
}
|
||||
assetFileAbsoluteUrlPathname =
|
||||
assetFileAbsoluteUrlPathname.replace(
|
||||
buildContext.urlPathname,
|
||||
"/"
|
||||
);
|
||||
}
|
||||
|
||||
inline_style_in_html: {
|
||||
if (cssFileRelativeDirPath !== undefined) {
|
||||
break inline_style_in_html;
|
||||
}
|
||||
|
||||
return `url("\${xKeycloakify.resourcesPath}/${BASENAME_OF_KEYCLOAKIFY_RESOURCES_DIR}${assetFileAbsoluteUrlPathname}")`;
|
||||
}
|
||||
|
||||
const assetFileRelativeUrlPathname = posix.relative(
|
||||
cssFileRelativeDirPath.replace(/\\/g, "/"),
|
||||
assetFileAbsoluteUrlPathname.replace(/^\//, "")
|
||||
);
|
||||
|
||||
return `url("${assetFileRelativeUrlPathname}")`;
|
||||
}
|
||||
assetFileAbsoluteUrlPathname = assetFileAbsoluteUrlPathname.replace(
|
||||
buildContext.urlPathname,
|
||||
"/"
|
||||
);
|
||||
}
|
||||
|
||||
inline_style_in_html: {
|
||||
if (cssFileRelativeDirPath !== undefined) {
|
||||
break inline_style_in_html;
|
||||
}
|
||||
|
||||
return `url(\${${!isAccountV3 ? "url.resourcesPath" : "resourceUrl"}}/${basenameOfTheKeycloakifyResourcesDir}${assetFileAbsoluteUrlPathname})`;
|
||||
}
|
||||
|
||||
const assetFileRelativeUrlPathname = posix.relative(
|
||||
cssFileRelativeDirPath.replace(/\\/g, "/"),
|
||||
assetFileAbsoluteUrlPathname.replace(/^\//, "")
|
||||
);
|
||||
|
||||
return `url(${assetFileRelativeUrlPathname})`;
|
||||
}
|
||||
))
|
||||
);
|
||||
|
||||
return { fixedCssCode };
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { basenameOfTheKeycloakifyResourcesDir } from "../../../shared/constants";
|
||||
import { BASENAME_OF_KEYCLOAKIFY_RESOURCES_DIR } from "../../../shared/constants";
|
||||
import { assert } from "tsafe/assert";
|
||||
import type { BuildContext } from "../../../shared/buildContext";
|
||||
import * as nodePath from "path";
|
||||
@ -31,13 +31,13 @@ export function replaceImportsInJsCode_vite(params: {
|
||||
|
||||
let fixedJsCode = jsCode;
|
||||
|
||||
replace_base_javacript_import: {
|
||||
replace_base_js_import: {
|
||||
if (buildContext.urlPathname === undefined) {
|
||||
break replace_base_javacript_import;
|
||||
break replace_base_js_import;
|
||||
}
|
||||
// Optimization
|
||||
if (!jsCode.includes(buildContext.urlPathname)) {
|
||||
break replace_base_javacript_import;
|
||||
break replace_base_js_import;
|
||||
}
|
||||
|
||||
// Replace `Hv=function(e){return"/abcde12345/"+e}` by `Hv=function(e){return"/"+e}`
|
||||
@ -85,13 +85,13 @@ export function replaceImportsInJsCode_vite(params: {
|
||||
fixedJsCode = replaceAll(
|
||||
fixedJsCode,
|
||||
`"${relativePathOfAssetFile}"`,
|
||||
`(window.kcContext.url.resourcesPath.substring(1) + "/${basenameOfTheKeycloakifyResourcesDir}/${relativePathOfAssetFile}")`
|
||||
`(window.kcContext["x-keycloakify"].resourcesPath.substring(1) + "/${BASENAME_OF_KEYCLOAKIFY_RESOURCES_DIR}/${relativePathOfAssetFile}")`
|
||||
);
|
||||
|
||||
fixedJsCode = replaceAll(
|
||||
fixedJsCode,
|
||||
`"${buildContext.urlPathname ?? "/"}${relativePathOfAssetFile}"`,
|
||||
`(window.kcContext.url.resourcesPath + "/${basenameOfTheKeycloakifyResourcesDir}/${relativePathOfAssetFile}")`
|
||||
`(window.kcContext["x-keycloakify"].resourcesPath + "/${BASENAME_OF_KEYCLOAKIFY_RESOURCES_DIR}/${relativePathOfAssetFile}")`
|
||||
);
|
||||
});
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { basenameOfTheKeycloakifyResourcesDir } from "../../../shared/constants";
|
||||
import { BASENAME_OF_KEYCLOAKIFY_RESOURCES_DIR } from "../../../shared/constants";
|
||||
import { assert } from "tsafe/assert";
|
||||
import type { BuildContext } from "../../../shared/buildContext";
|
||||
import * as nodePath from "path";
|
||||
@ -83,14 +83,14 @@ export function replaceImportsInJsCode_webpack(params: {
|
||||
var pd = Object.getOwnPropertyDescriptor(${n}, "p");
|
||||
if( pd === undefined || pd.configurable ){
|
||||
Object.defineProperty(${n}, "p", {
|
||||
get: function() { return window.kcContext.url.resourcesPath; },
|
||||
get: function() { return window.kcContext["x-keycloakify"].resourcesPath; },
|
||||
set: function() {}
|
||||
});
|
||||
}
|
||||
return "${u}";
|
||||
})()] = ${
|
||||
isArrowFunction ? `${e} =>` : `function(${e}) { return `
|
||||
} "/${basenameOfTheKeycloakifyResourcesDir}/${staticDir}${language}/"`
|
||||
} "/${BASENAME_OF_KEYCLOAKIFY_RESOURCES_DIR}/${staticDir}${language}/"`
|
||||
.replace(/\s+/g, " ")
|
||||
.trim();
|
||||
}
|
||||
@ -104,7 +104,7 @@ export function replaceImportsInJsCode_webpack(params: {
|
||||
`[a-zA-Z]+\\.[a-zA-Z]+\\+"${staticDir.replace(/\//g, "\\/")}`,
|
||||
"g"
|
||||
),
|
||||
`window.kcContext.url.resourcesPath + "/${basenameOfTheKeycloakifyResourcesDir}/${staticDir}`
|
||||
`window.kcContext["x-keycloakify"].resourcesPath + "/${BASENAME_OF_KEYCLOAKIFY_RESOURCES_DIR}/${staticDir}`
|
||||
);
|
||||
|
||||
return { fixedJsCode };
|
||||
|
@ -13,13 +13,13 @@ import * as fs from "fs";
|
||||
import { assert, type Equals } from "tsafe/assert";
|
||||
import * as child_process from "child_process";
|
||||
import {
|
||||
vitePluginSubScriptEnvNames,
|
||||
buildForKeycloakMajorVersionEnvName
|
||||
VITE_PLUGIN_SUB_SCRIPTS_ENV_NAMES,
|
||||
BUILD_FOR_KEYCLOAK_MAJOR_VERSION_ENV_NAME
|
||||
} from "./constants";
|
||||
import type { KeycloakVersionRange } from "./KeycloakVersionRange";
|
||||
import { exclude } from "tsafe";
|
||||
import { crawl } from "../tools/crawl";
|
||||
import { themeTypes } from "./constants";
|
||||
import { THEME_TYPES } from "./constants";
|
||||
import { objectFromEntries } from "tsafe/objectFromEntries";
|
||||
import { objectEntries } from "tsafe/objectEntries";
|
||||
import { type ThemeType } from "./constants";
|
||||
@ -136,7 +136,7 @@ export function getBuildContext(params: {
|
||||
return { themeSrcDirPath };
|
||||
}
|
||||
|
||||
for (const themeType of [...themeTypes, "email"]) {
|
||||
for (const themeType of [...THEME_TYPES, "email"]) {
|
||||
if (!fs.existsSync(pathJoin(srcDirPath, themeType))) {
|
||||
continue;
|
||||
}
|
||||
@ -171,18 +171,18 @@ export function getBuildContext(params: {
|
||||
cwd: projectDirPath,
|
||||
env: {
|
||||
...process.env,
|
||||
[vitePluginSubScriptEnvNames.resolveViteConfig]: "true"
|
||||
[VITE_PLUGIN_SUB_SCRIPTS_ENV_NAMES.RESOLVE_VITE_CONFIG]: "true"
|
||||
}
|
||||
})
|
||||
.toString("utf8");
|
||||
|
||||
assert(
|
||||
output.includes(vitePluginSubScriptEnvNames.resolveViteConfig),
|
||||
output.includes(VITE_PLUGIN_SUB_SCRIPTS_ENV_NAMES.RESOLVE_VITE_CONFIG),
|
||||
"Seems like the Keycloakify's Vite plugin is not installed."
|
||||
);
|
||||
|
||||
const resolvedViteConfigStr = output
|
||||
.split(vitePluginSubScriptEnvNames.resolveViteConfig)
|
||||
.split(VITE_PLUGIN_SUB_SCRIPTS_ENV_NAMES.RESOLVE_VITE_CONFIG)
|
||||
.reverse()[0];
|
||||
|
||||
const resolvedViteConfig: ResolvedViteConfig = JSON.parse(resolvedViteConfigStr);
|
||||
@ -594,7 +594,8 @@ export function getBuildContext(params: {
|
||||
|
||||
build_for_specific_keycloak_major_version: {
|
||||
const buildForKeycloakMajorVersionNumber = (() => {
|
||||
const envValue = process.env[buildForKeycloakMajorVersionEnvName];
|
||||
const envValue =
|
||||
process.env[BUILD_FOR_KEYCLOAK_MAJOR_VERSION_ENV_NAME];
|
||||
|
||||
if (envValue === undefined) {
|
||||
return undefined;
|
||||
|
@ -1,22 +1,22 @@
|
||||
export const keycloak_resources = "keycloak-resources";
|
||||
export const resources_common = "resources-common";
|
||||
export const lastKeycloakVersionWithAccountV1 = "21.1.2";
|
||||
export const basenameOfTheKeycloakifyResourcesDir = "dist";
|
||||
export const KEYCLOAK_RESOURCES = "keycloak-resources";
|
||||
export const RESOURCES_COMMON = "resources-common";
|
||||
export const LAST_KEYCLOAK_VERSION_WITH_ACCOUNT_V1 = "21.1.2";
|
||||
export const BASENAME_OF_KEYCLOAKIFY_RESOURCES_DIR = "dist";
|
||||
|
||||
export const themeTypes = ["login", "account"] as const;
|
||||
export const accountV1ThemeName = "account-v1";
|
||||
export const THEME_TYPES = ["login", "account"] as const;
|
||||
export const ACCOUNT_V1_THEME_NAME = "account-v1";
|
||||
|
||||
export type ThemeType = (typeof themeTypes)[number];
|
||||
export type ThemeType = (typeof THEME_TYPES)[number];
|
||||
|
||||
export const vitePluginSubScriptEnvNames = {
|
||||
runPostBuildScript: "KEYCLOAKIFY_RUN_POST_BUILD_SCRIPT",
|
||||
resolveViteConfig: "KEYCLOAKIFY_RESOLVE_VITE_CONFIG"
|
||||
export const VITE_PLUGIN_SUB_SCRIPTS_ENV_NAMES = {
|
||||
RUN_POST_BUILD_SCRIPT: "KEYCLOAKIFY_RUN_POST_BUILD_SCRIPT",
|
||||
RESOLVE_VITE_CONFIG: "KEYCLOAKIFY_RESOLVE_VITE_CONFIG"
|
||||
} as const;
|
||||
|
||||
export const buildForKeycloakMajorVersionEnvName =
|
||||
export const BUILD_FOR_KEYCLOAK_MAJOR_VERSION_ENV_NAME =
|
||||
"KEYCLOAKIFY_BUILD_FOR_KEYCLOAK_MAJOR_VERSION";
|
||||
|
||||
export const loginThemePageIds = [
|
||||
export const LOGIN_THEME_PAGE_IDS = [
|
||||
"login.ftl",
|
||||
"login-username.ftl",
|
||||
"login-password.ftl",
|
||||
@ -53,7 +53,7 @@ export const loginThemePageIds = [
|
||||
"webauthn-error.ftl"
|
||||
] as const;
|
||||
|
||||
export const accountThemePageIds = [
|
||||
export const ACCOUNT_THEME_PAGE_IDS = [
|
||||
"password.ftl",
|
||||
"account.ftl",
|
||||
"sessions.ftl",
|
||||
@ -63,9 +63,9 @@ export const accountThemePageIds = [
|
||||
"federatedIdentity.ftl"
|
||||
] as const;
|
||||
|
||||
export type LoginThemePageId = (typeof loginThemePageIds)[number];
|
||||
export type AccountThemePageId = (typeof accountThemePageIds)[number];
|
||||
export type LoginThemePageId = (typeof LOGIN_THEME_PAGE_IDS)[number];
|
||||
export type AccountThemePageId = (typeof ACCOUNT_THEME_PAGE_IDS)[number];
|
||||
|
||||
export const containerName = "keycloak-keycloakify";
|
||||
export const CONTAINER_NAME = "keycloak-keycloakify";
|
||||
|
||||
export const fallbackLanguageTag = "en";
|
||||
export const FALLBACK_LANGUAGE_TAG = "en";
|
||||
|
@ -4,9 +4,9 @@ import {
|
||||
} from "./downloadKeycloakStaticResources";
|
||||
import { join as pathJoin, relative as pathRelative } from "path";
|
||||
import {
|
||||
themeTypes,
|
||||
keycloak_resources,
|
||||
lastKeycloakVersionWithAccountV1
|
||||
THEME_TYPES,
|
||||
KEYCLOAK_RESOURCES,
|
||||
LAST_KEYCLOAK_VERSION_WITH_ACCOUNT_V1
|
||||
} from "../shared/constants";
|
||||
import { readThisNpmPackageVersion } from "../tools/readThisNpmPackageVersion";
|
||||
import { assert } from "tsafe/assert";
|
||||
@ -26,7 +26,7 @@ export async function copyKeycloakResourcesToPublic(params: {
|
||||
}) {
|
||||
const { buildContext } = params;
|
||||
|
||||
const destDirPath = pathJoin(buildContext.publicDirPath, keycloak_resources);
|
||||
const destDirPath = pathJoin(buildContext.publicDirPath, KEYCLOAK_RESOURCES);
|
||||
|
||||
const keycloakifyBuildinfoFilePath = pathJoin(destDirPath, "keycloakify.buildinfo");
|
||||
|
||||
@ -66,14 +66,14 @@ export async function copyKeycloakResourcesToPublic(params: {
|
||||
|
||||
fs.writeFileSync(pathJoin(destDirPath, ".gitignore"), Buffer.from("*", "utf8"));
|
||||
|
||||
for (const themeType of themeTypes) {
|
||||
for (const themeType of THEME_TYPES) {
|
||||
await downloadKeycloakStaticResources({
|
||||
keycloakVersion: (() => {
|
||||
switch (themeType) {
|
||||
case "login":
|
||||
return buildContext.loginThemeResourcesFromKeycloakVersion;
|
||||
case "account":
|
||||
return lastKeycloakVersionWithAccountV1;
|
||||
return LAST_KEYCLOAK_VERSION_WITH_ACCOUNT_V1;
|
||||
}
|
||||
})(),
|
||||
themeType,
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { join as pathJoin, relative as pathRelative } from "path";
|
||||
import { type BuildContext } from "./buildContext";
|
||||
import { assert } from "tsafe/assert";
|
||||
import { lastKeycloakVersionWithAccountV1 } from "./constants";
|
||||
import { LAST_KEYCLOAK_VERSION_WITH_ACCOUNT_V1 } from "./constants";
|
||||
import { downloadAndExtractArchive } from "../tools/downloadAndExtractArchive";
|
||||
|
||||
export type BuildContextLike = {
|
||||
@ -43,7 +43,7 @@ export async function downloadKeycloakDefaultTheme(params: {
|
||||
}
|
||||
|
||||
last_account_v1_transformations: {
|
||||
if (lastKeycloakVersionWithAccountV1 !== keycloakVersion) {
|
||||
if (LAST_KEYCLOAK_VERSION_WITH_ACCOUNT_V1 !== keycloakVersion) {
|
||||
break last_account_v1_transformations;
|
||||
}
|
||||
|
||||
|
@ -4,7 +4,7 @@ import {
|
||||
downloadKeycloakDefaultTheme,
|
||||
type BuildContextLike as BuildContextLike_downloadKeycloakDefaultTheme
|
||||
} from "./downloadKeycloakDefaultTheme";
|
||||
import { resources_common, type ThemeType } from "./constants";
|
||||
import { RESOURCES_COMMON, type ThemeType } from "./constants";
|
||||
import type { BuildContext } from "./buildContext";
|
||||
import { assert } from "tsafe/assert";
|
||||
import { existsAsync } from "../tools/fs.existsAsync";
|
||||
@ -48,6 +48,6 @@ export async function downloadKeycloakStaticResources(params: {
|
||||
|
||||
transformCodebase({
|
||||
srcDirPath: pathJoin(defaultThemeDirPath, "keycloak", "common", "resources"),
|
||||
destDirPath: pathJoin(resourcesDirPath, resources_common)
|
||||
destDirPath: pathJoin(resourcesDirPath, RESOURCES_COMMON)
|
||||
});
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { buildForKeycloakMajorVersionEnvName } from "../shared/constants";
|
||||
import { BUILD_FOR_KEYCLOAK_MAJOR_VERSION_ENV_NAME } from "../shared/constants";
|
||||
import * as child_process from "child_process";
|
||||
import { Deferred } from "evt/tools/Deferred";
|
||||
import { assert } from "tsafe/assert";
|
||||
@ -26,7 +26,7 @@ export async function keycloakifyBuild(params: {
|
||||
cwd: buildContext.projectDirPath,
|
||||
env: {
|
||||
...process.env,
|
||||
[buildForKeycloakMajorVersionEnvName]: `${buildForKeycloakMajorVersionNumber}`
|
||||
[BUILD_FOR_KEYCLOAK_MAJOR_VERSION_ENV_NAME]: `${buildForKeycloakMajorVersionNumber}`
|
||||
},
|
||||
shell: true
|
||||
});
|
||||
|
@ -2,7 +2,7 @@ import { getBuildContext } from "../shared/buildContext";
|
||||
import { exclude } from "tsafe/exclude";
|
||||
import type { CliCommandOptions as CliCommandOptions_common } from "../main";
|
||||
import { promptKeycloakVersion } from "../shared/promptKeycloakVersion";
|
||||
import { accountV1ThemeName, containerName } from "../shared/constants";
|
||||
import { ACCOUNT_V1_THEME_NAME, CONTAINER_NAME } from "../shared/constants";
|
||||
import { SemVer } from "../tools/SemVer";
|
||||
import { assert } from "tsafe/assert";
|
||||
import * as fs from "fs";
|
||||
@ -269,7 +269,7 @@ export async function command(params: { cliCommandOptions: CliCommandOptions })
|
||||
fs.copyFileSync(jarFilePath, jarFilePath_cacheDir);
|
||||
|
||||
try {
|
||||
child_process.execSync(`docker rm --force ${containerName}`, {
|
||||
child_process.execSync(`docker rm --force ${CONTAINER_NAME}`, {
|
||||
stdio: "ignore"
|
||||
});
|
||||
} catch {}
|
||||
@ -279,7 +279,7 @@ export async function command(params: { cliCommandOptions: CliCommandOptions })
|
||||
[
|
||||
"run",
|
||||
...["-p", `${cliCommandOptions.port}:8080`],
|
||||
...["--name", containerName],
|
||||
...["--name", CONTAINER_NAME],
|
||||
...["-e", "KEYCLOAK_ADMIN=admin"],
|
||||
...["-e", "KEYCLOAK_ADMIN_PASSWORD=admin"],
|
||||
...(realmJsonFilePath === undefined
|
||||
@ -301,10 +301,10 @@ export async function command(params: { cliCommandOptions: CliCommandOptions })
|
||||
pathJoin(
|
||||
buildContext.keycloakifyBuildDirPath,
|
||||
"theme",
|
||||
accountV1ThemeName
|
||||
ACCOUNT_V1_THEME_NAME
|
||||
)
|
||||
)
|
||||
? [accountV1ThemeName]
|
||||
? [ACCOUNT_V1_THEME_NAME]
|
||||
: [])
|
||||
]
|
||||
.map(themeName => ({
|
||||
|
@ -73,8 +73,8 @@ export function createGetKcClsx<ClassKey extends string>(params: {
|
||||
|
||||
return clsx(
|
||||
classKey,
|
||||
doUseDefaultCss ? defaultClasses[classKey] : undefined,
|
||||
classes?.[classKey]
|
||||
classes?.[classKey] ??
|
||||
(doUseDefaultCss ? defaultClasses[classKey] : undefined)
|
||||
);
|
||||
}
|
||||
});
|
||||
|
@ -1,8 +1,8 @@
|
||||
import "keycloakify/tools/Object.fromEntries";
|
||||
import type { KcContext, Attribute } from "./KcContext";
|
||||
import {
|
||||
resources_common,
|
||||
keycloak_resources,
|
||||
RESOURCES_COMMON,
|
||||
KEYCLOAK_RESOURCES,
|
||||
type LoginThemePageId
|
||||
} from "keycloakify/bin/shared/constants";
|
||||
import { id } from "tsafe/id";
|
||||
@ -76,7 +76,7 @@ const attributesByName = Object.fromEntries(
|
||||
]).map(attribute => [attribute.name, attribute])
|
||||
);
|
||||
|
||||
const resourcesPath = `${BASE_URL}${keycloak_resources}/login/resources`;
|
||||
const resourcesPath = `${BASE_URL}${KEYCLOAK_RESOURCES}/login/resources`;
|
||||
|
||||
export const kcContextCommonMock: KcContext.Common = {
|
||||
themeVersion: "0.0.0",
|
||||
@ -86,7 +86,7 @@ export const kcContextCommonMock: KcContext.Common = {
|
||||
url: {
|
||||
loginAction: "#",
|
||||
resourcesPath,
|
||||
resourcesCommonPath: `${resourcesPath}/${resources_common}`,
|
||||
resourcesCommonPath: `${resourcesPath}/${RESOURCES_COMMON}`,
|
||||
loginRestartFlowUrl: "#",
|
||||
loginUrl: "#",
|
||||
ssoLoginInOtherTabsUrl: "#"
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { useEffect, useReducer, Fragment } from "react";
|
||||
import { assert } from "tsafe/assert";
|
||||
import { assert } from "keycloakify/tools/assert";
|
||||
import type { KcClsx } from "keycloakify/login/lib/kcClsx";
|
||||
import {
|
||||
useUserProfileForm,
|
||||
@ -70,7 +70,7 @@ export default function UserProfileFormFields(props: UserProfileFormFieldsProps<
|
||||
{advancedMsg(attribute.annotations.inputHelperTextBefore)}
|
||||
</div>
|
||||
)}
|
||||
<InputFiledByType
|
||||
<InputFieldByType
|
||||
attribute={attribute}
|
||||
valueOrValues={valueOrValues}
|
||||
displayableErrors={displayableErrors}
|
||||
@ -196,7 +196,7 @@ function FieldErrors(props: { attribute: Attribute; displayableErrors: FormField
|
||||
);
|
||||
}
|
||||
|
||||
type InputFiledByTypeProps = {
|
||||
type InputFieldByTypeProps = {
|
||||
attribute: Attribute;
|
||||
valueOrValues: string | string[];
|
||||
displayableErrors: FormFieldError[];
|
||||
@ -205,7 +205,7 @@ type InputFiledByTypeProps = {
|
||||
kcClsx: KcClsx;
|
||||
};
|
||||
|
||||
function InputFiledByType(props: InputFiledByTypeProps) {
|
||||
function InputFieldByType(props: InputFieldByTypeProps) {
|
||||
const { attribute, valueOrValues } = props;
|
||||
|
||||
switch (attribute.annotations.inputType) {
|
||||
@ -274,9 +274,11 @@ function PasswordWrapper(props: { kcClsx: KcClsx; i18n: I18n; passwordInputId: s
|
||||
);
|
||||
}
|
||||
|
||||
function InputTag(props: InputFiledByTypeProps & { fieldIndex: number | undefined }) {
|
||||
function InputTag(props: InputFieldByTypeProps & { fieldIndex: number | undefined }) {
|
||||
const { attribute, fieldIndex, kcClsx, dispatchFormAction, valueOrValues, i18n, displayableErrors } = props;
|
||||
|
||||
const { advancedMsgStr } = i18n;
|
||||
|
||||
return (
|
||||
<>
|
||||
<input
|
||||
@ -305,7 +307,9 @@ function InputTag(props: InputFiledByTypeProps & { fieldIndex: number | undefine
|
||||
aria-invalid={displayableErrors.find(error => error.fieldIndex === fieldIndex) !== undefined}
|
||||
disabled={attribute.readOnly}
|
||||
autoComplete={attribute.autocomplete}
|
||||
placeholder={attribute.annotations.inputTypePlaceholder}
|
||||
placeholder={
|
||||
attribute.annotations.inputTypePlaceholder === undefined ? undefined : advancedMsgStr(attribute.annotations.inputTypePlaceholder)
|
||||
}
|
||||
pattern={attribute.annotations.inputTypePattern}
|
||||
size={attribute.annotations.inputTypeSize === undefined ? undefined : parseInt(`${attribute.annotations.inputTypeSize}`)}
|
||||
maxLength={
|
||||
@ -429,7 +433,7 @@ function AddRemoveButtonsMultiValuedAttribute(props: {
|
||||
);
|
||||
}
|
||||
|
||||
function InputTagSelects(props: InputFiledByTypeProps) {
|
||||
function InputTagSelects(props: InputFieldByTypeProps) {
|
||||
const { attribute, dispatchFormAction, kcClsx, valueOrValues } = props;
|
||||
|
||||
const { advancedMsg } = props.i18n;
|
||||
@ -537,7 +541,7 @@ function InputTagSelects(props: InputFiledByTypeProps) {
|
||||
);
|
||||
}
|
||||
|
||||
function TextareaTag(props: InputFiledByTypeProps) {
|
||||
function TextareaTag(props: InputFieldByTypeProps) {
|
||||
const { attribute, dispatchFormAction, kcClsx, displayableErrors, valueOrValues } = props;
|
||||
|
||||
assert(typeof valueOrValues === "string");
|
||||
@ -573,7 +577,7 @@ function TextareaTag(props: InputFiledByTypeProps) {
|
||||
);
|
||||
}
|
||||
|
||||
function SelectTag(props: InputFiledByTypeProps) {
|
||||
function SelectTag(props: InputFieldByTypeProps) {
|
||||
const { attribute, dispatchFormAction, kcClsx, displayableErrors, i18n, valueOrValues } = props;
|
||||
|
||||
const { advancedMsg } = i18n;
|
||||
|
@ -3,7 +3,7 @@ import { assert } from "tsafe/assert";
|
||||
import messages_defaultSet_fallbackLanguage from "./messages_defaultSet/en";
|
||||
import { fetchMessages_defaultSet } from "./messages_defaultSet";
|
||||
import type { KcContext } from "../KcContext";
|
||||
import { fallbackLanguageTag } from "keycloakify/bin/shared/constants";
|
||||
import { FALLBACK_LANGUAGE_TAG } from "keycloakify/bin/shared/constants";
|
||||
import { id } from "tsafe/id";
|
||||
|
||||
export type KcContextLike = {
|
||||
@ -104,7 +104,7 @@ export function createGetI18n<MessageKey_themeDefined extends string = never>(me
|
||||
}
|
||||
|
||||
const partialI18n: Pick<I18n, "currentLanguageTag" | "getChangeLocaleUrl" | "labelBySupportedLanguageTag"> = {
|
||||
currentLanguageTag: kcContext.locale?.currentLanguageTag ?? fallbackLanguageTag,
|
||||
currentLanguageTag: kcContext.locale?.currentLanguageTag ?? FALLBACK_LANGUAGE_TAG,
|
||||
getChangeLocaleUrl: newLanguageTag => {
|
||||
const { locale } = kcContext;
|
||||
|
||||
@ -122,7 +122,7 @@ export function createGetI18n<MessageKey_themeDefined extends string = never>(me
|
||||
const { createI18nTranslationFunctions } = createI18nTranslationFunctionsFactory<MessageKey_themeDefined>({
|
||||
messages_themeDefined:
|
||||
messagesByLanguageTag_themeDefined[partialI18n.currentLanguageTag] ??
|
||||
messagesByLanguageTag_themeDefined[fallbackLanguageTag] ??
|
||||
messagesByLanguageTag_themeDefined[FALLBACK_LANGUAGE_TAG] ??
|
||||
(() => {
|
||||
const firstLanguageTag = Object.keys(messagesByLanguageTag_themeDefined)[0];
|
||||
if (firstLanguageTag === undefined) {
|
||||
@ -133,7 +133,7 @@ export function createGetI18n<MessageKey_themeDefined extends string = never>(me
|
||||
messages_fromKcServer: kcContext["x-keycloakify"].messages
|
||||
});
|
||||
|
||||
const isCurrentLanguageFallbackLanguage = partialI18n.currentLanguageTag === fallbackLanguageTag;
|
||||
const isCurrentLanguageFallbackLanguage = partialI18n.currentLanguageTag === FALLBACK_LANGUAGE_TAG;
|
||||
|
||||
const result: Result = {
|
||||
i18n: {
|
||||
|
@ -3,7 +3,7 @@ import { createGetI18n, type GenericI18n_noJsx, type KcContextLike, type Message
|
||||
import { GenericI18n } from "./GenericI18n";
|
||||
import { Reflect } from "tsafe/Reflect";
|
||||
|
||||
export function createUseI18n<MessageKey_themeDefined extends string = never>(messageBundle: {
|
||||
export function createUseI18n<MessageKey_themeDefined extends string = never>(messagesByLanguageTag: {
|
||||
[languageTag: string]: { [key in MessageKey_themeDefined]: string };
|
||||
}) {
|
||||
type MessageKey = MessageKey_defaultSet | MessageKey_themeDefined;
|
||||
@ -13,24 +13,16 @@ export function createUseI18n<MessageKey_themeDefined extends string = never>(me
|
||||
const { withJsx } = (() => {
|
||||
const cache = new WeakMap<GenericI18n_noJsx<MessageKey>, GenericI18n<MessageKey>>();
|
||||
|
||||
function renderHtmlString(htmlString: string): JSX.Element {
|
||||
function renderHtmlString(params: { htmlString: string; msgKey: string }): JSX.Element {
|
||||
const { htmlString, msgKey } = params;
|
||||
return (
|
||||
<div
|
||||
style={{ display: "inline-block" }}
|
||||
data-kc-msg={msgKey}
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: htmlString
|
||||
}}
|
||||
/>
|
||||
);
|
||||
/*
|
||||
return (
|
||||
<span
|
||||
dangerouslySetInnerHTML={{
|
||||
"__html": htmlString
|
||||
}}
|
||||
/>
|
||||
);
|
||||
*/
|
||||
}
|
||||
|
||||
function withJsx(i18n_noJsx: GenericI18n_noJsx<MessageKey>): I18n {
|
||||
@ -46,8 +38,8 @@ export function createUseI18n<MessageKey_themeDefined extends string = never>(me
|
||||
|
||||
const i18n: I18n = {
|
||||
...i18n_noJsx,
|
||||
msg: (...args) => renderHtmlString(i18n_noJsx.msgStr(...args)),
|
||||
advancedMsg: (...args) => renderHtmlString(i18n_noJsx.advancedMsgStr(...args))
|
||||
msg: (msgKey, ...args) => renderHtmlString({ htmlString: i18n_noJsx.msgStr(msgKey, ...args), msgKey }),
|
||||
advancedMsg: (msgKey, ...args) => renderHtmlString({ htmlString: i18n_noJsx.advancedMsgStr(msgKey, ...args), msgKey })
|
||||
};
|
||||
|
||||
cache.set(i18n_noJsx, i18n);
|
||||
@ -58,7 +50,20 @@ export function createUseI18n<MessageKey_themeDefined extends string = never>(me
|
||||
return { withJsx };
|
||||
})();
|
||||
|
||||
const { getI18n } = createGetI18n(messageBundle);
|
||||
add_style: {
|
||||
const attributeName = "data-kc-i18n";
|
||||
|
||||
// Check if already exists in head
|
||||
if (document.querySelector(`style[${attributeName}]`) !== null) {
|
||||
break add_style;
|
||||
}
|
||||
|
||||
const styleElement = document.createElement("style");
|
||||
styleElement.attributes.setNamedItem(document.createAttribute(attributeName));
|
||||
(styleElement.textContent = `[data-kc-msg] { display: inline-block; }`), document.head.prepend(styleElement);
|
||||
}
|
||||
|
||||
const { getI18n } = createGetI18n(messagesByLanguageTag);
|
||||
|
||||
function useI18n(params: { kcContext: KcContextLike }): { i18n: I18n } {
|
||||
const { kcContext } = params;
|
||||
|
@ -429,6 +429,28 @@ export function useUserProfileForm(params: UseUserProfileFormParams): ReturnType
|
||||
});
|
||||
}
|
||||
|
||||
trigger_password_confirm_validation_on_password_change: {
|
||||
if (!doMakeUserConfirmPassword) {
|
||||
break trigger_password_confirm_validation_on_password_change;
|
||||
}
|
||||
|
||||
if (formAction.name !== "password") {
|
||||
break trigger_password_confirm_validation_on_password_change;
|
||||
}
|
||||
|
||||
state = reducer(state, {
|
||||
action: "update",
|
||||
name: "password-confirm",
|
||||
valueOrValues: (() => {
|
||||
const formFieldState = state.formFieldStates.find(({ attribute }) => attribute.name === "password-confirm");
|
||||
|
||||
assert(formFieldState !== undefined);
|
||||
|
||||
return formFieldState.valueOrValues;
|
||||
})()
|
||||
});
|
||||
}
|
||||
|
||||
return;
|
||||
case "focus lost":
|
||||
if (formFieldState.hasLostFocusAtLeastOnce instanceof Array) {
|
||||
|
@ -1,9 +1,9 @@
|
||||
import { join as pathJoin, relative as pathRelative, sep as pathSep } from "path";
|
||||
import type { Plugin } from "vite";
|
||||
import {
|
||||
basenameOfTheKeycloakifyResourcesDir,
|
||||
keycloak_resources,
|
||||
vitePluginSubScriptEnvNames
|
||||
BASENAME_OF_KEYCLOAKIFY_RESOURCES_DIR,
|
||||
KEYCLOAK_RESOURCES,
|
||||
VITE_PLUGIN_SUB_SCRIPTS_ENV_NAMES
|
||||
} from "../bin/shared/constants";
|
||||
import { id } from "tsafe/id";
|
||||
import { rm } from "../bin/tools/fs.rm";
|
||||
@ -38,7 +38,7 @@ export function keycloakify(params?: Params) {
|
||||
|
||||
run_post_build_script_case: {
|
||||
const envValue =
|
||||
process.env[vitePluginSubScriptEnvNames.runPostBuildScript];
|
||||
process.env[VITE_PLUGIN_SUB_SCRIPTS_ENV_NAMES.RUN_POST_BUILD_SCRIPT];
|
||||
|
||||
if (envValue === undefined) {
|
||||
break run_post_build_script_case;
|
||||
@ -94,13 +94,13 @@ export function keycloakify(params?: Params) {
|
||||
|
||||
resolve_vite_config_case: {
|
||||
const envValue =
|
||||
process.env[vitePluginSubScriptEnvNames.resolveViteConfig];
|
||||
process.env[VITE_PLUGIN_SUB_SCRIPTS_ENV_NAMES.RESOLVE_VITE_CONFIG];
|
||||
|
||||
if (envValue === undefined) {
|
||||
break resolve_vite_config_case;
|
||||
}
|
||||
|
||||
console.log(vitePluginSubScriptEnvNames.resolveViteConfig);
|
||||
console.log(VITE_PLUGIN_SUB_SCRIPTS_ENV_NAMES.RESOLVE_VITE_CONFIG);
|
||||
|
||||
console.log(
|
||||
JSON.stringify(
|
||||
@ -172,7 +172,7 @@ export function keycloakify(params?: Params) {
|
||||
`(`,
|
||||
`(window.kcContext === undefined || import.meta.env.MODE === "development")?`,
|
||||
`"${urlPathname ?? "/"}":`,
|
||||
`(window.kcContext.url.resourcesPath + "/${basenameOfTheKeycloakifyResourcesDir}/")`,
|
||||
`(window.kcContext["x-keycloakify"].resourcesPath + "/${BASENAME_OF_KEYCLOAKIFY_RESOURCES_DIR}/")`,
|
||||
`)`
|
||||
].join("")
|
||||
);
|
||||
@ -205,7 +205,7 @@ export function keycloakify(params?: Params) {
|
||||
|
||||
assert(buildDirPath !== undefined);
|
||||
|
||||
await rm(pathJoin(buildDirPath, keycloak_resources), {
|
||||
await rm(pathJoin(buildDirPath, KEYCLOAK_RESOURCES), {
|
||||
recursive: true,
|
||||
force: true
|
||||
});
|
||||
|
@ -2,7 +2,7 @@ import { replaceImportsInJsCode_vite } from "keycloakify/bin/keycloakify/replace
|
||||
import { replaceImportsInJsCode_webpack } from "keycloakify/bin/keycloakify/replacers/replaceImportsInJsCode/webpack";
|
||||
import { replaceImportsInCssCode } from "keycloakify/bin/keycloakify/replacers/replaceImportsInCssCode";
|
||||
import { expect, it, describe } from "vitest";
|
||||
import { basenameOfTheKeycloakifyResourcesDir } from "keycloakify/bin/shared/constants";
|
||||
import { BASENAME_OF_KEYCLOAKIFY_RESOURCES_DIR } from "keycloakify/bin/shared/constants";
|
||||
|
||||
describe("js replacer - vite", () => {
|
||||
it("replaceImportsInJsCode_vite - 1", () => {
|
||||
@ -87,13 +87,13 @@ describe("js replacer - vite", () => {
|
||||
});
|
||||
|
||||
const fixedJsCodeExpected = `
|
||||
S=(window.kcContext.url.resourcesPath + "/${basenameOfTheKeycloakifyResourcesDir}/assets/keycloakify-logo-mqjydaoZ.png"),H=(()=>{
|
||||
S=(window.kcContext["x-keycloakify"].resourcesPath + "/${BASENAME_OF_KEYCLOAKIFY_RESOURCES_DIR}/assets/keycloakify-logo-mqjydaoZ.png"),H=(()=>{
|
||||
|
||||
function __vite__mapDeps(indexes) {
|
||||
if (!__vite__mapDeps.viteFileDeps) {
|
||||
__vite__mapDeps.viteFileDeps = [
|
||||
(window.kcContext.url.resourcesPath.substring(1) + "/${basenameOfTheKeycloakifyResourcesDir}/assets/Login-dJpPRzM4.js"),
|
||||
(window.kcContext.url.resourcesPath.substring(1) + "/${basenameOfTheKeycloakifyResourcesDir}/assets/index-XwzrZ5Gu.js")
|
||||
(window.kcContext["x-keycloakify"].resourcesPath.substring(1) + "/${BASENAME_OF_KEYCLOAKIFY_RESOURCES_DIR}/assets/Login-dJpPRzM4.js"),
|
||||
(window.kcContext["x-keycloakify"].resourcesPath.substring(1) + "/${BASENAME_OF_KEYCLOAKIFY_RESOURCES_DIR}/assets/index-XwzrZ5Gu.js")
|
||||
]
|
||||
}
|
||||
return indexes.map((i) => __vite__mapDeps.viteFileDeps[i])
|
||||
@ -146,13 +146,13 @@ describe("js replacer - vite", () => {
|
||||
});
|
||||
|
||||
const fixedJsCodeExpected = `
|
||||
S=(window.kcContext.url.resourcesPath + "/${basenameOfTheKeycloakifyResourcesDir}/foo/bar/keycloakify-logo-mqjydaoZ.png"),H=(()=>{
|
||||
S=(window.kcContext["x-keycloakify"].resourcesPath + "/${BASENAME_OF_KEYCLOAKIFY_RESOURCES_DIR}/foo/bar/keycloakify-logo-mqjydaoZ.png"),H=(()=>{
|
||||
|
||||
function __vite__mapDeps(indexes) {
|
||||
if (!__vite__mapDeps.viteFileDeps) {
|
||||
__vite__mapDeps.viteFileDeps = [
|
||||
(window.kcContext.url.resourcesPath.substring(1) + "/${basenameOfTheKeycloakifyResourcesDir}/foo/bar/Login-dJpPRzM4.js"),
|
||||
(window.kcContext.url.resourcesPath.substring(1) + "/${basenameOfTheKeycloakifyResourcesDir}/foo/bar/index-XwzrZ5Gu.js")
|
||||
(window.kcContext["x-keycloakify"].resourcesPath.substring(1) + "/${BASENAME_OF_KEYCLOAKIFY_RESOURCES_DIR}/foo/bar/Login-dJpPRzM4.js"),
|
||||
(window.kcContext["x-keycloakify"].resourcesPath.substring(1) + "/${BASENAME_OF_KEYCLOAKIFY_RESOURCES_DIR}/foo/bar/index-XwzrZ5Gu.js")
|
||||
]
|
||||
}
|
||||
return indexes.map((i) => __vite__mapDeps.viteFileDeps[i])
|
||||
@ -205,13 +205,13 @@ describe("js replacer - vite", () => {
|
||||
});
|
||||
|
||||
const fixedJsCodeExpected = `
|
||||
S=(window.kcContext.url.resourcesPath + "/${basenameOfTheKeycloakifyResourcesDir}/assets/keycloakify-logo-mqjydaoZ.png"),H=(()=>{
|
||||
S=(window.kcContext["x-keycloakify"].resourcesPath + "/${BASENAME_OF_KEYCLOAKIFY_RESOURCES_DIR}/assets/keycloakify-logo-mqjydaoZ.png"),H=(()=>{
|
||||
|
||||
function __vite__mapDeps(indexes) {
|
||||
if (!__vite__mapDeps.viteFileDeps) {
|
||||
__vite__mapDeps.viteFileDeps = [
|
||||
(window.kcContext.url.resourcesPath.substring(1) + "/${basenameOfTheKeycloakifyResourcesDir}/assets/Login-dJpPRzM4.js"),
|
||||
(window.kcContext.url.resourcesPath.substring(1) + "/${basenameOfTheKeycloakifyResourcesDir}/assets/index-XwzrZ5Gu.js")
|
||||
(window.kcContext["x-keycloakify"].resourcesPath.substring(1) + "/${BASENAME_OF_KEYCLOAKIFY_RESOURCES_DIR}/assets/Login-dJpPRzM4.js"),
|
||||
(window.kcContext["x-keycloakify"].resourcesPath.substring(1) + "/${BASENAME_OF_KEYCLOAKIFY_RESOURCES_DIR}/assets/index-XwzrZ5Gu.js")
|
||||
]
|
||||
}
|
||||
return indexes.map((i) => __vite__mapDeps.viteFileDeps[i])
|
||||
@ -267,13 +267,13 @@ describe("js replacer - webpack", () => {
|
||||
|
||||
const fixedJsCodeExpected = `
|
||||
function f() {
|
||||
return window.kcContext.url.resourcesPath + "/${basenameOfTheKeycloakifyResourcesDir}/static/js/" + ({}[e] || e) + "." + {
|
||||
return window.kcContext["x-keycloakify"].resourcesPath + "/${BASENAME_OF_KEYCLOAKIFY_RESOURCES_DIR}/static/js/" + ({}[e] || e) + "." + {
|
||||
3: "0664cdc0"
|
||||
}[e] + ".chunk.js"
|
||||
}
|
||||
|
||||
function sameAsF() {
|
||||
return window.kcContext.url.resourcesPath + "/${basenameOfTheKeycloakifyResourcesDir}/static/js/" + ({}[e] || e) + "." + {
|
||||
return window.kcContext["x-keycloakify"].resourcesPath + "/${BASENAME_OF_KEYCLOAKIFY_RESOURCES_DIR}/static/js/" + ({}[e] || e) + "." + {
|
||||
3: "0664cdc0"
|
||||
}[e] + ".chunk.js"
|
||||
}
|
||||
@ -282,13 +282,13 @@ describe("js replacer - webpack", () => {
|
||||
var pd = Object.getOwnPropertyDescriptor(__webpack_require__, "p");
|
||||
if( pd === undefined || pd.configurable ){
|
||||
Object.defineProperty(__webpack_require__, "p", {
|
||||
get: function() { return window.kcContext.url.resourcesPath; },
|
||||
get: function() { return window.kcContext["x-keycloakify"].resourcesPath; },
|
||||
set: function() {}
|
||||
});
|
||||
}
|
||||
return "u";
|
||||
})()] = function(e) {
|
||||
return "/${basenameOfTheKeycloakifyResourcesDir}/static/js/" + e + "." + {
|
||||
return "/${BASENAME_OF_KEYCLOAKIFY_RESOURCES_DIR}/static/js/" + e + "." + {
|
||||
147: "6c5cee76",
|
||||
787: "8da10fcf",
|
||||
922: "be170a73"
|
||||
@ -299,13 +299,13 @@ describe("js replacer - webpack", () => {
|
||||
var pd = Object.getOwnPropertyDescriptor(t, "p");
|
||||
if( pd === undefined || pd.configurable ){
|
||||
Object.defineProperty(t, "p", {
|
||||
get: function() { return window.kcContext.url.resourcesPath; },
|
||||
get: function() { return window.kcContext["x-keycloakify"].resourcesPath; },
|
||||
set: function() {}
|
||||
});
|
||||
}
|
||||
return "miniCssF";
|
||||
})()] = function(e) {
|
||||
return "/${basenameOfTheKeycloakifyResourcesDir}/static/css/" + e + "." + {
|
||||
return "/${BASENAME_OF_KEYCLOAKIFY_RESOURCES_DIR}/static/css/" + e + "." + {
|
||||
164:"dcfd7749",
|
||||
908:"67c9ed2c"
|
||||
} [e] + ".chunk.css"
|
||||
@ -315,23 +315,23 @@ describe("js replacer - webpack", () => {
|
||||
var pd = Object.getOwnPropertyDescriptor(n, "p");
|
||||
if( pd === undefined || pd.configurable ){
|
||||
Object.defineProperty(n, "p", {
|
||||
get: function() { return window.kcContext.url.resourcesPath; },
|
||||
get: function() { return window.kcContext["x-keycloakify"].resourcesPath; },
|
||||
set: function() {}
|
||||
});
|
||||
}
|
||||
return "u";
|
||||
})()] = e => "/${basenameOfTheKeycloakifyResourcesDir}/static/js/"+e+"."+{69:"4f205f87",128:"49264537",453:"b2fed72e",482:"f0106901"}[e]+".chunk.js"
|
||||
})()] = e => "/${BASENAME_OF_KEYCLOAKIFY_RESOURCES_DIR}/static/js/"+e+"."+{69:"4f205f87",128:"49264537",453:"b2fed72e",482:"f0106901"}[e]+".chunk.js"
|
||||
|
||||
t[(function(){
|
||||
var pd = Object.getOwnPropertyDescriptor(t, "p");
|
||||
if( pd === undefined || pd.configurable ){
|
||||
Object.defineProperty(t, "p", {
|
||||
get: function() { return window.kcContext.url.resourcesPath; },
|
||||
get: function() { return window.kcContext["x-keycloakify"].resourcesPath; },
|
||||
set: function() {}
|
||||
});
|
||||
}
|
||||
return "miniCssF";
|
||||
})()] = e => "/${basenameOfTheKeycloakifyResourcesDir}/static/css/"+e+"."+{164:"dcfd7749",908:"67c9ed2c"}[e]+".chunk.css"
|
||||
})()] = e => "/${BASENAME_OF_KEYCLOAKIFY_RESOURCES_DIR}/static/css/"+e+"."+{164:"dcfd7749",908:"67c9ed2c"}[e]+".chunk.css"
|
||||
`;
|
||||
|
||||
expect(isSameCode(fixedJsCode, fixedJsCodeExpected)).toBe(true);
|
||||
@ -394,9 +394,16 @@ describe("css replacer", () => {
|
||||
.my-div3 {
|
||||
background-image: url(/assets/media/something.svg);
|
||||
}
|
||||
|
||||
.my-div4 {
|
||||
background-image: url("/assets/media/something(cool).svg");
|
||||
}
|
||||
|
||||
.my-div5 {
|
||||
background-image: url('/assets/media/something(cool).svg');
|
||||
}
|
||||
`,
|
||||
cssFileRelativeDirPath: "assets/",
|
||||
isAccountV3: false,
|
||||
buildContext: {
|
||||
urlPathname: undefined
|
||||
}
|
||||
@ -404,15 +411,23 @@ describe("css replacer", () => {
|
||||
|
||||
const fixedCssCodeExpected = `
|
||||
.my-div {
|
||||
background: url(../background.png) no-repeat center center;
|
||||
background: url("../background.png") no-repeat center center;
|
||||
}
|
||||
|
||||
.my-div2 {
|
||||
background: url(background.png) repeat center center;
|
||||
background: url("background.png") repeat center center;
|
||||
}
|
||||
|
||||
.my-div3 {
|
||||
background-image: url(media/something.svg);
|
||||
background-image: url("media/something.svg");
|
||||
}
|
||||
|
||||
.my-div4 {
|
||||
background-image: url("media/something(cool).svg");
|
||||
}
|
||||
|
||||
.my-div5 {
|
||||
background-image: url("media/something(cool).svg");
|
||||
}
|
||||
`;
|
||||
|
||||
@ -435,7 +450,6 @@ describe("css replacer", () => {
|
||||
}
|
||||
`,
|
||||
cssFileRelativeDirPath: "assets/",
|
||||
isAccountV3: false,
|
||||
buildContext: {
|
||||
urlPathname: "/a/b/"
|
||||
}
|
||||
@ -443,15 +457,15 @@ describe("css replacer", () => {
|
||||
|
||||
const fixedCssCodeExpected = `
|
||||
.my-div {
|
||||
background: url(../background.png) no-repeat center center;
|
||||
background: url("../background.png") no-repeat center center;
|
||||
}
|
||||
|
||||
.my-div2 {
|
||||
background: url(background.png) repeat center center;
|
||||
background: url("background.png") repeat center center;
|
||||
}
|
||||
|
||||
.my-div3 {
|
||||
background-image: url(media/something.svg);
|
||||
background-image: url("media/something.svg");
|
||||
}
|
||||
`;
|
||||
|
||||
@ -474,7 +488,6 @@ describe("css replacer", () => {
|
||||
}
|
||||
`,
|
||||
cssFileRelativeDirPath: undefined,
|
||||
isAccountV3: false,
|
||||
buildContext: {
|
||||
urlPathname: "/a/b/"
|
||||
}
|
||||
@ -482,15 +495,15 @@ describe("css replacer", () => {
|
||||
|
||||
const fixedCssCodeExpected = `
|
||||
.my-div {
|
||||
background: url(\${url.resourcesPath}/${basenameOfTheKeycloakifyResourcesDir}/background.png) no-repeat center center;
|
||||
background: url("\${xKeycloakify.resourcesPath}/${BASENAME_OF_KEYCLOAKIFY_RESOURCES_DIR}/background.png") no-repeat center center;
|
||||
}
|
||||
|
||||
.my-div2 {
|
||||
background: url(\${url.resourcesPath}/${basenameOfTheKeycloakifyResourcesDir}/assets/background.png) repeat center center;
|
||||
background: url("\${xKeycloakify.resourcesPath}/${BASENAME_OF_KEYCLOAKIFY_RESOURCES_DIR}/assets/background.png") repeat center center;
|
||||
}
|
||||
|
||||
.my-div3 {
|
||||
background-image: url(\${url.resourcesPath}/${basenameOfTheKeycloakifyResourcesDir}/assets/media/something.svg);
|
||||
background-image: url("\${xKeycloakify.resourcesPath}/${BASENAME_OF_KEYCLOAKIFY_RESOURCES_DIR}/assets/media/something.svg");
|
||||
}
|
||||
`;
|
||||
|
||||
@ -513,7 +526,6 @@ describe("css replacer", () => {
|
||||
}
|
||||
`,
|
||||
cssFileRelativeDirPath: undefined,
|
||||
isAccountV3: false,
|
||||
buildContext: {
|
||||
urlPathname: undefined
|
||||
}
|
||||
@ -521,15 +533,15 @@ describe("css replacer", () => {
|
||||
|
||||
const fixedCssCodeExpected = `
|
||||
.my-div {
|
||||
background: url(\${url.resourcesPath}/${basenameOfTheKeycloakifyResourcesDir}/background.png) no-repeat center center;
|
||||
background: url("\${xKeycloakify.resourcesPath}/${BASENAME_OF_KEYCLOAKIFY_RESOURCES_DIR}/background.png") no-repeat center center;
|
||||
}
|
||||
|
||||
.my-div2 {
|
||||
background: url(\${url.resourcesPath}/${basenameOfTheKeycloakifyResourcesDir}/assets/background.png) repeat center center;
|
||||
background: url("\${xKeycloakify.resourcesPath}/${BASENAME_OF_KEYCLOAKIFY_RESOURCES_DIR}/assets/background.png") repeat center center;
|
||||
}
|
||||
|
||||
.my-div3 {
|
||||
background-image: url(\${url.resourcesPath}/${basenameOfTheKeycloakifyResourcesDir}/assets/media/something.svg);
|
||||
background-image: url("\${xKeycloakify.resourcesPath}/${BASENAME_OF_KEYCLOAKIFY_RESOURCES_DIR}/assets/media/something.svg");
|
||||
}
|
||||
`;
|
||||
|
||||
|
Reference in New Issue
Block a user