keycloakify 6.0.2 → 6.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -1
- package/bin/create-keycloak-email-directory.js +9 -4
- package/bin/create-keycloak-email-directory.js.map +1 -1
- package/bin/download-builtin-keycloak-theme.d.ts +1 -0
- package/bin/download-builtin-keycloak-theme.js +13 -6
- package/bin/download-builtin-keycloak-theme.js.map +1 -1
- package/bin/generate-i18n-messages.js +8 -3
- package/bin/generate-i18n-messages.js.map +1 -1
- package/bin/keycloakify/BuildOptions.d.ts +2 -0
- package/bin/keycloakify/BuildOptions.js +3 -2
- package/bin/keycloakify/BuildOptions.js.map +1 -1
- package/bin/keycloakify/generateFtl/generateFtl.d.ts +1 -1
- package/bin/keycloakify/generateFtl/generateFtl.js +2 -1
- package/bin/keycloakify/generateFtl/generateFtl.js.map +1 -1
- package/bin/keycloakify/generateKeycloakThemeResources.d.ts +1 -0
- package/bin/keycloakify/generateKeycloakThemeResources.js +5 -2
- package/bin/keycloakify/generateKeycloakThemeResources.js.map +1 -1
- package/bin/keycloakify/keycloakify.js +8 -4
- package/bin/keycloakify/keycloakify.js.map +1 -1
- package/bin/tools/cliOptions.d.ts +5 -0
- package/bin/tools/cliOptions.js +16 -0
- package/bin/tools/cliOptions.js.map +1 -0
- package/bin/tools/downloadAndUnzip.d.ts +1 -0
- package/bin/tools/downloadAndUnzip.js +1 -1
- package/bin/tools/downloadAndUnzip.js.map +1 -1
- package/bin/tools/logger.d.ts +12 -0
- package/bin/tools/logger.js +23 -0
- package/bin/tools/logger.js.map +1 -0
- package/bin/tsconfig.tsbuildinfo +1 -1
- package/lib/components/KcApp.js +3 -0
- package/lib/components/KcApp.js.map +1 -1
- package/lib/components/RegisterUserProfile.js +3 -62
- package/lib/components/RegisterUserProfile.js.map +1 -1
- package/lib/components/UpdateUserProfile.d.ts +9 -0
- package/lib/components/UpdateUserProfile.js +32 -0
- package/lib/components/UpdateUserProfile.js.map +1 -0
- package/lib/components/shared/UserProfileCommons.d.ts +17 -0
- package/lib/components/shared/UserProfileCommons.js +76 -0
- package/lib/components/shared/UserProfileCommons.js.map +1 -0
- package/lib/getKcContext/KcContextBase.d.ts +9 -1
- package/lib/getKcContext/KcContextBase.js.map +1 -1
- package/lib/getKcContext/kcContextMocks/kcContextMocks.js +103 -98
- package/lib/getKcContext/kcContextMocks/kcContextMocks.js.map +1 -1
- package/lib/i18n/index.js +0 -7
- package/lib/i18n/index.js.map +1 -1
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/lib/useFormValidationSlice.d.ts +1 -1
- package/package.json +20 -2
- package/src/bin/create-keycloak-email-directory.ts +8 -3
- package/src/bin/download-builtin-keycloak-theme.ts +11 -5
- package/src/bin/generate-i18n-messages.ts +9 -3
- package/src/bin/keycloakify/BuildOptions.ts +5 -2
- package/src/bin/keycloakify/generateFtl/generateFtl.ts +2 -1
- package/src/bin/keycloakify/generateKeycloakThemeResources.ts +6 -2
- package/src/bin/keycloakify/keycloakify.ts +8 -3
- package/src/bin/tools/cliOptions.ts +15 -0
- package/src/bin/tools/downloadAndUnzip.ts +8 -2
- package/src/bin/tools/logger.ts +27 -0
- package/src/lib/components/KcApp.tsx +3 -0
- package/src/lib/components/RegisterUserProfile.tsx +10 -157
- package/src/lib/components/UpdateUserProfile.tsx +77 -0
- package/src/lib/components/shared/UserProfileCommons.tsx +172 -0
- package/src/lib/getKcContext/KcContextBase.ts +11 -1
- package/src/lib/getKcContext/kcContextMocks/kcContextMocks.ts +105 -98
- package/src/lib/i18n/index.tsx +0 -10
- package/src/lib/useFormValidationSlice.tsx +1 -1
- package/src/test/bin/generateKeycloakThemeResources.ts +2 -1
- package/src/test/bin/setupSampleReactProject.ts +2 -1
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "keycloakify",
|
3
|
-
"version": "6.0
|
3
|
+
"version": "6.2.0",
|
4
4
|
"description": "Keycloak theme generator for Reacts app",
|
5
5
|
"repository": {
|
6
6
|
"type": "git",
|
@@ -46,11 +46,13 @@
|
|
46
46
|
"src/bin/mockTestingResourcesPath.ts",
|
47
47
|
"src/bin/promptKeycloakVersion.ts",
|
48
48
|
"src/bin/tools/NpmModuleVersion.ts",
|
49
|
+
"src/bin/tools/cliOptions.ts",
|
49
50
|
"src/bin/tools/crawl.ts",
|
50
51
|
"src/bin/tools/downloadAndUnzip.ts",
|
51
52
|
"src/bin/tools/getProjectRoot.ts",
|
52
53
|
"src/bin/tools/grant-exec-perms.ts",
|
53
54
|
"src/bin/tools/isInside.ts",
|
55
|
+
"src/bin/tools/logger.ts",
|
54
56
|
"src/bin/tools/octokit-addons/getLatestsSemVersionedTag.ts",
|
55
57
|
"src/bin/tools/octokit-addons/listTags.ts",
|
56
58
|
"src/bin/tools/pathJoin.ts",
|
@@ -76,6 +78,8 @@
|
|
76
78
|
"src/lib/components/RegisterUserProfile.tsx",
|
77
79
|
"src/lib/components/Template.tsx",
|
78
80
|
"src/lib/components/Terms.tsx",
|
81
|
+
"src/lib/components/UpdateUserProfile.tsx",
|
82
|
+
"src/lib/components/shared/UserProfileCommons.tsx",
|
79
83
|
"src/lib/getKcContext/KcContextBase.ts",
|
80
84
|
"src/lib/getKcContext/getKcContext.ts",
|
81
85
|
"src/lib/getKcContext/getKcContextFromWindow.ts",
|
@@ -391,6 +395,9 @@
|
|
391
395
|
"bin/tools/NpmModuleVersion.d.ts",
|
392
396
|
"bin/tools/NpmModuleVersion.js",
|
393
397
|
"bin/tools/NpmModuleVersion.js.map",
|
398
|
+
"bin/tools/cliOptions.d.ts",
|
399
|
+
"bin/tools/cliOptions.js",
|
400
|
+
"bin/tools/cliOptions.js.map",
|
394
401
|
"bin/tools/crawl.d.ts",
|
395
402
|
"bin/tools/crawl.js",
|
396
403
|
"bin/tools/crawl.js.map",
|
@@ -406,6 +413,9 @@
|
|
406
413
|
"bin/tools/isInside.d.ts",
|
407
414
|
"bin/tools/isInside.js",
|
408
415
|
"bin/tools/isInside.js.map",
|
416
|
+
"bin/tools/logger.d.ts",
|
417
|
+
"bin/tools/logger.js",
|
418
|
+
"bin/tools/logger.js.map",
|
409
419
|
"bin/tools/octokit-addons/getLatestsSemVersionedTag.d.ts",
|
410
420
|
"bin/tools/octokit-addons/getLatestsSemVersionedTag.js",
|
411
421
|
"bin/tools/octokit-addons/getLatestsSemVersionedTag.js.map",
|
@@ -479,6 +489,12 @@
|
|
479
489
|
"lib/components/Terms.d.ts",
|
480
490
|
"lib/components/Terms.js",
|
481
491
|
"lib/components/Terms.js.map",
|
492
|
+
"lib/components/UpdateUserProfile.d.ts",
|
493
|
+
"lib/components/UpdateUserProfile.js",
|
494
|
+
"lib/components/UpdateUserProfile.js.map",
|
495
|
+
"lib/components/shared/UserProfileCommons.d.ts",
|
496
|
+
"lib/components/shared/UserProfileCommons.js",
|
497
|
+
"lib/components/shared/UserProfileCommons.js.map",
|
482
498
|
"lib/getKcContext/KcContextBase.d.ts",
|
483
499
|
"lib/getKcContext/KcContextBase.js",
|
484
500
|
"lib/getKcContext/KcContextBase.js.map",
|
@@ -1237,6 +1253,7 @@
|
|
1237
1253
|
"devDependencies": {
|
1238
1254
|
"@emotion/react": "^11.4.1",
|
1239
1255
|
"@types/memoizee": "^0.4.7",
|
1256
|
+
"@types/minimist": "^1.2.2",
|
1240
1257
|
"@types/node": "^17.0.25",
|
1241
1258
|
"@types/react": "18.0.9",
|
1242
1259
|
"copyfiles": "^2.4.1",
|
@@ -1255,12 +1272,13 @@
|
|
1255
1272
|
"evt": "^2.4.0",
|
1256
1273
|
"memoizee": "^0.4.15",
|
1257
1274
|
"minimal-polyfills": "^2.2.2",
|
1275
|
+
"minimist": "^1.2.6",
|
1258
1276
|
"path-browserify": "^1.0.1",
|
1259
1277
|
"powerhooks": "^0.20.15",
|
1260
1278
|
"react-markdown": "^5.0.3",
|
1261
1279
|
"scripting-tools": "^0.19.13",
|
1262
1280
|
"tsafe": "^1.0.1",
|
1263
|
-
"tss-react": "^4.1.
|
1281
|
+
"tss-react": "^4.1.3",
|
1264
1282
|
"zod": "^3.17.10"
|
1265
1283
|
}
|
1266
1284
|
}
|
@@ -6,11 +6,15 @@ import { join as pathJoin, basename as pathBasename } from "path";
|
|
6
6
|
import { transformCodebase } from "./tools/transformCodebase";
|
7
7
|
import { promptKeycloakVersion } from "./promptKeycloakVersion";
|
8
8
|
import * as fs from "fs";
|
9
|
+
import { getCliOptions } from "./tools/cliOptions";
|
10
|
+
import { getLogger } from "./tools/logger";
|
9
11
|
|
10
12
|
if (require.main === module) {
|
11
13
|
(async () => {
|
14
|
+
const { isSilent } = getCliOptions(process.argv.slice(2));
|
15
|
+
const logger = getLogger({ isSilent });
|
12
16
|
if (fs.existsSync(keycloakThemeEmailDirPath)) {
|
13
|
-
|
17
|
+
logger.warn(`There is already a ./${pathBasename(keycloakThemeEmailDirPath)} directory in your project. Aborting.`);
|
14
18
|
|
15
19
|
process.exit(-1);
|
16
20
|
}
|
@@ -21,7 +25,8 @@ if (require.main === module) {
|
|
21
25
|
|
22
26
|
downloadBuiltinKeycloakTheme({
|
23
27
|
keycloakVersion,
|
24
|
-
"destDirPath": builtinKeycloakThemeTmpDirPath
|
28
|
+
"destDirPath": builtinKeycloakThemeTmpDirPath,
|
29
|
+
isSilent
|
25
30
|
});
|
26
31
|
|
27
32
|
transformCodebase({
|
@@ -29,7 +34,7 @@ if (require.main === module) {
|
|
29
34
|
"destDirPath": keycloakThemeEmailDirPath
|
30
35
|
});
|
31
36
|
|
32
|
-
|
37
|
+
logger.log(`./${pathBasename(keycloakThemeEmailDirPath)} ready to be customized`);
|
33
38
|
|
34
39
|
fs.rmSync(builtinKeycloakThemeTmpDirPath, { "recursive": true, "force": true });
|
35
40
|
})();
|
@@ -4,31 +4,37 @@ import { keycloakThemeBuildingDirPath } from "./keycloakify";
|
|
4
4
|
import { join as pathJoin } from "path";
|
5
5
|
import { downloadAndUnzip } from "./tools/downloadAndUnzip";
|
6
6
|
import { promptKeycloakVersion } from "./promptKeycloakVersion";
|
7
|
+
import { getCliOptions } from "./tools/cliOptions";
|
8
|
+
import { getLogger } from "./tools/logger";
|
7
9
|
|
8
|
-
export function downloadBuiltinKeycloakTheme(params: { keycloakVersion: string; destDirPath: string }) {
|
9
|
-
const { keycloakVersion, destDirPath } = params;
|
10
|
+
export function downloadBuiltinKeycloakTheme(params: { keycloakVersion: string; destDirPath: string; isSilent: boolean }) {
|
11
|
+
const { keycloakVersion, destDirPath, isSilent } = params;
|
10
12
|
|
11
13
|
for (const ext of ["", "-community"]) {
|
12
14
|
downloadAndUnzip({
|
13
15
|
"destDirPath": destDirPath,
|
14
16
|
"url": `https://github.com/keycloak/keycloak/archive/refs/tags/${keycloakVersion}.zip`,
|
15
17
|
"pathOfDirToExtractInArchive": `keycloak-${keycloakVersion}/themes/src/main/resources${ext}/theme`,
|
16
|
-
"cacheDirPath": pathJoin(keycloakThemeBuildingDirPath, ".cache")
|
18
|
+
"cacheDirPath": pathJoin(keycloakThemeBuildingDirPath, ".cache"),
|
19
|
+
isSilent
|
17
20
|
});
|
18
21
|
}
|
19
22
|
}
|
20
23
|
|
21
24
|
if (require.main === module) {
|
22
25
|
(async () => {
|
26
|
+
const { isSilent } = getCliOptions(process.argv.slice(2));
|
27
|
+
const logger = getLogger({ isSilent });
|
23
28
|
const { keycloakVersion } = await promptKeycloakVersion();
|
24
29
|
|
25
30
|
const destDirPath = pathJoin(keycloakThemeBuildingDirPath, "src", "main", "resources", "theme");
|
26
31
|
|
27
|
-
|
32
|
+
logger.log(`Downloading builtins theme of Keycloak ${keycloakVersion} here ${destDirPath}`);
|
28
33
|
|
29
34
|
downloadBuiltinKeycloakTheme({
|
30
35
|
keycloakVersion,
|
31
|
-
destDirPath
|
36
|
+
destDirPath,
|
37
|
+
isSilent
|
32
38
|
});
|
33
39
|
})();
|
34
40
|
}
|
@@ -5,6 +5,8 @@ import { crawl } from "./tools/crawl";
|
|
5
5
|
import { downloadBuiltinKeycloakTheme } from "./download-builtin-keycloak-theme";
|
6
6
|
import { getProjectRoot } from "./tools/getProjectRoot";
|
7
7
|
import { rm_rf, rm_r } from "./tools/rm";
|
8
|
+
import { getCliOptions } from "./tools/cliOptions";
|
9
|
+
import { getLogger } from "./tools/logger";
|
8
10
|
|
9
11
|
//NOTE: To run without argument when we want to generate src/i18n/generated_kcMessages files,
|
10
12
|
// update the version array for generating for newer version.
|
@@ -12,8 +14,11 @@ import { rm_rf, rm_r } from "./tools/rm";
|
|
12
14
|
//@ts-ignore
|
13
15
|
const propertiesParser = require("properties-parser");
|
14
16
|
|
17
|
+
const { isSilent } = getCliOptions(process.argv.slice(2));
|
18
|
+
const logger = getLogger({ isSilent });
|
19
|
+
|
15
20
|
for (const keycloakVersion of ["11.0.3", "15.0.2", "18.0.1"]) {
|
16
|
-
|
21
|
+
logger.log(JSON.stringify({ keycloakVersion }));
|
17
22
|
|
18
23
|
const tmpDirPath = pathJoin(getProjectRoot(), "tmp_xImOef9dOd44");
|
19
24
|
|
@@ -21,7 +26,8 @@ for (const keycloakVersion of ["11.0.3", "15.0.2", "18.0.1"]) {
|
|
21
26
|
|
22
27
|
downloadBuiltinKeycloakTheme({
|
23
28
|
keycloakVersion,
|
24
|
-
"destDirPath": tmpDirPath
|
29
|
+
"destDirPath": tmpDirPath,
|
30
|
+
isSilent
|
25
31
|
});
|
26
32
|
|
27
33
|
type Dictionary = { [idiomId: string]: string };
|
@@ -75,7 +81,7 @@ for (const keycloakVersion of ["11.0.3", "15.0.2", "18.0.1"]) {
|
|
75
81
|
)
|
76
82
|
);
|
77
83
|
|
78
|
-
|
84
|
+
logger.log(`${filePath} wrote`);
|
79
85
|
});
|
80
86
|
});
|
81
87
|
}
|
@@ -35,6 +35,7 @@ export type BuildOptions = BuildOptions.Standalone | BuildOptions.ExternalAssets
|
|
35
35
|
|
36
36
|
export namespace BuildOptions {
|
37
37
|
export type Common = {
|
38
|
+
isSilent: boolean;
|
38
39
|
version: string;
|
39
40
|
themeName: string;
|
40
41
|
extraPages?: string[];
|
@@ -71,8 +72,9 @@ export function readBuildOptions(params: {
|
|
71
72
|
packageJson: string;
|
72
73
|
CNAME: string | undefined;
|
73
74
|
isExternalAssetsCliParamProvided: boolean;
|
75
|
+
isSilent: boolean;
|
74
76
|
}): BuildOptions {
|
75
|
-
const { packageJson, CNAME, isExternalAssetsCliParamProvided } = params;
|
77
|
+
const { packageJson, CNAME, isExternalAssetsCliParamProvided, isSilent } = params;
|
76
78
|
|
77
79
|
const parsedPackageJson = zParsedPackageJson.parse(JSON.parse(packageJson));
|
78
80
|
|
@@ -130,7 +132,8 @@ export function readBuildOptions(params: {
|
|
130
132
|
})(),
|
131
133
|
"version": version,
|
132
134
|
extraPages,
|
133
|
-
extraThemeProperties
|
135
|
+
extraThemeProperties,
|
136
|
+
isSilent
|
134
137
|
};
|
135
138
|
})();
|
136
139
|
|
@@ -27,7 +27,8 @@ export const pageIds = [
|
|
27
27
|
"login-idp-link-email.ftl",
|
28
28
|
"login-page-expired.ftl",
|
29
29
|
"login-config-totp.ftl",
|
30
|
-
"logout-confirm.ftl"
|
30
|
+
"logout-confirm.ftl",
|
31
|
+
"update-user-profile.ftl"
|
31
32
|
] as const;
|
32
33
|
|
33
34
|
export type BuildOptionsLike = BuildOptionsLike.Standalone | BuildOptionsLike.ExternalAssets;
|
@@ -11,6 +11,7 @@ import { isInside } from "../tools/isInside";
|
|
11
11
|
import type { BuildOptions } from "./BuildOptions";
|
12
12
|
import { assert } from "tsafe/assert";
|
13
13
|
import { Reflect } from "tsafe/Reflect";
|
14
|
+
import { getLogger } from "../tools/logger";
|
14
15
|
|
15
16
|
export type BuildOptionsLike = BuildOptionsLike.Standalone | BuildOptionsLike.ExternalAssets;
|
16
17
|
|
@@ -19,6 +20,7 @@ export namespace BuildOptionsLike {
|
|
19
20
|
themeName: string;
|
20
21
|
extraPages?: string[];
|
21
22
|
extraThemeProperties?: string[];
|
23
|
+
isSilent: boolean;
|
22
24
|
};
|
23
25
|
|
24
26
|
export type Standalone = Common & {
|
@@ -60,6 +62,7 @@ export function generateKeycloakThemeResources(params: {
|
|
60
62
|
}): { doBundlesEmailTemplate: boolean } {
|
61
63
|
const { reactAppBuildDirPath, keycloakThemeBuildingDirPath, keycloakThemeEmailDirPath, keycloakVersion, buildOptions } = params;
|
62
64
|
|
65
|
+
const logger = getLogger({ isSilent: buildOptions.isSilent });
|
63
66
|
const themeDirPath = pathJoin(keycloakThemeBuildingDirPath, "src", "main", "resources", "theme", buildOptions.themeName, "login");
|
64
67
|
|
65
68
|
let allCssGlobalsToDefine: Record<string, string> = {};
|
@@ -117,7 +120,7 @@ export function generateKeycloakThemeResources(params: {
|
|
117
120
|
|
118
121
|
email: {
|
119
122
|
if (!fs.existsSync(keycloakThemeEmailDirPath)) {
|
120
|
-
|
123
|
+
logger.log(
|
121
124
|
[
|
122
125
|
`Not bundling email template because ${pathBasename(keycloakThemeEmailDirPath)} does not exist`,
|
123
126
|
`To start customizing the email template, run: 👉 npx create-keycloak-email-directory 👈`
|
@@ -154,7 +157,8 @@ export function generateKeycloakThemeResources(params: {
|
|
154
157
|
|
155
158
|
downloadBuiltinKeycloakTheme({
|
156
159
|
keycloakVersion,
|
157
|
-
"destDirPath": tmpDirPath
|
160
|
+
"destDirPath": tmpDirPath,
|
161
|
+
isSilent: buildOptions.isSilent
|
158
162
|
});
|
159
163
|
|
160
164
|
const themeResourcesDirPath = pathJoin(themeDirPath, "resources");
|
@@ -5,6 +5,8 @@ import * as child_process from "child_process";
|
|
5
5
|
import { generateStartKeycloakTestingContainer } from "./generateStartKeycloakTestingContainer";
|
6
6
|
import * as fs from "fs";
|
7
7
|
import { readBuildOptions } from "./BuildOptions";
|
8
|
+
import { getLogger } from "../tools/logger";
|
9
|
+
import { getCliOptions } from "../tools/cliOptions";
|
8
10
|
|
9
11
|
const reactProjectDirPath = process.cwd();
|
10
12
|
|
@@ -12,7 +14,9 @@ export const keycloakThemeBuildingDirPath = pathJoin(reactProjectDirPath, "build
|
|
12
14
|
export const keycloakThemeEmailDirPath = pathJoin(keycloakThemeBuildingDirPath, "..", "keycloak_email");
|
13
15
|
|
14
16
|
export function main() {
|
15
|
-
|
17
|
+
const { isSilent, hasExternalAssets } = getCliOptions(process.argv.slice(2));
|
18
|
+
const logger = getLogger({ isSilent });
|
19
|
+
logger.log("🔏 Building the keycloak theme...⌚");
|
16
20
|
|
17
21
|
const buildOptions = readBuildOptions({
|
18
22
|
"packageJson": fs.readFileSync(pathJoin(reactProjectDirPath, "package.json")).toString("utf8"),
|
@@ -25,7 +29,8 @@ export function main() {
|
|
25
29
|
|
26
30
|
return fs.readFileSync(cnameFilePath).toString("utf8");
|
27
31
|
})(),
|
28
|
-
"isExternalAssetsCliParamProvided":
|
32
|
+
"isExternalAssetsCliParamProvided": hasExternalAssets,
|
33
|
+
"isSilent": isSilent
|
29
34
|
});
|
30
35
|
|
31
36
|
const { doBundlesEmailTemplate } = generateKeycloakThemeResources({
|
@@ -59,7 +64,7 @@ export function main() {
|
|
59
64
|
buildOptions
|
60
65
|
});
|
61
66
|
|
62
|
-
|
67
|
+
logger.log(
|
63
68
|
[
|
64
69
|
"",
|
65
70
|
`✅ Your keycloak theme has been generated and bundled into ./${pathRelative(reactProjectDirPath, jarFilePath)} 🚀`,
|
@@ -0,0 +1,15 @@
|
|
1
|
+
import parseArgv from "minimist";
|
2
|
+
|
3
|
+
export type CliOptions = {
|
4
|
+
isSilent: boolean;
|
5
|
+
hasExternalAssets: boolean;
|
6
|
+
};
|
7
|
+
|
8
|
+
export const getCliOptions = (processArgv: string[]): CliOptions => {
|
9
|
+
const argv = parseArgv(processArgv);
|
10
|
+
|
11
|
+
return {
|
12
|
+
isSilent: typeof argv["silent"] === "boolean" ? argv["silent"] : false,
|
13
|
+
hasExternalAssets: typeof argv["external-assets"] === "boolean" ? argv["external-assets"] : false
|
14
|
+
};
|
15
|
+
};
|
@@ -6,7 +6,13 @@ import { rm, rm_rf } from "./rm";
|
|
6
6
|
import * as crypto from "crypto";
|
7
7
|
|
8
8
|
/** assert url ends with .zip */
|
9
|
-
export function downloadAndUnzip(params: {
|
9
|
+
export function downloadAndUnzip(params: {
|
10
|
+
isSilent: boolean;
|
11
|
+
url: string;
|
12
|
+
destDirPath: string;
|
13
|
+
pathOfDirToExtractInArchive?: string;
|
14
|
+
cacheDirPath: string;
|
15
|
+
}) {
|
10
16
|
const { url, destDirPath, pathOfDirToExtractInArchive, cacheDirPath } = params;
|
11
17
|
|
12
18
|
const extractDirPath = pathJoin(
|
@@ -54,7 +60,7 @@ export function downloadAndUnzip(params: { url: string; destDirPath: string; pat
|
|
54
60
|
|
55
61
|
const zipFileBasename = pathBasename(url);
|
56
62
|
|
57
|
-
execSync(`curl -L ${url} -o ${zipFileBasename}`, { "cwd": extractDirPath });
|
63
|
+
execSync(`curl -L ${url} -o ${zipFileBasename} ${params.isSilent ? "-s" : ""}`, { "cwd": extractDirPath });
|
58
64
|
|
59
65
|
execSync(`unzip -o ${zipFileBasename}${pathOfDirToExtractInArchive === undefined ? "" : ` "${pathOfDirToExtractInArchive}/**/*"`}`, {
|
60
66
|
"cwd": extractDirPath
|
@@ -0,0 +1,27 @@
|
|
1
|
+
type LoggerOpts = {
|
2
|
+
force?: boolean;
|
3
|
+
};
|
4
|
+
|
5
|
+
type Logger = {
|
6
|
+
log: (message: string, opts?: LoggerOpts) => void;
|
7
|
+
warn: (message: string) => void;
|
8
|
+
error: (message: string) => void;
|
9
|
+
};
|
10
|
+
|
11
|
+
export const getLogger = ({ isSilent }: { isSilent?: boolean } = {}): Logger => {
|
12
|
+
return {
|
13
|
+
log: (message, { force } = {}) => {
|
14
|
+
if (isSilent && !force) {
|
15
|
+
return;
|
16
|
+
}
|
17
|
+
|
18
|
+
console.log(message);
|
19
|
+
},
|
20
|
+
warn: message => {
|
21
|
+
console.warn(message);
|
22
|
+
},
|
23
|
+
error: message => {
|
24
|
+
console.error(message);
|
25
|
+
}
|
26
|
+
};
|
27
|
+
};
|
@@ -20,6 +20,7 @@ const LoginPageExpired = lazy(() => import("./LoginPageExpired"));
|
|
20
20
|
const LoginIdpLinkEmail = lazy(() => import("./LoginIdpLinkEmail"));
|
21
21
|
const LoginConfigTotp = lazy(() => import("./LoginConfigTotp"));
|
22
22
|
const LogoutConfirm = lazy(() => import("./LogoutConfirm"));
|
23
|
+
const UpdateUserProfile = lazy(() => import("./UpdateUserProfile"));
|
23
24
|
|
24
25
|
const KcApp = memo(({ kcContext, i18n: userProvidedI18n, ...kcProps }: { kcContext: KcContextBase; i18n?: I18n } & KcProps) => {
|
25
26
|
const i18n = (function useClosure() {
|
@@ -74,6 +75,8 @@ const KcApp = memo(({ kcContext, i18n: userProvidedI18n, ...kcProps }: { kcConte
|
|
74
75
|
return <LoginConfigTotp {...{ kcContext, ...props }} />;
|
75
76
|
case "logout-confirm.ftl":
|
76
77
|
return <LogoutConfirm {...{ kcContext, ...props }} />;
|
78
|
+
case "update-user-profile.ftl":
|
79
|
+
return <UpdateUserProfile {...{ kcContext, ...props }} />;
|
77
80
|
}
|
78
81
|
})()}
|
79
82
|
</Suspense>
|
@@ -1,12 +1,10 @@
|
|
1
|
-
import React, { useMemo, memo,
|
1
|
+
import React, { useMemo, memo, useState } from "react";
|
2
2
|
import Template from "./Template";
|
3
3
|
import type { KcProps } from "./KcProps";
|
4
|
-
import type { KcContextBase
|
4
|
+
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
5
5
|
import { useCssAndCx } from "../tools/useCssAndCx";
|
6
|
-
import type { ReactComponent } from "../tools/ReactComponent";
|
7
|
-
import { useCallbackFactory } from "powerhooks/useCallbackFactory";
|
8
|
-
import { useFormValidationSlice } from "../useFormValidationSlice";
|
9
6
|
import type { I18n } from "../i18n";
|
7
|
+
import { UserProfileFormFields } from "./shared/UserProfileCommons";
|
10
8
|
|
11
9
|
const RegisterUserProfile = memo(({ kcContext, i18n, ...props_ }: { kcContext: KcContextBase.RegisterUserProfile; i18n: I18n } & KcProps) => {
|
12
10
|
const { url, messagesPerField, recaptchaRequired, recaptchaSiteKey } = kcContext;
|
@@ -34,7 +32,13 @@ const RegisterUserProfile = memo(({ kcContext, i18n, ...props_ }: { kcContext: K
|
|
34
32
|
headerNode={msg("registerTitle")}
|
35
33
|
formNode={
|
36
34
|
<form id="kc-register-form" className={cx(props.kcFormClass)} action={url.registrationAction} method="post">
|
37
|
-
<UserProfileFormFields
|
35
|
+
<UserProfileFormFields
|
36
|
+
kcContext={kcContext}
|
37
|
+
doInsertPasswordFields={true}
|
38
|
+
onIsFormSubmittableValueChange={setIsFomSubmittable}
|
39
|
+
i18n={i18n}
|
40
|
+
{...props}
|
41
|
+
/>
|
38
42
|
{recaptchaRequired && (
|
39
43
|
<div className="form-group">
|
40
44
|
<div className={cx(props.kcInputWrapperClass)}>
|
@@ -66,155 +70,4 @@ const RegisterUserProfile = memo(({ kcContext, i18n, ...props_ }: { kcContext: K
|
|
66
70
|
);
|
67
71
|
});
|
68
72
|
|
69
|
-
type UserProfileFormFieldsProps = { kcContext: KcContextBase.RegisterUserProfile; i18n: I18n } & KcProps &
|
70
|
-
Partial<Record<"BeforeField" | "AfterField", ReactComponent<{ attribute: Attribute }>>> & {
|
71
|
-
onIsFormSubmittableValueChange: (isFormSubmittable: boolean) => void;
|
72
|
-
};
|
73
|
-
|
74
|
-
const UserProfileFormFields = memo(({ kcContext, onIsFormSubmittableValueChange, i18n, ...props }: UserProfileFormFieldsProps) => {
|
75
|
-
const { cx, css } = useCssAndCx();
|
76
|
-
|
77
|
-
const { advancedMsg } = i18n;
|
78
|
-
|
79
|
-
const {
|
80
|
-
formValidationState: { fieldStateByAttributeName, isFormSubmittable },
|
81
|
-
formValidationReducer,
|
82
|
-
attributesWithPassword
|
83
|
-
} = useFormValidationSlice({
|
84
|
-
kcContext,
|
85
|
-
i18n
|
86
|
-
});
|
87
|
-
|
88
|
-
useEffect(() => {
|
89
|
-
onIsFormSubmittableValueChange(isFormSubmittable);
|
90
|
-
}, [isFormSubmittable]);
|
91
|
-
|
92
|
-
const onChangeFactory = useCallbackFactory(
|
93
|
-
(
|
94
|
-
[name]: [string],
|
95
|
-
[
|
96
|
-
{
|
97
|
-
target: { value }
|
98
|
-
}
|
99
|
-
]: [React.ChangeEvent<HTMLInputElement | HTMLSelectElement>]
|
100
|
-
) =>
|
101
|
-
formValidationReducer({
|
102
|
-
"action": "update value",
|
103
|
-
name,
|
104
|
-
"newValue": value
|
105
|
-
})
|
106
|
-
);
|
107
|
-
|
108
|
-
const onBlurFactory = useCallbackFactory(([name]: [string]) =>
|
109
|
-
formValidationReducer({
|
110
|
-
"action": "focus lost",
|
111
|
-
name
|
112
|
-
})
|
113
|
-
);
|
114
|
-
|
115
|
-
let currentGroup = "";
|
116
|
-
|
117
|
-
return (
|
118
|
-
<>
|
119
|
-
{attributesWithPassword.map((attribute, i) => {
|
120
|
-
const { group = "", groupDisplayHeader = "", groupDisplayDescription = "" } = attribute;
|
121
|
-
|
122
|
-
const { value, displayableErrors } = fieldStateByAttributeName[attribute.name];
|
123
|
-
|
124
|
-
const formGroupClassName = cx(props.kcFormGroupClass, displayableErrors.length !== 0 && props.kcFormGroupErrorClass);
|
125
|
-
|
126
|
-
return (
|
127
|
-
<Fragment key={i}>
|
128
|
-
{group !== currentGroup && (currentGroup = group) !== "" && (
|
129
|
-
<div className={formGroupClassName}>
|
130
|
-
<div className={cx(props.kcContentWrapperClass)}>
|
131
|
-
<label id={`header-${group}`} className={cx(props.kcFormGroupHeader)}>
|
132
|
-
{advancedMsg(groupDisplayHeader) || currentGroup}
|
133
|
-
</label>
|
134
|
-
</div>
|
135
|
-
{groupDisplayDescription !== "" && (
|
136
|
-
<div className={cx(props.kcLabelWrapperClass)}>
|
137
|
-
<label id={`description-${group}`} className={`${cx(props.kcLabelClass)}`}>
|
138
|
-
{advancedMsg(groupDisplayDescription)}
|
139
|
-
</label>
|
140
|
-
</div>
|
141
|
-
)}
|
142
|
-
</div>
|
143
|
-
)}
|
144
|
-
<div className={formGroupClassName}>
|
145
|
-
<div className={cx(props.kcLabelWrapperClass)}>
|
146
|
-
<label htmlFor={attribute.name} className={cx(props.kcLabelClass)}>
|
147
|
-
{advancedMsg(attribute.displayName ?? "")}
|
148
|
-
</label>
|
149
|
-
{attribute.required && <>*</>}
|
150
|
-
</div>
|
151
|
-
<div className={cx(props.kcInputWrapperClass)}>
|
152
|
-
{(() => {
|
153
|
-
const { options } = attribute.validators;
|
154
|
-
|
155
|
-
if (options !== undefined) {
|
156
|
-
return (
|
157
|
-
<select
|
158
|
-
id={attribute.name}
|
159
|
-
name={attribute.name}
|
160
|
-
onChange={onChangeFactory(attribute.name)}
|
161
|
-
onBlur={onBlurFactory(attribute.name)}
|
162
|
-
value={value}
|
163
|
-
>
|
164
|
-
{options.options.map(option => (
|
165
|
-
<option key={option} value={option}>
|
166
|
-
{option}
|
167
|
-
</option>
|
168
|
-
))}
|
169
|
-
</select>
|
170
|
-
);
|
171
|
-
}
|
172
|
-
|
173
|
-
return (
|
174
|
-
<input
|
175
|
-
type={(() => {
|
176
|
-
switch (attribute.name) {
|
177
|
-
case "password-confirm":
|
178
|
-
case "password":
|
179
|
-
return "password";
|
180
|
-
default:
|
181
|
-
return "text";
|
182
|
-
}
|
183
|
-
})()}
|
184
|
-
id={attribute.name}
|
185
|
-
name={attribute.name}
|
186
|
-
value={value}
|
187
|
-
onChange={onChangeFactory(attribute.name)}
|
188
|
-
className={cx(props.kcInputClass)}
|
189
|
-
aria-invalid={displayableErrors.length !== 0}
|
190
|
-
disabled={attribute.readOnly}
|
191
|
-
autoComplete={attribute.autocomplete}
|
192
|
-
onBlur={onBlurFactory(attribute.name)}
|
193
|
-
/>
|
194
|
-
);
|
195
|
-
})()}
|
196
|
-
{displayableErrors.length !== 0 && (
|
197
|
-
<span
|
198
|
-
id={`input-error-${attribute.name}`}
|
199
|
-
className={cx(
|
200
|
-
props.kcInputErrorMessageClass,
|
201
|
-
css({
|
202
|
-
"position": displayableErrors.length === 1 ? "absolute" : undefined,
|
203
|
-
"& > span": { "display": "block" }
|
204
|
-
})
|
205
|
-
)}
|
206
|
-
aria-live="polite"
|
207
|
-
>
|
208
|
-
{displayableErrors.map(({ errorMessage }) => errorMessage)}
|
209
|
-
</span>
|
210
|
-
)}
|
211
|
-
</div>
|
212
|
-
</div>
|
213
|
-
</Fragment>
|
214
|
-
);
|
215
|
-
})}
|
216
|
-
</>
|
217
|
-
);
|
218
|
-
});
|
219
|
-
|
220
73
|
export default RegisterUserProfile;
|