Compare commits
916 Commits
v0.0.24
...
v6.0.0-bet
Author | SHA1 | Date | |
---|---|---|---|
b7e75d8828 | |||
30e20f4e7d | |||
ce0ab8dccf | |||
5b20ab2f7c | |||
daaaed43df | |||
3a4bd791ad | |||
eecddd7f6b | |||
a34eaa136e | |||
53be8b5e96 | |||
f0ae5ea908 | |||
9910556a8b | |||
5997416e1b | |||
9a9fc56f85 | |||
2a5e919f29 | |||
8031d51e15 | |||
56ce9c0d0d | |||
8cd584cbd5 | |||
f5b87f4669 | |||
a1a65c5529 | |||
832434095e | |||
b85f1ef351 | |||
8bee5d788e | |||
0752d857e2 | |||
07e4056694 | |||
0eb4ab85b3 | |||
69ef47daf8 | |||
6eaa1f69ac | |||
5aab75fae0 | |||
7407c98005 | |||
dcd4322e44 | |||
81a4d46b08 | |||
e85895ab55 | |||
095bdb16ba | |||
68de7f897d | |||
51b4c6b1bd | |||
6d4ac977c1 | |||
a73fc5ebc1 | |||
3c8461a39f | |||
de76d06e48 | |||
a27c28c24f | |||
ed234ec88b | |||
7a0a046596 | |||
0641151ca1 | |||
c6dc2377fa | |||
a3050b3983 | |||
69c15bd473 | |||
2be40816b2 | |||
a98bb25133 | |||
d130c23f5d | |||
cd936ee4ef | |||
67e3dca0c3 | |||
de53f1ff40 | |||
d79081dee4 | |||
449f100bc0 | |||
0612b2d0a4 | |||
583a3e541a | |||
9d8d30a864 | |||
95cda8538f | |||
b116f22152 | |||
ff2fb0d6dc | |||
020823e933 | |||
5879972924 | |||
8031294230 | |||
e0a6935c49 | |||
3d581a5454 | |||
317ad8386c | |||
7ad5011280 | |||
76990702f0 | |||
9a27824fe9 | |||
d877d90bf3 | |||
0b790c47e6 | |||
6b49c8dd95 | |||
e56f9b144e | |||
3c82944daf | |||
5e3070a6c4 | |||
a3a0e9eebe | |||
c6593f03bc | |||
54dc4f650c | |||
dec7af0381 | |||
ae001eea54 | |||
4d0e17a11e | |||
c708e619e9 | |||
2cceb3c929 | |||
aee8704691 | |||
43af60237b | |||
e615479b41 | |||
973fb4d2d5 | |||
964feae846 | |||
ea3d8a5634 | |||
48aab2d92a | |||
00eab73954 | |||
5f6f8b12bd | |||
c2d4d6fd49 | |||
04b660ff9b | |||
c292d926be | |||
23b83ceef7 | |||
1324648db6 | |||
735bff3348 | |||
05a6aee782 | |||
c7349c2556 | |||
51f3d06752 | |||
31759d86ab | |||
7c6eed99d2 | |||
bc4b0ec17d | |||
f766348b87 | |||
82281303d0 | |||
1caa17beb0 | |||
1c4d346f9f | |||
4320efb049 | |||
a756423768 | |||
8525fc74c0 | |||
30c0cc5aa8 | |||
b3bbd7c07d | |||
09d4ba2bb0 | |||
30315027c1 | |||
05acefe70e | |||
6c14758e33 | |||
b93ec20119 | |||
ce04646576 | |||
9282dfe491 | |||
fca6280bcc | |||
cdeb575ec6 | |||
271dbe4fb7 | |||
9a0337114d | |||
2d28f4eb55 | |||
f673927e16 | |||
52896b82a9 | |||
9d53ecb0cd | |||
aec3ac32e5 | |||
f150f1568e | |||
309189c55d | |||
f68c54cd3a | |||
bef8545161 | |||
c21cd14ac2 | |||
275d7f0072 | |||
58c8306cf4 | |||
f782b684ad | |||
092b2a5f52 | |||
42b2d40ad6 | |||
3f6fe6cfc0 | |||
1abf542a74 | |||
c4720ca03d | |||
4316878cce | |||
c180d75a83 | |||
4a040b32c0 | |||
ea330a1eef | |||
2451ba0a77 | |||
2c276a56e5 | |||
708030b8b5 | |||
d5fc0582bc | |||
f9dce82c83 | |||
e82602f994 | |||
1d36395e5a | |||
8f8857bc22 | |||
226247b3b6 | |||
b2ea5014f3 | |||
48bc416aa7 | |||
386e7203b2 | |||
9bdb224631 | |||
dd36aacbee | |||
6b57b1c720 | |||
9e9e6d41ff | |||
5140389502 | |||
fc6328131f | |||
9de0083ca6 | |||
f5231b840d | |||
afb6596c4b | |||
dde9afef92 | |||
6595e9c3cb | |||
c0e3b5fe06 | |||
6b8f3bbc51 | |||
9a5a021e64 | |||
14c05fec8c | |||
eaf7a455cd | |||
55bb21f3ee | |||
f123bc0912 | |||
572eb7b1c0 | |||
2befaff8a8 | |||
437a9ce2d3 | |||
1b967b250a | |||
e221f39e07 | |||
21a8838a24 | |||
fad91ccae0 | |||
825914aa4b | |||
a8246d12ee | |||
abb8bf2ebb | |||
7e7071305f | |||
cc8b2e72c1 | |||
a3d6ee44a1 | |||
ac99e2f41f | |||
bf1839c061 | |||
fd5c132a40 | |||
4dfa268eb3 | |||
332ca084f5 | |||
01cbb8680a | |||
bbdaaf30bc | |||
0550b9ff8b | |||
b1a4c5cca5 | |||
785080e14a | |||
3c7e093a3c | |||
89be9f3a86 | |||
6f2ffa7861 | |||
7091f283f2 | |||
2d28003451 | |||
f0ba7d3c0d | |||
cd5f346895 | |||
66cd5aef0c | |||
6f8ec53e8b | |||
622504ff72 | |||
c9d47c483c | |||
07098b89a5 | |||
c583b83cbb | |||
1670e1fe42 | |||
de8809608c | |||
0e194ee045 | |||
4205f6ecbe | |||
4d90ec60e2 | |||
d126a6563b | |||
aecb6ae79c | |||
a65c826717 | |||
66c3705f2b | |||
d18ebb45f8 | |||
d8e01f2c5d | |||
4abbaa3841 | |||
42a463b348 | |||
8e15cf1d45 | |||
2468b4108e | |||
528b1bb607 | |||
b4449bb289 | |||
737e00b490 | |||
55d4c7f4ab | |||
7afb078efd | |||
2c04f6c1e9 | |||
2ad5ed7e73 | |||
f2b7fe46a2 | |||
1a1af62f62 | |||
98f715e652 | |||
fa5f1c230a | |||
c92ae9cfa9 | |||
3dcb3a1a5b | |||
efde71d07c | |||
bff8cf2f32 | |||
72730135f1 | |||
50cf27b686 | |||
b293abffa4 | |||
be84ea299c | |||
d54586426a | |||
6ccf72c707 | |||
5817118461 | |||
ebac1de111 | |||
0d2f841b27 | |||
780ca383c9 | |||
a652a0f4f3 | |||
5bdc812c43 | |||
357bc8d19d | |||
85b54ac011 | |||
17f888019c | |||
947fd0564e | |||
bd51d02902 | |||
36d75c8641 | |||
c75f158b48 | |||
bb37ce9cef | |||
77ff33570d | |||
20383d60a9 | |||
f15c0ecbb0 | |||
79aa5ac5f2 | |||
8be6c0d1d2 | |||
7f5a9e77de | |||
ff19ab8b08 | |||
63dcb2ad39 | |||
795e8ed0e5 | |||
bccb56ed61 | |||
02e2ad89ec | |||
a236e2e5de | |||
ba294c85f8 | |||
beb3dca495 | |||
04101536c6 | |||
2912e7e5dd | |||
bf6fadbde8 | |||
001b49d09a | |||
bbd5bdda95 | |||
7e950e8e2b | |||
8b0efbc737 | |||
93cfbd6696 | |||
acc1d028ab | |||
3476b5acc3 | |||
72ca5da842 | |||
e214280fcd | |||
bf32987a3e | |||
8941fe230b | |||
b6d4abee21 | |||
786bdc41c2 | |||
ed9f08f678 | |||
33fd6768f1 | |||
87b8456531 | |||
a12bde4656 | |||
6f219a4c2a | |||
49d7818b64 | |||
fb0be3272c | |||
994f7d6bea | |||
6e8dcecaf1 | |||
40237374a8 | |||
3d98860369 | |||
804fe33665 | |||
703171f96b | |||
27bdefeea8 | |||
7a3c74020d | |||
7509170dd0 | |||
cd17a97916 | |||
d5e690f964 | |||
a19bd20b6b | |||
f78526dfff | |||
11d6a2020f | |||
fabd48a22c | |||
e2ea98b5ef | |||
4473ab0704 | |||
1f68cc305a | |||
ec2543551f | |||
7b0bedc755 | |||
ab054ca515 | |||
1b49c7804c | |||
764a288b1a | |||
fc6910bc2c | |||
91dd1dcddc | |||
97e6aaca65 | |||
af5ff1ecfb | |||
c9b53b0d3a | |||
d05a62e1ea | |||
a83eec31d8 | |||
729503fe31 | |||
7137ff4257 | |||
6db11a7433 | |||
8666aa62dd | |||
eedcd7a2a6 | |||
e3e8fb663a | |||
6e663210ee | |||
42cd0fe2f0 | |||
daac05c1ad | |||
1d63c393a3 | |||
e8a3751b32 | |||
cb8a41d5be | |||
a8d4f7e23c | |||
93bcdac3be | |||
32d0388556 | |||
633a32ffd6 | |||
cb8b165c8e | |||
57134359b9 | |||
377436e46a | |||
51129aaeff | |||
a2bd5050ff | |||
128c416ce7 | |||
7184773521 | |||
1138313028 | |||
46bd319ebe | |||
cfcc48259c | |||
785ce7a8ab | |||
ad5de216b0 | |||
26b80d6af7 | |||
a8623d8066 | |||
86ab9f72a5 | |||
b3892dab8d | |||
57a5d034dd | |||
cee9569581 | |||
159429da6e | |||
a292cb0b4b | |||
d70985d8d2 | |||
484f95f5d2 | |||
6e0553af9b | |||
cb18d3d765 | |||
f316f38ae5 | |||
5f07cb374b | |||
96d31e07c3 | |||
99a5efe36c | |||
5c46ecc0ed | |||
cf93b68816 | |||
457421b8d6 | |||
d36ea9539a | |||
5a5337dc63 | |||
443081cc28 | |||
ac8503f8c8 | |||
1cc1fd0a5a | |||
34314aa4ca | |||
0d8dcf4829 | |||
47c6d0dd62 | |||
84937e3eec | |||
303e270b56 | |||
29fbcdc0a6 | |||
bb1ada6e14 | |||
4a422cc796 | |||
be0f244c02 | |||
78a8dc8458 | |||
38062af889 | |||
f2eadf5441 | |||
a42931384f | |||
8116ce697b | |||
4964b86d67 | |||
2b331e7655 | |||
c1468b688e | |||
4f7837c88e | |||
fd8e06f1dd | |||
b01a351eaa | |||
604655c02d | |||
6603ac4389 | |||
cca6f952ee | |||
df94a6322d | |||
73e7f64860 | |||
e17e1650d5 | |||
3ecb63d500 | |||
41ee7e90ef | |||
c70bba727e | |||
747248454d | |||
59386241b4 | |||
c70b9b0dd1 | |||
2ee00ed919 | |||
cbfc271da5 | |||
d45b492837 | |||
ed54c145b7 | |||
64ed9a6044 | |||
75267abd91 | |||
ba9a3992b7 | |||
a74c32ed6d | |||
c5f9812acc | |||
bb0d6853e5 | |||
8c9fe168d8 | |||
6c874c01b7 | |||
5bc84b621c | |||
dd421eedf5 | |||
570d8a73cc | |||
a95df42843 | |||
4ecbb30a1b | |||
96b40b9c49 | |||
c32eebdd46 | |||
5b17287555 | |||
fb01257c8b | |||
53470f8788 | |||
89b86936f6 | |||
d3a07edfcb | |||
98a3d6564e | |||
50a20c68ed | |||
3aad681538 | |||
92fb3b7529 | |||
1572f1137a | |||
b5075dd1eb | |||
9119caa843 | |||
f5c5a79064 | |||
357d804124 | |||
d59cb3b470 | |||
8ac292bd97 | |||
c6cab82546 | |||
04bf3692e4 | |||
6602aa0ee4 | |||
c8e099dedb | |||
9ede0800f1 | |||
fbdae316c7 | |||
da0baebb31 | |||
47906499a8 | |||
9ceef8f09e | |||
d5b5c79d14 | |||
6b3ca3230c | |||
49376b1572 | |||
c94c037f65 | |||
2ee45cd7c9 | |||
72079ca028 | |||
94d0bd29cd | |||
8cea4239aa | |||
6dca6a93d8 | |||
92d577e3e2 | |||
59f106bf9e | |||
913a6c3ec3 | |||
57932386bf | |||
e3df4b83eb | |||
ef5b01956a | |||
0e8984e5b1 | |||
403aedf1fe | |||
53d3646523 | |||
305ce9e44d | |||
9f8218efb7 | |||
c4ba470dc4 | |||
637bc75fc2 | |||
4ad3affadb | |||
bd403beb5c | |||
20f528a167 | |||
4ca2bc59b6 | |||
91c6839447 | |||
c388c77f4a | |||
19fb365271 | |||
92946ef6bb | |||
7fad7a67a0 | |||
392377bf2d | |||
95a53d37f6 | |||
2d03fbce79 | |||
37a0692f9f | |||
2c82a2332f | |||
2f78f0f5e2 | |||
c52b8cc98e | |||
fb5f358686 | |||
60d5a8e881 | |||
6cbebf3b13 | |||
f1269bbfe1 | |||
d1be21ace3 | |||
cf2317884c | |||
7a37a6127d | |||
1d4e5792fd | |||
1df329448b | |||
d6762547a3 | |||
a31a6d17d4 | |||
8069ede90a | |||
ce956172df | |||
874cec81e0 | |||
7b24b13dca | |||
e4dd3ed898 | |||
e5804ce778 | |||
b6db47c243 | |||
cde1881ab1 | |||
23afef7857 | |||
bdf09c7a45 | |||
6957a03a87 | |||
11e688d638 | |||
eb39de6b34 | |||
a0f937615e | |||
2ee7d2e698 | |||
7dc39a323a | |||
672717372e | |||
2ad2efa15e | |||
f638fcdf51 | |||
a47695d7e0 | |||
1bc1fea5cc | |||
97544a952f | |||
1c2a55f2fd | |||
8aa0bd189e | |||
d648caa37f | |||
34263661d7 | |||
f4a6fd5c5e | |||
fc9ddfb8d8 | |||
f8d0aff386 | |||
ac100d74bf | |||
9b1e4e9111 | |||
3e54b64bc4 | |||
914d2a787d | |||
95add5b1d0 | |||
b1e24212ea | |||
a1bec78ea2 | |||
05c98eb074 | |||
2688aefdfb | |||
eaa6582e67 | |||
a136bc619d | |||
8780c516fa | |||
b4bdec7970 | |||
671aeadf29 | |||
341d985610 | |||
76b9a78182 | |||
021c0a9429 | |||
7b3462d158 | |||
c180dee414 | |||
769da5c5ca | |||
1a4dd79240 | |||
5cf290b033 | |||
aec3da25b3 | |||
66d7cb563d | |||
551e9c041e | |||
fffb6d5b5e | |||
ac0bfeb360 | |||
7c30059ca3 | |||
fdb9ae6c40 | |||
3c82ffc0ab | |||
5dd3103aba | |||
84fc81f531 | |||
a20cbc62a5 | |||
e6a93e2838 | |||
3cff54561f | |||
e50a6a7876 | |||
b887ec839b | |||
465daa19a0 | |||
6c2b761d95 | |||
0e8f95ce19 | |||
6a17f343c6 | |||
1a45fb0039 | |||
75032898d6 | |||
88a4c97428 | |||
82e7a7edae | |||
eac28f97b8 | |||
e160882db9 | |||
2bc07e77fd | |||
c9b2db625c | |||
e3b41c9bd1 | |||
4aaee35d9c | |||
beedbc695a | |||
7123edc986 | |||
3008a754ce | |||
70e3fb8de6 | |||
9cf75da732 | |||
2cd266caff | |||
28036f1da5 | |||
0dacf2fe30 | |||
32f5ef5e5c | |||
98d91bbde7 | |||
7a92a75d83 | |||
f5556a02fc | |||
1050f4d928 | |||
9276b08f4b | |||
a501af669c | |||
85343fcefe | |||
b11dfde6e6 | |||
38d2108f02 | |||
2b67544517 | |||
0d443ca88e | |||
71f7a5819d | |||
5f4abee615 | |||
f5ee949006 | |||
7e85085558 | |||
55a0b27f16 | |||
eb0e814f94 | |||
b7fe20c5a5 | |||
2b23d03ca5 | |||
7075be20c8 | |||
3ce8b06246 | |||
ee5c29f30f | |||
242dad3ea0 | |||
d8701925df | |||
e2d669ce31 | |||
af93664c71 | |||
daa3efa534 | |||
2c7c8397f0 | |||
821ba2cbe2 | |||
a17ddb02fa | |||
b89557e8d8 | |||
cad1f8b957 | |||
f82cc788bf | |||
06f9cd3e68 | |||
5113a838e7 | |||
645a84c82a | |||
925fc43d0f | |||
8e33d24c63 | |||
984ef63661 | |||
a8daf175ea | |||
055263a3da | |||
9990b0ab05 | |||
423397ce3e | |||
954567712c | |||
9f52eb8123 | |||
744b198fb4 | |||
15eab797c3 | |||
8ff86b1e29 | |||
e1b8760ee3 | |||
bd0d890b2c | |||
2a2118d769 | |||
9839b64650 | |||
2bf55e12f9 | |||
2249fa9232 | |||
f673a65304 | |||
0163459ad6 | |||
b21123cc9d | |||
7800d125b2 | |||
89ea648f18 | |||
ab7ac3c2d0 | |||
b16319d962 | |||
f8012d5dfb | |||
45a2015597 | |||
524ab000be | |||
d73cfb8765 | |||
8164f5373f | |||
824b0c275e | |||
f8d83d7a37 | |||
b291526b13 | |||
e1c310d383 | |||
242777a8eb | |||
10a6b70fe9 | |||
c829f5969c | |||
ba6a5047b1 | |||
852f48c05f | |||
c342f04a92 | |||
42eb8147c6 | |||
ebcdbd782f | |||
d2059e08d1 | |||
4f075882d5 | |||
044ec1a2da | |||
a49a32703d | |||
46ec832767 | |||
fc858b3db6 | |||
3cd8843157 | |||
c9358ea8dd | |||
354a4db0f6 | |||
90d435d96b | |||
2d804f0f0f | |||
1c9acedac0 | |||
6e914e4ea3 | |||
f0c4786267 | |||
0b16159312 | |||
ea8a91e069 | |||
59db202fe4 | |||
09927afd43 | |||
13c6122b9b | |||
1bb19f65a2 | |||
918a80cfb6 | |||
ed7d5eabcb | |||
2795109162 | |||
966f277628 | |||
36d60411f9 | |||
60fe33f3fd | |||
1df685df92 | |||
7894d95ace | |||
a8b4493aa1 | |||
715a7399cf | |||
a1e59bae23 | |||
b0819314a1 | |||
0099442543 | |||
66a0b07228 | |||
85f9544754 | |||
2f16a09ab8 | |||
183ae98c30 | |||
ba15e63879 | |||
654277feda | |||
81279a5cc5 | |||
59f0a843b0 | |||
c094f70171 | |||
0858fe6319 | |||
5012ec0ccc | |||
990a24fab2 | |||
036b6bf82a | |||
8272a02b52 | |||
e346b1d9d2 | |||
2309bd21c6 | |||
7d6476c1b5 | |||
e892a0e7e6 | |||
ca5b41e730 | |||
9b18234112 | |||
5274368f47 | |||
1415c24028 | |||
4a084f5859 | |||
a30c9eb0cd | |||
85d3b40b8e | |||
335afec230 | |||
69fa49848a | |||
7a09051127 | |||
07ee0ecb8b | |||
6f133428f8 | |||
4f733736db | |||
d96ff13a67 | |||
2c1351ce47 | |||
96cd56ec77 | |||
e5b2096d65 | |||
3aa140335f | |||
4cafaa2492 | |||
9c633a7521 | |||
e27845ba91 | |||
2a8708a45b | |||
6874fa4c24 | |||
ba531a4927 | |||
20175b57cf | |||
ad275e4c34 | |||
060b9fe0de | |||
17b24d14ed | |||
2d278b0680 | |||
fb5975e4f1 | |||
24fccaf513 | |||
293953aa1b | |||
1049e312f9 | |||
a2db250600 | |||
cf7fe8c337 | |||
f5350097bf | |||
1cb5dd461b | |||
845599a5e8 | |||
0cc02c292f | |||
1919702326 | |||
0c0052e1cd | |||
78622770ec | |||
7b86727394 | |||
0965f8648e | |||
98974b4367 | |||
597bcadd9e | |||
4d9aabcb91 | |||
1606c2884d | |||
12f69b593f | |||
1ca45f90d0 | |||
a91a5616f9 | |||
c525e09368 | |||
f5bba4a6a0 | |||
77a37fb573 | |||
6b24c5878c | |||
f4414e1249 | |||
b72971f4ce | |||
b9af4e6804 | |||
2fd1d42d1e | |||
3cfc7d7fa9 | |||
b5d9055fcf | |||
63d644d95f | |||
e16192b416 | |||
505e018448 | |||
5ced0e2809 | |||
0e1d919f7e | |||
a009db998e | |||
d6c6bd933b | |||
859cc03f35 | |||
1a3b8ae3b8 | |||
863a08abf3 | |||
fd9c6afa5e | |||
8f3797407b | |||
7eedb23285 | |||
e4a2c95dd8 | |||
9429228b71 | |||
aafbc60f12 | |||
7170611791 | |||
59e57f3dd5 | |||
fd0d25b081 | |||
fa529c911a | |||
dc997b7ef4 | |||
0168d32f96 | |||
d6fc0c779c | |||
08be36edfa | |||
990d287953 | |||
a629d4ab45 | |||
78b48af886 | |||
72d267853c | |||
66a218c2ec | |||
d4f3ec2245 | |||
7bdc19bf4b | |||
103ef788fb | |||
b6f6d1f3cc | |||
ef3d2e4e04 | |||
8ec8b91ead | |||
819a1d473d | |||
c930337255 | |||
57bb4a9d96 | |||
1776341242 | |||
983eec6941 | |||
86d390ee1a | |||
91703409d9 | |||
3322d0e4a5 | |||
d09038fde2 | |||
71966deaac | |||
12e83c9468 | |||
fe27357dbb | |||
b93003e76d | |||
d6c0e9f783 | |||
18a1baae59 | |||
2330788995 | |||
40c146022a | |||
9844e7554f | |||
b064b8cbe6 | |||
997941fbf7 | |||
8d5e080bd6 | |||
1c1ca25287 | |||
5195b3bc1e | |||
3ff56cfea7 | |||
adc6d69201 | |||
438ca4595f | |||
740d9b7af5 | |||
919a6947bc | |||
85fdaa2f22 | |||
6ade3d6375 | |||
73e9ebbe40 | |||
ad78221025 | |||
714fec0bf3 | |||
de312c60b1 | |||
1d07fd7675 | |||
307650aaea | |||
6eccd313b6 | |||
b8751d67db | |||
25d9d3dc26 | |||
68e6c9faaf | |||
f3fb360ce0 | |||
d3631dd10c | |||
891c91aa20 | |||
880018e926 | |||
06ab2ab82e | |||
aafcfb62f2 | |||
a69bee8726 | |||
240208793d | |||
e7a320f8f8 | |||
f76438dd82 | |||
d6dbb42dea | |||
2e076aa058 | |||
b59447b840 | |||
702352bea2 | |||
98f647fadf | |||
85d3a011d8 | |||
26b0b55a7d | |||
50d0e74c03 | |||
5a7fb7c303 | |||
e9f5a79d69 | |||
1378dbebee | |||
7daa818996 | |||
c8e219361b | |||
b74323a48e | |||
0cfe240590 | |||
533105d63a | |||
62e4af2c78 | |||
934c07f365 | |||
624409434a | |||
546c74aa28 | |||
2ee12abc43 | |||
b9717e649f | |||
09c7b6ac03 | |||
079da86cf1 | |||
57502a57af | |||
6738f6f6cf | |||
21763db561 | |||
24e07c3e00 | |||
8820262b0f | |||
2d69a1f946 | |||
2a5a4c16ea | |||
52ba14cd8f | |||
6222850453 | |||
ec2b085855 | |||
305d5b7328 | |||
deb4ddaed2 | |||
379f052e6e | |||
2f592ab38c | |||
43c412405a | |||
7f6ab1d391 | |||
e5e39d036d | |||
6a93c6e894 | |||
069262c83d | |||
69f017df36 | |||
63ccb0609e | |||
292881532a |
6
.gitattributes
vendored
6
.gitattributes
vendored
@ -1,3 +1,3 @@
|
||||
src/lib/i18n/generated_messages/* linguist-documentation
|
||||
src/bin/build-keycloak-theme/index.ts -linguist-detectable
|
||||
src/bin/download-sample-keycloak-themes.ts -linguist-detectable
|
||||
src/lib/i18n/generated_kcMessages/* linguist-documentation
|
||||
src/bin/keycloakify/index.ts -linguist-detectable
|
||||
src/bin/install-builtin-keycloak-themes.ts -linguist-detectable
|
||||
|
25
.github/release.yaml
vendored
Normal file
25
.github/release.yaml
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
changelog:
|
||||
exclude:
|
||||
labels:
|
||||
- ignore-for-release
|
||||
authors:
|
||||
- octocat
|
||||
categories:
|
||||
- title: Breaking Changes 🛠
|
||||
labels:
|
||||
- breaking
|
||||
- title: Exciting New Features 🎉
|
||||
labels:
|
||||
- feature
|
||||
- title: Fixes 🔧
|
||||
labels:
|
||||
- fix
|
||||
- title: Documentation 🔧
|
||||
labels:
|
||||
- docs
|
||||
- title: CI 👷
|
||||
labels:
|
||||
- ci
|
||||
- title: Other Changes
|
||||
labels:
|
||||
- '*'
|
177
.github/workflows/ci.yaml
vendored
177
.github/workflows/ci.yaml
vendored
@ -2,87 +2,142 @@ name: ci
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- develop
|
||||
- main
|
||||
- v6
|
||||
pull_request:
|
||||
branches:
|
||||
- develop
|
||||
- main
|
||||
- v6
|
||||
|
||||
jobs:
|
||||
|
||||
test_node:
|
||||
test_lint:
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ !github.event.created && github.repository != 'garronej/ts-ci' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2.3.4
|
||||
- uses: actions/setup-node@v2.1.3
|
||||
- uses: bahmutov/npm-install@v1
|
||||
- name: If this step fails run 'npm run lint' and 'npm run format' then commit again.
|
||||
run: |
|
||||
PACKAGE_MANAGER=npm
|
||||
if [ -f "./yarn.lock" ]; then
|
||||
PACKAGE_MANAGER=yarn
|
||||
fi
|
||||
$PACKAGE_MANAGER run format:check
|
||||
test:
|
||||
runs-on: macos-10.15
|
||||
needs: test_lint
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
strategy:
|
||||
matrix:
|
||||
node: [ '14', '13', '12' ]
|
||||
node: [ '15', '14' ]
|
||||
name: Test with Node v${{ matrix.node }}
|
||||
steps:
|
||||
- name: Tell if project is using npm or yarn
|
||||
id: _1
|
||||
uses: garronej/github_actions_toolkit@v1.11
|
||||
id: step1
|
||||
uses: garronej/ts-ci@v1.1.7
|
||||
with:
|
||||
action_name: tell_if_project_uses_npm_or_yarn
|
||||
owner: ${{github.repository_owner}}
|
||||
repo: ${{github.event.repository.name}}
|
||||
branch: ${{github.ref}}
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v1
|
||||
- uses: actions/checkout@v2.3.4
|
||||
- uses: actions/setup-node@v2.1.3
|
||||
with:
|
||||
node-version: ${{ matrix.node }}
|
||||
- if: steps._1.outputs.npm_or_yarn == 'yarn'
|
||||
- uses: bahmutov/npm-install@v1
|
||||
- if: steps.step1.outputs.npm_or_yarn == 'yarn'
|
||||
run: |
|
||||
yarn install --frozen-lockfile
|
||||
yarn run build
|
||||
yarn run test
|
||||
- if: steps._1.outputs.npm_or_yarn == 'npm'
|
||||
yarn build
|
||||
yarn test
|
||||
- if: steps.step1.outputs.npm_or_yarn == 'npm'
|
||||
run: |
|
||||
npm ci
|
||||
npm run build
|
||||
npm run test
|
||||
trigger_publish:
|
||||
name: Trigger publish.yaml workflow if package.json version updated ( and secrets.PAT is set ).
|
||||
npm test
|
||||
check_if_version_upgraded:
|
||||
name: Check if version upgrade
|
||||
# We run this only if it's a push on the default branch or if it's a PR from a
|
||||
# branch (meaning not a PR from a fork). It would be more straightforward to test if secrets.NPM_TOKEN is
|
||||
# defined but GitHub Action don't allow it yet.
|
||||
if: |
|
||||
github.event_name == 'push' ||
|
||||
github.event.pull_request.head.repo.owner.login == github.event.pull_request.base.repo.owner.login
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
PAT: ${{secrets.PAT}}
|
||||
if: github.event_name == 'push' && github.event.head_commit.author.name != 'ts_ci'
|
||||
needs: test_node
|
||||
needs: test
|
||||
outputs:
|
||||
from_version: ${{ steps.step1.outputs.from_version }}
|
||||
to_version: ${{ steps.step1.outputs.to_version }}
|
||||
is_upgraded_version: ${{ steps.step1.outputs.is_upgraded_version }}
|
||||
is_release_beta: ${{steps.step1.outputs.is_release_beta }}
|
||||
steps:
|
||||
|
||||
- name: Get version on latest
|
||||
id: v_latest
|
||||
uses: garronej/github_actions_toolkit@v1.11
|
||||
- uses: garronej/ts-ci@v1.1.7
|
||||
id: step1
|
||||
with:
|
||||
action_name: get_package_json_version
|
||||
owner: ${{github.repository_owner}}
|
||||
repo: ${{github.event.repository.name}}
|
||||
branch: latest
|
||||
compare_to_version: '0.0.0'
|
||||
action_name: is_package_json_version_upgraded
|
||||
branch: ${{ github.head_ref || github.ref }}
|
||||
|
||||
- name: Get version on develop
|
||||
id: v_develop
|
||||
uses: garronej/github_actions_toolkit@v1.11
|
||||
with:
|
||||
action_name: get_package_json_version
|
||||
owner: ${{github.repository_owner}}
|
||||
repo: ${{github.event.repository.name}}
|
||||
branch: ${{ github.sha }}
|
||||
compare_to_version: ${{steps.v_latest.outputs.version || '0.0.0'}}
|
||||
|
||||
- name: 'Trigger the ''publish'' workflow'
|
||||
if: ${{ !!env.PAT && steps.v_develop.outputs.compare_result == '1' }}
|
||||
uses: garronej/github_actions_toolkit@v1.11
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.PAT }}
|
||||
create_github_release:
|
||||
runs-on: ubuntu-latest
|
||||
# We create a release only if the version have been upgraded and we are on a default branch
|
||||
# PR on the default branch can release beta but not real release
|
||||
if: |
|
||||
needs.check_if_version_upgraded.outputs.is_upgraded_version == 'true' &&
|
||||
(
|
||||
github.event_name == 'push' ||
|
||||
needs.check_if_version_upgraded.outputs.is_release_beta == 'true'
|
||||
)
|
||||
needs:
|
||||
- check_if_version_upgraded
|
||||
steps:
|
||||
- uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
action_name: dispatch_event
|
||||
owner: ${{github.repository_owner}}
|
||||
repo: ${{github.event.repository.name}}
|
||||
event_type: publish
|
||||
client_payload_json: |
|
||||
${{
|
||||
format(
|
||||
'{{"from_version":"{0}","to_version":"{1}","repo":"{2}"}}',
|
||||
steps.v_latest.outputs.version,
|
||||
steps.v_develop.outputs.version,
|
||||
github.event.repository.name
|
||||
)
|
||||
}}
|
||||
name: Release v${{ needs.check_if_version_upgraded.outputs.to_version }}
|
||||
tag_name: v${{ needs.check_if_version_upgraded.outputs.to_version }}
|
||||
target_commitish: ${{ github.head_ref || github.ref }}
|
||||
generate_release_notes: true
|
||||
draft: false
|
||||
prerelease: ${{ needs.check_if_version_upgraded.outputs.is_release_beta == 'true' }}
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
publish_on_npm:
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
- create_github_release
|
||||
- check_if_version_upgraded
|
||||
steps:
|
||||
- uses: actions/checkout@v2.3.4
|
||||
with:
|
||||
ref: ${{ github.ref }}
|
||||
- uses: actions/setup-node@v2.1.3
|
||||
with:
|
||||
node-version: '15'
|
||||
registry-url: https://registry.npmjs.org/
|
||||
- uses: bahmutov/npm-install@v1
|
||||
- run: |
|
||||
PACKAGE_MANAGER=npm
|
||||
if [ -f "./yarn.lock" ]; then
|
||||
PACKAGE_MANAGER=yarn
|
||||
fi
|
||||
$PACKAGE_MANAGER run build
|
||||
- run: npx -y -p denoify@1.0.2 enable_short_npm_import_path
|
||||
env:
|
||||
DRY_RUN: "0"
|
||||
- name: Publishing on NPM
|
||||
run: |
|
||||
if [ "$(npm show . version)" = "$VERSION" ]; then
|
||||
echo "This version is already published"
|
||||
exit 0
|
||||
fi
|
||||
if [ "$NODE_AUTH_TOKEN" = "" ]; then
|
||||
echo "Can't publish on NPM, You must first create a secret called NPM_TOKEN that contains your NPM auth token. https://help.github.com/en/actions/automating-your-workflow-with-github-actions/creating-and-using-encrypted-secrets"
|
||||
false
|
||||
fi
|
||||
EXTRA_ARGS=""
|
||||
if [ "$IS_BETA" = "true" ]; then
|
||||
EXTRA_ARGS="--tag beta"
|
||||
fi
|
||||
npm publish $EXTRA_ARGS
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
|
||||
VERSION: ${{ needs.check_if_version_upgraded.outputs.to_version }}
|
||||
IS_BETA: ${{ needs.check_if_version_upgraded.outputs.is_release_beta }}
|
116
.github/workflows/publish.yaml
vendored
116
.github/workflows/publish.yaml
vendored
@ -1,116 +0,0 @@
|
||||
on:
|
||||
repository_dispatch:
|
||||
types: publish
|
||||
|
||||
jobs:
|
||||
update_changelog_and_sync_package_lock_version:
|
||||
name: Update CHANGELOG.md and make sure package.json and package-lock.json versions matches.
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Synchronize package.json and package-lock.json version if needed.
|
||||
uses: garronej/github_actions_toolkit@v1.11
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.PAT }}
|
||||
with:
|
||||
action_name: sync_package_and_package_lock_version
|
||||
owner: ${{github.repository_owner}}
|
||||
repo: ${{github.event.client_payload.repo}}
|
||||
branch: develop
|
||||
commit_author_email: ts_ci@github.com
|
||||
- name: Update CHANGELOG.md
|
||||
if: ${{ !!github.event.client_payload.from_version }}
|
||||
uses: garronej/github_actions_toolkit@v1.11
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.PAT }}
|
||||
with:
|
||||
action_name: update_changelog
|
||||
owner: ${{github.repository_owner}}
|
||||
repo: ${{github.event.client_payload.repo}}
|
||||
branch_behind: latest
|
||||
branch_ahead: develop
|
||||
commit_author_email: ts_ci@github.com
|
||||
exclude_commit_from_author_names_json: '["ts_ci"]'
|
||||
|
||||
publish:
|
||||
runs-on: ubuntu-latest
|
||||
needs: update_changelog_and_sync_package_lock_version
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
ref: develop
|
||||
- name: Remove .github directory, useless on 'latest' branch
|
||||
run: rm -r .github
|
||||
- name: Remove branch 'latest'
|
||||
continue-on-error: true
|
||||
run: |
|
||||
git branch -d latest || true
|
||||
git push origin :latest
|
||||
- name: Create the new 'latest' branch
|
||||
run: |
|
||||
git branch latest
|
||||
git checkout latest
|
||||
- uses: actions/setup-node@v1
|
||||
- run: |
|
||||
if [ -f "./yarn.lock" ]; then
|
||||
yarn install --frozen-lockfile
|
||||
else
|
||||
npm ci
|
||||
fi
|
||||
- run: |
|
||||
PACKAGE_MANAGER=npm
|
||||
if [ -f "./yarn.lock" ]; then
|
||||
PACKAGE_MANAGER=yarn
|
||||
fi
|
||||
$PACKAGE_MANAGER run enable_short_import_path
|
||||
env:
|
||||
DRY_RUN: "0"
|
||||
- name: (DEBUG) Show how the files have been moved to enable short import
|
||||
run: ls -lR
|
||||
- name: Publishing on NPM
|
||||
run: |
|
||||
if [ "$(npm show . version)" = "$VERSION" ]; then
|
||||
echo "This version is already published"
|
||||
exit 0
|
||||
fi
|
||||
if [ "$NPM_TOKEN" = "" ]; then
|
||||
echo "Can't publish on NPM, You must first create a secret called NPM_TOKEN that contains your NPM auth token. https://help.github.com/en/actions/automating-your-workflow-with-github-actions/creating-and-using-encrypted-secrets"
|
||||
false
|
||||
fi
|
||||
echo '//registry.npmjs.org/:_authToken=${NPM_TOKEN}' > .npmrc
|
||||
npm publish
|
||||
rm .npmrc
|
||||
env:
|
||||
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
VERSION: ${{ github.event.client_payload.to_version }}
|
||||
- name: Commit changes
|
||||
run: |
|
||||
git config --local user.email "ts_ci@github.com"
|
||||
git config --local user.name "ts_ci"
|
||||
git add -A
|
||||
git commit -am "Enabling shorter import paths [automatic]"
|
||||
- run: git push origin latest
|
||||
- name: Release body
|
||||
id: id_rb
|
||||
run: |
|
||||
if [ "$FROM_VERSION" = "" ]; then
|
||||
echo "::set-output name=body::🚀"
|
||||
else
|
||||
echo "::set-output name=body::📋 [CHANGELOG](https://github.com/$OWNER/$REPO/blob/$REF/CHANGELOG.md)"
|
||||
fi
|
||||
env:
|
||||
FROM_VERSION: ${{ github.event.client_payload.from_version }}
|
||||
OWNER: ${{github.repository_owner}}
|
||||
REPO: ${{github.event.client_payload.repo}}
|
||||
REF: v${{github.event.client_payload.to_version}}
|
||||
- name: Create Release
|
||||
uses: garronej/create-release@master
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.PAT }}
|
||||
with:
|
||||
tag_name: v${{ github.event.client_payload.to_version }}
|
||||
release_name: Release v${{ github.event.client_payload.to_version }}
|
||||
branch: latest
|
||||
draft: false
|
||||
prerelease: false
|
||||
body: ${{ steps.id_rb.outputs.body }}
|
7
.gitignore
vendored
7
.gitignore
vendored
@ -41,5 +41,12 @@ jspm_packages
|
||||
.DS_Store
|
||||
|
||||
/dist
|
||||
/dist_test
|
||||
|
||||
/sample_react_project/
|
||||
/.yarn_home/
|
||||
|
||||
.idea
|
||||
|
||||
/keycloak_email
|
||||
/build_keycloak
|
9
.prettierignore
Normal file
9
.prettierignore
Normal file
@ -0,0 +1,9 @@
|
||||
node_modules/
|
||||
/dist/
|
||||
/dist_test/
|
||||
/CHANGELOG.md
|
||||
/.yarn_home/
|
||||
/src/test/apps/
|
||||
/src/tools/types/
|
||||
/sample_react_project
|
||||
/build_keycloak/
|
11
.prettierrc.json
Normal file
11
.prettierrc.json
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
"printWidth": 150,
|
||||
"tabWidth": 4,
|
||||
"useTabs": false,
|
||||
"semi": true,
|
||||
"singleQuote": false,
|
||||
"quoteProps": "preserve",
|
||||
"trailingComma": "none",
|
||||
"bracketSpacing": true,
|
||||
"arrowParens": "avoid"
|
||||
}
|
133
CHANGELOG.md
133
CHANGELOG.md
@ -1,133 +0,0 @@
|
||||
### **0.0.24** (2021-03-01)
|
||||
|
||||
- update
|
||||
- update
|
||||
- update
|
||||
|
||||
### **0.0.23** (2021-03-01)
|
||||
|
||||
- update
|
||||
|
||||
### **0.0.23** (2021-03-01)
|
||||
|
||||
- update
|
||||
- update
|
||||
|
||||
### **0.0.23** (2021-03-01)
|
||||
|
||||
- update
|
||||
- update
|
||||
|
||||
### **0.0.23** (2021-03-01)
|
||||
|
||||
- update
|
||||
- Handle formatting in translation function
|
||||
|
||||
### **0.0.22** (2021-02-28)
|
||||
|
||||
- Split page messages
|
||||
|
||||
### **0.0.21** (2021-02-28)
|
||||
|
||||
- Restore yarn file
|
||||
- Multiple fixes
|
||||
- Update deps
|
||||
- Update deps
|
||||
- includes translations
|
||||
- Update README.md
|
||||
- improve docs
|
||||
- update
|
||||
- Update README.md
|
||||
- update
|
||||
- update
|
||||
- update
|
||||
- update
|
||||
|
||||
### **0.0.20** (2021-02-27)
|
||||
|
||||
- update
|
||||
- update
|
||||
|
||||
### **0.0.19** (2021-02-27)
|
||||
|
||||
- update
|
||||
- update
|
||||
|
||||
### **0.0.18** (2021-02-23)
|
||||
|
||||
- Bump version number
|
||||
- Moving on with implementation of the lib
|
||||
- Update readme
|
||||
- Readme eddit
|
||||
- Fixing video link
|
||||
|
||||
### **0.0.16** (2021-02-23)
|
||||
|
||||
- Bump version
|
||||
- Give test container credentials
|
||||
|
||||
### **0.0.14** (2021-02-23)
|
||||
|
||||
- Bump version number
|
||||
- enable the docker container to be run from the root of the react project
|
||||
|
||||
### **0.0.13** (2021-02-23)
|
||||
|
||||
- bump version
|
||||
|
||||
### **0.0.12** (2021-02-23)
|
||||
|
||||
- update readme
|
||||
|
||||
### **0.0.11** (2021-02-23)
|
||||
|
||||
- Add documentation
|
||||
|
||||
### **0.0.10** (2021-02-23)
|
||||
|
||||
- Remove extra closing bracket
|
||||
|
||||
### **0.0.9** (2021-02-22)
|
||||
|
||||
- fix container startup script
|
||||
- minor update
|
||||
|
||||
### **0.0.8** (2021-02-21)
|
||||
|
||||
- Include theme properties
|
||||
|
||||
### **0.0.7** (2021-02-21)
|
||||
|
||||
- fix build
|
||||
- Fix bundle
|
||||
|
||||
### **0.0.6** (2021-02-21)
|
||||
|
||||
- Include missing files in the release bundle
|
||||
|
||||
### **0.0.5** (2021-02-21)
|
||||
|
||||
- Bump version number
|
||||
- Make the install faster
|
||||
|
||||
### **0.0.4** (2021-02-21)
|
||||
|
||||
- Fix script visibility
|
||||
|
||||
### **0.0.3** (2021-02-21)
|
||||
|
||||
- Do not run tests on window
|
||||
- Add script for downloading base themes
|
||||
- Generate debug files to be able to test the container
|
||||
- Fix many little bugs
|
||||
- refactor
|
||||
- Almoste there
|
||||
- Things are starting to take form
|
||||
- Seems to be working
|
||||
- First draft
|
||||
- Remove eslint and prettyer
|
||||
|
||||
### **0.0.2** (2021-02-20)
|
||||
|
||||
- Update package.json
|
||||
|
3
CONTRIBUTING.md
Normal file
3
CONTRIBUTING.md
Normal file
@ -0,0 +1,3 @@
|
||||
Looking to contribute? Thank you! PR are more than welcome.
|
||||
|
||||
Please refers to [this documentation page](https://docs.keycloakify.dev/contributing) that will help you get started.
|
153
README.md
153
README.md
@ -2,98 +2,137 @@
|
||||
<img src="https://user-images.githubusercontent.com/6702424/109387840-eba11f80-7903-11eb-9050-db1dad883f78.png">
|
||||
</p>
|
||||
<p align="center">
|
||||
<i>🔏 Keycloak theme generator for Reacts app 🔏</i>
|
||||
<i>🔏 Create Keycloak themes using React 🔏</i>
|
||||
<br>
|
||||
<br>
|
||||
<img src="https://github.com/garronej/keycloak-react-theming/workflows/ci/badge.svg?branch=develop">
|
||||
<img src="https://img.shields.io/bundlephobia/minzip/keycloak-react-theming">
|
||||
<img src="https://img.shields.io/npm/dw/keycloak-react-theming">
|
||||
<img src="https://img.shields.io/npm/l/keycloak-react-theming">
|
||||
<a href="https://github.com/garronej/keycloakify/actions">
|
||||
<img src="https://github.com/garronej/keycloakify/workflows/ci/badge.svg?branch=main">
|
||||
</a>
|
||||
<a href="https://bundlephobia.com/package/keycloakify">
|
||||
<img src="https://img.shields.io/bundlephobia/minzip/keycloakify">
|
||||
</a>
|
||||
<a href="https://www.npmjs.com/package/keycloakify">
|
||||
<img src="https://img.shields.io/npm/dm/keycloakify">
|
||||
</a>
|
||||
<a href="https://github.com/garronej/keycloakify/blob/main/LICENSE">
|
||||
<img src="https://img.shields.io/npm/l/keycloakify">
|
||||
</a>
|
||||
<a href="https://github.com/InseeFrLab/keycloakify/blob/729503fe31a155a823f46dd66ad4ff34ca274e0a/tsconfig.json#L14">
|
||||
<img src="https://camo.githubusercontent.com/0f9fcc0ac1b8617ad4989364f60f78b2d6b32985ad6a508f215f14d8f897b8d3/68747470733a2f2f62616467656e2e6e65742f62616467652f547970655363726970742f7374726963742532302546302539462539322541412f626c7565">
|
||||
</a>
|
||||
<a href="https://github.com/thomasdarimont/awesome-keycloak">
|
||||
<img src="https://awesome.re/mentioned-badge.svg"/>
|
||||
</a>
|
||||
<p align="center">
|
||||
<a href="https://www.keycloakify.dev">Home</a>
|
||||
-
|
||||
<a href="https://docs.keycloakify.dev">Documentation</a>
|
||||
</p>
|
||||
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<i>Ultimately this build tool generates a Keycloak theme <a href="https://www.keycloakify.dev">Learn more</a></i>
|
||||
<img src="https://user-images.githubusercontent.com/6702424/110260457-a1c3d380-7fac-11eb-853a-80459b65626b.png">
|
||||
</p>
|
||||
|
||||
**!!! This module is under active developement. It is not usable yet!!!**
|
||||
# Changelog highlights
|
||||
|
||||
# Motivations
|
||||
## 6.0.0
|
||||
|
||||
The problem:
|
||||
- Bundle size drastically reduced, locals and component dynamically loaded.
|
||||
- First print much quicker, use of React.lazy() everywhere.
|
||||
- Real i18n API.
|
||||
- Actual documentation for build options.
|
||||
|
||||

|
||||
Checkout the migration guide.
|
||||
|
||||
When we redirected to Keycloak the user suffers from a harsh context switch.
|
||||
On je login/register pages the language is set back to default and the theme is different that the one on the app.
|
||||
## 5.8.0
|
||||
|
||||
Keycloak does offer a way to customize theses pages but it requires a lot of raw HTML/CSS hacking
|
||||
to reproduce the look and feel of a specific app. Not mentioning the maintenance cost of such an endeavour.
|
||||
- [React.lazy()](https://reactjs.org/docs/code-splitting.html#reactlazy) support 🎉. [#141](https://github.com/InseeFrLab/keycloakify/issues/141)
|
||||
|
||||
Wouldn't it be great if we could just design the login and register pages as if they where part of our app while
|
||||
still letting Keycloak handle the heavy lifting of actually authenticating the users?
|
||||
## 5.7.0
|
||||
|
||||
Here is `yarn add keycloak-react-theming` for you 🍸
|
||||
- Feat `logout-confirm.ftl`. [PR](https://github.com/InseeFrLab/keycloakify/pull/120)
|
||||
|
||||
TODO: Insert video after.
|
||||
## 5.6.4
|
||||
|
||||
# How to use
|
||||
Fix `login-verify-email.ftl` page. [Before](https://user-images.githubusercontent.com/6702424/177436014-0bad22c4-5bfb-45bb-8fc9-dad65143cd0c.png) - [After](https://user-images.githubusercontent.com/6702424/177435797-ec5d7db3-84cf-49cb-8efc-3427a81f744e.png)
|
||||
|
||||
## Setting up the build tool
|
||||
## v5.6.0
|
||||
|
||||
Add `keycloak-react-theming` to the dev dependencies of your project `npm install --save-dev keycloak-react-theming` or `yarn add --dev keycloak-react-theming`
|
||||
then configure your `package.json` build's script to build the keycloak's theme by adding `&& build-keycloak-theme`.
|
||||
Add support for `login-config-totp.ftl` page [#127](https://github.com/InseeFrLab/keycloakify/pull/127).
|
||||
|
||||
Typically you will get:
|
||||
## v5.3.0
|
||||
|
||||
`package.json`:
|
||||
```json
|
||||
"devDependencies": {
|
||||
"keycloak-react-theming": "^0.0.10"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "react-scripts build && build-keycloak-theme"
|
||||
},
|
||||
```
|
||||
Rename `keycloak_theme_email` to `keycloak_email`.
|
||||
If you already had a `keycloak_theme_email` you should rename it `keycloak_email`.
|
||||
|
||||
Then build your app with `yarn run build` or `npm run build`, you will be provided with instructions
|
||||
about how to load the theme into Keycloak.
|
||||
## v5.0.0
|
||||
|
||||
[](https://youtu.be/xTz0Rj7i2v8)
|
||||
[Migration guide](https://github.com/garronej/keycloakify-demo-app/blob/a5b6a50f24bc25e082931f5ad9ebf47492acd12a/src/index.tsx#L46-L63)
|
||||
New i18n system.
|
||||
Import of terms and services have changed. [See example](https://github.com/garronej/keycloakify-demo-app/blob/a5b6a50f24bc25e082931f5ad9ebf47492acd12a/src/index.tsx#L46-L63).
|
||||
|
||||
## Developing your login and register pages in your React app
|
||||
## v4.10.0
|
||||
|
||||
TODO
|
||||
Add `login-idp-link-email.ftl` page [See PR](https://github.com/InseeFrLab/keycloakify/pull/92).
|
||||
|
||||
# How to implement context persistance
|
||||
## v4.8.0
|
||||
|
||||
If you want dark mode preference, language and others users preferences your can do so
|
||||
very easily by using [`powerhooks/useGlobalState`](https://github.com/garronej/powerhooks)
|
||||
[Email template customization.](#email-template-customization)
|
||||
|
||||
WARNING: `powerhooks` is still a work in progress.
|
||||
## v4.7.4
|
||||
|
||||
# REQUIREMENTS
|
||||
**M1 Mac** support (for testing locally with a dockerized Keycloak).
|
||||
|
||||
This tools assumes you are bundling your app with Webpack (tested with 4.44.2) .
|
||||
It assumes there is a `build/` directory at the root of your react project directory containing a `index.html` file
|
||||
and a `static/` directory generated by webpack.
|
||||
## v4.7.2
|
||||
|
||||
**All this is defaults with [`create-react-app`](https://create-react-app.dev)** (tested with 4.0.3=)
|
||||
> WARNING: This is broken.
|
||||
> Testing with local Keycloak container working with M1 Mac. Thanks to [@eduardosanzb](https://github.com/InseeFrLab/keycloakify/issues/43#issuecomment-975699658).
|
||||
> Be aware: When running M1s you are testing with Keycloak v15 else the local container spun will be a Keycloak v16.1.0.
|
||||
|
||||
- For building the theme: `mvn` (Maven) must be installed
|
||||
- For development, (testing the theme in a local container ): `rm`, `mkdir`, `wget`, `unzip` are assumed to be available
|
||||
and `docker` up and running.
|
||||
## v4.7.0
|
||||
|
||||
NOTE: This build tool has only be tested on MacOS.
|
||||
Register with user profile enabled: Out of the box `options` validator support.
|
||||
[Example](https://user-images.githubusercontent.com/6702424/158911163-81e6bbe8-feb0-4dc8-abff-de199d7a678e.mov)
|
||||
|
||||
# API Reference
|
||||
## v4.6.0
|
||||
|
||||
## The build tool
|
||||
`tss-react` and `powerhooks` are no longer peer dependencies of `keycloakify`.
|
||||
After updating Keycloakify you can remove `tss-react` and `powerhooks` from your dependencies if you don't use them explicitly.
|
||||
|
||||
Part of the lib that runs with node, at build time.
|
||||
## v4.5.3
|
||||
|
||||
- `npx build-keycloak-theme`: Builds the theme, the CWD is assumed to be the root of your react project.
|
||||
- `npx download-sample-keycloak-themes`: Downloads the keycloak default themes (for development purposes)
|
||||
There is a new recommended way to setup highly customized theme. See [here](https://github.com/garronej/keycloakify-demo-app/blob/look_and_feel/src/KcApp/KcApp.tsx).
|
||||
Unlike with [the previous recommended method](https://github.com/garronej/keycloakify-demo-app/blob/a51660578bea15fb3e506b8a2b78e1056c6d68bb/src/KcApp/KcApp.tsx),
|
||||
with this new method your theme wont break on minor Keycloakify update.
|
||||
|
||||
## The fronted lib ( imported into your react app )
|
||||
## v4.3.0
|
||||
|
||||
Part of the lib that you import in your react project and runs on the browser.
|
||||
Feature [`login-update-password.ftl`](https://user-images.githubusercontent.com/6702424/147517600-6191cf72-93dd-437b-a35c-47180142063e.png).
|
||||
Every time a page is added it's a breaking change for non CSS-only theme.
|
||||
Change [this](https://github.com/garronej/keycloakify-demo-app/blob/df664c13c77ce3c53ac7df0622d94d04e76d3f9f/src/KcApp/KcApp.tsx#L17) and [this](https://github.com/garronej/keycloakify-demo-app/blob/df664c13c77ce3c53ac7df0622d94d04e76d3f9f/src/KcApp/KcApp.tsx#L37) to update.
|
||||
|
||||
**TODO**
|
||||
## v4
|
||||
|
||||
- Out of the box [frontend form validation](#user-profile-and-frontend-form-validation) 🥳
|
||||
- Improvements (and breaking changes in `import { useKcMessage } from "keycloakify"`.
|
||||
|
||||
## v3
|
||||
|
||||
No breaking changes except that `@emotion/react`, [`tss-react`](https://www.npmjs.com/package/tss-react) and [`powerhooks`](https://www.npmjs.com/package/powerhooks) are now `peerDependencies` instead of being just dependencies.
|
||||
It's important to avoid problem when using `keycloakify` alongside [`mui`](https://mui.com) and
|
||||
[when passing params from the app to the login page](https://github.com/InseeFrLab/keycloakify#implement-context-persistence-optional).
|
||||
|
||||
## v2.5
|
||||
|
||||
- Feature [Use advanced message](https://github.com/InseeFrLab/keycloakify/blob/59f106bf9e210b63b190826da2bf5f75fc8b7644/src/lib/i18n/useKcMessage.tsx#L53-L66)
|
||||
and [`messagesPerFields`](https://github.com/InseeFrLab/keycloakify/blob/59f106bf9e210b63b190826da2bf5f75fc8b7644/src/lib/getKcContext/KcContextBase.ts#L70-L75) (implementation [here](https://github.com/InseeFrLab/keycloakify/blob/59f106bf9e210b63b190826da2bf5f75fc8b7644/src/bin/build-keycloak-theme/generateFtl/common.ftl#L130-L189))
|
||||
- Test container now uses Keycloak version `15.0.2`.
|
||||
|
||||
## v2
|
||||
|
||||
- It's now possible to implement custom `.ftl` pages.
|
||||
- Support for Keycloak plugins that introduce non standard ftl values.
|
||||
(Like for example [this plugin](https://github.com/micedre/keycloak-mail-whitelisting) that define `authorizedMailDomains` in `register.ftl`).
|
||||
|
74
package.json
74
package.json
@ -1,37 +1,49 @@
|
||||
{
|
||||
"name": "keycloak-react-theming",
|
||||
"version": "0.0.24",
|
||||
"name": "keycloakify",
|
||||
"version": "6.0.0-beta.8",
|
||||
"description": "Keycloak theme generator for Reacts app",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/garronej/keycloak-react-theming.git"
|
||||
"url": "git://github.com/garronej/keycloakify.git"
|
||||
},
|
||||
"main": "dist/lib/index.js",
|
||||
"types": "dist/lib/index.d.ts",
|
||||
"scripts": {
|
||||
"clean": "rimraf dist/",
|
||||
"build": "yarn clean && tsc && yarn grant-exec-perms && yarn copy-files",
|
||||
"build": "rimraf dist/ && tsc -p src/bin && tsc -p src/lib && yarn grant-exec-perms && yarn copy-files dist/",
|
||||
"build:test": "rimraf dist_test/ && tsc -p src/test && yarn copy-files dist_test/",
|
||||
"grant-exec-perms": "node dist/bin/tools/grant-exec-perms.js",
|
||||
"test": "node dist/test/build-keycloak-theme.js && node dist/test/download-sample-keycloak-themes.js",
|
||||
"enable_short_import_path": "yarn build && denoify_enable_short_npm_import_path",
|
||||
"copy-files": "copyfiles -u 1 src/**/*.ftl src/**/*.xml dist/",
|
||||
"copy-files": "copyfiles -u 1 src/**/*.ftl",
|
||||
"test": "yarn build:test && node dist_test/test/bin && node dist_test/test/lib",
|
||||
"generate-messages": "node dist/bin/generate-i18n-messages.js",
|
||||
"watch": "tsc -w"
|
||||
"link_in_test_app": "node dist/bin/link_in_test_app.js",
|
||||
"_format": "prettier '**/*.{ts,tsx,json,md}'",
|
||||
"format": "yarn _format --write",
|
||||
"format:check": "yarn _format --list-different"
|
||||
},
|
||||
"bin": {
|
||||
"build-keycloak-theme": "dist/bin/build-keycloak-theme/index.js",
|
||||
"download-sample-keycloak-themes": "dist/bin/download-sample-keycloak-themes.js"
|
||||
"keycloakify": "dist/bin/keycloakify/index.js",
|
||||
"create-keycloak-email-directory": "dist/bin/create-keycloak-email-directory.js",
|
||||
"download-builtin-keycloak-theme": "dist/bin/download-builtin-keycloak-theme.js"
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.{ts,tsx,json,md}": [
|
||||
"prettier --write"
|
||||
]
|
||||
},
|
||||
"husky": {
|
||||
"hooks": {
|
||||
"pre-commit": "lint-staged -v"
|
||||
}
|
||||
},
|
||||
"author": "u/garronej",
|
||||
"license": "MIT",
|
||||
"files": [
|
||||
"src/",
|
||||
"!src/test/",
|
||||
"dist/",
|
||||
"!dist/test/",
|
||||
"!dist/tsconfig.tsbuildinfo"
|
||||
],
|
||||
"keywords": [
|
||||
"bluehats",
|
||||
"keycloak",
|
||||
"react",
|
||||
"theme",
|
||||
@ -40,22 +52,38 @@
|
||||
"login",
|
||||
"register"
|
||||
],
|
||||
"homepage": "https://github.com/garronej/keycloak-react-theming",
|
||||
"homepage": "https://github.com/garronej/keycloakify",
|
||||
"peerDependencies": {
|
||||
"@emotion/react": "^11.4.1",
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^10.0.0",
|
||||
"@types/react": "^17.0.0",
|
||||
"@emotion/react": "^11.4.1",
|
||||
"@types/memoizee": "^0.4.7",
|
||||
"@types/node": "^17.0.25",
|
||||
"@types/react": "18.0.9",
|
||||
"copyfiles": "^2.4.1",
|
||||
"denoify": "^0.6.5",
|
||||
"husky": "^4.3.8",
|
||||
"lint-staged": "^11.0.0",
|
||||
"prettier": "^2.3.0",
|
||||
"properties-parser": "^0.3.1",
|
||||
"react": "^17.0.1",
|
||||
"react": "18.1.0",
|
||||
"rimraf": "^3.0.2",
|
||||
"scripting-tools": "^0.19.13",
|
||||
"typescript": "^4.1.5"
|
||||
"typescript": "^4.2.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"@octokit/rest": "^18.12.0",
|
||||
"cheerio": "^1.0.0-rc.5",
|
||||
"evt": "^1.9.12",
|
||||
"powerhooks": "^0.0.14",
|
||||
"tss-react": "^0.0.9"
|
||||
"cli-select": "^1.1.2",
|
||||
"evt": "^2.4.0",
|
||||
"memoizee": "^0.4.15",
|
||||
"minimal-polyfills": "^2.2.2",
|
||||
"path-browserify": "^1.0.1",
|
||||
"powerhooks": "^0.20.15",
|
||||
"react-markdown": "^5.0.3",
|
||||
"scripting-tools": "^0.19.13",
|
||||
"tsafe": "^1.0.1",
|
||||
"tss-react": "^4.0.0",
|
||||
"zod": "^3.17.10"
|
||||
}
|
||||
}
|
||||
|
27
renovate.json
Normal file
27
renovate.json
Normal file
@ -0,0 +1,27 @@
|
||||
{
|
||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||
"baseBranches": ["main", "landingpage"],
|
||||
"extends": ["config:base"],
|
||||
"dependencyDashboard": false,
|
||||
"bumpVersion": "patch",
|
||||
"rangeStrategy": "bump",
|
||||
"ignorePaths": [".github/**"],
|
||||
"branchPrefix": "renovate_",
|
||||
"vulnerabilityAlerts": {
|
||||
"enabled": false
|
||||
},
|
||||
"packageRules": [
|
||||
{
|
||||
"packagePatterns": ["*"],
|
||||
"excludePackagePatterns": ["tss-react", "powerhooks", "tsafe", "evt"],
|
||||
"enabled": false
|
||||
},
|
||||
{
|
||||
"packagePatterns": ["tss-react", "powerhooks", "tsafe", "evt"],
|
||||
"matchUpdateTypes": ["minor", "patch"],
|
||||
"automerge": true,
|
||||
"automergeType": "branch",
|
||||
"groupName": "garronej_modules_update"
|
||||
}
|
||||
]
|
||||
}
|
@ -1,74 +0,0 @@
|
||||
|
||||
import * as fs from "fs";
|
||||
import { join as pathJoin, dirname as pathDirname, basename as pathBasename } from "path";
|
||||
|
||||
export const containerLaunchScriptBasename = "start_keycloak_testing_container.sh";
|
||||
|
||||
/** Files for being able to run a hot reload keycloak container */
|
||||
export function generateDebugFiles(
|
||||
params: {
|
||||
packageJsonName: string;
|
||||
keycloakThemeBuildingDirPath: string;
|
||||
}
|
||||
) {
|
||||
|
||||
const { packageJsonName, keycloakThemeBuildingDirPath } = params;
|
||||
|
||||
fs.writeFileSync(
|
||||
pathJoin(keycloakThemeBuildingDirPath, "Dockerfile"),
|
||||
Buffer.from(
|
||||
[
|
||||
"FROM jboss/keycloak:11.0.3",
|
||||
"",
|
||||
"USER root",
|
||||
"",
|
||||
"WORKDIR /",
|
||||
"",
|
||||
"ADD configuration /opt/jboss/keycloak/standalone/configuration/",
|
||||
"",
|
||||
'ENTRYPOINT [ "/opt/jboss/tools/docker-entrypoint.sh" ]',
|
||||
].join("\n"),
|
||||
"utf8"
|
||||
)
|
||||
);
|
||||
|
||||
const dockerImage = `${packageJsonName}/keycloak-hot-reload`;
|
||||
const containerName = "keycloak-testing-container";
|
||||
|
||||
fs.writeFileSync(
|
||||
pathJoin(keycloakThemeBuildingDirPath, containerLaunchScriptBasename),
|
||||
Buffer.from(
|
||||
[
|
||||
"#!/bin/bash",
|
||||
"",
|
||||
`cd ${keycloakThemeBuildingDirPath}`,
|
||||
"",
|
||||
`docker rm ${containerName} || true`,
|
||||
"",
|
||||
`docker build . -t ${dockerImage}`,
|
||||
"",
|
||||
"docker run \\",
|
||||
" -p 8080:8080 \\",
|
||||
` --name ${containerName} \\`,
|
||||
" -e KEYCLOAK_USER=admin \\",
|
||||
" -e KEYCLOAK_PASSWORD=admin \\",
|
||||
` -v ${pathJoin(keycloakThemeBuildingDirPath, "src", "main", "resources", "theme", packageJsonName)
|
||||
}:/opt/jboss/keycloak/themes/${packageJsonName}:rw \\`,
|
||||
` -it ${dockerImage}:latest`,
|
||||
""
|
||||
].join("\n"),
|
||||
"utf8"
|
||||
),
|
||||
{ "mode": 0o755 }
|
||||
);
|
||||
|
||||
const standaloneHaFilePath = pathJoin(keycloakThemeBuildingDirPath, "configuration", "standalone-ha.xml");
|
||||
|
||||
try { fs.mkdirSync(pathDirname(standaloneHaFilePath)); } catch { }
|
||||
|
||||
fs.writeFileSync(
|
||||
standaloneHaFilePath,
|
||||
fs.readFileSync(pathJoin(__dirname, pathBasename(standaloneHaFilePath)))
|
||||
);
|
||||
|
||||
}
|
@ -1,666 +0,0 @@
|
||||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
|
||||
<server xmlns="urn:jboss:domain:13.0">
|
||||
<extensions>
|
||||
<extension module="org.jboss.as.clustering.infinispan"/>
|
||||
<extension module="org.jboss.as.clustering.jgroups"/>
|
||||
<extension module="org.jboss.as.connector"/>
|
||||
<extension module="org.jboss.as.deployment-scanner"/>
|
||||
<extension module="org.jboss.as.ee"/>
|
||||
<extension module="org.jboss.as.ejb3"/>
|
||||
<extension module="org.jboss.as.jaxrs"/>
|
||||
<extension module="org.jboss.as.jmx"/>
|
||||
<extension module="org.jboss.as.jpa"/>
|
||||
<extension module="org.jboss.as.logging"/>
|
||||
<extension module="org.jboss.as.mail"/>
|
||||
<extension module="org.jboss.as.modcluster"/>
|
||||
<extension module="org.jboss.as.naming"/>
|
||||
<extension module="org.jboss.as.remoting"/>
|
||||
<extension module="org.jboss.as.security"/>
|
||||
<extension module="org.jboss.as.transactions"/>
|
||||
<extension module="org.jboss.as.weld"/>
|
||||
<extension module="org.keycloak.keycloak-server-subsystem"/>
|
||||
<extension module="org.wildfly.extension.bean-validation"/>
|
||||
<extension module="org.wildfly.extension.core-management"/>
|
||||
<extension module="org.wildfly.extension.elytron"/>
|
||||
<extension module="org.wildfly.extension.io"/>
|
||||
<extension module="org.wildfly.extension.microprofile.config-smallrye"/>
|
||||
<extension module="org.wildfly.extension.microprofile.health-smallrye"/>
|
||||
<extension module="org.wildfly.extension.microprofile.metrics-smallrye"/>
|
||||
<extension module="org.wildfly.extension.request-controller"/>
|
||||
<extension module="org.wildfly.extension.security.manager"/>
|
||||
<extension module="org.wildfly.extension.undertow"/>
|
||||
</extensions>
|
||||
<management>
|
||||
<security-realms>
|
||||
<security-realm name="ManagementRealm">
|
||||
<authentication>
|
||||
<local default-user="$local" skip-group-loading="true"/>
|
||||
<properties path="mgmt-users.properties" relative-to="jboss.server.config.dir"/>
|
||||
</authentication>
|
||||
<authorization map-groups-to-roles="false">
|
||||
<properties path="mgmt-groups.properties" relative-to="jboss.server.config.dir"/>
|
||||
</authorization>
|
||||
</security-realm>
|
||||
<security-realm name="ApplicationRealm">
|
||||
<server-identities>
|
||||
<ssl>
|
||||
<keystore path="application.keystore" relative-to="jboss.server.config.dir" keystore-password="password" alias="server" key-password="password" generate-self-signed-certificate-host="localhost"/>
|
||||
</ssl>
|
||||
</server-identities>
|
||||
<authentication>
|
||||
<local default-user="$local" allowed-users="*" skip-group-loading="true"/>
|
||||
<properties path="application-users.properties" relative-to="jboss.server.config.dir"/>
|
||||
</authentication>
|
||||
<authorization>
|
||||
<properties path="application-roles.properties" relative-to="jboss.server.config.dir"/>
|
||||
</authorization>
|
||||
</security-realm>
|
||||
</security-realms>
|
||||
<audit-log>
|
||||
<formatters>
|
||||
<json-formatter name="json-formatter"/>
|
||||
</formatters>
|
||||
<handlers>
|
||||
<file-handler name="file" formatter="json-formatter" path="audit-log.log" relative-to="jboss.server.data.dir"/>
|
||||
</handlers>
|
||||
<logger log-boot="true" log-read-only="false" enabled="false">
|
||||
<handlers>
|
||||
<handler name="file"/>
|
||||
</handlers>
|
||||
</logger>
|
||||
</audit-log>
|
||||
<management-interfaces>
|
||||
<http-interface security-realm="ManagementRealm">
|
||||
<http-upgrade enabled="true"/>
|
||||
<socket-binding http="management-http"/>
|
||||
</http-interface>
|
||||
</management-interfaces>
|
||||
<access-control provider="simple">
|
||||
<role-mapping>
|
||||
<role name="SuperUser">
|
||||
<include>
|
||||
<user name="$local"/>
|
||||
</include>
|
||||
</role>
|
||||
</role-mapping>
|
||||
</access-control>
|
||||
</management>
|
||||
<profile>
|
||||
<subsystem xmlns="urn:jboss:domain:logging:8.0">
|
||||
<console-handler name="CONSOLE">
|
||||
<formatter>
|
||||
<named-formatter name="COLOR-PATTERN"/>
|
||||
</formatter>
|
||||
</console-handler>
|
||||
<logger category="com.arjuna">
|
||||
<level name="WARN"/>
|
||||
</logger>
|
||||
<logger category="io.jaegertracing.Configuration">
|
||||
<level name="WARN"/>
|
||||
</logger>
|
||||
<logger category="org.jboss.as.config">
|
||||
<level name="DEBUG"/>
|
||||
</logger>
|
||||
<logger category="sun.rmi">
|
||||
<level name="WARN"/>
|
||||
</logger>
|
||||
<logger category="org.keycloak">
|
||||
<level name="${env.KEYCLOAK_LOGLEVEL:INFO}"/>
|
||||
</logger>
|
||||
<root-logger>
|
||||
<level name="${env.ROOT_LOGLEVEL:INFO}"/>
|
||||
<handlers>
|
||||
<handler name="CONSOLE"/>
|
||||
</handlers>
|
||||
</root-logger>
|
||||
<formatter name="PATTERN">
|
||||
<pattern-formatter pattern="%d{yyyy-MM-dd HH:mm:ss,SSS} %-5p [%c] (%t) %s%e%n"/>
|
||||
</formatter>
|
||||
<formatter name="COLOR-PATTERN">
|
||||
<pattern-formatter pattern="%K{level}%d{HH:mm:ss,SSS} %-5p [%c] (%t) %s%e%n"/>
|
||||
</formatter>
|
||||
</subsystem>
|
||||
<subsystem xmlns="urn:jboss:domain:bean-validation:1.0"/>
|
||||
<subsystem xmlns="urn:jboss:domain:core-management:1.0"/>
|
||||
<subsystem xmlns="urn:jboss:domain:datasources:6.0">
|
||||
<datasources>
|
||||
<datasource jndi-name="java:jboss/datasources/ExampleDS" pool-name="ExampleDS" enabled="true" use-java-context="true" statistics-enabled="${wildfly.datasources.statistics-enabled:${wildfly.statistics-enabled:false}}">
|
||||
<connection-url>jdbc:h2:mem:test;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE</connection-url>
|
||||
<driver>h2</driver>
|
||||
<security>
|
||||
<user-name>sa</user-name>
|
||||
<password>sa</password>
|
||||
</security>
|
||||
</datasource>
|
||||
<datasource jndi-name="java:jboss/datasources/KeycloakDS" pool-name="KeycloakDS" enabled="true" use-java-context="true" statistics-enabled="${wildfly.datasources.statistics-enabled:${wildfly.statistics-enabled:false}}">
|
||||
<connection-url>jdbc:h2:${jboss.server.data.dir}/keycloak;AUTO_SERVER=TRUE</connection-url>
|
||||
<driver>h2</driver>
|
||||
<pool>
|
||||
<max-pool-size>100</max-pool-size>
|
||||
</pool>
|
||||
<security>
|
||||
<user-name>sa</user-name>
|
||||
<password>sa</password>
|
||||
</security>
|
||||
</datasource>
|
||||
<drivers>
|
||||
<driver name="h2" module="com.h2database.h2">
|
||||
<xa-datasource-class>org.h2.jdbcx.JdbcDataSource</xa-datasource-class>
|
||||
</driver>
|
||||
</drivers>
|
||||
</datasources>
|
||||
</subsystem>
|
||||
<subsystem xmlns="urn:jboss:domain:deployment-scanner:2.0">
|
||||
<deployment-scanner path="deployments" relative-to="jboss.server.base.dir" scan-interval="5000" runtime-failure-causes-rollback="${jboss.deployment.scanner.rollback.on.failure:false}"/>
|
||||
</subsystem>
|
||||
<subsystem xmlns="urn:jboss:domain:ee:5.0">
|
||||
<spec-descriptor-property-replacement>false</spec-descriptor-property-replacement>
|
||||
<concurrent>
|
||||
<context-services>
|
||||
<context-service name="default" jndi-name="java:jboss/ee/concurrency/context/default" use-transaction-setup-provider="true"/>
|
||||
</context-services>
|
||||
<managed-thread-factories>
|
||||
<managed-thread-factory name="default" jndi-name="java:jboss/ee/concurrency/factory/default" context-service="default"/>
|
||||
</managed-thread-factories>
|
||||
<managed-executor-services>
|
||||
<managed-executor-service name="default" jndi-name="java:jboss/ee/concurrency/executor/default" context-service="default" hung-task-threshold="60000" keepalive-time="5000"/>
|
||||
</managed-executor-services>
|
||||
<managed-scheduled-executor-services>
|
||||
<managed-scheduled-executor-service name="default" jndi-name="java:jboss/ee/concurrency/scheduler/default" context-service="default" hung-task-threshold="60000" keepalive-time="3000"/>
|
||||
</managed-scheduled-executor-services>
|
||||
</concurrent>
|
||||
<default-bindings context-service="java:jboss/ee/concurrency/context/default" datasource="java:jboss/datasources/ExampleDS" managed-executor-service="java:jboss/ee/concurrency/executor/default" managed-scheduled-executor-service="java:jboss/ee/concurrency/scheduler/default" managed-thread-factory="java:jboss/ee/concurrency/factory/default"/>
|
||||
</subsystem>
|
||||
<subsystem xmlns="urn:jboss:domain:ejb3:7.0">
|
||||
<session-bean>
|
||||
<stateless>
|
||||
<bean-instance-pool-ref pool-name="slsb-strict-max-pool"/>
|
||||
</stateless>
|
||||
<stateful default-access-timeout="5000" cache-ref="distributable" passivation-disabled-cache-ref="simple"/>
|
||||
<singleton default-access-timeout="5000"/>
|
||||
</session-bean>
|
||||
<pools>
|
||||
<bean-instance-pools>
|
||||
<strict-max-pool name="mdb-strict-max-pool" derive-size="from-cpu-count" instance-acquisition-timeout="5" instance-acquisition-timeout-unit="MINUTES"/>
|
||||
<strict-max-pool name="slsb-strict-max-pool" derive-size="from-worker-pools" instance-acquisition-timeout="5" instance-acquisition-timeout-unit="MINUTES"/>
|
||||
</bean-instance-pools>
|
||||
</pools>
|
||||
<caches>
|
||||
<cache name="simple"/>
|
||||
<cache name="distributable" passivation-store-ref="infinispan" aliases="passivating clustered"/>
|
||||
</caches>
|
||||
<passivation-stores>
|
||||
<passivation-store name="infinispan" cache-container="ejb" max-size="10000"/>
|
||||
</passivation-stores>
|
||||
<async thread-pool-name="default"/>
|
||||
<timer-service thread-pool-name="default" default-data-store="default-file-store">
|
||||
<data-stores>
|
||||
<file-data-store name="default-file-store" path="timer-service-data" relative-to="jboss.server.data.dir"/>
|
||||
</data-stores>
|
||||
</timer-service>
|
||||
<remote connector-ref="http-remoting-connector" thread-pool-name="default">
|
||||
<channel-creation-options>
|
||||
<option name="MAX_OUTBOUND_MESSAGES" value="1234" type="remoting"/>
|
||||
</channel-creation-options>
|
||||
</remote>
|
||||
<thread-pools>
|
||||
<thread-pool name="default">
|
||||
<max-threads count="10"/>
|
||||
<keepalive-time time="60" unit="seconds"/>
|
||||
</thread-pool>
|
||||
</thread-pools>
|
||||
<default-security-domain value="other"/>
|
||||
<default-missing-method-permissions-deny-access value="true"/>
|
||||
<statistics enabled="${wildfly.ejb3.statistics-enabled:${wildfly.statistics-enabled:false}}"/>
|
||||
<log-system-exceptions value="true"/>
|
||||
</subsystem>
|
||||
<subsystem xmlns="urn:wildfly:elytron:10.0" final-providers="combined-providers" disallowed-providers="OracleUcrypto">
|
||||
<providers>
|
||||
<aggregate-providers name="combined-providers">
|
||||
<providers name="elytron"/>
|
||||
<providers name="openssl"/>
|
||||
</aggregate-providers>
|
||||
<provider-loader name="elytron" module="org.wildfly.security.elytron"/>
|
||||
<provider-loader name="openssl" module="org.wildfly.openssl"/>
|
||||
</providers>
|
||||
<audit-logging>
|
||||
<file-audit-log name="local-audit" path="audit.log" relative-to="jboss.server.log.dir" format="JSON"/>
|
||||
</audit-logging>
|
||||
<security-domains>
|
||||
<security-domain name="ApplicationDomain" default-realm="ApplicationRealm" permission-mapper="default-permission-mapper">
|
||||
<realm name="ApplicationRealm" role-decoder="groups-to-roles"/>
|
||||
<realm name="local"/>
|
||||
</security-domain>
|
||||
<security-domain name="ManagementDomain" default-realm="ManagementRealm" permission-mapper="default-permission-mapper">
|
||||
<realm name="ManagementRealm" role-decoder="groups-to-roles"/>
|
||||
<realm name="local" role-mapper="super-user-mapper"/>
|
||||
</security-domain>
|
||||
</security-domains>
|
||||
<security-realms>
|
||||
<identity-realm name="local" identity="$local"/>
|
||||
<properties-realm name="ApplicationRealm">
|
||||
<users-properties path="application-users.properties" relative-to="jboss.server.config.dir" digest-realm-name="ApplicationRealm"/>
|
||||
<groups-properties path="application-roles.properties" relative-to="jboss.server.config.dir"/>
|
||||
</properties-realm>
|
||||
<properties-realm name="ManagementRealm">
|
||||
<users-properties path="mgmt-users.properties" relative-to="jboss.server.config.dir" digest-realm-name="ManagementRealm"/>
|
||||
<groups-properties path="mgmt-groups.properties" relative-to="jboss.server.config.dir"/>
|
||||
</properties-realm>
|
||||
</security-realms>
|
||||
<mappers>
|
||||
<simple-permission-mapper name="default-permission-mapper" mapping-mode="first">
|
||||
<permission-mapping>
|
||||
<principal name="anonymous"/>
|
||||
<permission-set name="default-permissions"/>
|
||||
</permission-mapping>
|
||||
<permission-mapping match-all="true">
|
||||
<permission-set name="login-permission"/>
|
||||
<permission-set name="default-permissions"/>
|
||||
</permission-mapping>
|
||||
</simple-permission-mapper>
|
||||
<constant-realm-mapper name="local" realm-name="local"/>
|
||||
<simple-role-decoder name="groups-to-roles" attribute="groups"/>
|
||||
<constant-role-mapper name="super-user-mapper">
|
||||
<role name="SuperUser"/>
|
||||
</constant-role-mapper>
|
||||
</mappers>
|
||||
<permission-sets>
|
||||
<permission-set name="login-permission">
|
||||
<permission class-name="org.wildfly.security.auth.permission.LoginPermission"/>
|
||||
</permission-set>
|
||||
<permission-set name="default-permissions">
|
||||
<permission class-name="org.wildfly.extension.batch.jberet.deployment.BatchPermission" module="org.wildfly.extension.batch.jberet" target-name="*"/>
|
||||
<permission class-name="org.wildfly.transaction.client.RemoteTransactionPermission" module="org.wildfly.transaction.client"/>
|
||||
<permission class-name="org.jboss.ejb.client.RemoteEJBPermission" module="org.jboss.ejb-client"/>
|
||||
</permission-set>
|
||||
</permission-sets>
|
||||
<http>
|
||||
<http-authentication-factory name="management-http-authentication" security-domain="ManagementDomain" http-server-mechanism-factory="global">
|
||||
<mechanism-configuration>
|
||||
<mechanism mechanism-name="DIGEST">
|
||||
<mechanism-realm realm-name="ManagementRealm"/>
|
||||
</mechanism>
|
||||
</mechanism-configuration>
|
||||
</http-authentication-factory>
|
||||
<provider-http-server-mechanism-factory name="global"/>
|
||||
</http>
|
||||
<sasl>
|
||||
<sasl-authentication-factory name="application-sasl-authentication" sasl-server-factory="configured" security-domain="ApplicationDomain">
|
||||
<mechanism-configuration>
|
||||
<mechanism mechanism-name="JBOSS-LOCAL-USER" realm-mapper="local"/>
|
||||
<mechanism mechanism-name="DIGEST-MD5">
|
||||
<mechanism-realm realm-name="ApplicationRealm"/>
|
||||
</mechanism>
|
||||
</mechanism-configuration>
|
||||
</sasl-authentication-factory>
|
||||
<sasl-authentication-factory name="management-sasl-authentication" sasl-server-factory="configured" security-domain="ManagementDomain">
|
||||
<mechanism-configuration>
|
||||
<mechanism mechanism-name="JBOSS-LOCAL-USER" realm-mapper="local"/>
|
||||
<mechanism mechanism-name="DIGEST-MD5">
|
||||
<mechanism-realm realm-name="ManagementRealm"/>
|
||||
</mechanism>
|
||||
</mechanism-configuration>
|
||||
</sasl-authentication-factory>
|
||||
<configurable-sasl-server-factory name="configured" sasl-server-factory="elytron">
|
||||
<properties>
|
||||
<property name="wildfly.sasl.local-user.default-user" value="$local"/>
|
||||
</properties>
|
||||
</configurable-sasl-server-factory>
|
||||
<mechanism-provider-filtering-sasl-server-factory name="elytron" sasl-server-factory="global">
|
||||
<filters>
|
||||
<filter provider-name="WildFlyElytron"/>
|
||||
</filters>
|
||||
</mechanism-provider-filtering-sasl-server-factory>
|
||||
<provider-sasl-server-factory name="global"/>
|
||||
</sasl>
|
||||
</subsystem>
|
||||
<subsystem xmlns="urn:jboss:domain:infinispan:10.0">
|
||||
<cache-container name="keycloak" module="org.keycloak.keycloak-model-infinispan">
|
||||
<transport lock-timeout="60000"/>
|
||||
<local-cache name="realms">
|
||||
<object-memory size="10000"/>
|
||||
</local-cache>
|
||||
<local-cache name="users">
|
||||
<object-memory size="10000"/>
|
||||
</local-cache>
|
||||
<local-cache name="authorization">
|
||||
<object-memory size="10000"/>
|
||||
</local-cache>
|
||||
<local-cache name="keys">
|
||||
<object-memory size="1000"/>
|
||||
<expiration max-idle="3600000"/>
|
||||
</local-cache>
|
||||
<replicated-cache name="work"/>
|
||||
<distributed-cache name="sessions" owners="1"/>
|
||||
<distributed-cache name="authenticationSessions" owners="1"/>
|
||||
<distributed-cache name="offlineSessions" owners="1"/>
|
||||
<distributed-cache name="clientSessions" owners="1"/>
|
||||
<distributed-cache name="offlineClientSessions" owners="1"/>
|
||||
<distributed-cache name="loginFailures" owners="1"/>
|
||||
<distributed-cache name="actionTokens" owners="2">
|
||||
<object-memory size="-1"/>
|
||||
<expiration interval="300000" max-idle="-1"/>
|
||||
</distributed-cache>
|
||||
</cache-container>
|
||||
<cache-container name="server" aliases="singleton cluster" default-cache="default" module="org.wildfly.clustering.server">
|
||||
<transport lock-timeout="60000"/>
|
||||
<replicated-cache name="default">
|
||||
<transaction mode="BATCH"/>
|
||||
</replicated-cache>
|
||||
</cache-container>
|
||||
<cache-container name="web" default-cache="dist" module="org.wildfly.clustering.web.infinispan">
|
||||
<transport lock-timeout="60000"/>
|
||||
<replicated-cache name="sso">
|
||||
<locking isolation="REPEATABLE_READ"/>
|
||||
<transaction mode="BATCH"/>
|
||||
</replicated-cache>
|
||||
<distributed-cache name="dist">
|
||||
<locking isolation="REPEATABLE_READ"/>
|
||||
<transaction mode="BATCH"/>
|
||||
<file-store/>
|
||||
</distributed-cache>
|
||||
<distributed-cache name="routing"/>
|
||||
</cache-container>
|
||||
<cache-container name="ejb" aliases="sfsb" default-cache="dist" module="org.wildfly.clustering.ejb.infinispan">
|
||||
<transport lock-timeout="60000"/>
|
||||
<distributed-cache name="dist">
|
||||
<locking isolation="REPEATABLE_READ"/>
|
||||
<transaction mode="BATCH"/>
|
||||
<file-store/>
|
||||
</distributed-cache>
|
||||
</cache-container>
|
||||
<cache-container name="hibernate" module="org.infinispan.hibernate-cache">
|
||||
<transport lock-timeout="60000"/>
|
||||
<local-cache name="local-query">
|
||||
<object-memory size="10000"/>
|
||||
<expiration max-idle="100000"/>
|
||||
</local-cache>
|
||||
<invalidation-cache name="entity">
|
||||
<transaction mode="NON_XA"/>
|
||||
<object-memory size="10000"/>
|
||||
<expiration max-idle="100000"/>
|
||||
</invalidation-cache>
|
||||
<replicated-cache name="timestamps"/>
|
||||
</cache-container>
|
||||
</subsystem>
|
||||
<subsystem xmlns="urn:jboss:domain:io:3.0">
|
||||
<worker name="default"/>
|
||||
<buffer-pool name="default"/>
|
||||
</subsystem>
|
||||
<subsystem xmlns="urn:jboss:domain:jaxrs:2.0"/>
|
||||
<subsystem xmlns="urn:jboss:domain:jca:5.0">
|
||||
<archive-validation enabled="true" fail-on-error="true" fail-on-warn="false"/>
|
||||
<bean-validation enabled="true"/>
|
||||
<default-workmanager>
|
||||
<short-running-threads>
|
||||
<core-threads count="50"/>
|
||||
<queue-length count="50"/>
|
||||
<max-threads count="50"/>
|
||||
<keepalive-time time="10" unit="seconds"/>
|
||||
</short-running-threads>
|
||||
<long-running-threads>
|
||||
<core-threads count="50"/>
|
||||
<queue-length count="50"/>
|
||||
<max-threads count="50"/>
|
||||
<keepalive-time time="10" unit="seconds"/>
|
||||
</long-running-threads>
|
||||
</default-workmanager>
|
||||
<cached-connection-manager/>
|
||||
</subsystem>
|
||||
<subsystem xmlns="urn:jboss:domain:jgroups:8.0">
|
||||
<channels default="ee">
|
||||
<channel name="ee" stack="udp" cluster="ejb"/>
|
||||
</channels>
|
||||
<stacks>
|
||||
<stack name="udp">
|
||||
<transport type="UDP" socket-binding="jgroups-udp"/>
|
||||
<protocol type="PING"/>
|
||||
<protocol type="MERGE3"/>
|
||||
<socket-protocol type="FD_SOCK" socket-binding="jgroups-udp-fd"/>
|
||||
<protocol type="FD_ALL"/>
|
||||
<protocol type="VERIFY_SUSPECT"/>
|
||||
<protocol type="pbcast.NAKACK2"/>
|
||||
<protocol type="UNICAST3"/>
|
||||
<protocol type="pbcast.STABLE"/>
|
||||
<protocol type="pbcast.GMS"/>
|
||||
<protocol type="UFC"/>
|
||||
<protocol type="MFC"/>
|
||||
<protocol type="FRAG3"/>
|
||||
</stack>
|
||||
<stack name="tcp">
|
||||
<transport type="TCP" socket-binding="jgroups-tcp"/>
|
||||
<socket-protocol type="MPING" socket-binding="jgroups-mping"/>
|
||||
<protocol type="MERGE3"/>
|
||||
<socket-protocol type="FD_SOCK" socket-binding="jgroups-tcp-fd"/>
|
||||
<protocol type="FD_ALL"/>
|
||||
<protocol type="VERIFY_SUSPECT"/>
|
||||
<protocol type="pbcast.NAKACK2"/>
|
||||
<protocol type="UNICAST3"/>
|
||||
<protocol type="pbcast.STABLE"/>
|
||||
<protocol type="pbcast.GMS"/>
|
||||
<protocol type="MFC"/>
|
||||
<protocol type="FRAG3"/>
|
||||
</stack>
|
||||
</stacks>
|
||||
</subsystem>
|
||||
<subsystem xmlns="urn:jboss:domain:jmx:1.3">
|
||||
<expose-resolved-model/>
|
||||
<expose-expression-model/>
|
||||
<remoting-connector/>
|
||||
</subsystem>
|
||||
<subsystem xmlns="urn:jboss:domain:jpa:1.1">
|
||||
<jpa default-datasource="" default-extended-persistence-inheritance="DEEP"/>
|
||||
</subsystem>
|
||||
<subsystem xmlns="urn:jboss:domain:keycloak-server:1.1">
|
||||
<web-context>auth</web-context>
|
||||
<providers>
|
||||
<provider>
|
||||
classpath:${jboss.home.dir}/providers/*
|
||||
</provider>
|
||||
</providers>
|
||||
<master-realm-name>master</master-realm-name>
|
||||
<scheduled-task-interval>900</scheduled-task-interval>
|
||||
<theme>
|
||||
<staticMaxAge>-1</staticMaxAge>
|
||||
<cacheThemes>false</cacheThemes>
|
||||
<cacheTemplates>false</cacheTemplates>
|
||||
<welcomeTheme>${env.KEYCLOAK_WELCOME_THEME:keycloak}</welcomeTheme>
|
||||
<default>${env.KEYCLOAK_DEFAULT_THEME:keycloak}</default>
|
||||
<dir>${jboss.home.dir}/themes</dir>
|
||||
</theme>
|
||||
<spi name="eventsStore">
|
||||
<provider name="jpa" enabled="true">
|
||||
<properties>
|
||||
<property name="exclude-events" value="["REFRESH_TOKEN"]"/>
|
||||
</properties>
|
||||
</provider>
|
||||
</spi>
|
||||
<spi name="userCache">
|
||||
<provider name="default" enabled="true"/>
|
||||
</spi>
|
||||
<spi name="userSessionPersister">
|
||||
<default-provider>jpa</default-provider>
|
||||
</spi>
|
||||
<spi name="timer">
|
||||
<default-provider>basic</default-provider>
|
||||
</spi>
|
||||
<spi name="connectionsHttpClient">
|
||||
<provider name="default" enabled="true"/>
|
||||
</spi>
|
||||
<spi name="connectionsJpa">
|
||||
<provider name="default" enabled="true">
|
||||
<properties>
|
||||
<property name="dataSource" value="java:jboss/datasources/KeycloakDS"/>
|
||||
<property name="initializeEmpty" value="true"/>
|
||||
<property name="migrationStrategy" value="update"/>
|
||||
<property name="migrationExport" value="${jboss.home.dir}/keycloak-database-update.sql"/>
|
||||
</properties>
|
||||
</provider>
|
||||
</spi>
|
||||
<spi name="realmCache">
|
||||
<provider name="default" enabled="true"/>
|
||||
</spi>
|
||||
<spi name="connectionsInfinispan">
|
||||
<default-provider>default</default-provider>
|
||||
<provider name="default" enabled="true">
|
||||
<properties>
|
||||
<property name="cacheContainer" value="java:jboss/infinispan/container/keycloak"/>
|
||||
</properties>
|
||||
</provider>
|
||||
</spi>
|
||||
<spi name="jta-lookup">
|
||||
<default-provider>${keycloak.jta.lookup.provider:jboss}</default-provider>
|
||||
<provider name="jboss" enabled="true"/>
|
||||
</spi>
|
||||
<spi name="publicKeyStorage">
|
||||
<provider name="infinispan" enabled="true">
|
||||
<properties>
|
||||
<property name="minTimeBetweenRequests" value="10"/>
|
||||
</properties>
|
||||
</provider>
|
||||
</spi>
|
||||
<spi name="x509cert-lookup">
|
||||
<default-provider>${keycloak.x509cert.lookup.provider:default}</default-provider>
|
||||
<provider name="default" enabled="true"/>
|
||||
</spi>
|
||||
<spi name="hostname">
|
||||
<default-provider>${keycloak.hostname.provider:default}</default-provider>
|
||||
<provider name="default" enabled="true">
|
||||
<properties>
|
||||
<property name="frontendUrl" value="${keycloak.frontendUrl:}"/>
|
||||
<property name="forceBackendUrlToFrontendUrl" value="false"/>
|
||||
</properties>
|
||||
</provider>
|
||||
<provider name="fixed" enabled="true">
|
||||
<properties>
|
||||
<property name="hostname" value="${keycloak.hostname.fixed.hostname:localhost}"/>
|
||||
<property name="httpPort" value="${keycloak.hostname.fixed.httpPort:-1}"/>
|
||||
<property name="httpsPort" value="${keycloak.hostname.fixed.httpsPort:-1}"/>
|
||||
<property name="alwaysHttps" value="${keycloak.hostname.fixed.alwaysHttps:false}"/>
|
||||
</properties>
|
||||
</provider>
|
||||
</spi>
|
||||
</subsystem>
|
||||
<subsystem xmlns="urn:jboss:domain:mail:4.0">
|
||||
<mail-session name="default" jndi-name="java:jboss/mail/Default">
|
||||
<smtp-server outbound-socket-binding-ref="mail-smtp"/>
|
||||
</mail-session>
|
||||
</subsystem>
|
||||
<subsystem xmlns="urn:wildfly:microprofile-config-smallrye:1.0"/>
|
||||
<subsystem xmlns="urn:wildfly:microprofile-health-smallrye:2.0" security-enabled="false" empty-liveness-checks-status="${env.MP_HEALTH_EMPTY_LIVENESS_CHECKS_STATUS:UP}" empty-readiness-checks-status="${env.MP_HEALTH_EMPTY_READINESS_CHECKS_STATUS:UP}"/>
|
||||
<subsystem xmlns="urn:wildfly:microprofile-metrics-smallrye:2.0" security-enabled="false" exposed-subsystems="*" prefix="${wildfly.metrics.prefix:wildfly}"/>
|
||||
<subsystem xmlns="urn:jboss:domain:modcluster:5.0">
|
||||
<proxy name="default" advertise-socket="modcluster" listener="ajp">
|
||||
<dynamic-load-provider>
|
||||
<load-metric type="cpu"/>
|
||||
</dynamic-load-provider>
|
||||
</proxy>
|
||||
</subsystem>
|
||||
<subsystem xmlns="urn:jboss:domain:naming:2.0">
|
||||
<remote-naming/>
|
||||
</subsystem>
|
||||
<subsystem xmlns="urn:jboss:domain:remoting:4.0">
|
||||
<http-connector name="http-remoting-connector" connector-ref="default" security-realm="ApplicationRealm"/>
|
||||
</subsystem>
|
||||
<subsystem xmlns="urn:jboss:domain:request-controller:1.0"/>
|
||||
<subsystem xmlns="urn:jboss:domain:security:2.0">
|
||||
<security-domains>
|
||||
<security-domain name="other" cache-type="default">
|
||||
<authentication>
|
||||
<login-module code="Remoting" flag="optional">
|
||||
<module-option name="password-stacking" value="useFirstPass"/>
|
||||
</login-module>
|
||||
<login-module code="RealmDirect" flag="required">
|
||||
<module-option name="password-stacking" value="useFirstPass"/>
|
||||
</login-module>
|
||||
</authentication>
|
||||
</security-domain>
|
||||
<security-domain name="jboss-web-policy" cache-type="default">
|
||||
<authorization>
|
||||
<policy-module code="Delegating" flag="required"/>
|
||||
</authorization>
|
||||
</security-domain>
|
||||
<security-domain name="jaspitest" cache-type="default">
|
||||
<authentication-jaspi>
|
||||
<login-module-stack name="dummy">
|
||||
<login-module code="Dummy" flag="optional"/>
|
||||
</login-module-stack>
|
||||
<auth-module code="Dummy"/>
|
||||
</authentication-jaspi>
|
||||
</security-domain>
|
||||
<security-domain name="jboss-ejb-policy" cache-type="default">
|
||||
<authorization>
|
||||
<policy-module code="Delegating" flag="required"/>
|
||||
</authorization>
|
||||
</security-domain>
|
||||
</security-domains>
|
||||
</subsystem>
|
||||
<subsystem xmlns="urn:jboss:domain:security-manager:1.0">
|
||||
<deployment-permissions>
|
||||
<maximum-set>
|
||||
<permission class="java.security.AllPermission"/>
|
||||
</maximum-set>
|
||||
</deployment-permissions>
|
||||
</subsystem>
|
||||
<subsystem xmlns="urn:jboss:domain:transactions:5.0">
|
||||
<core-environment node-identifier="${jboss.tx.node.id:1}">
|
||||
<process-id>
|
||||
<uuid/>
|
||||
</process-id>
|
||||
</core-environment>
|
||||
<recovery-environment socket-binding="txn-recovery-environment" status-socket-binding="txn-status-manager"/>
|
||||
<coordinator-environment statistics-enabled="${wildfly.transactions.statistics-enabled:${wildfly.statistics-enabled:false}}"/>
|
||||
<object-store path="tx-object-store" relative-to="jboss.server.data.dir"/>
|
||||
</subsystem>
|
||||
<subsystem xmlns="urn:jboss:domain:undertow:11.0" default-server="default-server" default-virtual-host="default-host" default-servlet-container="default" default-security-domain="other" statistics-enabled="${wildfly.undertow.statistics-enabled:${wildfly.statistics-enabled:false}}">
|
||||
<buffer-cache name="default"/>
|
||||
<server name="default-server">
|
||||
<ajp-listener name="ajp" socket-binding="ajp"/>
|
||||
<http-listener name="default" read-timeout="30000" socket-binding="http" redirect-socket="https" proxy-address-forwarding="${env.PROXY_ADDRESS_FORWARDING:false}" enable-http2="true"/>
|
||||
<https-listener name="https" read-timeout="30000" socket-binding="https" proxy-address-forwarding="${env.PROXY_ADDRESS_FORWARDING:false}" security-realm="ApplicationRealm" enable-http2="true"/>
|
||||
<host name="default-host" alias="localhost">
|
||||
<location name="/" handler="welcome-content"/>
|
||||
<http-invoker security-realm="ApplicationRealm"/>
|
||||
</host>
|
||||
</server>
|
||||
<servlet-container name="default">
|
||||
<jsp-config/>
|
||||
<websockets/>
|
||||
</servlet-container>
|
||||
<handlers>
|
||||
<file name="welcome-content" path="${jboss.home.dir}/welcome-content"/>
|
||||
</handlers>
|
||||
</subsystem>
|
||||
<subsystem xmlns="urn:jboss:domain:weld:4.0"/>
|
||||
</profile>
|
||||
<interfaces>
|
||||
<interface name="management">
|
||||
<inet-address value="${jboss.bind.address.management:127.0.0.1}"/>
|
||||
</interface>
|
||||
<interface name="private">
|
||||
<inet-address value="${jboss.bind.address.private:127.0.0.1}"/>
|
||||
</interface>
|
||||
<interface name="public">
|
||||
<inet-address value="${jboss.bind.address:127.0.0.1}"/>
|
||||
</interface>
|
||||
</interfaces>
|
||||
<socket-binding-group name="standard-sockets" default-interface="public" port-offset="${jboss.socket.binding.port-offset:0}">
|
||||
<socket-binding name="ajp" port="${jboss.ajp.port:8009}"/>
|
||||
<socket-binding name="http" port="${jboss.http.port:8080}"/>
|
||||
<socket-binding name="https" port="${jboss.https.port:8443}"/>
|
||||
<socket-binding name="jgroups-mping" interface="private" multicast-address="${jboss.default.multicast.address:230.0.0.4}" multicast-port="45700"/>
|
||||
<socket-binding name="jgroups-tcp" interface="private" port="7600"/>
|
||||
<socket-binding name="jgroups-tcp-fd" interface="private" port="57600"/>
|
||||
<socket-binding name="jgroups-udp" interface="private" port="55200" multicast-address="${jboss.default.multicast.address:230.0.0.4}" multicast-port="45688"/>
|
||||
<socket-binding name="jgroups-udp-fd" interface="private" port="54200"/>
|
||||
<socket-binding name="management-http" interface="management" port="${jboss.management.http.port:9990}"/>
|
||||
<socket-binding name="management-https" interface="management" port="${jboss.management.https.port:9993}"/>
|
||||
<socket-binding name="modcluster" multicast-address="${jboss.modcluster.multicast.address:224.0.1.105}" multicast-port="23364"/>
|
||||
<socket-binding name="txn-recovery-environment" port="4712"/>
|
||||
<socket-binding name="txn-status-manager" port="4713"/>
|
||||
<outbound-socket-binding name="mail-smtp">
|
||||
<remote-destination host="localhost" port="25"/>
|
||||
</outbound-socket-binding>
|
||||
</socket-binding-group>
|
||||
</server>
|
@ -1,113 +0,0 @@
|
||||
<script>const _=
|
||||
{
|
||||
"url": {
|
||||
"loginAction": "${url.loginAction}",
|
||||
"resourcesPath": "${url.resourcesPath}",
|
||||
"resourcesCommonPath": "${url.resourcesCommonPath}",
|
||||
"loginRestartFlowUrl": "${url.loginRestartFlowUrl}"
|
||||
},
|
||||
"realm": {
|
||||
"displayName": "${realm.displayName!''}" || undefined,
|
||||
"displayNameHtml": "${realm.displayNameHtml!''}" || undefined,
|
||||
"internationalizationEnabled": ${realm.internationalizationEnabled?c}
|
||||
},
|
||||
"locale": (function (){
|
||||
|
||||
<#if realm.internationalizationEnabled>
|
||||
|
||||
return {
|
||||
"supported": (function(){
|
||||
|
||||
<#if realm.internationalizationEnabled>
|
||||
|
||||
var out= [];
|
||||
|
||||
<#list locale.supported as lng>
|
||||
out.push({
|
||||
"url": "${lng.url}",
|
||||
"label": "${lng.label}",
|
||||
"languageTag": "${lng.languageTag}"
|
||||
});
|
||||
</#list>
|
||||
|
||||
return out;
|
||||
|
||||
</#if>
|
||||
|
||||
return undefined;
|
||||
|
||||
})(),
|
||||
"current": "${locale.current}"
|
||||
};
|
||||
|
||||
</#if>
|
||||
|
||||
return undefined;
|
||||
|
||||
})(),
|
||||
"auth": (function (){
|
||||
|
||||
|
||||
<#if auth?has_content>
|
||||
|
||||
var out= {
|
||||
"showUsername": ${auth.showUsername()?c},
|
||||
"showResetCredentials": ${auth.showResetCredentials()?c},
|
||||
"showTryAnotherWayLink": ${auth.showTryAnotherWayLink()?c}
|
||||
};
|
||||
|
||||
<#if auth.showUsername() && !auth.showResetCredentials()>
|
||||
Object.assign(
|
||||
out,
|
||||
{
|
||||
"attemptedUsername": "${auth.attemptedUsername}"
|
||||
}
|
||||
);
|
||||
</#if>
|
||||
|
||||
return out;
|
||||
|
||||
</#if>
|
||||
|
||||
|
||||
return undefined;
|
||||
|
||||
})(),
|
||||
"scripts": (function(){
|
||||
|
||||
var out = [];
|
||||
|
||||
<#if scripts??>
|
||||
<#list scripts as script>
|
||||
out.push("${script}");
|
||||
</#list>
|
||||
</#if>
|
||||
|
||||
return out;
|
||||
|
||||
})(),
|
||||
"message": (function (){
|
||||
|
||||
<#if message?has_content>
|
||||
|
||||
return {
|
||||
"type": "${message.type}",
|
||||
"summary": "${kcSanitize(message.summary)?no_esc}"
|
||||
};
|
||||
|
||||
</#if>
|
||||
|
||||
return undefined;
|
||||
|
||||
})(),
|
||||
"isAppInitiatedAction": (function (){
|
||||
|
||||
<#if isAppInitiatedAction??>
|
||||
return true;
|
||||
</#if>
|
||||
return false;
|
||||
|
||||
})()
|
||||
|
||||
}
|
||||
</script>
|
@ -1,127 +0,0 @@
|
||||
|
||||
|
||||
import cheerio from "cheerio";
|
||||
import {
|
||||
replaceImportFromStaticInJsCode,
|
||||
generateCssCodeToDefineGlobals
|
||||
} from "../replaceImportFromStatic";
|
||||
import fs from "fs";
|
||||
import { join as pathJoin } from "path";
|
||||
import { objectKeys } from "evt/tools/typeSafety/objectKeys";
|
||||
|
||||
export function generateFtlFilesCodeFactory(
|
||||
params: {
|
||||
ftlValuesGlobalName: string;
|
||||
cssGlobalsToDefine: Record<string, string>;
|
||||
indexHtmlCode: string;
|
||||
}
|
||||
) {
|
||||
|
||||
const { ftlValuesGlobalName, cssGlobalsToDefine, indexHtmlCode } = params;
|
||||
|
||||
const $ = cheerio.load(indexHtmlCode);
|
||||
|
||||
$("script:not([src])").each((...[, element]) => {
|
||||
|
||||
const { fixedJsCode } = replaceImportFromStaticInJsCode({
|
||||
ftlValuesGlobalName,
|
||||
"jsCode": $(element).html()!
|
||||
});
|
||||
|
||||
$(element).text(fixedJsCode);
|
||||
|
||||
});
|
||||
|
||||
|
||||
([
|
||||
["link", "href"],
|
||||
["script", "src"],
|
||||
] as const).forEach(([selector, attrName]) =>
|
||||
$(selector).each((...[, element]) => {
|
||||
|
||||
const href = $(element).attr(attrName);
|
||||
|
||||
if (!href?.startsWith("/")) {
|
||||
return;
|
||||
}
|
||||
|
||||
$(element).attr(attrName, "${url.resourcesPath}" + href);
|
||||
|
||||
})
|
||||
);
|
||||
|
||||
//FTL is no valid html, we can't insert with cheerio, we put placeholder for injecting later.
|
||||
const ftlPlaceholders = {
|
||||
'{ "x": "xIdLqMeOed9sdLdIdOxdK0d" }':
|
||||
fs.readFileSync(pathJoin(__dirname, "ftl2js.ftl"))
|
||||
.toString("utf8")
|
||||
.match(/^<script>const _=((?:.|\n)+)<\/script>[\n]?$/)![1],
|
||||
'<!-- xIdLqMeOedErIdLsPdNdI9dSlxI -->':
|
||||
[
|
||||
'<#if scripts??>',
|
||||
' <#list scripts as script>',
|
||||
' <script src="${script}" type="text/javascript"></script>',
|
||||
' </#list>',
|
||||
'</#if>',
|
||||
].join("\n")
|
||||
};
|
||||
|
||||
$("head").prepend(
|
||||
[
|
||||
...(Object.keys(cssGlobalsToDefine).length === 0 ? [] : [
|
||||
'',
|
||||
'<style>',
|
||||
generateCssCodeToDefineGlobals(
|
||||
{ cssGlobalsToDefine }
|
||||
).cssCodeToPrependInHead,
|
||||
'</style>',
|
||||
''
|
||||
]),
|
||||
'<script>',
|
||||
' Object.assign(',
|
||||
` window.${ftlValuesGlobalName},`,
|
||||
` ${objectKeys(ftlPlaceholders)[0]}`,
|
||||
' );',
|
||||
'</script>',
|
||||
'',
|
||||
objectKeys(ftlPlaceholders)[1],
|
||||
''
|
||||
].join("\n"),
|
||||
);
|
||||
|
||||
|
||||
const partiallyFixedIndexHtmlCode = $.html();
|
||||
|
||||
function generateFtlFilesCode(
|
||||
params: {
|
||||
pageBasename: "login.ftl" | "register.ftl"
|
||||
}
|
||||
): { ftlCode: string; } {
|
||||
|
||||
const { pageBasename } = params;
|
||||
|
||||
const $ = cheerio.load(partiallyFixedIndexHtmlCode);
|
||||
|
||||
$("head").prepend(
|
||||
[
|
||||
'',
|
||||
'<script>',
|
||||
` window.${ftlValuesGlobalName} = { "pageBasename": "${pageBasename}" };`,
|
||||
'</script>',
|
||||
''
|
||||
].join("\n")
|
||||
);
|
||||
|
||||
let ftlCode = $.html();
|
||||
|
||||
objectKeys(ftlPlaceholders)
|
||||
.forEach(id => ftlCode = ftlCode.replace(id, ftlPlaceholders[id]));
|
||||
|
||||
return { ftlCode };
|
||||
|
||||
}
|
||||
|
||||
return { generateFtlFilesCode };
|
||||
|
||||
|
||||
}
|
@ -1,100 +0,0 @@
|
||||
|
||||
import * as url from "url";
|
||||
import * as fs from "fs";
|
||||
import { join as pathJoin, dirname as pathDirname } from "path";
|
||||
|
||||
export type ParsedPackageJson = {
|
||||
name: string;
|
||||
version: string;
|
||||
homepage?: string;
|
||||
};
|
||||
|
||||
export function generateJavaStackFiles(
|
||||
params: {
|
||||
parsedPackageJson: ParsedPackageJson;
|
||||
keycloakThemeBuildingDirPath: string;
|
||||
}
|
||||
): { jarFilePath: string; } {
|
||||
|
||||
const {
|
||||
parsedPackageJson: { name, version, homepage },
|
||||
keycloakThemeBuildingDirPath
|
||||
} = params;
|
||||
|
||||
{
|
||||
|
||||
const { pomFileCode } = (function generatePomFileCode(): { pomFileCode: string; } {
|
||||
|
||||
|
||||
const groupId = (() => {
|
||||
|
||||
const fallbackGroupId = `there.was.no.homepage.field.in.the.package.json.${name}`;
|
||||
|
||||
return (!homepage ?
|
||||
fallbackGroupId :
|
||||
url.parse(homepage).host?.split(".").reverse().join(".") ?? fallbackGroupId
|
||||
) + ".keycloak";
|
||||
|
||||
})();
|
||||
|
||||
const artefactId = `${name}-keycloak-theme`;
|
||||
|
||||
const pomFileCode = [
|
||||
`<?xml version="1.0"?>`,
|
||||
`<project xmlns="http://maven.apache.org/POM/4.0.0"`,
|
||||
` xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"`,
|
||||
` xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">`,
|
||||
` <modelVersion>4.0.0</modelVersion>`,
|
||||
` <groupId>${groupId}</groupId>`,
|
||||
` <artifactId>${artefactId}</artifactId>`,
|
||||
` <version>${version}</version>`,
|
||||
` <name>${artefactId}</name>`,
|
||||
` <description />`,
|
||||
`</project>`
|
||||
].join("\n");
|
||||
|
||||
return { pomFileCode };
|
||||
|
||||
})();
|
||||
|
||||
fs.writeFileSync(
|
||||
pathJoin(keycloakThemeBuildingDirPath, "pom.xml"),
|
||||
Buffer.from(pomFileCode, "utf8")
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
{
|
||||
|
||||
const themeManifestFilePath = pathJoin(
|
||||
keycloakThemeBuildingDirPath, "src", "main",
|
||||
"resources", "META-INF", "keycloak-themes.json"
|
||||
);
|
||||
|
||||
try {
|
||||
|
||||
fs.mkdirSync(pathDirname(themeManifestFilePath));
|
||||
|
||||
} catch { }
|
||||
|
||||
fs.writeFileSync(
|
||||
themeManifestFilePath,
|
||||
Buffer.from(
|
||||
JSON.stringify({
|
||||
"themes": [
|
||||
{
|
||||
"name": name,
|
||||
"types": ["login"]
|
||||
}
|
||||
]
|
||||
}, null, 2),
|
||||
"utf8"
|
||||
)
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
return { "jarFilePath": pathJoin(keycloakThemeBuildingDirPath, "target", `${name}-${version}.jar`) };
|
||||
|
||||
}
|
||||
|
@ -1,88 +0,0 @@
|
||||
|
||||
import { transformCodebase } from "../tools/transformCodebase";
|
||||
import * as fs from "fs";
|
||||
import { join as pathJoin } from "path";
|
||||
import {
|
||||
replaceImportFromStaticInCssCode,
|
||||
replaceImportFromStaticInJsCode
|
||||
} from "./replaceImportFromStatic";
|
||||
import { generateFtlFilesCodeFactory } from "./generateFtl";
|
||||
|
||||
export const ftlValuesGlobalName = "keycloakPagesContext";
|
||||
|
||||
export function generateKeycloakThemeResources(
|
||||
params: {
|
||||
themeName: string;
|
||||
reactAppBuildDirPath: string;
|
||||
keycloakThemeBuildingDirPath: string;
|
||||
}
|
||||
) {
|
||||
|
||||
const { themeName, reactAppBuildDirPath, keycloakThemeBuildingDirPath } = params;
|
||||
|
||||
const themeDirPath = pathJoin(keycloakThemeBuildingDirPath, "src", "main", "resources", "theme", themeName, "login");
|
||||
|
||||
let allCssGlobalsToDefine: Record<string, string> = {};
|
||||
|
||||
transformCodebase({
|
||||
"destDirPath": pathJoin(themeDirPath, "resources"),
|
||||
"srcDirPath": reactAppBuildDirPath,
|
||||
"transformSourceCodeString": ({ filePath, sourceCode }) => {
|
||||
|
||||
if (/\.css?$/i.test(filePath)) {
|
||||
|
||||
const { cssGlobalsToDefine, fixedCssCode } = replaceImportFromStaticInCssCode(
|
||||
{ "cssCode": sourceCode.toString("utf8") }
|
||||
);
|
||||
|
||||
allCssGlobalsToDefine = {
|
||||
...allCssGlobalsToDefine,
|
||||
...cssGlobalsToDefine
|
||||
};
|
||||
|
||||
return { "modifiedSourceCode": Buffer.from(fixedCssCode, "utf8") };
|
||||
|
||||
}
|
||||
|
||||
if (/\.js?$/i.test(filePath)) {
|
||||
|
||||
const { fixedJsCode } = replaceImportFromStaticInJsCode({
|
||||
"jsCode": sourceCode.toString("utf8"),
|
||||
ftlValuesGlobalName
|
||||
});
|
||||
|
||||
return { "modifiedSourceCode": Buffer.from(fixedJsCode, "utf8") };
|
||||
|
||||
}
|
||||
|
||||
return { "modifiedSourceCode": sourceCode };
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
const { generateFtlFilesCode } = generateFtlFilesCodeFactory({
|
||||
"cssGlobalsToDefine": allCssGlobalsToDefine,
|
||||
ftlValuesGlobalName,
|
||||
"indexHtmlCode": fs.readFileSync(
|
||||
pathJoin(reactAppBuildDirPath, "index.html")
|
||||
).toString("utf8")
|
||||
});
|
||||
|
||||
(["login.ftl", "register.ftl"] as const).forEach(pageBasename => {
|
||||
|
||||
const { ftlCode } = generateFtlFilesCode({ pageBasename });
|
||||
|
||||
fs.writeFileSync(
|
||||
pathJoin(themeDirPath, pageBasename),
|
||||
Buffer.from(ftlCode, "utf8")
|
||||
)
|
||||
|
||||
});
|
||||
|
||||
fs.writeFileSync(
|
||||
pathJoin(themeDirPath, "theme.properties"),
|
||||
Buffer.from("parent=base\n", "utf8")
|
||||
);
|
||||
|
||||
}
|
||||
|
@ -1,81 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import { generateKeycloakThemeResources } from "./generateKeycloakThemeResources";
|
||||
import { generateJavaStackFiles } from "./generateJavaStackFiles";
|
||||
import type { ParsedPackageJson } from "./generateJavaStackFiles";
|
||||
import { join as pathJoin, relative as pathRelative, basename as pathBasename } from "path";
|
||||
import * as child_process from "child_process";
|
||||
import { generateDebugFiles, containerLaunchScriptBasename } from "./generateDebugFiles";
|
||||
|
||||
|
||||
const reactProjectDirPath = process.cwd();
|
||||
|
||||
const parsedPackageJson: ParsedPackageJson = require(pathJoin(reactProjectDirPath, "package.json"));
|
||||
|
||||
export const keycloakThemeBuildingDirPath = pathJoin(reactProjectDirPath, "build_keycloak");
|
||||
|
||||
|
||||
if (require.main === module) {
|
||||
|
||||
console.log("🔏 Building the keycloak theme...⌚");
|
||||
|
||||
generateKeycloakThemeResources({
|
||||
keycloakThemeBuildingDirPath,
|
||||
"reactAppBuildDirPath": pathJoin(reactProjectDirPath, "build"),
|
||||
"themeName": parsedPackageJson.name
|
||||
});
|
||||
|
||||
const { jarFilePath } = generateJavaStackFiles({
|
||||
parsedPackageJson,
|
||||
keycloakThemeBuildingDirPath
|
||||
});
|
||||
|
||||
child_process.execSync(
|
||||
"mvn package",
|
||||
{ "cwd": keycloakThemeBuildingDirPath }
|
||||
);
|
||||
|
||||
generateDebugFiles({
|
||||
keycloakThemeBuildingDirPath,
|
||||
"packageJsonName": parsedPackageJson.name
|
||||
});
|
||||
|
||||
console.log([
|
||||
'',
|
||||
`✅ Your keycloak theme has been generated and bundled into ./${pathRelative(reactProjectDirPath, jarFilePath)} 🚀`,
|
||||
`It is to be placed in "/opt/jboss/keycloak/standalone/deployments" in the container running a jboss/keycloak Docker image. (Tested with 11.0.3)`,
|
||||
'',
|
||||
'Using Helm (https://github.com/codecentric/helm-charts), edit to reflect:',
|
||||
'',
|
||||
'value.yaml: ',
|
||||
' extraInitContainers: |',
|
||||
' - name: realm-ext-provider',
|
||||
' image: curlimages/curl',
|
||||
' imagePullPolicy: IfNotPresent',
|
||||
' command:',
|
||||
' - sh',
|
||||
' args:',
|
||||
' - -c',
|
||||
` - curl -L -f -S -o /extensions/${pathBasename(jarFilePath)} https://AN.URL.FOR/${pathBasename(jarFilePath)}`,
|
||||
' volumeMounts:',
|
||||
' - name: extensions',
|
||||
' mountPath: /extensions',
|
||||
' ',
|
||||
' extraVolumeMounts: |',
|
||||
' - name: extensions',
|
||||
' mountPath: /opt/jboss/keycloak/standalone/deployments',
|
||||
'',
|
||||
'',
|
||||
'To test your theme locally, with hot reloading, you can spin up a Keycloak container image with the theme loaded by running:',
|
||||
'',
|
||||
`👉 $ ./${pathRelative(reactProjectDirPath, pathJoin(keycloakThemeBuildingDirPath, containerLaunchScriptBasename))} 👈`,
|
||||
'',
|
||||
'To enable the theme within keycloak log into the admin console ( 👉 http://localhost:8080 username: admin, password: admin 👈), create a realm (called "myrealm" for example),',
|
||||
`go to your realm settings, click on the theme tab then select ${parsedPackageJson.name}.`,
|
||||
`More details: https://www.keycloak.org/getting-started/getting-started-docker`,
|
||||
'',
|
||||
'Once your container is up and configured 👉 http://localhost:8080/auth/realms/myrealm/account 👈',
|
||||
'',
|
||||
].join("\n"));
|
||||
|
||||
}
|
@ -1,91 +0,0 @@
|
||||
|
||||
import * as crypto from "crypto";
|
||||
|
||||
export function replaceImportFromStaticInJsCode(
|
||||
params: {
|
||||
ftlValuesGlobalName: string;
|
||||
jsCode: string;
|
||||
}
|
||||
): { fixedJsCode: string; } {
|
||||
|
||||
const { jsCode, ftlValuesGlobalName } = params;
|
||||
|
||||
const fixedJsCode = jsCode!.replace(
|
||||
/"static\//g,
|
||||
`window.${ftlValuesGlobalName}.url.resourcesPath.replace(/^\\//,"") + "/" + "static/`
|
||||
);
|
||||
|
||||
return { fixedJsCode };
|
||||
|
||||
}
|
||||
|
||||
export function replaceImportFromStaticInCssCode(
|
||||
params: {
|
||||
cssCode: string;
|
||||
}
|
||||
): {
|
||||
fixedCssCode: string;
|
||||
cssGlobalsToDefine: Record<string, string>;
|
||||
} {
|
||||
|
||||
const { cssCode } = params;
|
||||
|
||||
const cssGlobalsToDefine: Record<string, string> = {};
|
||||
|
||||
new Set(cssCode.match(/(url\(\/[^)]+\))/g) ?? [])
|
||||
.forEach(match =>
|
||||
cssGlobalsToDefine[
|
||||
"url" + crypto
|
||||
.createHash("sha256")
|
||||
.update(match)
|
||||
.digest("hex")
|
||||
.substring(0, 15)
|
||||
] = match
|
||||
);
|
||||
|
||||
let fixedCssCode = cssCode;
|
||||
|
||||
Object.keys(cssGlobalsToDefine).forEach(
|
||||
cssVariableName =>
|
||||
//NOTE: split/join pattern ~ replace all
|
||||
fixedCssCode =
|
||||
fixedCssCode.split(cssGlobalsToDefine[cssVariableName])
|
||||
.join(`var(--${cssVariableName})`)
|
||||
);
|
||||
|
||||
return { fixedCssCode, cssGlobalsToDefine };
|
||||
|
||||
}
|
||||
|
||||
export function generateCssCodeToDefineGlobals(
|
||||
params: {
|
||||
cssGlobalsToDefine: Record<string, string>;
|
||||
}
|
||||
): {
|
||||
cssCodeToPrependInHead: string;
|
||||
} {
|
||||
|
||||
const { cssGlobalsToDefine } = params;
|
||||
|
||||
return {
|
||||
"cssCodeToPrependInHead": [
|
||||
":root {",
|
||||
...Object.keys(cssGlobalsToDefine)
|
||||
.map(cssVariableName => [
|
||||
`--${cssVariableName}:`,
|
||||
[
|
||||
"url(",
|
||||
"${url.resourcesPath}" +
|
||||
cssGlobalsToDefine[cssVariableName].match(/^url\(([^)]+)\)$/)![1],
|
||||
")"
|
||||
].join("")
|
||||
].join(" "))
|
||||
.map(line => ` ${line};`),
|
||||
"}"
|
||||
].join("\n")
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
36
src/bin/create-keycloak-email-directory.ts
Normal file
36
src/bin/create-keycloak-email-directory.ts
Normal file
@ -0,0 +1,36 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import { downloadBuiltinKeycloakTheme } from "./download-builtin-keycloak-theme";
|
||||
import { keycloakThemeEmailDirPath } from "./keycloakify";
|
||||
import { join as pathJoin, basename as pathBasename } from "path";
|
||||
import { transformCodebase } from "./tools/transformCodebase";
|
||||
import { promptKeycloakVersion } from "./promptKeycloakVersion";
|
||||
import * as fs from "fs";
|
||||
|
||||
if (require.main === module) {
|
||||
(async () => {
|
||||
if (fs.existsSync(keycloakThemeEmailDirPath)) {
|
||||
console.log(`There is already a ./${pathBasename(keycloakThemeEmailDirPath)} directory in your project. Aborting.`);
|
||||
|
||||
process.exit(-1);
|
||||
}
|
||||
|
||||
const { keycloakVersion } = await promptKeycloakVersion();
|
||||
|
||||
const builtinKeycloakThemeTmpDirPath = pathJoin(keycloakThemeEmailDirPath, "..", "tmp_xIdP3_builtin_keycloak_theme");
|
||||
|
||||
downloadBuiltinKeycloakTheme({
|
||||
keycloakVersion,
|
||||
"destDirPath": builtinKeycloakThemeTmpDirPath
|
||||
});
|
||||
|
||||
transformCodebase({
|
||||
"srcDirPath": pathJoin(builtinKeycloakThemeTmpDirPath, "base", "email"),
|
||||
"destDirPath": keycloakThemeEmailDirPath
|
||||
});
|
||||
|
||||
console.log(`./${pathBasename(keycloakThemeEmailDirPath)} ready to be customized`);
|
||||
|
||||
fs.rmSync(builtinKeycloakThemeTmpDirPath, { "recursive": true, "force": true });
|
||||
})();
|
||||
}
|
34
src/bin/download-builtin-keycloak-theme.ts
Normal file
34
src/bin/download-builtin-keycloak-theme.ts
Normal file
@ -0,0 +1,34 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import { keycloakThemeBuildingDirPath } from "./keycloakify";
|
||||
import { join as pathJoin } from "path";
|
||||
import { downloadAndUnzip } from "./tools/downloadAndUnzip";
|
||||
import { promptKeycloakVersion } from "./promptKeycloakVersion";
|
||||
|
||||
export function downloadBuiltinKeycloakTheme(params: { keycloakVersion: string; destDirPath: string }) {
|
||||
const { keycloakVersion, destDirPath } = params;
|
||||
|
||||
for (const ext of ["", "-community"]) {
|
||||
downloadAndUnzip({
|
||||
"destDirPath": destDirPath,
|
||||
"url": `https://github.com/keycloak/keycloak/archive/refs/tags/${keycloakVersion}.zip`,
|
||||
"pathOfDirToExtractInArchive": `keycloak-${keycloakVersion}/themes/src/main/resources${ext}/theme`,
|
||||
"cacheDirPath": pathJoin(keycloakThemeBuildingDirPath, ".cache")
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (require.main === module) {
|
||||
(async () => {
|
||||
const { keycloakVersion } = await promptKeycloakVersion();
|
||||
|
||||
const destDirPath = pathJoin(keycloakThemeBuildingDirPath, "src", "main", "resources", "theme");
|
||||
|
||||
console.log(`Downloading builtins theme of Keycloak ${keycloakVersion} here ${destDirPath}`);
|
||||
|
||||
downloadBuiltinKeycloakTheme({
|
||||
keycloakVersion,
|
||||
destDirPath
|
||||
});
|
||||
})();
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import { keycloakThemeBuildingDirPath } from "./build-keycloak-theme";
|
||||
import { downloadAndUnzip } from "./tools/downloadAndUnzip";
|
||||
import { join as pathJoin } from "path";
|
||||
|
||||
export const keycloakBuiltinThemesAndThirdPartyExamplesThemsUrl =
|
||||
"https://github.com/garronej/keycloak-react-theming/releases/download/v0.0.1/other_keycloak_thems.zip";
|
||||
|
||||
if (require.main === module) {
|
||||
|
||||
downloadAndUnzip({
|
||||
"url": keycloakBuiltinThemesAndThirdPartyExamplesThemsUrl,
|
||||
"destDirPath": pathJoin(keycloakThemeBuildingDirPath, "src", "main", "resources", "theme")
|
||||
});
|
||||
|
||||
}
|
||||
|
@ -1,71 +1,81 @@
|
||||
import "minimal-polyfills/Object.fromEntries";
|
||||
import * as fs from "fs";
|
||||
import { join as pathJoin, relative as pathRelative } from "path";
|
||||
import { join as pathJoin, relative as pathRelative, dirname as pathDirname } from "path";
|
||||
import { crawl } from "./tools/crawl";
|
||||
import { downloadAndUnzip } from "./tools/downloadAndUnzip";
|
||||
import { keycloakBuiltinThemesAndThirdPartyExamplesThemsUrl } from "./download-sample-keycloak-themes";
|
||||
import { downloadBuiltinKeycloakTheme } from "./download-builtin-keycloak-theme";
|
||||
import { getProjectRoot } from "./tools/getProjectRoot";
|
||||
import * as child_process from "child_process";
|
||||
import { rm_rf, rm_r } from "./tools/rm";
|
||||
|
||||
//NOTE: To run without argument when we want to generate src/i18n/generated_kcMessages files,
|
||||
// update the version array for generating for newer version.
|
||||
|
||||
//@ts-ignore
|
||||
const propertiesParser = require("properties-parser");
|
||||
|
||||
const tmpDirPath = pathJoin(getProjectRoot(), "tmp_xImOef9dOd44");
|
||||
for (const keycloakVersion of ["11.0.3", "15.0.2", "18.0.1"]) {
|
||||
console.log({ keycloakVersion });
|
||||
|
||||
child_process.execSync(`rm -rf ${tmpDirPath}`);
|
||||
const tmpDirPath = pathJoin(getProjectRoot(), "tmp_xImOef9dOd44");
|
||||
|
||||
downloadAndUnzip({
|
||||
"destDirPath": tmpDirPath,
|
||||
"url": keycloakBuiltinThemesAndThirdPartyExamplesThemsUrl
|
||||
});
|
||||
rm_rf(tmpDirPath);
|
||||
|
||||
type Dictionary = { [idiomId: string]: string };
|
||||
downloadBuiltinKeycloakTheme({
|
||||
keycloakVersion,
|
||||
"destDirPath": tmpDirPath
|
||||
});
|
||||
|
||||
const record: { [typeOfPage: string]: { [language: string]: Dictionary } } = {};
|
||||
type Dictionary = { [idiomId: string]: string };
|
||||
|
||||
process.chdir(pathJoin(tmpDirPath, "base"));
|
||||
const record: { [typeOfPage: string]: { [language: string]: Dictionary } } = {};
|
||||
|
||||
crawl(".").forEach(filePath => {
|
||||
{
|
||||
const baseThemeDirPath = pathJoin(tmpDirPath, "base");
|
||||
|
||||
const match = filePath.match(/^([^/]+)\/messages\/messages_([^.]+)\.properties$/);
|
||||
crawl(baseThemeDirPath).forEach(filePath => {
|
||||
const match = filePath.match(/^([^/]+)\/messages\/messages_([^.]+)\.properties$/);
|
||||
|
||||
if (match === null) {
|
||||
return;
|
||||
if (match === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
const [, typeOfPage, language] = match;
|
||||
|
||||
(record[typeOfPage] ??= {})[language.replace(/_/g, "-")] = Object.fromEntries(
|
||||
Object.entries(propertiesParser.parse(fs.readFileSync(pathJoin(baseThemeDirPath, filePath)).toString("utf8"))).map(
|
||||
([key, value]: any) => [key, value.replace(/''/g, "'")]
|
||||
)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
const [, typeOfPage, language] = match;
|
||||
rm_r(tmpDirPath);
|
||||
|
||||
(record[typeOfPage] ??= {})[language] =
|
||||
propertiesParser.parse(
|
||||
fs.readFileSync(filePath)
|
||||
.toString("utf8")
|
||||
);
|
||||
Object.keys(record).forEach(pageType => {
|
||||
const recordForPageType = record[pageType];
|
||||
|
||||
});
|
||||
Object.keys(recordForPageType).forEach(language => {
|
||||
const filePath = pathJoin(getProjectRoot(), "src", "lib", "i18n", "generated_messages", keycloakVersion, pageType, `${language}.ts`);
|
||||
|
||||
child_process.execSync(`rm -r ${tmpDirPath}`);
|
||||
fs.mkdirSync(pathDirname(filePath), { "recursive": true });
|
||||
|
||||
const targetDirPath = pathJoin(getProjectRoot(), "src", "lib", "i18n", "generated_messages");
|
||||
fs.writeFileSync(
|
||||
filePath,
|
||||
Buffer.from(
|
||||
[
|
||||
`//This code was automatically generated by running ${pathRelative(getProjectRoot(), __filename)}`,
|
||||
"//PLEASE DO NOT EDIT MANUALLY",
|
||||
"",
|
||||
"/* spell-checker: disable */",
|
||||
`const messages= ${JSON.stringify(recordForPageType[language], null, 2)};`,
|
||||
"",
|
||||
"export default messages;",
|
||||
"/* spell-checker: enable */"
|
||||
].join("\n"),
|
||||
"utf8"
|
||||
)
|
||||
);
|
||||
|
||||
fs.mkdirSync(targetDirPath, { "recursive": true });
|
||||
|
||||
Object.keys(record).forEach(pageType => {
|
||||
|
||||
const filePath = pathJoin(targetDirPath, `${pageType}.ts`);
|
||||
|
||||
fs.writeFileSync(
|
||||
filePath,
|
||||
Buffer.from(
|
||||
[
|
||||
`//This code was automatically generated by running ${pathRelative(getProjectRoot(), __filename)}`,
|
||||
'//PLEASE DO NOT EDIT MANUALLY',
|
||||
'',
|
||||
'/* spell-checker: disable */',
|
||||
`export const messages= ${JSON.stringify(record[pageType], null, 2)} as const;`,
|
||||
'/* spell-checker: enable */'
|
||||
].join("\n"), "utf8")
|
||||
);
|
||||
|
||||
console.log(`${filePath} wrote`);
|
||||
|
||||
|
||||
});
|
||||
console.log(`${filePath} wrote`);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
177
src/bin/keycloakify/BuildOptions.ts
Normal file
177
src/bin/keycloakify/BuildOptions.ts
Normal file
@ -0,0 +1,177 @@
|
||||
import { z } from "zod";
|
||||
import { assert } from "tsafe/assert";
|
||||
import type { Equals } from "tsafe";
|
||||
import { id } from "tsafe/id";
|
||||
import { parse as urlParse } from "url";
|
||||
|
||||
type ParsedPackageJson = {
|
||||
name: string;
|
||||
version: string;
|
||||
homepage?: string;
|
||||
keycloakify?: {
|
||||
extraPages?: string[];
|
||||
extraThemeProperties?: string[];
|
||||
isAppAndKeycloakServerSharingSameDomain?: boolean;
|
||||
};
|
||||
};
|
||||
|
||||
const zParsedPackageJson = z.object({
|
||||
"name": z.string(),
|
||||
"version": z.string(),
|
||||
"homepage": z.string().optional(),
|
||||
"keycloakify": z
|
||||
.object({
|
||||
"extraPages": z.array(z.string()).optional(),
|
||||
"extraThemeProperties": z.array(z.string()).optional(),
|
||||
"isAppAndKeycloakServerSharingSameDomain": z.boolean().optional()
|
||||
})
|
||||
.optional()
|
||||
});
|
||||
|
||||
assert<Equals<ReturnType<typeof zParsedPackageJson["parse"]>, ParsedPackageJson>>();
|
||||
|
||||
/** Consolidated build option gathered form CLI arguments and config in package.json */
|
||||
export type BuildOptions = BuildOptions.Standalone | BuildOptions.ExternalAssets;
|
||||
|
||||
export namespace BuildOptions {
|
||||
export type Common = {
|
||||
version: string;
|
||||
themeName: string;
|
||||
extraPages?: string[];
|
||||
extraThemeProperties?: string[];
|
||||
//NOTE: Only for the pom.xml file, questionable utility...
|
||||
groupId: string;
|
||||
};
|
||||
|
||||
export type Standalone = Common & {
|
||||
isStandalone: true;
|
||||
urlPathname: string | undefined;
|
||||
};
|
||||
|
||||
export type ExternalAssets = ExternalAssets.SameDomain | ExternalAssets.DifferentDomains;
|
||||
|
||||
export namespace ExternalAssets {
|
||||
export type CommonExternalAssets = Common & {
|
||||
isStandalone: false;
|
||||
};
|
||||
|
||||
export type SameDomain = CommonExternalAssets & {
|
||||
isAppAndKeycloakServerSharingSameDomain: true;
|
||||
};
|
||||
|
||||
export type DifferentDomains = CommonExternalAssets & {
|
||||
isAppAndKeycloakServerSharingSameDomain: false;
|
||||
urlOrigin: string;
|
||||
urlPathname: string | undefined;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export function readBuildOptions(params: {
|
||||
packageJson: string;
|
||||
CNAME: string | undefined;
|
||||
isExternalAssetsCliParamProvided: boolean;
|
||||
}): BuildOptions {
|
||||
const { packageJson, CNAME, isExternalAssetsCliParamProvided } = params;
|
||||
|
||||
const parsedPackageJson = zParsedPackageJson.parse(JSON.parse(packageJson));
|
||||
|
||||
const url = (() => {
|
||||
const { homepage } = parsedPackageJson;
|
||||
|
||||
let url: URL | undefined = undefined;
|
||||
|
||||
if (homepage !== undefined) {
|
||||
url = new URL(homepage);
|
||||
}
|
||||
|
||||
if (CNAME !== undefined) {
|
||||
url = new URL(`https://${CNAME.replace(/\s+$/, "")}`);
|
||||
}
|
||||
|
||||
if (url === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return {
|
||||
"origin": url.origin,
|
||||
"pathname": (() => {
|
||||
const out = url.pathname.replace(/([^/])$/, "$1/");
|
||||
|
||||
return out === "/" ? undefined : out;
|
||||
})()
|
||||
};
|
||||
})();
|
||||
|
||||
const common: BuildOptions.Common = (() => {
|
||||
const { name, keycloakify = {}, version, homepage } = parsedPackageJson;
|
||||
|
||||
const { extraPages, extraThemeProperties } = keycloakify ?? {};
|
||||
|
||||
const themeName = name
|
||||
.replace(/^@(.*)/, "$1")
|
||||
.split("/")
|
||||
.join("-");
|
||||
|
||||
return {
|
||||
themeName,
|
||||
"groupId": (() => {
|
||||
const fallbackGroupId = `${themeName}.keycloak`;
|
||||
|
||||
return (
|
||||
(!homepage
|
||||
? fallbackGroupId
|
||||
: urlParse(homepage)
|
||||
.host?.replace(/:[0-9]+$/, "")
|
||||
?.split(".")
|
||||
.reverse()
|
||||
.join(".") ?? fallbackGroupId) + ".keycloak"
|
||||
);
|
||||
})(),
|
||||
"version": version,
|
||||
extraPages,
|
||||
extraThemeProperties
|
||||
};
|
||||
})();
|
||||
|
||||
if (isExternalAssetsCliParamProvided) {
|
||||
const commonExternalAssets = id<BuildOptions.ExternalAssets.CommonExternalAssets>({
|
||||
...common,
|
||||
"isStandalone": false
|
||||
});
|
||||
|
||||
if (parsedPackageJson.keycloakify?.isAppAndKeycloakServerSharingSameDomain) {
|
||||
return id<BuildOptions.ExternalAssets.SameDomain>({
|
||||
...commonExternalAssets,
|
||||
"isAppAndKeycloakServerSharingSameDomain": true
|
||||
});
|
||||
} else {
|
||||
assert(
|
||||
url !== undefined,
|
||||
[
|
||||
"Can't compile in external assets mode if we don't know where",
|
||||
"the app will be hosted.",
|
||||
"You should provide a homepage field in the package.json (or create a",
|
||||
"public/CNAME file.",
|
||||
"Alternatively, if your app and the Keycloak server are on the same domain, ",
|
||||
"eg https://example.com is your app and https://example.com/auth is the keycloak",
|
||||
'admin UI, you can set "keycloakify": { "isAppAndKeycloakServerSharingSameDomain": true }',
|
||||
"in your package.json"
|
||||
].join(" ")
|
||||
);
|
||||
|
||||
return id<BuildOptions.ExternalAssets.DifferentDomains>({
|
||||
...commonExternalAssets,
|
||||
"isAppAndKeycloakServerSharingSameDomain": false,
|
||||
"urlOrigin": url.origin,
|
||||
"urlPathname": url.pathname
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return id<BuildOptions.Standalone>({
|
||||
...common,
|
||||
"isStandalone": true,
|
||||
"urlPathname": url?.pathname
|
||||
});
|
||||
}
|
1
src/bin/keycloakify/ftlValuesGlobalName.ts
Normal file
1
src/bin/keycloakify/ftlValuesGlobalName.ts
Normal file
@ -0,0 +1 @@
|
||||
export const ftlValuesGlobalName = "kcContext";
|
@ -0,0 +1,341 @@
|
||||
<script>const _=
|
||||
<#assign pageId="PAGE_ID_xIgLsPgGId9D8e">
|
||||
(()=>{
|
||||
|
||||
const out =
|
||||
${ftl_object_to_js_code_declaring_an_object(.data_model, [])?no_esc};
|
||||
|
||||
out["msg"]= function(){ throw new Error("use import { useKcMessage } from 'keycloakify'"); };
|
||||
out["advancedMsg"]= function(){ throw new Error("use import { useKcMessage } from 'keycloakify'"); };
|
||||
|
||||
out["messagesPerField"]= {
|
||||
<#assign fieldNames = [
|
||||
"global", "userLabel", "username", "email", "firstName", "lastName", "password", "password-confirm",
|
||||
"totp", "totpSecret", "SAMLRequest", "SAMLResponse", "relayState", "device_user_code", "code",
|
||||
"password-new", "rememberMe", "login", "authenticationExecution", "cancel-aia", "clientDataJSON",
|
||||
"authenticatorData", "signature", "credentialId", "userHandle", "error", "authn_use_chk", "authenticationExecution",
|
||||
"isSetRetry", "try-again", "attestationObject", "publicKeyCredentialId", "authenticatorLabel"
|
||||
]>
|
||||
|
||||
<#attempt>
|
||||
<#if profile?? && profile.attributes?? && profile.attributes?is_enumerable>
|
||||
<#list profile.attributes as attribute>
|
||||
<#if fieldNames?seq_contains(attribute.name)>
|
||||
<#continue>
|
||||
</#if>
|
||||
<#assign fieldNames += [attribute.name]>
|
||||
</#list>
|
||||
</#if>
|
||||
<#recover>
|
||||
</#attempt>
|
||||
|
||||
"printIfExists": function (fieldName, x) {
|
||||
<#if !messagesPerField?? >
|
||||
return undefined;
|
||||
</#if>
|
||||
<#list fieldNames as fieldName>
|
||||
if(fieldName === "${fieldName}" ){
|
||||
<#attempt>
|
||||
return "${messagesPerField.printIfExists(fieldName,'1')}" ? x : undefined;
|
||||
<#recover>
|
||||
</#attempt>
|
||||
}
|
||||
</#list>
|
||||
throw new Error("There is no " + fieldName + " field");
|
||||
},
|
||||
"existsError": function (fieldName) {
|
||||
<#if !messagesPerField?? >
|
||||
return false;
|
||||
</#if>
|
||||
<#list fieldNames as fieldName>
|
||||
if(fieldName === "${fieldName}" ){
|
||||
<#attempt>
|
||||
return <#if messagesPerField.existsError('${fieldName}')>true<#else>false</#if>;
|
||||
<#recover>
|
||||
</#attempt>
|
||||
}
|
||||
</#list>
|
||||
throw new Error("There is no " + fieldName + " field");
|
||||
},
|
||||
"get": function (fieldName) {
|
||||
<#if !messagesPerField?? >
|
||||
return '';
|
||||
</#if>
|
||||
<#list fieldNames as fieldName>
|
||||
if(fieldName === "${fieldName}" ){
|
||||
<#attempt>
|
||||
<#if messagesPerField.existsError('${fieldName}')>
|
||||
return "${messagesPerField.get('${fieldName}')?no_esc}";
|
||||
</#if>
|
||||
<#recover>
|
||||
</#attempt>
|
||||
}
|
||||
</#list>
|
||||
throw new Error("There is no " + fieldName + " field");
|
||||
},
|
||||
"exists": function (fieldName) {
|
||||
<#if !messagesPerField?? >
|
||||
return false;
|
||||
</#if>
|
||||
<#list fieldNames as fieldName>
|
||||
if(fieldName === "${fieldName}" ){
|
||||
<#attempt>
|
||||
return <#if messagesPerField.exists('${fieldName}')>true<#else>false</#if>;
|
||||
<#recover>
|
||||
</#attempt>
|
||||
}
|
||||
</#list>
|
||||
throw new Error("There is no " + fieldName + " field");
|
||||
}
|
||||
};
|
||||
|
||||
out["pageId"] = "${pageId}";
|
||||
|
||||
return out;
|
||||
|
||||
})()
|
||||
<#function ftl_object_to_js_code_declaring_an_object object path>
|
||||
|
||||
<#local isHash = "">
|
||||
<#attempt>
|
||||
<#local isHash = object?is_hash || object?is_hash_ex>
|
||||
<#recover>
|
||||
<#return "ABORT: Can't evaluate if " + path?join(".") + " is hash">
|
||||
</#attempt>
|
||||
|
||||
<#if isHash>
|
||||
|
||||
<#if path?size gt 10>
|
||||
<#return "ABORT: Too many recursive calls">
|
||||
</#if>
|
||||
|
||||
<#local keys = "">
|
||||
|
||||
<#attempt>
|
||||
<#local keys = object?keys>
|
||||
<#recover>
|
||||
<#return "ABORT: We can't list keys on this object">
|
||||
</#attempt>
|
||||
|
||||
|
||||
<#local out_seq = []>
|
||||
|
||||
<#list keys as key>
|
||||
|
||||
<#if ["class","declaredConstructors","superclass","declaringClass" ]?seq_contains(key) >
|
||||
<#continue>
|
||||
</#if>
|
||||
|
||||
<#if
|
||||
(
|
||||
["loginUpdatePasswordUrl", "loginUpdateProfileUrl", "loginUsernameReminderUrl", "loginUpdateTotpUrl"]?seq_contains(key) &&
|
||||
are_same_path(path, ["url"])
|
||||
) || (
|
||||
key == "updateProfileCtx" &&
|
||||
are_same_path(path, [])
|
||||
) || (
|
||||
<#-- https://github.com/InseeFrLab/keycloakify/pull/65#issuecomment-991896344 (reports with saml-post-form.ftl) -->
|
||||
<#-- https://github.com/InseeFrLab/keycloakify/issues/91#issue-1212319466 (reports with error.ftl and Kc18) -->
|
||||
<#-- https://github.com/InseeFrLab/keycloakify/issues/109#issuecomment-1134610163 -->
|
||||
key == "loginAction" &&
|
||||
are_same_path(path, ["url"]) &&
|
||||
["saml-post-form.ftl", "error.ftl", "info.ftl"]?seq_contains(pageId) &&
|
||||
!(auth?has_content && auth.showTryAnotherWayLink())
|
||||
) || (
|
||||
["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"])
|
||||
)
|
||||
>
|
||||
<#local out_seq += ["/*If you need '" + key + "' on " + pageId + ", please submit an issue to the Keycloakify repo*/"]>
|
||||
<#continue>
|
||||
</#if>
|
||||
|
||||
<#if 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())>
|
||||
<#continue>
|
||||
</#if>
|
||||
<#recover>
|
||||
</#attempt>
|
||||
|
||||
</#if>
|
||||
|
||||
<#attempt>
|
||||
<#if !object[key]??>
|
||||
<#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>
|
||||
|
||||
<#continue>
|
||||
</#if>
|
||||
|
||||
<#local out_seq += ['"' + key + '": ' + rec_out + ","]>
|
||||
|
||||
</#list>
|
||||
|
||||
<#return (["{"] + out_seq?map(str -> ""?right_pad(4 * (path?size + 1)) + str) + [ ""?right_pad(4 * path?size) + "}"])?join("\n")>
|
||||
|
||||
</#if>
|
||||
|
||||
<#local isMethod = "">
|
||||
<#attempt>
|
||||
<#local isMethod = object?is_method>
|
||||
<#recover>
|
||||
<#return "ABORT: Can't test if it'sa method.">
|
||||
</#attempt>
|
||||
|
||||
<#if isMethod>
|
||||
|
||||
<#if are_same_path(path, ["auth", "showUsername"])>
|
||||
<#attempt>
|
||||
<#return auth.showUsername()?c>
|
||||
<#recover>
|
||||
<#return "ABORT: Couldn't evaluate auth.showUsername()">
|
||||
</#attempt>
|
||||
</#if>
|
||||
|
||||
<#if are_same_path(path, ["auth", "showResetCredentials"])>
|
||||
<#attempt>
|
||||
<#return auth.showResetCredentials()?c>
|
||||
<#recover>
|
||||
<#return "ABORT: Couldn't evaluate auth.showResetCredentials()">
|
||||
</#attempt>
|
||||
</#if>
|
||||
|
||||
<#if are_same_path(path, ["auth", "showTryAnotherWayLink"])>
|
||||
<#attempt>
|
||||
<#return auth.showTryAnotherWayLink()?c>
|
||||
<#recover>
|
||||
<#return "ABORT: Couldn't evaluate auth.showTryAnotherWayLink()">
|
||||
</#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>
|
||||
|
||||
<#local rec_out = ftl_object_to_js_code_declaring_an_object(array_item, path + [ i ])>
|
||||
|
||||
<#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>
|
||||
|
||||
<#local out_seq += [rec_out + ","]>
|
||||
|
||||
</#list>
|
||||
|
||||
<#return (["["] + out_seq?map(str -> ""?right_pad(4 * (path?size + 1)) + str) + [ ""?right_pad(4 * path?size) + "]"])?join("\n")>
|
||||
|
||||
</#if>
|
||||
|
||||
<#attempt>
|
||||
<#return '"' + object?js_string + '"'>;
|
||||
<#recover>
|
||||
</#attempt>
|
||||
|
||||
<#return "ABORT: Couldn't convert into string non hash, non method, non boolean, non enumerable object">
|
||||
|
||||
</#function>
|
||||
<#function are_same_path path searchedPath>
|
||||
|
||||
<#if path?size != searchedPath?size>
|
||||
<#return false>
|
||||
</#if>
|
||||
|
||||
<#local i=0>
|
||||
|
||||
<#list path as property>
|
||||
|
||||
<#local searchedProperty=searchedPath[i]>
|
||||
|
||||
<#if searchedProperty?is_string && searchedProperty == "*">
|
||||
<#continue>
|
||||
</#if>
|
||||
|
||||
<#if searchedProperty?is_string && !property?is_string>
|
||||
<#return false>
|
||||
</#if>
|
||||
|
||||
<#if searchedProperty?is_number && !property?is_number>
|
||||
<#return false>
|
||||
</#if>
|
||||
|
||||
<#if searchedProperty?string != property?string>
|
||||
<#return false>
|
||||
</#if>
|
||||
|
||||
<#local i+= 1>
|
||||
|
||||
</#list>
|
||||
|
||||
<#return true>
|
||||
|
||||
</#function>
|
||||
</script>
|
185
src/bin/keycloakify/generateFtl/generateFtl.ts
Normal file
185
src/bin/keycloakify/generateFtl/generateFtl.ts
Normal file
@ -0,0 +1,185 @@
|
||||
import cheerio from "cheerio";
|
||||
import { replaceImportsFromStaticInJsCode } from "../replacers/replaceImportsFromStaticInJsCode";
|
||||
import { generateCssCodeToDefineGlobals } from "../replacers/replaceImportsInCssCode";
|
||||
import { replaceImportsInInlineCssCode } from "../replacers/replaceImportsInInlineCssCode";
|
||||
import * as fs from "fs";
|
||||
import { join as pathJoin } from "path";
|
||||
import { objectKeys } from "tsafe/objectKeys";
|
||||
import { ftlValuesGlobalName } from "../ftlValuesGlobalName";
|
||||
import type { BuildOptions } from "../BuildOptions";
|
||||
import { assert } from "tsafe/assert";
|
||||
import { Reflect } from "tsafe/Reflect";
|
||||
|
||||
// https://github.com/keycloak/keycloak/blob/main/services/src/main/java/org/keycloak/forms/login/freemarker/Templates.java
|
||||
export const pageIds = [
|
||||
"login.ftl",
|
||||
"register.ftl",
|
||||
"register-user-profile.ftl",
|
||||
"info.ftl",
|
||||
"error.ftl",
|
||||
"login-reset-password.ftl",
|
||||
"login-verify-email.ftl",
|
||||
"terms.ftl",
|
||||
"login-otp.ftl",
|
||||
"login-update-profile.ftl",
|
||||
"login-update-password.ftl",
|
||||
"login-idp-link-confirm.ftl",
|
||||
"login-idp-link-email.ftl",
|
||||
"login-page-expired.ftl",
|
||||
"login-config-totp.ftl",
|
||||
"logout-confirm.ftl"
|
||||
] as const;
|
||||
|
||||
export type BuildOptionsLike = BuildOptionsLike.Standalone | BuildOptionsLike.ExternalAssets;
|
||||
|
||||
export namespace BuildOptionsLike {
|
||||
export type Standalone = {
|
||||
isStandalone: true;
|
||||
urlPathname: string | undefined;
|
||||
};
|
||||
|
||||
export type ExternalAssets = ExternalAssets.SameDomain | ExternalAssets.DifferentDomains;
|
||||
|
||||
export namespace ExternalAssets {
|
||||
export type CommonExternalAssets = {
|
||||
isStandalone: false;
|
||||
};
|
||||
|
||||
export type SameDomain = CommonExternalAssets & {
|
||||
isAppAndKeycloakServerSharingSameDomain: true;
|
||||
};
|
||||
|
||||
export type DifferentDomains = CommonExternalAssets & {
|
||||
isAppAndKeycloakServerSharingSameDomain: false;
|
||||
urlOrigin: string;
|
||||
urlPathname: string | undefined;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
const buildOptions = Reflect<BuildOptions>();
|
||||
|
||||
assert<typeof buildOptions extends BuildOptionsLike ? true : false>();
|
||||
}
|
||||
|
||||
export type PageId = typeof pageIds[number];
|
||||
|
||||
export function generateFtlFilesCodeFactory(params: {
|
||||
indexHtmlCode: string;
|
||||
//NOTE: Expected to be an empty object if external assets mode is enabled.
|
||||
cssGlobalsToDefine: Record<string, string>;
|
||||
buildOptions: BuildOptionsLike;
|
||||
}) {
|
||||
const { cssGlobalsToDefine, indexHtmlCode, buildOptions } = params;
|
||||
|
||||
const $ = cheerio.load(indexHtmlCode);
|
||||
|
||||
fix_imports_statements: {
|
||||
if (!buildOptions.isStandalone && buildOptions.isAppAndKeycloakServerSharingSameDomain) {
|
||||
break fix_imports_statements;
|
||||
}
|
||||
|
||||
$("script:not([src])").each((...[, element]) => {
|
||||
const { fixedJsCode } = replaceImportsFromStaticInJsCode({
|
||||
"jsCode": $(element).html()!,
|
||||
buildOptions
|
||||
});
|
||||
|
||||
$(element).text(fixedJsCode);
|
||||
});
|
||||
|
||||
$("style").each((...[, element]) => {
|
||||
const { fixedCssCode } = replaceImportsInInlineCssCode({
|
||||
"cssCode": $(element).html()!,
|
||||
buildOptions
|
||||
});
|
||||
|
||||
$(element).text(fixedCssCode);
|
||||
});
|
||||
|
||||
(
|
||||
[
|
||||
["link", "href"],
|
||||
["script", "src"]
|
||||
] as const
|
||||
).forEach(([selector, attrName]) =>
|
||||
$(selector).each((...[, element]) => {
|
||||
const href = $(element).attr(attrName);
|
||||
|
||||
if (href === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
$(element).attr(
|
||||
attrName,
|
||||
buildOptions.isStandalone
|
||||
? href.replace(new RegExp(`^${(buildOptions.urlPathname ?? "/").replace(/\//g, "\\/")}`), "${url.resourcesPath}/build/")
|
||||
: href.replace(/^\//, `${buildOptions.urlOrigin}/`)
|
||||
);
|
||||
})
|
||||
);
|
||||
|
||||
if (Object.keys(cssGlobalsToDefine).length !== 0) {
|
||||
$("head").prepend(
|
||||
[
|
||||
"",
|
||||
"<style>",
|
||||
generateCssCodeToDefineGlobals({
|
||||
cssGlobalsToDefine,
|
||||
buildOptions
|
||||
}).cssCodeToPrependInHead,
|
||||
"</style>",
|
||||
""
|
||||
].join("\n")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
//FTL is no valid html, we can't insert with cheerio, we put placeholder for injecting later.
|
||||
const replaceValueBySearchValue = {
|
||||
'{ "x": "vIdLqMeOed9sdLdIdOxdK0d" }': fs
|
||||
.readFileSync(pathJoin(__dirname, "ftl_object_to_js_code_declaring_an_object.ftl"))
|
||||
.toString("utf8")
|
||||
.match(/^<script>const _=((?:.|\n)+)<\/script>[\n]?$/)![1],
|
||||
"<!-- xIdLqMeOedErIdLsPdNdI9dSlxI -->": [
|
||||
"<#if scripts??>",
|
||||
" <#list scripts as script>",
|
||||
' <script src="${script}" type="text/javascript"></script>',
|
||||
" </#list>",
|
||||
"</#if>"
|
||||
].join("\n")
|
||||
};
|
||||
|
||||
$("head").prepend(
|
||||
[
|
||||
"<script>",
|
||||
` window.${ftlValuesGlobalName}= ${objectKeys(replaceValueBySearchValue)[0]};`,
|
||||
"</script>",
|
||||
"",
|
||||
objectKeys(replaceValueBySearchValue)[1]
|
||||
].join("\n")
|
||||
);
|
||||
|
||||
const partiallyFixedIndexHtmlCode = $.html();
|
||||
|
||||
function generateFtlFilesCode(params: { pageId: string }): {
|
||||
ftlCode: string;
|
||||
} {
|
||||
const { pageId } = params;
|
||||
|
||||
const $ = cheerio.load(partiallyFixedIndexHtmlCode);
|
||||
|
||||
let ftlCode = $.html();
|
||||
|
||||
Object.entries({
|
||||
...replaceValueBySearchValue,
|
||||
//If updated, don't forget to change in the ftl script as well.
|
||||
"PAGE_ID_xIgLsPgGId9D8e": pageId
|
||||
}).map(([searchValue, replaceValue]) => (ftlCode = ftlCode.replace(searchValue, replaceValue)));
|
||||
|
||||
return { ftlCode };
|
||||
}
|
||||
|
||||
return { generateFtlFilesCode };
|
||||
}
|
1
src/bin/keycloakify/generateFtl/index.ts
Normal file
1
src/bin/keycloakify/generateFtl/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from "./generateFtl";
|
89
src/bin/keycloakify/generateJavaStackFiles.ts
Normal file
89
src/bin/keycloakify/generateJavaStackFiles.ts
Normal file
@ -0,0 +1,89 @@
|
||||
import * as fs from "fs";
|
||||
import { join as pathJoin, dirname as pathDirname } from "path";
|
||||
import { assert } from "tsafe/assert";
|
||||
import { Reflect } from "tsafe/Reflect";
|
||||
import type { BuildOptions } from "./BuildOptions";
|
||||
|
||||
export type BuildOptionsLike = {
|
||||
themeName: string;
|
||||
groupId: string;
|
||||
};
|
||||
|
||||
{
|
||||
const buildOptions = Reflect<BuildOptions>();
|
||||
|
||||
assert<typeof buildOptions extends BuildOptionsLike ? true : false>();
|
||||
}
|
||||
|
||||
export function generateJavaStackFiles(params: {
|
||||
version: string;
|
||||
keycloakThemeBuildingDirPath: string;
|
||||
doBundlesEmailTemplate: boolean;
|
||||
buildOptions: BuildOptionsLike;
|
||||
}): {
|
||||
jarFilePath: string;
|
||||
} {
|
||||
const {
|
||||
version,
|
||||
buildOptions: { groupId, themeName },
|
||||
keycloakThemeBuildingDirPath,
|
||||
doBundlesEmailTemplate
|
||||
} = params;
|
||||
|
||||
{
|
||||
const { pomFileCode } = (function generatePomFileCode(): {
|
||||
pomFileCode: string;
|
||||
} {
|
||||
const artefactId = `${themeName}-keycloak-theme`;
|
||||
|
||||
const pomFileCode = [
|
||||
`<?xml version="1.0"?>`,
|
||||
`<project xmlns="http://maven.apache.org/POM/4.0.0"`,
|
||||
` xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"`,
|
||||
` xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">`,
|
||||
` <modelVersion>4.0.0</modelVersion>`,
|
||||
` <groupId>${groupId}</groupId>`,
|
||||
` <artifactId>${artefactId}</artifactId>`,
|
||||
` <version>${version}</version>`,
|
||||
` <name>${artefactId}</name>`,
|
||||
` <description />`,
|
||||
`</project>`
|
||||
].join("\n");
|
||||
|
||||
return { pomFileCode };
|
||||
})();
|
||||
|
||||
fs.writeFileSync(pathJoin(keycloakThemeBuildingDirPath, "pom.xml"), Buffer.from(pomFileCode, "utf8"));
|
||||
}
|
||||
|
||||
{
|
||||
const themeManifestFilePath = pathJoin(keycloakThemeBuildingDirPath, "src", "main", "resources", "META-INF", "keycloak-themes.json");
|
||||
|
||||
try {
|
||||
fs.mkdirSync(pathDirname(themeManifestFilePath));
|
||||
} catch {}
|
||||
|
||||
fs.writeFileSync(
|
||||
themeManifestFilePath,
|
||||
Buffer.from(
|
||||
JSON.stringify(
|
||||
{
|
||||
"themes": [
|
||||
{
|
||||
"name": themeName,
|
||||
"types": ["login", ...(doBundlesEmailTemplate ? ["email"] : [])]
|
||||
}
|
||||
]
|
||||
},
|
||||
null,
|
||||
2
|
||||
),
|
||||
"utf8"
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
"jarFilePath": pathJoin(keycloakThemeBuildingDirPath, "target", `${themeName}-${version}.jar`)
|
||||
};
|
||||
}
|
199
src/bin/keycloakify/generateKeycloakThemeResources.ts
Normal file
199
src/bin/keycloakify/generateKeycloakThemeResources.ts
Normal file
@ -0,0 +1,199 @@
|
||||
import { transformCodebase } from "../tools/transformCodebase";
|
||||
import * as fs from "fs";
|
||||
import { join as pathJoin, basename as pathBasename } from "path";
|
||||
import { replaceImportsFromStaticInJsCode } from "./replacers/replaceImportsFromStaticInJsCode";
|
||||
import { replaceImportsInCssCode } from "./replacers/replaceImportsInCssCode";
|
||||
import { generateFtlFilesCodeFactory, pageIds } from "./generateFtl";
|
||||
import { downloadBuiltinKeycloakTheme } from "../download-builtin-keycloak-theme";
|
||||
import * as child_process from "child_process";
|
||||
import { mockTestingResourcesCommonPath, mockTestingResourcesPath, mockTestingSubDirOfPublicDirBasename } from "../mockTestingResourcesPath";
|
||||
import { isInside } from "../tools/isInside";
|
||||
import type { BuildOptions } from "./BuildOptions";
|
||||
import { assert } from "tsafe/assert";
|
||||
import { Reflect } from "tsafe/Reflect";
|
||||
|
||||
export type BuildOptionsLike = BuildOptionsLike.Standalone | BuildOptionsLike.ExternalAssets;
|
||||
|
||||
export namespace BuildOptionsLike {
|
||||
export type Common = {
|
||||
themeName: string;
|
||||
extraPages?: string[];
|
||||
extraThemeProperties?: string[];
|
||||
};
|
||||
|
||||
export type Standalone = Common & {
|
||||
isStandalone: true;
|
||||
urlPathname: string | undefined;
|
||||
};
|
||||
|
||||
export type ExternalAssets = ExternalAssets.SameDomain | ExternalAssets.DifferentDomains;
|
||||
|
||||
export namespace ExternalAssets {
|
||||
export type CommonExternalAssets = Common & {
|
||||
isStandalone: false;
|
||||
};
|
||||
|
||||
export type SameDomain = CommonExternalAssets & {
|
||||
isAppAndKeycloakServerSharingSameDomain: true;
|
||||
};
|
||||
|
||||
export type DifferentDomains = CommonExternalAssets & {
|
||||
isAppAndKeycloakServerSharingSameDomain: false;
|
||||
urlOrigin: string;
|
||||
urlPathname: string | undefined;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
const buildOptions = Reflect<BuildOptions>();
|
||||
|
||||
assert<typeof buildOptions extends BuildOptionsLike ? true : false>();
|
||||
}
|
||||
|
||||
export function generateKeycloakThemeResources(params: {
|
||||
reactAppBuildDirPath: string;
|
||||
keycloakThemeBuildingDirPath: string;
|
||||
keycloakThemeEmailDirPath: string;
|
||||
keycloakVersion: string;
|
||||
buildOptions: BuildOptionsLike;
|
||||
}): { doBundlesEmailTemplate: boolean } {
|
||||
const { reactAppBuildDirPath, keycloakThemeBuildingDirPath, keycloakThemeEmailDirPath, keycloakVersion, buildOptions } = params;
|
||||
|
||||
const themeDirPath = pathJoin(keycloakThemeBuildingDirPath, "src", "main", "resources", "theme", buildOptions.themeName, "login");
|
||||
|
||||
let allCssGlobalsToDefine: Record<string, string> = {};
|
||||
|
||||
transformCodebase({
|
||||
"destDirPath": buildOptions.isStandalone ? pathJoin(themeDirPath, "resources", "build") : reactAppBuildDirPath,
|
||||
"srcDirPath": reactAppBuildDirPath,
|
||||
"transformSourceCode": ({ filePath, sourceCode }) => {
|
||||
//NOTE: Prevent cycles, excludes the folder we generated for debug in public/
|
||||
if (
|
||||
buildOptions.isStandalone &&
|
||||
isInside({
|
||||
"dirPath": pathJoin(reactAppBuildDirPath, mockTestingSubDirOfPublicDirBasename),
|
||||
filePath
|
||||
})
|
||||
) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (/\.css?$/i.test(filePath)) {
|
||||
if (!buildOptions.isStandalone) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const { cssGlobalsToDefine, fixedCssCode } = replaceImportsInCssCode({
|
||||
"cssCode": sourceCode.toString("utf8")
|
||||
});
|
||||
|
||||
allCssGlobalsToDefine = {
|
||||
...allCssGlobalsToDefine,
|
||||
...cssGlobalsToDefine
|
||||
};
|
||||
|
||||
return { "modifiedSourceCode": Buffer.from(fixedCssCode, "utf8") };
|
||||
}
|
||||
|
||||
if (/\.js?$/i.test(filePath)) {
|
||||
if (!buildOptions.isStandalone && buildOptions.isAppAndKeycloakServerSharingSameDomain) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const { fixedJsCode } = replaceImportsFromStaticInJsCode({
|
||||
"jsCode": sourceCode.toString("utf8"),
|
||||
buildOptions
|
||||
});
|
||||
|
||||
return { "modifiedSourceCode": Buffer.from(fixedJsCode, "utf8") };
|
||||
}
|
||||
|
||||
return buildOptions.isStandalone ? { "modifiedSourceCode": sourceCode } : undefined;
|
||||
}
|
||||
});
|
||||
|
||||
let doBundlesEmailTemplate: boolean;
|
||||
|
||||
email: {
|
||||
if (!fs.existsSync(keycloakThemeEmailDirPath)) {
|
||||
console.log(
|
||||
[
|
||||
`Not bundling email template because ${pathBasename(keycloakThemeEmailDirPath)} does not exist`,
|
||||
`To start customizing the email template, run: 👉 npx create-keycloak-email-directory 👈`
|
||||
].join("\n")
|
||||
);
|
||||
doBundlesEmailTemplate = false;
|
||||
break email;
|
||||
}
|
||||
|
||||
doBundlesEmailTemplate = true;
|
||||
|
||||
transformCodebase({
|
||||
"srcDirPath": keycloakThemeEmailDirPath,
|
||||
"destDirPath": pathJoin(themeDirPath, "..", "email")
|
||||
});
|
||||
}
|
||||
|
||||
const { generateFtlFilesCode } = generateFtlFilesCodeFactory({
|
||||
"indexHtmlCode": fs.readFileSync(pathJoin(reactAppBuildDirPath, "index.html")).toString("utf8"),
|
||||
"cssGlobalsToDefine": allCssGlobalsToDefine,
|
||||
"buildOptions": buildOptions
|
||||
});
|
||||
|
||||
[...pageIds, ...(buildOptions.extraPages ?? [])].forEach(pageId => {
|
||||
const { ftlCode } = generateFtlFilesCode({ pageId });
|
||||
|
||||
fs.mkdirSync(themeDirPath, { "recursive": true });
|
||||
|
||||
fs.writeFileSync(pathJoin(themeDirPath, pageId), Buffer.from(ftlCode, "utf8"));
|
||||
});
|
||||
|
||||
{
|
||||
const tmpDirPath = pathJoin(themeDirPath, "..", "tmp_xxKdLpdIdLd");
|
||||
|
||||
downloadBuiltinKeycloakTheme({
|
||||
keycloakVersion,
|
||||
"destDirPath": tmpDirPath
|
||||
});
|
||||
|
||||
const themeResourcesDirPath = pathJoin(themeDirPath, "resources");
|
||||
|
||||
transformCodebase({
|
||||
"srcDirPath": pathJoin(tmpDirPath, "keycloak", "login", "resources"),
|
||||
"destDirPath": themeResourcesDirPath
|
||||
});
|
||||
|
||||
const reactAppPublicDirPath = pathJoin(reactAppBuildDirPath, "..", "public");
|
||||
|
||||
transformCodebase({
|
||||
"srcDirPath": pathJoin(tmpDirPath, "keycloak", "common", "resources"),
|
||||
"destDirPath": pathJoin(themeResourcesDirPath, pathBasename(mockTestingResourcesCommonPath))
|
||||
});
|
||||
|
||||
transformCodebase({
|
||||
"srcDirPath": themeResourcesDirPath,
|
||||
"destDirPath": pathJoin(reactAppPublicDirPath, mockTestingResourcesPath)
|
||||
});
|
||||
|
||||
const keycloakResourcesWithinPublicDirPath = pathJoin(reactAppPublicDirPath, mockTestingSubDirOfPublicDirBasename);
|
||||
|
||||
fs.writeFileSync(
|
||||
pathJoin(keycloakResourcesWithinPublicDirPath, "README.txt"),
|
||||
Buffer.from(
|
||||
["This is just a test folder that helps develop", "the login and register page without having to run a Keycloak container"].join(" ")
|
||||
)
|
||||
);
|
||||
|
||||
fs.writeFileSync(pathJoin(keycloakResourcesWithinPublicDirPath, ".gitignore"), Buffer.from("*", "utf8"));
|
||||
|
||||
child_process.execSync(`rm -r ${tmpDirPath}`);
|
||||
}
|
||||
|
||||
fs.writeFileSync(
|
||||
pathJoin(themeDirPath, "theme.properties"),
|
||||
Buffer.from(["parent=keycloak", ...(buildOptions.extraThemeProperties ?? [])].join("\n\n"), "utf8")
|
||||
);
|
||||
|
||||
return { doBundlesEmailTemplate };
|
||||
}
|
65
src/bin/keycloakify/generateStartKeycloakTestingContainer.ts
Normal file
65
src/bin/keycloakify/generateStartKeycloakTestingContainer.ts
Normal file
@ -0,0 +1,65 @@
|
||||
import * as fs from "fs";
|
||||
import { join as pathJoin } from "path";
|
||||
import { assert } from "tsafe/assert";
|
||||
import { Reflect } from "tsafe/Reflect";
|
||||
import type { BuildOptions } from "./BuildOptions";
|
||||
|
||||
export type BuildOptionsLike = {
|
||||
themeName: string;
|
||||
};
|
||||
|
||||
{
|
||||
const buildOptions = Reflect<BuildOptions>();
|
||||
|
||||
assert<typeof buildOptions extends BuildOptionsLike ? true : false>();
|
||||
}
|
||||
|
||||
generateStartKeycloakTestingContainer.basename = "start_keycloak_testing_container.sh";
|
||||
|
||||
const containerName = "keycloak-testing-container";
|
||||
|
||||
/** Files for being able to run a hot reload keycloak container */
|
||||
export function generateStartKeycloakTestingContainer(params: {
|
||||
keycloakVersion: string;
|
||||
keycloakThemeBuildingDirPath: string;
|
||||
buildOptions: BuildOptionsLike;
|
||||
}) {
|
||||
const {
|
||||
keycloakThemeBuildingDirPath,
|
||||
keycloakVersion,
|
||||
buildOptions: { themeName }
|
||||
} = params;
|
||||
|
||||
fs.writeFileSync(
|
||||
pathJoin(keycloakThemeBuildingDirPath, generateStartKeycloakTestingContainer.basename),
|
||||
Buffer.from(
|
||||
[
|
||||
"#!/bin/bash",
|
||||
"",
|
||||
`docker rm ${containerName} || true`,
|
||||
"",
|
||||
`cd ${keycloakThemeBuildingDirPath}`,
|
||||
"",
|
||||
"docker run \\",
|
||||
" -p 8080:8080 \\",
|
||||
` --name ${containerName} \\`,
|
||||
" -e KEYCLOAK_ADMIN=admin \\",
|
||||
" -e KEYCLOAK_ADMIN_PASSWORD=admin \\",
|
||||
" -e JAVA_OPTS=-Dkeycloak.profile=preview \\",
|
||||
` -v ${pathJoin(
|
||||
keycloakThemeBuildingDirPath,
|
||||
"src",
|
||||
"main",
|
||||
"resources",
|
||||
"theme",
|
||||
themeName
|
||||
)}:/opt/keycloak/themes/${themeName}:rw \\`,
|
||||
` -it quay.io/keycloak/keycloak:${keycloakVersion} \\`,
|
||||
` start-dev`,
|
||||
""
|
||||
].join("\n"),
|
||||
"utf8"
|
||||
),
|
||||
{ "mode": 0o755 }
|
||||
);
|
||||
}
|
8
src/bin/keycloakify/index.ts
Normal file
8
src/bin/keycloakify/index.ts
Normal file
@ -0,0 +1,8 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
export * from "./keycloakify";
|
||||
import { main } from "./keycloakify";
|
||||
|
||||
if (require.main === module) {
|
||||
main();
|
||||
}
|
114
src/bin/keycloakify/keycloakify.ts
Normal file
114
src/bin/keycloakify/keycloakify.ts
Normal file
@ -0,0 +1,114 @@
|
||||
import { generateKeycloakThemeResources } from "./generateKeycloakThemeResources";
|
||||
import { generateJavaStackFiles } from "./generateJavaStackFiles";
|
||||
import { join as pathJoin, relative as pathRelative, basename as pathBasename } from "path";
|
||||
import * as child_process from "child_process";
|
||||
import { generateStartKeycloakTestingContainer } from "./generateStartKeycloakTestingContainer";
|
||||
import * as fs from "fs";
|
||||
import { readBuildOptions } from "./BuildOptions";
|
||||
|
||||
const reactProjectDirPath = process.cwd();
|
||||
|
||||
export const keycloakThemeBuildingDirPath = pathJoin(reactProjectDirPath, "build_keycloak");
|
||||
export const keycloakThemeEmailDirPath = pathJoin(keycloakThemeBuildingDirPath, "..", "keycloak_email");
|
||||
|
||||
export function main() {
|
||||
console.log("🔏 Building the keycloak theme...⌚");
|
||||
|
||||
const buildOptions = readBuildOptions({
|
||||
"packageJson": fs.readFileSync(pathJoin(reactProjectDirPath, "package.json")).toString("utf8"),
|
||||
"CNAME": (() => {
|
||||
const cnameFilePath = pathJoin(reactProjectDirPath, "public", "CNAME");
|
||||
|
||||
if (!fs.existsSync(cnameFilePath)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return fs.readFileSync(cnameFilePath).toString("utf8");
|
||||
})(),
|
||||
"isExternalAssetsCliParamProvided": process.argv[2]?.toLowerCase() === "--external-assets"
|
||||
});
|
||||
|
||||
const { doBundlesEmailTemplate } = generateKeycloakThemeResources({
|
||||
keycloakThemeBuildingDirPath,
|
||||
keycloakThemeEmailDirPath,
|
||||
"reactAppBuildDirPath": pathJoin(reactProjectDirPath, "build"),
|
||||
buildOptions,
|
||||
//We have to leave it at that otherwise we break our default theme.
|
||||
//Problem is that we can't guarantee that the the old resources
|
||||
//will still be available on the newer keycloak version.
|
||||
"keycloakVersion": "11.0.3"
|
||||
});
|
||||
|
||||
const { jarFilePath } = generateJavaStackFiles({
|
||||
"version": buildOptions.version,
|
||||
keycloakThemeBuildingDirPath,
|
||||
doBundlesEmailTemplate,
|
||||
buildOptions
|
||||
});
|
||||
|
||||
child_process.execSync("mvn package", {
|
||||
"cwd": keycloakThemeBuildingDirPath
|
||||
});
|
||||
|
||||
//We want, however, to test in a container running the latest Keycloak version
|
||||
const containerKeycloakVersion = "18.0.2";
|
||||
|
||||
generateStartKeycloakTestingContainer({
|
||||
keycloakThemeBuildingDirPath,
|
||||
"keycloakVersion": containerKeycloakVersion,
|
||||
buildOptions
|
||||
});
|
||||
|
||||
console.log(
|
||||
[
|
||||
"",
|
||||
`✅ Your keycloak theme has been generated and bundled into ./${pathRelative(reactProjectDirPath, jarFilePath)} 🚀`,
|
||||
`It is to be placed in "/opt/keycloak/providers" in the container running a quay.io/keycloak/keycloak Docker image.`,
|
||||
"",
|
||||
//TODO: Restore when we find a good Helm chart for Keycloak.
|
||||
//"Using Helm (https://github.com/codecentric/helm-charts), edit to reflect:",
|
||||
"",
|
||||
"value.yaml: ",
|
||||
" extraInitContainers: |",
|
||||
" - name: realm-ext-provider",
|
||||
" image: curlimages/curl",
|
||||
" imagePullPolicy: IfNotPresent",
|
||||
" command:",
|
||||
" - sh",
|
||||
" args:",
|
||||
" - -c",
|
||||
` - curl -L -f -S -o /extensions/${pathBasename(jarFilePath)} https://AN.URL.FOR/${pathBasename(jarFilePath)}`,
|
||||
" volumeMounts:",
|
||||
" - name: extensions",
|
||||
" mountPath: /extensions",
|
||||
" ",
|
||||
" extraVolumeMounts: |",
|
||||
" - name: extensions",
|
||||
" mountPath: /opt/keycloak/providers",
|
||||
" extraEnv: |",
|
||||
" - name: KEYCLOAK_USER",
|
||||
" value: admin",
|
||||
" - name: KEYCLOAK_PASSWORD",
|
||||
" value: xxxxxxxxx",
|
||||
" - name: JAVA_OPTS",
|
||||
" value: -Dkeycloak.profile=preview",
|
||||
"",
|
||||
"",
|
||||
`To test your theme locally you can spin up a Keycloak ${containerKeycloakVersion} container image with the theme pre loaded by running:`,
|
||||
"",
|
||||
`👉 $ ./${pathRelative(reactProjectDirPath, pathJoin(keycloakThemeBuildingDirPath, generateStartKeycloakTestingContainer.basename))} 👈`,
|
||||
"",
|
||||
"Test with different Keycloak versions by editing the .sh file. see available versions here: https://quay.io/repository/keycloak/keycloak?tab=tags",
|
||||
"",
|
||||
"Once your container is up and running: ",
|
||||
"- Log into the admin console 👉 http://localhost:8080/admin username: admin, password: admin 👈",
|
||||
'- Create a realm named "myrealm"',
|
||||
'- Create a client with ID: "myclient", "Root URL": "https://www.keycloak.org/app/" and "Valid redirect URIs": "https://www.keycloak.org/app/*"',
|
||||
`- Select Login Theme: ${buildOptions.themeName} (don't forget to save at the bottom of the page)`,
|
||||
`- Go to 👉 https://www.keycloak.org/app/ 👈 Click "Save" then "Sign in". You should see your login page`,
|
||||
"",
|
||||
"Video demoing this process: https://youtu.be/N3wlBoH4hKg",
|
||||
""
|
||||
].join("\n")
|
||||
);
|
||||
}
|
@ -0,0 +1,86 @@
|
||||
import { ftlValuesGlobalName } from "../ftlValuesGlobalName";
|
||||
import type { BuildOptions } from "../BuildOptions";
|
||||
import { assert } from "tsafe/assert";
|
||||
import { is } from "tsafe/is";
|
||||
import { Reflect } from "tsafe/Reflect";
|
||||
|
||||
export type BuildOptionsLike = BuildOptionsLike.Standalone | BuildOptionsLike.ExternalAssets;
|
||||
|
||||
export namespace BuildOptionsLike {
|
||||
export type Standalone = {
|
||||
isStandalone: true;
|
||||
};
|
||||
|
||||
export type ExternalAssets = {
|
||||
isStandalone: false;
|
||||
urlOrigin: string;
|
||||
};
|
||||
}
|
||||
|
||||
{
|
||||
const buildOptions = Reflect<BuildOptions>();
|
||||
|
||||
assert(!is<BuildOptions.ExternalAssets.CommonExternalAssets>(buildOptions));
|
||||
|
||||
assert<typeof buildOptions extends BuildOptionsLike ? true : false>();
|
||||
}
|
||||
|
||||
export function replaceImportsFromStaticInJsCode(params: { jsCode: string; buildOptions: BuildOptionsLike }): { fixedJsCode: string } {
|
||||
/*
|
||||
NOTE:
|
||||
|
||||
When we have urlOrigin defined it means that
|
||||
we are building with --external-assets
|
||||
so we have to make sur that the fixed js code will run
|
||||
inside and outside keycloak.
|
||||
|
||||
When urlOrigin isn't defined we can assume the fixedJsCode
|
||||
will always run in keycloak context.
|
||||
*/
|
||||
|
||||
const { jsCode, buildOptions } = params;
|
||||
|
||||
const getReplaceArgs = (language: "js" | "css"): Parameters<typeof String.prototype.replace> => [
|
||||
new RegExp(`([a-zA-Z]+)\\.([a-zA-Z]+)=function\\(([a-zA-Z]+)\\){return"static\\/${language}\\/"`, "g"),
|
||||
(...[, n, u, e]) => `
|
||||
${n}[(function(){
|
||||
var pd= Object.getOwnPropertyDescriptor(n, "p");
|
||||
if( pd === undefined || pd.configurable ){
|
||||
${
|
||||
buildOptions.isStandalone
|
||||
? `
|
||||
Object.defineProperty(${n}, "p", {
|
||||
get: function() { return window.${ftlValuesGlobalName}.url.resourcesPath; },
|
||||
set: function (){}
|
||||
});
|
||||
`
|
||||
: `
|
||||
var p= "";
|
||||
Object.defineProperty(${n}, "p", {
|
||||
get: function() { return ("${ftlValuesGlobalName}" in window ? "${buildOptions.urlOrigin}" : "") + p; },
|
||||
set: function (value){ p = value;}
|
||||
});
|
||||
`
|
||||
}
|
||||
}
|
||||
return "${u}";
|
||||
})()] = function(${e}) { return "${buildOptions.isStandalone ? "/build/" : ""}static/${language}/"`
|
||||
];
|
||||
|
||||
const fixedJsCode = jsCode
|
||||
.replace(...getReplaceArgs("js"))
|
||||
.replace(...getReplaceArgs("css"))
|
||||
.replace(/([a-zA-Z]+\.[a-zA-Z]+)\+"static\//g, (...[, group]) =>
|
||||
buildOptions.isStandalone
|
||||
? `window.${ftlValuesGlobalName}.url.resourcesPath + "/build/static/`
|
||||
: `("${ftlValuesGlobalName}" in window ? "${buildOptions.urlOrigin}" : "") + ${group} + "static/`
|
||||
)
|
||||
//TODO: Write a test case for this
|
||||
.replace(/".chunk.css",([a-zA-Z])+=([a-zA-Z]+\.[a-zA-Z]+)\+([a-zA-Z]+),/, (...[, group1, group2, group3]) =>
|
||||
buildOptions.isStandalone
|
||||
? `".chunk.css",${group1} = window.${ftlValuesGlobalName}.url.resourcesPath + "/build/" + ${group3},`
|
||||
: `".chunk.css",${group1} = ("${ftlValuesGlobalName}" in window ? "${buildOptions.urlOrigin}" : "") + ${group2} + ${group3},`
|
||||
);
|
||||
|
||||
return { fixedJsCode };
|
||||
}
|
64
src/bin/keycloakify/replacers/replaceImportsInCssCode.ts
Normal file
64
src/bin/keycloakify/replacers/replaceImportsInCssCode.ts
Normal file
@ -0,0 +1,64 @@
|
||||
import * as crypto from "crypto";
|
||||
import type { BuildOptions } from "../BuildOptions";
|
||||
import { assert } from "tsafe/assert";
|
||||
import { is } from "tsafe/is";
|
||||
import { Reflect } from "tsafe/Reflect";
|
||||
|
||||
export type BuildOptionsLike = {
|
||||
urlPathname: string | undefined;
|
||||
};
|
||||
|
||||
{
|
||||
const buildOptions = Reflect<BuildOptions>();
|
||||
|
||||
assert(!is<BuildOptions.ExternalAssets.CommonExternalAssets>(buildOptions));
|
||||
|
||||
assert<typeof buildOptions extends BuildOptionsLike ? true : false>();
|
||||
}
|
||||
|
||||
export function replaceImportsInCssCode(params: { cssCode: string }): {
|
||||
fixedCssCode: string;
|
||||
cssGlobalsToDefine: Record<string, string>;
|
||||
} {
|
||||
const { cssCode } = params;
|
||||
|
||||
const cssGlobalsToDefine: Record<string, string> = {};
|
||||
|
||||
new Set(cssCode.match(/url\(["']?\/[^/][^)"']+["']?\)[^;}]*/g) ?? []).forEach(
|
||||
match => (cssGlobalsToDefine["url" + crypto.createHash("sha256").update(match).digest("hex").substring(0, 15)] = match)
|
||||
);
|
||||
|
||||
let fixedCssCode = cssCode;
|
||||
|
||||
Object.keys(cssGlobalsToDefine).forEach(
|
||||
cssVariableName =>
|
||||
//NOTE: split/join pattern ~ replace all
|
||||
(fixedCssCode = fixedCssCode.split(cssGlobalsToDefine[cssVariableName]).join(`var(--${cssVariableName})`))
|
||||
);
|
||||
|
||||
return { fixedCssCode, cssGlobalsToDefine };
|
||||
}
|
||||
|
||||
export function generateCssCodeToDefineGlobals(params: { cssGlobalsToDefine: Record<string, string>; buildOptions: BuildOptionsLike }): {
|
||||
cssCodeToPrependInHead: string;
|
||||
} {
|
||||
const { cssGlobalsToDefine, buildOptions } = params;
|
||||
|
||||
return {
|
||||
"cssCodeToPrependInHead": [
|
||||
":root {",
|
||||
...Object.keys(cssGlobalsToDefine)
|
||||
.map(cssVariableName =>
|
||||
[
|
||||
`--${cssVariableName}:`,
|
||||
cssGlobalsToDefine[cssVariableName].replace(
|
||||
new RegExp(`url\\(${(buildOptions.urlPathname ?? "/").replace(/\//g, "\\/")}`, "g"),
|
||||
"url(${url.resourcesPath}/build/"
|
||||
)
|
||||
].join(" ")
|
||||
)
|
||||
.map(line => ` ${line};`),
|
||||
"}"
|
||||
].join("\n")
|
||||
};
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
import type { BuildOptions } from "../BuildOptions";
|
||||
import { assert } from "tsafe/assert";
|
||||
import { is } from "tsafe/is";
|
||||
import { Reflect } from "tsafe/Reflect";
|
||||
|
||||
export type BuildOptionsLike = BuildOptionsLike.Standalone | BuildOptionsLike.ExternalAssets;
|
||||
|
||||
export namespace BuildOptionsLike {
|
||||
export type Common = {
|
||||
urlPathname: string | undefined;
|
||||
};
|
||||
|
||||
export type Standalone = Common & {
|
||||
isStandalone: true;
|
||||
};
|
||||
|
||||
export type ExternalAssets = Common & {
|
||||
isStandalone: false;
|
||||
urlOrigin: string;
|
||||
};
|
||||
}
|
||||
|
||||
{
|
||||
const buildOptions = Reflect<BuildOptions>();
|
||||
|
||||
assert(!is<BuildOptions.ExternalAssets.CommonExternalAssets>(buildOptions));
|
||||
|
||||
assert<typeof buildOptions extends BuildOptionsLike ? true : false>();
|
||||
}
|
||||
|
||||
export function replaceImportsInInlineCssCode(params: { cssCode: string; buildOptions: BuildOptionsLike }): {
|
||||
fixedCssCode: string;
|
||||
} {
|
||||
const { cssCode, buildOptions } = params;
|
||||
|
||||
const fixedCssCode = cssCode.replace(
|
||||
buildOptions.urlPathname === undefined
|
||||
? /url\(["']?\/([^/][^)"']+)["']?\)/g
|
||||
: new RegExp(`url\\(["']?${buildOptions.urlPathname}([^)"']+)["']?\\)`, "g"),
|
||||
(...[, group]) =>
|
||||
`url(${
|
||||
buildOptions.isStandalone ? "${url.resourcesPath}/build/" + group : buildOptions.urlOrigin + (buildOptions.urlPathname ?? "/") + group
|
||||
})`
|
||||
);
|
||||
|
||||
return { fixedCssCode };
|
||||
}
|
128
src/bin/link_in_test_app.ts
Normal file
128
src/bin/link_in_test_app.ts
Normal file
@ -0,0 +1,128 @@
|
||||
import { execSync } from "child_process";
|
||||
import { join as pathJoin, relative as pathRelative } from "path";
|
||||
import { exclude } from "tsafe/exclude";
|
||||
import * as fs from "fs";
|
||||
|
||||
const keycloakifyDirPath = pathJoin(__dirname, "..", "..");
|
||||
|
||||
fs.writeFileSync(
|
||||
pathJoin(keycloakifyDirPath, "dist", "package.json"),
|
||||
Buffer.from(
|
||||
JSON.stringify(
|
||||
(() => {
|
||||
const packageJsonParsed = JSON.parse(fs.readFileSync(pathJoin(keycloakifyDirPath, "package.json")).toString("utf8"));
|
||||
|
||||
return {
|
||||
...packageJsonParsed,
|
||||
"main": packageJsonParsed["main"].replace(/^dist\//, ""),
|
||||
"types": packageJsonParsed["types"].replace(/^dist\//, "")
|
||||
};
|
||||
})(),
|
||||
null,
|
||||
2
|
||||
),
|
||||
"utf8"
|
||||
)
|
||||
);
|
||||
|
||||
const commonThirdPartyDeps = (() => {
|
||||
const namespaceModuleNames = ["@emotion"];
|
||||
const standaloneModuleNames = ["react", "@types/react", "powerhooks", "tss-react", "evt"];
|
||||
|
||||
return [
|
||||
...namespaceModuleNames
|
||||
.map(namespaceModuleName =>
|
||||
fs
|
||||
.readdirSync(pathJoin(keycloakifyDirPath, "node_modules", namespaceModuleName))
|
||||
.map(submoduleName => `${namespaceModuleName}/${submoduleName}`)
|
||||
)
|
||||
.reduce((prev, curr) => [...prev, ...curr], []),
|
||||
...standaloneModuleNames
|
||||
];
|
||||
})();
|
||||
|
||||
const yarnHomeDirPath = pathJoin(keycloakifyDirPath, ".yarn_home");
|
||||
|
||||
execSync(["rm -rf", "mkdir"].map(cmd => `${cmd} ${yarnHomeDirPath}`).join(" && "));
|
||||
|
||||
const execYarnLink = (params: { targetModuleName?: string; cwd: string }) => {
|
||||
const { targetModuleName, cwd } = params;
|
||||
|
||||
const cmd = ["yarn", "link", ...(targetModuleName !== undefined ? [targetModuleName] : [])].join(" ");
|
||||
|
||||
console.log(`$ cd ${pathRelative(keycloakifyDirPath, cwd) || "."} && ${cmd}`);
|
||||
|
||||
execSync(cmd, {
|
||||
cwd,
|
||||
"env": {
|
||||
...process.env,
|
||||
"HOME": yarnHomeDirPath
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const testAppPaths = (() => {
|
||||
const arg = process.argv[2];
|
||||
|
||||
const testAppNames = arg !== undefined ? [arg] : ["keycloakify-starter", "keycloakify-advanced-starter"];
|
||||
|
||||
return testAppNames
|
||||
.map(testAppName => {
|
||||
const testAppPath = pathJoin(keycloakifyDirPath, "..", testAppName);
|
||||
|
||||
if (fs.existsSync(testAppPath)) {
|
||||
return testAppPath;
|
||||
}
|
||||
|
||||
console.warn(`Skipping ${testAppName} since it cant be found here: ${testAppPath}`);
|
||||
|
||||
return undefined;
|
||||
})
|
||||
.filter(exclude(undefined));
|
||||
})();
|
||||
|
||||
console.log(testAppPaths);
|
||||
|
||||
if (testAppPaths.length === 0) {
|
||||
console.error("No test app to link into!");
|
||||
process.exit(-1);
|
||||
}
|
||||
|
||||
testAppPaths.forEach(testAppPath => execSync("yarn install", { "cwd": testAppPath }));
|
||||
|
||||
console.log("=== Linking common dependencies ===");
|
||||
|
||||
const total = commonThirdPartyDeps.length;
|
||||
let current = 0;
|
||||
|
||||
commonThirdPartyDeps.forEach(commonThirdPartyDep => {
|
||||
current++;
|
||||
|
||||
console.log(`${current}/${total} ${commonThirdPartyDep}`);
|
||||
|
||||
const localInstallPath = pathJoin(
|
||||
...[keycloakifyDirPath, "node_modules", ...(commonThirdPartyDep.startsWith("@") ? commonThirdPartyDep.split("/") : [commonThirdPartyDep])]
|
||||
);
|
||||
|
||||
execYarnLink({ "cwd": localInstallPath });
|
||||
});
|
||||
|
||||
commonThirdPartyDeps.forEach(commonThirdPartyDep =>
|
||||
testAppPaths.forEach(testAppPath =>
|
||||
execYarnLink({
|
||||
"cwd": testAppPath,
|
||||
"targetModuleName": commonThirdPartyDep
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
console.log("=== Linking in house dependencies ===");
|
||||
|
||||
execYarnLink({ "cwd": pathJoin(keycloakifyDirPath, "dist") });
|
||||
|
||||
testAppPaths.forEach(testAppPath =>
|
||||
execYarnLink({
|
||||
"cwd": testAppPath,
|
||||
"targetModuleName": "keycloakify"
|
||||
})
|
||||
);
|
5
src/bin/mockTestingResourcesPath.ts
Normal file
5
src/bin/mockTestingResourcesPath.ts
Normal file
@ -0,0 +1,5 @@
|
||||
import { pathJoin } from "./tools/pathJoin";
|
||||
|
||||
export const mockTestingSubDirOfPublicDirBasename = "keycloak_static";
|
||||
export const mockTestingResourcesPath = pathJoin(mockTestingSubDirOfPublicDirBasename, "resources");
|
||||
export const mockTestingResourcesCommonPath = pathJoin(mockTestingResourcesPath, "resources_common");
|
47
src/bin/promptKeycloakVersion.ts
Normal file
47
src/bin/promptKeycloakVersion.ts
Normal file
@ -0,0 +1,47 @@
|
||||
import { getLatestsSemVersionedTagFactory } from "./tools/octokit-addons/getLatestsSemVersionedTag";
|
||||
import { Octokit } from "@octokit/rest";
|
||||
import cliSelect from "cli-select";
|
||||
|
||||
export async function promptKeycloakVersion() {
|
||||
const { getLatestsSemVersionedTag } = (() => {
|
||||
const { octokit } = (() => {
|
||||
const githubToken = process.env.GITHUB_TOKEN;
|
||||
|
||||
const octokit = new Octokit(githubToken === undefined ? undefined : { "auth": githubToken });
|
||||
|
||||
return { octokit };
|
||||
})();
|
||||
|
||||
const { getLatestsSemVersionedTag } = getLatestsSemVersionedTagFactory({ octokit });
|
||||
|
||||
return { getLatestsSemVersionedTag };
|
||||
})();
|
||||
|
||||
console.log("Initialize the directory with email template from which keycloak version?");
|
||||
|
||||
const tags = [
|
||||
...(await getLatestsSemVersionedTag({
|
||||
"count": 10,
|
||||
"doIgnoreBeta": true,
|
||||
"owner": "keycloak",
|
||||
"repo": "keycloak"
|
||||
}).then(arr => arr.map(({ tag }) => tag))),
|
||||
"11.0.3"
|
||||
];
|
||||
|
||||
if (process.env["GITHUB_ACTIONS"] === "true") {
|
||||
return { "keycloakVersion": tags[0] };
|
||||
}
|
||||
|
||||
const { value: keycloakVersion } = await cliSelect<string>({
|
||||
"values": tags
|
||||
}).catch(() => {
|
||||
console.log("Aborting");
|
||||
|
||||
process.exit(-1);
|
||||
});
|
||||
|
||||
console.log(keycloakVersion);
|
||||
|
||||
return { keycloakVersion };
|
||||
}
|
73
src/bin/tools/NpmModuleVersion.ts
Normal file
73
src/bin/tools/NpmModuleVersion.ts
Normal file
@ -0,0 +1,73 @@
|
||||
export type NpmModuleVersion = {
|
||||
major: number;
|
||||
minor: number;
|
||||
patch: number;
|
||||
betaPreRelease?: number;
|
||||
};
|
||||
|
||||
export namespace NpmModuleVersion {
|
||||
export function parse(versionStr: string): NpmModuleVersion {
|
||||
const match = versionStr.match(/^([0-9]+)\.([0-9]+)\.([0-9]+)(?:-beta.([0-9]+))?/);
|
||||
|
||||
if (!match) {
|
||||
throw new Error(`${versionStr} is not a valid NPM version`);
|
||||
}
|
||||
|
||||
return {
|
||||
"major": parseInt(match[1]),
|
||||
"minor": parseInt(match[2]),
|
||||
"patch": parseInt(match[3]),
|
||||
...(() => {
|
||||
const str = match[4];
|
||||
return str === undefined ? {} : { "betaPreRelease": parseInt(str) };
|
||||
})()
|
||||
};
|
||||
}
|
||||
|
||||
export function stringify(v: NpmModuleVersion) {
|
||||
return `${v.major}.${v.minor}.${v.patch}${v.betaPreRelease === undefined ? "" : `-beta.${v.betaPreRelease}`}`;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* v1 < v2 => -1
|
||||
* v1 === v2 => 0
|
||||
* v1 > v2 => 1
|
||||
*
|
||||
*/
|
||||
export function compare(v1: NpmModuleVersion, v2: NpmModuleVersion): -1 | 0 | 1 {
|
||||
const sign = (diff: number): -1 | 0 | 1 => (diff === 0 ? 0 : diff < 0 ? -1 : 1);
|
||||
const noUndefined = (n: number | undefined) => n ?? Infinity;
|
||||
|
||||
for (const level of ["major", "minor", "patch", "betaPreRelease"] as const) {
|
||||
if (noUndefined(v1[level]) !== noUndefined(v2[level])) {
|
||||
return sign(noUndefined(v1[level]) - noUndefined(v2[level]));
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
console.log(compare(parse("3.0.0-beta.3"), parse("3.0.0")) === -1 )
|
||||
console.log(compare(parse("3.0.0-beta.3"), parse("3.0.0-beta.4")) === -1 )
|
||||
console.log(compare(parse("3.0.0-beta.3"), parse("4.0.0")) === -1 )
|
||||
*/
|
||||
|
||||
export function bumpType(params: { versionBehindStr: string; versionAheadStr: string }): "major" | "minor" | "patch" | "betaPreRelease" | "same" {
|
||||
const versionAhead = parse(params.versionAheadStr);
|
||||
const versionBehind = parse(params.versionBehindStr);
|
||||
|
||||
if (compare(versionBehind, versionAhead) === 1) {
|
||||
throw new Error(`Version regression ${versionBehind} -> ${versionAhead}`);
|
||||
}
|
||||
|
||||
for (const level of ["major", "minor", "patch", "betaPreRelease"] as const) {
|
||||
if (versionBehind[level] !== versionAhead[level]) {
|
||||
return level;
|
||||
}
|
||||
}
|
||||
|
||||
return "same";
|
||||
}
|
||||
}
|
@ -3,35 +3,25 @@ import * as path from "path";
|
||||
|
||||
/** List all files in a given directory return paths relative to the dir_path */
|
||||
export const crawl = (() => {
|
||||
|
||||
const crawlRec = (dir_path: string, paths: string[]) => {
|
||||
|
||||
for (const file_name of fs.readdirSync(dir_path)) {
|
||||
|
||||
const file_path = path.join(dir_path, file_name);
|
||||
|
||||
if (fs.lstatSync(file_path).isDirectory()) {
|
||||
|
||||
crawlRec(file_path, paths);
|
||||
|
||||
continue;
|
||||
|
||||
}
|
||||
|
||||
paths.push(file_path);
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
return function crawl(dir_path: string): string[] {
|
||||
|
||||
const paths: string[] = [];
|
||||
|
||||
crawlRec(dir_path, paths);
|
||||
|
||||
return paths.map(file_path => path.relative(dir_path, file_path));
|
||||
|
||||
}
|
||||
|
||||
})();
|
||||
};
|
||||
})();
|
||||
|
@ -1,24 +1,75 @@
|
||||
import { basename as pathBasename, join as pathJoin } from "path";
|
||||
import { execSync } from "child_process";
|
||||
import * as fs from "fs";
|
||||
import { transformCodebase } from "./transformCodebase";
|
||||
import { rm, rm_rf } from "./rm";
|
||||
import * as crypto from "crypto";
|
||||
|
||||
import { basename as pathBasename } from "path";
|
||||
import child_process from "child_process";
|
||||
import fs from "fs";
|
||||
/** assert url ends with .zip */
|
||||
export function downloadAndUnzip(params: { url: string; destDirPath: string; pathOfDirToExtractInArchive?: string; cacheDirPath: string }) {
|
||||
const { url, destDirPath, pathOfDirToExtractInArchive, cacheDirPath } = params;
|
||||
|
||||
export function downloadAndUnzip(
|
||||
params: {
|
||||
url: string;
|
||||
destDirPath: string;
|
||||
const extractDirPath = pathJoin(
|
||||
cacheDirPath,
|
||||
`_${crypto.createHash("sha256").update(JSON.stringify({ url, pathOfDirToExtractInArchive })).digest("hex").substring(0, 15)}`
|
||||
);
|
||||
|
||||
fs.mkdirSync(cacheDirPath, { "recursive": true });
|
||||
|
||||
const { readIsSuccessByExtractDirPath, writeIsSuccessByExtractDirPath } = (() => {
|
||||
const filePath = pathJoin(cacheDirPath, "isSuccessByExtractDirPath.json");
|
||||
|
||||
type IsSuccessByExtractDirPath = Record<string, boolean | undefined>;
|
||||
|
||||
function readIsSuccessByExtractDirPath(): IsSuccessByExtractDirPath {
|
||||
if (!fs.existsSync(filePath)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return JSON.parse(fs.readFileSync(filePath).toString("utf8"));
|
||||
}
|
||||
|
||||
function writeIsSuccessByExtractDirPath(isSuccessByExtractDirPath: IsSuccessByExtractDirPath): void {
|
||||
fs.writeFileSync(filePath, Buffer.from(JSON.stringify(isSuccessByExtractDirPath, null, 2), "utf8"));
|
||||
}
|
||||
|
||||
return { readIsSuccessByExtractDirPath, writeIsSuccessByExtractDirPath };
|
||||
})();
|
||||
|
||||
downloadAndUnzip: {
|
||||
const isSuccessByExtractDirPath = readIsSuccessByExtractDirPath();
|
||||
|
||||
if (isSuccessByExtractDirPath[extractDirPath]) {
|
||||
break downloadAndUnzip;
|
||||
}
|
||||
|
||||
writeIsSuccessByExtractDirPath({
|
||||
...isSuccessByExtractDirPath,
|
||||
[extractDirPath]: false
|
||||
});
|
||||
|
||||
rm_rf(extractDirPath);
|
||||
|
||||
fs.mkdirSync(extractDirPath);
|
||||
|
||||
const zipFileBasename = pathBasename(url);
|
||||
|
||||
execSync(`curl -L ${url} -o ${zipFileBasename}`, { "cwd": extractDirPath });
|
||||
|
||||
execSync(`unzip -o ${zipFileBasename}${pathOfDirToExtractInArchive === undefined ? "" : ` "${pathOfDirToExtractInArchive}/**/*"`}`, {
|
||||
"cwd": extractDirPath
|
||||
});
|
||||
|
||||
rm(zipFileBasename, { "cwd": extractDirPath });
|
||||
|
||||
writeIsSuccessByExtractDirPath({
|
||||
...isSuccessByExtractDirPath,
|
||||
[extractDirPath]: true
|
||||
});
|
||||
}
|
||||
) {
|
||||
|
||||
const { url, destDirPath } = params;
|
||||
|
||||
fs.mkdirSync(destDirPath, { "recursive": true });
|
||||
|
||||
console.log({ url, destDirPath });
|
||||
|
||||
[
|
||||
`wget ${url}`,
|
||||
...["unzip", "rm"].map(prg => `${prg} ${pathBasename(url)}`),
|
||||
].forEach(cmd => child_process.execSync(cmd, { "cwd": destDirPath }));
|
||||
|
||||
}
|
||||
transformCodebase({
|
||||
"srcDirPath": pathOfDirToExtractInArchive === undefined ? extractDirPath : pathJoin(extractDirPath, pathOfDirToExtractInArchive),
|
||||
destDirPath
|
||||
});
|
||||
}
|
||||
|
@ -16,4 +16,4 @@ export function getProjectRoot(): string {
|
||||
}
|
||||
|
||||
return (result = getProjectRootRec(__dirname));
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,10 @@
|
||||
import { getProjectRoot } from "./getProjectRoot";
|
||||
import { join as pathJoin } from "path";
|
||||
import * as child_process from "child_process";
|
||||
import * as fs from "fs";
|
||||
|
||||
import { getProjectRoot } from "./getProjectRoot";
|
||||
import { join as pathJoin } from "path";
|
||||
import child_process from "child_process";
|
||||
|
||||
Object.entries<string>(require(pathJoin(getProjectRoot(), "package.json"))["bin"])
|
||||
.forEach(([, scriptPath]) => child_process.execSync(`chmod +x ${scriptPath}`, { "cwd": getProjectRoot() }));
|
||||
|
||||
Object.entries<string>(JSON.parse(fs.readFileSync(pathJoin(getProjectRoot(), "package.json")).toString("utf8"))["bin"]).forEach(([, scriptPath]) =>
|
||||
child_process.execSync(`chmod +x ${scriptPath}`, {
|
||||
"cwd": getProjectRoot()
|
||||
})
|
||||
);
|
||||
|
7
src/bin/tools/isInside.ts
Normal file
7
src/bin/tools/isInside.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import { relative as pathRelative } from "path";
|
||||
|
||||
export function isInside(params: { dirPath: string; filePath: string }) {
|
||||
const { dirPath, filePath } = params;
|
||||
|
||||
return !pathRelative(dirPath, filePath).startsWith("..");
|
||||
}
|
40
src/bin/tools/octokit-addons/getLatestsSemVersionedTag.ts
Normal file
40
src/bin/tools/octokit-addons/getLatestsSemVersionedTag.ts
Normal file
@ -0,0 +1,40 @@
|
||||
import { listTagsFactory } from "./listTags";
|
||||
import type { Octokit } from "@octokit/rest";
|
||||
import { NpmModuleVersion } from "../NpmModuleVersion";
|
||||
|
||||
export function getLatestsSemVersionedTagFactory(params: { octokit: Octokit }) {
|
||||
const { octokit } = params;
|
||||
|
||||
async function getLatestsSemVersionedTag(params: { owner: string; repo: string; doIgnoreBeta: boolean; count: number }): Promise<
|
||||
{
|
||||
tag: string;
|
||||
version: NpmModuleVersion;
|
||||
}[]
|
||||
> {
|
||||
const { owner, repo, doIgnoreBeta, count } = params;
|
||||
|
||||
const semVersionedTags: { tag: string; version: NpmModuleVersion }[] = [];
|
||||
|
||||
const { listTags } = listTagsFactory({ octokit });
|
||||
|
||||
for await (const tag of listTags({ owner, repo })) {
|
||||
let version: NpmModuleVersion;
|
||||
|
||||
try {
|
||||
version = NpmModuleVersion.parse(tag.replace(/^[vV]?/, ""));
|
||||
} catch {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (doIgnoreBeta && version.betaPreRelease !== undefined) {
|
||||
continue;
|
||||
}
|
||||
|
||||
semVersionedTags.push({ tag, version });
|
||||
}
|
||||
|
||||
return semVersionedTags.sort(({ version: vX }, { version: vY }) => NpmModuleVersion.compare(vY, vX)).slice(0, count);
|
||||
}
|
||||
|
||||
return { getLatestsSemVersionedTag };
|
||||
}
|
49
src/bin/tools/octokit-addons/listTags.ts
Normal file
49
src/bin/tools/octokit-addons/listTags.ts
Normal file
@ -0,0 +1,49 @@
|
||||
import type { Octokit } from "@octokit/rest";
|
||||
|
||||
const per_page = 99;
|
||||
|
||||
export function listTagsFactory(params: { octokit: Octokit }) {
|
||||
const { octokit } = params;
|
||||
|
||||
const octokit_repo_listTags = async (params: { owner: string; repo: string; per_page: number; page: number }) => {
|
||||
return octokit.repos.listTags(params);
|
||||
};
|
||||
|
||||
async function* listTags(params: { owner: string; repo: string }): AsyncGenerator<string> {
|
||||
const { owner, repo } = params;
|
||||
|
||||
let page = 1;
|
||||
|
||||
while (true) {
|
||||
const resp = await octokit_repo_listTags({
|
||||
owner,
|
||||
repo,
|
||||
per_page,
|
||||
"page": page++
|
||||
});
|
||||
|
||||
for (const branch of resp.data.map(({ name }) => name)) {
|
||||
yield branch;
|
||||
}
|
||||
|
||||
if (resp.data.length < 99) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns the same "latest" tag as deno.land/x, not actually the latest though */
|
||||
async function getLatestTag(params: { owner: string; repo: string }): Promise<string | undefined> {
|
||||
const { owner, repo } = params;
|
||||
|
||||
const itRes = await listTags({ owner, repo }).next();
|
||||
|
||||
if (itRes.done) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return itRes.value;
|
||||
}
|
||||
|
||||
return { listTags, getLatestTag };
|
||||
}
|
6
src/bin/tools/pathJoin.ts
Normal file
6
src/bin/tools/pathJoin.ts
Normal file
@ -0,0 +1,6 @@
|
||||
export function pathJoin(...path: string[]): string {
|
||||
return path
|
||||
.map((part, i) => (i === 0 ? part : part.replace(/^\/+/, "")))
|
||||
.map((part, i) => (i === path.length - 1 ? part : part.replace(/\/+$/, "")))
|
||||
.join("/");
|
||||
}
|
31
src/bin/tools/rm.ts
Normal file
31
src/bin/tools/rm.ts
Normal file
@ -0,0 +1,31 @@
|
||||
import { execSync } from "child_process";
|
||||
|
||||
function rmInternal(params: { pathToRemove: string; args: string | undefined; cwd: string | undefined }) {
|
||||
const { pathToRemove, args, cwd } = params;
|
||||
|
||||
execSync(`rm ${args ? `-${args} ` : ""}${pathToRemove.replace(/ /g, "\\ ")}`, cwd !== undefined ? { cwd } : undefined);
|
||||
}
|
||||
|
||||
export function rm(pathToRemove: string, options?: { cwd: string }) {
|
||||
rmInternal({
|
||||
pathToRemove,
|
||||
"args": undefined,
|
||||
"cwd": options?.cwd
|
||||
});
|
||||
}
|
||||
|
||||
export function rm_r(pathToRemove: string, options?: { cwd: string }) {
|
||||
rmInternal({
|
||||
pathToRemove,
|
||||
"args": "r",
|
||||
"cwd": options?.cwd
|
||||
});
|
||||
}
|
||||
|
||||
export function rm_rf(pathToRemove: string, options?: { cwd: string }) {
|
||||
rmInternal({
|
||||
pathToRemove,
|
||||
"args": "rf",
|
||||
"cwd": options?.cwd
|
||||
});
|
||||
}
|
@ -1,61 +1,46 @@
|
||||
|
||||
|
||||
import * as fs from "fs";
|
||||
import * as path from "path";
|
||||
import { crawl } from "./crawl";
|
||||
import { id } from "tsafe/id";
|
||||
|
||||
type TransformSourceCode = (params: { sourceCode: Buffer; filePath: string }) =>
|
||||
| {
|
||||
modifiedSourceCode: Buffer;
|
||||
newFileName?: string;
|
||||
}
|
||||
| undefined;
|
||||
|
||||
/** Apply a transformation function to every file of directory */
|
||||
export function transformCodebase(
|
||||
params: {
|
||||
srcDirPath: string;
|
||||
destDirPath: string;
|
||||
transformSourceCodeString: (params: {
|
||||
sourceCode: Buffer;
|
||||
filePath: string;
|
||||
}) => {
|
||||
modifiedSourceCode: Buffer;
|
||||
newFileName?: string;
|
||||
} | undefined;
|
||||
}
|
||||
) {
|
||||
|
||||
const { srcDirPath, destDirPath, transformSourceCodeString } = params;
|
||||
export function transformCodebase(params: { srcDirPath: string; destDirPath: string; transformSourceCode?: TransformSourceCode }) {
|
||||
const {
|
||||
srcDirPath,
|
||||
destDirPath,
|
||||
transformSourceCode = id<TransformSourceCode>(({ sourceCode }) => ({
|
||||
"modifiedSourceCode": sourceCode
|
||||
}))
|
||||
} = params;
|
||||
|
||||
for (const file_relative_path of crawl(srcDirPath)) {
|
||||
|
||||
const filePath = path.join(srcDirPath, file_relative_path);
|
||||
|
||||
const transformSourceCodeStringResult = transformSourceCodeString({
|
||||
const transformSourceCodeResult = transformSourceCode({
|
||||
"sourceCode": fs.readFileSync(filePath),
|
||||
"filePath": path.join(srcDirPath, file_relative_path)
|
||||
});
|
||||
|
||||
if (transformSourceCodeStringResult === undefined) {
|
||||
if (transformSourceCodeResult === undefined) {
|
||||
continue;
|
||||
}
|
||||
|
||||
fs.mkdirSync(
|
||||
path.dirname(
|
||||
path.join(
|
||||
destDirPath,
|
||||
file_relative_path
|
||||
)
|
||||
),
|
||||
{ "recursive": true }
|
||||
);
|
||||
fs.mkdirSync(path.dirname(path.join(destDirPath, file_relative_path)), {
|
||||
"recursive": true
|
||||
});
|
||||
|
||||
const { newFileName, modifiedSourceCode } = transformSourceCodeStringResult;
|
||||
const { newFileName, modifiedSourceCode } = transformSourceCodeResult;
|
||||
|
||||
fs.writeFileSync(
|
||||
path.join(
|
||||
path.dirname(path.join(destDirPath, file_relative_path)),
|
||||
newFileName ?? path.basename(file_relative_path)
|
||||
),
|
||||
path.join(path.dirname(path.join(destDirPath, file_relative_path)), newFileName ?? path.basename(file_relative_path)),
|
||||
modifiedSourceCode
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
11
src/bin/tsconfig.json
Normal file
11
src/bin/tsconfig.json
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
"extends": "../../tsproject.json",
|
||||
"compilerOptions": {
|
||||
"module": "CommonJS",
|
||||
"target": "ES5",
|
||||
"esModuleInterop": true,
|
||||
"lib": ["es2015", "DOM", "ES2019.Object"],
|
||||
"outDir": "../../dist/bin",
|
||||
"rootDir": "."
|
||||
}
|
||||
}
|
@ -1,211 +0,0 @@
|
||||
|
||||
import { useState } from "react";
|
||||
import { useKeycloakThemeTranslation } from "./i18n/useKeycloakTranslation";
|
||||
import { keycloakPagesContext } from "./keycloakFtlValues";
|
||||
import { assert } from "evt/tools/typeSafety/assert";
|
||||
import { cx } from "tss-react";
|
||||
import { useKeycloakLanguage, AvailableLanguages } from "./i18n/useKeycloakLanguage";
|
||||
import { getLanguageLabel } from "./i18n/getLanguageLabel";
|
||||
import { useCallbackFactory } from "powerhooks";
|
||||
|
||||
export type Props = {
|
||||
displayInfo?: boolean;
|
||||
displayMessage: boolean;
|
||||
displayRequiredFields: boolean;
|
||||
displayWide: boolean;
|
||||
showAnotherWayIfPresent: boolean;
|
||||
};
|
||||
|
||||
export function Template(props: Props) {
|
||||
|
||||
const {
|
||||
displayInfo = false,
|
||||
displayMessage = true,
|
||||
displayRequiredFields = false,
|
||||
displayWide = false,
|
||||
showAnotherWayIfPresent = true
|
||||
} = props;
|
||||
|
||||
const { t } = useKeycloakThemeTranslation();
|
||||
|
||||
const { keycloakLanguage, setKeycloakLanguage } = useKeycloakLanguage();
|
||||
|
||||
const onChangeLanguageClickFactory = useCallbackFactory(
|
||||
([languageTag]: [AvailableLanguages]) =>
|
||||
setKeycloakLanguage(languageTag)
|
||||
);
|
||||
|
||||
const [{ realm, locale, auth }] = useState(() => {
|
||||
|
||||
assert(keycloakPagesContext !== undefined);
|
||||
|
||||
return keycloakPagesContext;
|
||||
|
||||
});
|
||||
//<div className="kcBodyClass"></div>
|
||||
|
||||
return (
|
||||
|
||||
<div className="kcLoginClass">
|
||||
<div id="kc-header" className="kcHeaderClass">
|
||||
<div id="kc-header-wrapper" className="kcHeaderWrapperClass">
|
||||
{t("loginTitleHtml", realm.displayNameHtml)}
|
||||
</div>
|
||||
</div>
|
||||
<div className={cx("kcFormCardClass", displayWide && "kcFormCardAccountClass")}>
|
||||
<header className="kcFormHeaderClass">
|
||||
|
||||
{
|
||||
(
|
||||
realm.internationalizationEnabled &&
|
||||
(assert(locale !== undefined), true) &&
|
||||
locale.supported.length > 1
|
||||
) && (
|
||||
<div id="kc-locale">
|
||||
<div id="kc-locale-wrapper" className="kcLocaleWrapperClass">
|
||||
<div className="kc-dropdown" id="kc-locale-dropdown">
|
||||
<a href="#" id="kc-current-locale-link">
|
||||
{getLanguageLabel(keycloakLanguage)}
|
||||
</a>
|
||||
<ul>
|
||||
{
|
||||
locale.supported.map(
|
||||
({ languageTag }) =>
|
||||
<li className="kc-dropdown-item">
|
||||
<a href="#" onClick={onChangeLanguageClickFactory(languageTag)}>
|
||||
{getLanguageLabel(languageTag)}
|
||||
</a>
|
||||
|
||||
</li>
|
||||
)
|
||||
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
{
|
||||
(
|
||||
auth !== undefined &&
|
||||
auth.showUsername &&
|
||||
!auth.showResetCredentials
|
||||
) ?
|
||||
(
|
||||
displayRequiredFields ?
|
||||
(
|
||||
|
||||
<div className="kcContentWrapperClass">
|
||||
<div className="kcLabelWrapperClass subtitle">
|
||||
<span className="subtitle">
|
||||
<span className="required">*</span>
|
||||
{t("requiredFields")}
|
||||
</span>
|
||||
</div>
|
||||
<div className="col-md-10">
|
||||
<h1 id="kc-page-title"><#nested "header"></h1>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
)
|
||||
:
|
||||
(
|
||||
|
||||
<h1 id="kc-page-title"><#nested "header"></h1>
|
||||
|
||||
)
|
||||
)
|
||||
:
|
||||
(
|
||||
displayRequiredFields ? (
|
||||
<div className="kcContentWrapperClass">
|
||||
<div class="${properties.kcLabelWrapperClass!} subtitle">
|
||||
<span class="subtitle"><span class="required">*</span> ${msg("requiredFields")}</span>
|
||||
</div>
|
||||
<div class="col-md-10">
|
||||
<#nested "show-username">
|
||||
<div class="${properties.kcFormGroupClass!}">
|
||||
<div id="kc-username">
|
||||
<label id="kc-attempted-username">${auth.attemptedUsername}</label>
|
||||
<a id="reset-login" href="${url.loginRestartFlowUrl}">
|
||||
<div class="kc-login-tooltip">
|
||||
<i class="${properties.kcResetFlowIcon!}"></i>
|
||||
<span class="kc-tooltip-text">${msg("restartLoginTooltip")}</span>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
|
||||
<#nested "show-username">
|
||||
<div class="${properties.kcFormGroupClass!}">
|
||||
<div id="kc-username">
|
||||
<label id="kc-attempted-username">${auth.attemptedUsername}</label>
|
||||
<a id="reset-login" href="${url.loginRestartFlowUrl}">
|
||||
<div class="kc-login-tooltip">
|
||||
<i class="${properties.kcResetFlowIcon!}"></i>
|
||||
<span class="kc-tooltip-text">${msg("restartLoginTooltip")}</span>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
</header>
|
||||
<div id="kc-content">
|
||||
<div id="kc-content-wrapper">
|
||||
|
||||
<#-- App-initiated actions should not see warning messages about the need to complete the action -->
|
||||
<#-- during login. -->
|
||||
<#if displayMessage && message?has_content && (message.type != 'warning' || !isAppInitiatedAction??)>
|
||||
<div class="alert alert-${message.type}">
|
||||
<#if message.<span class="${properties.kcFeedbackSuccessIcon!}"></span></#if>
|
||||
<#if message.<span class="${properties.kcFeedbackWarningIcon!}"></span></#if>
|
||||
<#if message.<span class="${properties.kcFeedbackErrorIcon!}"></span></#if>
|
||||
<#if message.<span class="${properties.kcFeedbackInfoIcon!}"></span></#if>
|
||||
<span class="kc-feedback-text">${kcSanitize(message.summary) ? no_esc}</span>
|
||||
</div >
|
||||
</#if >
|
||||
|
||||
<#nested "form" >
|
||||
|
||||
<#if auth?has_content && auth.showTryAnotherWayLink() && showAnotherWayIfPresent >
|
||||
<form id="kc-select-try-another-way-form" action="${url.loginAction}" method="post" <#if displayWide>class="${properties.kcContentWrapperClass!}"</#if> >
|
||||
<div <#if displayWide>class="${properties.kcFormSocialAccountContentClass!} ${properties.kcFormSocialAccountClass!}"</#if> >
|
||||
<div class="${properties.kcFormGroupClass!}">
|
||||
<input type="hidden" name="tryAnotherWay" value="on" />
|
||||
<a href="#" id="try-another-way" onclick="document.forms['kc-select-try-another-way-form'].submit();return false;">${msg("doTryAnotherWay")}</a>
|
||||
</div>
|
||||
</div >
|
||||
</form >
|
||||
</#if >
|
||||
|
||||
<#if displayInfo>
|
||||
<div id="kc-info" class="${properties.kcSignUpClass!}">
|
||||
<div id="kc-info-wrapper" class="${properties.kcInfoAreaWrapperClass!}">
|
||||
<#nested "info">
|
||||
</div>
|
||||
</div>
|
||||
</#if>
|
||||
</div >
|
||||
</div >
|
||||
|
||||
</div >
|
||||
</div >
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
);
|
||||
|
||||
}
|
34
src/lib/components/Error.tsx
Normal file
34
src/lib/components/Error.tsx
Normal file
@ -0,0 +1,34 @@
|
||||
import React, { memo } from "react";
|
||||
import Template from "./Template";
|
||||
import type { KcProps } from "./KcProps";
|
||||
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
||||
import type { I18n } from "../i18n";
|
||||
|
||||
const Error = memo(({ kcContext, i18n, ...props }: { kcContext: KcContextBase.Error; i18n: I18n } & KcProps) => {
|
||||
const { message, client } = kcContext;
|
||||
|
||||
const { msg } = i18n;
|
||||
|
||||
return (
|
||||
<Template
|
||||
{...{ kcContext, i18n, ...props }}
|
||||
doFetchDefaultThemeResources={true}
|
||||
displayMessage={false}
|
||||
headerNode={msg("errorTitle")}
|
||||
formNode={
|
||||
<div id="kc-error-message">
|
||||
<p className="instruction">{message.summary}</p>
|
||||
{client !== undefined && client.baseUrl !== undefined && (
|
||||
<p>
|
||||
<a id="backToApplication" href={client.baseUrl}>
|
||||
{msg("backToApplication")}
|
||||
</a>
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
export default Error;
|
51
src/lib/components/Info.tsx
Normal file
51
src/lib/components/Info.tsx
Normal file
@ -0,0 +1,51 @@
|
||||
import React, { memo } from "react";
|
||||
import Template from "./Template";
|
||||
import type { KcProps } from "./KcProps";
|
||||
import { assert } from "../tools/assert";
|
||||
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
||||
import type { I18n } from "../i18n";
|
||||
|
||||
const Info = memo(({ kcContext, i18n, ...props }: { kcContext: KcContextBase.Info; i18n: I18n } & KcProps) => {
|
||||
const { msgStr, msg } = i18n;
|
||||
|
||||
assert(kcContext.message !== undefined);
|
||||
|
||||
const { messageHeader, message, requiredActions, skipLink, pageRedirectUri, actionUri, client } = kcContext;
|
||||
|
||||
return (
|
||||
<Template
|
||||
{...{ kcContext, i18n, ...props }}
|
||||
doFetchDefaultThemeResources={true}
|
||||
displayMessage={false}
|
||||
headerNode={messageHeader !== undefined ? <>{messageHeader}</> : <>{message.summary}</>}
|
||||
formNode={
|
||||
<div id="kc-info-message">
|
||||
<p className="instruction">
|
||||
{message.summary}
|
||||
|
||||
{requiredActions !== undefined && (
|
||||
<b>{requiredActions.map(requiredAction => msgStr(`requiredAction.${requiredAction}` as const)).join(",")}</b>
|
||||
)}
|
||||
</p>
|
||||
{!skipLink && pageRedirectUri !== undefined ? (
|
||||
<p>
|
||||
<a href={pageRedirectUri}>{msg("backToApplication")}</a>
|
||||
</p>
|
||||
) : actionUri !== undefined ? (
|
||||
<p>
|
||||
<a href={actionUri}>{msg("proceedWithAction")}</a>
|
||||
</p>
|
||||
) : (
|
||||
client.baseUrl !== undefined && (
|
||||
<p>
|
||||
<a href={client.baseUrl}>{msg("backToApplication")}</a>
|
||||
</p>
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
export default Info;
|
81
src/lib/components/KcApp.tsx
Normal file
81
src/lib/components/KcApp.tsx
Normal file
@ -0,0 +1,81 @@
|
||||
import React, { lazy, memo, Suspense } from "react";
|
||||
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
||||
import type { KcProps } from "./KcProps";
|
||||
import { __unsafe_useI18n as useI18n } from "../i18n";
|
||||
import type { I18n } from "../i18n";
|
||||
|
||||
const Login = lazy(() => import("./Login"));
|
||||
const Register = lazy(() => import("./Register"));
|
||||
const RegisterUserProfile = lazy(() => import("./RegisterUserProfile"));
|
||||
const Info = lazy(() => import("./Info"));
|
||||
const Error = lazy(() => import("./Error"));
|
||||
const LoginResetPassword = lazy(() => import("./LoginResetPassword"));
|
||||
const LoginVerifyEmail = lazy(() => import("./LoginVerifyEmail"));
|
||||
const Terms = lazy(() => import("./Terms"));
|
||||
const LoginOtp = lazy(() => import("./LoginOtp"));
|
||||
const LoginUpdatePassword = lazy(() => import("./LoginUpdatePassword"));
|
||||
const LoginUpdateProfile = lazy(() => import("./LoginUpdateProfile"));
|
||||
const LoginIdpLinkConfirm = lazy(() => import("./LoginIdpLinkConfirm"));
|
||||
const LoginPageExpired = lazy(() => import("./LoginPageExpired"));
|
||||
const LoginIdpLinkEmail = lazy(() => import("./LoginIdpLinkEmail"));
|
||||
const LoginConfigTotp = lazy(() => import("./LoginConfigTotp"));
|
||||
const LogoutConfirm = lazy(() => import("./LogoutConfirm"));
|
||||
|
||||
const KcApp = memo(({ kcContext, i18n: userProvidedI18n, ...props }: { kcContext: KcContextBase; i18n?: I18n } & KcProps) => {
|
||||
const i18n = (function useClosure() {
|
||||
const i18n = useI18n({
|
||||
kcContext,
|
||||
"extraMessages": {},
|
||||
"doSkip": userProvidedI18n !== undefined
|
||||
});
|
||||
|
||||
return userProvidedI18n ?? i18n;
|
||||
})();
|
||||
|
||||
if (i18n === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Suspense>
|
||||
{(() => {
|
||||
switch (kcContext.pageId) {
|
||||
case "login.ftl":
|
||||
return <Login {...{ kcContext, i18n, ...props }} />;
|
||||
case "register.ftl":
|
||||
return <Register {...{ kcContext, i18n, ...props }} />;
|
||||
case "register-user-profile.ftl":
|
||||
return <RegisterUserProfile {...{ kcContext, i18n, ...props }} />;
|
||||
case "info.ftl":
|
||||
return <Info {...{ kcContext, i18n, ...props }} />;
|
||||
case "error.ftl":
|
||||
return <Error {...{ kcContext, i18n, ...props }} />;
|
||||
case "login-reset-password.ftl":
|
||||
return <LoginResetPassword {...{ kcContext, i18n, ...props }} />;
|
||||
case "login-verify-email.ftl":
|
||||
return <LoginVerifyEmail {...{ kcContext, i18n, ...props }} />;
|
||||
case "terms.ftl":
|
||||
return <Terms {...{ kcContext, i18n, ...props }} />;
|
||||
case "login-otp.ftl":
|
||||
return <LoginOtp {...{ kcContext, i18n, ...props }} />;
|
||||
case "login-update-password.ftl":
|
||||
return <LoginUpdatePassword {...{ kcContext, i18n, ...props }} />;
|
||||
case "login-update-profile.ftl":
|
||||
return <LoginUpdateProfile {...{ kcContext, i18n, ...props }} />;
|
||||
case "login-idp-link-confirm.ftl":
|
||||
return <LoginIdpLinkConfirm {...{ kcContext, i18n, ...props }} />;
|
||||
case "login-idp-link-email.ftl":
|
||||
return <LoginIdpLinkEmail {...{ kcContext, i18n, ...props }} />;
|
||||
case "login-page-expired.ftl":
|
||||
return <LoginPageExpired {...{ kcContext, i18n, ...props }} />;
|
||||
case "login-config-totp.ftl":
|
||||
return <LoginConfigTotp {...{ kcContext, i18n, ...props }} />;
|
||||
case "logout-confirm.ftl":
|
||||
return <LogoutConfirm {...{ kcContext, i18n, ...props }} />;
|
||||
}
|
||||
})()}
|
||||
</Suspense>
|
||||
);
|
||||
});
|
||||
|
||||
export default KcApp;
|
203
src/lib/components/KcProps.ts
Normal file
203
src/lib/components/KcProps.ts
Normal file
@ -0,0 +1,203 @@
|
||||
import { allPropertiesValuesToUndefined } from "../tools/allPropertiesValuesToUndefined";
|
||||
import { assert } from "tsafe/assert";
|
||||
|
||||
/** Class names can be provided as an array or separated by whitespace */
|
||||
export type KcPropsGeneric<CssClasses extends string> = {
|
||||
[key in CssClasses]: readonly string[] | string | undefined;
|
||||
};
|
||||
|
||||
export type KcTemplateClassKey =
|
||||
| "stylesCommon"
|
||||
| "styles"
|
||||
| "scripts"
|
||||
| "kcHtmlClass"
|
||||
| "kcLoginClass"
|
||||
| "kcHeaderClass"
|
||||
| "kcHeaderWrapperClass"
|
||||
| "kcFormCardClass"
|
||||
| "kcFormCardAccountClass"
|
||||
| "kcFormHeaderClass"
|
||||
| "kcLocaleWrapperClass"
|
||||
| "kcContentWrapperClass"
|
||||
| "kcLabelWrapperClass"
|
||||
| "kcFormGroupClass"
|
||||
| "kcResetFlowIcon"
|
||||
| "kcFeedbackSuccessIcon"
|
||||
| "kcFeedbackWarningIcon"
|
||||
| "kcFeedbackErrorIcon"
|
||||
| "kcFeedbackInfoIcon"
|
||||
| "kcFormSocialAccountContentClass"
|
||||
| "kcFormSocialAccountClass"
|
||||
| "kcSignUpClass"
|
||||
| "kcInfoAreaWrapperClass";
|
||||
|
||||
export type KcTemplateProps = KcPropsGeneric<KcTemplateClassKey>;
|
||||
|
||||
export const defaultKcTemplateProps = {
|
||||
"stylesCommon": [
|
||||
"node_modules/patternfly/dist/css/patternfly.min.css",
|
||||
"node_modules/patternfly/dist/css/patternfly-additions.min.css",
|
||||
"lib/zocial/zocial.css"
|
||||
],
|
||||
"styles": ["css/login.css"],
|
||||
"scripts": [],
|
||||
"kcHtmlClass": ["login-pf"],
|
||||
"kcLoginClass": ["login-pf-page"],
|
||||
"kcContentWrapperClass": ["row"],
|
||||
"kcHeaderClass": ["login-pf-page-header"],
|
||||
"kcHeaderWrapperClass": [],
|
||||
"kcFormCardClass": ["card-pf"],
|
||||
"kcFormCardAccountClass": ["login-pf-accounts"],
|
||||
"kcFormSocialAccountClass": ["login-pf-social-section"],
|
||||
"kcFormSocialAccountContentClass": ["col-xs-12", "col-sm-6"],
|
||||
"kcFormHeaderClass": ["login-pf-header"],
|
||||
"kcLocaleWrapperClass": [],
|
||||
"kcFeedbackErrorIcon": ["pficon", "pficon-error-circle-o"],
|
||||
"kcFeedbackWarningIcon": ["pficon", "pficon-warning-triangle-o"],
|
||||
"kcFeedbackSuccessIcon": ["pficon", "pficon-ok"],
|
||||
"kcFeedbackInfoIcon": ["pficon", "pficon-info"],
|
||||
"kcResetFlowIcon": ["pficon", "pficon-arrow fa-2x"],
|
||||
"kcFormGroupClass": ["form-group"],
|
||||
"kcLabelWrapperClass": ["col-xs-12", "col-sm-12", "col-md-12", "col-lg-12"],
|
||||
"kcSignUpClass": ["login-pf-signup"],
|
||||
"kcInfoAreaWrapperClass": []
|
||||
} as const;
|
||||
|
||||
assert<typeof defaultKcTemplateProps extends KcTemplateProps ? true : false>();
|
||||
|
||||
/** Tu use if you don't want any default */
|
||||
export const allClearKcTemplateProps = allPropertiesValuesToUndefined(defaultKcTemplateProps);
|
||||
|
||||
assert<typeof allClearKcTemplateProps extends KcTemplateProps ? true : false>();
|
||||
|
||||
export type KcProps = KcPropsGeneric<
|
||||
| KcTemplateClassKey
|
||||
| "kcLogoLink"
|
||||
| "kcLogoClass"
|
||||
| "kcContainerClass"
|
||||
| "kcContentClass"
|
||||
| "kcFeedbackAreaClass"
|
||||
| "kcLocaleClass"
|
||||
| "kcAlertIconClasserror"
|
||||
| "kcFormAreaClass"
|
||||
| "kcFormSocialAccountListClass"
|
||||
| "kcFormSocialAccountDoubleListClass"
|
||||
| "kcFormSocialAccountListLinkClass"
|
||||
| "kcWebAuthnKeyIcon"
|
||||
| "kcFormClass"
|
||||
| "kcFormGroupErrorClass"
|
||||
| "kcLabelClass"
|
||||
| "kcInputClass"
|
||||
| "kcInputErrorMessageClass"
|
||||
| "kcInputWrapperClass"
|
||||
| "kcFormOptionsClass"
|
||||
| "kcFormButtonsClass"
|
||||
| "kcFormSettingClass"
|
||||
| "kcTextareaClass"
|
||||
| "kcInfoAreaClass"
|
||||
| "kcFormGroupHeader"
|
||||
| "kcButtonClass"
|
||||
| "kcButtonPrimaryClass"
|
||||
| "kcButtonDefaultClass"
|
||||
| "kcButtonLargeClass"
|
||||
| "kcButtonBlockClass"
|
||||
| "kcInputLargeClass"
|
||||
| "kcSrOnlyClass"
|
||||
| "kcSelectAuthListClass"
|
||||
| "kcSelectAuthListItemClass"
|
||||
| "kcSelectAuthListItemInfoClass"
|
||||
| "kcSelectAuthListItemLeftClass"
|
||||
| "kcSelectAuthListItemBodyClass"
|
||||
| "kcSelectAuthListItemDescriptionClass"
|
||||
| "kcSelectAuthListItemHeadingClass"
|
||||
| "kcSelectAuthListItemHelpTextClass"
|
||||
| "kcAuthenticatorDefaultClass"
|
||||
| "kcAuthenticatorPasswordClass"
|
||||
| "kcAuthenticatorOTPClass"
|
||||
| "kcAuthenticatorWebAuthnClass"
|
||||
| "kcAuthenticatorWebAuthnPasswordlessClass"
|
||||
| "kcSelectOTPListClass"
|
||||
| "kcSelectOTPListItemClass"
|
||||
| "kcAuthenticatorOtpCircleClass"
|
||||
| "kcSelectOTPItemHeadingClass"
|
||||
| "kcFormOptionsWrapperClass"
|
||||
>;
|
||||
|
||||
export const defaultKcProps = {
|
||||
...defaultKcTemplateProps,
|
||||
"kcLogoLink": "http://www.keycloak.org",
|
||||
"kcLogoClass": "login-pf-brand",
|
||||
"kcContainerClass": "container-fluid",
|
||||
"kcContentClass": ["col-sm-8", "col-sm-offset-2", "col-md-6", "col-md-offset-3", "col-lg-6", "col-lg-offset-3"],
|
||||
"kcFeedbackAreaClass": ["col-md-12"],
|
||||
"kcLocaleClass": ["col-xs-12", "col-sm-1"],
|
||||
"kcAlertIconClasserror": ["pficon", "pficon-error-circle-o"],
|
||||
|
||||
"kcFormAreaClass": ["col-sm-10", "col-sm-offset-1", "col-md-8", "col-md-offset-2", "col-lg-8", "col-lg-offset-2"],
|
||||
"kcFormSocialAccountListClass": ["login-pf-social", "list-unstyled", "login-pf-social-all"],
|
||||
"kcFormSocialAccountDoubleListClass": ["login-pf-social-double-col"],
|
||||
"kcFormSocialAccountListLinkClass": ["login-pf-social-link"],
|
||||
"kcWebAuthnKeyIcon": ["pficon", "pficon-key"],
|
||||
|
||||
"kcFormClass": ["form-horizontal"],
|
||||
"kcFormGroupErrorClass": ["has-error"],
|
||||
"kcLabelClass": ["control-label"],
|
||||
"kcInputClass": ["form-control"],
|
||||
"kcInputErrorMessageClass": ["pf-c-form__helper-text", "pf-m-error", "required", "kc-feedback-text"],
|
||||
"kcInputWrapperClass": ["col-xs-12", "col-sm-12", "col-md-12", "col-lg-12"],
|
||||
"kcFormOptionsClass": ["col-xs-12", "col-sm-12", "col-md-12", "col-lg-12"],
|
||||
"kcFormButtonsClass": ["col-xs-12", "col-sm-12", "col-md-12", "col-lg-12"],
|
||||
"kcFormSettingClass": ["login-pf-settings"],
|
||||
"kcTextareaClass": ["form-control"],
|
||||
|
||||
"kcInfoAreaClass": ["col-xs-12", "col-sm-4", "col-md-4", "col-lg-5", "details"],
|
||||
|
||||
// user-profile grouping
|
||||
"kcFormGroupHeader": ["pf-c-form__group"],
|
||||
|
||||
// css classes for form buttons main class used for all buttons
|
||||
"kcButtonClass": ["btn"],
|
||||
// classes defining priority of the button - primary or default (there is typically only one priority button for the form)
|
||||
"kcButtonPrimaryClass": ["btn-primary"],
|
||||
"kcButtonDefaultClass": ["btn-default"],
|
||||
// classes defining size of the button
|
||||
"kcButtonLargeClass": ["btn-lg"],
|
||||
"kcButtonBlockClass": ["btn-block"],
|
||||
|
||||
// css classes for input
|
||||
"kcInputLargeClass": ["input-lg"],
|
||||
|
||||
// css classes for form accessability
|
||||
"kcSrOnlyClass": ["sr-only"],
|
||||
|
||||
// css classes for select-authenticator form
|
||||
"kcSelectAuthListClass": ["list-group", "list-view-pf"],
|
||||
"kcSelectAuthListItemClass": ["list-group-item", "list-view-pf-stacked"],
|
||||
"kcSelectAuthListItemInfoClass": ["list-view-pf-main-info"],
|
||||
"kcSelectAuthListItemLeftClass": ["list-view-pf-left"],
|
||||
"kcSelectAuthListItemBodyClass": ["list-view-pf-body"],
|
||||
"kcSelectAuthListItemDescriptionClass": ["list-view-pf-description"],
|
||||
"kcSelectAuthListItemHeadingClass": ["list-group-item-heading"],
|
||||
"kcSelectAuthListItemHelpTextClass": ["list-group-item-text"],
|
||||
|
||||
// css classes for the authenticators
|
||||
"kcAuthenticatorDefaultClass": ["fa", "list-view-pf-icon-lg"],
|
||||
"kcAuthenticatorPasswordClass": ["fa", "fa-unlock list-view-pf-icon-lg"],
|
||||
"kcAuthenticatorOTPClass": ["fa", "fa-mobile", "list-view-pf-icon-lg"],
|
||||
"kcAuthenticatorWebAuthnClass": ["fa", "fa-key", "list-view-pf-icon-lg"],
|
||||
"kcAuthenticatorWebAuthnPasswordlessClass": ["fa", "fa-key", "list-view-pf-icon-lg"],
|
||||
|
||||
//css classes for the OTP Login Form
|
||||
"kcSelectOTPListClass": ["card-pf", "card-pf-view", "card-pf-view-select", "card-pf-view-single-select"],
|
||||
"kcSelectOTPListItemClass": ["card-pf-body", "card-pf-top-element"],
|
||||
"kcAuthenticatorOtpCircleClass": ["fa", "fa-mobile", "card-pf-icon-circle"],
|
||||
"kcSelectOTPItemHeadingClass": ["card-pf-title", "text-center"],
|
||||
"kcFormOptionsWrapperClass": []
|
||||
} as const;
|
||||
|
||||
assert<typeof defaultKcProps extends KcProps ? true : false>();
|
||||
|
||||
/** Tu use if you don't want any default */
|
||||
export const allClearKcProps = allPropertiesValuesToUndefined(defaultKcProps);
|
||||
|
||||
assert<typeof allClearKcProps extends KcProps ? true : false>();
|
195
src/lib/components/Login.tsx
Normal file
195
src/lib/components/Login.tsx
Normal file
@ -0,0 +1,195 @@
|
||||
import React, { useState, memo } from "react";
|
||||
import Template from "./Template";
|
||||
import type { KcProps } from "./KcProps";
|
||||
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
||||
import { useCssAndCx } from "../tools/useCssAndCx";
|
||||
import { useConstCallback } from "powerhooks/useConstCallback";
|
||||
import type { FormEventHandler } from "react";
|
||||
import type { I18n } from "../i18n";
|
||||
|
||||
const Login = memo(({ kcContext, i18n, ...props }: { kcContext: KcContextBase.Login; i18n: I18n } & KcProps) => {
|
||||
const { social, realm, url, usernameEditDisabled, login, auth, registrationDisabled } = kcContext;
|
||||
|
||||
const { msg, msgStr } = i18n;
|
||||
|
||||
const { cx } = useCssAndCx();
|
||||
|
||||
const [isLoginButtonDisabled, setIsLoginButtonDisabled] = useState(false);
|
||||
|
||||
const onSubmit = useConstCallback<FormEventHandler<HTMLFormElement>>(e => {
|
||||
e.preventDefault();
|
||||
|
||||
setIsLoginButtonDisabled(true);
|
||||
|
||||
const formElement = e.target as HTMLFormElement;
|
||||
|
||||
//NOTE: Even if we login with email Keycloak expect username and password in
|
||||
//the POST request.
|
||||
formElement.querySelector("input[name='email']")?.setAttribute("name", "username");
|
||||
|
||||
formElement.submit();
|
||||
});
|
||||
|
||||
return (
|
||||
<Template
|
||||
{...{ kcContext, i18n, ...props }}
|
||||
doFetchDefaultThemeResources={true}
|
||||
displayInfo={social.displayInfo}
|
||||
displayWide={realm.password && social.providers !== undefined}
|
||||
headerNode={msg("doLogIn")}
|
||||
formNode={
|
||||
<div id="kc-form" className={cx(realm.password && social.providers !== undefined && props.kcContentWrapperClass)}>
|
||||
<div
|
||||
id="kc-form-wrapper"
|
||||
className={cx(realm.password && social.providers && [props.kcFormSocialAccountContentClass, props.kcFormSocialAccountClass])}
|
||||
>
|
||||
{realm.password && (
|
||||
<form id="kc-form-login" onSubmit={onSubmit} action={url.loginAction} method="post">
|
||||
<div className={cx(props.kcFormGroupClass)}>
|
||||
{(() => {
|
||||
const label = !realm.loginWithEmailAllowed
|
||||
? "username"
|
||||
: realm.registrationEmailAsUsername
|
||||
? "email"
|
||||
: "usernameOrEmail";
|
||||
|
||||
const autoCompleteHelper: typeof label = label === "usernameOrEmail" ? "username" : label;
|
||||
|
||||
return (
|
||||
<>
|
||||
<label htmlFor={autoCompleteHelper} className={cx(props.kcLabelClass)}>
|
||||
{msg(label)}
|
||||
</label>
|
||||
<input
|
||||
tabIndex={1}
|
||||
id={autoCompleteHelper}
|
||||
className={cx(props.kcInputClass)}
|
||||
//NOTE: This is used by Google Chrome auto fill so we use it to tell
|
||||
//the browser how to pre fill the form but before submit we put it back
|
||||
//to username because it is what keycloak expects.
|
||||
name={autoCompleteHelper}
|
||||
defaultValue={login.username ?? ""}
|
||||
type="text"
|
||||
{...(usernameEditDisabled
|
||||
? { "disabled": true }
|
||||
: {
|
||||
"autoFocus": true,
|
||||
"autoComplete": "off"
|
||||
})}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
})()}
|
||||
</div>
|
||||
<div className={cx(props.kcFormGroupClass)}>
|
||||
<label htmlFor="password" className={cx(props.kcLabelClass)}>
|
||||
{msg("password")}
|
||||
</label>
|
||||
<input
|
||||
tabIndex={2}
|
||||
id="password"
|
||||
className={cx(props.kcInputClass)}
|
||||
name="password"
|
||||
type="password"
|
||||
autoComplete="off"
|
||||
/>
|
||||
</div>
|
||||
<div className={cx(props.kcFormGroupClass, props.kcFormSettingClass)}>
|
||||
<div id="kc-form-options">
|
||||
{realm.rememberMe && !usernameEditDisabled && (
|
||||
<div className="checkbox">
|
||||
<label>
|
||||
<input
|
||||
tabIndex={3}
|
||||
id="rememberMe"
|
||||
name="rememberMe"
|
||||
type="checkbox"
|
||||
{...(login.rememberMe
|
||||
? {
|
||||
"checked": true
|
||||
}
|
||||
: {})}
|
||||
/>
|
||||
{msg("rememberMe")}
|
||||
</label>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className={cx(props.kcFormOptionsWrapperClass)}>
|
||||
{realm.resetPasswordAllowed && (
|
||||
<span>
|
||||
<a tabIndex={5} href={url.loginResetCredentialsUrl}>
|
||||
{msg("doForgotPassword")}
|
||||
</a>
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div id="kc-form-buttons" className={cx(props.kcFormGroupClass)}>
|
||||
<input
|
||||
type="hidden"
|
||||
id="id-hidden-input"
|
||||
name="credentialId"
|
||||
{...(auth?.selectedCredential !== undefined
|
||||
? {
|
||||
"value": auth.selectedCredential
|
||||
}
|
||||
: {})}
|
||||
/>
|
||||
<input
|
||||
tabIndex={4}
|
||||
className={cx(
|
||||
props.kcButtonClass,
|
||||
props.kcButtonPrimaryClass,
|
||||
props.kcButtonBlockClass,
|
||||
props.kcButtonLargeClass
|
||||
)}
|
||||
name="login"
|
||||
id="kc-login"
|
||||
type="submit"
|
||||
value={msgStr("doLogIn")}
|
||||
disabled={isLoginButtonDisabled}
|
||||
/>
|
||||
</div>
|
||||
</form>
|
||||
)}
|
||||
</div>
|
||||
{realm.password && social.providers !== undefined && (
|
||||
<div id="kc-social-providers" className={cx(props.kcFormSocialAccountContentClass, props.kcFormSocialAccountClass)}>
|
||||
<ul
|
||||
className={cx(
|
||||
props.kcFormSocialAccountListClass,
|
||||
social.providers.length > 4 && props.kcFormSocialAccountDoubleListClass
|
||||
)}
|
||||
>
|
||||
{social.providers.map(p => (
|
||||
<li key={p.providerId} className={cx(props.kcFormSocialAccountListLinkClass)}>
|
||||
<a href={p.loginUrl} id={`zocial-${p.alias}`} className={cx("zocial", p.providerId)}>
|
||||
<span>{p.displayName}</span>
|
||||
</a>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
}
|
||||
infoNode={
|
||||
realm.password &&
|
||||
realm.registrationAllowed &&
|
||||
!registrationDisabled && (
|
||||
<div id="kc-registration">
|
||||
<span>
|
||||
{msg("noAccount")}
|
||||
<a tabIndex={6} href={url.registrationUrl}>
|
||||
{msg("doRegister")}
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
export default Login;
|
186
src/lib/components/LoginConfigTotp.tsx
Normal file
186
src/lib/components/LoginConfigTotp.tsx
Normal file
@ -0,0 +1,186 @@
|
||||
import React, { memo } from "react";
|
||||
import Template from "./Template";
|
||||
import type { KcProps } from "./KcProps";
|
||||
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
||||
import { useCssAndCx } from "../tools/useCssAndCx";
|
||||
import type { I18n } from "../i18n";
|
||||
|
||||
const LoginConfigTotp = memo(({ kcContext, i18n, ...props }: { kcContext: KcContextBase.LoginConfigTotp; i18n: I18n } & KcProps) => {
|
||||
const { url, isAppInitiatedAction, totp, mode, messagesPerField } = kcContext;
|
||||
|
||||
const { cx } = useCssAndCx();
|
||||
|
||||
const { msg, msgStr } = i18n;
|
||||
|
||||
const algToKeyUriAlg: Record<KcContextBase.LoginConfigTotp["totp"]["policy"]["algorithm"], string> = {
|
||||
HmacSHA1: "SHA1",
|
||||
HmacSHA256: "SHA256",
|
||||
HmacSHA512: "SHA512"
|
||||
};
|
||||
|
||||
return (
|
||||
<Template
|
||||
{...{ kcContext, i18n, ...props }}
|
||||
doFetchDefaultThemeResources={true}
|
||||
headerNode={msg("loginTotpTitle")}
|
||||
formNode={
|
||||
<>
|
||||
<ol id="kc-totp-settings">
|
||||
<li>
|
||||
<p>{msg("loginTotpStep1")}</p>
|
||||
|
||||
<ul id="kc-totp-supported-apps">
|
||||
{totp.policy.supportedApplications.map(app => (
|
||||
<li>{app}</li>
|
||||
))}
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
{mode && mode == "manual" ? (
|
||||
<>
|
||||
<li>
|
||||
<p>{msg("loginTotpManualStep2")}</p>
|
||||
<p>
|
||||
<span id="kc-totp-secret-key">{totp.totpSecretEncoded}</span>
|
||||
</p>
|
||||
<p>
|
||||
<a href={totp.qrUrl} id="mode-barcode">
|
||||
{msg("loginTotpScanBarcode")}
|
||||
</a>
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>{msg("loginTotpManualStep3")}</p>
|
||||
<p>
|
||||
<ul>
|
||||
<li id="kc-totp-type">
|
||||
{msg("loginTotpType")}: {msg(`loginTotp.${totp.policy.type}`)}
|
||||
</li>
|
||||
<li id="kc-totp-algorithm">
|
||||
{msg("loginTotpAlgorithm")}: {algToKeyUriAlg?.[totp.policy.algorithm] ?? totp.policy.algorithm}
|
||||
</li>
|
||||
<li id="kc-totp-digits">
|
||||
{msg("loginTotpDigits")}: {totp.policy.digits}
|
||||
</li>
|
||||
{totp.policy.type === "totp" ? (
|
||||
<li id="kc-totp-period">
|
||||
{msg("loginTotpInterval")}: {totp.policy.period}
|
||||
</li>
|
||||
) : (
|
||||
<li id="kc-totp-counter">
|
||||
{msg("loginTotpCounter")}: {totp.policy.initialCounter}
|
||||
</li>
|
||||
)}
|
||||
</ul>
|
||||
</p>
|
||||
</li>
|
||||
</>
|
||||
) : (
|
||||
<li>
|
||||
<p>{msg("loginTotpStep2")}</p>
|
||||
<img id="kc-totp-secret-qr-code" src={`data:image/png;base64, ${totp.totpSecretQrCode}`} alt="Figure: Barcode" />
|
||||
<br />
|
||||
<p>
|
||||
<a href={totp.manualUrl} id="mode-manual">
|
||||
{msg("loginTotpUnableToScan")}
|
||||
</a>
|
||||
</p>
|
||||
</li>
|
||||
)}
|
||||
<li>
|
||||
<p>{msg("loginTotpStep3")}</p>
|
||||
<p>{msg("loginTotpStep3DeviceName")}</p>
|
||||
</li>
|
||||
</ol>
|
||||
|
||||
<form action={url.loginAction} className={cx(props.kcFormClass)} id="kc-totp-settings-form" method="post">
|
||||
<div className={cx(props.kcFormGroupClass)}>
|
||||
<div className={cx(props.kcInputWrapperClass)}>
|
||||
<label htmlFor="totp" className={cx(props.kcLabelClass)}>
|
||||
{msg("authenticatorCode")}
|
||||
</label>{" "}
|
||||
<span className="required">*</span>
|
||||
</div>
|
||||
<div className={cx(props.kcInputWrapperClass)}>
|
||||
<input
|
||||
type="text"
|
||||
id="totp"
|
||||
name="totp"
|
||||
autoComplete="off"
|
||||
className={cx(props.kcInputClass)}
|
||||
aria-invalid={messagesPerField.existsError("totp")}
|
||||
/>
|
||||
|
||||
{messagesPerField.existsError("totp") && (
|
||||
<span id="input-error-otp-code" className={cx(props.kcInputErrorMessageClass)} aria-live="polite">
|
||||
{messagesPerField.get("totp")}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<input type="hidden" id="totpSecret" name="totpSecret" value={totp.totpSecret} />
|
||||
{mode && <input type="hidden" id="mode" value={mode} />}
|
||||
</div>
|
||||
|
||||
<div className={cx(props.kcFormGroupClass)}>
|
||||
<div className={cx(props.kcInputWrapperClass)}>
|
||||
<label htmlFor="userLabel" className={cx(props.kcLabelClass)}>
|
||||
{msg("loginTotpDeviceName")}
|
||||
</label>{" "}
|
||||
{totp.otpCredentials.length >= 1 && <span className="required">*</span>}
|
||||
</div>
|
||||
<div className={cx(props.kcInputWrapperClass)}>
|
||||
<input
|
||||
type="text"
|
||||
id="userLabel"
|
||||
name="userLabel"
|
||||
autoComplete="off"
|
||||
className={cx(props.kcInputClass)}
|
||||
aria-invalid={messagesPerField.existsError("userLabel")}
|
||||
/>
|
||||
{messagesPerField.existsError("userLabel") && (
|
||||
<span id="input-error-otp-label" className={cx(props.kcInputErrorMessageClass)} aria-live="polite">
|
||||
{messagesPerField.get("userLabel")}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{isAppInitiatedAction ? (
|
||||
<>
|
||||
<input
|
||||
type="submit"
|
||||
className={cx(props.kcButtonClass, props.kcButtonPrimaryClass, props.kcButtonLargeClass)}
|
||||
id="saveTOTPBtn"
|
||||
value={msgStr("doSubmit")}
|
||||
/>
|
||||
<button
|
||||
type="submit"
|
||||
className={cx(
|
||||
props.kcButtonClass,
|
||||
props.kcButtonDefaultClass,
|
||||
props.kcButtonLargeClass,
|
||||
props.kcButtonLargeClass
|
||||
)}
|
||||
id="cancelTOTPBtn"
|
||||
name="cancel-aia"
|
||||
value="true"
|
||||
>
|
||||
${msg("doCancel")}
|
||||
</button>
|
||||
</>
|
||||
) : (
|
||||
<input
|
||||
type="submit"
|
||||
className={cx(props.kcButtonClass, props.kcButtonPrimaryClass, props.kcButtonLargeClass)}
|
||||
id="saveTOTPBtn"
|
||||
value={msgStr("doSubmit")}
|
||||
/>
|
||||
)}
|
||||
</form>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
export default LoginConfigTotp;
|
48
src/lib/components/LoginIdpLinkConfirm.tsx
Normal file
48
src/lib/components/LoginIdpLinkConfirm.tsx
Normal file
@ -0,0 +1,48 @@
|
||||
import React, { memo } from "react";
|
||||
import Template from "./Template";
|
||||
import type { KcProps } from "./KcProps";
|
||||
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
||||
import { useCssAndCx } from "../tools/useCssAndCx";
|
||||
import type { I18n } from "../i18n";
|
||||
|
||||
const LoginIdpLinkConfirm = memo(({ kcContext, i18n, ...props }: { kcContext: KcContextBase.LoginIdpLinkConfirm; i18n: I18n } & KcProps) => {
|
||||
const { url, idpAlias } = kcContext;
|
||||
|
||||
const { msg } = i18n;
|
||||
|
||||
const { cx } = useCssAndCx();
|
||||
|
||||
return (
|
||||
<Template
|
||||
{...{ kcContext, i18n, ...props }}
|
||||
doFetchDefaultThemeResources={true}
|
||||
headerNode={msg("confirmLinkIdpTitle")}
|
||||
formNode={
|
||||
<form id="kc-register-form" action={url.loginAction} method="post">
|
||||
<div className={cx(props.kcFormGroupClass)}>
|
||||
<button
|
||||
type="submit"
|
||||
className={cx(props.kcButtonClass, props.kcButtonDefaultClass, props.kcButtonBlockClass, props.kcButtonLargeClass)}
|
||||
name="submitAction"
|
||||
id="updateProfile"
|
||||
value="updateProfile"
|
||||
>
|
||||
{msg("confirmLinkIdpReviewProfile")}
|
||||
</button>
|
||||
<button
|
||||
type="submit"
|
||||
className={cx(props.kcButtonClass, props.kcButtonDefaultClass, props.kcButtonBlockClass, props.kcButtonLargeClass)}
|
||||
name="submitAction"
|
||||
id="linkAccount"
|
||||
value="linkAccount"
|
||||
>
|
||||
{msg("confirmLinkIdpContinue", idpAlias)}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
export default LoginIdpLinkConfirm;
|
34
src/lib/components/LoginIdpLinkEmail.tsx
Normal file
34
src/lib/components/LoginIdpLinkEmail.tsx
Normal file
@ -0,0 +1,34 @@
|
||||
import React, { memo } from "react";
|
||||
import Template from "./Template";
|
||||
import type { KcProps } from "./KcProps";
|
||||
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
||||
import type { I18n } from "../i18n";
|
||||
|
||||
const LoginIdpLinkEmail = memo(({ kcContext, i18n, ...props }: { kcContext: KcContextBase.LoginIdpLinkEmail; i18n: I18n } & KcProps) => {
|
||||
const { url, realm, brokerContext, idpAlias } = kcContext;
|
||||
|
||||
const { msg } = i18n;
|
||||
|
||||
return (
|
||||
<Template
|
||||
{...{ kcContext, i18n, ...props }}
|
||||
doFetchDefaultThemeResources={true}
|
||||
headerNode={msg("emailLinkIdpTitle", idpAlias)}
|
||||
formNode={
|
||||
<>
|
||||
<p id="instruction1" className="instruction">
|
||||
{msg("emailLinkIdp1", idpAlias, brokerContext.username, realm.displayName)}
|
||||
</p>
|
||||
<p id="instruction2" className="instruction">
|
||||
{msg("emailLinkIdp2")} <a href={url.loginAction}>{msg("doClickHere")}</a> {msg("emailLinkIdp3")}
|
||||
</p>
|
||||
<p id="instruction3" className="instruction">
|
||||
{msg("emailLinkIdp4")} <a href={url.loginAction}>{msg("doClickHere")}</a> {msg("emailLinkIdp5")}
|
||||
</p>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
export default LoginIdpLinkEmail;
|
113
src/lib/components/LoginOtp.tsx
Normal file
113
src/lib/components/LoginOtp.tsx
Normal file
@ -0,0 +1,113 @@
|
||||
import React, { useEffect, memo } from "react";
|
||||
import Template from "./Template";
|
||||
import type { KcProps } from "./KcProps";
|
||||
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
||||
import { headInsert } from "../tools/headInsert";
|
||||
import { pathJoin } from "../../bin/tools/pathJoin";
|
||||
import { useCssAndCx } from "../tools/useCssAndCx";
|
||||
import type { I18n } from "../i18n";
|
||||
|
||||
const LoginOtp = memo(({ kcContext, i18n, ...props }: { kcContext: KcContextBase.LoginOtp; i18n: I18n } & KcProps) => {
|
||||
const { otpLogin, url } = kcContext;
|
||||
|
||||
const { cx } = useCssAndCx();
|
||||
|
||||
const { msg, msgStr } = i18n;
|
||||
|
||||
useEffect(() => {
|
||||
let isCleanedUp = false;
|
||||
|
||||
headInsert({
|
||||
"type": "javascript",
|
||||
"src": pathJoin(kcContext.url.resourcesCommonPath, "node_modules/jquery/dist/jquery.min.js")
|
||||
}).then(() => {
|
||||
if (isCleanedUp) return;
|
||||
|
||||
evaluateInlineScript();
|
||||
});
|
||||
|
||||
return () => {
|
||||
isCleanedUp = true;
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Template
|
||||
{...{ kcContext, i18n, ...props }}
|
||||
doFetchDefaultThemeResources={true}
|
||||
headerNode={msg("doLogIn")}
|
||||
formNode={
|
||||
<form id="kc-otp-login-form" className={cx(props.kcFormClass)} action={url.loginAction} method="post">
|
||||
{otpLogin.userOtpCredentials.length > 1 && (
|
||||
<div className={cx(props.kcFormGroupClass)}>
|
||||
<div className={cx(props.kcInputWrapperClass)}>
|
||||
{otpLogin.userOtpCredentials.map(otpCredential => (
|
||||
<div key={otpCredential.id} className={cx(props.kcSelectOTPListClass)}>
|
||||
<input type="hidden" value="${otpCredential.id}" />
|
||||
<div className={cx(props.kcSelectOTPListItemClass)}>
|
||||
<span className={cx(props.kcAuthenticatorOtpCircleClass)} />
|
||||
<h2 className={cx(props.kcSelectOTPItemHeadingClass)}>{otpCredential.userLabel}</h2>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<div className={cx(props.kcFormGroupClass)}>
|
||||
<div className={cx(props.kcLabelWrapperClass)}>
|
||||
<label htmlFor="otp" className={cx(props.kcLabelClass)}>
|
||||
{msg("loginOtpOneTime")}
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div className={cx(props.kcInputWrapperClass)}>
|
||||
<input id="otp" name="otp" autoComplete="off" type="text" className={cx(props.kcInputClass)} autoFocus />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={cx(props.kcFormGroupClass)}>
|
||||
<div id="kc-form-options" className={cx(props.kcFormOptionsClass)}>
|
||||
<div className={cx(props.kcFormOptionsWrapperClass)} />
|
||||
</div>
|
||||
|
||||
<div id="kc-form-buttons" className={cx(props.kcFormButtonsClass)}>
|
||||
<input
|
||||
className={cx(props.kcButtonClass, props.kcButtonPrimaryClass, props.kcButtonBlockClass, props.kcButtonLargeClass)}
|
||||
name="login"
|
||||
id="kc-login"
|
||||
type="submit"
|
||||
value={msgStr("doLogIn")}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
declare const $: any;
|
||||
|
||||
function evaluateInlineScript() {
|
||||
$(document).ready(function () {
|
||||
// Card Single Select
|
||||
$(".card-pf-view-single-select").click(function (this: any) {
|
||||
if ($(this).hasClass("active")) {
|
||||
$(this).removeClass("active");
|
||||
$(this).children().removeAttr("name");
|
||||
} else {
|
||||
$(".card-pf-view-single-select").removeClass("active");
|
||||
$(".card-pf-view-single-select").children().removeAttr("name");
|
||||
$(this).addClass("active");
|
||||
$(this).children().attr("name", "selectedCredentialId");
|
||||
}
|
||||
});
|
||||
|
||||
var defaultCred = $(".card-pf-view-single-select")[0];
|
||||
if (defaultCred) {
|
||||
defaultCred.click();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export default LoginOtp;
|
38
src/lib/components/LoginPageExpired.tsx
Normal file
38
src/lib/components/LoginPageExpired.tsx
Normal file
@ -0,0 +1,38 @@
|
||||
import React, { memo } from "react";
|
||||
import Template from "./Template";
|
||||
import type { KcProps } from "./KcProps";
|
||||
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
||||
import type { I18n } from "../i18n";
|
||||
|
||||
const LoginPageExpired = memo(({ kcContext, i18n, ...props }: { kcContext: KcContextBase.LoginPageExpired; i18n: I18n } & KcProps) => {
|
||||
const { url } = kcContext;
|
||||
|
||||
const { msg } = i18n;
|
||||
|
||||
return (
|
||||
<Template
|
||||
{...{ kcContext, i18n, ...props }}
|
||||
doFetchDefaultThemeResources={true}
|
||||
displayMessage={false}
|
||||
headerNode={msg("pageExpiredTitle")}
|
||||
formNode={
|
||||
<>
|
||||
<p id="instruction1" className="instruction">
|
||||
{msg("pageExpiredMsg1")}
|
||||
<a id="loginRestartLink" href={url.loginRestartFlowUrl}>
|
||||
{msg("doClickHere")}
|
||||
</a>{" "}
|
||||
.<br />
|
||||
{msg("pageExpiredMsg2")}{" "}
|
||||
<a id="loginContinueLink" href={url.loginAction}>
|
||||
{msg("doClickHere")}
|
||||
</a>{" "}
|
||||
.
|
||||
</p>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
export default LoginPageExpired;
|
68
src/lib/components/LoginResetPassword.tsx
Normal file
68
src/lib/components/LoginResetPassword.tsx
Normal file
@ -0,0 +1,68 @@
|
||||
import React, { memo } from "react";
|
||||
import Template from "./Template";
|
||||
import type { KcProps } from "./KcProps";
|
||||
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
||||
import { useCssAndCx } from "../tools/useCssAndCx";
|
||||
import type { I18n } from "../i18n";
|
||||
|
||||
const LoginResetPassword = memo(({ kcContext, i18n, ...props }: { kcContext: KcContextBase.LoginResetPassword; i18n: I18n } & KcProps) => {
|
||||
const { url, realm, auth } = kcContext;
|
||||
|
||||
const { msg, msgStr } = i18n;
|
||||
|
||||
const { cx } = useCssAndCx();
|
||||
|
||||
return (
|
||||
<Template
|
||||
{...{ kcContext, i18n, ...props }}
|
||||
doFetchDefaultThemeResources={true}
|
||||
displayMessage={false}
|
||||
headerNode={msg("emailForgotTitle")}
|
||||
formNode={
|
||||
<form id="kc-reset-password-form" className={cx(props.kcFormClass)} action={url.loginAction} method="post">
|
||||
<div className={cx(props.kcFormGroupClass)}>
|
||||
<div className={cx(props.kcLabelWrapperClass)}>
|
||||
<label htmlFor="username" className={cx(props.kcLabelClass)}>
|
||||
{!realm.loginWithEmailAllowed
|
||||
? msg("username")
|
||||
: !realm.registrationEmailAsUsername
|
||||
? msg("usernameOrEmail")
|
||||
: msg("email")}
|
||||
</label>
|
||||
</div>
|
||||
<div className={cx(props.kcInputWrapperClass)}>
|
||||
<input
|
||||
type="text"
|
||||
id="username"
|
||||
name="username"
|
||||
className={cx(props.kcInputClass)}
|
||||
autoFocus
|
||||
defaultValue={auth !== undefined && auth.showUsername ? auth.attemptedUsername : undefined}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className={cx(props.kcFormGroupClass, props.kcFormSettingClass)}>
|
||||
<div id="kc-form-options" className={cx(props.kcFormOptionsClass)}>
|
||||
<div className={cx(props.kcFormOptionsWrapperClass)}>
|
||||
<span>
|
||||
<a href={url.loginUrl}>{msg("backToLogin")}</a>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="kc-form-buttons" className={cx(props.kcFormButtonsClass)}>
|
||||
<input
|
||||
className={cx(props.kcButtonClass, props.kcButtonPrimaryClass, props.kcButtonBlockClass, props.kcButtonLargeClass)}
|
||||
type="submit"
|
||||
value={msgStr("doSubmit")}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
}
|
||||
infoNode={msg("emailInstruction")}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
export default LoginResetPassword;
|
119
src/lib/components/LoginUpdatePassword.tsx
Normal file
119
src/lib/components/LoginUpdatePassword.tsx
Normal file
@ -0,0 +1,119 @@
|
||||
import React, { memo } from "react";
|
||||
import Template from "./Template";
|
||||
import type { KcProps } from "./KcProps";
|
||||
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
||||
import { useCssAndCx } from "../tools/useCssAndCx";
|
||||
import type { I18n } from "../i18n";
|
||||
|
||||
const LoginUpdatePassword = memo(({ kcContext, i18n, ...props }: { kcContext: KcContextBase.LoginUpdatePassword; i18n: I18n } & KcProps) => {
|
||||
const { cx } = useCssAndCx();
|
||||
|
||||
const { msg, msgStr } = i18n;
|
||||
|
||||
const { url, messagesPerField, isAppInitiatedAction, username } = kcContext;
|
||||
|
||||
return (
|
||||
<Template
|
||||
{...{ kcContext, i18n, ...props }}
|
||||
doFetchDefaultThemeResources={true}
|
||||
headerNode={msg("updatePasswordTitle")}
|
||||
formNode={
|
||||
<form id="kc-passwd-update-form" className={cx(props.kcFormClass)} action={url.loginAction} method="post">
|
||||
<input
|
||||
type="text"
|
||||
id="username"
|
||||
name="username"
|
||||
value={username}
|
||||
readOnly={true}
|
||||
autoComplete="username"
|
||||
style={{ display: "none" }}
|
||||
/>
|
||||
<input type="password" id="password" name="password" autoComplete="current-password" style={{ display: "none" }} />
|
||||
|
||||
<div className={cx(props.kcFormGroupClass, messagesPerField.printIfExists("password", props.kcFormGroupErrorClass))}>
|
||||
<div className={cx(props.kcLabelWrapperClass)}>
|
||||
<label htmlFor="password-new" className={cx(props.kcLabelClass)}>
|
||||
{msg("passwordNew")}
|
||||
</label>
|
||||
</div>
|
||||
<div className={cx(props.kcInputWrapperClass)}>
|
||||
<input
|
||||
type="password"
|
||||
id="password-new"
|
||||
name="password-new"
|
||||
autoFocus
|
||||
autoComplete="new-password"
|
||||
className={cx(props.kcInputClass)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={cx(props.kcFormGroupClass, messagesPerField.printIfExists("password-confirm", props.kcFormGroupErrorClass))}>
|
||||
<div className={cx(props.kcLabelWrapperClass)}>
|
||||
<label htmlFor="password-confirm" className={cx(props.kcLabelClass)}>
|
||||
{msg("passwordConfirm")}
|
||||
</label>
|
||||
</div>
|
||||
<div className={cx(props.kcInputWrapperClass)}>
|
||||
<input
|
||||
type="password"
|
||||
id="password-confirm"
|
||||
name="password-confirm"
|
||||
autoComplete="new-password"
|
||||
className={cx(props.kcInputClass)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={cx(props.kcFormGroupClass)}>
|
||||
<div id="kc-form-options" className={cx(props.kcFormOptionsClass)}>
|
||||
<div className={cx(props.kcFormOptionsWrapperClass)}>
|
||||
{isAppInitiatedAction && (
|
||||
<div className="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" id="logout-sessions" name="logout-sessions" value="on" checked />
|
||||
{msgStr("logoutOtherSessions")}
|
||||
</label>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="kc-form-buttons" className={cx(props.kcFormButtonsClass)}>
|
||||
{isAppInitiatedAction ? (
|
||||
<>
|
||||
<input
|
||||
className={cx(props.kcButtonClass, props.kcButtonPrimaryClass, props.kcButtonLargeClass)}
|
||||
type="submit"
|
||||
defaultValue={msgStr("doSubmit")}
|
||||
/>
|
||||
<button
|
||||
className={cx(props.kcButtonClass, props.kcButtonDefaultClass, props.kcButtonLargeClass)}
|
||||
type="submit"
|
||||
name="cancel-aia"
|
||||
value="true"
|
||||
>
|
||||
{msg("doCancel")}
|
||||
</button>
|
||||
</>
|
||||
) : (
|
||||
<input
|
||||
className={cx(
|
||||
props.kcButtonClass,
|
||||
props.kcButtonPrimaryClass,
|
||||
props.kcButtonBlockClass,
|
||||
props.kcButtonLargeClass
|
||||
)}
|
||||
type="submit"
|
||||
defaultValue={msgStr("doSubmit")}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
export default LoginUpdatePassword;
|
122
src/lib/components/LoginUpdateProfile.tsx
Normal file
122
src/lib/components/LoginUpdateProfile.tsx
Normal file
@ -0,0 +1,122 @@
|
||||
import React, { memo } from "react";
|
||||
import Template from "./Template";
|
||||
import type { KcProps } from "./KcProps";
|
||||
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
||||
import { useCssAndCx } from "../tools/useCssAndCx";
|
||||
import type { I18n } from "../i18n";
|
||||
|
||||
const LoginUpdateProfile = memo(({ kcContext, i18n, ...props }: { kcContext: KcContextBase.LoginUpdateProfile; i18n: I18n } & KcProps) => {
|
||||
const { cx } = useCssAndCx();
|
||||
|
||||
const { msg, msgStr } = i18n;
|
||||
|
||||
const { url, user, messagesPerField, isAppInitiatedAction } = kcContext;
|
||||
|
||||
return (
|
||||
<Template
|
||||
{...{ kcContext, i18n, ...props }}
|
||||
doFetchDefaultThemeResources={true}
|
||||
headerNode={msg("loginProfileTitle")}
|
||||
formNode={
|
||||
<form id="kc-update-profile-form" className={cx(props.kcFormClass)} action={url.loginAction} method="post">
|
||||
{user.editUsernameAllowed && (
|
||||
<div className={cx(props.kcFormGroupClass, messagesPerField.printIfExists("username", props.kcFormGroupErrorClass))}>
|
||||
<div className={cx(props.kcLabelWrapperClass)}>
|
||||
<label htmlFor="username" className={cx(props.kcLabelClass)}>
|
||||
{msg("username")}
|
||||
</label>
|
||||
</div>
|
||||
<div className={cx(props.kcInputWrapperClass)}>
|
||||
<input
|
||||
type="text"
|
||||
id="username"
|
||||
name="username"
|
||||
defaultValue={user.username ?? ""}
|
||||
className={cx(props.kcInputClass)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className={cx(props.kcFormGroupClass, messagesPerField.printIfExists("email", props.kcFormGroupErrorClass))}>
|
||||
<div className={cx(props.kcLabelWrapperClass)}>
|
||||
<label htmlFor="email" className={cx(props.kcLabelClass)}>
|
||||
{msg("email")}
|
||||
</label>
|
||||
</div>
|
||||
<div className={cx(props.kcInputWrapperClass)}>
|
||||
<input type="text" id="email" name="email" defaultValue={user.email ?? ""} className={cx(props.kcInputClass)} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={cx(props.kcFormGroupClass, messagesPerField.printIfExists("firstName", props.kcFormGroupErrorClass))}>
|
||||
<div className={cx(props.kcLabelWrapperClass)}>
|
||||
<label htmlFor="firstName" className={cx(props.kcLabelClass)}>
|
||||
{msg("firstName")}
|
||||
</label>
|
||||
</div>
|
||||
<div className={cx(props.kcInputWrapperClass)}>
|
||||
<input
|
||||
type="text"
|
||||
id="firstName"
|
||||
name="firstName"
|
||||
defaultValue={user.firstName ?? ""}
|
||||
className={cx(props.kcInputClass)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={cx(props.kcFormGroupClass, messagesPerField.printIfExists("lastName", props.kcFormGroupErrorClass))}>
|
||||
<div className={cx(props.kcLabelWrapperClass)}>
|
||||
<label htmlFor="lastName" className={cx(props.kcLabelClass)}>
|
||||
{msg("lastName")}
|
||||
</label>
|
||||
</div>
|
||||
<div className={cx(props.kcInputWrapperClass)}>
|
||||
<input type="text" id="lastName" name="lastName" defaultValue={user.lastName ?? ""} className={cx(props.kcInputClass)} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={cx(props.kcFormGroupClass)}>
|
||||
<div id="kc-form-options" className={cx(props.kcFormOptionsClass)}>
|
||||
<div className={cx(props.kcFormOptionsWrapperClass)} />
|
||||
</div>
|
||||
|
||||
<div id="kc-form-buttons" className={cx(props.kcFormButtonsClass)}>
|
||||
{isAppInitiatedAction ? (
|
||||
<>
|
||||
<input
|
||||
className={cx(props.kcButtonClass, props.kcButtonPrimaryClass, props.kcButtonLargeClass)}
|
||||
type="submit"
|
||||
defaultValue={msgStr("doSubmit")}
|
||||
/>
|
||||
<button
|
||||
className={cx(props.kcButtonClass, props.kcButtonDefaultClass, props.kcButtonLargeClass)}
|
||||
type="submit"
|
||||
name="cancel-aia"
|
||||
value="true"
|
||||
>
|
||||
{msg("doCancel")}
|
||||
</button>
|
||||
</>
|
||||
) : (
|
||||
<input
|
||||
className={cx(
|
||||
props.kcButtonClass,
|
||||
props.kcButtonPrimaryClass,
|
||||
props.kcButtonBlockClass,
|
||||
props.kcButtonLargeClass
|
||||
)}
|
||||
type="submit"
|
||||
defaultValue={msgStr("doSubmit")}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
export default LoginUpdateProfile;
|
34
src/lib/components/LoginVerifyEmail.tsx
Normal file
34
src/lib/components/LoginVerifyEmail.tsx
Normal file
@ -0,0 +1,34 @@
|
||||
import React, { memo } from "react";
|
||||
import Template from "./Template";
|
||||
import type { KcProps } from "./KcProps";
|
||||
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
||||
import type { I18n } from "../i18n";
|
||||
|
||||
const LoginVerifyEmail = memo(({ kcContext, i18n, ...props }: { kcContext: KcContextBase.LoginVerifyEmail; i18n: I18n } & KcProps) => {
|
||||
const { msg } = i18n;
|
||||
|
||||
const { url, user } = kcContext;
|
||||
|
||||
return (
|
||||
<Template
|
||||
{...{ kcContext, i18n, ...props }}
|
||||
doFetchDefaultThemeResources={true}
|
||||
displayMessage={false}
|
||||
headerNode={msg("emailVerifyTitle")}
|
||||
formNode={
|
||||
<>
|
||||
<p className="instruction">{msg("emailVerifyInstruction1", user?.email)}</p>
|
||||
<p className="instruction">
|
||||
{msg("emailVerifyInstruction2")}
|
||||
<br />
|
||||
<a href={url.loginAction}>{msg("doClickHere")}</a>
|
||||
|
||||
{msg("emailVerifyInstruction3")}
|
||||
</p>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
export default LoginVerifyEmail;
|
62
src/lib/components/LogoutConfirm.tsx
Normal file
62
src/lib/components/LogoutConfirm.tsx
Normal file
@ -0,0 +1,62 @@
|
||||
import React, { memo } from "react";
|
||||
import { useCssAndCx } from "../tools/useCssAndCx";
|
||||
import Template from "./Template";
|
||||
import type { KcProps } from "./KcProps";
|
||||
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
||||
import type { I18n } from "../i18n";
|
||||
|
||||
const LogoutConfirm = memo(({ kcContext, i18n, ...props }: { kcContext: KcContextBase.LogoutConfirm; i18n: I18n } & KcProps) => {
|
||||
const { url, client, logoutConfirm } = kcContext;
|
||||
|
||||
const { cx } = useCssAndCx();
|
||||
|
||||
const { msg, msgStr } = i18n;
|
||||
|
||||
return (
|
||||
<Template
|
||||
{...{ kcContext, i18n, ...props }}
|
||||
doFetchDefaultThemeResources={true}
|
||||
displayMessage={false}
|
||||
headerNode={msg("logoutConfirmTitle")}
|
||||
formNode={
|
||||
<>
|
||||
<div id="kc-logout-confirm" className="content-area">
|
||||
<p className="instruction">{msg("logoutConfirmHeader")}</p>
|
||||
<form className="form-actions" action={url.logoutConfirmAction} method="POST">
|
||||
<input type="hidden" name="session_code" value={logoutConfirm.code} />
|
||||
<div className={cx(props.kcFormGroupClass)}>
|
||||
<div id="kc-form-options">
|
||||
<div className={cx(props.kcFormOptionsWrapperClass)}></div>
|
||||
</div>
|
||||
<div id="kc-form-buttons" className={cx(props.kcFormGroupClass)}>
|
||||
<input
|
||||
tabIndex={4}
|
||||
className={cx(
|
||||
props.kcButtonClass,
|
||||
props.kcButtonPrimaryClass,
|
||||
props.kcButtonBlockClass,
|
||||
props.kcButtonLargeClass
|
||||
)}
|
||||
name="confirmLogout"
|
||||
id="kc-logout"
|
||||
type="submit"
|
||||
value={msgStr("doLogout")}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<div id="kc-info-message">
|
||||
{!logoutConfirm.skipLink && client.baseUrl && (
|
||||
<p>
|
||||
<a href={client.baseUrl} dangerouslySetInnerHTML={{ __html: msgStr("backToApplication") }} />
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
export default LogoutConfirm;
|
158
src/lib/components/Register.tsx
Normal file
158
src/lib/components/Register.tsx
Normal file
@ -0,0 +1,158 @@
|
||||
import React, { memo } from "react";
|
||||
import Template from "./Template";
|
||||
import type { KcProps } from "./KcProps";
|
||||
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
||||
import { useCssAndCx } from "../tools/useCssAndCx";
|
||||
import type { I18n } from "../i18n";
|
||||
|
||||
const Register = memo(({ kcContext, i18n, ...props }: { kcContext: KcContextBase.Register; i18n: I18n } & KcProps) => {
|
||||
const { url, messagesPerField, register, realm, passwordRequired, recaptchaRequired, recaptchaSiteKey } = kcContext;
|
||||
|
||||
const { msg, msgStr } = i18n;
|
||||
|
||||
const { cx } = useCssAndCx();
|
||||
|
||||
return (
|
||||
<Template
|
||||
{...{ kcContext, i18n, ...props }}
|
||||
doFetchDefaultThemeResources={true}
|
||||
headerNode={msg("registerTitle")}
|
||||
formNode={
|
||||
<form id="kc-register-form" className={cx(props.kcFormClass)} action={url.registrationAction} method="post">
|
||||
<div className={cx(props.kcFormGroupClass, messagesPerField.printIfExists("firstName", props.kcFormGroupErrorClass))}>
|
||||
<div className={cx(props.kcLabelWrapperClass)}>
|
||||
<label htmlFor="firstName" className={cx(props.kcLabelClass)}>
|
||||
{msg("firstName")}
|
||||
</label>
|
||||
</div>
|
||||
<div className={cx(props.kcInputWrapperClass)}>
|
||||
<input
|
||||
type="text"
|
||||
id="firstName"
|
||||
className={cx(props.kcInputClass)}
|
||||
name="firstName"
|
||||
defaultValue={register.formData.firstName ?? ""}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={cx(props.kcFormGroupClass, messagesPerField.printIfExists("lastName", props.kcFormGroupErrorClass))}>
|
||||
<div className={cx(props.kcLabelWrapperClass)}>
|
||||
<label htmlFor="lastName" className={cx(props.kcLabelClass)}>
|
||||
{msg("lastName")}
|
||||
</label>
|
||||
</div>
|
||||
<div className={cx(props.kcInputWrapperClass)}>
|
||||
<input
|
||||
type="text"
|
||||
id="lastName"
|
||||
className={cx(props.kcInputClass)}
|
||||
name="lastName"
|
||||
defaultValue={register.formData.lastName ?? ""}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={cx(props.kcFormGroupClass, messagesPerField.printIfExists("email", props.kcFormGroupErrorClass))}>
|
||||
<div className={cx(props.kcLabelWrapperClass)}>
|
||||
<label htmlFor="email" className={cx(props.kcLabelClass)}>
|
||||
{msg("email")}
|
||||
</label>
|
||||
</div>
|
||||
<div className={cx(props.kcInputWrapperClass)}>
|
||||
<input
|
||||
type="text"
|
||||
id="email"
|
||||
className={cx(props.kcInputClass)}
|
||||
name="email"
|
||||
defaultValue={register.formData.email ?? ""}
|
||||
autoComplete="email"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{!realm.registrationEmailAsUsername && (
|
||||
<div className={cx(props.kcFormGroupClass, messagesPerField.printIfExists("username", props.kcFormGroupErrorClass))}>
|
||||
<div className={cx(props.kcLabelWrapperClass)}>
|
||||
<label htmlFor="username" className={cx(props.kcLabelClass)}>
|
||||
{msg("username")}
|
||||
</label>
|
||||
</div>
|
||||
<div className={cx(props.kcInputWrapperClass)}>
|
||||
<input
|
||||
type="text"
|
||||
id="username"
|
||||
className={cx(props.kcInputClass)}
|
||||
name="username"
|
||||
defaultValue={register.formData.username ?? ""}
|
||||
autoComplete="username"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{passwordRequired && (
|
||||
<>
|
||||
<div className={cx(props.kcFormGroupClass, messagesPerField.printIfExists("password", props.kcFormGroupErrorClass))}>
|
||||
<div className={cx(props.kcLabelWrapperClass)}>
|
||||
<label htmlFor="password" className={cx(props.kcLabelClass)}>
|
||||
{msg("password")}
|
||||
</label>
|
||||
</div>
|
||||
<div className={cx(props.kcInputWrapperClass)}>
|
||||
<input
|
||||
type="password"
|
||||
id="password"
|
||||
className={cx(props.kcInputClass)}
|
||||
name="password"
|
||||
autoComplete="new-password"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
className={cx(
|
||||
props.kcFormGroupClass,
|
||||
messagesPerField.printIfExists("password-confirm", props.kcFormGroupErrorClass)
|
||||
)}
|
||||
>
|
||||
<div className={cx(props.kcLabelWrapperClass)}>
|
||||
<label htmlFor="password-confirm" className={cx(props.kcLabelClass)}>
|
||||
{msg("passwordConfirm")}
|
||||
</label>
|
||||
</div>
|
||||
<div className={cx(props.kcInputWrapperClass)}>
|
||||
<input type="password" id="password-confirm" className={cx(props.kcInputClass)} name="password-confirm" />
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
{recaptchaRequired && (
|
||||
<div className="form-group">
|
||||
<div className={cx(props.kcInputWrapperClass)}>
|
||||
<div className="g-recaptcha" data-size="compact" data-sitekey={recaptchaSiteKey}></div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<div className={cx(props.kcFormGroupClass)}>
|
||||
<div id="kc-form-options" className={cx(props.kcFormOptionsClass)}>
|
||||
<div className={cx(props.kcFormOptionsWrapperClass)}>
|
||||
<span>
|
||||
<a href={url.loginUrl}>{msg("backToLogin")}</a>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="kc-form-buttons" className={cx(props.kcFormButtonsClass)}>
|
||||
<input
|
||||
className={cx(props.kcButtonClass, props.kcButtonPrimaryClass, props.kcButtonBlockClass, props.kcButtonLargeClass)}
|
||||
type="submit"
|
||||
value={msgStr("doRegister")}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
export default Register;
|
220
src/lib/components/RegisterUserProfile.tsx
Normal file
220
src/lib/components/RegisterUserProfile.tsx
Normal file
@ -0,0 +1,220 @@
|
||||
import React, { useMemo, memo, useEffect, useState, Fragment } from "react";
|
||||
import Template from "./Template";
|
||||
import type { KcProps } from "./KcProps";
|
||||
import type { KcContextBase, Attribute } from "../getKcContext/KcContextBase";
|
||||
import { useCssAndCx } from "../tools/useCssAndCx";
|
||||
import type { ReactComponent } from "../tools/ReactComponent";
|
||||
import { useCallbackFactory } from "powerhooks/useCallbackFactory";
|
||||
import { useFormValidationSlice } from "../useFormValidationSlice";
|
||||
import type { I18n } from "../i18n";
|
||||
|
||||
const RegisterUserProfile = memo(({ kcContext, i18n, ...props_ }: { kcContext: KcContextBase.RegisterUserProfile; i18n: I18n } & KcProps) => {
|
||||
const { url, messagesPerField, recaptchaRequired, recaptchaSiteKey } = kcContext;
|
||||
|
||||
const { msg, msgStr } = i18n;
|
||||
|
||||
const { cx, css } = useCssAndCx();
|
||||
|
||||
const props = useMemo(
|
||||
() => ({
|
||||
...props_,
|
||||
"kcFormGroupClass": cx(props_.kcFormGroupClass, css({ "marginBottom": 20 }))
|
||||
}),
|
||||
[cx, css]
|
||||
);
|
||||
|
||||
const [isFomSubmittable, setIsFomSubmittable] = useState(false);
|
||||
|
||||
return (
|
||||
<Template
|
||||
{...{ kcContext, i18n, ...props }}
|
||||
displayMessage={messagesPerField.exists("global")}
|
||||
displayRequiredFields={true}
|
||||
doFetchDefaultThemeResources={true}
|
||||
headerNode={msg("registerTitle")}
|
||||
formNode={
|
||||
<form id="kc-register-form" className={cx(props.kcFormClass)} action={url.registrationAction} method="post">
|
||||
<UserProfileFormFields kcContext={kcContext} onIsFormSubmittableValueChange={setIsFomSubmittable} i18n={i18n} {...props} />
|
||||
{recaptchaRequired && (
|
||||
<div className="form-group">
|
||||
<div className={cx(props.kcInputWrapperClass)}>
|
||||
<div className="g-recaptcha" data-size="compact" data-sitekey={recaptchaSiteKey} />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<div className={cx(props.kcFormGroupClass)}>
|
||||
<div id="kc-form-options" className={cx(props.kcFormOptionsClass)}>
|
||||
<div className={cx(props.kcFormOptionsWrapperClass)}>
|
||||
<span>
|
||||
<a href={url.loginUrl}>{msg("backToLogin")}</a>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="kc-form-buttons" className={cx(props.kcFormButtonsClass)}>
|
||||
<input
|
||||
className={cx(props.kcButtonClass, props.kcButtonPrimaryClass, props.kcButtonBlockClass, props.kcButtonLargeClass)}
|
||||
type="submit"
|
||||
value={msgStr("doRegister")}
|
||||
disabled={!isFomSubmittable}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
type UserProfileFormFieldsProps = { kcContext: KcContextBase.RegisterUserProfile; i18n: I18n } & KcProps &
|
||||
Partial<Record<"BeforeField" | "AfterField", ReactComponent<{ attribute: Attribute }>>> & {
|
||||
onIsFormSubmittableValueChange: (isFormSubmittable: boolean) => void;
|
||||
};
|
||||
|
||||
const UserProfileFormFields = memo(({ kcContext, onIsFormSubmittableValueChange, i18n, ...props }: UserProfileFormFieldsProps) => {
|
||||
const { cx, css } = useCssAndCx();
|
||||
|
||||
const { advancedMsg } = i18n;
|
||||
|
||||
const {
|
||||
formValidationState: { fieldStateByAttributeName, isFormSubmittable },
|
||||
formValidationReducer,
|
||||
attributesWithPassword
|
||||
} = useFormValidationSlice({
|
||||
kcContext,
|
||||
i18n
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
onIsFormSubmittableValueChange(isFormSubmittable);
|
||||
}, [isFormSubmittable]);
|
||||
|
||||
const onChangeFactory = useCallbackFactory(
|
||||
(
|
||||
[name]: [string],
|
||||
[
|
||||
{
|
||||
target: { value }
|
||||
}
|
||||
]: [React.ChangeEvent<HTMLInputElement | HTMLSelectElement>]
|
||||
) =>
|
||||
formValidationReducer({
|
||||
"action": "update value",
|
||||
name,
|
||||
"newValue": value
|
||||
})
|
||||
);
|
||||
|
||||
const onBlurFactory = useCallbackFactory(([name]: [string]) =>
|
||||
formValidationReducer({
|
||||
"action": "focus lost",
|
||||
name
|
||||
})
|
||||
);
|
||||
|
||||
let currentGroup = "";
|
||||
|
||||
return (
|
||||
<>
|
||||
{attributesWithPassword.map((attribute, i) => {
|
||||
const { group = "", groupDisplayHeader = "", groupDisplayDescription = "" } = attribute;
|
||||
|
||||
const { value, displayableErrors } = fieldStateByAttributeName[attribute.name];
|
||||
|
||||
const formGroupClassName = cx(props.kcFormGroupClass, displayableErrors.length !== 0 && props.kcFormGroupErrorClass);
|
||||
|
||||
return (
|
||||
<Fragment key={i}>
|
||||
{group !== currentGroup && (currentGroup = group) !== "" && (
|
||||
<div className={formGroupClassName}>
|
||||
<div className={cx(props.kcContentWrapperClass)}>
|
||||
<label id={`header-${group}`} className={cx(props.kcFormGroupHeader)}>
|
||||
{advancedMsg(groupDisplayHeader) || currentGroup}
|
||||
</label>
|
||||
</div>
|
||||
{groupDisplayDescription !== "" && (
|
||||
<div className={cx(props.kcLabelWrapperClass)}>
|
||||
<label id={`description-${group}`} className={`${cx(props.kcLabelClass)}`}>
|
||||
{advancedMsg(groupDisplayDescription)}
|
||||
</label>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<div className={formGroupClassName}>
|
||||
<div className={cx(props.kcLabelWrapperClass)}>
|
||||
<label htmlFor={attribute.name} className={cx(props.kcLabelClass)}>
|
||||
{advancedMsg(attribute.displayName ?? "")}
|
||||
</label>
|
||||
{attribute.required && <>*</>}
|
||||
</div>
|
||||
<div className={cx(props.kcInputWrapperClass)}>
|
||||
{(() => {
|
||||
const { options } = attribute.validators;
|
||||
|
||||
if (options !== undefined) {
|
||||
return (
|
||||
<select
|
||||
id={attribute.name}
|
||||
name={attribute.name}
|
||||
onChange={onChangeFactory(attribute.name)}
|
||||
onBlur={onBlurFactory(attribute.name)}
|
||||
value={value}
|
||||
>
|
||||
{options.options.map(option => (
|
||||
<option key={option} value={option}>
|
||||
{option}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<input
|
||||
type={(() => {
|
||||
switch (attribute.name) {
|
||||
case "password-confirm":
|
||||
case "password":
|
||||
return "password";
|
||||
default:
|
||||
return "text";
|
||||
}
|
||||
})()}
|
||||
id={attribute.name}
|
||||
name={attribute.name}
|
||||
value={value}
|
||||
onChange={onChangeFactory(attribute.name)}
|
||||
className={cx(props.kcInputClass)}
|
||||
aria-invalid={displayableErrors.length !== 0}
|
||||
disabled={attribute.readOnly}
|
||||
autoComplete={attribute.autocomplete}
|
||||
onBlur={onBlurFactory(attribute.name)}
|
||||
/>
|
||||
);
|
||||
})()}
|
||||
{displayableErrors.length !== 0 && (
|
||||
<span
|
||||
id={`input-error-${attribute.name}`}
|
||||
className={cx(
|
||||
props.kcInputErrorMessageClass,
|
||||
css({
|
||||
"position": displayableErrors.length === 1 ? "absolute" : undefined,
|
||||
"& > span": { "display": "block" }
|
||||
})
|
||||
)}
|
||||
aria-live="polite"
|
||||
>
|
||||
{displayableErrors.map(({ errorMessage }) => errorMessage)}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</Fragment>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
);
|
||||
});
|
||||
|
||||
export default RegisterUserProfile;
|
251
src/lib/components/Template.tsx
Normal file
251
src/lib/components/Template.tsx
Normal file
@ -0,0 +1,251 @@
|
||||
import React, { useReducer, useEffect, memo } from "react";
|
||||
import type { ReactNode } from "react";
|
||||
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
||||
import { assert } from "../tools/assert";
|
||||
import { useCallbackFactory } from "powerhooks/useCallbackFactory";
|
||||
import { headInsert } from "../tools/headInsert";
|
||||
import { pathJoin } from "../../bin/tools/pathJoin";
|
||||
import { useConstCallback } from "powerhooks/useConstCallback";
|
||||
import type { KcTemplateProps } from "./KcProps";
|
||||
import { useCssAndCx } from "../tools/useCssAndCx";
|
||||
import type { I18n } from "../i18n";
|
||||
|
||||
export type TemplateProps = {
|
||||
displayInfo?: boolean;
|
||||
displayMessage?: boolean;
|
||||
displayRequiredFields?: boolean;
|
||||
displayWide?: boolean;
|
||||
showAnotherWayIfPresent?: boolean;
|
||||
headerNode: ReactNode;
|
||||
showUsernameNode?: ReactNode;
|
||||
formNode: ReactNode;
|
||||
infoNode?: ReactNode;
|
||||
/** If you write your own page you probably want
|
||||
* to avoid pulling the default theme assets.
|
||||
*/
|
||||
doFetchDefaultThemeResources: boolean;
|
||||
} & { kcContext: KcContextBase; i18n: I18n } & KcTemplateProps;
|
||||
|
||||
const Template = memo((props: TemplateProps) => {
|
||||
const {
|
||||
displayInfo = false,
|
||||
displayMessage = true,
|
||||
displayRequiredFields = false,
|
||||
displayWide = false,
|
||||
showAnotherWayIfPresent = true,
|
||||
headerNode,
|
||||
showUsernameNode = null,
|
||||
formNode,
|
||||
infoNode = null,
|
||||
kcContext,
|
||||
i18n,
|
||||
doFetchDefaultThemeResources
|
||||
} = props;
|
||||
|
||||
const { cx } = useCssAndCx();
|
||||
|
||||
const { msg, changeLocale, labelBySupportedLanguageTag, currentLanguageTag } = i18n;
|
||||
|
||||
const onChangeLanguageClickFactory = useCallbackFactory(([kcLanguageTag]: [string]) => changeLocale(kcLanguageTag));
|
||||
|
||||
const onTryAnotherWayClick = useConstCallback(() => (document.forms["kc-select-try-another-way-form" as never].submit(), false));
|
||||
|
||||
const { realm, locale, auth, url, message, isAppInitiatedAction } = kcContext;
|
||||
|
||||
const [isExtraCssLoaded, setExtraCssLoaded] = useReducer(() => true, false);
|
||||
|
||||
useEffect(() => {
|
||||
if (!doFetchDefaultThemeResources) {
|
||||
setExtraCssLoaded();
|
||||
return;
|
||||
}
|
||||
|
||||
let isUnmounted = false;
|
||||
const cleanups: (() => void)[] = [];
|
||||
|
||||
const toArr = (x: string | readonly string[] | undefined) => (typeof x === "string" ? x.split(" ") : x ?? []);
|
||||
|
||||
Promise.all(
|
||||
[
|
||||
...toArr(props.stylesCommon).map(relativePath => pathJoin(url.resourcesCommonPath, relativePath)),
|
||||
...toArr(props.styles).map(relativePath => pathJoin(url.resourcesPath, relativePath))
|
||||
]
|
||||
.reverse()
|
||||
.map(href =>
|
||||
headInsert({
|
||||
"type": "css",
|
||||
href,
|
||||
"position": "prepend"
|
||||
})
|
||||
)
|
||||
).then(() => {
|
||||
if (isUnmounted) {
|
||||
return;
|
||||
}
|
||||
|
||||
setExtraCssLoaded();
|
||||
});
|
||||
|
||||
toArr(props.scripts).forEach(relativePath =>
|
||||
headInsert({
|
||||
"type": "javascript",
|
||||
"src": pathJoin(url.resourcesPath, relativePath)
|
||||
})
|
||||
);
|
||||
|
||||
if (props.kcHtmlClass !== undefined) {
|
||||
const htmlClassList = document.getElementsByTagName("html")[0].classList;
|
||||
|
||||
const tokens = cx(props.kcHtmlClass).split(" ");
|
||||
|
||||
htmlClassList.add(...tokens);
|
||||
|
||||
cleanups.push(() => htmlClassList.remove(...tokens));
|
||||
}
|
||||
|
||||
return () => {
|
||||
isUnmounted = true;
|
||||
|
||||
cleanups.forEach(f => f());
|
||||
};
|
||||
}, [props.kcHtmlClass]);
|
||||
|
||||
if (!isExtraCssLoaded) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={cx(props.kcLoginClass)}>
|
||||
<div id="kc-header" className={cx(props.kcHeaderClass)}>
|
||||
<div id="kc-header-wrapper" className={cx(props.kcHeaderWrapperClass)}>
|
||||
{msg("loginTitleHtml", realm.displayNameHtml)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={cx(props.kcFormCardClass, displayWide && props.kcFormCardAccountClass)}>
|
||||
<header className={cx(props.kcFormHeaderClass)}>
|
||||
{realm.internationalizationEnabled && (assert(locale !== undefined), true) && locale.supported.length > 1 && (
|
||||
<div id="kc-locale">
|
||||
<div id="kc-locale-wrapper" className={cx(props.kcLocaleWrapperClass)}>
|
||||
<div className="kc-dropdown" id="kc-locale-dropdown">
|
||||
<a href="#" id="kc-current-locale-link">
|
||||
{labelBySupportedLanguageTag[currentLanguageTag]}
|
||||
</a>
|
||||
<ul>
|
||||
{locale.supported.map(({ languageTag }) => (
|
||||
<li key={languageTag} className="kc-dropdown-item">
|
||||
<a href="#" onClick={onChangeLanguageClickFactory(languageTag)}>
|
||||
{labelBySupportedLanguageTag[languageTag]}
|
||||
</a>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{!(auth !== undefined && auth.showUsername && !auth.showResetCredentials) ? (
|
||||
displayRequiredFields ? (
|
||||
<div className={cx(props.kcContentWrapperClass)}>
|
||||
<div className={cx(props.kcLabelWrapperClass, "subtitle")}>
|
||||
<span className="subtitle">
|
||||
<span className="required">*</span>
|
||||
{msg("requiredFields")}
|
||||
</span>
|
||||
</div>
|
||||
<div className="col-md-10">
|
||||
<h1 id="kc-page-title">{headerNode}</h1>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<h1 id="kc-page-title">{headerNode}</h1>
|
||||
)
|
||||
) : displayRequiredFields ? (
|
||||
<div className={cx(props.kcContentWrapperClass)}>
|
||||
<div className={cx(props.kcLabelWrapperClass, "subtitle")}>
|
||||
<span className="subtitle">
|
||||
<span className="required">*</span> {msg("requiredFields")}
|
||||
</span>
|
||||
</div>
|
||||
<div className="col-md-10">
|
||||
{showUsernameNode}
|
||||
<div className={cx(props.kcFormGroupClass)}>
|
||||
<div id="kc-username">
|
||||
<label id="kc-attempted-username">{auth?.attemptedUsername}</label>
|
||||
<a id="reset-login" href={url.loginRestartFlowUrl}>
|
||||
<div className="kc-login-tooltip">
|
||||
<i className={cx(props.kcResetFlowIcon)}></i>
|
||||
<span className="kc-tooltip-text">{msg("restartLoginTooltip")}</span>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
{showUsernameNode}
|
||||
<div className={cx(props.kcFormGroupClass)}>
|
||||
<div id="kc-username">
|
||||
<label id="kc-attempted-username">{auth?.attemptedUsername}</label>
|
||||
<a id="reset-login" href={url.loginRestartFlowUrl}>
|
||||
<div className="kc-login-tooltip">
|
||||
<i className={cx(props.kcResetFlowIcon)}></i>
|
||||
<span className="kc-tooltip-text">{msg("restartLoginTooltip")}</span>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</header>
|
||||
<div id="kc-content">
|
||||
<div id="kc-content-wrapper">
|
||||
{/* App-initiated actions should not see warning messages about the need to complete the action during login. */}
|
||||
{displayMessage && message !== undefined && (message.type !== "warning" || !isAppInitiatedAction) && (
|
||||
<div className={cx("alert", `alert-${message.type}`)}>
|
||||
{message.type === "success" && <span className={cx(props.kcFeedbackSuccessIcon)}></span>}
|
||||
{message.type === "warning" && <span className={cx(props.kcFeedbackWarningIcon)}></span>}
|
||||
{message.type === "error" && <span className={cx(props.kcFeedbackErrorIcon)}></span>}
|
||||
{message.type === "info" && <span className={cx(props.kcFeedbackInfoIcon)}></span>}
|
||||
<span
|
||||
className="kc-feedback-text"
|
||||
dangerouslySetInnerHTML={{
|
||||
"__html": message.summary
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{formNode}
|
||||
{auth !== undefined && auth.showTryAnotherWayLink && showAnotherWayIfPresent && (
|
||||
<form
|
||||
id="kc-select-try-another-way-form"
|
||||
action={url.loginAction}
|
||||
method="post"
|
||||
className={cx(displayWide && props.kcContentWrapperClass)}
|
||||
>
|
||||
<div className={cx(displayWide && [props.kcFormSocialAccountContentClass, props.kcFormSocialAccountClass])}>
|
||||
<div className={cx(props.kcFormGroupClass)}>
|
||||
<input type="hidden" name="tryAnotherWay" value="on" />
|
||||
<a href="#" id="try-another-way" onClick={onTryAnotherWayClick}>
|
||||
{msg("doTryAnotherWay")}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
)}
|
||||
{displayInfo && (
|
||||
<div id="kc-info" className={cx(props.kcSignUpClass)}>
|
||||
<div id="kc-info-wrapper" className={cx(props.kcInfoAreaWrapperClass)}>
|
||||
{infoNode}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
export default Template;
|
107
src/lib/components/Terms.tsx
Normal file
107
src/lib/components/Terms.tsx
Normal file
@ -0,0 +1,107 @@
|
||||
import React, { useEffect, memo } from "react";
|
||||
import Template from "./Template";
|
||||
import type { KcProps } from "./KcProps";
|
||||
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
||||
import { useCssAndCx } from "../tools/useCssAndCx";
|
||||
import { Evt } from "evt";
|
||||
import { useRerenderOnStateChange } from "evt/hooks";
|
||||
import { assert } from "tsafe/assert";
|
||||
import { fallbackLanguageTag } from "../i18n";
|
||||
import type { I18n } from "../i18n";
|
||||
import memoize from "memoizee";
|
||||
import { useConst } from "powerhooks/useConst";
|
||||
import { useConstCallback } from "powerhooks/useConstCallback";
|
||||
|
||||
export const evtTermMarkdown = Evt.create<string | undefined>(undefined);
|
||||
|
||||
export type KcContextLike = {
|
||||
pageId: KcContextBase["pageId"];
|
||||
locale?: {
|
||||
currentLanguageTag: string;
|
||||
};
|
||||
};
|
||||
|
||||
assert<KcContextBase extends KcContextLike ? true : false>();
|
||||
|
||||
/** Allow to avoid bundling the terms and download it on demand*/
|
||||
export function useDownloadTerms(params: {
|
||||
kcContext: KcContextLike;
|
||||
downloadTermMarkdown: (params: { currentLanguageTag: string }) => Promise<string>;
|
||||
}) {
|
||||
const { kcContext } = params;
|
||||
|
||||
const { downloadTermMarkdownMemoized } = (function useClosure() {
|
||||
const { downloadTermMarkdown } = params;
|
||||
|
||||
const downloadTermMarkdownConst = useConstCallback(downloadTermMarkdown);
|
||||
|
||||
const downloadTermMarkdownMemoized = useConst(() =>
|
||||
memoize((currentLanguageTag: string) => downloadTermMarkdownConst({ currentLanguageTag }), { "promise": true })
|
||||
);
|
||||
|
||||
return { downloadTermMarkdownMemoized };
|
||||
})();
|
||||
|
||||
useEffect(() => {
|
||||
if (kcContext.pageId !== "terms.ftl") {
|
||||
return;
|
||||
}
|
||||
|
||||
downloadTermMarkdownMemoized(kcContext.locale?.currentLanguageTag ?? fallbackLanguageTag).then(
|
||||
thermMarkdown => (evtTermMarkdown.state = thermMarkdown)
|
||||
);
|
||||
}, []);
|
||||
}
|
||||
|
||||
const Terms = memo(({ kcContext, i18n, ...props }: { kcContext: KcContextBase.Terms; i18n: I18n } & KcProps) => {
|
||||
const { msg, msgStr } = i18n;
|
||||
|
||||
useRerenderOnStateChange(evtTermMarkdown);
|
||||
|
||||
const { cx } = useCssAndCx();
|
||||
|
||||
const { url } = kcContext;
|
||||
|
||||
if (evtTermMarkdown.state === undefined) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Template
|
||||
{...{ kcContext, i18n, ...props }}
|
||||
doFetchDefaultThemeResources={true}
|
||||
displayMessage={false}
|
||||
headerNode={msg("termsTitle")}
|
||||
formNode={
|
||||
<>
|
||||
<div id="kc-terms-text">{evtTermMarkdown.state}</div>
|
||||
<form className="form-actions" action={url.loginAction} method="POST">
|
||||
<input
|
||||
className={cx(
|
||||
props.kcButtonClass,
|
||||
props.kcButtonClass,
|
||||
props.kcButtonClass,
|
||||
props.kcButtonPrimaryClass,
|
||||
props.kcButtonLargeClass
|
||||
)}
|
||||
name="accept"
|
||||
id="kc-accept"
|
||||
type="submit"
|
||||
value={msgStr("doAccept")}
|
||||
/>
|
||||
<input
|
||||
className={cx(props.kcButtonClass, props.kcButtonDefaultClass, props.kcButtonLargeClass)}
|
||||
name="cancel"
|
||||
id="kc-decline"
|
||||
type="submit"
|
||||
value={msgStr("doDecline")}
|
||||
/>
|
||||
</form>
|
||||
<div className="clearfix" />
|
||||
</>
|
||||
}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
export default Terms;
|
392
src/lib/getKcContext/KcContextBase.ts
Normal file
392
src/lib/getKcContext/KcContextBase.ts
Normal file
@ -0,0 +1,392 @@
|
||||
import type { PageId } from "../../bin/keycloakify/generateFtl";
|
||||
import { assert } from "tsafe/assert";
|
||||
import type { Equals } from "tsafe";
|
||||
import type { MessageKeyBase } from "../i18n";
|
||||
|
||||
type ExtractAfterStartingWith<Prefix extends string, StrEnum> = StrEnum extends `${Prefix}${infer U}` ? U : never;
|
||||
|
||||
/** Take theses type definition with a grain of salt.
|
||||
* Some values might be undefined on some pages.
|
||||
* (ex: url.loginAction is undefined on error.ftl)
|
||||
*/
|
||||
export type KcContextBase =
|
||||
| KcContextBase.Login
|
||||
| KcContextBase.Register
|
||||
| KcContextBase.RegisterUserProfile
|
||||
| KcContextBase.Info
|
||||
| KcContextBase.Error
|
||||
| KcContextBase.LoginResetPassword
|
||||
| KcContextBase.LoginVerifyEmail
|
||||
| KcContextBase.Terms
|
||||
| KcContextBase.LoginOtp
|
||||
| KcContextBase.LoginUpdatePassword
|
||||
| KcContextBase.LoginUpdateProfile
|
||||
| KcContextBase.LoginIdpLinkConfirm
|
||||
| KcContextBase.LoginIdpLinkEmail
|
||||
| KcContextBase.LoginPageExpired
|
||||
| KcContextBase.LoginConfigTotp
|
||||
| KcContextBase.LogoutConfirm;
|
||||
|
||||
export declare namespace KcContextBase {
|
||||
export type Common = {
|
||||
url: {
|
||||
loginAction: string;
|
||||
resourcesPath: string;
|
||||
resourcesCommonPath: string;
|
||||
loginRestartFlowUrl: string;
|
||||
loginUrl: string;
|
||||
};
|
||||
realm: {
|
||||
name: string;
|
||||
displayName?: string;
|
||||
displayNameHtml?: string;
|
||||
internationalizationEnabled: boolean;
|
||||
registrationEmailAsUsername: boolean;
|
||||
};
|
||||
/** Undefined if !realm.internationalizationEnabled */
|
||||
locale?: {
|
||||
supported: {
|
||||
url: string;
|
||||
label: string;
|
||||
languageTag: string;
|
||||
}[];
|
||||
currentLanguageTag: string;
|
||||
};
|
||||
auth?: {
|
||||
showUsername?: boolean;
|
||||
showResetCredentials?: boolean;
|
||||
showTryAnotherWayLink?: boolean;
|
||||
attemptedUsername?: string;
|
||||
};
|
||||
scripts: string[];
|
||||
message?: {
|
||||
type: "success" | "warning" | "error" | "info";
|
||||
summary: string;
|
||||
};
|
||||
client: {
|
||||
clientId: string;
|
||||
name?: string;
|
||||
description?: string;
|
||||
};
|
||||
isAppInitiatedAction: boolean;
|
||||
messagesPerField: {
|
||||
printIfExists: <T>(fieldName: string, x: T) => T | undefined;
|
||||
existsError: (fieldName: string) => boolean;
|
||||
get: (fieldName: string) => string;
|
||||
exists: (fieldName: string) => boolean;
|
||||
};
|
||||
};
|
||||
|
||||
export type Login = Common & {
|
||||
pageId: "login.ftl";
|
||||
url: {
|
||||
loginResetCredentialsUrl: string;
|
||||
registrationUrl: string;
|
||||
};
|
||||
realm: {
|
||||
loginWithEmailAllowed: boolean;
|
||||
rememberMe: boolean;
|
||||
password: boolean;
|
||||
resetPasswordAllowed: boolean;
|
||||
registrationAllowed: boolean;
|
||||
};
|
||||
auth: {
|
||||
selectedCredential?: string;
|
||||
};
|
||||
registrationDisabled: boolean;
|
||||
login: {
|
||||
username?: string;
|
||||
rememberMe?: boolean;
|
||||
};
|
||||
usernameEditDisabled: boolean;
|
||||
social: {
|
||||
displayInfo: boolean;
|
||||
providers?: {
|
||||
loginUrl: string;
|
||||
alias: string;
|
||||
providerId: string;
|
||||
displayName: string;
|
||||
}[];
|
||||
};
|
||||
};
|
||||
|
||||
export type RegisterCommon = Common & {
|
||||
url: {
|
||||
registrationAction: string;
|
||||
};
|
||||
passwordRequired: boolean;
|
||||
recaptchaRequired: boolean;
|
||||
recaptchaSiteKey?: string;
|
||||
social: {
|
||||
displayInfo: boolean;
|
||||
providers?: {
|
||||
loginUrl: string;
|
||||
alias: string;
|
||||
providerId: string;
|
||||
displayName: string;
|
||||
}[];
|
||||
};
|
||||
};
|
||||
|
||||
export type Register = RegisterCommon & {
|
||||
pageId: "register.ftl";
|
||||
register: {
|
||||
formData: {
|
||||
firstName?: string;
|
||||
displayName?: string;
|
||||
lastName?: string;
|
||||
email?: string;
|
||||
username?: string;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
export type RegisterUserProfile = RegisterCommon & {
|
||||
pageId: "register-user-profile.ftl";
|
||||
profile: {
|
||||
context: "REGISTRATION_PROFILE";
|
||||
attributes: Attribute[];
|
||||
attributesByName: Record<string, Attribute>;
|
||||
};
|
||||
};
|
||||
|
||||
export type Info = Common & {
|
||||
pageId: "info.ftl";
|
||||
messageHeader?: string;
|
||||
requiredActions?: ExtractAfterStartingWith<"requiredAction.", MessageKeyBase>[];
|
||||
skipLink: boolean;
|
||||
pageRedirectUri?: string;
|
||||
actionUri?: string;
|
||||
client: {
|
||||
baseUrl?: string;
|
||||
};
|
||||
};
|
||||
|
||||
export type Error = Common & {
|
||||
pageId: "error.ftl";
|
||||
client?: {
|
||||
baseUrl?: string;
|
||||
};
|
||||
message: NonNullable<Common["message"]>;
|
||||
};
|
||||
|
||||
export type LoginResetPassword = Common & {
|
||||
pageId: "login-reset-password.ftl";
|
||||
realm: {
|
||||
loginWithEmailAllowed: boolean;
|
||||
};
|
||||
};
|
||||
|
||||
export type LoginVerifyEmail = Common & {
|
||||
pageId: "login-verify-email.ftl";
|
||||
//NOTE: Optional because maybe it wasn't defined in older keycloak versions.
|
||||
user?: {
|
||||
email: string;
|
||||
};
|
||||
};
|
||||
|
||||
export type Terms = Common & {
|
||||
pageId: "terms.ftl";
|
||||
};
|
||||
|
||||
export type LoginOtp = Common & {
|
||||
pageId: "login-otp.ftl";
|
||||
otpLogin: {
|
||||
userOtpCredentials: { id: string; userLabel: string }[];
|
||||
};
|
||||
};
|
||||
|
||||
export type LoginUpdatePassword = Common & {
|
||||
pageId: "login-update-password.ftl";
|
||||
username: string;
|
||||
};
|
||||
|
||||
export type LoginUpdateProfile = Common & {
|
||||
pageId: "login-update-profile.ftl";
|
||||
user: {
|
||||
editUsernameAllowed: boolean;
|
||||
username?: string;
|
||||
email?: string;
|
||||
firstName?: string;
|
||||
lastName?: string;
|
||||
};
|
||||
};
|
||||
|
||||
export type LoginIdpLinkConfirm = Common & {
|
||||
pageId: "login-idp-link-confirm.ftl";
|
||||
idpAlias: string;
|
||||
};
|
||||
|
||||
export type LoginIdpLinkEmail = Common & {
|
||||
pageId: "login-idp-link-email.ftl";
|
||||
brokerContext: {
|
||||
username: string;
|
||||
};
|
||||
idpAlias: string;
|
||||
};
|
||||
|
||||
export type LoginPageExpired = Common & {
|
||||
pageId: "login-page-expired.ftl";
|
||||
};
|
||||
|
||||
export type LoginConfigTotp = Common & {
|
||||
pageId: "login-config-totp.ftl";
|
||||
mode?: "qr" | "manual" | undefined | null;
|
||||
totp: {
|
||||
totpSecretEncoded: string;
|
||||
qrUrl: string;
|
||||
policy: {
|
||||
supportedApplications: string[];
|
||||
algorithm: "HmacSHA1" | "HmacSHA256" | "HmacSHA512";
|
||||
digits: number;
|
||||
lookAheadWindow: number;
|
||||
} & (
|
||||
| {
|
||||
type: "totp";
|
||||
period: number;
|
||||
}
|
||||
| {
|
||||
type: "hotp";
|
||||
initialCounter: number;
|
||||
}
|
||||
);
|
||||
totpSecretQrCode: string;
|
||||
manualUrl: string;
|
||||
totpSecret: string;
|
||||
otpCredentials: { id: string; userLabel: string }[];
|
||||
};
|
||||
};
|
||||
|
||||
export type LogoutConfirm = Common & {
|
||||
pageId: "logout-confirm.ftl";
|
||||
url: {
|
||||
logoutConfirmAction: string;
|
||||
};
|
||||
client: {
|
||||
baseUrl?: string;
|
||||
};
|
||||
logoutConfirm: {
|
||||
code: string;
|
||||
skipLink?: boolean;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export type Attribute = {
|
||||
name: string;
|
||||
displayName?: string;
|
||||
required: boolean;
|
||||
value?: string;
|
||||
group?: string;
|
||||
groupDisplayHeader?: string;
|
||||
groupDisplayDescription?: string;
|
||||
readOnly: boolean;
|
||||
validators: Validators;
|
||||
annotations: Record<string, string>;
|
||||
groupAnnotations: Record<string, string>;
|
||||
autocomplete?:
|
||||
| "on"
|
||||
| "off"
|
||||
| "name"
|
||||
| "honorific-prefix"
|
||||
| "given-name"
|
||||
| "additional-name"
|
||||
| "family-name"
|
||||
| "honorific-suffix"
|
||||
| "nickname"
|
||||
| "email"
|
||||
| "username"
|
||||
| "new-password"
|
||||
| "current-password"
|
||||
| "one-time-code"
|
||||
| "organization-title"
|
||||
| "organization"
|
||||
| "street-address"
|
||||
| "address-line1"
|
||||
| "address-line2"
|
||||
| "address-line3"
|
||||
| "address-level4"
|
||||
| "address-level3"
|
||||
| "address-level2"
|
||||
| "address-level1"
|
||||
| "country"
|
||||
| "country-name"
|
||||
| "postal-code"
|
||||
| "cc-name"
|
||||
| "cc-given-name"
|
||||
| "cc-additional-name"
|
||||
| "cc-family-name"
|
||||
| "cc-number"
|
||||
| "cc-exp"
|
||||
| "cc-exp-month"
|
||||
| "cc-exp-year"
|
||||
| "cc-csc"
|
||||
| "cc-type"
|
||||
| "transaction-currency"
|
||||
| "transaction-amount"
|
||||
| "language"
|
||||
| "bday"
|
||||
| "bday-day"
|
||||
| "bday-month"
|
||||
| "bday-year"
|
||||
| "sex"
|
||||
| "tel"
|
||||
| "tel-country-code"
|
||||
| "tel-national"
|
||||
| "tel-area-code"
|
||||
| "tel-local"
|
||||
| "tel-extension"
|
||||
| "impp"
|
||||
| "url"
|
||||
| "photo";
|
||||
};
|
||||
|
||||
export type Validators = Partial<{
|
||||
length: Validators.DoIgnoreEmpty & Validators.Range;
|
||||
double: Validators.DoIgnoreEmpty & Validators.Range;
|
||||
integer: Validators.DoIgnoreEmpty & Validators.Range;
|
||||
email: Validators.DoIgnoreEmpty;
|
||||
"up-immutable-attribute": {};
|
||||
"up-attribute-required-by-metadata-value": {};
|
||||
"up-username-has-value": {};
|
||||
"up-duplicate-username": {};
|
||||
"up-username-mutation": {};
|
||||
"up-email-exists-as-username": {};
|
||||
"up-blank-attribute-value": Validators.ErrorMessage & {
|
||||
"fail-on-null": boolean;
|
||||
};
|
||||
"up-duplicate-email": {};
|
||||
"local-date": Validators.DoIgnoreEmpty;
|
||||
pattern: Validators.DoIgnoreEmpty & Validators.ErrorMessage & { pattern: string };
|
||||
"person-name-prohibited-characters": Validators.DoIgnoreEmpty & Validators.ErrorMessage;
|
||||
uri: Validators.DoIgnoreEmpty;
|
||||
"username-prohibited-characters": Validators.DoIgnoreEmpty & Validators.ErrorMessage;
|
||||
/** Made up validator that only exists in Keycloakify */
|
||||
_compareToOther: Validators.DoIgnoreEmpty &
|
||||
Validators.ErrorMessage & {
|
||||
name: string;
|
||||
shouldBe: "equal" | "different";
|
||||
};
|
||||
options: Validators.Options;
|
||||
}>;
|
||||
|
||||
export declare namespace Validators {
|
||||
export type DoIgnoreEmpty = {
|
||||
"ignore.empty.value"?: boolean;
|
||||
};
|
||||
|
||||
export type ErrorMessage = {
|
||||
"error-message"?: string;
|
||||
};
|
||||
|
||||
export type Range = {
|
||||
/** "0", "1", "2"... yeah I know, don't tell me */
|
||||
min?: `${number}`;
|
||||
max?: `${number}`;
|
||||
};
|
||||
export type Options = {
|
||||
options: string[];
|
||||
};
|
||||
}
|
||||
|
||||
assert<Equals<KcContextBase["pageId"], PageId>>();
|
108
src/lib/getKcContext/getKcContext.ts
Normal file
108
src/lib/getKcContext/getKcContext.ts
Normal file
@ -0,0 +1,108 @@
|
||||
import type { KcContextBase, Attribute } from "./KcContextBase";
|
||||
import { kcContextMocks, kcContextCommonMock } from "./kcContextMocks";
|
||||
import type { DeepPartial } from "../tools/DeepPartial";
|
||||
import { deepAssign } from "../tools/deepAssign";
|
||||
import { id } from "tsafe/id";
|
||||
import { exclude } from "tsafe/exclude";
|
||||
import { assert } from "tsafe/assert";
|
||||
import type { ExtendsKcContextBase } from "./getKcContextFromWindow";
|
||||
import { getKcContextFromWindow } from "./getKcContextFromWindow";
|
||||
import { pathJoin } from "../../bin/tools/pathJoin";
|
||||
import { pathBasename } from "../tools/pathBasename";
|
||||
import { mockTestingResourcesCommonPath } from "../../bin/mockTestingResourcesPath";
|
||||
|
||||
export function getKcContext<KcContextExtended extends { pageId: string } = never>(params?: {
|
||||
mockPageId?: ExtendsKcContextBase<KcContextExtended>["pageId"];
|
||||
mockData?: readonly DeepPartial<ExtendsKcContextBase<KcContextExtended>>[];
|
||||
}): { kcContext: ExtendsKcContextBase<KcContextExtended> | undefined } {
|
||||
const { mockPageId, mockData } = params ?? {};
|
||||
|
||||
if (mockPageId !== undefined) {
|
||||
//TODO maybe trow if no mock fo custom page
|
||||
|
||||
const kcContextDefaultMock = kcContextMocks.find(({ pageId }) => pageId === mockPageId);
|
||||
|
||||
const partialKcContextCustomMock = mockData?.find(({ pageId }) => pageId === mockPageId);
|
||||
|
||||
if (kcContextDefaultMock === undefined && partialKcContextCustomMock === undefined) {
|
||||
console.warn(
|
||||
[
|
||||
`WARNING: You declared the non build in page ${mockPageId} but you didn't `,
|
||||
`provide mock data needed to debug the page outside of Keycloak as you are trying to do now.`,
|
||||
`Please check the documentation of the getKcContext function`
|
||||
].join("\n")
|
||||
);
|
||||
}
|
||||
|
||||
const kcContext: any = {};
|
||||
|
||||
deepAssign({
|
||||
"target": kcContext,
|
||||
"source": kcContextDefaultMock !== undefined ? kcContextDefaultMock : { "pageId": mockPageId, ...kcContextCommonMock }
|
||||
});
|
||||
|
||||
if (partialKcContextCustomMock !== undefined) {
|
||||
deepAssign({
|
||||
"target": kcContext,
|
||||
"source": partialKcContextCustomMock
|
||||
});
|
||||
|
||||
if (partialKcContextCustomMock.pageId === "register-user-profile.ftl") {
|
||||
assert(kcContextDefaultMock?.pageId === "register-user-profile.ftl");
|
||||
|
||||
const { attributes } = kcContextDefaultMock.profile;
|
||||
|
||||
id<KcContextBase.RegisterUserProfile>(kcContext).profile.attributes = [];
|
||||
id<KcContextBase.RegisterUserProfile>(kcContext).profile.attributesByName = {};
|
||||
|
||||
const partialAttributes = [
|
||||
...((partialKcContextCustomMock as DeepPartial<KcContextBase.RegisterUserProfile>).profile?.attributes ?? [])
|
||||
].filter(exclude(undefined));
|
||||
|
||||
attributes.forEach(attribute => {
|
||||
const partialAttribute = partialAttributes.find(({ name }) => name === attribute.name);
|
||||
|
||||
const augmentedAttribute: Attribute = {} as any;
|
||||
|
||||
deepAssign({
|
||||
"target": augmentedAttribute,
|
||||
"source": attribute
|
||||
});
|
||||
|
||||
if (partialAttribute !== undefined) {
|
||||
partialAttributes.splice(partialAttributes.indexOf(partialAttribute), 1);
|
||||
|
||||
deepAssign({
|
||||
"target": augmentedAttribute,
|
||||
"source": partialAttribute
|
||||
});
|
||||
}
|
||||
|
||||
id<KcContextBase.RegisterUserProfile>(kcContext).profile.attributes.push(augmentedAttribute);
|
||||
id<KcContextBase.RegisterUserProfile>(kcContext).profile.attributesByName[augmentedAttribute.name] = augmentedAttribute;
|
||||
});
|
||||
|
||||
partialAttributes.forEach(partialAttribute => {
|
||||
const { name } = partialAttribute;
|
||||
|
||||
assert(name !== undefined, "If you define a mock attribute it must have at least a name");
|
||||
|
||||
id<KcContextBase.RegisterUserProfile>(kcContext).profile.attributes.push(partialAttribute as any);
|
||||
id<KcContextBase.RegisterUserProfile>(kcContext).profile.attributesByName[name] = partialAttribute as any;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return { kcContext };
|
||||
}
|
||||
|
||||
const kcContext = getKcContextFromWindow<KcContextExtended>();
|
||||
|
||||
if (kcContext !== undefined) {
|
||||
const { url } = kcContext;
|
||||
|
||||
url.resourcesCommonPath = pathJoin(url.resourcesPath, pathBasename(mockTestingResourcesCommonPath));
|
||||
}
|
||||
|
||||
return { kcContext };
|
||||
}
|
11
src/lib/getKcContext/getKcContextFromWindow.ts
Normal file
11
src/lib/getKcContext/getKcContextFromWindow.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import type { KcContextBase } from "./KcContextBase";
|
||||
import type { AndByDiscriminatingKey } from "../tools/AndByDiscriminatingKey";
|
||||
import { ftlValuesGlobalName } from "../../bin/keycloakify/ftlValuesGlobalName";
|
||||
|
||||
export type ExtendsKcContextBase<KcContextExtended extends { pageId: string }> = [KcContextExtended] extends [never]
|
||||
? KcContextBase
|
||||
: AndByDiscriminatingKey<"pageId", KcContextExtended & KcContextBase.Common, KcContextBase>;
|
||||
|
||||
export function getKcContextFromWindow<KcContextExtended extends { pageId: string } = never>(): ExtendsKcContextBase<KcContextExtended> | undefined {
|
||||
return typeof window === "undefined" ? undefined : (window as any)[ftlValuesGlobalName];
|
||||
}
|
3
src/lib/getKcContext/index.ts
Normal file
3
src/lib/getKcContext/index.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export type { KcContextBase, Attribute, Validators } from "./KcContextBase";
|
||||
export type { ExtendsKcContextBase } from "./getKcContextFromWindow";
|
||||
export { getKcContext } from "./getKcContext";
|
1
src/lib/getKcContext/kcContextMocks/index.ts
Normal file
1
src/lib/getKcContext/kcContextMocks/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from "./kcContextMocks";
|
427
src/lib/getKcContext/kcContextMocks/kcContextMocks.ts
Normal file
427
src/lib/getKcContext/kcContextMocks/kcContextMocks.ts
Normal file
@ -0,0 +1,427 @@
|
||||
import "minimal-polyfills/Object.fromEntries";
|
||||
import type { KcContextBase, Attribute } from "../KcContextBase";
|
||||
//NOTE: Aside because we want to be able to import them from node
|
||||
import { mockTestingResourcesCommonPath, mockTestingResourcesPath } from "../../../bin/mockTestingResourcesPath";
|
||||
import { id } from "tsafe/id";
|
||||
import { pathJoin } from "../../../bin/tools/pathJoin";
|
||||
|
||||
const PUBLIC_URL = process.env["PUBLIC_URL"] ?? "/";
|
||||
|
||||
export const kcContextCommonMock: KcContextBase.Common = {
|
||||
"url": {
|
||||
"loginAction": "#",
|
||||
"resourcesPath": pathJoin(PUBLIC_URL, mockTestingResourcesPath),
|
||||
"resourcesCommonPath": pathJoin(PUBLIC_URL, mockTestingResourcesCommonPath),
|
||||
"loginRestartFlowUrl": "/auth/realms/myrealm/login-actions/restart?client_id=account&tab_id=HoAx28ja4xg",
|
||||
"loginUrl": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg"
|
||||
},
|
||||
"realm": {
|
||||
"name": "myrealm",
|
||||
"displayName": "myrealm",
|
||||
"displayNameHtml": "myrealm",
|
||||
"internationalizationEnabled": true,
|
||||
"registrationEmailAsUsername": false
|
||||
},
|
||||
"messagesPerField": {
|
||||
"printIfExists": (...[, x]) => x,
|
||||
"existsError": () => true,
|
||||
"get": key => `Fake error for ${key}`,
|
||||
"exists": () => true
|
||||
},
|
||||
"locale": {
|
||||
"supported": [
|
||||
/* spell-checker: disable */
|
||||
{
|
||||
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=de",
|
||||
"label": "Deutsch",
|
||||
"languageTag": "de"
|
||||
},
|
||||
{
|
||||
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=no",
|
||||
"label": "Norsk",
|
||||
"languageTag": "no"
|
||||
},
|
||||
{
|
||||
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=ru",
|
||||
"label": "Русский",
|
||||
"languageTag": "ru"
|
||||
},
|
||||
{
|
||||
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=sv",
|
||||
"label": "Svenska",
|
||||
"languageTag": "sv"
|
||||
},
|
||||
{
|
||||
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=pt-BR",
|
||||
"label": "Português (Brasil)",
|
||||
"languageTag": "pt-BR"
|
||||
},
|
||||
{
|
||||
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=lt",
|
||||
"label": "Lietuvių",
|
||||
"languageTag": "lt"
|
||||
},
|
||||
{
|
||||
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=en",
|
||||
"label": "English",
|
||||
"languageTag": "en"
|
||||
},
|
||||
{
|
||||
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=it",
|
||||
"label": "Italiano",
|
||||
"languageTag": "it"
|
||||
},
|
||||
{
|
||||
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=fr",
|
||||
"label": "Français",
|
||||
"languageTag": "fr"
|
||||
},
|
||||
{
|
||||
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=zh-CN",
|
||||
"label": "中文简体",
|
||||
"languageTag": "zh-CN"
|
||||
},
|
||||
{
|
||||
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=es",
|
||||
"label": "Español",
|
||||
"languageTag": "es"
|
||||
},
|
||||
{
|
||||
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=cs",
|
||||
"label": "Čeština",
|
||||
"languageTag": "cs"
|
||||
},
|
||||
{
|
||||
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=ja",
|
||||
"label": "日本語",
|
||||
"languageTag": "ja"
|
||||
},
|
||||
{
|
||||
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=sk",
|
||||
"label": "Slovenčina",
|
||||
"languageTag": "sk"
|
||||
},
|
||||
{
|
||||
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=pl",
|
||||
"label": "Polski",
|
||||
"languageTag": "pl"
|
||||
},
|
||||
{
|
||||
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=ca",
|
||||
"label": "Català",
|
||||
"languageTag": "ca"
|
||||
},
|
||||
{
|
||||
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=nl",
|
||||
"label": "Nederlands",
|
||||
"languageTag": "nl"
|
||||
},
|
||||
{
|
||||
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=tr",
|
||||
"label": "Türkçe",
|
||||
"languageTag": "tr"
|
||||
}
|
||||
/* spell-checker: enable */
|
||||
],
|
||||
"currentLanguageTag": "en"
|
||||
},
|
||||
"auth": {
|
||||
"showUsername": false,
|
||||
"showResetCredentials": false,
|
||||
"showTryAnotherWayLink": false
|
||||
},
|
||||
"client": {
|
||||
"clientId": "myApp"
|
||||
},
|
||||
"scripts": [],
|
||||
"message": {
|
||||
"type": "success",
|
||||
"summary": "This is a test message"
|
||||
},
|
||||
"isAppInitiatedAction": false
|
||||
};
|
||||
|
||||
const loginUrl = {
|
||||
...kcContextCommonMock.url,
|
||||
"loginResetCredentialsUrl": "/auth/realms/myrealm/login-actions/reset-credentials?client_id=account&tab_id=HoAx28ja4xg",
|
||||
"registrationUrl": "/auth/realms/myrealm/login-actions/registration?client_id=account&tab_id=HoAx28ja4xg"
|
||||
};
|
||||
|
||||
export const kcContextMocks: KcContextBase[] = [
|
||||
id<KcContextBase.Login>({
|
||||
...kcContextCommonMock,
|
||||
"pageId": "login.ftl",
|
||||
"url": loginUrl,
|
||||
"realm": {
|
||||
...kcContextCommonMock.realm,
|
||||
"loginWithEmailAllowed": true,
|
||||
"rememberMe": true,
|
||||
"password": true,
|
||||
"resetPasswordAllowed": true,
|
||||
"registrationAllowed": true
|
||||
},
|
||||
"auth": kcContextCommonMock.auth!,
|
||||
"social": {
|
||||
"displayInfo": true
|
||||
},
|
||||
"usernameEditDisabled": false,
|
||||
"login": {
|
||||
"rememberMe": false
|
||||
},
|
||||
"registrationDisabled": false
|
||||
}),
|
||||
...(() => {
|
||||
const registerCommon: KcContextBase.RegisterCommon = {
|
||||
...kcContextCommonMock,
|
||||
"url": {
|
||||
...loginUrl,
|
||||
"registrationAction":
|
||||
"http://localhost:8080/auth/realms/myrealm/login-actions/registration?session_code=gwZdUeO7pbYpFTRxiIxRg_QtzMbtFTKrNu6XW_f8asM&execution=12146ce0-b139-4bbd-b25b-0eccfee6577e&client_id=account&tab_id=uS8lYfebLa0"
|
||||
},
|
||||
"scripts": [],
|
||||
"isAppInitiatedAction": false,
|
||||
"passwordRequired": true,
|
||||
"recaptchaRequired": false,
|
||||
"social": {
|
||||
"displayInfo": true
|
||||
}
|
||||
};
|
||||
|
||||
return [
|
||||
id<KcContextBase.Register>({
|
||||
"pageId": "register.ftl",
|
||||
...registerCommon,
|
||||
"register": {
|
||||
"formData": {}
|
||||
}
|
||||
}),
|
||||
id<KcContextBase.RegisterUserProfile>({
|
||||
"pageId": "register-user-profile.ftl",
|
||||
...registerCommon,
|
||||
"profile": {
|
||||
"context": "REGISTRATION_PROFILE" as const,
|
||||
...(() => {
|
||||
const attributes: Attribute[] = [
|
||||
{
|
||||
"validators": {
|
||||
"username-prohibited-characters": {
|
||||
"ignore.empty.value": true
|
||||
},
|
||||
"up-username-has-value": {},
|
||||
"length": {
|
||||
"ignore.empty.value": true,
|
||||
"min": "3",
|
||||
"max": "255"
|
||||
},
|
||||
"up-duplicate-username": {},
|
||||
"up-username-mutation": {}
|
||||
},
|
||||
"displayName": "${username}",
|
||||
"annotations": {},
|
||||
"required": true,
|
||||
"groupAnnotations": {},
|
||||
"autocomplete": "username",
|
||||
"readOnly": false,
|
||||
"name": "username",
|
||||
"value": "xxxx"
|
||||
},
|
||||
{
|
||||
"validators": {
|
||||
"up-email-exists-as-username": {},
|
||||
"length": {
|
||||
"max": "255",
|
||||
"ignore.empty.value": true
|
||||
},
|
||||
"up-blank-attribute-value": {
|
||||
"error-message": "missingEmailMessage",
|
||||
"fail-on-null": false
|
||||
},
|
||||
"up-duplicate-email": {},
|
||||
"email": {
|
||||
"ignore.empty.value": true
|
||||
},
|
||||
"pattern": {
|
||||
"ignore.empty.value": true,
|
||||
"pattern": "gmail\\.com$"
|
||||
}
|
||||
},
|
||||
"displayName": "${email}",
|
||||
"annotations": {},
|
||||
"required": true,
|
||||
"groupAnnotations": {},
|
||||
"autocomplete": "email",
|
||||
"readOnly": false,
|
||||
"name": "email"
|
||||
},
|
||||
{
|
||||
"validators": {
|
||||
"length": {
|
||||
"max": "255",
|
||||
"ignore.empty.value": true
|
||||
},
|
||||
"person-name-prohibited-characters": {
|
||||
"ignore.empty.value": true
|
||||
},
|
||||
"up-immutable-attribute": {},
|
||||
"up-attribute-required-by-metadata-value": {}
|
||||
},
|
||||
"displayName": "${firstName}",
|
||||
"annotations": {},
|
||||
"required": true,
|
||||
"groupAnnotations": {},
|
||||
"readOnly": false,
|
||||
"name": "firstName"
|
||||
},
|
||||
{
|
||||
"validators": {
|
||||
"length": {
|
||||
"max": "255",
|
||||
"ignore.empty.value": true
|
||||
},
|
||||
"person-name-prohibited-characters": {
|
||||
"ignore.empty.value": true
|
||||
},
|
||||
"up-immutable-attribute": {},
|
||||
"up-attribute-required-by-metadata-value": {}
|
||||
},
|
||||
"displayName": "${lastName}",
|
||||
"annotations": {},
|
||||
"required": true,
|
||||
"groupAnnotations": {},
|
||||
"readOnly": false,
|
||||
"name": "lastName"
|
||||
}
|
||||
];
|
||||
|
||||
return {
|
||||
attributes,
|
||||
"attributesByName": Object.fromEntries(attributes.map(attribute => [attribute.name, attribute])) as any
|
||||
} as any;
|
||||
})()
|
||||
}
|
||||
})
|
||||
];
|
||||
})(),
|
||||
id<KcContextBase.Info>({
|
||||
...kcContextCommonMock,
|
||||
"pageId": "info.ftl",
|
||||
"messageHeader": "<Message header>",
|
||||
"requiredActions": undefined,
|
||||
"skipLink": false,
|
||||
"actionUri": "#",
|
||||
"client": {
|
||||
"clientId": "myApp",
|
||||
"baseUrl": "#"
|
||||
}
|
||||
}),
|
||||
id<KcContextBase.Error>({
|
||||
...kcContextCommonMock,
|
||||
"pageId": "error.ftl",
|
||||
"client": {
|
||||
"clientId": "myApp",
|
||||
"baseUrl": "#"
|
||||
},
|
||||
"message": {
|
||||
"type": "error",
|
||||
"summary": "This is the error message"
|
||||
}
|
||||
}),
|
||||
id<KcContextBase.LoginResetPassword>({
|
||||
...kcContextCommonMock,
|
||||
"pageId": "login-reset-password.ftl",
|
||||
"realm": {
|
||||
...kcContextCommonMock.realm,
|
||||
"loginWithEmailAllowed": false
|
||||
}
|
||||
}),
|
||||
id<KcContextBase.LoginVerifyEmail>({
|
||||
...kcContextCommonMock,
|
||||
"pageId": "login-verify-email.ftl",
|
||||
"user": {
|
||||
"email": "john.doe@gmail.com"
|
||||
}
|
||||
}),
|
||||
id<KcContextBase.Terms>({
|
||||
...kcContextCommonMock,
|
||||
"pageId": "terms.ftl"
|
||||
}),
|
||||
id<KcContextBase.LoginOtp>({
|
||||
...kcContextCommonMock,
|
||||
"pageId": "login-otp.ftl",
|
||||
"otpLogin": {
|
||||
"userOtpCredentials": [
|
||||
{
|
||||
"id": "id1",
|
||||
"userLabel": "label1"
|
||||
},
|
||||
{
|
||||
"id": "id2",
|
||||
"userLabel": "label2"
|
||||
}
|
||||
]
|
||||
}
|
||||
}),
|
||||
id<KcContextBase.LoginUpdatePassword>({
|
||||
...kcContextCommonMock,
|
||||
"pageId": "login-update-password.ftl",
|
||||
"username": "anUsername"
|
||||
}),
|
||||
id<KcContextBase.LoginUpdateProfile>({
|
||||
...kcContextCommonMock,
|
||||
"pageId": "login-update-profile.ftl",
|
||||
"user": {
|
||||
"editUsernameAllowed": true,
|
||||
"username": "anUsername",
|
||||
"email": "foo@example.com",
|
||||
"firstName": "aFirstName",
|
||||
"lastName": "aLastName"
|
||||
}
|
||||
}),
|
||||
id<KcContextBase.LoginIdpLinkConfirm>({
|
||||
...kcContextCommonMock,
|
||||
"pageId": "login-idp-link-confirm.ftl",
|
||||
"idpAlias": "FranceConnect"
|
||||
}),
|
||||
id<KcContextBase.LoginIdpLinkEmail>({
|
||||
...kcContextCommonMock,
|
||||
"pageId": "login-idp-link-email.ftl",
|
||||
"idpAlias": "FranceConnect",
|
||||
"brokerContext": {
|
||||
"username": "anUsername"
|
||||
}
|
||||
}),
|
||||
id<KcContextBase.LoginConfigTotp>({
|
||||
...kcContextCommonMock,
|
||||
"pageId": "login-config-totp.ftl",
|
||||
totp: {
|
||||
totpSecretEncoded: "KVVF G2BY N4YX S6LB IUYT K2LH IFYE 4SBV",
|
||||
qrUrl: "#",
|
||||
totpSecretQrCode:
|
||||
"iVBORw0KGgoAAAANSUhEUgAAAPYAAAD2AQAAAADNaUdlAAACM0lEQVR4Xu3OIZJgOQwDUDFd2UxiurLAVnnbHw4YGDKtSiWOn4Gxf81//7r/+q8b4HfLGBZDK9d85NmNR+sB42sXvOYrN5P1DcgYYFTGfOlbzE8gzwy3euweGizw7cfdl34/GRhlkxjKNV+5AebPXPORX1JuB9x8ZfbyyD2y1krWAKsbMq1HnqQDaLfa77p4+MqvzEGSqvSAD/2IHW2yHaigR9tX3m8dDIYGcNf3f+gDpVBZbZU77zyJ6Rlcy+qoTMG887KAPD9hsh6a1Sv3gJUHGHUAxSMzj7zqDDe7Phmt2eG+8UsMxjRGm816MAO+8VMl1R1jGHOrZB/5Zo/WXAPgxixm9Mo96vDGrM1eOto8c4Ax4wF437mifOXlpiPzCnN7Y9l95NnEMxgMY9AAGA8fucH14Y1aVb6N/cqrmyh0BVht7k1e+bU8LK0Cg5vmVq9c5vHIjOfqxDIfeTraNVTwewa4wVe+SW5N+uP1qACeudUZbqGOfA6VZV750Noq2Xx3kpveV44ZelSV1V7KFHzkWyVrrlUwG0Pl9pWnoy3vsQoME6vKI69i5osVgwWzHT7zjmJtMcNUSVn1oYMd7ZodbgowZl45VG0uVuLPUr1yc79uaQBag/mqR34xhlWyHm1prplHboCWdZ4TeZjsK8+dI+jbz1C5hl65mcpgB5dhcj8+dGO+0Ko68+lD37JDD83dpDLzzK+TrQyaVwGj6pUboGV+7+AyN8An/pf84/7rv/4/1l4OCc/1BYMAAAAASUVORK5CYII=",
|
||||
manualUrl: "#",
|
||||
totpSecret: "G4nsI8lQagRMUchH8jEG",
|
||||
otpCredentials: [],
|
||||
policy: {
|
||||
supportedApplications: ["FreeOTP", "Google Authenticator"],
|
||||
algorithm: "HmacSHA1",
|
||||
digits: 6,
|
||||
lookAheadWindow: 1,
|
||||
type: "totp",
|
||||
period: 30
|
||||
}
|
||||
}
|
||||
}),
|
||||
id<KcContextBase.LogoutConfirm>({
|
||||
...kcContextCommonMock,
|
||||
"pageId": "logout-confirm.ftl",
|
||||
"url": {
|
||||
...kcContextCommonMock.url,
|
||||
"logoutConfirmAction": "Continuer?"
|
||||
},
|
||||
"client": {
|
||||
"clientId": "myApp",
|
||||
"baseUrl": "#"
|
||||
},
|
||||
"logoutConfirm": { "code": "123", skipLink: false }
|
||||
})
|
||||
];
|
137
src/lib/i18n/generated_messages/11.0.3/account/ca.ts
Normal file
137
src/lib/i18n/generated_messages/11.0.3/account/ca.ts
Normal file
@ -0,0 +1,137 @@
|
||||
//This code was automatically generated by running dist/bin/generate-i18n-messages.js
|
||||
//PLEASE DO NOT EDIT MANUALLY
|
||||
|
||||
/* spell-checker: disable */
|
||||
const messages = {
|
||||
"doSave": "Desa",
|
||||
"doCancel": "Cancel·la",
|
||||
"doLogOutAllSessions": "Desconnecta de totes les sessions",
|
||||
"doRemove": "Elimina",
|
||||
"doAdd": "Afegeix",
|
||||
"doSignOut": "Desconnectar",
|
||||
"editAccountHtmlTitle": "Edita compte",
|
||||
"federatedIdentitiesHtmlTitle": "Identitats federades",
|
||||
"accountLogHtmlTitle": "Registre del compte",
|
||||
"changePasswordHtmlTitle": "Canvia contrasenya",
|
||||
"sessionsHtmlTitle": "Sessions",
|
||||
"accountManagementTitle": "Gestió de Compte Keycloak",
|
||||
"authenticatorTitle": "Autenticador",
|
||||
"applicationsHtmlTitle": "Aplicacions",
|
||||
"authenticatorCode": "Codi d'un sol ús",
|
||||
"email": "Email",
|
||||
"firstName": "Nom",
|
||||
"givenName": "Nom de pila",
|
||||
"fullName": "Nom complet",
|
||||
"lastName": "Cognoms",
|
||||
"familyName": "Cognom",
|
||||
"password": "Contrasenya",
|
||||
"passwordConfirm": "Confirma la contrasenya",
|
||||
"passwordNew": "Nova contrasenya",
|
||||
"username": "Usuari",
|
||||
"address": "Adreça",
|
||||
"street": "Carrer",
|
||||
"locality": "Ciutat o Municipi",
|
||||
"region": "Estat, Província, o Regió",
|
||||
"postal_code": "Postal code",
|
||||
"country": "País",
|
||||
"emailVerified": "Email verificat",
|
||||
"gssDelegationCredential": "GSS Delegation Credential",
|
||||
"role_admin": "Administrador",
|
||||
"role_realm-admin": "Administrador del domini",
|
||||
"role_create-realm": "Crear domini",
|
||||
"role_view-realm": "Veure domini",
|
||||
"role_view-users": "Veure usuaris",
|
||||
"role_view-applications": "Veure aplicacions",
|
||||
"role_view-clients": "Veure clients",
|
||||
"role_view-events": "Veure events",
|
||||
"role_view-identity-providers": "Veure proveïdors d'identitat",
|
||||
"role_manage-realm": "Gestionar domini",
|
||||
"role_manage-users": "Gestinar usuaris",
|
||||
"role_manage-applications": "Gestionar aplicacions",
|
||||
"role_manage-identity-providers": "Gestionar proveïdors d'identitat",
|
||||
"role_manage-clients": "Gestionar clients",
|
||||
"role_manage-events": "Gestionar events",
|
||||
"role_view-profile": "Veure perfil",
|
||||
"role_manage-account": "Gestionar compte",
|
||||
"role_read-token": "Llegir token",
|
||||
"role_offline-access": "Accés sense connexió",
|
||||
"client_account": "Compte",
|
||||
"client_security-admin-console": "Consola d'Administració de Seguretat",
|
||||
"client_realm-management": "Gestió de domini",
|
||||
"client_broker": "Broker",
|
||||
"requiredFields": "Camps obligatoris",
|
||||
"allFieldsRequired": "Tots els camps obligatoris",
|
||||
"backToApplication": "« Torna a l'aplicació",
|
||||
"backTo": "Torna a {0}",
|
||||
"date": "Data",
|
||||
"event": "Event",
|
||||
"ip": "IP",
|
||||
"client": "Client",
|
||||
"clients": "Clients",
|
||||
"details": "Detalls",
|
||||
"started": "Iniciat",
|
||||
"lastAccess": "Últim accés",
|
||||
"expires": "Expira",
|
||||
"applications": "Aplicacions",
|
||||
"account": "Compte",
|
||||
"federatedIdentity": "Identitat federada",
|
||||
"authenticator": "Autenticador",
|
||||
"sessions": "Sessions",
|
||||
"log": "Registre",
|
||||
"application": "Aplicació",
|
||||
"availablePermissions": "Permisos disponibles",
|
||||
"grantedPermissions": "Permisos concedits",
|
||||
"grantedPersonalInfo": "Informació personal concedida",
|
||||
"additionalGrants": "Permisos addicionals",
|
||||
"action": "Acció",
|
||||
"inResource": "a",
|
||||
"fullAccess": "Accés total",
|
||||
"offlineToken": "Codi d'autorització offline",
|
||||
"revoke": "Revocar permís",
|
||||
"configureAuthenticators": "Autenticadors configurats",
|
||||
"mobile": "Mòbil",
|
||||
"totpStep1":
|
||||
'Instal·la <a href="https://freeotp.github.io/" target="_blank">FreeOTP</a> o Google Authenticator al teu telèfon mòbil. Les dues aplicacions estan disponibles a <a href="https://play.google.com">Google Play</a> i en l\'App Store d\'Apple.',
|
||||
"totpStep2": "Obre l'aplicació i escaneja el codi o introdueix la clau.",
|
||||
"totpStep3": "Introdueix el codi únic que et mostra l'aplicació d'autenticació i fes clic a Envia per finalitzar la configuració",
|
||||
"missingUsernameMessage": "Si us plau indica el teu usuari.",
|
||||
"missingFirstNameMessage": "Si us plau indica el nom.",
|
||||
"invalidEmailMessage": "Email no vàlid",
|
||||
"missingLastNameMessage": "Si us plau indica els teus cognoms.",
|
||||
"missingEmailMessage": "Si us plau indica l'email.",
|
||||
"missingPasswordMessage": "Si us plau indica la contrasenya.",
|
||||
"notMatchPasswordMessage": "Les contrasenyes no coincideixen.",
|
||||
"missingTotpMessage": "Si us plau indica el teu codi d'autenticació",
|
||||
"invalidPasswordExistingMessage": "La contrasenya actual no és correcta.",
|
||||
"invalidPasswordConfirmMessage": "La confirmació de contrasenya no coincideix.",
|
||||
"invalidTotpMessage": "El código de autenticación no es válido.",
|
||||
"usernameExistsMessage": "L'usuari ja existeix",
|
||||
"emailExistsMessage": "L'email ja existeix",
|
||||
"readOnlyUserMessage": "No pots actualitzar el teu usuari perquè el teu compte és de només lectura.",
|
||||
"readOnlyPasswordMessage": "No pots actualitzar la contrasenya perquè el teu compte és de només lectura.",
|
||||
"successTotpMessage": "Aplicació d'autenticació mòbil configurada.",
|
||||
"successTotpRemovedMessage": "Aplicació d'autenticació mòbil eliminada.",
|
||||
"successGrantRevokedMessage": "Permís revocat correctament",
|
||||
"accountUpdatedMessage": "El teu compte s'ha actualitzat.",
|
||||
"accountPasswordUpdatedMessage": "La contrasenya s'ha actualitzat.",
|
||||
"missingIdentityProviderMessage": "Proveïdor d'identitat no indicat.",
|
||||
"invalidFederatedIdentityActionMessage": "Acció no vàlida o no indicada.",
|
||||
"identityProviderNotFoundMessage": "No s'ha trobat un proveïdor d'identitat.",
|
||||
"federatedIdentityLinkNotActiveMessage": "Aquesta identitat ja no està activa",
|
||||
"federatedIdentityRemovingLastProviderMessage": "No pots eliminar l'última identitat federada perquè no tens fixada una contrasenya.",
|
||||
"identityProviderRedirectErrorMessage": "Error en la redirecció al proveïdor d'identitat",
|
||||
"identityProviderRemovedMessage": "Proveïdor d'identitat esborrat correctament.",
|
||||
"accountDisabledMessage": "El compte està desactivada, contacteu amb l'administrador.",
|
||||
"accountTemporarilyDisabledMessage": "El compte està temporalment desactivat, contacta amb l'administrador o intenta-ho de nou més tard.",
|
||||
"invalidPasswordMinLengthMessage": "Contrasenya incorrecta: longitud mínima {0}.",
|
||||
"invalidPasswordMinLowerCaseCharsMessage": "Contrasenya incorrecta: ha de contenir almenys {0} lletres minúscules.",
|
||||
"invalidPasswordMinDigitsMessage": "Contraseña incorrecta: debe contener al menos {0} caracteres numéricos.",
|
||||
"invalidPasswordMinUpperCaseCharsMessage": "Contrasenya incorrecta: ha de contenir almenys {0} lletres majúscules.",
|
||||
"invalidPasswordMinSpecialCharsMessage": "Contrasenya incorrecta: ha de contenir almenys {0} caràcters especials.",
|
||||
"invalidPasswordNotUsernameMessage": "Contrasenya incorrecta: no pot ser igual al nom d'usuari.",
|
||||
"invalidPasswordRegexPatternMessage": "Contrasenya incorrecta: no compleix l'expressió regular.",
|
||||
"invalidPasswordHistoryMessage": "Contrasenya incorrecta: no pot ser igual a cap de les últimes {0} contrasenyes."
|
||||
};
|
||||
|
||||
export default messages;
|
||||
/* spell-checker: enable */
|
156
src/lib/i18n/generated_messages/11.0.3/account/cs.ts
Normal file
156
src/lib/i18n/generated_messages/11.0.3/account/cs.ts
Normal file
@ -0,0 +1,156 @@
|
||||
//This code was automatically generated by running dist/bin/generate-i18n-messages.js
|
||||
//PLEASE DO NOT EDIT MANUALLY
|
||||
|
||||
/* spell-checker: disable */
|
||||
const messages = {
|
||||
"doSave": "Uložit",
|
||||
"doCancel": "Zrušit",
|
||||
"doLogOutAllSessions": "Odhlásit všechny relace",
|
||||
"doRemove": "Odstranit",
|
||||
"doAdd": "Přidat",
|
||||
"doSignOut": "Odhlásit se",
|
||||
"editAccountHtmlTitle": "Upravit účet",
|
||||
"federatedIdentitiesHtmlTitle": "Propojené identity",
|
||||
"accountLogHtmlTitle": "Log účtu",
|
||||
"changePasswordHtmlTitle": "Změnit heslo",
|
||||
"sessionsHtmlTitle": "Relace",
|
||||
"accountManagementTitle": "Správa účtů Keycloak",
|
||||
"authenticatorTitle": "Autentizátor",
|
||||
"applicationsHtmlTitle": "Aplikace",
|
||||
"authenticatorCode": "Jednorázový kód",
|
||||
"email": "E-mail",
|
||||
"firstName": "První křestní jméno",
|
||||
"givenName": "Křestní jména",
|
||||
"fullName": "Celé jméno",
|
||||
"lastName": "Příjmení",
|
||||
"familyName": "Rodinné jméno",
|
||||
"password": "Heslo",
|
||||
"passwordConfirm": "Nové heslo (znovu)",
|
||||
"passwordNew": "Nové heslo",
|
||||
"username": "Uživatelské jméno",
|
||||
"address": "Adresa",
|
||||
"street": "Ulice",
|
||||
"locality": "Město nebo lokalita",
|
||||
"region": "Kraj",
|
||||
"postal_code": "PSČ",
|
||||
"country": "Stát",
|
||||
"emailVerified": "E-mail ověřen",
|
||||
"gssDelegationCredential": "GSS delegované oprávnění",
|
||||
"role_admin": "Správce",
|
||||
"role_realm-admin": "Správce realmu",
|
||||
"role_create-realm": "Vytvořit realm",
|
||||
"role_view-realm": "Zobrazit realm",
|
||||
"role_view-users": "Zobrazit uživatele",
|
||||
"role_view-applications": "Zobrazit aplikace",
|
||||
"role_view-clients": "Zobrazit klienty",
|
||||
"role_view-events": "Zobrazit události",
|
||||
"role_view-identity-providers": "Zobrazit poskytovatele identity",
|
||||
"role_manage-realm": "Spravovat realm",
|
||||
"role_manage-users": "Spravovat uživatele",
|
||||
"role_manage-applications": "Spravovat aplikace",
|
||||
"role_manage-identity-providers": "Spravovat poskytovatele identity",
|
||||
"role_manage-clients": "Spravovat klienty",
|
||||
"role_manage-events": "Spravovat události",
|
||||
"role_view-profile": "Zobrazit profil",
|
||||
"role_manage-account": "Spravovat účet",
|
||||
"role_manage-account-links": "Spravovat odkazy na účet",
|
||||
"role_read-token": "Číst token",
|
||||
"role_offline-access": "Přístup offline",
|
||||
"role_uma_authorization": "Získání oprávnění",
|
||||
"client_account": "Účet",
|
||||
"client_security-admin-console": "Administrátorská bezpečnostní konzole",
|
||||
"client_admin-cli": "Administrátorské CLI",
|
||||
"client_realm-management": "Správa realmů",
|
||||
"client_broker": "Broker",
|
||||
"requiredFields": "Požadovaná pole",
|
||||
"allFieldsRequired": "Všechna pole vyžadovaná",
|
||||
"backToApplication": "« Zpět na aplikaci",
|
||||
"backTo": "Zpět na {0}",
|
||||
"date": "Datum",
|
||||
"event": "Událost",
|
||||
"ip": "IP",
|
||||
"client": "Klient",
|
||||
"clients": "Klienti",
|
||||
"details": "Podrobnosti",
|
||||
"started": "Zahájeno",
|
||||
"lastAccess": "Poslední přístup",
|
||||
"expires": "Vyprší",
|
||||
"applications": "Aplikace",
|
||||
"account": "Účet",
|
||||
"federatedIdentity": "Propojená identita",
|
||||
"authenticator": "Autentizátor",
|
||||
"sessions": "Relace",
|
||||
"log": "Log",
|
||||
"application": "Aplikace",
|
||||
"availablePermissions": "Dostupná oprávnění",
|
||||
"grantedPermissions": "Udělené oprávnění",
|
||||
"grantedPersonalInfo": "Poskytnuté osobní informace",
|
||||
"additionalGrants": "Dodatečné oprávnění",
|
||||
"action": "Akce",
|
||||
"inResource": "v",
|
||||
"fullAccess": "Úplný přístup",
|
||||
"offlineToken": "Offline Token",
|
||||
"revoke": "Zrušit oprávnění",
|
||||
"configureAuthenticators": "Konfigurované autentizátory",
|
||||
"mobile": "Mobilní",
|
||||
"totpStep1": "Nainstalujte jednu z následujících aplikací",
|
||||
"totpStep2": "Otevřete aplikaci a naskenujte čárový kód",
|
||||
"totpStep3": "Zadejte jednorázový kód poskytnutý aplikací a klepnutím na tlačítko Uložit dokončete nastavení.",
|
||||
"totpManualStep2": "Otevřete aplikaci a zadejte klíč",
|
||||
"totpManualStep3": "Použijte následující hodnoty konfigurace, pokud aplikace umožňuje jejich nastavení",
|
||||
"totpUnableToScan": "Nelze skenovat?",
|
||||
"totpScanBarcode": "Skenovat čárový kód?",
|
||||
"totp.totp": "Založeno na čase",
|
||||
"totp.hotp": "Založeno na čítači",
|
||||
"totpType": "Typ",
|
||||
"totpAlgorithm": "Algoritmus",
|
||||
"totpDigits": "Číslice",
|
||||
"totpInterval": "Interval",
|
||||
"totpCounter": "Čítač",
|
||||
"missingUsernameMessage": "Zadejte uživatelské jméno.",
|
||||
"missingFirstNameMessage": "Zadejte prosím křestní jméno.",
|
||||
"invalidEmailMessage": "Neplatná e-mailová adresa.",
|
||||
"missingLastNameMessage": "Zadejte prosím příjmení.",
|
||||
"missingEmailMessage": "Zadejte prosím e-mail.",
|
||||
"missingPasswordMessage": "Zadejte prosím heslo.",
|
||||
"notMatchPasswordMessage": "Hesla se neshodují.",
|
||||
"missingTotpMessage": "Zadejte prosím kód autentizátoru.",
|
||||
"invalidPasswordExistingMessage": "Neplatné stávající heslo.",
|
||||
"invalidPasswordConfirmMessage": "Nová hesla se neshodují.",
|
||||
"invalidTotpMessage": "Neplatný kód autentizátoru.",
|
||||
"usernameExistsMessage": "Uživatelské jméno již existuje.",
|
||||
"emailExistsMessage": "E-mail již existuje.",
|
||||
"readOnlyUserMessage": "Nemůžete svůj účet aktualizovat, protože je pouze pro čtení.",
|
||||
"readOnlyUsernameMessage": "Nemůžete aktualizovat své uživatelské jméno, protože je pouze pro čtení.",
|
||||
"readOnlyPasswordMessage": "Nemůžete aktualizovat své heslo, protože váš účet je jen pro čtení.",
|
||||
"successTotpMessage": "Ověření pomocí OTP úspěšně konfigurováno.",
|
||||
"successTotpRemovedMessage": "Ověření pomocí OTP úspěšně odstraněno.",
|
||||
"successGrantRevokedMessage": "Oprávnění bylo úspěšně zrušeno.",
|
||||
"accountUpdatedMessage": "Váš účet byl aktualizován.",
|
||||
"accountPasswordUpdatedMessage": "Vaše heslo bylo aktualizováno.",
|
||||
"missingIdentityProviderMessage": "Chybějící poskytovatel identity.",
|
||||
"invalidFederatedIdentityActionMessage": "Neplatná nebo chybějící akce.",
|
||||
"identityProviderNotFoundMessage": "Poskytovatel identity nenalezen.",
|
||||
"federatedIdentityLinkNotActiveMessage": "Tato identita již není aktivní.",
|
||||
"federatedIdentityRemovingLastProviderMessage": "Nemůžete odstranit poslední propojenou identitu, protože nemáte heslo.",
|
||||
"identityProviderRedirectErrorMessage": "Nepodařilo se přesměrovat na poskytovatele identity.",
|
||||
"identityProviderRemovedMessage": "Poskytovatel identity byl úspěšně odstraněn.",
|
||||
"identityProviderAlreadyLinkedMessage": "Propojená identita vrácená uživatelem {0} je již propojena s jiným uživatelem.",
|
||||
"staleCodeAccountMessage": "Platnost vypršela. Zkuste to ještě jednou.",
|
||||
"consentDenied": "Souhlas byl zamítnut.",
|
||||
"accountDisabledMessage": "Účet je zakázán, kontaktujte správce.",
|
||||
"accountTemporarilyDisabledMessage": "Účet je dočasně zakázán, kontaktujte správce nebo zkuste to později.",
|
||||
"invalidPasswordMinLengthMessage": "Neplatné heslo: musí obsahovat minimálně {0} malých znaků.",
|
||||
"invalidPasswordMinLowerCaseCharsMessage": "Neplatné heslo: musí obsahovat minimálně {0} malé znaky.",
|
||||
"invalidPasswordMinDigitsMessage": "Neplatné heslo: musí obsahovat nejméně {0} číslic.",
|
||||
"invalidPasswordMinUpperCaseCharsMessage": "Neplatné heslo: musí obsahovat nejméně {0} velkých písmenen.",
|
||||
"invalidPasswordMinSpecialCharsMessage": "Neplatné heslo: musí obsahovat nejméně {0} speciálních znaků.",
|
||||
"invalidPasswordNotUsernameMessage": "Neplatné heslo: nesmí být totožné s uživatelským jménem.",
|
||||
"invalidPasswordRegexPatternMessage": "Neplatné heslo: neshoduje se zadaným regulárním výrazem.",
|
||||
"invalidPasswordHistoryMessage": "Neplatné heslo: Nesmí se opakovat žádné z posledních {0} hesel.",
|
||||
"invalidPasswordBlacklistedMessage": "Neplatné heslo: heslo je na černé listině.",
|
||||
"invalidPasswordGenericMessage": "Neplatné heslo: nové heslo neodpovídá pravidlům hesla."
|
||||
};
|
||||
|
||||
export default messages;
|
||||
/* spell-checker: enable */
|
156
src/lib/i18n/generated_messages/11.0.3/account/de.ts
Normal file
156
src/lib/i18n/generated_messages/11.0.3/account/de.ts
Normal file
@ -0,0 +1,156 @@
|
||||
//This code was automatically generated by running dist/bin/generate-i18n-messages.js
|
||||
//PLEASE DO NOT EDIT MANUALLY
|
||||
|
||||
/* spell-checker: disable */
|
||||
const messages = {
|
||||
"doSave": "Speichern",
|
||||
"doCancel": "Abbrechen",
|
||||
"doLogOutAllSessions": "Alle Sitzungen abmelden",
|
||||
"doRemove": "Entfernen",
|
||||
"doAdd": "Hinzufügen",
|
||||
"doSignOut": "Abmelden",
|
||||
"editAccountHtmlTitle": "Benutzerkonto bearbeiten",
|
||||
"federatedIdentitiesHtmlTitle": "Föderierte Identitäten",
|
||||
"accountLogHtmlTitle": "Benutzerkonto Log",
|
||||
"changePasswordHtmlTitle": "Passwort Ändern",
|
||||
"sessionsHtmlTitle": "Sitzungen",
|
||||
"accountManagementTitle": "Keycloak Benutzerkontoverwaltung",
|
||||
"authenticatorTitle": "Mehrfachauthentifizierung",
|
||||
"applicationsHtmlTitle": "Applikationen",
|
||||
"authenticatorCode": "One-time Code",
|
||||
"email": "E-Mail",
|
||||
"firstName": "Vorname",
|
||||
"givenName": "Vorname",
|
||||
"fullName": "Voller Name",
|
||||
"lastName": "Nachname",
|
||||
"familyName": "Nachname",
|
||||
"password": "Passwort",
|
||||
"passwordConfirm": "Passwort bestätigen",
|
||||
"passwordNew": "Neues Passwort",
|
||||
"username": "Benutzername",
|
||||
"address": "Adresse",
|
||||
"street": "Straße",
|
||||
"region": "Staat, Provinz, Region",
|
||||
"postal_code": "PLZ",
|
||||
"locality": "Stadt oder Ortschaft",
|
||||
"country": "Land",
|
||||
"emailVerified": "E-Mail verifiziert",
|
||||
"gssDelegationCredential": "GSS delegierte Berechtigung",
|
||||
"role_admin": "Admin",
|
||||
"role_realm-admin": "Realm Admin",
|
||||
"role_create-realm": "Realm erstellen",
|
||||
"role_view-realm": "Realm ansehen",
|
||||
"role_view-users": "Benutzer ansehen",
|
||||
"role_view-applications": "Applikationen ansehen",
|
||||
"role_view-clients": "Clients ansehen",
|
||||
"role_view-events": "Events ansehen",
|
||||
"role_view-identity-providers": "Identity Provider ansehen",
|
||||
"role_manage-realm": "Realm verwalten",
|
||||
"role_manage-users": "Benutzer verwalten",
|
||||
"role_manage-applications": "Applikationen verwalten",
|
||||
"role_manage-identity-providers": "Identity Provider verwalten",
|
||||
"role_manage-clients": "Clients verwalten",
|
||||
"role_manage-events": "Events verwalten",
|
||||
"role_view-profile": "Profile ansehen",
|
||||
"role_manage-account": "Profile verwalten",
|
||||
"role_manage-account-links": "Profil-Links verwalten",
|
||||
"role_read-token": "Token lesen",
|
||||
"role_offline-access": "Offline-Zugriff",
|
||||
"role_uma_authorization": "Berechtigungen einholen",
|
||||
"client_account": "Clientkonto",
|
||||
"client_security-admin-console": "Security Adminkonsole",
|
||||
"client_realm-management": "Realm-Management",
|
||||
"client_broker": "Broker",
|
||||
"requiredFields": "Erforderliche Felder",
|
||||
"allFieldsRequired": "Alle Felder sind erforderlich",
|
||||
"backToApplication": "« Zurück zur Applikation",
|
||||
"backTo": "Zurück zu {0}",
|
||||
"date": "Datum",
|
||||
"event": "Ereignis",
|
||||
"ip": "IP",
|
||||
"client": "Client",
|
||||
"clients": "Clients",
|
||||
"details": "Details",
|
||||
"started": "Startdatum",
|
||||
"lastAccess": "Letzter Zugriff",
|
||||
"expires": "Ablaufdatum",
|
||||
"applications": "Applikationen",
|
||||
"account": "Benutzerkonto",
|
||||
"federatedIdentity": "Föderierte Identität",
|
||||
"authenticator": "Mehrfachauthentifizierung",
|
||||
"sessions": "Sitzungen",
|
||||
"log": "Log",
|
||||
"application": "Applikation",
|
||||
"availablePermissions": "verfügbare Berechtigungen",
|
||||
"grantedPermissions": "gewährte Berechtigungen",
|
||||
"grantedPersonalInfo": "gewährte persönliche Informationen",
|
||||
"additionalGrants": "zusätzliche Berechtigungen",
|
||||
"action": "Aktion",
|
||||
"inResource": "in",
|
||||
"fullAccess": "Vollzugriff",
|
||||
"offlineToken": "Offline-Token",
|
||||
"revoke": "Berechtigung widerrufen",
|
||||
"configureAuthenticators": "Mehrfachauthentifizierung konfigurieren",
|
||||
"mobile": "Mobil",
|
||||
"totpStep1": "Installieren Sie eine der folgenden Applikationen auf Ihrem Smartphone:",
|
||||
"totpStep2": "Öffnen Sie die Applikation und scannen Sie den Barcode.",
|
||||
"totpStep3": "Geben Sie den von der Applikation generierten One-time Code ein und klicken Sie auf Speichern.",
|
||||
"totpManualStep2": "Öffnen Sie die Applikation und geben Sie den folgenden Schlüssel ein.",
|
||||
"totpManualStep3": "Verwenden Sie die folgenden Konfigurationswerte, falls Sie diese für die Applikation anpassen können:",
|
||||
"totpUnableToScan": "Sie können den Barcode nicht scannen?",
|
||||
"totpScanBarcode": "Barcode scannen?",
|
||||
"totp.totp": "zeitbasiert (time-based)",
|
||||
"totp.hotp": "zählerbasiert (counter-based)",
|
||||
"totpType": "Typ",
|
||||
"totpAlgorithm": "Algorithmus",
|
||||
"totpDigits": "Ziffern",
|
||||
"totpInterval": "Intervall",
|
||||
"totpCounter": "Zähler",
|
||||
"missingUsernameMessage": "Bitte geben Sie einen Benutzernamen ein.",
|
||||
"missingFirstNameMessage": "Bitte geben Sie einen Vornamen ein.",
|
||||
"invalidEmailMessage": "Ungültige E-Mail Adresse.",
|
||||
"missingLastNameMessage": "Bitte geben Sie einen Nachnamen ein.",
|
||||
"missingEmailMessage": "Bitte geben Sie eine E-Mail Adresse ein.",
|
||||
"missingPasswordMessage": "Bitte geben Sie ein Passwort ein.",
|
||||
"notMatchPasswordMessage": "Die Passwörter sind nicht identisch.",
|
||||
"missingTotpMessage": "Bitte geben Sie den One-time Code ein.",
|
||||
"invalidPasswordExistingMessage": "Das aktuelle Passwort ist ungültig.",
|
||||
"invalidPasswordConfirmMessage": "Die Passwortbestätigung ist nicht identisch.",
|
||||
"invalidTotpMessage": "Ungültiger One-time Code.",
|
||||
"usernameExistsMessage": "Der Benutzername existiert bereits.",
|
||||
"emailExistsMessage": "Die E-Mail-Adresse existiert bereits.",
|
||||
"readOnlyUserMessage": "Sie können Ihr Benutzerkonto nicht ändern, da es schreibgeschützt ist.",
|
||||
"readOnlyUsernameMessage": "Sie können Ihren Benutzernamen nicht ändern, da er schreibgeschützt ist.",
|
||||
"readOnlyPasswordMessage": "Sie können Ihr Passwort nicht ändern, da es schreibgeschützt ist.",
|
||||
"successTotpMessage": "Mehrfachauthentifizierung erfolgreich konfiguriert.",
|
||||
"successTotpRemovedMessage": "Mehrfachauthentifizierung erfolgreich entfernt.",
|
||||
"successGrantRevokedMessage": "Berechtigung erfolgreich widerrufen.",
|
||||
"accountUpdatedMessage": "Ihr Benutzerkonto wurde aktualisiert.",
|
||||
"accountPasswordUpdatedMessage": "Ihr Passwort wurde aktualisiert.",
|
||||
"missingIdentityProviderMessage": "Identity Provider nicht angegeben.",
|
||||
"invalidFederatedIdentityActionMessage": "Ungültige oder fehlende Aktion.",
|
||||
"identityProviderNotFoundMessage": "Angegebener Identity Provider nicht gefunden.",
|
||||
"federatedIdentityLinkNotActiveMessage": "Diese Identität ist nicht mehr aktiv.",
|
||||
"federatedIdentityRemovingLastProviderMessage": "Sie können den letzten Eintrag nicht entfernen, da Sie kein Passwort haben.",
|
||||
"identityProviderRedirectErrorMessage": "Fehler bei der Weiterleitung zum Identity Provider.",
|
||||
"identityProviderRemovedMessage": "Identity Provider erfolgreich entfernt.",
|
||||
"identityProviderAlreadyLinkedMessage": "Die föderierte Identität von {0} ist bereits einem anderen Benutzer zugewiesen.",
|
||||
"staleCodeAccountMessage": "Diese Seite ist nicht mehr gültig, bitte versuchen Sie es noch einmal.",
|
||||
"consentDenied": "Einverständnis verweigert.",
|
||||
"accountDisabledMessage": "Ihr Benutzerkonto ist gesperrt, bitte kontaktieren Sie den Admin.",
|
||||
"accountTemporarilyDisabledMessage":
|
||||
"Ihr Benutzerkonto ist temporär gesperrt, bitte kontaktieren Sie den Admin oder versuchen Sie es später noch einmal.",
|
||||
"invalidPasswordMinLengthMessage": "Ungültiges Passwort: Es muss mindestens {0} Zeichen lang sein.",
|
||||
"invalidPasswordMinLowerCaseCharsMessage": "Ungültiges Passwort: Es muss mindestens {0} Kleinbuchstaben beinhalten.",
|
||||
"invalidPasswordMinDigitsMessage": "Ungültiges Passwort: Es muss mindestens {0} Zahl(en) beinhalten.",
|
||||
"invalidPasswordMinUpperCaseCharsMessage": "Ungültiges Passwort: Es muss mindestens {0} Großbuchstaben beinhalten.",
|
||||
"invalidPasswordMinSpecialCharsMessage": "Ungültiges Passwort: Es muss mindestens {0} Sonderzeichen beinhalten.",
|
||||
"invalidPasswordNotUsernameMessage": "Ungültiges Passwort: Es darf nicht gleich sein wie der Benutzername.",
|
||||
"invalidPasswordRegexPatternMessage": "Ungültiges Passwort: Es entspricht nicht dem Regex-Muster.",
|
||||
"invalidPasswordHistoryMessage": "Ungültiges Passwort: Es darf nicht einem der letzten {0} Passwörter entsprechen.",
|
||||
"invalidPasswordBlacklistedMessage": "Ungültiges Passwort: Das Passwort steht auf der Blocklist (schwarzen Liste).",
|
||||
"invalidPasswordGenericMessge": "Ungültiges Passwort: Das neue Passwort verletzt die Passwort-Richtlinien."
|
||||
};
|
||||
|
||||
export default messages;
|
||||
/* spell-checker: enable */
|
325
src/lib/i18n/generated_messages/11.0.3/account/en.ts
Normal file
325
src/lib/i18n/generated_messages/11.0.3/account/en.ts
Normal file
@ -0,0 +1,325 @@
|
||||
//This code was automatically generated by running dist/bin/generate-i18n-messages.js
|
||||
//PLEASE DO NOT EDIT MANUALLY
|
||||
|
||||
/* spell-checker: disable */
|
||||
const messages = {
|
||||
"doSave": "Save",
|
||||
"doCancel": "Cancel",
|
||||
"doLogOutAllSessions": "Log out all sessions",
|
||||
"doRemove": "Remove",
|
||||
"doAdd": "Add",
|
||||
"doSignOut": "Sign Out",
|
||||
"doLogIn": "Log In",
|
||||
"doLink": "Link",
|
||||
"editAccountHtmlTitle": "Edit Account",
|
||||
"personalInfoHtmlTitle": "Personal Info",
|
||||
"federatedIdentitiesHtmlTitle": "Federated Identities",
|
||||
"accountLogHtmlTitle": "Account Log",
|
||||
"changePasswordHtmlTitle": "Change Password",
|
||||
"deviceActivityHtmlTitle": "Device Activity",
|
||||
"sessionsHtmlTitle": "Sessions",
|
||||
"accountManagementTitle": "Keycloak Account Management",
|
||||
"authenticatorTitle": "Authenticator",
|
||||
"applicationsHtmlTitle": "Applications",
|
||||
"linkedAccountsHtmlTitle": "Linked Accounts",
|
||||
"accountManagementWelcomeMessage": "Welcome to Keycloak Account Management",
|
||||
"personalInfoIntroMessage": "Manage your basic information",
|
||||
"accountSecurityTitle": "Account Security",
|
||||
"accountSecurityIntroMessage": "Control your password and account access",
|
||||
"applicationsIntroMessage": "Track and manage your app permission to access your account",
|
||||
"resourceIntroMessage": "Share your resources among team members",
|
||||
"passwordLastUpdateMessage": "Your password was updated at",
|
||||
"updatePasswordTitle": "Update Password",
|
||||
"updatePasswordMessageTitle": "Make sure you choose a strong password",
|
||||
"updatePasswordMessage":
|
||||
"A strong password contains a mix of numbers, letters, and symbols. It is hard to guess, does not resemble a real word, and is only used for this account.",
|
||||
"personalSubTitle": "Your Personal Info",
|
||||
"personalSubMessage": "Manage this basic information: your first name, last name and email",
|
||||
"authenticatorCode": "One-time code",
|
||||
"email": "Email",
|
||||
"firstName": "First name",
|
||||
"givenName": "Given name",
|
||||
"fullName": "Full name",
|
||||
"lastName": "Last name",
|
||||
"familyName": "Family name",
|
||||
"password": "Password",
|
||||
"currentPassword": "Current Password",
|
||||
"passwordConfirm": "Confirmation",
|
||||
"passwordNew": "New Password",
|
||||
"username": "Username",
|
||||
"address": "Address",
|
||||
"street": "Street",
|
||||
"locality": "City or Locality",
|
||||
"region": "State, Province, or Region",
|
||||
"postal_code": "Zip or Postal code",
|
||||
"country": "Country",
|
||||
"emailVerified": "Email verified",
|
||||
"gssDelegationCredential": "GSS Delegation Credential",
|
||||
"profileScopeConsentText": "User profile",
|
||||
"emailScopeConsentText": "Email address",
|
||||
"addressScopeConsentText": "Address",
|
||||
"phoneScopeConsentText": "Phone number",
|
||||
"offlineAccessScopeConsentText": "Offline Access",
|
||||
"samlRoleListScopeConsentText": "My Roles",
|
||||
"rolesScopeConsentText": "User roles",
|
||||
"role_admin": "Admin",
|
||||
"role_realm-admin": "Realm Admin",
|
||||
"role_create-realm": "Create realm",
|
||||
"role_view-realm": "View realm",
|
||||
"role_view-users": "View users",
|
||||
"role_view-applications": "View applications",
|
||||
"role_view-clients": "View clients",
|
||||
"role_view-events": "View events",
|
||||
"role_view-identity-providers": "View identity providers",
|
||||
"role_view-consent": "View consents",
|
||||
"role_manage-realm": "Manage realm",
|
||||
"role_manage-users": "Manage users",
|
||||
"role_manage-applications": "Manage applications",
|
||||
"role_manage-identity-providers": "Manage identity providers",
|
||||
"role_manage-clients": "Manage clients",
|
||||
"role_manage-events": "Manage events",
|
||||
"role_view-profile": "View profile",
|
||||
"role_manage-account": "Manage account",
|
||||
"role_manage-account-links": "Manage account links",
|
||||
"role_manage-consent": "Manage consents",
|
||||
"role_read-token": "Read token",
|
||||
"role_offline-access": "Offline access",
|
||||
"role_uma_authorization": "Obtain permissions",
|
||||
"client_account": "Account",
|
||||
"client_account-console": "Account Console",
|
||||
"client_security-admin-console": "Security Admin Console",
|
||||
"client_admin-cli": "Admin CLI",
|
||||
"client_realm-management": "Realm Management",
|
||||
"client_broker": "Broker",
|
||||
"requiredFields": "Required fields",
|
||||
"allFieldsRequired": "All fields required",
|
||||
"backToApplication": "« Back to application",
|
||||
"backTo": "Back to {0}",
|
||||
"date": "Date",
|
||||
"event": "Event",
|
||||
"ip": "IP",
|
||||
"client": "Client",
|
||||
"clients": "Clients",
|
||||
"details": "Details",
|
||||
"started": "Started",
|
||||
"lastAccess": "Last Access",
|
||||
"expires": "Expires",
|
||||
"applications": "Applications",
|
||||
"account": "Account",
|
||||
"federatedIdentity": "Federated Identity",
|
||||
"authenticator": "Authenticator",
|
||||
"device-activity": "Device Activity",
|
||||
"sessions": "Sessions",
|
||||
"log": "Log",
|
||||
"application": "Application",
|
||||
"availableRoles": "Available Roles",
|
||||
"grantedPermissions": "Granted Permissions",
|
||||
"grantedPersonalInfo": "Granted Personal Info",
|
||||
"additionalGrants": "Additional Grants",
|
||||
"action": "Action",
|
||||
"inResource": "in",
|
||||
"fullAccess": "Full Access",
|
||||
"offlineToken": "Offline Token",
|
||||
"revoke": "Revoke Grant",
|
||||
"configureAuthenticators": "Configured Authenticators",
|
||||
"mobile": "Mobile",
|
||||
"totpStep1": "Install one of the following applications on your mobile:",
|
||||
"totpStep2": "Open the application and scan the barcode:",
|
||||
"totpStep3": "Enter the one-time code provided by the application and click Save to finish the setup.",
|
||||
"totpStep3DeviceName": "Provide a Device Name to help you manage your OTP devices.",
|
||||
"totpManualStep2": "Open the application and enter the key:",
|
||||
"totpManualStep3": "Use the following configuration values if the application allows setting them:",
|
||||
"totpUnableToScan": "Unable to scan?",
|
||||
"totpScanBarcode": "Scan barcode?",
|
||||
"totp.totp": "Time-based",
|
||||
"totp.hotp": "Counter-based",
|
||||
"totpType": "Type",
|
||||
"totpAlgorithm": "Algorithm",
|
||||
"totpDigits": "Digits",
|
||||
"totpInterval": "Interval",
|
||||
"totpCounter": "Counter",
|
||||
"totpDeviceName": "Device Name",
|
||||
"missingUsernameMessage": "Please specify username.",
|
||||
"missingFirstNameMessage": "Please specify first name.",
|
||||
"invalidEmailMessage": "Invalid email address.",
|
||||
"missingLastNameMessage": "Please specify last name.",
|
||||
"missingEmailMessage": "Please specify email.",
|
||||
"missingPasswordMessage": "Please specify password.",
|
||||
"notMatchPasswordMessage": "Passwords don't match.",
|
||||
"invalidUserMessage": "Invalid user",
|
||||
"missingTotpMessage": "Please specify authenticator code.",
|
||||
"missingTotpDeviceNameMessage": "Please specify device name.",
|
||||
"invalidPasswordExistingMessage": "Invalid existing password.",
|
||||
"invalidPasswordConfirmMessage": "Password confirmation doesn't match.",
|
||||
"invalidTotpMessage": "Invalid authenticator code.",
|
||||
"usernameExistsMessage": "Username already exists.",
|
||||
"emailExistsMessage": "Email already exists.",
|
||||
"readOnlyUserMessage": "You can't update your account as it is read-only.",
|
||||
"readOnlyUsernameMessage": "You can't update your username as it is read-only.",
|
||||
"readOnlyPasswordMessage": "You can't update your password as your account is read-only.",
|
||||
"successTotpMessage": "Mobile authenticator configured.",
|
||||
"successTotpRemovedMessage": "Mobile authenticator removed.",
|
||||
"successGrantRevokedMessage": "Grant revoked successfully.",
|
||||
"accountUpdatedMessage": "Your account has been updated.",
|
||||
"accountPasswordUpdatedMessage": "Your password has been updated.",
|
||||
"missingIdentityProviderMessage": "Identity provider not specified.",
|
||||
"invalidFederatedIdentityActionMessage": "Invalid or missing action.",
|
||||
"identityProviderNotFoundMessage": "Specified identity provider not found.",
|
||||
"federatedIdentityLinkNotActiveMessage": "This identity is not active anymore.",
|
||||
"federatedIdentityRemovingLastProviderMessage": "You can't remove last federated identity as you don't have a password.",
|
||||
"identityProviderRedirectErrorMessage": "Failed to redirect to identity provider.",
|
||||
"identityProviderRemovedMessage": "Identity provider removed successfully.",
|
||||
"identityProviderAlreadyLinkedMessage": "Federated identity returned by {0} is already linked to another user.",
|
||||
"staleCodeAccountMessage": "The page expired. Please try one more time.",
|
||||
"consentDenied": "Consent denied.",
|
||||
"accountDisabledMessage": "Account is disabled, contact your administrator.",
|
||||
"accountTemporarilyDisabledMessage": "Account is temporarily disabled, contact your administrator or try again later.",
|
||||
"invalidPasswordMinLengthMessage": "Invalid password: minimum length {0}.",
|
||||
"invalidPasswordMinLowerCaseCharsMessage": "Invalid password: must contain at least {0} lower case characters.",
|
||||
"invalidPasswordMinDigitsMessage": "Invalid password: must contain at least {0} numerical digits.",
|
||||
"invalidPasswordMinUpperCaseCharsMessage": "Invalid password: must contain at least {0} upper case characters.",
|
||||
"invalidPasswordMinSpecialCharsMessage": "Invalid password: must contain at least {0} special characters.",
|
||||
"invalidPasswordNotUsernameMessage": "Invalid password: must not be equal to the username.",
|
||||
"invalidPasswordRegexPatternMessage": "Invalid password: fails to match regex pattern(s).",
|
||||
"invalidPasswordHistoryMessage": "Invalid password: must not be equal to any of last {0} passwords.",
|
||||
"invalidPasswordBlacklistedMessage": "Invalid password: password is blacklisted.",
|
||||
"invalidPasswordGenericMessage": "Invalid password: new password doesn't match password policies.",
|
||||
"myResources": "My Resources",
|
||||
"myResourcesSub": "My resources",
|
||||
"doDeny": "Deny",
|
||||
"doRevoke": "Revoke",
|
||||
"doApprove": "Approve",
|
||||
"doRemoveSharing": "Remove Sharing",
|
||||
"doRemoveRequest": "Remove Request",
|
||||
"peopleAccessResource": "People with access to this resource",
|
||||
"resourceManagedPolicies": "Permissions granting access to this resource",
|
||||
"resourceNoPermissionsGrantingAccess": "No permissions granting access to this resource",
|
||||
"anyAction": "Any action",
|
||||
"description": "Description",
|
||||
"name": "Name",
|
||||
"scopes": "Scopes",
|
||||
"resource": "Resource",
|
||||
"user": "User",
|
||||
"peopleSharingThisResource": "People sharing this resource",
|
||||
"shareWithOthers": "Share with others",
|
||||
"needMyApproval": "Need my approval",
|
||||
"requestsWaitingApproval": "Your requests waiting approval",
|
||||
"icon": "Icon",
|
||||
"requestor": "Requestor",
|
||||
"owner": "Owner",
|
||||
"resourcesSharedWithMe": "Resources shared with me",
|
||||
"permissionRequestion": "Permission Requestion",
|
||||
"permission": "Permission",
|
||||
"shares": "share(s)",
|
||||
"notBeingShared": "This resource is not being shared.",
|
||||
"notHaveAnyResource": "You don't have any resources",
|
||||
"noResourcesSharedWithYou": "There are no resources shared with you",
|
||||
"havePermissionRequestsWaitingForApproval": "You have {0} permission request(s) waiting for approval.",
|
||||
"clickHereForDetails": "Click here for details.",
|
||||
"resourceIsNotBeingShared": "The resource is not being shared",
|
||||
"locale_ca": "Català",
|
||||
"locale_cs": "Čeština",
|
||||
"locale_de": "Deutsch",
|
||||
"locale_en": "English",
|
||||
"locale_es": "Español",
|
||||
"locale_fr": "Français",
|
||||
"locale_it": "Italian",
|
||||
"locale_ja": "日本語",
|
||||
"locale_nl": "Nederlands",
|
||||
"locale_no": "Norsk",
|
||||
"locale_lt": "Lietuvių",
|
||||
"locale_pt-BR": "Português (Brasil)",
|
||||
"locale_ru": "Русский",
|
||||
"locale_sk": "Slovenčina",
|
||||
"locale_sv": "Svenska",
|
||||
"locale_tr": "Turkish",
|
||||
"locale_zh-CN": "中文简体",
|
||||
"applicaitonName": "Name",
|
||||
"applicationType": "Application Type",
|
||||
"applicationInUse": "In-use app only",
|
||||
"clearAllFilter": "Clear all filters",
|
||||
"activeFilters": "Active filters",
|
||||
"filterByName": "Filter By Name ...",
|
||||
"allApps": "All applications",
|
||||
"internalApps": "Internal applications",
|
||||
"thirdpartyApps": "Third-Party applications",
|
||||
"appResults": "Results",
|
||||
"clientNotFoundMessage": "Client not found.",
|
||||
"authorizedProvider": "Authorized Provider",
|
||||
"authorizedProviderMessage": "Authorized Providers linked with your account",
|
||||
"identityProvider": "Identity Provider",
|
||||
"identityProviderMessage": "To link your account with identity providers you have configured",
|
||||
"socialLogin": "Social Login",
|
||||
"userDefined": "User Defined",
|
||||
"removeAccess": "Remove Access",
|
||||
"removeAccessMessage": "You will need to grant access again, if you want to use this app account.",
|
||||
"authenticatorStatusMessage": "Two-factor authentication is currently",
|
||||
"authenticatorFinishSetUpTitle": "Your Two-Factor Authentication",
|
||||
"authenticatorFinishSetUpMessage":
|
||||
"Each time you sign in to your Keycloak account, you will be asked to provide a two-factor authentication code.",
|
||||
"authenticatorSubTitle": "Set Up Two-Factor Authentication",
|
||||
"authenticatorSubMessage": "To enhance the security of your account, enable at least one of the available two-factor authentication methods.",
|
||||
"authenticatorMobileTitle": "Mobile Authenticator",
|
||||
"authenticatorMobileMessage": "Use mobile Authenticator to get Verification codes as the two-factor authentication.",
|
||||
"authenticatorMobileFinishSetUpMessage": "The authenticator has been bound to your phone.",
|
||||
"authenticatorActionSetup": "Set up",
|
||||
"authenticatorSMSTitle": "SMS Code",
|
||||
"authenticatorSMSMessage": "Keycloak will send the Verification code to your phone as the two-factor authentication.",
|
||||
"authenticatorSMSFinishSetUpMessage": "Text messages are sent to",
|
||||
"authenticatorDefaultStatus": "Default",
|
||||
"authenticatorChangePhone": "Change Phone Number",
|
||||
"authenticatorBackupCodesTitle": "Backup Codes",
|
||||
"authenticatorBackupCodesMessage": "Get your 8-digit backup codes",
|
||||
"authenticatorBackupCodesFinishSetUpMessage": "12 backup codes were generated at this time. Each one can be used once.",
|
||||
"authenticatorMobileSetupTitle": "Mobile Authenticator Setup",
|
||||
"smscodeIntroMessage": "Enter your phone number and a verification code will be sent to your phone.",
|
||||
"mobileSetupStep1": "Install an authenticator application on your phone. The applications listed here are supported.",
|
||||
"mobileSetupStep2": "Open the application and scan the barcode:",
|
||||
"mobileSetupStep3": "Enter the one-time code provided by the application and click Save to finish the setup.",
|
||||
"scanBarCode": "Want to scan the barcode?",
|
||||
"enterBarCode": "Enter the one-time code",
|
||||
"doCopy": "Copy",
|
||||
"doFinish": "Finish",
|
||||
"authenticatorSMSCodeSetupTitle": "SMS Code Setup",
|
||||
"chooseYourCountry": "Choose your country",
|
||||
"enterYourPhoneNumber": "Enter your phone number",
|
||||
"sendVerficationCode": "Send Verification Code",
|
||||
"enterYourVerficationCode": "Enter your verification code",
|
||||
"authenticatorBackupCodesSetupTitle": "Backup Codes Setup",
|
||||
"backupcodesIntroMessage":
|
||||
"If you lose access to your phone, you can still log into your account through backup codes. Keep them somewhere safe and accessible.",
|
||||
"realmName": "Realm",
|
||||
"doDownload": "Download",
|
||||
"doPrint": "Print",
|
||||
"backupCodesTips-1": "Each backup code can be used once.",
|
||||
"backupCodesTips-2": "These codes were generated on",
|
||||
"generateNewBackupCodes": "Generate New Backup Codes",
|
||||
"backupCodesTips-3": "When you generate new backup codes, the current codes will not work anymore.",
|
||||
"backtoAuthenticatorPage": "Back to Authenticator Page",
|
||||
"resources": "Resources",
|
||||
"sharedwithMe": "Shared with Me",
|
||||
"share": "Share",
|
||||
"sharedwith": "Shared with",
|
||||
"accessPermissions": "Access Permissions",
|
||||
"permissionRequests": "Permission Requests",
|
||||
"approve": "Approve",
|
||||
"approveAll": "Approve all",
|
||||
"people": "people",
|
||||
"perPage": "per page",
|
||||
"currentPage": "Current Page",
|
||||
"sharetheResource": "Share the resource",
|
||||
"group": "Group",
|
||||
"selectPermission": "Select Permission",
|
||||
"addPeople": "Add people to share your resource with",
|
||||
"addTeam": "Add team to share your resource with",
|
||||
"myPermissions": "My Permissions",
|
||||
"waitingforApproval": "Waiting for approval",
|
||||
"anyPermission": "Any Permission",
|
||||
"openshift.scope.user_info": "User information",
|
||||
"openshift.scope.user_check-access": "User access information",
|
||||
"openshift.scope.user_full": "Full Access",
|
||||
"openshift.scope.list-projects": "List projects"
|
||||
};
|
||||
|
||||
export default messages;
|
||||
/* spell-checker: enable */
|
137
src/lib/i18n/generated_messages/11.0.3/account/es.ts
Normal file
137
src/lib/i18n/generated_messages/11.0.3/account/es.ts
Normal file
@ -0,0 +1,137 @@
|
||||
//This code was automatically generated by running dist/bin/generate-i18n-messages.js
|
||||
//PLEASE DO NOT EDIT MANUALLY
|
||||
|
||||
/* spell-checker: disable */
|
||||
const messages = {
|
||||
"doSave": "Guardar",
|
||||
"doCancel": "Cancelar",
|
||||
"doLogOutAllSessions": "Desconectar de todas las sesiones",
|
||||
"doRemove": "Eliminar",
|
||||
"doAdd": "Añadir",
|
||||
"doSignOut": "Desconectar",
|
||||
"editAccountHtmlTitle": "Editar cuenta",
|
||||
"federatedIdentitiesHtmlTitle": "Identidades federadas",
|
||||
"accountLogHtmlTitle": "Registro de la cuenta",
|
||||
"changePasswordHtmlTitle": "Cambiar contraseña",
|
||||
"sessionsHtmlTitle": "Sesiones",
|
||||
"accountManagementTitle": "Gestión de Cuenta Keycloak",
|
||||
"authenticatorTitle": "Autenticador",
|
||||
"applicationsHtmlTitle": "Aplicaciones",
|
||||
"authenticatorCode": "Código de un solo uso",
|
||||
"email": "Email",
|
||||
"firstName": "Nombre",
|
||||
"givenName": "Nombre de pila",
|
||||
"fullName": "Nombre completo",
|
||||
"lastName": "Apellidos",
|
||||
"familyName": "Apellido",
|
||||
"password": "Contraseña",
|
||||
"passwordConfirm": "Confirma la contraseña",
|
||||
"passwordNew": "Nueva contraseña",
|
||||
"username": "Usuario",
|
||||
"address": "Dirección",
|
||||
"street": "Calle",
|
||||
"locality": "Ciudad o Municipio",
|
||||
"region": "Estado, Provincia, o Región",
|
||||
"postal_code": "Código Postal",
|
||||
"country": "País",
|
||||
"emailVerified": "Email verificado",
|
||||
"gssDelegationCredential": "GSS Delegation Credential",
|
||||
"role_admin": "Administrador",
|
||||
"role_realm-admin": "Administrador del dominio",
|
||||
"role_create-realm": "Crear dominio",
|
||||
"role_view-realm": "Ver dominio",
|
||||
"role_view-users": "Ver usuarios",
|
||||
"role_view-applications": "Ver aplicaciones",
|
||||
"role_view-clients": "Ver clientes",
|
||||
"role_view-events": "Ver eventos",
|
||||
"role_view-identity-providers": "Ver proveedores de identidad",
|
||||
"role_manage-realm": "Gestionar dominio",
|
||||
"role_manage-users": "Gestionar usuarios",
|
||||
"role_manage-applications": "Gestionar aplicaciones",
|
||||
"role_manage-identity-providers": "Gestionar proveedores de identidad",
|
||||
"role_manage-clients": "Gestionar clientes",
|
||||
"role_manage-events": "Gestionar eventos",
|
||||
"role_view-profile": "Ver perfil",
|
||||
"role_manage-account": "Gestionar cuenta",
|
||||
"role_read-token": "Leer token",
|
||||
"role_offline-access": "Acceso sin conexión",
|
||||
"client_account": "Cuenta",
|
||||
"client_security-admin-console": "Consola de Administración de Seguridad",
|
||||
"client_realm-management": "Gestión de dominio",
|
||||
"client_broker": "Broker",
|
||||
"requiredFields": "Campos obligatorios",
|
||||
"allFieldsRequired": "Todos los campos obligatorios",
|
||||
"backToApplication": "« Volver a la aplicación",
|
||||
"backTo": "Volver a {0}",
|
||||
"date": "Fecha",
|
||||
"event": "Evento",
|
||||
"ip": "IP",
|
||||
"client": "Cliente",
|
||||
"clients": "Clientes",
|
||||
"details": "Detalles",
|
||||
"started": "Iniciado",
|
||||
"lastAccess": "Último acceso",
|
||||
"expires": "Expira",
|
||||
"applications": "Aplicaciones",
|
||||
"account": "Cuenta",
|
||||
"federatedIdentity": "Identidad federada",
|
||||
"authenticator": "Autenticador",
|
||||
"sessions": "Sesiones",
|
||||
"log": "Regisro",
|
||||
"application": "Aplicación",
|
||||
"availablePermissions": "Permisos disponibles",
|
||||
"grantedPermissions": "Permisos concedidos",
|
||||
"grantedPersonalInfo": "Información personal concedida",
|
||||
"additionalGrants": "Permisos adicionales",
|
||||
"action": "Acción",
|
||||
"inResource": "en",
|
||||
"fullAccess": "Acceso total",
|
||||
"offlineToken": "Código de autorización offline",
|
||||
"revoke": "Revocar permiso",
|
||||
"configureAuthenticators": "Autenticadores configurados",
|
||||
"mobile": "Móvil",
|
||||
"totpStep1":
|
||||
'Instala <a href="https://freeotp.github.io/" target="_blank">FreeOTP</a> o Google Authenticator en tu teléfono móvil. Ambas aplicaciones están disponibles en <a href="https://play.google.com">Google Play</a> y en la App Store de Apple.',
|
||||
"totpStep2": "Abre la aplicación y escanea el código o introduce la clave.",
|
||||
"totpStep3": "Introduce el código único que te muestra la aplicación de autenticación y haz clic en Enviar para finalizar la configuración",
|
||||
"missingUsernameMessage": "Por favor indica tu usuario.",
|
||||
"missingFirstNameMessage": "Por favor indica el nombre.",
|
||||
"invalidEmailMessage": "Email no válido",
|
||||
"missingLastNameMessage": "Por favor indica tus apellidos.",
|
||||
"missingEmailMessage": "Por favor indica el email.",
|
||||
"missingPasswordMessage": "Por favor indica tu contraseña.",
|
||||
"notMatchPasswordMessage": "Las contraseñas no coinciden.",
|
||||
"missingTotpMessage": "Por favor indica tu código de autenticación",
|
||||
"invalidPasswordExistingMessage": "La contraseña actual no es correcta.",
|
||||
"invalidPasswordConfirmMessage": "La confirmación de contraseña no coincide.",
|
||||
"invalidTotpMessage": "El código de autenticación no es válido.",
|
||||
"usernameExistsMessage": "El usuario ya existe",
|
||||
"emailExistsMessage": "El email ya existe",
|
||||
"readOnlyUserMessage": "No puedes actualizar tu usuario porque tu cuenta es de solo lectura.",
|
||||
"readOnlyPasswordMessage": "No puedes actualizar tu contraseña porque tu cuenta es de solo lectura.",
|
||||
"successTotpMessage": "Aplicación de autenticación móvil configurada.",
|
||||
"successTotpRemovedMessage": "Aplicación de autenticación móvil eliminada.",
|
||||
"successGrantRevokedMessage": "Permiso revocado correctamente",
|
||||
"accountUpdatedMessage": "Tu cuenta se ha actualizado.",
|
||||
"accountPasswordUpdatedMessage": "Tu contraseña se ha actualizado.",
|
||||
"missingIdentityProviderMessage": "Proveedor de identidad no indicado.",
|
||||
"invalidFederatedIdentityActionMessage": "Acción no válida o no indicada.",
|
||||
"identityProviderNotFoundMessage": "No se encontró un proveedor de identidad.",
|
||||
"federatedIdentityLinkNotActiveMessage": "Esta identidad ya no está activa",
|
||||
"federatedIdentityRemovingLastProviderMessage": "No puedes eliminar la última identidad federada porque no tienes fijada una contraseña.",
|
||||
"identityProviderRedirectErrorMessage": "Error en la redirección al proveedor de identidad",
|
||||
"identityProviderRemovedMessage": "Proveedor de identidad borrado correctamente.",
|
||||
"accountDisabledMessage": "La cuenta está desactivada, contacta con el administrador.",
|
||||
"accountTemporarilyDisabledMessage": "La cuenta está temporalmente desactivada, contacta con el administrador o inténtalo de nuevo más tarde.",
|
||||
"invalidPasswordMinLengthMessage": "Contraseña incorrecta: longitud mínima {0}.",
|
||||
"invalidPasswordMinLowerCaseCharsMessage": "Contraseña incorrecta: debe contener al menos {0} letras minúsculas.",
|
||||
"invalidPasswordMinDigitsMessage": "Contraseña incorrecta: debe contener al menos {0} caracteres numéricos.",
|
||||
"invalidPasswordMinUpperCaseCharsMessage": "Contraseña incorrecta: debe contener al menos {0} letras mayúsculas.",
|
||||
"invalidPasswordMinSpecialCharsMessage": "Contraseña incorrecta: debe contener al menos {0} caracteres especiales.",
|
||||
"invalidPasswordNotUsernameMessage": "Contraseña incorrecta: no puede ser igual al nombre de usuario.",
|
||||
"invalidPasswordRegexPatternMessage": "Contraseña incorrecta: no cumple la expresión regular.",
|
||||
"invalidPasswordHistoryMessage": "Contraseña incorrecta: no puede ser igual a ninguna de las últimas {0} contraseñas."
|
||||
};
|
||||
|
||||
export default messages;
|
||||
/* spell-checker: enable */
|
150
src/lib/i18n/generated_messages/11.0.3/account/fr.ts
Normal file
150
src/lib/i18n/generated_messages/11.0.3/account/fr.ts
Normal file
@ -0,0 +1,150 @@
|
||||
//This code was automatically generated by running dist/bin/generate-i18n-messages.js
|
||||
//PLEASE DO NOT EDIT MANUALLY
|
||||
|
||||
/* spell-checker: disable */
|
||||
const messages = {
|
||||
"doSave": "Sauvegarder",
|
||||
"doCancel": "Annuler",
|
||||
"doLogOutAllSessions": "Déconnexion de toutes les sessions",
|
||||
"doRemove": "Supprimer",
|
||||
"doAdd": "Ajouter",
|
||||
"doSignOut": "Déconnexion",
|
||||
"editAccountHtmlTitle": "Édition du compte",
|
||||
"federatedIdentitiesHtmlTitle": "Identités fédérées",
|
||||
"accountLogHtmlTitle": "Accès au compte",
|
||||
"changePasswordHtmlTitle": "Changer de mot de passe",
|
||||
"sessionsHtmlTitle": "Sessions",
|
||||
"accountManagementTitle": "Gestion du compte Keycloak",
|
||||
"authenticatorTitle": "Authentification",
|
||||
"applicationsHtmlTitle": "Applications",
|
||||
"authenticatorCode": "Mot de passe unique",
|
||||
"email": "Courriel",
|
||||
"firstName": "Prénom",
|
||||
"givenName": "Prénom",
|
||||
"fullName": "Nom complet",
|
||||
"lastName": "Nom",
|
||||
"familyName": "Nom de famille",
|
||||
"password": "Mot de passe",
|
||||
"passwordConfirm": "Confirmation",
|
||||
"passwordNew": "Nouveau mot de passe",
|
||||
"username": "Compte",
|
||||
"address": "Adresse",
|
||||
"street": "Rue",
|
||||
"locality": "Ville ou Localité",
|
||||
"region": "État, Province ou Région",
|
||||
"postal_code": "Code Postal",
|
||||
"country": "Pays",
|
||||
"emailVerified": "Courriel vérifié",
|
||||
"gssDelegationCredential": "Accréditation de délégation GSS",
|
||||
"role_admin": "Administrateur",
|
||||
"role_realm-admin": "Administrateur du domaine",
|
||||
"role_create-realm": "Créer un domaine",
|
||||
"role_view-realm": "Voir un domaine",
|
||||
"role_view-users": "Voir les utilisateurs",
|
||||
"role_view-applications": "Voir les applications",
|
||||
"role_view-clients": "Voir les clients",
|
||||
"role_view-events": "Voir les événements",
|
||||
"role_view-identity-providers": "Voir les fournisseurs d'identités",
|
||||
"role_manage-realm": "Gérer le domaine",
|
||||
"role_manage-users": "Gérer les utilisateurs",
|
||||
"role_manage-applications": "Gérer les applications",
|
||||
"role_manage-identity-providers": "Gérer les fournisseurs d'identités",
|
||||
"role_manage-clients": "Gérer les clients",
|
||||
"role_manage-events": "Gérer les événements",
|
||||
"role_view-profile": "Voir le profil",
|
||||
"role_manage-account": "Gérer le compte",
|
||||
"role_read-token": "Lire le jeton d'authentification",
|
||||
"role_offline-access": "Accès hors-ligne",
|
||||
"client_account": "Compte",
|
||||
"client_security-admin-console": "Console d'administration de la sécurité",
|
||||
"client_admin-cli": "Admin CLI",
|
||||
"client_realm-management": "Gestion du domaine",
|
||||
"client_broker": "Broker",
|
||||
"requiredFields": "Champs obligatoires",
|
||||
"allFieldsRequired": "Tous les champs sont obligatoires",
|
||||
"backToApplication": "« Revenir à l'application",
|
||||
"backTo": "Revenir à {0}",
|
||||
"date": "Date",
|
||||
"event": "Evénement",
|
||||
"ip": "IP",
|
||||
"client": "Client",
|
||||
"clients": "Clients",
|
||||
"details": "Détails",
|
||||
"started": "Début",
|
||||
"lastAccess": "Dernier accès",
|
||||
"expires": "Expiration",
|
||||
"applications": "Applications",
|
||||
"account": "Compte",
|
||||
"federatedIdentity": "Identité fédérée",
|
||||
"authenticator": "Authentification",
|
||||
"sessions": "Sessions",
|
||||
"log": "Connexion",
|
||||
"application": "Application",
|
||||
"availablePermissions": "Permissions disponibles",
|
||||
"grantedPermissions": "Permissions accordées",
|
||||
"grantedPersonalInfo": "Informations personnelles accordées",
|
||||
"additionalGrants": "Droits additionnels",
|
||||
"action": "Action",
|
||||
"inResource": "dans",
|
||||
"fullAccess": "Accès complet",
|
||||
"offlineToken": "Jeton d'authentification hors-ligne",
|
||||
"revoke": "Révoquer un droit",
|
||||
"configureAuthenticators": "Authentifications configurées.",
|
||||
"mobile": "Téléphone mobile",
|
||||
"totpStep1": "Installez une des applications suivantes sur votre mobile",
|
||||
"totpStep2": "Ouvrez l'application et scannez le code-barres ou entrez la clef.",
|
||||
"totpStep3": "Entrez le code à usage unique fourni par l'application et cliquez sur Sauvegarder pour terminer.",
|
||||
"totpManualStep2": "Ouvrez l'application et entrez la clef",
|
||||
"totpManualStep3": "Utilisez les valeurs de configuration suivante si l'application les autorise",
|
||||
"totpUnableToScan": "Impossible de scanner ?",
|
||||
"totpScanBarcode": "Scanner le code-barres ?",
|
||||
"totp.totp": "Basé sur le temps",
|
||||
"totp.hotp": "Basé sur un compteur",
|
||||
"totpType": "Type",
|
||||
"totpAlgorithm": "Algorithme",
|
||||
"totpDigits": "Chiffres",
|
||||
"totpInterval": "Intervalle",
|
||||
"totpCounter": "Compteur",
|
||||
"missingUsernameMessage": "Veuillez entrer votre nom d'utilisateur.",
|
||||
"missingFirstNameMessage": "Veuillez entrer votre prénom.",
|
||||
"invalidEmailMessage": "Courriel invalide.",
|
||||
"missingLastNameMessage": "Veuillez entrer votre nom.",
|
||||
"missingEmailMessage": "Veuillez entrer votre courriel.",
|
||||
"missingPasswordMessage": "Veuillez entrer votre mot de passe.",
|
||||
"notMatchPasswordMessage": "Les mots de passe ne sont pas identiques",
|
||||
"missingTotpMessage": "Veuillez entrer le code d'authentification.",
|
||||
"invalidPasswordExistingMessage": "Mot de passe existant invalide.",
|
||||
"invalidPasswordConfirmMessage": "Le mot de passe de confirmation ne correspond pas.",
|
||||
"invalidTotpMessage": "Le code d'authentification est invalide.",
|
||||
"usernameExistsMessage": "Le nom d'utilisateur existe déjà.",
|
||||
"emailExistsMessage": "Le courriel existe déjà.",
|
||||
"readOnlyUserMessage": "Vous ne pouvez pas mettre à jour votre compte car il est en lecture seule.",
|
||||
"readOnlyPasswordMessage": "Vous ne pouvez pas mettre à jour votre mot de passe car votre compte est en lecture seule.",
|
||||
"successTotpMessage": "L'authentification via téléphone mobile est configurée.",
|
||||
"successTotpRemovedMessage": "L'authentification via téléphone mobile est supprimée.",
|
||||
"successGrantRevokedMessage": "Droit révoqué avec succès.",
|
||||
"accountUpdatedMessage": "Votre compte a été mis à jour.",
|
||||
"accountPasswordUpdatedMessage": "Votre mot de passe a été mis à jour.",
|
||||
"missingIdentityProviderMessage": "Le fournisseur d'identité n'est pas spécifié.",
|
||||
"invalidFederatedIdentityActionMessage": "Action manquante ou invalide.",
|
||||
"identityProviderNotFoundMessage": "Le fournisseur d'identité spécifié n'est pas trouvé.",
|
||||
"federatedIdentityLinkNotActiveMessage": "Cette identité n'est plus active dorénavant.",
|
||||
"federatedIdentityRemovingLastProviderMessage":
|
||||
"Vous ne pouvez pas supprimer votre dernière fédération d'identité sans avoir de mot de passe spécifié.",
|
||||
"identityProviderRedirectErrorMessage": "Erreur de redirection vers le fournisseur d'identité.",
|
||||
"identityProviderRemovedMessage": "Le fournisseur d'identité a été supprimé correctement.",
|
||||
"identityProviderAlreadyLinkedMessage": "Le fournisseur d'identité retourné par {0} est déjà lié à un autre utilisateur.",
|
||||
"accountDisabledMessage": "Ce compte est désactivé, veuillez contacter votre administrateur.",
|
||||
"accountTemporarilyDisabledMessage": "Ce compte est temporairement désactivé, veuillez contacter votre administrateur ou réessayez plus tard.",
|
||||
"invalidPasswordMinLengthMessage": "Mot de passe invalide: longueur minimale {0}.",
|
||||
"invalidPasswordMinLowerCaseCharsMessage": "Mot de passe invalide: doit contenir au moins {0} lettre(s) en minuscule.",
|
||||
"invalidPasswordMinDigitsMessage": "Mot de passe invalide: doit contenir au moins {0} chiffre(s).",
|
||||
"invalidPasswordMinUpperCaseCharsMessage": "Mot de passe invalide: doit contenir au moins {0} lettre(s) en majuscule.",
|
||||
"invalidPasswordMinSpecialCharsMessage": "Mot de passe invalide: doit contenir au moins {0} caractère(s) spéciaux.",
|
||||
"invalidPasswordNotUsernameMessage": "Mot de passe invalide: ne doit pas être identique au nom d'utilisateur.",
|
||||
"invalidPasswordRegexPatternMessage": "Mot de passe invalide: ne valide pas l'expression rationnelle.",
|
||||
"invalidPasswordHistoryMessage": "Mot de passe invalide: ne doit pas être égal aux {0} derniers mots de passe."
|
||||
};
|
||||
|
||||
export default messages;
|
||||
/* spell-checker: enable */
|
310
src/lib/i18n/generated_messages/11.0.3/account/it.ts
Normal file
310
src/lib/i18n/generated_messages/11.0.3/account/it.ts
Normal file
@ -0,0 +1,310 @@
|
||||
//This code was automatically generated by running dist/bin/generate-i18n-messages.js
|
||||
//PLEASE DO NOT EDIT MANUALLY
|
||||
|
||||
/* spell-checker: disable */
|
||||
const messages = {
|
||||
"doSave": "Salva",
|
||||
"doCancel": "Annulla",
|
||||
"doLogOutAllSessions": "Effettua il logout da tutte le sessioni",
|
||||
"doRemove": "Elimina",
|
||||
"doAdd": "Aggiungi",
|
||||
"doSignOut": "Esci",
|
||||
"doLogIn": "Log In",
|
||||
"doLink": "Link",
|
||||
"editAccountHtmlTitle": "Modifica Account",
|
||||
"personalInfoHtmlTitle": "Informazioni personali",
|
||||
"federatedIdentitiesHtmlTitle": "Identità federate",
|
||||
"accountLogHtmlTitle": "Log dell'account",
|
||||
"changePasswordHtmlTitle": "Cambia password",
|
||||
"deviceActivityHtmlTitle": "Attività dei dispositivi",
|
||||
"sessionsHtmlTitle": "Sessioni",
|
||||
"accountManagementTitle": "Gestione degli account di Keycloak",
|
||||
"authenticatorTitle": "Autenticatore",
|
||||
"applicationsHtmlTitle": "Applicazioni",
|
||||
"linkedAccountsHtmlTitle": "Account collegati",
|
||||
"accountManagementWelcomeMessage": "Benvenuto nella gestione degli account di Keycloak",
|
||||
"personalInfoIntroMessage": "Gestisci le tue informazioni di base",
|
||||
"accountSecurityTitle": "Sicurezza dell'account",
|
||||
"accountSecurityIntroMessage": "Controlla la tua password e gli accessi dell'account",
|
||||
"applicationsIntroMessage": "Traccia e gestisci i permessi delle applicazioni nell'accesso al tuo account",
|
||||
"resourceIntroMessage": "Condividi le tue risorse tra i membri del team",
|
||||
"passwordLastUpdateMessage": "La tua password è stata aggiornata il",
|
||||
"updatePasswordTitle": "Aggiornamento password",
|
||||
"updatePasswordMessageTitle": "Assicurati di scegliere una password robusta",
|
||||
"updatePasswordMessage":
|
||||
"Una password robusta contiene un misto di numeri, lettere, e simboli. È difficile da indovinare, non assomiglia a una parola reale, ed è utilizzata solo per questo account.",
|
||||
"personalSubTitle": "Le tue informazioni personali",
|
||||
"personalSubMessage": "Gestisce queste informazioni di base: il tuo nome, cognome, e indirizzo email",
|
||||
"authenticatorCode": "Codice monouso",
|
||||
"email": "Email",
|
||||
"firstName": "Nome",
|
||||
"givenName": "Nome",
|
||||
"fullName": "Nome completo",
|
||||
"lastName": "Cognome",
|
||||
"familyName": "Cognome",
|
||||
"password": "Password",
|
||||
"currentPassword": "Password attuale",
|
||||
"passwordConfirm": "Conferma password",
|
||||
"passwordNew": "Nuova password",
|
||||
"username": "Username",
|
||||
"address": "Indirizzo",
|
||||
"street": "Via",
|
||||
"locality": "Città o località",
|
||||
"region": "Stato, Provincia, o Regione",
|
||||
"postal_code": "CAP",
|
||||
"country": "Paese",
|
||||
"emailVerified": "Email verificata",
|
||||
"gssDelegationCredential": "Credenziali delega GSS",
|
||||
"profileScopeConsentText": "Profilo utente",
|
||||
"emailScopeConsentText": "Indirizzo email",
|
||||
"addressScopeConsentText": "Indirizzo",
|
||||
"phoneScopeConsentText": "Numero di telefono",
|
||||
"offlineAccessScopeConsentText": "Accesso offline",
|
||||
"samlRoleListScopeConsentText": "I miei ruoli",
|
||||
"rolesScopeConsentText": "Ruoli utente",
|
||||
"role_admin": "Admin",
|
||||
"role_realm-admin": "Realm admin",
|
||||
"role_create-realm": "Crea realm",
|
||||
"role_view-realm": "Visualizza realm",
|
||||
"role_view-users": "Visualizza utenti",
|
||||
"role_view-applications": "Visualizza applicazioni",
|
||||
"role_view-clients": "Visualizza client",
|
||||
"role_view-events": "Visualizza eventi",
|
||||
"role_view-identity-providers": "Visualizza identity provider",
|
||||
"role_view-consent": "Visualizza consensi",
|
||||
"role_manage-realm": "Gestisci realm",
|
||||
"role_manage-users": "Gestisci utenti",
|
||||
"role_manage-applications": "Gestisci applicazioni",
|
||||
"role_manage-identity-providers": "Gestisci identity provider",
|
||||
"role_manage-clients": "Gestisci client",
|
||||
"role_manage-events": "Gestisci eventi",
|
||||
"role_view-profile": "Visualizza profilo",
|
||||
"role_manage-account": "Gestisci account",
|
||||
"role_manage-account-links": "Gestisci i link dell'account",
|
||||
"role_manage-consent": "Gestisci consensi",
|
||||
"role_read-token": "Leggi token",
|
||||
"role_offline-access": "Accesso offline",
|
||||
"role_uma_authorization": "Ottieni permessi",
|
||||
"client_account": "Account",
|
||||
"client_account-console": "Console account",
|
||||
"client_security-admin-console": "Console di amministrazione di sicurezza",
|
||||
"client_admin-cli": "Admin CLI",
|
||||
"client_realm-management": "Gestione realm",
|
||||
"client_broker": "Broker",
|
||||
"requiredFields": "Campi obbligatori",
|
||||
"allFieldsRequired": "Tutti campi obbligatori",
|
||||
"backToApplication": "« Torna all'applicazione",
|
||||
"backTo": "Torna a {0}",
|
||||
"date": "Data",
|
||||
"event": "Evento",
|
||||
"ip": "IP",
|
||||
"client": "Client",
|
||||
"clients": "Client",
|
||||
"details": "Dettagli",
|
||||
"started": "Iniziato",
|
||||
"lastAccess": "Ultimo accesso",
|
||||
"expires": "Scade",
|
||||
"applications": "Applicazioni",
|
||||
"account": "Account",
|
||||
"federatedIdentity": "Identità federate",
|
||||
"authenticator": "Autenticatore",
|
||||
"device-activity": "Attività dei dispositivi",
|
||||
"sessions": "Sessioni",
|
||||
"log": "Log",
|
||||
"application": "Applicazione",
|
||||
"availablePermissions": "Autorizzazioni disponibili",
|
||||
"grantedPermissions": "Autorizzazioni concesse",
|
||||
"grantedPersonalInfo": "Informazioni personali concesse",
|
||||
"additionalGrants": "Ulteriori concessioni",
|
||||
"action": "Azione",
|
||||
"inResource": "in",
|
||||
"fullAccess": "Accesso completo",
|
||||
"offlineToken": "Token offline",
|
||||
"revoke": "Revoca concessione",
|
||||
"configureAuthenticators": "Autenticatori configurati",
|
||||
"mobile": "Dispositivo mobile",
|
||||
"totpStep1": "Installa una delle seguenti applicazioni sul tuo dispositivo mobile",
|
||||
"totpStep2": "Apri l'applicazione e scansiona il codice QR",
|
||||
"totpStep3": "Scrivi il codice monouso fornito dall'applicazione e clicca Salva per completare il setup.",
|
||||
"totpStep3DeviceName": "Fornisci il nome del dispositivo per aiutarti a gestire i dispositivi di autenticazione.",
|
||||
"totpManualStep2": "Apri l'applicazione e scrivi la chiave",
|
||||
"totpManualStep3": "Usa le seguenti impostazioni se l'applicazione lo consente",
|
||||
"totpUnableToScan": "Non riesci a scansionare il codice QR?",
|
||||
"totpScanBarcode": "Vuoi scansionare il codice QR?",
|
||||
"totp.totp": "Basato sull'ora",
|
||||
"totp.hotp": "Basato sul contatore",
|
||||
"totpType": "Tipo",
|
||||
"totpAlgorithm": "Algoritmo",
|
||||
"totpDigits": "Cifre",
|
||||
"totpInterval": "Intervallo",
|
||||
"totpCounter": "Contatore",
|
||||
"totpDeviceName": "Nome dispositivo",
|
||||
"missingUsernameMessage": "Inserisci lo username.",
|
||||
"missingFirstNameMessage": "Inserisci il nome.",
|
||||
"invalidEmailMessage": "Indirizzo email non valido.",
|
||||
"missingLastNameMessage": "Inserisci il cognome.",
|
||||
"missingEmailMessage": "Inserisci l'indirizzo email.",
|
||||
"missingPasswordMessage": "Inserisci la password.",
|
||||
"notMatchPasswordMessage": "Le password non coincidono.",
|
||||
"invalidUserMessage": "Utente non valido",
|
||||
"missingTotpMessage": "Inserisci il codice di autenticazione.",
|
||||
"missingTotpDeviceNameMessage": "Inserisci il nome del dispositivo di autenticazione.",
|
||||
"invalidPasswordExistingMessage": "Password esistente non valida.",
|
||||
"invalidPasswordConfirmMessage": "La password di conferma non coincide.",
|
||||
"invalidTotpMessage": "Codice di autenticazione non valido.",
|
||||
"usernameExistsMessage": "Username già esistente.",
|
||||
"emailExistsMessage": "Email già esistente.",
|
||||
"readOnlyUserMessage": "Non puoi aggiornare il tuo account poiché è in modalità sola lettura.",
|
||||
"readOnlyUsernameMessage": "Non puoi aggiornare il tuo nome utente poiché è in modalità sola lettura.",
|
||||
"readOnlyPasswordMessage": "Non puoi aggiornare il tuo account poiché è in modalità sola lettura.",
|
||||
"successTotpMessage": "Autenticatore mobile configurato.",
|
||||
"successTotpRemovedMessage": "Autenticatore mobile eliminato.",
|
||||
"successGrantRevokedMessage": "Concessione revocata con successo.",
|
||||
"accountUpdatedMessage": "Il tuo account è stato aggiornato.",
|
||||
"accountPasswordUpdatedMessage": "La tua password è stata aggiornata.",
|
||||
"missingIdentityProviderMessage": "Identity provider non specificato.",
|
||||
"invalidFederatedIdentityActionMessage": "Azione non valida o mancante.",
|
||||
"identityProviderNotFoundMessage": "L'identity provider specificato non è stato trovato.",
|
||||
"federatedIdentityLinkNotActiveMessage": "Questo identity non è più attivo.",
|
||||
"federatedIdentityRemovingLastProviderMessage": "Non puoi rimuovere l'ultima identità federata poiché non hai più la password.",
|
||||
"identityProviderRedirectErrorMessage": "Il reindirizzamento all'identity provider è fallito.",
|
||||
"identityProviderRemovedMessage": "Identity provider eliminato correttamente.",
|
||||
"identityProviderAlreadyLinkedMessage": "L'identità federata restituita da {0} è già collegata ad un altro utente.",
|
||||
"staleCodeAccountMessage": "La pagina è scaduta. Prova di nuovo.",
|
||||
"consentDenied": "Consenso negato.",
|
||||
"accountDisabledMessage": "Account disabilitato, contatta l'amministratore.",
|
||||
"accountTemporarilyDisabledMessage": "L'account è temporaneamente disabilitato, contatta l'amministratore o riprova più tardi.",
|
||||
"invalidPasswordMinLengthMessage": "Password non valida: lunghezza minima {0}.",
|
||||
"invalidPasswordMinLowerCaseCharsMessage": "Password non valida: deve contenere almeno {0} caratteri minuscoli.",
|
||||
"invalidPasswordMinDigitsMessage": "Password non valida: deve contenere almeno {0} numeri.",
|
||||
"invalidPasswordMinUpperCaseCharsMessage": "Password non valida: deve contenere almeno {0} caratteri maiuscoli.",
|
||||
"invalidPasswordMinSpecialCharsMessage": "Password non valida: deve contenere almeno {0} caratteri speciali.",
|
||||
"invalidPasswordNotUsernameMessage": "Password non valida: non deve essere uguale allo username.",
|
||||
"invalidPasswordRegexPatternMessage": "Password non valida: fallito il match con una o più espressioni regolari.",
|
||||
"invalidPasswordHistoryMessage": "Password non valida: non deve essere uguale a una delle ultime {0} password.",
|
||||
"invalidPasswordBlacklistedMessage": "Password non valida: la password non è consentita.",
|
||||
"invalidPasswordGenericMessage": "Password non valida: la nuova password non rispetta le indicazioni previste.",
|
||||
"myResources": "Le mie risorse",
|
||||
"myResourcesSub": "Le mie risorse",
|
||||
"doDeny": "Nega",
|
||||
"doRevoke": "Revoca",
|
||||
"doApprove": "Approva",
|
||||
"doRemoveSharing": "Rimuovi condivisione",
|
||||
"doRemoveRequest": "Rimuovi richiesta",
|
||||
"peopleAccessResource": "Persone che hanno accesso a questa risorsa",
|
||||
"resourceManagedPolicies": "Permessi che danno accesso a questa risorsa",
|
||||
"resourceNoPermissionsGrantingAccess": "Nessun permesso dà accesso a questa risorsa",
|
||||
"anyAction": "Qualsiasi azione",
|
||||
"description": "Descrizione",
|
||||
"name": "Nome",
|
||||
"scopes": "Ambito",
|
||||
"resource": "Risorsa",
|
||||
"user": "Utente",
|
||||
"peopleSharingThisResource": "Persone che condividono questa risorsa",
|
||||
"shareWithOthers": "Condividi con altri",
|
||||
"needMyApproval": "Richiede la mia approvazione",
|
||||
"requestsWaitingApproval": "La tua richiesta è in attesa di approvazione",
|
||||
"icon": "Icona",
|
||||
"requestor": "Richiedente",
|
||||
"owner": "Proprietario",
|
||||
"resourcesSharedWithMe": "Risorse condivise con me",
|
||||
"permissionRequestion": "Richiesta di permesso",
|
||||
"permission": "Permesso",
|
||||
"shares": "condivisioni",
|
||||
"notBeingShared": "Questa risorsa non è in condivisione.",
|
||||
"notHaveAnyResource": "Non hai nessuna risorsa",
|
||||
"noResourcesSharedWithYou": "Non ci sono risorse condivise con te",
|
||||
"havePermissionRequestsWaitingForApproval": "Hai {0} richiesta(e) di permesso in attesa di approvazione.",
|
||||
"clickHereForDetails": "Clicca qui per i dettagli.",
|
||||
"resourceIsNotBeingShared": "La risorsa non è in condivisione",
|
||||
"locale_it": "Italiano",
|
||||
"applicaitonName": "Nome",
|
||||
"applicationType": "Tipo applicazione",
|
||||
"applicationInUse": "In-use app only",
|
||||
"clearAllFilter": "Azzera tutti i filtri",
|
||||
"activeFilters": "Filtri attivi",
|
||||
"filterByName": "Filtra per nome ...",
|
||||
"allApps": "Tutte le applicazioni",
|
||||
"internalApps": "Applicazioni interne",
|
||||
"thirdpartyApps": "Applicazioni di terze parti",
|
||||
"appResults": "Risultati",
|
||||
"clientNotFoundMessage": "Client non trovato.",
|
||||
"authorizedProvider": "Provider autorizzato",
|
||||
"authorizedProviderMessage": "Provider autorizzati collegati al tuo account",
|
||||
"identityProvider": "Identity provider",
|
||||
"identityProviderMessage": "Collegare il tuo account con gli identity provider che hai configurato",
|
||||
"socialLogin": "Social Login",
|
||||
"userDefined": "Definito dall'utente",
|
||||
"removeAccess": "Rimuovi accesso",
|
||||
"removeAccessMessage": "Devi concedere di nuovo l'accesso, se vuoi utilizzare l'account di questa applicazione.",
|
||||
"authenticatorStatusMessage": "L'autenticazione a due fattori è attualmente",
|
||||
"authenticatorFinishSetUpTitle": "La tua autenticazione a due fattori",
|
||||
"authenticatorFinishSetUpMessage":
|
||||
"Ogni volta che effettui l'accesso al tuo account Keycloak, ti verrà richiesto di fornire il tuo codice di autenticazione a due fattori.",
|
||||
"authenticatorSubTitle": "Imposta l'autenticazione a due fattori",
|
||||
"authenticatorSubMessage":
|
||||
"Per incrementare la sicurezza del tuo account, attiva almeno uno dei metodi disponibili per l'autenticazione a due fattori.",
|
||||
"authenticatorMobileTitle": "Autenticatore mobile",
|
||||
"authenticatorMobileMessage": "Utilizza l'autenticatore mobile per ottenere i codici di verifica per l'autenticazione a due fattori.",
|
||||
"authenticatorMobileFinishSetUpMessage": "L'autenticatore è stato collegato al tuo telefono.",
|
||||
"authenticatorActionSetup": "Set up",
|
||||
"authenticatorSMSTitle": "Codice SMS",
|
||||
"authenticatorSMSMessage": "Keycloak invierà il codice di verifica al tuo telefono per l'autenticazione a due fattori.",
|
||||
"authenticatorSMSFinishSetUpMessage": "I messaggi di testo vengono inviati a",
|
||||
"authenticatorDefaultStatus": "Default",
|
||||
"authenticatorChangePhone": "Cambia numero di telefono",
|
||||
"authenticatorBackupCodesTitle": "Codici di backup",
|
||||
"authenticatorBackupCodesMessage": "Ottieni i tuoi codici di backup a otto cifre",
|
||||
"authenticatorBackupCodesFinishSetUpMessage": "Sono stati generati dodici codici di backup. Ognuno può essere usato una sola volta.",
|
||||
"authenticatorMobileSetupTitle": "Setup autenticatore mobile",
|
||||
"smscodeIntroMessage": "Inserisci il tuo numero di telefono e ti verrà inviato un codice di verifica.",
|
||||
"mobileSetupStep1": "Installa un'applicazione di autenticazione sul tuo telefono. Sono supportate le applicazioni qui elencate.",
|
||||
"mobileSetupStep2": "Apri l'applicazione e scansiona il codice QR:",
|
||||
"mobileSetupStep3": "Inserisci il codice monouso fornito dall'applicazione e clicca Salva per completare il setup.",
|
||||
"scanBarCode": "Vuoi scansionare il codice QR?",
|
||||
"enterBarCode": "Inserisci il codice monouso",
|
||||
"doCopy": "Copia",
|
||||
"doFinish": "Termina",
|
||||
"authenticatorSMSCodeSetupTitle": "Setup codice SMS",
|
||||
"chooseYourCountry": "Scegli la tua nazione",
|
||||
"enterYourPhoneNumber": "Inserisci il tuo numero di telefono",
|
||||
"sendVerficationCode": "Invia il codice di verifica",
|
||||
"enterYourVerficationCode": "Inserisci il codice di verifica",
|
||||
"authenticatorBackupCodesSetupTitle": "Setup backup codici",
|
||||
"backupcodesIntroMessage":
|
||||
"Se non disponi più del tuo telefono, puoi comunque accedere al tuo account attraverso i codici di backup. Conservali in un posto sicuro e accessibile.",
|
||||
"realmName": "Realm",
|
||||
"doDownload": "Download",
|
||||
"doPrint": "Stampa",
|
||||
"backupCodesTips-1": "Ogni codice di backup può essere usato una sola volta.",
|
||||
"backupCodesTips-2": "Questi codici sono stati generati il",
|
||||
"generateNewBackupCodes": "Genera dei nuovi codici di backup",
|
||||
"backupCodesTips-3": "Quando generi dei nuovi codici di backup, quelli attuali non funzioneranno più.",
|
||||
"backtoAuthenticatorPage": "Torna alla pagina dell'autenticatore",
|
||||
"resources": "Risorse",
|
||||
"sharedwithMe": "Condiviso con me",
|
||||
"share": "Condiviso",
|
||||
"sharedwith": "Condiviso con",
|
||||
"accessPermissions": "Permessi di accesso",
|
||||
"permissionRequests": "Richieste di permesso",
|
||||
"approve": "Approva",
|
||||
"approveAll": "Approva tutti",
|
||||
"people": "persone",
|
||||
"perPage": "per pagina",
|
||||
"currentPage": "Pagina corrente",
|
||||
"sharetheResource": "Condividi la risorsa",
|
||||
"group": "Gruppo",
|
||||
"selectPermission": "Seleziona permessi",
|
||||
"addPeople": "Aggiungi persone con le quali condividere la tua risorsa",
|
||||
"addTeam": "Aggiungi gruppi con i quali condividere la tua risorsa",
|
||||
"myPermissions": "Miei permessi",
|
||||
"waitingforApproval": "Attesa dell'approvazione",
|
||||
"anyPermission": "Qualsiasi permesso",
|
||||
"openshift.scope.user_info": "Informazioni utente",
|
||||
"openshift.scope.user_check-access": "Informazioni per l'accesso dell'utente",
|
||||
"openshift.scope.user_full": "Accesso completo",
|
||||
"openshift.scope.list-projects": "Elenca progetti"
|
||||
};
|
||||
|
||||
export default messages;
|
||||
/* spell-checker: enable */
|
324
src/lib/i18n/generated_messages/11.0.3/account/ja.ts
Normal file
324
src/lib/i18n/generated_messages/11.0.3/account/ja.ts
Normal file
@ -0,0 +1,324 @@
|
||||
//This code was automatically generated by running dist/bin/generate-i18n-messages.js
|
||||
//PLEASE DO NOT EDIT MANUALLY
|
||||
|
||||
/* spell-checker: disable */
|
||||
const messages = {
|
||||
"doSave": "保存",
|
||||
"doCancel": "キャンセル",
|
||||
"doLogOutAllSessions": "全セッションからログアウト",
|
||||
"doRemove": "削除",
|
||||
"doAdd": "追加",
|
||||
"doSignOut": "サインアウト",
|
||||
"doLogIn": "ログイン",
|
||||
"doLink": "リンク",
|
||||
"editAccountHtmlTitle": "アカウントの編集",
|
||||
"personalInfoHtmlTitle": "個人情報",
|
||||
"federatedIdentitiesHtmlTitle": "連携済みアイデンティティー",
|
||||
"accountLogHtmlTitle": "アカウントログ",
|
||||
"changePasswordHtmlTitle": "パスワード変更",
|
||||
"deviceActivityHtmlTitle": "デバイス・アクティビティー",
|
||||
"sessionsHtmlTitle": "セッション",
|
||||
"accountManagementTitle": "Keycloakアカウント管理",
|
||||
"authenticatorTitle": "オーセンティケーター",
|
||||
"applicationsHtmlTitle": "アプリケーション",
|
||||
"linkedAccountsHtmlTitle": "リンクされたアカウント",
|
||||
"accountManagementWelcomeMessage": "Keycloakアカウント管理へようこそ",
|
||||
"personalInfoIntroMessage": "基本情報を管理する",
|
||||
"accountSecurityTitle": "アカウント・セキュリティー",
|
||||
"accountSecurityIntroMessage": "パスワードとアカウント・アクセスを制御する",
|
||||
"applicationsIntroMessage": "アカウントへアクセスするためにアプリのパーミッションを追跡して管理する",
|
||||
"resourceIntroMessage": "チームメンバー間でリソースを共有する",
|
||||
"passwordLastUpdateMessage": "パスワードは更新されました",
|
||||
"updatePasswordTitle": "パスワードの更新",
|
||||
"updatePasswordMessageTitle": "強力なパスワードを選択してください",
|
||||
"updatePasswordMessage":
|
||||
"強力なパスワードは、数字、文字、記号を含みます。推測が難しく、実在する言葉に似ておらず、このアカウントだけで使用されています。",
|
||||
"personalSubTitle": "個人情報",
|
||||
"personalSubMessage": "この基本情報を管理してください:名、姓、メール",
|
||||
"authenticatorCode": "ワンタイムコード",
|
||||
"email": "Eメール",
|
||||
"firstName": "名",
|
||||
"givenName": "名",
|
||||
"fullName": "氏名",
|
||||
"lastName": "姓",
|
||||
"familyName": "姓",
|
||||
"password": "パスワード",
|
||||
"currentPassword": "現在のパスワード",
|
||||
"passwordConfirm": "新しいパスワード(確認)",
|
||||
"passwordNew": "新しいパスワード",
|
||||
"username": "ユーザー名",
|
||||
"address": "住所",
|
||||
"street": "番地",
|
||||
"locality": "市区町村",
|
||||
"region": "都道府県",
|
||||
"postal_code": "郵便番号",
|
||||
"country": "国",
|
||||
"emailVerified": "確認済みEメール",
|
||||
"gssDelegationCredential": "GSS委譲クレデンシャル",
|
||||
"profileScopeConsentText": "ユーザー・プロファイル",
|
||||
"emailScopeConsentText": "メールアドレス",
|
||||
"addressScopeConsentText": "アドレス",
|
||||
"phoneScopeConsentText": "電話番号",
|
||||
"offlineAccessScopeConsentText": "オフライン・アクセス",
|
||||
"samlRoleListScopeConsentText": "ロール",
|
||||
"rolesScopeConsentText": "ユーザーロール",
|
||||
"role_admin": "管理者",
|
||||
"role_realm-admin": "レルム管理者",
|
||||
"role_create-realm": "レルムの作成",
|
||||
"role_view-realm": "レルムの参照",
|
||||
"role_view-users": "ユーザーの参照",
|
||||
"role_view-applications": "アプリケーションの参照",
|
||||
"role_view-clients": "クライアントの参照",
|
||||
"role_view-events": "イベントの参照",
|
||||
"role_view-identity-providers": "アイデンティティー・プロバイダーの参照",
|
||||
"role_view-consent": "同意の参照",
|
||||
"role_manage-realm": "レルムの管理",
|
||||
"role_manage-users": "ユーザーの管理",
|
||||
"role_manage-applications": "アプリケーションの管理",
|
||||
"role_manage-identity-providers": "アイデンティティー・プロバイダーの管理",
|
||||
"role_manage-clients": "クライアントの管理",
|
||||
"role_manage-events": "イベントの管理",
|
||||
"role_view-profile": "プロファイルの参照",
|
||||
"role_manage-account": "アカウントの管理",
|
||||
"role_manage-account-links": "アカウントリンクの管理",
|
||||
"role_manage-consent": "同意の管理",
|
||||
"role_read-token": "トークンの参照",
|
||||
"role_offline-access": "オフライン・アクセス",
|
||||
"role_uma_authorization": "パーミッションの取得",
|
||||
"client_account": "アカウント",
|
||||
"client_account-console": "アカウント・コンソール",
|
||||
"client_security-admin-console": "セキュリティー管理コンソール",
|
||||
"client_admin-cli": "管理CLI",
|
||||
"client_realm-management": "レルム管理",
|
||||
"client_broker": "ブローカー",
|
||||
"requiredFields": "必須",
|
||||
"allFieldsRequired": "全ての入力項目が必須",
|
||||
"backToApplication": "« アプリケーションに戻る",
|
||||
"backTo": "{0}に戻る",
|
||||
"date": "日付",
|
||||
"event": "イベント",
|
||||
"ip": "IP",
|
||||
"client": "クライアント",
|
||||
"clients": "クライアント",
|
||||
"details": "詳細",
|
||||
"started": "開始",
|
||||
"lastAccess": "最終アクセス",
|
||||
"expires": "有効期限",
|
||||
"applications": "アプリケーション",
|
||||
"account": "アカウント",
|
||||
"federatedIdentity": "連携済みアイデンティティー",
|
||||
"authenticator": "オーセンティケーター",
|
||||
"device-activity": "デバイス・アクティビティー",
|
||||
"sessions": "セッション",
|
||||
"log": "ログ",
|
||||
"application": "アプリケーション",
|
||||
"availableRoles": "利用可能なロール",
|
||||
"grantedPermissions": "許可されたパーミッション",
|
||||
"grantedPersonalInfo": "許可された個人情報",
|
||||
"additionalGrants": "追加の許可",
|
||||
"action": "アクション",
|
||||
"inResource": "in",
|
||||
"fullAccess": "フルアクセス",
|
||||
"offlineToken": "オフライン・トークン",
|
||||
"revoke": "許可の取り消し",
|
||||
"configureAuthenticators": "設定済みのオーセンティケーター",
|
||||
"mobile": "モバイル",
|
||||
"totpStep1": "モバイルに以下のアプリケーションのいずれかをインストールしてください。",
|
||||
"totpStep2": "アプリケーションを開き、バーコードをスキャンしてください。",
|
||||
"totpStep3": "アプリケーションで提供されたワンタイムコードを入力して保存をクリックし、セットアップを完了してください。",
|
||||
"totpStep3DeviceName": "OTPデバイスの管理に役立つようなデバイス名を指定してください。",
|
||||
"totpManualStep2": "アプリケーションを開き、キーを入力してください。",
|
||||
"totpManualStep3": "アプリケーションが設定できる場合は、次の設定値を使用してください。",
|
||||
"totpUnableToScan": "スキャンできませんか?",
|
||||
"totpScanBarcode": "バーコードをスキャンしますか?",
|
||||
"totp.totp": "時間ベース",
|
||||
"totp.hotp": "カウンターベース",
|
||||
"totpType": "タイプ",
|
||||
"totpAlgorithm": "アルゴリズム",
|
||||
"totpDigits": "数字",
|
||||
"totpInterval": "間隔",
|
||||
"totpCounter": "カウンター",
|
||||
"totpDeviceName": "デバイス名",
|
||||
"missingUsernameMessage": "ユーザー名を入力してください。",
|
||||
"missingFirstNameMessage": "名を入力してください。",
|
||||
"invalidEmailMessage": "無効なメールアドレスです。",
|
||||
"missingLastNameMessage": "姓を入力してください。",
|
||||
"missingEmailMessage": "Eメールを入力してください。",
|
||||
"missingPasswordMessage": "パスワードを入力してください。",
|
||||
"notMatchPasswordMessage": "パスワードが一致していません。",
|
||||
"invalidUserMessage": "無効なユーザーです。",
|
||||
"missingTotpMessage": "オーセンティケーター・コードを入力してください。",
|
||||
"missingTotpDeviceNameMessage": "デバイス名を指定してください。",
|
||||
"invalidPasswordExistingMessage": "既存のパスワードが不正です。",
|
||||
"invalidPasswordConfirmMessage": "新しいパスワード(確認)と一致していません。",
|
||||
"invalidTotpMessage": "無効なオーセンティケーター・コードです。",
|
||||
"usernameExistsMessage": "既に存在するユーザー名です。",
|
||||
"emailExistsMessage": "既に存在するEメールです。",
|
||||
"readOnlyUserMessage": "読み取り専用のため、アカウントを更新することはできません。",
|
||||
"readOnlyUsernameMessage": "読み取り専用のため、ユーザー名を更新することはできません。",
|
||||
"readOnlyPasswordMessage": "読み取り専用のため、パスワードを更新することはできません。",
|
||||
"successTotpMessage": "モバイル・オーセンティケーターが設定されました。",
|
||||
"successTotpRemovedMessage": "モバイル・オーセンティケーターが削除されました。",
|
||||
"successGrantRevokedMessage": "許可が正常に取り消しされました。",
|
||||
"accountUpdatedMessage": "アカウントが更新されました。",
|
||||
"accountPasswordUpdatedMessage": "パスワードが更新されました。",
|
||||
"missingIdentityProviderMessage": "アイデンティティー・プロバイダーが指定されていません。",
|
||||
"invalidFederatedIdentityActionMessage": "無効または存在しないアクションです。",
|
||||
"identityProviderNotFoundMessage": "指定されたアイデンティティー・プロバイダーが見つかりません。",
|
||||
"federatedIdentityLinkNotActiveMessage": "このアイデンティティーは有効ではありません。",
|
||||
"federatedIdentityRemovingLastProviderMessage": "パスワードがないため、最後の連携済みアイデンティティーが削除できません。",
|
||||
"identityProviderRedirectErrorMessage": "アイデンティティー・プロバイダーへのリダイレクトに失敗しました。",
|
||||
"identityProviderRemovedMessage": "アイデンティティー・プロバイダーが正常に削除されました。",
|
||||
"identityProviderAlreadyLinkedMessage": "{0}から返された連携済みアイデンティティーは既に他のユーザーに関連付けされています。",
|
||||
"staleCodeAccountMessage": "有効期限切れです。再度お試しください。",
|
||||
"consentDenied": "同意が拒否されました。",
|
||||
"accountDisabledMessage": "アカウントが無効です。管理者に連絡してください。",
|
||||
"accountTemporarilyDisabledMessage": "アカウントが一時的に無効です。管理者に連絡するか、しばらく時間をおいてから再度お試しください。",
|
||||
"invalidPasswordMinLengthMessage": "無効なパスワード: 最小{0}の長さが必要です。",
|
||||
"invalidPasswordMinLowerCaseCharsMessage": "無効なパスワード: 少なくとも{0}文字の小文字を含む必要があります。",
|
||||
"invalidPasswordMinDigitsMessage": "無効なパスワード: 少なくとも{0}文字の数字を含む必要があります。",
|
||||
"invalidPasswordMinUpperCaseCharsMessage": "無効なパスワード:少なくとも{0}文字の大文字を含む必要があります。",
|
||||
"invalidPasswordMinSpecialCharsMessage": "無効なパスワード: 少なくとも{0}文字の特殊文字を含む必要があります。",
|
||||
"invalidPasswordNotUsernameMessage": "無効なパスワード: ユーザー名と同じパスワードは禁止されています。",
|
||||
"invalidPasswordRegexPatternMessage": "無効なパスワード: 正規表現パターンと一致しません。",
|
||||
"invalidPasswordHistoryMessage": "無効なパスワード: 最近の{0}パスワードのいずれかと同じパスワードは禁止されています。",
|
||||
"invalidPasswordBlacklistedMessage": "無効なパスワード: パスワードがブラックリストに含まれています。",
|
||||
"invalidPasswordGenericMessage": "無効なパスワード: 新しいパスワードはパスワード・ポリシーと一致しません。",
|
||||
"myResources": "マイリソース",
|
||||
"myResourcesSub": "マイリソース",
|
||||
"doDeny": "拒否",
|
||||
"doRevoke": "取り消し",
|
||||
"doApprove": "承認",
|
||||
"doRemoveSharing": "共有の削除",
|
||||
"doRemoveRequest": "要求の削除",
|
||||
"peopleAccessResource": "このリソースにアクセスできる人",
|
||||
"resourceManagedPolicies": "このリソースへのアクセスを許可するパーミッション",
|
||||
"resourceNoPermissionsGrantingAccess": "このリソースへのアクセスを許可する権限はありません",
|
||||
"anyAction": "任意のアクション",
|
||||
"description": "説明",
|
||||
"name": "名前",
|
||||
"scopes": "スコープ",
|
||||
"resource": "リソース",
|
||||
"user": "ユーザー",
|
||||
"peopleSharingThisResource": "このリソースを共有している人",
|
||||
"shareWithOthers": "他人と共有",
|
||||
"needMyApproval": "承認が必要",
|
||||
"requestsWaitingApproval": "承認待ちの要求",
|
||||
"icon": "アイコン",
|
||||
"requestor": "要求者",
|
||||
"owner": "オーナー",
|
||||
"resourcesSharedWithMe": "共有しているリソース",
|
||||
"permissionRequestion": "パーミッションの要求",
|
||||
"permission": "パーミッション",
|
||||
"shares": "共有(複数)",
|
||||
"notBeingShared": "このリソースは共有されていません。",
|
||||
"notHaveAnyResource": "リソースがありません。",
|
||||
"noResourcesSharedWithYou": "共有しているリソースはありません",
|
||||
"havePermissionRequestsWaitingForApproval": "承認を待っている{0}個のパーミッションの要求があります。",
|
||||
"clickHereForDetails": "詳細はこちらをクリックしてください。",
|
||||
"resourceIsNotBeingShared": "リソースは共有されていません。",
|
||||
"locale_ca": "Català",
|
||||
"locale_de": "Deutsch",
|
||||
"locale_en": "English",
|
||||
"locale_es": "Español",
|
||||
"locale_fr": "Français",
|
||||
"locale_it": "Italian",
|
||||
"locale_ja": "日本語",
|
||||
"locale_nl": "Nederlands",
|
||||
"locale_no": "Norsk",
|
||||
"locale_lt": "Lietuvių",
|
||||
"locale_pt-BR": "Português (Brasil)",
|
||||
"locale_ru": "Русский",
|
||||
"locale_sk": "Slovenčina",
|
||||
"locale_sv": "Svenska",
|
||||
"locale_tr": "Turkish",
|
||||
"locale_zh-CN": "中文简体",
|
||||
"applicaitonName": "名前",
|
||||
"applicationType": "アプリケーション・タイプ",
|
||||
"applicationInUse": "使用中のアプリケーションのみ",
|
||||
"clearAllFilter": "すべてのフィルターをクリア",
|
||||
"activeFilters": "アクティブなフィルター",
|
||||
"filterByName": "名前でフィルタリング...",
|
||||
"allApps": "すべてのアプリケーション",
|
||||
"internalApps": "内部アプリケーション",
|
||||
"thirdpartyApps": "サードパーティーのアプリケーション",
|
||||
"appResults": "結果",
|
||||
"clientNotFoundMessage": "クライアントが見つかりません。",
|
||||
"authorizedProvider": "認可済みプロバイダー",
|
||||
"authorizedProviderMessage": "アカウントにリンクされた認可済みプロバイダー",
|
||||
"identityProvider": "アイデンティティー・プロバイダー",
|
||||
"identityProviderMessage": "アカウントと設定したアイデンティティー・プロバイダーをリンクするには",
|
||||
"socialLogin": "ソーシャル・ログイン",
|
||||
"userDefined": "ユーザー定義",
|
||||
"removeAccess": "アクセス権の削除",
|
||||
"removeAccessMessage": "このアプリ・アカウントを使用する場合は、アクセス権を再度付与する必要があります。",
|
||||
"authenticatorStatusMessage": "2要素認証は現在",
|
||||
"authenticatorFinishSetUpTitle": "あなたの2要素認証",
|
||||
"authenticatorFinishSetUpMessage": "Keycloakアカウントにサインインするたびに、2要素認証コードを入力するように求められます。",
|
||||
"authenticatorSubTitle": "2要素認証を設定する",
|
||||
"authenticatorSubMessage": "アカウントのセキュリティーを強化するには、利用可能な2要素認証の方式のうち少なくとも1つを有効にします。",
|
||||
"authenticatorMobileTitle": "モバイル・オーセンティケーター",
|
||||
"authenticatorMobileMessage": "モバイル・オーセンティケーターを使用して、2要素認証として確認コードを取得します。",
|
||||
"authenticatorMobileFinishSetUpMessage": "オーセンティケーターはあなたの携帯電話にバインドされています。",
|
||||
"authenticatorActionSetup": "セットアップ",
|
||||
"authenticatorSMSTitle": "SMSコード",
|
||||
"authenticatorSMSMessage": "Keycloakは、2要素認証として確認コードを携帯電話に送信します。",
|
||||
"authenticatorSMSFinishSetUpMessage": "テキスト・メッセージが次の電話番号宛に送信されます:",
|
||||
"authenticatorDefaultStatus": "デフォルト",
|
||||
"authenticatorChangePhone": "電話番号の変更",
|
||||
"authenticatorBackupCodesTitle": "バックアップ・コード",
|
||||
"authenticatorBackupCodesMessage": "8桁のバックアップ・コードの入手",
|
||||
"authenticatorBackupCodesFinishSetUpMessage": "この時点で12個のバックアップ・コードが生成されました。それぞれ一度だけ使用できます。",
|
||||
"authenticatorMobileSetupTitle": "モバイル・オーセンティケーターのセットアップ",
|
||||
"smscodeIntroMessage": "電話番号を入力すると、確認コードがあなたの電話に送信されます。",
|
||||
"mobileSetupStep1":
|
||||
"携帯電話にオーセンティケーター・アプリケーションをインストールします。ここにリストされているアプリケーションがサポートされています。",
|
||||
"mobileSetupStep2": "アプリケーションを開き、バーコードをスキャンしてください。",
|
||||
"mobileSetupStep3": "アプリケーションから提供されたワンタイムコードを入力し、保存をクリックしてセットアップを終了します。",
|
||||
"scanBarCode": "バーコードをスキャンしますか?",
|
||||
"enterBarCode": "ワンタイムコードを入力してください",
|
||||
"doCopy": "コピー",
|
||||
"doFinish": "終了",
|
||||
"authenticatorSMSCodeSetupTitle": "SMSコードのセットアップ",
|
||||
"chooseYourCountry": "国を選んでください",
|
||||
"enterYourPhoneNumber": "電話番号を入力してください",
|
||||
"sendVerficationCode": "確認コードの送信",
|
||||
"enterYourVerficationCode": "確認コードを入力してください",
|
||||
"authenticatorBackupCodesSetupTitle": "バックアップ・コードのセットアップ",
|
||||
"backupcodesIntroMessage":
|
||||
"携帯電話にアクセスできない場合でも、バックアップ・コードを使用してアカウントにログインできます。どこか安全でアクセス可能な場所に保管してください。",
|
||||
"realmName": "レルム",
|
||||
"doDownload": "ダウンロード",
|
||||
"doPrint": "印刷",
|
||||
"backupCodesTips-1": "各バックアップ・コードは1回使用できます。",
|
||||
"backupCodesTips-2": "これらのコードはこの日に生成されました:",
|
||||
"generateNewBackupCodes": "新しいバックアップ・コードを生成する",
|
||||
"backupCodesTips-3": "新しいバックアップ・コードを生成すると、現在のコードは機能しなくなります。",
|
||||
"backtoAuthenticatorPage": "オーセンティケーター・ページに戻る",
|
||||
"resources": "リソース",
|
||||
"sharedwithMe": "私と共有",
|
||||
"share": "共有",
|
||||
"sharedwith": "共有",
|
||||
"accessPermissions": "アクセス・パーミッション",
|
||||
"permissionRequests": "パーミッションの要求",
|
||||
"approve": "承認",
|
||||
"approveAll": "すべて承認",
|
||||
"people": "人",
|
||||
"perPage": "1ページあたり",
|
||||
"currentPage": "現在のページ",
|
||||
"sharetheResource": "リソースの共有",
|
||||
"group": "グループ",
|
||||
"selectPermission": "パーミッションを選択",
|
||||
"addPeople": "あなたのリソースを共有する人を追加",
|
||||
"addTeam": "あなたのリソースを共有するチームを追加",
|
||||
"myPermissions": "私のパーミッション",
|
||||
"waitingforApproval": "承認待ち",
|
||||
"anyPermission": "任意のパーミッション",
|
||||
"openshift.scope.user_info": "ユーザー情報",
|
||||
"openshift.scope.user_check-access": "ユーザーアクセス情報",
|
||||
"openshift.scope.user_full": "フルアクセス",
|
||||
"openshift.scope.list-projects": "プロジェクトの一覧表示"
|
||||
};
|
||||
|
||||
export default messages;
|
||||
/* spell-checker: enable */
|
143
src/lib/i18n/generated_messages/11.0.3/account/lt.ts
Normal file
143
src/lib/i18n/generated_messages/11.0.3/account/lt.ts
Normal file
@ -0,0 +1,143 @@
|
||||
//This code was automatically generated by running dist/bin/generate-i18n-messages.js
|
||||
//PLEASE DO NOT EDIT MANUALLY
|
||||
|
||||
/* spell-checker: disable */
|
||||
const messages = {
|
||||
"doSave": "Saugoti",
|
||||
"doCancel": "Atšaukti",
|
||||
"doLogOutAllSessions": "Atjungti visas sesijas",
|
||||
"doRemove": "Šalinti",
|
||||
"doAdd": "Pridėti",
|
||||
"doSignOut": "Atsijungti",
|
||||
"editAccountHtmlTitle": "Redaguoti paskyrą",
|
||||
"federatedIdentitiesHtmlTitle": "Susietos paskyros",
|
||||
"accountLogHtmlTitle": "Paskyros žurnalas",
|
||||
"changePasswordHtmlTitle": "Keisti slaptažodį",
|
||||
"sessionsHtmlTitle": "Prisijungimo sesijos",
|
||||
"accountManagementTitle": "Keycloak Naudotojų Administravimas",
|
||||
"authenticatorTitle": "Autentifikatorius",
|
||||
"applicationsHtmlTitle": "Programos",
|
||||
"authenticatorCode": "Vienkartinis kodas",
|
||||
"email": "El. paštas",
|
||||
"firstName": "Vardas",
|
||||
"givenName": "Pavardė",
|
||||
"fullName": "Pilnas vardas",
|
||||
"lastName": "Pavardė",
|
||||
"familyName": "Pavardė",
|
||||
"password": "Slaptažodis",
|
||||
"passwordConfirm": "Pakartotas slaptažodis",
|
||||
"passwordNew": "Naujas slaptažodis",
|
||||
"username": "Naudotojo vardas",
|
||||
"address": "Adresas",
|
||||
"street": "Gatvė",
|
||||
"locality": "Miestas arba vietovė",
|
||||
"region": "Rajonas",
|
||||
"postal_code": "Pašto kodas",
|
||||
"country": "Šalis",
|
||||
"emailVerified": "El. pašto adresas patvirtintas",
|
||||
"gssDelegationCredential": "GSS prisijungimo duomenų delegavimas",
|
||||
"role_admin": "Administratorius",
|
||||
"role_realm-admin": "Srities administravimas",
|
||||
"role_create-realm": "Kurti sritį",
|
||||
"role_view-realm": "Peržiūrėti sritį",
|
||||
"role_view-users": "Peržiūrėti naudotojus",
|
||||
"role_view-applications": "Peržiūrėti programas",
|
||||
"role_view-clients": "Peržiūrėti klientines programas",
|
||||
"role_view-events": "Peržiūrėti įvykių žurnalą",
|
||||
"role_view-identity-providers": "Peržiūrėti tapatybės teikėjus",
|
||||
"role_manage-realm": "Valdyti sritis",
|
||||
"role_manage-users": "Valdyti naudotojus",
|
||||
"role_manage-applications": "Valdyti programas",
|
||||
"role_manage-identity-providers": "Valdyti tapatybės teikėjus",
|
||||
"role_manage-clients": "Valdyti programas",
|
||||
"role_manage-events": "Valdyti įvykius",
|
||||
"role_view-profile": "Peržiūrėti paskyrą",
|
||||
"role_manage-account": "Valdyti paskyrą",
|
||||
"role_read-token": "Skaityti prieigos rakšą",
|
||||
"role_offline-access": "Darbas neprisijungus",
|
||||
"role_uma_authorization": "Įgauti UMA autorizavimo teises",
|
||||
"client_account": "Paskyra",
|
||||
"client_security-admin-console": "Saugumo administravimo konsolė",
|
||||
"client_admin-cli": "Administravimo CLI",
|
||||
"client_realm-management": "Srities valdymas",
|
||||
"client_broker": "Tarpininkas",
|
||||
"requiredFields": "Privalomi laukai",
|
||||
"allFieldsRequired": "Visi laukai yra privalomi",
|
||||
"backToApplication": "« Grįžti į programą",
|
||||
"backTo": "Atgal į {0}",
|
||||
"date": "Data",
|
||||
"event": "Įvykis",
|
||||
"ip": "IP",
|
||||
"client": "Klientas",
|
||||
"clients": "Klientai",
|
||||
"details": "Detaliau",
|
||||
"started": "Sukūrimo laikas",
|
||||
"lastAccess": "Vėliausia prieiga",
|
||||
"expires": "Galioja iki",
|
||||
"applications": "Programos",
|
||||
"account": "Paskyra",
|
||||
"federatedIdentity": "Susieta tapatybė",
|
||||
"authenticator": "Autentifikatorius",
|
||||
"sessions": "Sesijos",
|
||||
"log": "Įvykiai",
|
||||
"application": "Programa",
|
||||
"availablePermissions": "Galimos teisės",
|
||||
"grantedPermissions": "Įgalintos teisės",
|
||||
"grantedPersonalInfo": "Įgalinta asmeninė informacija",
|
||||
"additionalGrants": "Papildomi įgaliojimai",
|
||||
"action": "Veiksmas",
|
||||
"inResource": "yra",
|
||||
"fullAccess": "Pilna prieiga",
|
||||
"offlineToken": "Režimo neprisijungus raktas (token)",
|
||||
"revoke": "Atšaukti įgaliojimą",
|
||||
"configureAuthenticators": "Sukonfigūruotas autentifikatorius",
|
||||
"mobile": "Mobilus",
|
||||
"totpStep1":
|
||||
'Įdiekite <a href="https://freeotp.github.io/" target="_blank">FreeOTP</a> arba Google Authenticator savo įrenginyje. Programėlės prieinamos <a href="https://play.google.com">Google Play</a> ir Apple App Store.',
|
||||
"totpStep2": "Atidarykite programėlę ir nuskenuokite barkodą arba įveskite kodą.",
|
||||
"totpStep3": "Įveskite programėlėje sugeneruotą vieną kartą galiojantį kodą ir paspauskite Saugoti norėdami prisijungti.",
|
||||
"missingUsernameMessage": "Prašome įvesti naudotojo vardą.",
|
||||
"missingFirstNameMessage": "Prašome įvesti vardą.",
|
||||
"invalidEmailMessage": "Neteisingas el. pašto adresas.",
|
||||
"missingLastNameMessage": "Prašome įvesti pavardę.",
|
||||
"missingEmailMessage": "Prašome įvesti el. pašto adresą.",
|
||||
"missingPasswordMessage": "Prašome įvesti slaptažodį.",
|
||||
"notMatchPasswordMessage": "Slaptažodžiai nesutampa.",
|
||||
"missingTotpMessage": "Prašome įvesti autentifikacijos kodą.",
|
||||
"invalidPasswordExistingMessage": "Neteisingas dabartinis slaptažodis.",
|
||||
"invalidPasswordConfirmMessage": "Pakartotas slaptažodis nesutampa.",
|
||||
"invalidTotpMessage": "Neteisingas autentifikacijos kodas.",
|
||||
"usernameExistsMessage": "Toks naudotojas jau egzistuoja.",
|
||||
"emailExistsMessage": "El. pašto adresas jau egzistuoja.",
|
||||
"readOnlyUserMessage": "Tik skaitymui sukonfigūruotos paskyros duomenų atnaujinti neleidžiama.",
|
||||
"readOnlyPasswordMessage": "Tik skaitymui sukonfigūruotos paskyros slaptažodžio atnaujinti neleidžiama.",
|
||||
"successTotpMessage": "Mobilus autentifikatorius sukonfigūruotas.",
|
||||
"successTotpRemovedMessage": "Mobilus autentifikatorius pašalintas.",
|
||||
"successGrantRevokedMessage": "Įgalinimas pašalintas sėkmingai.",
|
||||
"accountUpdatedMessage": "Jūsų paskyros duomenys sėkmingai atnaujinti.",
|
||||
"accountPasswordUpdatedMessage": "Jūsų paskyros slaptažodis pakeistas.",
|
||||
"missingIdentityProviderMessage": "Nenurodytas tapatybės teikėjas.",
|
||||
"invalidFederatedIdentityActionMessage": "Neteisingas arba nežinomas veiksmas.",
|
||||
"identityProviderNotFoundMessage": "Nurodytas tapatybės teikėjas nerastas.",
|
||||
"federatedIdentityLinkNotActiveMessage": "Nurodyta susieta tapatybė neaktyvi.",
|
||||
"federatedIdentityRemovingLastProviderMessage":
|
||||
"Jūs negalite pašalinti paskutinio tapatybės teikėjo sąsajos, nes Jūs neturite nusistatę paskyros slaptažodžio.",
|
||||
"identityProviderRedirectErrorMessage": "Klaida nukreipiant į tapatybės teikėjo puslapį.",
|
||||
"identityProviderRemovedMessage": "Tapatybės teikėjas sėkmingai pašalintas.",
|
||||
"identityProviderAlreadyLinkedMessage": "Susieta tapatybė iš {0} jau susieta su kita paskyra.",
|
||||
"staleCodeAccountMessage": "Puslapio galiojimas baigėsi. Bandykite dar kartą.",
|
||||
"consentDenied": "Prieiga draudžiama.",
|
||||
"accountDisabledMessage": "Paskyros galiojimas sustabdytas, kreipkitės į administratorių.",
|
||||
"accountTemporarilyDisabledMessage": "Paskyros galiojimas laikinai sustabdytas. Kreipkitės į administratorių arba pabandykite vėliau.",
|
||||
"invalidPasswordMinLengthMessage": "Per trumpas slaptažodis: mažiausias ilgis {0}.",
|
||||
"invalidPasswordMinLowerCaseCharsMessage": "Neteisingas slaptažodis: privaloma įvesti {0} mažąją raidę.",
|
||||
"invalidPasswordMinDigitsMessage": "Neteisingas slaptažodis: privaloma įvesti {0} skaitmenį.",
|
||||
"invalidPasswordMinUpperCaseCharsMessage": "Neteisingas slaptažodis: privaloma įvesti {0} didžiąją raidę.",
|
||||
"invalidPasswordMinSpecialCharsMessage": "Neteisingas slaptažodis: privaloma įvesti {0} specialų simbolį.",
|
||||
"invalidPasswordNotUsernameMessage": "Neteisingas slaptažodis: slaptažodis negali sutapti su naudotojo vardu.",
|
||||
"invalidPasswordRegexPatternMessage": "Neteisingas slaptažodis: slaptažodis netenkina regex taisyklės(ių).",
|
||||
"invalidPasswordHistoryMessage": "Neteisingas slaptažodis: slaptažodis negali sutapti su prieš tai buvusiais {0} slaptažodžiais."
|
||||
};
|
||||
|
||||
export default messages;
|
||||
/* spell-checker: enable */
|
143
src/lib/i18n/generated_messages/11.0.3/account/nl.ts
Normal file
143
src/lib/i18n/generated_messages/11.0.3/account/nl.ts
Normal file
@ -0,0 +1,143 @@
|
||||
//This code was automatically generated by running dist/bin/generate-i18n-messages.js
|
||||
//PLEASE DO NOT EDIT MANUALLY
|
||||
|
||||
/* spell-checker: disable */
|
||||
const messages = {
|
||||
"doSave": "Opslaan",
|
||||
"doCancel": "Annuleer",
|
||||
"doLogOutAllSessions": "Alle sessies uitloggen",
|
||||
"doRemove": "Verwijder",
|
||||
"doAdd": "Voeg toe",
|
||||
"doSignOut": "Afmelden",
|
||||
"editAccountHtmlTitle": "Bewerk account",
|
||||
"federatedIdentitiesHtmlTitle": "Federated Identities",
|
||||
"accountLogHtmlTitle": "Account log",
|
||||
"changePasswordHtmlTitle": "Verander wachtwoord",
|
||||
"sessionsHtmlTitle": "Sessies",
|
||||
"accountManagementTitle": "Keycloak Accountbeheer",
|
||||
"authenticatorTitle": "Authenticator",
|
||||
"applicationsHtmlTitle": "Toepassingen",
|
||||
"authenticatorCode": "Eenmalige code",
|
||||
"email": "E-mailadres",
|
||||
"firstName": "Voornaam",
|
||||
"givenName": "Voornaam",
|
||||
"fullName": "Volledige naam",
|
||||
"lastName": "Achternaam",
|
||||
"familyName": "Achternaam",
|
||||
"password": "Wachtwoord",
|
||||
"passwordConfirm": "Bevestiging",
|
||||
"passwordNew": "Nieuw Wachtwoord",
|
||||
"username": "Gebruikersnaam",
|
||||
"address": "Adres",
|
||||
"street": "Straat",
|
||||
"locality": "Stad of plaats",
|
||||
"region": "Staat, provincie of regio",
|
||||
"postal_code": "Postcode",
|
||||
"country": "Land",
|
||||
"emailVerified": "E-mailadres geverifieerd",
|
||||
"gssDelegationCredential": "GSS gedelegeerde aanmeldgegevens",
|
||||
"role_admin": "Beheer",
|
||||
"role_realm-admin": "Realmbeheer",
|
||||
"role_create-realm": "Creëer realm",
|
||||
"role_view-realm": "Bekijk realm",
|
||||
"role_view-users": "Bekijk gebruikers",
|
||||
"role_view-applications": "Bekijk toepassingen",
|
||||
"role_view-clients": "Bekijk clients",
|
||||
"role_view-events": "Bekijk gebeurtenissen",
|
||||
"role_view-identity-providers": "Bekijk identity providers",
|
||||
"role_manage-realm": "Beheer realm",
|
||||
"role_manage-users": "Beheer gebruikers",
|
||||
"role_manage-applications": "Beheer toepassingen",
|
||||
"role_manage-identity-providers": "Beheer identity providers",
|
||||
"role_manage-clients": "Beheer clients",
|
||||
"role_manage-events": "Beheer gebeurtenissen",
|
||||
"role_view-profile": "Bekijk profiel",
|
||||
"role_manage-account": "Beheer account",
|
||||
"role_manage-account-links": "Beheer accountkoppelingen",
|
||||
"role_read-token": "Lees token",
|
||||
"role_offline-access": "Offline toegang",
|
||||
"role_uma_authorization": "Verkrijg UMA rechten",
|
||||
"client_account": "Account",
|
||||
"client_security-admin-console": "Console Veligheidsbeheer",
|
||||
"client_admin-cli": "Beheer CLI",
|
||||
"client_realm-management": "Realmbeheer",
|
||||
"client_broker": "Broker",
|
||||
"requiredFields": "Verplichte velden",
|
||||
"allFieldsRequired": "Alle velden verplicht",
|
||||
"backToApplication": "« Terug naar toepassing",
|
||||
"backTo": "Terug naar {0}",
|
||||
"date": "Datum",
|
||||
"event": "Gebeurtenis",
|
||||
"ip": "IP",
|
||||
"client": "Client",
|
||||
"clients": "Clients",
|
||||
"details": "Details",
|
||||
"started": "Gestart",
|
||||
"lastAccess": "Laatste toegang",
|
||||
"expires": "Vervalt",
|
||||
"applications": "Toepassingen",
|
||||
"account": "Account",
|
||||
"federatedIdentity": "Federated Identity",
|
||||
"authenticator": "Authenticator",
|
||||
"sessions": "Sessies",
|
||||
"log": "Log",
|
||||
"application": "Toepassing",
|
||||
"availablePermissions": "Beschikbare rechten",
|
||||
"grantedPermissions": "Gegunde rechten",
|
||||
"grantedPersonalInfo": "Gegunde Persoonsgegevens",
|
||||
"additionalGrants": "Verdere vergunningen",
|
||||
"action": "Actie",
|
||||
"inResource": "in",
|
||||
"fullAccess": "Volledige toegang",
|
||||
"offlineToken": "Offline Token",
|
||||
"revoke": "Vergunning intrekken",
|
||||
"configureAuthenticators": "Ingestelde authenticators",
|
||||
"mobile": "Mobiel nummer",
|
||||
"totpStep1": "Installeer een van de onderstaande applicaties op uw mobiele apparaat:",
|
||||
"totpStep2": "Open de toepassing en scan de QR-code of voer de sleutel in.",
|
||||
"totpStep3": "Voer de door de toepassing gegeven eenmalige code in en klik op Opslaan om de configuratie af te ronden.",
|
||||
"missingUsernameMessage": "Gebruikersnaam ontbreekt.",
|
||||
"missingFirstNameMessage": "Voornaam onbreekt.",
|
||||
"invalidEmailMessage": "Ongeldig e-mailadres.",
|
||||
"missingLastNameMessage": "Achternaam ontbreekt.",
|
||||
"missingEmailMessage": "E-mailadres ontbreekt.",
|
||||
"missingPasswordMessage": "Wachtwoord ontbreekt.",
|
||||
"notMatchPasswordMessage": "Wachtwoorden komen niet overeen.",
|
||||
"missingTotpMessage": "Authenticatiecode ontbreekt.",
|
||||
"invalidPasswordExistingMessage": "Ongeldig bestaand wachtwoord.",
|
||||
"invalidPasswordConfirmMessage": "Wachtwoordbevestiging komt niet overeen.",
|
||||
"invalidTotpMessage": "Ongeldige authenticatiecode.",
|
||||
"emailExistsMessage": "E-mailadres bestaat reeds.",
|
||||
"readOnlyUserMessage": "U kunt uw account niet bijwerken aangezien het account alleen-lezen is.",
|
||||
"readOnlyPasswordMessage": "U kunt uw wachtwoord niet wijzigen omdat uw account alleen-lezen is.",
|
||||
"successTotpMessage": "Mobiele authenticator geconfigureerd.",
|
||||
"successTotpRemovedMessage": "Mobiele authenticator verwijderd.",
|
||||
"successGrantRevokedMessage": "Vergunning succesvol ingetrokken",
|
||||
"accountUpdatedMessage": "Uw account is gewijzigd.",
|
||||
"accountPasswordUpdatedMessage": "Uw wachtwoord is gewijzigd.",
|
||||
"missingIdentityProviderMessage": "Geen identity provider aangegeven.",
|
||||
"invalidFederatedIdentityActionMessage": "Ongeldige of ontbrekende actie op federated identity.",
|
||||
"identityProviderNotFoundMessage": "Gespecificeerde identity provider niet gevonden.",
|
||||
"federatedIdentityLinkNotActiveMessage": "Deze federated identity is niet langer geldig.",
|
||||
"federatedIdentityRemovingLastProviderMessage":
|
||||
"U kunt de laatste federated identity provider niet verwijderen aangezien u dan niet langer zou kunnen inloggen.",
|
||||
"identityProviderRedirectErrorMessage": "Kon niet herverwijzen naar identity provider.",
|
||||
"identityProviderRemovedMessage": "Identity provider met succes verwijderd.",
|
||||
"identityProviderAlreadyLinkedMessage": "Door {0} teruggegeven federated identity is al gekoppeld aan een andere gebruiker.",
|
||||
"staleCodeAccountMessage": "De pagina is verlopen. Probeer het nogmaals.",
|
||||
"consentDenied": "Toestemming geweigerd",
|
||||
"accountDisabledMessage": "Account is gedeactiveerd. Contacteer de beheerder.",
|
||||
"accountTemporarilyDisabledMessage": "Account is tijdelijk deactiveerd, neem contact op met de beheerder of probeer het later opnieuw.",
|
||||
"invalidPasswordMinLengthMessage": "Ongeldig wachtwoord: de minimale lengte is {0} karakters.",
|
||||
"invalidPasswordMinLowerCaseCharsMessage": "Ongeldig wachtwoord: het moet minstens {0} kleine letters bevatten.",
|
||||
"invalidPasswordMinDigitsMessage": "Ongeldig wachtwoord: het moet minstens {0} getallen bevatten.",
|
||||
"invalidPasswordMinUpperCaseCharsMessage": "Ongeldig wachtwoord: het moet minstens {0} hoofdletters bevatten.",
|
||||
"invalidPasswordMinSpecialCharsMessage": "Ongeldig wachtwoord: het moet minstens {0} speciale karakters bevatten.",
|
||||
"invalidPasswordNotUsernameMessage": "Ongeldig wachtwoord: het mag niet overeenkomen met de gebruikersnaam.",
|
||||
"invalidPasswordRegexPatternMessage": "Ongeldig wachtwoord: het voldoet niet aan het door de beheerder ingestelde patroon.",
|
||||
"invalidPasswordHistoryMessage": "Ongeldig wachtwoord: het mag niet overeen komen met een van de laatste {0} wachtwoorden.",
|
||||
"invalidPasswordGenericMessage": "Ongeldig wachtwoord: het nieuwe wachtwoord voldoet niet aan het wachtwoordbeleid."
|
||||
};
|
||||
|
||||
export default messages;
|
||||
/* spell-checker: enable */
|
153
src/lib/i18n/generated_messages/11.0.3/account/no.ts
Normal file
153
src/lib/i18n/generated_messages/11.0.3/account/no.ts
Normal file
@ -0,0 +1,153 @@
|
||||
//This code was automatically generated by running dist/bin/generate-i18n-messages.js
|
||||
//PLEASE DO NOT EDIT MANUALLY
|
||||
|
||||
/* spell-checker: disable */
|
||||
const messages = {
|
||||
"doSave": "Lagre",
|
||||
"doCancel": "Avbryt",
|
||||
"doLogOutAllSessions": "Logg ut av alle sesjoner",
|
||||
"doRemove": "Fjern",
|
||||
"doAdd": "Legg til",
|
||||
"doSignOut": "Logg ut",
|
||||
"editAccountHtmlTitle": "Rediger konto",
|
||||
"federatedIdentitiesHtmlTitle": "Federerte identiteter",
|
||||
"accountLogHtmlTitle": "Kontologg",
|
||||
"changePasswordHtmlTitle": "Endre passord",
|
||||
"sessionsHtmlTitle": "Sesjoner",
|
||||
"accountManagementTitle": "Keycloak kontoadministrasjon",
|
||||
"authenticatorTitle": "Autentikator",
|
||||
"applicationsHtmlTitle": "Applikasjoner",
|
||||
"authenticatorCode": "Engangskode",
|
||||
"email": "E-post",
|
||||
"firstName": "Fornavn",
|
||||
"givenName": "Fornavn",
|
||||
"fullName": "Fullt navn",
|
||||
"lastName": "Etternavn",
|
||||
"familyName": "Etternavn",
|
||||
"password": "Passord",
|
||||
"passwordConfirm": "Bekreftelse",
|
||||
"passwordNew": "Nytt passord",
|
||||
"username": "Brukernavn",
|
||||
"address": "Adresse",
|
||||
"street": "Gate-/veinavn + husnummer",
|
||||
"locality": "By",
|
||||
"region": "Fylke",
|
||||
"postal_code": "Postnummer",
|
||||
"country": "Land",
|
||||
"emailVerified": "E-post bekreftet",
|
||||
"gssDelegationCredential": "GSS legitimasjonsdelegering",
|
||||
"role_admin": "Administrator",
|
||||
"role_realm-admin": "Administrator for sikkerhetsdomene",
|
||||
"role_create-realm": "Opprette sikkerhetsdomene",
|
||||
"role_view-realm": "Se sikkerhetsdomene",
|
||||
"role_view-users": "Se brukere",
|
||||
"role_view-applications": "Se applikasjoner",
|
||||
"role_view-clients": "Se klienter",
|
||||
"role_view-events": "Se hendelser",
|
||||
"role_view-identity-providers": "Se identitetsleverandører",
|
||||
"role_manage-realm": "Administrere sikkerhetsdomene",
|
||||
"role_manage-users": "Administrere brukere",
|
||||
"role_manage-applications": "Administrere applikasjoner",
|
||||
"role_manage-identity-providers": "Administrere identitetsleverandører",
|
||||
"role_manage-clients": "Administrere klienter",
|
||||
"role_manage-events": "Administrere hendelser",
|
||||
"role_view-profile": "Se profil",
|
||||
"role_manage-account": "Administrere konto",
|
||||
"role_read-token": "Lese token",
|
||||
"role_offline-access": "Frakoblet tilgang",
|
||||
"role_uma_authorization": "Skaffe tillatelser",
|
||||
"client_account": "Konto",
|
||||
"client_security-admin-console": "Sikkerhetsadministrasjonskonsoll",
|
||||
"client_admin-cli": "Kommandolinje-grensesnitt for administrator",
|
||||
"client_realm-management": "Sikkerhetsdomene-administrasjon",
|
||||
"client_broker": "Broker",
|
||||
"requiredFields": "Obligatoriske felt",
|
||||
"allFieldsRequired": "Alle felt må fylles ut",
|
||||
"backToApplication": "« Tilbake til applikasjonen",
|
||||
"backTo": "Tilbake til {0}",
|
||||
"date": "Dato",
|
||||
"event": "Hendelse",
|
||||
"ip": "IP",
|
||||
"client": "Klient",
|
||||
"clients": "Klienter",
|
||||
"details": "Detaljer",
|
||||
"started": "Startet",
|
||||
"lastAccess": "Sist benyttet",
|
||||
"expires": "Utløper",
|
||||
"applications": "Applikasjoner",
|
||||
"account": "Konto",
|
||||
"federatedIdentity": "Federert identitet",
|
||||
"authenticator": "Autentikator",
|
||||
"sessions": "Sesjoner",
|
||||
"log": "Logg",
|
||||
"application": "Applikasjon",
|
||||
"availablePermissions": "Tilgjengelige rettigheter",
|
||||
"grantedPermissions": "Innvilgede rettigheter",
|
||||
"grantedPersonalInfo": "Innvilget personlig informasjon",
|
||||
"additionalGrants": "Ekstra rettigheter",
|
||||
"action": "Handling",
|
||||
"inResource": "i",
|
||||
"fullAccess": "Full tilgang",
|
||||
"offlineToken": "Offline token",
|
||||
"revoke": "Opphev rettighet",
|
||||
"configureAuthenticators": "Konfigurerte autentikatorer",
|
||||
"mobile": "Mobiltelefon",
|
||||
"totpStep1": "Installer ett av følgende programmer på mobilen din.",
|
||||
"totpStep2": "Åpne applikasjonen og skann strekkoden eller skriv inn koden.",
|
||||
"totpStep3": "Skriv inn engangskoden gitt av applikasjonen og klikk Lagre for å fullføre.",
|
||||
"missingUsernameMessage": "Vennligst oppgi brukernavn.",
|
||||
"missingFirstNameMessage": "Vennligst oppgi fornavn.",
|
||||
"invalidEmailMessage": "Ugyldig e-postadresse.",
|
||||
"missingLastNameMessage": "Vennligst oppgi etternavn.",
|
||||
"missingEmailMessage": "Vennligst oppgi e-postadresse.",
|
||||
"missingPasswordMessage": "Vennligst oppgi passord.",
|
||||
"notMatchPasswordMessage": "Passordene er ikke like.",
|
||||
"missingTotpMessage": "Vennligst oppgi engangskode.",
|
||||
"invalidPasswordExistingMessage": "Ugyldig eksisterende passord.",
|
||||
"invalidPasswordConfirmMessage": "Passordene er ikke like.",
|
||||
"invalidTotpMessage": "Ugyldig engangskode.",
|
||||
"usernameExistsMessage": "Brukernavnet finnes allerede.",
|
||||
"emailExistsMessage": "E-postadressen finnes allerede.",
|
||||
"readOnlyUserMessage": "Du kan ikke oppdatere kontoen din ettersom den er skrivebeskyttet.",
|
||||
"readOnlyPasswordMessage": "Du kan ikke oppdatere passordet ditt ettersom kontoen din er skrivebeskyttet.",
|
||||
"successTotpMessage": "Autentikator for mobiltelefon er konfigurert.",
|
||||
"successTotpRemovedMessage": "Autentikator for mobiltelefon er fjernet.",
|
||||
"successGrantRevokedMessage": "Vellykket oppheving av rettighet.",
|
||||
"accountUpdatedMessage": "Kontoen din har blitt oppdatert.",
|
||||
"accountPasswordUpdatedMessage": "Ditt passord har blitt oppdatert.",
|
||||
"missingIdentityProviderMessage": "Identitetsleverandør er ikke spesifisert.",
|
||||
"invalidFederatedIdentityActionMessage": "Ugyldig eller manglende handling.",
|
||||
"identityProviderNotFoundMessage": "Spesifisert identitetsleverandør ikke funnet.",
|
||||
"federatedIdentityLinkNotActiveMessage": "Denne identiteten er ikke lenger aktiv.",
|
||||
"federatedIdentityRemovingLastProviderMessage": "Du kan ikke fjerne siste federerte identitet ettersom du ikke har et passord.",
|
||||
"identityProviderRedirectErrorMessage": "Redirect til identitetsleverandør feilet.",
|
||||
"identityProviderRemovedMessage": "Fjerning av identitetsleverandør var vellykket.",
|
||||
"identityProviderAlreadyLinkedMessage": "Federert identitet returnert av {0} er allerede koblet til en annen bruker.",
|
||||
"staleCodeAccountMessage": "Siden har utløpt. Vennligst prøv en gang til.",
|
||||
"consentDenied": "Samtykke avslått.",
|
||||
"accountDisabledMessage": "Konto er deaktivert, kontakt administrator.",
|
||||
"accountTemporarilyDisabledMessage": "Konto er midlertidig deaktivert, kontakt administrator eller prøv igjen senere.",
|
||||
"invalidPasswordMinLengthMessage": "Ugyldig passord: minimum lengde {0}.",
|
||||
"invalidPasswordMinLowerCaseCharsMessage": "Ugyldig passord: må inneholde minimum {0} små bokstaver.",
|
||||
"invalidPasswordMinDigitsMessage": "Ugyldig passord: må inneholde minimum {0} sifre.",
|
||||
"invalidPasswordMinUpperCaseCharsMessage": "Ugyldig passord: må inneholde minimum {0} store bokstaver.",
|
||||
"invalidPasswordMinSpecialCharsMessage": "Ugyldig passord: må inneholde minimum {0} spesialtegn.",
|
||||
"invalidPasswordNotUsernameMessage": "Ugyldig passord: kan ikke være likt brukernavn.",
|
||||
"invalidPasswordRegexPatternMessage": "Ugyldig passord: tilfredsstiller ikke kravene for passord-mønster.",
|
||||
"invalidPasswordHistoryMessage": "Ugyldig passord: kan ikke være likt noen av de {0} foregående passordene.",
|
||||
"locale_ca": "Català",
|
||||
"locale_de": "Deutsch",
|
||||
"locale_en": "English",
|
||||
"locale_es": "Español",
|
||||
"locale_fr": "Français",
|
||||
"locale_it": "Italian",
|
||||
"locale_ja": "日本語",
|
||||
"locale_no": "Norsk",
|
||||
"locale_nl": "Nederlands",
|
||||
"locale_pt-BR": "Português (Brasil)",
|
||||
"locale_ru": "Русский",
|
||||
"locale_zh-CN": "中文简体"
|
||||
};
|
||||
|
||||
export default messages;
|
||||
/* spell-checker: enable */
|
8
src/lib/i18n/generated_messages/11.0.3/account/pl.ts
Normal file
8
src/lib/i18n/generated_messages/11.0.3/account/pl.ts
Normal file
@ -0,0 +1,8 @@
|
||||
//This code was automatically generated by running dist/bin/generate-i18n-messages.js
|
||||
//PLEASE DO NOT EDIT MANUALLY
|
||||
|
||||
/* spell-checker: disable */
|
||||
const messages = {};
|
||||
|
||||
export default messages;
|
||||
/* spell-checker: enable */
|
140
src/lib/i18n/generated_messages/11.0.3/account/pt-BR.ts
Normal file
140
src/lib/i18n/generated_messages/11.0.3/account/pt-BR.ts
Normal file
@ -0,0 +1,140 @@
|
||||
//This code was automatically generated by running dist/bin/generate-i18n-messages.js
|
||||
//PLEASE DO NOT EDIT MANUALLY
|
||||
|
||||
/* spell-checker: disable */
|
||||
const messages = {
|
||||
"doSave": "Salvar",
|
||||
"doCancel": "Cancelar",
|
||||
"doLogOutAllSessions": "Sair de todas as sessões",
|
||||
"doRemove": "Remover",
|
||||
"doAdd": "Adicionar",
|
||||
"doSignOut": "Sair",
|
||||
"editAccountHtmlTitle": "Editar Conta",
|
||||
"federatedIdentitiesHtmlTitle": "Identidades Federadas",
|
||||
"accountLogHtmlTitle": "Log da conta",
|
||||
"changePasswordHtmlTitle": "Alterar senha",
|
||||
"sessionsHtmlTitle": "Sessões",
|
||||
"accountManagementTitle": "Gerenciamento de Conta",
|
||||
"authenticatorTitle": "Autenticator",
|
||||
"applicationsHtmlTitle": "Aplicativos",
|
||||
"authenticatorCode": "Código autenticador",
|
||||
"email": "E-mail",
|
||||
"firstName": "Primeiro nome",
|
||||
"givenName": "Primeiro nome",
|
||||
"fullName": "Nome completo",
|
||||
"lastName": "Sobrenome",
|
||||
"familyName": "Sobrenome",
|
||||
"password": "Senha",
|
||||
"passwordConfirm": "Confirmação",
|
||||
"passwordNew": "Nova senha",
|
||||
"username": "Nome de usúario",
|
||||
"address": "Endereço",
|
||||
"street": "Logradouro",
|
||||
"locality": "Cidade ou Localidade",
|
||||
"region": "Estado",
|
||||
"postal_code": "CEP",
|
||||
"country": "País",
|
||||
"emailVerified": "E-mail verificado",
|
||||
"gssDelegationCredential": "GSS Delegação de Credencial",
|
||||
"role_admin": "Admin",
|
||||
"role_realm-admin": "Realm Admin",
|
||||
"role_create-realm": "Cria realm",
|
||||
"role_view-realm": "Visualiza realm",
|
||||
"role_view-users": "Visualiza usuários",
|
||||
"role_view-applications": "Visualiza aplicações",
|
||||
"role_view-clients": "Visualiza clientes",
|
||||
"role_view-events": "Visualiza eventos",
|
||||
"role_view-identity-providers": "Visualiza provedores de identidade",
|
||||
"role_manage-realm": "Gerencia realm",
|
||||
"role_manage-users": "Gerencia usuários",
|
||||
"role_manage-applications": "Gerencia aplicações",
|
||||
"role_manage-identity-providers": "Gerencia provedores de identidade",
|
||||
"role_manage-clients": "Gerencia clientes",
|
||||
"role_manage-events": "Gerencia eventos",
|
||||
"role_view-profile": "Visualiza perfil",
|
||||
"role_manage-account": "Gerencia conta",
|
||||
"role_read-token": "Lê token",
|
||||
"role_offline-access": "Acesso Offline",
|
||||
"role_uma_authorization": "Obter permissões",
|
||||
"client_account": "Conta",
|
||||
"client_security-admin-console": "Console de Administração de Segurança",
|
||||
"client_admin-cli": "Admin CLI",
|
||||
"client_realm-management": "Gerenciamento de Realm",
|
||||
"client_broker": "Broker",
|
||||
"requiredFields": "Campos obrigatórios",
|
||||
"allFieldsRequired": "Todos os campos são obrigatórios",
|
||||
"backToApplication": "« Voltar para aplicação",
|
||||
"backTo": "Voltar para {0}",
|
||||
"date": "Data",
|
||||
"event": "Evento",
|
||||
"ip": "IP",
|
||||
"client": "Cliente",
|
||||
"clients": "Clientes",
|
||||
"details": "Detalhes",
|
||||
"started": "Iniciado",
|
||||
"lastAccess": "Último acesso",
|
||||
"expires": "Expira",
|
||||
"applications": "Aplicativos",
|
||||
"account": "Conta",
|
||||
"federatedIdentity": "Identidade Federada",
|
||||
"authenticator": "Autenticador",
|
||||
"sessions": "Sessões",
|
||||
"log": "Log",
|
||||
"application": "Aplicativo",
|
||||
"availablePermissions": "Permissões Disponíveis",
|
||||
"grantedPermissions": "Permissões Concedidas",
|
||||
"grantedPersonalInfo": "Informações Pessoais Concedidas",
|
||||
"additionalGrants": "Concessões Adicionais",
|
||||
"action": "Ação",
|
||||
"inResource": "em",
|
||||
"fullAccess": "Acesso Completo",
|
||||
"offlineToken": "Offline Token",
|
||||
"revoke": "Revogar Concessões",
|
||||
"configureAuthenticators": "Autenticadores Configurados",
|
||||
"mobile": "Mobile",
|
||||
"totpStep1":
|
||||
'Instalar <a href="https://freeotp.github.io/" target="_blank">FreeOTP</a> ou Google Authenticator em seu dispositivo. Ambas aplicações estão disponíveis no <a href="https://play.google.com">Google Play</a> e na Apple App Store.',
|
||||
"totpStep2": "Abra o aplicativo e escaneie o código de barras ou entre com o código.",
|
||||
"totpStep3": "Digite o código fornecido pelo aplicativo e clique em Salvar para concluir a configuração.",
|
||||
"missingUsernameMessage": "Por favor, especifique o nome de usuário.",
|
||||
"missingFirstNameMessage": "Por favor, informe o primeiro nome.",
|
||||
"invalidEmailMessage": "E-mail inválido.",
|
||||
"missingLastNameMessage": "Por favor, informe o sobrenome.",
|
||||
"missingEmailMessage": "Por favor, informe o e-mail.",
|
||||
"missingPasswordMessage": "Por favor, informe a senha.",
|
||||
"notMatchPasswordMessage": "As senhas não coincidem.",
|
||||
"missingTotpMessage": "Por favor, informe o código autenticador.",
|
||||
"invalidPasswordExistingMessage": "Senha atual inválida.",
|
||||
"invalidPasswordConfirmMessage": "A senha de confirmação não coincide.",
|
||||
"invalidTotpMessage": "Código autenticador inválido.",
|
||||
"usernameExistsMessage": "Este nome de usuário já existe.",
|
||||
"emailExistsMessage": "Este e-mail já existe.",
|
||||
"readOnlyUserMessage": "Você não pode atualizar sua conta, uma vez que é apenas de leitura",
|
||||
"readOnlyPasswordMessage": "Você não pode atualizar sua senha, sua conta é somente leitura",
|
||||
"successTotpMessage": "Autenticador mobile configurado.",
|
||||
"successTotpRemovedMessage": "Autenticador mobile removido.",
|
||||
"successGrantRevokedMessage": "Concessões revogadas com sucesso.",
|
||||
"accountUpdatedMessage": "Sua conta foi atualizada",
|
||||
"accountPasswordUpdatedMessage": "Sua senha foi atualizada",
|
||||
"missingIdentityProviderMessage": "Provedor de identidade não especificado",
|
||||
"invalidFederatedIdentityActionMessage": "Ação inválida ou ausente",
|
||||
"identityProviderNotFoundMessage": "O provedor de identidade especificado não foi encontrado",
|
||||
"federatedIdentityLinkNotActiveMessage": "Esta identidade não está mais em atividade",
|
||||
"federatedIdentityRemovingLastProviderMessage": "Você não pode remover a última identidade federada como você não tem senha",
|
||||
"identityProviderRedirectErrorMessage": "Falha ao redirecionar para o provedor de identidade",
|
||||
"identityProviderRemovedMessage": "Provedor de identidade removido com sucesso",
|
||||
"identityProviderAlreadyLinkedMessage": "Identidade federada retornado por {0} já está ligado a outro usuário.",
|
||||
"accountDisabledMessage": "Conta desativada, contate o administrador",
|
||||
"accountTemporarilyDisabledMessage": "A conta está temporariamente indisponível, contate o administrador ou tente novamente mais tarde",
|
||||
"invalidPasswordMinLengthMessage": "Senha inválida: comprimento mínimo {0}",
|
||||
"invalidPasswordMinLowerCaseCharsMessage": "Senha inválida: deve conter pelo menos {0} caractere(s) minúsculo",
|
||||
"invalidPasswordMinDigitsMessage": "Senha inválida: deve conter pelo menos {0} número(s)",
|
||||
"invalidPasswordMinUpperCaseCharsMessage": "Senha inválida: deve conter pelo menos {0} caractere(s) maiúsculo",
|
||||
"invalidPasswordMinSpecialCharsMessage": "Senha inválida: deve conter pelo menos {0} caractere(s) especial",
|
||||
"invalidPasswordNotUsernameMessage": "Senha inválida: não deve ser igual ao nome de usuário",
|
||||
"invalidPasswordRegexPatternMessage": "Senha inválida: não corresponde ao padrão da expressão regular.",
|
||||
"invalidPasswordHistoryMessage": "Senha inválida: não pode ser igual a qualquer uma das {0} últimas senhas."
|
||||
};
|
||||
|
||||
export default messages;
|
||||
/* spell-checker: enable */
|
143
src/lib/i18n/generated_messages/11.0.3/account/ru.ts
Normal file
143
src/lib/i18n/generated_messages/11.0.3/account/ru.ts
Normal file
@ -0,0 +1,143 @@
|
||||
//This code was automatically generated by running dist/bin/generate-i18n-messages.js
|
||||
//PLEASE DO NOT EDIT MANUALLY
|
||||
|
||||
/* spell-checker: disable */
|
||||
const messages = {
|
||||
"doSave": "Сохранить",
|
||||
"doCancel": "Отмена",
|
||||
"doLogOutAllSessions": "Выйти из всех сессий",
|
||||
"doRemove": "Удалить",
|
||||
"doAdd": "Добавить",
|
||||
"doSignOut": "Выход",
|
||||
"editAccountHtmlTitle": "Изменение учетной записи",
|
||||
"federatedIdentitiesHtmlTitle": "Федеративные идентификаторы",
|
||||
"accountLogHtmlTitle": "Лог учетной записи",
|
||||
"changePasswordHtmlTitle": "Смена пароля",
|
||||
"sessionsHtmlTitle": "Сессии",
|
||||
"accountManagementTitle": "Управление учетной записью",
|
||||
"authenticatorTitle": "Аутентификатор",
|
||||
"applicationsHtmlTitle": "Приложения",
|
||||
"authenticatorCode": "Одноразовый код",
|
||||
"email": "E-mail",
|
||||
"firstName": "Имя",
|
||||
"givenName": "Имя",
|
||||
"fullName": "Полное имя",
|
||||
"lastName": "Фамилия",
|
||||
"familyName": "Фамилия",
|
||||
"password": "Пароль",
|
||||
"passwordConfirm": "Подтверждение пароля",
|
||||
"passwordNew": "Новый пароль",
|
||||
"username": "Имя пользователя",
|
||||
"address": "Адрес",
|
||||
"street": "Улица",
|
||||
"locality": "Город",
|
||||
"region": "Регион",
|
||||
"postal_code": "Почтовый индекс",
|
||||
"country": "Страна",
|
||||
"emailVerified": "E-mail подтвержден",
|
||||
"gssDelegationCredential": "Делегирование учетных данных через GSS",
|
||||
"role_admin": "Администратор",
|
||||
"role_realm-admin": "Администратор realm",
|
||||
"role_create-realm": "Создать realm",
|
||||
"role_view-realm": "Просмотр realm",
|
||||
"role_view-users": "Просмотр пользователей",
|
||||
"role_view-applications": "Просмотр приложений",
|
||||
"role_view-clients": "Просмотр клиентов",
|
||||
"role_view-events": "Просмотр событий",
|
||||
"role_view-identity-providers": "Просмотр провайдеров учетных записей",
|
||||
"role_manage-realm": "Управление realm",
|
||||
"role_manage-users": "Управление пользователями",
|
||||
"role_manage-applications": "Управление приложениями",
|
||||
"role_manage-identity-providers": "Управление провайдерами учетных записей",
|
||||
"role_manage-clients": "Управление клиентами",
|
||||
"role_manage-events": "Управление событиями",
|
||||
"role_view-profile": "Просмотр профиля",
|
||||
"role_manage-account": "Управление учетной записью",
|
||||
"role_read-token": "Чтение токена",
|
||||
"role_offline-access": "Доступ оффлайн",
|
||||
"role_uma_authorization": "Получение разрешений",
|
||||
"client_account": "Учетная запись",
|
||||
"client_security-admin-console": "Консоль администратора безопасности",
|
||||
"client_admin-cli": "Командный интерфейс администратора",
|
||||
"client_realm-management": "Управление Realm",
|
||||
"client_broker": "Брокер",
|
||||
"requiredFields": "Обязательные поля",
|
||||
"allFieldsRequired": "Все поля обязательны",
|
||||
"backToApplication": "« Назад в приложение",
|
||||
"backTo": "Назад в {0}",
|
||||
"date": "Дата",
|
||||
"event": "Событие",
|
||||
"ip": "IP",
|
||||
"client": "Клиент",
|
||||
"clients": "Клиенты",
|
||||
"details": "Детали",
|
||||
"started": "Начата",
|
||||
"lastAccess": "Последний доступ",
|
||||
"expires": "Истекает",
|
||||
"applications": "Приложения",
|
||||
"account": "Учетная запись",
|
||||
"federatedIdentity": "Федеративный идентификатор",
|
||||
"authenticator": "Аутентификатор",
|
||||
"sessions": "Сессии",
|
||||
"log": "Журнал",
|
||||
"application": "Приложение",
|
||||
"availablePermissions": "Доступные разрешения",
|
||||
"grantedPermissions": "Согласованные разрешения",
|
||||
"grantedPersonalInfo": "Согласованная персональная информация",
|
||||
"additionalGrants": "Дополнительные согласования",
|
||||
"action": "Действие",
|
||||
"inResource": "в",
|
||||
"fullAccess": "Полный доступ",
|
||||
"offlineToken": "Оффлайн токен",
|
||||
"revoke": "Отозвать согласование",
|
||||
"configureAuthenticators": "Сконфигурированные аутентификаторы",
|
||||
"mobile": "Мобильное приложение",
|
||||
"totpStep1":
|
||||
'Установите <a href="https://freeotp.github.io/" target="_blank">FreeOTP</a> или Google Authenticator. Оба приложения доступны на <a href="https://play.google.com">Google Play</a> и в Apple App Store.',
|
||||
"totpStep2": "Откройте приложение и просканируйте баркод, либо введите ключ.",
|
||||
"totpStep3": "Введите одноразовый код, выданный приложением, и нажмите сохранить для завершения установки.",
|
||||
"missingUsernameMessage": "Введите имя пользователя.",
|
||||
"missingFirstNameMessage": "Введите имя.",
|
||||
"invalidEmailMessage": "Введите корректный E-mail.",
|
||||
"missingLastNameMessage": "Введите фамилию.",
|
||||
"missingEmailMessage": "Введите E-mail.",
|
||||
"missingPasswordMessage": "Введите пароль.",
|
||||
"notMatchPasswordMessage": "Пароли не совпадают.",
|
||||
"missingTotpMessage": "Введите код аутентификатора.",
|
||||
"invalidPasswordExistingMessage": "Существующий пароль неверный.",
|
||||
"invalidPasswordConfirmMessage": "Подтверждение пароля не совпадает.",
|
||||
"invalidTotpMessage": "Неверный код аутентификатора.",
|
||||
"usernameExistsMessage": "Имя пользователя уже существует.",
|
||||
"emailExistsMessage": "E-mail уже существует.",
|
||||
"readOnlyUserMessage": "Вы не можете обновить информацию вашей учетной записи, т.к. она доступна только для чтения.",
|
||||
"readOnlyPasswordMessage": "Вы не можете обновить пароль вашей учетной записи, т.к. он доступен только для чтения.",
|
||||
"successTotpMessage": "Аутентификатор в мобильном приложении сконфигурирован.",
|
||||
"successTotpRemovedMessage": "Аутентификатор в мобильном приложении удален.",
|
||||
"successGrantRevokedMessage": "Согласование отозвано успешно.",
|
||||
"accountUpdatedMessage": "Ваша учетная запись обновлена.",
|
||||
"accountPasswordUpdatedMessage": "Ваш пароль обновлен.",
|
||||
"missingIdentityProviderMessage": "Провайдер учетных записей не задан.",
|
||||
"invalidFederatedIdentityActionMessage": "Некорректное или недопустимое действие.",
|
||||
"identityProviderNotFoundMessage": "Заданный провайдер учетных записей не найден.",
|
||||
"federatedIdentityLinkNotActiveMessage": "Идентификатор больше не активен.",
|
||||
"federatedIdentityRemovingLastProviderMessage": "Вы не можете удалить последний федеративный идентификатор, т.к. Вы не имеете пароля.",
|
||||
"identityProviderRedirectErrorMessage": "Ошибка перенаправления в провайдер учетных записей.",
|
||||
"identityProviderRemovedMessage": "Провайдер учетных записей успешно удален.",
|
||||
"identityProviderAlreadyLinkedMessage": "Федеративный идентификатор, возвращенный {0} уже используется другим пользователем.",
|
||||
"staleCodeAccountMessage": "Страница устарела. Попробуйте еще раз.",
|
||||
"consentDenied": "В согласовании отказано.",
|
||||
"accountDisabledMessage": "Учетная запись заблокирована, обратитесь к администратору.",
|
||||
"accountTemporarilyDisabledMessage": "Учетная запись временно заблокирована, обратитесь к администратору или попробуйте позже.",
|
||||
"invalidPasswordMinLengthMessage": "Некорректный пароль: длина пароля должна быть не менее {0} символа(ов).",
|
||||
"invalidPasswordMinLowerCaseCharsMessage": "Некорректный пароль: пароль должен содержать не менее {0} символа(ов) в нижнем регистре.",
|
||||
"invalidPasswordMinDigitsMessage": "Некорректный пароль: пароль должен содержать не менее {0} цифр(ы).",
|
||||
"invalidPasswordMinUpperCaseCharsMessage": "Некорректный пароль: пароль должен содержать не менее {0} символа(ов) в верхнем регистре.",
|
||||
"invalidPasswordMinSpecialCharsMessage": "Некорректный пароль: пароль должен содержать не менее {0} спецсимвола(ов).",
|
||||
"invalidPasswordNotUsernameMessage": "Некорректный пароль: пароль не должен совпадать с именем пользователя.",
|
||||
"invalidPasswordRegexPatternMessage": "Некорректный пароль: пароль не удовлетворяет регулярному выражению.",
|
||||
"invalidPasswordHistoryMessage": "Некорректный пароль: пароль не должен совпадать с последним(и) {0} паролями.",
|
||||
"invalidPasswordGenericMessage": "Некорректный пароль: новый пароль не соответствует правилам пароля."
|
||||
};
|
||||
|
||||
export default messages;
|
||||
/* spell-checker: enable */
|
180
src/lib/i18n/generated_messages/11.0.3/account/sk.ts
Normal file
180
src/lib/i18n/generated_messages/11.0.3/account/sk.ts
Normal file
@ -0,0 +1,180 @@
|
||||
//This code was automatically generated by running dist/bin/generate-i18n-messages.js
|
||||
//PLEASE DO NOT EDIT MANUALLY
|
||||
|
||||
/* spell-checker: disable */
|
||||
const messages = {
|
||||
"doSave": "Uložiť",
|
||||
"doCancel": "Zrušiť",
|
||||
"doLogOutAllSessions": "Odhlásenie všetkých relácií",
|
||||
"doRemove": "Odstrániť",
|
||||
"doAdd": "Pridať",
|
||||
"doSignOut": "Odhlásiť",
|
||||
"editAccountHtmlTitle": "Upraviť účet",
|
||||
"federatedIdentitiesHtmlTitle": "Prepojená identita",
|
||||
"accountLogHtmlTitle": "Denník zmien užívateľských účtov",
|
||||
"changePasswordHtmlTitle": "Zmena hesla",
|
||||
"sessionsHtmlTitle": "Relácie",
|
||||
"accountManagementTitle": "Správa účtu Keycloak",
|
||||
"authenticatorTitle": "Autentifikátor",
|
||||
"applicationsHtmlTitle": "Aplikácie",
|
||||
"authenticatorCode": "Jednorázový kód",
|
||||
"email": "E-mail",
|
||||
"firstName": "Meno",
|
||||
"givenName": "Meno pri narodení",
|
||||
"fullName": "Celé meno",
|
||||
"lastName": "Priezvisko",
|
||||
"familyName": "Rodné meno",
|
||||
"password": "Heslo",
|
||||
"passwordConfirm": "Potrvrdenie hesla",
|
||||
"passwordNew": "Nové heslo",
|
||||
"username": "Meno používateľa",
|
||||
"address": "Adresa",
|
||||
"street": "Ulica",
|
||||
"locality": "Mesto alebo lokalita",
|
||||
"region": "Kraj",
|
||||
"postal_code": "PSČ",
|
||||
"country": "Štát",
|
||||
"emailVerified": "E-mail overený",
|
||||
"gssDelegationCredential": "GSS delegované oprávnenie",
|
||||
"role_admin": "Administrátor",
|
||||
"role_realm-admin": "Administrátor realmu",
|
||||
"role_create-realm": "Vytvoriť realm",
|
||||
"role_view-realm": "Zobraziť realm",
|
||||
"role_view-users": "Zobraziť používateľov",
|
||||
"role_view-applications": "Zobraziť aplikácie",
|
||||
"role_view-clients": "Zobraziť klientov",
|
||||
"role_view-events": "Zobraziť udalosti",
|
||||
"role_view-identity-providers": "Zobraziť klientov poskytovateľov identity",
|
||||
"role_manage-realm": "Spravovať realm",
|
||||
"role_manage-users": "Spravovať používateľov",
|
||||
"role_manage-applications": "Spravovať aplikácie",
|
||||
"role_manage-identity-providers": "Spravovať poskytovateľov identity",
|
||||
"role_manage-clients": "Spravovať klientov",
|
||||
"role_manage-events": "Spravovať udalosti",
|
||||
"role_view-profile": "Zobraziť profil",
|
||||
"role_manage-account": "Spravovať účet",
|
||||
"role_manage-account-links": "Spravovať odkazy na účet",
|
||||
"role_read-token": "Čítať token",
|
||||
"role_offline-access": "Offline prístup",
|
||||
"role_uma_authorization": "Autorizácia používateľom riadeného prístupu",
|
||||
"client_account": "Účet klienta",
|
||||
"client_security-admin-console": "Bezpečnostná administrátorská konzola",
|
||||
"client_admin-cli": "Spravovať CLI klienta",
|
||||
"client_realm-management": "Spravovať realmy klienta",
|
||||
"client_broker": "Broker",
|
||||
"requiredFields": "Povinné polia",
|
||||
"allFieldsRequired": "Všetky požadované polia",
|
||||
"backToApplication": "« Späť na aplikáciu",
|
||||
"backTo": "Späť na {0}",
|
||||
"date": "Dátum",
|
||||
"event": "Udalosť",
|
||||
"ip": "IP",
|
||||
"client": "Klient",
|
||||
"clients": "Klienti",
|
||||
"details": "Podrobnosti",
|
||||
"started": "Začíname",
|
||||
"lastAccess": "Posledný prístup",
|
||||
"expires": "Vyprší",
|
||||
"applications": "Aplikácie",
|
||||
"account": "Účet",
|
||||
"federatedIdentity": "Prepojená identita",
|
||||
"authenticator": "Autentifikátor",
|
||||
"sessions": "Relácie",
|
||||
"log": "Denník",
|
||||
"application": "Aplikácia",
|
||||
"availablePermissions": "Dostupné oprávnenia",
|
||||
"grantedPermissions": "Pridelené oprávnenia",
|
||||
"grantedPersonalInfo": "Poskytnuté osobné informácie",
|
||||
"additionalGrants": "Dodatočné oprávnenia",
|
||||
"action": "Akcia",
|
||||
"inResource": "v",
|
||||
"fullAccess": "Úplný prístup",
|
||||
"offlineToken": "Offline token",
|
||||
"revoke": "Zrušiť oprávnenie",
|
||||
"configureAuthenticators": "Nakonfigurované autentifikátory",
|
||||
"mobile": "Mobilný",
|
||||
"totpStep1":
|
||||
'Nainštalujte vo svojom zariadení <a href="https://freeotp.github.io/" target="_blank"> FreeOTP </a> alebo Google Authenticator. Obidve aplikácie sú k dispozícii v <a href="https://play.google.com"> Google Play </a> a Apple App Store.',
|
||||
"totpStep2": "Otvorte aplikáciu a naskenujte čiarový kód alebo zadajte kľúč.",
|
||||
"totpStep3": "Zadajte jednorazový kód poskytnutý aplikáciou a kliknutím na tlačidlo Uložiť dokončíte nastavenie.",
|
||||
"totpManualStep2": "Otvorte aplikáciu a zadajte kľúč",
|
||||
"totpManualStep3": "Použite nasledujúce hodnoty konfigurácie, ak aplikácia umožňuje ich nastavenie",
|
||||
"totpUnableToScan": "Nemožno skenovať?",
|
||||
"totpScanBarcode": "Skenovanie čiarového kódu?",
|
||||
"totp.totp": "Založené na čase",
|
||||
"totp.hotp": "Založené na počítadle",
|
||||
"totpType": "Typ",
|
||||
"totpAlgorithm": "Algoritmus",
|
||||
"totpDigits": "Číslica",
|
||||
"totpInterval": "Interval",
|
||||
"totpCounter": "Počítadlo",
|
||||
"missingUsernameMessage": "Zadajte používateľské meno.",
|
||||
"missingFirstNameMessage": "Zadajte meno.",
|
||||
"invalidEmailMessage": "Neplatná e-mailová adresa.",
|
||||
"missingLastNameMessage": "Zadajte priezvisko.",
|
||||
"missingEmailMessage": "Zadajte e-mail.",
|
||||
"missingPasswordMessage": "Zadajte heslo, prosím.",
|
||||
"notMatchPasswordMessage": "Heslá sa nezhodujú.",
|
||||
"missingTotpMessage": "Zadajte jednorazový kód, prosím",
|
||||
"invalidPasswordExistingMessage": "Neplatné existujúce heslo.",
|
||||
"invalidPasswordConfirmMessage": "Potvrdenie hesla sa nezhoduje.",
|
||||
"invalidTotpMessage": "Neplatný jednorazový kód.",
|
||||
"usernameExistsMessage": "Užívateľské meno už existuje.",
|
||||
"emailExistsMessage": "E-mail už existuje.",
|
||||
"readOnlyUserMessage": "Váš účet nemôžete aktualizovať, pretože je iba na čítanie.",
|
||||
"readOnlyUsernameMessage": "Nemôžete aktualizovať svoje používateľské meno, pretože je iba na čítanie.",
|
||||
"readOnlyPasswordMessage": "Heslo nemôžete aktualizovať, pretože váš účet je iba na čítanie.",
|
||||
"successTotpMessage": "Konfigurácia mobilného autentifikátora.",
|
||||
"successTotpRemovedMessage": "Mobilný autentifikátor bol odstránený.",
|
||||
"successGrantRevokedMessage": "Oprávnenie bolo úspešne zrušené.",
|
||||
"accountUpdatedMessage": "Váš účet bol aktualizovaný.",
|
||||
"accountPasswordUpdatedMessage": "Vaše heslo bolo aktualizované.",
|
||||
"missingIdentityProviderMessage": "Poskytovateľ identity nie je zadaný.",
|
||||
"invalidFederatedIdentityActionMessage": "Neplatná alebo chýbajúca akcia.",
|
||||
"identityProviderNotFoundMessage": "Zadaný poskytovateľ identity nenájdený.",
|
||||
"federatedIdentityLinkNotActiveMessage": "Identita už nie je aktívna.",
|
||||
"federatedIdentityRemovingLastProviderMessage": "Nemôžete odstrániť poslednú spojenú identitu, pretože nemáte heslo.",
|
||||
"identityProviderRedirectErrorMessage": "Nepodarilo sa presmerovať na poskytovateľa identity.",
|
||||
"identityProviderRemovedMessage": "Poskytovateľ identity bol úspešne odstránený.",
|
||||
"identityProviderAlreadyLinkedMessage": "Spojená identita vrátená {0} je už prepojená s iným používateľom.",
|
||||
"staleCodeAccountMessage": "Platnosť vypršala. Skúste ešte raz.",
|
||||
"consentDenied": "Súhlas bol zamietnutý.",
|
||||
"accountDisabledMessage": "Účet je zakázaný, kontaktujte správcu.",
|
||||
"accountTemporarilyDisabledMessage": "Účet je dočasne zakázaný, kontaktujte administrátora alebo skúste neskôr.",
|
||||
"invalidPasswordMinLengthMessage": "Neplatné heslo: minimálna dĺžka {0}.",
|
||||
"invalidPasswordMinLowerCaseCharsMessage": "Neplatné heslo: musí obsahovať minimálne {0} malé písmená.",
|
||||
"invalidPasswordMinDigitsMessage": "Neplatné heslo: musí obsahovať aspoň {0} číslic.",
|
||||
"invalidPasswordMinUpperCaseCharsMessage": "Neplatné heslo: musí obsahovať aspoň {0} veľké písmená.",
|
||||
"invalidPasswordMinSpecialCharsMessage": "Neplatné heslo: musí obsahovať aspoň {0} špeciálne znaky.",
|
||||
"invalidPasswordNotUsernameMessage": "Neplatné heslo: nesmie byť rovnaké ako používateľské meno.",
|
||||
"invalidPasswordRegexPatternMessage": "Neplatné heslo: nezodpovedá regulárnemu výrazu.",
|
||||
"invalidPasswordHistoryMessage": "Neplatné heslo: nesmie sa rovnať žiadnemu z posledných {0} hesiel.",
|
||||
"invalidPasswordBlacklistedMessage": "Neplatné heslo: heslo je na čiernej listine.",
|
||||
"invalidPasswordGenericMessage": "Neplatné heslo: nové heslo nezodpovedá pravidlám hesiel.",
|
||||
"myResources": "Moje Zdroje",
|
||||
"myResourcesSub": "Moje zdroje",
|
||||
"doDeny": "Zakázať",
|
||||
"doRevoke": "Odvolať",
|
||||
"doApprove": "Schváliť",
|
||||
"doRemoveSharing": "Odstránenie zdieľania",
|
||||
"doRemoveRequest": "Odstrániť požiadavku",
|
||||
"peopleAccessResource": "Ľudia s prístupom k tomuto zdroju",
|
||||
"name": "Názov",
|
||||
"scopes": "Rozsahy",
|
||||
"resource": "Zdroj",
|
||||
"user": "Používateľ",
|
||||
"peopleSharingThisResource": "Ľudia zdieľajúci tento zdroj",
|
||||
"shareWithOthers": "Zdieľať s ostatnými",
|
||||
"needMyApproval": "Potrebuje môj súhlas",
|
||||
"requestsWaitingApproval": "Vaše požiadavky čakajú na schválenie",
|
||||
"icon": "Ikona",
|
||||
"requestor": "Žiadateľ",
|
||||
"owner": "Vlastník",
|
||||
"resourcesSharedWithMe": "Zdroje zdieľané so mnou",
|
||||
"permissionRequestion": "Žiadosti o povolenie",
|
||||
"permission": "Oprávnenie",
|
||||
"shares": "podiel (y)"
|
||||
};
|
||||
|
||||
export default messages;
|
||||
/* spell-checker: enable */
|
139
src/lib/i18n/generated_messages/11.0.3/account/sv.ts
Normal file
139
src/lib/i18n/generated_messages/11.0.3/account/sv.ts
Normal file
@ -0,0 +1,139 @@
|
||||
//This code was automatically generated by running dist/bin/generate-i18n-messages.js
|
||||
//PLEASE DO NOT EDIT MANUALLY
|
||||
|
||||
/* spell-checker: disable */
|
||||
const messages = {
|
||||
"doSave": "Spara",
|
||||
"doCancel": "Avbryt",
|
||||
"doLogOutAllSessions": "Logga ut från samtliga sessioner",
|
||||
"doRemove": "Ta bort",
|
||||
"doAdd": "Lägg till",
|
||||
"doSignOut": "Logga ut",
|
||||
"editAccountHtmlTitle": "Redigera konto",
|
||||
"federatedIdentitiesHtmlTitle": "Federerade identiteter",
|
||||
"accountLogHtmlTitle": "Kontologg",
|
||||
"changePasswordHtmlTitle": "Byt lösenord",
|
||||
"sessionsHtmlTitle": "Sessioner",
|
||||
"accountManagementTitle": "Kontohantering för Keycloak",
|
||||
"authenticatorTitle": "Autentiserare",
|
||||
"applicationsHtmlTitle": "Applikationer",
|
||||
"authenticatorCode": "Engångskod",
|
||||
"email": "E-post",
|
||||
"firstName": "Förnamn",
|
||||
"lastName": "Efternamn",
|
||||
"password": "Lösenord",
|
||||
"passwordConfirm": "Bekräftelse",
|
||||
"passwordNew": "Nytt lösenord",
|
||||
"username": "Användarnamn",
|
||||
"address": "Adress",
|
||||
"street": "Gata",
|
||||
"locality": "Postort",
|
||||
"region": "Stat, Provins eller Region",
|
||||
"postal_code": "Postnummer",
|
||||
"country": "Land",
|
||||
"emailVerified": "E-post verifierad",
|
||||
"gssDelegationCredential": "GSS Delegation Credential",
|
||||
"role_admin": "Administratör",
|
||||
"role_realm-admin": "Realm-administratör",
|
||||
"role_create-realm": "Skapa realm",
|
||||
"role_view-realm": "Visa realm",
|
||||
"role_view-users": "Visa användare",
|
||||
"role_view-applications": "Visa applikationer",
|
||||
"role_view-clients": "Visa klienter",
|
||||
"role_view-events": "Visa event",
|
||||
"role_view-identity-providers": "Visa identitetsleverantörer",
|
||||
"role_manage-realm": "Hantera realm",
|
||||
"role_manage-users": "Hantera användare",
|
||||
"role_manage-applications": "Hantera applikationer",
|
||||
"role_manage-identity-providers": "Hantera identitetsleverantörer",
|
||||
"role_manage-clients": "Hantera klienter",
|
||||
"role_manage-events": "Hantera event",
|
||||
"role_view-profile": "Visa profil",
|
||||
"role_manage-account": "Hantera konto",
|
||||
"role_read-token": "Läs element",
|
||||
"role_offline-access": "Åtkomst offline",
|
||||
"role_uma_authorization": "Erhåll tillstånd",
|
||||
"client_account": "Konto",
|
||||
"client_security-admin-console": "Säkerhetsadministratörskonsol",
|
||||
"client_admin-cli": "Administratörs-CLI",
|
||||
"client_realm-management": "Realmhantering",
|
||||
"requiredFields": "Obligatoriska fält",
|
||||
"allFieldsRequired": "Samtliga fält krävs",
|
||||
"backToApplication": "« Tillbaka till applikationen",
|
||||
"backTo": "Tillbaka till {0}",
|
||||
"date": "Datum",
|
||||
"event": "Event",
|
||||
"ip": "IP",
|
||||
"client": "Klient",
|
||||
"clients": "Klienter",
|
||||
"details": "Detaljer",
|
||||
"started": "Startade",
|
||||
"lastAccess": "Senast åtkomst",
|
||||
"expires": "Upphör",
|
||||
"applications": "Applikationer",
|
||||
"account": "Konto",
|
||||
"federatedIdentity": "Federerad identitet",
|
||||
"authenticator": "Autentiserare",
|
||||
"sessions": "Sessioner",
|
||||
"log": "Logg",
|
||||
"application": "Applikation",
|
||||
"availablePermissions": "Tillgängliga rättigheter",
|
||||
"grantedPermissions": "Beviljade rättigheter",
|
||||
"grantedPersonalInfo": "Medgiven personlig information",
|
||||
"additionalGrants": "Ytterligare medgivanden",
|
||||
"action": "Åtgärd",
|
||||
"inResource": "i",
|
||||
"fullAccess": "Fullständig åtkomst",
|
||||
"offlineToken": "Offline token",
|
||||
"revoke": "Upphäv rättighet",
|
||||
"configureAuthenticators": "Konfigurerade autentiserare",
|
||||
"mobile": "Mobil",
|
||||
"totpStep1":
|
||||
'Installera <a href="https://freeotp.github.io/" target="_blank">FreeOTP</a> eller Google Authenticator på din enhet. Båda applikationerna finns tillgängliga på <a href="https://play.google.com">Google Play</a> och Apple App Store.',
|
||||
"totpStep2": "Öppna applikationen och skanna streckkoden eller skriv i nyckeln.",
|
||||
"totpStep3": "Fyll i engångskoden som tillhandahålls av applikationen och klicka på Spara för att avsluta inställningarna.",
|
||||
"missingUsernameMessage": "Vänligen ange användarnamn.",
|
||||
"missingFirstNameMessage": "Vänligen ange förnamn.",
|
||||
"invalidEmailMessage": "Ogiltig e-postadress.",
|
||||
"missingLastNameMessage": "Vänligen ange efternamn.",
|
||||
"missingEmailMessage": "Vänligen ange e-post.",
|
||||
"missingPasswordMessage": "Vänligen ange lösenord.",
|
||||
"notMatchPasswordMessage": "Lösenorden matchar inte.",
|
||||
"missingTotpMessage": "Vänligen ange autentiseringskoden.",
|
||||
"invalidPasswordExistingMessage": "Det nuvarande lösenordet är ogiltigt.",
|
||||
"invalidPasswordConfirmMessage": "Lösenordsbekräftelsen matchar inte.",
|
||||
"invalidTotpMessage": "Autentiseringskoden är ogiltig.",
|
||||
"usernameExistsMessage": "Användarnamnet finns redan.",
|
||||
"emailExistsMessage": "E-posten finns redan.",
|
||||
"readOnlyUserMessage": "Du kan inte uppdatera ditt konto eftersom det är skrivskyddat.",
|
||||
"readOnlyPasswordMessage": "Du kan inte uppdatera ditt lösenord eftersom ditt konto är skrivskyddat.",
|
||||
"successTotpMessage": "Mobilautentiseraren är inställd.",
|
||||
"successTotpRemovedMessage": "Mobilautentiseraren är borttagen.",
|
||||
"successGrantRevokedMessage": "Upphävandet av rättigheten lyckades.",
|
||||
"accountUpdatedMessage": "Ditt konto har uppdaterats.",
|
||||
"accountPasswordUpdatedMessage": "Ditt lösenord har uppdaterats.",
|
||||
"missingIdentityProviderMessage": "Identitetsleverantör är inte angiven.",
|
||||
"invalidFederatedIdentityActionMessage": "Åtgärden är ogiltig eller saknas.",
|
||||
"identityProviderNotFoundMessage": "Angiven identitetsleverantör hittas inte.",
|
||||
"federatedIdentityLinkNotActiveMessage": "Den här identiteten är inte längre aktiv.",
|
||||
"federatedIdentityRemovingLastProviderMessage": "Du kan inte ta bort senaste federerade identiteten eftersom du inte har ett lösenord.",
|
||||
"identityProviderRedirectErrorMessage": "Misslyckades med att omdirigera till identitetsleverantör.",
|
||||
"identityProviderRemovedMessage": "Borttagningen av identitetsleverantören lyckades.",
|
||||
"identityProviderAlreadyLinkedMessage": "Den federerade identiteten som returnerades av {0} är redan länkad till en annan användare.",
|
||||
"staleCodeAccountMessage": "Sidan har upphört att gälla. Vänligen försök igen.",
|
||||
"consentDenied": "Samtycket förnekades.",
|
||||
"accountDisabledMessage": "Kontot är inaktiverat, kontakta administratör.",
|
||||
"accountTemporarilyDisabledMessage": "Kontot är tillfälligt inaktiverat, kontakta administratör eller försök igen senare.",
|
||||
"invalidPasswordMinLengthMessage": "Ogiltigt lösenord. Minsta längd är {0}.",
|
||||
"invalidPasswordMinLowerCaseCharsMessage": "Ogiltigt lösenord: måste innehålla minst {0} små bokstäver.",
|
||||
"invalidPasswordMinDigitsMessage": "Ogiltigt lösenord: måste innehålla minst {0} siffror.",
|
||||
"invalidPasswordMinUpperCaseCharsMessage": "Ogiltigt lösenord: måste innehålla minst {0} stora bokstäver.",
|
||||
"invalidPasswordMinSpecialCharsMessage": "Ogiltigt lösenord: måste innehålla minst {0} specialtecken.",
|
||||
"invalidPasswordNotUsernameMessage": "Ogiltigt lösenord: Får inte vara samma som användarnamnet.",
|
||||
"invalidPasswordRegexPatternMessage": "Ogiltigt lösenord: matchar inte kravet för lösenordsmönster.",
|
||||
"invalidPasswordHistoryMessage": "Ogiltigt lösenord: Får inte vara samma som de senaste {0} lösenorden.",
|
||||
"invalidPasswordGenericMessage": "Ogiltigt lösenord: Det nya lösenordet stämmer inte med lösenordspolicyn."
|
||||
};
|
||||
|
||||
export default messages;
|
||||
/* spell-checker: enable */
|
309
src/lib/i18n/generated_messages/11.0.3/account/tr.ts
Normal file
309
src/lib/i18n/generated_messages/11.0.3/account/tr.ts
Normal file
@ -0,0 +1,309 @@
|
||||
//This code was automatically generated by running dist/bin/generate-i18n-messages.js
|
||||
//PLEASE DO NOT EDIT MANUALLY
|
||||
|
||||
/* spell-checker: disable */
|
||||
const messages = {
|
||||
"doSave": "Kaydet",
|
||||
"doCancel": "İptal",
|
||||
"doLogOutAllSessions": "Tüm Oturumları Kapat",
|
||||
"doRemove": "Sil",
|
||||
"doAdd": "Ekle",
|
||||
"doSignOut": "Çıkış",
|
||||
"doLogIn": "Oturum aç",
|
||||
"doLink": "Bağlantı",
|
||||
"editAccountHtmlTitle": "Hesabım",
|
||||
"personalInfoHtmlTitle": "Kişisel bilgi",
|
||||
"federatedIdentitiesHtmlTitle": "Değiştirilen Kimlikler",
|
||||
"accountLogHtmlTitle": "Kullanıcı Logları",
|
||||
"changePasswordHtmlTitle": "Şifre Değiştirme",
|
||||
"deviceActivityHtmlTitle": "Cihaz Etkinliği",
|
||||
"sessionsHtmlTitle": "Oturum",
|
||||
"accountManagementTitle": "Keycloak Kullanıcı Hesabı Yönetimi",
|
||||
"authenticatorTitle": "Kimlik Doğrulama",
|
||||
"applicationsHtmlTitle": "Uygulama",
|
||||
"linkedAccountsHtmlTitle": "Bağlantılı Hesaplar",
|
||||
"accountManagementWelcomeMessage": "Keycloak Hesap Yönetimine Hoş Geldiniz",
|
||||
"personalInfoIntroMessage": "Temel bilgilerinizi yönetin",
|
||||
"accountSecurityTitle": "Hesap Güvenliği",
|
||||
"accountSecurityIntroMessage": "Şifrenizi ve hesap erişiminizi kontrol edin",
|
||||
"applicationsIntroMessage": "Hesabınıza erişmek için uygulama izninizi takip edin ve yönetin",
|
||||
"resourceIntroMessage": "Kaynaklarınızı ekip üyeleri arasında paylaşın",
|
||||
"passwordLastUpdateMessage": "Şifreniz güncellendi",
|
||||
"updatePasswordTitle": "Şifre güncelle",
|
||||
"updatePasswordMessageTitle": "Güçlü bir şifre seçtiğinizden emin olun",
|
||||
"updatePasswordMessage":
|
||||
"Güçlü bir şifre, sayılar, harfler ve sembollerin karışımından oluşmalıdır. Tahmin etmesi zor ve gerçek bir kelimeye benzemeyen şifre sadece bu hesap için kullanılır.",
|
||||
"personalSubTitle": "Kişisel Bilgileriniz",
|
||||
"personalSubMessage": "Bu temel bilgileri yönetin: adınız, soyadınız ve e-posta adresiniz",
|
||||
"authenticatorCode": "Kimlik Doğrulama Kodu",
|
||||
"email": "E-Mail",
|
||||
"firstName": "Ad",
|
||||
"givenName": "Ad",
|
||||
"fullName": "Ad Soyad",
|
||||
"lastName": "Soyad",
|
||||
"familyName": "Soyad",
|
||||
"password": "Şifre",
|
||||
"currentPassword": "Şimdiki Şifre",
|
||||
"passwordConfirm": "Şifre Doğrulama",
|
||||
"passwordNew": "Yeni Şifre",
|
||||
"username": "Kullanıcı Adı",
|
||||
"address": "Adres",
|
||||
"street": "Cadde",
|
||||
"region": "Bölge",
|
||||
"postal_code": "Posta Kodu",
|
||||
"locality": "Şehir",
|
||||
"country": "Ülke",
|
||||
"emailVerified": "E-Mail Doğrulandı",
|
||||
"gssDelegationCredential": "GSS Yetki Bilgisi",
|
||||
"profileScopeConsentText": "Kullanıcı profili",
|
||||
"emailScopeConsentText": "Email adresi",
|
||||
"addressScopeConsentText": "Adres",
|
||||
"phoneScopeConsentText": "Telefon numarası",
|
||||
"offlineAccessScopeConsentText": "Çevrimdışı Erişim",
|
||||
"samlRoleListScopeConsentText": "Rollerim",
|
||||
"rolesScopeConsentText": "Kullanıcı rolleri",
|
||||
"role_admin": "Admin",
|
||||
"role_realm-admin": "Realm Admin",
|
||||
"role_create-realm": "Realm Oluştur",
|
||||
"role_view-realm": "Realm görüntüle",
|
||||
"role_view-users": "Kullanıcıları görüntüle",
|
||||
"role_view-applications": "Uygulamaları görüntüle",
|
||||
"role_view-clients": "İstemci görüntüle",
|
||||
"role_view-events": "Olay görüntüle",
|
||||
"role_view-identity-providers": "Kimlik Sağlayıcılar",
|
||||
"role_manage-realm": "Realm yönet",
|
||||
"role_manage-users": "Kullanıcıları yönet",
|
||||
"role_manage-applications": "Uygulamaları yönet",
|
||||
"role_manage-identity-providers": "Kimlik Sağlayıcıları Yönet",
|
||||
"role_manage-clients": "İstemci yönet",
|
||||
"role_manage-events": "Olay yönet",
|
||||
"role_view-profile": "Profilleri görüntüle",
|
||||
"role_manage-account": "Profilleri Yönet",
|
||||
"role_manage-account-links": "Profil bağlantılarını yönet",
|
||||
"role_read-token": "Token oku",
|
||||
"role_offline-access": "Çevirimdışı Yetki",
|
||||
"role_uma_authorization": "İzinleri Al",
|
||||
"client_account": "Müşteri Hesabı",
|
||||
"client_security-admin-console": "Güvenlik Yönetici Konsolu",
|
||||
"client_admin-cli": "Admin CLI",
|
||||
"client_realm-management": "Realm-Management",
|
||||
"client_broker": "Broker",
|
||||
"requiredFields": "Zorunlu Alanlar",
|
||||
"allFieldsRequired": "Tüm Alanlar Zorunlu",
|
||||
"backToApplication": "« Uygulamaya Dön",
|
||||
"backTo": "Geri Dön {0}",
|
||||
"date": "Gün",
|
||||
"event": "Olay",
|
||||
"ip": "IP",
|
||||
"client": "İstemci",
|
||||
"clients": "İstemciler",
|
||||
"details": "Detaylar",
|
||||
"started": "Başlangıç Tarihi",
|
||||
"lastAccess": "Son Erişim Tarihi",
|
||||
"expires": "Son Kullanma Tarihi",
|
||||
"applications": "Uygulama",
|
||||
"account": "Hesap",
|
||||
"federatedIdentity": "Federal Kimlik",
|
||||
"authenticator": "Kimlik Doğrulama",
|
||||
"device-activity": "Cihaz Etkinliği",
|
||||
"sessions": "Oturum",
|
||||
"log": "Log",
|
||||
"application": "Uygulama",
|
||||
"availablePermissions": "Kullanılabilir İzinler",
|
||||
"availableRoles": "Kullanılabilir Roller",
|
||||
"grantedPermissions": "Verilen İzinler",
|
||||
"grantedPersonalInfo": "İzin Verilen Kişisel Bilgiler",
|
||||
"additionalGrants": "Ek İzinler",
|
||||
"action": "Aksiyon",
|
||||
"inResource": "Kaynak",
|
||||
"fullAccess": "Tam Yetki",
|
||||
"offlineToken": "Çevirimdışı-Token",
|
||||
"revoke": "İzni İptal et",
|
||||
"configureAuthenticators": "Çoklu Kimlik Doğrulama",
|
||||
"mobile": "Mobil",
|
||||
"totpStep1": "Akıllı Telefonunuza aşağıdaki uygulamalardan birini yükleyin:",
|
||||
"totpStep2": "Uygulamayı açın ve barkodu okutun.",
|
||||
"totpStep3": "Uygulama tarafından oluşturulan tek seferlik kodu girin ve Kaydet'i tıklayın.",
|
||||
"totpManualStep2": "Uygulamayı açın ve aşağıdaki anahtarı girin.",
|
||||
"totpManualStep3": "Bunları uygulama için özelleştirebilirseniz aşağıdaki yapılandırma değerlerini kullanın:",
|
||||
"totpUnableToScan": "Barkodu tarayamıyor musunuz?",
|
||||
"totpScanBarcode": "Barkod Tara?",
|
||||
"totp.totp": "Zaman bazlı (time-based)",
|
||||
"totp.hotp": "Sayaç tabanlı (counter-based)",
|
||||
"totpType": "Tip",
|
||||
"totpAlgorithm": "Algoritma",
|
||||
"totpDigits": "Basamak",
|
||||
"totpInterval": "Aralık",
|
||||
"totpCounter": "Sayaç",
|
||||
"missingUsernameMessage": "Lütfen bir kullanıcı adı giriniz.",
|
||||
"missingFirstNameMessage": "Lütfen bir ad girin.",
|
||||
"invalidEmailMessage": "Geçersiz e-posta adresi.",
|
||||
"missingLastNameMessage": "Lütfen bir soyadı giriniz.",
|
||||
"missingEmailMessage": "Lütfen bir e-mail adresi giriniz.",
|
||||
"missingPasswordMessage": "Lütfen bir şifre giriniz.",
|
||||
"notMatchPasswordMessage": "Şifreler aynı değil.",
|
||||
"missingTotpMessage": "Lütfen tek seferlik kodu girin.",
|
||||
"invalidPasswordExistingMessage": "Mevcut şifre geçersiz.",
|
||||
"invalidPasswordConfirmMessage": "Şifre onayı aynı değil.",
|
||||
"invalidTotpMessage": "Geçersiz tek seferlik kod.",
|
||||
"usernameExistsMessage": "Kullanıcı adı zaten mevcut.",
|
||||
"emailExistsMessage": "E-posta adresi zaten mevcut.",
|
||||
"readOnlyUserMessage": "Yazma korumalı olduğundan kullanıcı hesabınızı değiştiremezsiniz.",
|
||||
"readOnlyUsernameMessage": "Yazma korumalı olduğundan kullanıcı adınızı değiştiremezsiniz.",
|
||||
"readOnlyPasswordMessage": "Yazma korumalı olduğundan şifrenizi değiştiremezsiniz.",
|
||||
"successTotpMessage": "Çoklu kimlik doğrulaması başarıyla yapılandırıldı.",
|
||||
"successTotpRemovedMessage": "Çoklu kimlik doğrulama başarıyla kaldırıldı.",
|
||||
"successGrantRevokedMessage": "İzin başarıyla iptal edildi.",
|
||||
"accountUpdatedMessage": "Kullanıcı hesabınız güncellendi.",
|
||||
"accountPasswordUpdatedMessage": "Şifreniz güncellendi.",
|
||||
"missingIdentityProviderMessage": "Kimlik Sağlayıcısı belirtilmemiş.",
|
||||
"invalidFederatedIdentityActionMessage": "Geçersiz veya eksik eylem.",
|
||||
"identityProviderNotFoundMessage": "Belirtilen Kimlik Sağlayıcı bulunamadı.",
|
||||
"federatedIdentityLinkNotActiveMessage": "Bu kimlik artık aktif değil.",
|
||||
"federatedIdentityRemovingLastProviderMessage": "Şifreniz olmadığı için son girişi kaldıramazsınız.",
|
||||
"identityProviderRedirectErrorMessage": "Kimlik sağlayıcıya iletilirken hata oluştu.",
|
||||
"identityProviderRemovedMessage": "Kimlik Sağlayıcısı başarıyla kaldırıldı.",
|
||||
"identityProviderAlreadyLinkedMessage": "Değiştirilmiş {0} kimliği başka bir kullanıcıya atanmış.",
|
||||
"staleCodeAccountMessage": "Bu sayfa artık geçerli değil, lütfen tekrar deneyin.",
|
||||
"consentDenied": "Onay reddedildi.",
|
||||
"accountDisabledMessage": "Hesabınız kilitlendi, lütfen yöneticiyle iletişime geçin.",
|
||||
"accountTemporarilyDisabledMessage": "Hesabınız geçici olarak kilitlendi, lütfen yöneticiyle iletişime geçin veya daha sonra tekrar deneyin.",
|
||||
"invalidPasswordMinLengthMessage": "Geçersiz Şifre: En az {0} karakter uzunluğunda olmalı.",
|
||||
"invalidPasswordMinLowerCaseCharsMessage": "Geçersiz Şifre : En az {0} küçük harf içermelidir.",
|
||||
"invalidPasswordMinDigitsMessage": "Geçersiz Şifre: En az {0} sayı(lar) içermelidir.",
|
||||
"invalidPasswordMinUpperCaseCharsMessage": "Geçersiz Şifre: En az {0} büyük harf içermelidir.",
|
||||
"invalidPasswordMinSpecialCharsMessage": "Geçersiz Şifre: En az {0} özel karakter içermelidir.",
|
||||
"invalidPasswordNotUsernameMessage": "Geçersiz Şifre: Kullanıcı adıyla aynı olamaz.",
|
||||
"invalidPasswordRegexPatternMessage": "Geçersiz Şifre: Regex Patternine uygun değil.",
|
||||
"invalidPasswordHistoryMessage": "Geçersiz Şifre: Son {0} şifreden biri olamaz.",
|
||||
"invalidPasswordBlacklistedMessage": "Geçersiz Şifre: Şifre bloklanmış şifreler listesindedir (kara liste).",
|
||||
"invalidPasswordGenericMessge": "Geçersiz Şifre: Yeni şifre, şifre kurallarını ihlal ediyor.",
|
||||
"myResources": "Kaynaklarım",
|
||||
"myResourcesSub": "Kaynaklarım",
|
||||
"doDeny": "Reddet",
|
||||
"doRevoke": "Geri al",
|
||||
"doApprove": "Onayla",
|
||||
"doRemoveSharing": "Paylaşımı Kaldır",
|
||||
"doRemoveRequest": "İsteği Kaldır",
|
||||
"peopleAccessResource": "Bu kaynağa erişimi olan kişiler",
|
||||
"resourceManagedPolicies": "Bu kaynağa erişim izni veren izinler",
|
||||
"resourceNoPermissionsGrantingAccess": "Bu kaynağa erişim izni verilmeyen izin yok",
|
||||
"anyAction": "Herhangi bir eylem",
|
||||
"description": "Açıklama",
|
||||
"name": "İsim",
|
||||
"scopes": "Kapsam",
|
||||
"resource": "Kaynak",
|
||||
"user": "Kullanıcı",
|
||||
"peopleSharingThisResource": "Bu kaynağı paylaşan kullanıcılar",
|
||||
"shareWithOthers": "Başkalarıyla paylaş",
|
||||
"needMyApproval": "Onayım gerekli",
|
||||
"requestsWaitingApproval": "Talepleriniz onay bekliyor",
|
||||
"icon": "Icon",
|
||||
"requestor": "Talep eden",
|
||||
"owner": "Sahip",
|
||||
"resourcesSharedWithMe": "Kaynaklar benimle paylaşıldı",
|
||||
"permissionRequestion": "İzin Talepleri",
|
||||
"permission": "İzin",
|
||||
"shares": "Paylaşım(lar)",
|
||||
"locale_ca": "Katalanca",
|
||||
"locale_de": "Almanca",
|
||||
"locale_en": "İngilizce",
|
||||
"locale_es": "İspanyolca",
|
||||
"locale_fr": "Fransızca",
|
||||
"locale_it": "İtalyanca",
|
||||
"locale_ja": "Japonca",
|
||||
"locale_nl": "Felemenkçe",
|
||||
"locale_no": "Norveçce",
|
||||
"locale_pl": "Lehçe",
|
||||
"locale_pt_BR": "Portekizce",
|
||||
"locale_pt-BR": "Portekizce",
|
||||
"locale_ru": "Rusça",
|
||||
"locale_lt": "Litvanca",
|
||||
"locale_zh-CN": "Çince",
|
||||
"locale_sk": "Slovakça",
|
||||
"locale_sv": "İsveççe",
|
||||
"locale_tr": "Türkçe",
|
||||
"applicaitonName": "İsim",
|
||||
"applicationType": "Uygulama Tipi",
|
||||
"applicationInUse": "Yalnızca uygulama içi kullanım",
|
||||
"clearAllFilter": "Tüm filtreleri temizle",
|
||||
"activeFilters": "Aktif filtreler",
|
||||
"filterByName": "İsme Göre Filtrele ...",
|
||||
"allApps": "Bütün uygulamalar",
|
||||
"internalApps": "İç uygulamalar",
|
||||
"thirdpartyApps": "Üçüncü parti uygulamalar",
|
||||
"appResults": "Sonuçlar",
|
||||
"authorizedProvider": "Yetkili Tedarikçi",
|
||||
"authorizedProviderMessage": "Yetkili Sağlayıcılar hesabınızla bağlantılı",
|
||||
"identityProvider": "Kimlik Sağlayıcısı",
|
||||
"identityProviderMessage": "Hesabınızı yapılandırdığınız kimlik sağlayıcılarıyla bağlamak için",
|
||||
"socialLogin": "Sosyal Giriş",
|
||||
"userDefined": "Kullanıcı tanımlı",
|
||||
"removeAccess": "Erişimi Kaldır",
|
||||
"removeAccessMessage": "Bu uygulama hesabını kullanmak istiyorsanız tekrar erişim vermeniz gerekir.",
|
||||
"authenticatorStatusMessage": "İki faktörlü kimlik doğrulama aktif",
|
||||
"authenticatorFinishSetUpTitle": "İki Faktörlü Doğrulama",
|
||||
"authenticatorFinishSetUpMessage": "Keycloak hesabınızda her oturum açtığınızda, iki faktörlü bir doğrulama kodu girmeniz istenecektir.",
|
||||
"authenticatorSubTitle": "İki Faktörlü Kimlik Doğrulamayı Ayarlama",
|
||||
"authenticatorSubMessage":
|
||||
"Hesabınızın güvenliğini artırmak için mevcut iki faktörlü kimlik doğrulama yöntemlerinden en az birini etkinleştirin.",
|
||||
"authenticatorMobileTitle": "Mobil Kimlik Doğrulayıcı",
|
||||
"authenticatorMobileMessage": "Doğrulama kodlarını iki faktörlü kimlik doğrulama olarak almak için mobil Doğrulayıcı'yı kullanın.",
|
||||
"authenticatorMobileFinishSetUpMessage": "Doğrulayıcı, telefonunuza bağlı.",
|
||||
"authenticatorActionSetup": "Kur",
|
||||
"authenticatorSMSTitle": "SMS Kodu",
|
||||
"authenticatorSMSMessage": "Keycloak, doğrulama kodunu telefonunuza iki faktörlü kimlik doğrulaması olarak gönderecektir.",
|
||||
"authenticatorSMSFinishSetUpMessage": "Kısa mesajlar gönderilir",
|
||||
"authenticatorDefaultStatus": "Varsayılan",
|
||||
"authenticatorChangePhone": "Telefon Numarasını Değiştir",
|
||||
"authenticatorBackupCodesTitle": "Yedekleme Kodları",
|
||||
"authenticatorBackupCodesMessage": "8 haneli yedek kodlarınızı alın",
|
||||
"authenticatorBackupCodesFinishSetUpMessage": "Şu anda 12 haneli yedek kod oluşturuldu. Her biri bir kez kullanılabilir.",
|
||||
"authenticatorMobileSetupTitle": "Mobil Kimlik Doğrulama Kurulumu",
|
||||
"smscodeIntroMessage": "Telefon numaranızı girin ve telefonunuza bir doğrulama kodu gönderilecektir.",
|
||||
"mobileSetupStep1": "Telefonunuza bir kimlik doğrulama uygulaması yükleyin. Burada listelenen uygulamalar desteklenmektedir.",
|
||||
"mobileSetupStep2": "Uygulamayı açın ve barkodu tarayın.",
|
||||
"mobileSetupStep3": "Uygulama tarafından sağlanan tek seferlik kodu girin ve kurulumu tamamlamak için Kaydet'e tıklayın.",
|
||||
"scanBarCode": "Barkodu taramak ister misiniz?",
|
||||
"enterBarCode": "Tek seferlik kodu girin",
|
||||
"doCopy": "Kopyala",
|
||||
"doFinish": "Bitir",
|
||||
"authenticatorSMSCodeSetupTitle": "SMS Kodu Kurulumu",
|
||||
"chooseYourCountry": "Ülkenizi seçin",
|
||||
"enterYourPhoneNumber": "Telefon numaranızı girin",
|
||||
"sendVerficationCode": "Doğrulama kodu Gönder",
|
||||
"enterYourVerficationCode": "Onaylama kodunu girin",
|
||||
"authenticatorBackupCodesSetupTitle": "Yedekleme Kodları Kurulumu",
|
||||
"backupcodesIntroMessage":
|
||||
"Telefonunuza erişimi kaybederseniz, yine de yedek kodlar aracılığıyla hesabınıza giriş yapabilirsiniz. Onları güvenli ve erişilebilir bir yerde saklayın.",
|
||||
"realmName": "Realm",
|
||||
"doDownload": "İndir",
|
||||
"doPrint": "Yazdır",
|
||||
"backupCodesTips-1": "Her yedek kod bir kez kullanılabilir.",
|
||||
"backupCodesTips-2": "Bu kodlar üzerinde oluşturuldu",
|
||||
"generateNewBackupCodes": "Yeni Yedekleme Kodları Oluştur",
|
||||
"backupCodesTips-3": "Yeni yedek kodlar oluşturduğunuzda, mevcut kodlar artık çalışmayacaktır.",
|
||||
"backtoAuthenticatorPage": "Kimlik Doğrulayıcı Sayfasına Geri Dön",
|
||||
"resources": "Kaynaklar",
|
||||
"sharedwithMe": "Benimle paylaştı",
|
||||
"share": "Paylaşım",
|
||||
"sharedwith": "İle paylaştı",
|
||||
"accessPermissions": "Erişim İzinleri",
|
||||
"permissionRequests": "İzin İstekleri",
|
||||
"approve": "Onayla",
|
||||
"approveAll": "Tümünü onayla",
|
||||
"people": "İnsanlar",
|
||||
"perPage": "Sayfa başına",
|
||||
"currentPage": "Geçerli sayfa",
|
||||
"sharetheResource": "Kaynağı paylaş",
|
||||
"group": "Grup",
|
||||
"selectPermission": "İzin Seç",
|
||||
"addPeople": "Kaynağınızı paylaşmak için kullanıcı ekleyin",
|
||||
"addTeam": "Kaynağınızı paylaşmak için ekip ekleyin",
|
||||
"myPermissions": "İzinlerim",
|
||||
"waitingforApproval": "Onay bekleniyor",
|
||||
"anyPermission": "Herhangi bir izin"
|
||||
};
|
||||
|
||||
export default messages;
|
||||
/* spell-checker: enable */
|
155
src/lib/i18n/generated_messages/11.0.3/account/zh-CN.ts
Normal file
155
src/lib/i18n/generated_messages/11.0.3/account/zh-CN.ts
Normal file
@ -0,0 +1,155 @@
|
||||
//This code was automatically generated by running dist/bin/generate-i18n-messages.js
|
||||
//PLEASE DO NOT EDIT MANUALLY
|
||||
|
||||
/* spell-checker: disable */
|
||||
const messages = {
|
||||
"doSave": "保存",
|
||||
"doCancel": "取消",
|
||||
"doLogOutAllSessions": "登出所有会话",
|
||||
"doRemove": "删除",
|
||||
"doAdd": "添加",
|
||||
"doSignOut": "登出",
|
||||
"editAccountHtmlTitle": "编辑账户",
|
||||
"federatedIdentitiesHtmlTitle": "链接的身份",
|
||||
"accountLogHtmlTitle": "账户日志",
|
||||
"changePasswordHtmlTitle": "更改密码",
|
||||
"sessionsHtmlTitle": "会话",
|
||||
"accountManagementTitle": "Keycloak账户管理",
|
||||
"authenticatorTitle": "认证者",
|
||||
"applicationsHtmlTitle": "应用",
|
||||
"authenticatorCode": "一次性认证码",
|
||||
"email": "电子邮件",
|
||||
"firstName": "名",
|
||||
"givenName": "姓",
|
||||
"fullName": "全名",
|
||||
"lastName": "姓",
|
||||
"familyName": "姓",
|
||||
"password": "密码",
|
||||
"passwordConfirm": "确认",
|
||||
"passwordNew": "新密码",
|
||||
"username": "用户名",
|
||||
"address": "地址",
|
||||
"street": "街道",
|
||||
"locality": "城市住所",
|
||||
"region": "省,自治区,直辖市",
|
||||
"postal_code": "邮政编码",
|
||||
"country": "国家",
|
||||
"emailVerified": "验证过的Email",
|
||||
"gssDelegationCredential": "GSS Delegation Credential",
|
||||
"role_admin": "管理员",
|
||||
"role_realm-admin": "域管理员",
|
||||
"role_create-realm": "创建域",
|
||||
"role_view-realm": "查看域",
|
||||
"role_view-users": "查看用户",
|
||||
"role_view-applications": "查看应用",
|
||||
"role_view-clients": "查看客户",
|
||||
"role_view-events": "查看事件",
|
||||
"role_view-identity-providers": "查看身份提供者",
|
||||
"role_manage-realm": "管理域",
|
||||
"role_manage-users": "管理用户",
|
||||
"role_manage-applications": "管理应用",
|
||||
"role_manage-identity-providers": "管理身份提供者",
|
||||
"role_manage-clients": "管理客户",
|
||||
"role_manage-events": "管理事件",
|
||||
"role_view-profile": "查看用户信息",
|
||||
"role_manage-account": "管理账户",
|
||||
"role_read-token": "读取 token",
|
||||
"role_offline-access": "离线访问",
|
||||
"role_uma_authorization": "获取授权",
|
||||
"client_account": "账户",
|
||||
"client_security-admin-console": "安全管理终端",
|
||||
"client_admin-cli": "管理命令行",
|
||||
"client_realm-management": "域管理",
|
||||
"client_broker": "代理",
|
||||
"requiredFields": "必填项",
|
||||
"allFieldsRequired": "所有项必填",
|
||||
"backToApplication": "« 回到应用",
|
||||
"backTo": "回到 {0}",
|
||||
"date": "日期",
|
||||
"event": "事件",
|
||||
"ip": "IP",
|
||||
"client": "客户端",
|
||||
"clients": "客户端",
|
||||
"details": "详情",
|
||||
"started": "开始",
|
||||
"lastAccess": "最后一次访问",
|
||||
"expires": "过期时间",
|
||||
"applications": "应用",
|
||||
"account": "账户",
|
||||
"federatedIdentity": "关联身份",
|
||||
"authenticator": "认证方",
|
||||
"sessions": "会话",
|
||||
"log": "日志",
|
||||
"application": "应用",
|
||||
"availablePermissions": "可用权限",
|
||||
"grantedPermissions": "授予权限",
|
||||
"grantedPersonalInfo": "授权的个人信息",
|
||||
"additionalGrants": "可授予的权限",
|
||||
"action": "操作",
|
||||
"inResource": "in",
|
||||
"fullAccess": "所有权限",
|
||||
"offlineToken": "离线 token",
|
||||
"revoke": "收回授权",
|
||||
"configureAuthenticators": "配置的认证者",
|
||||
"mobile": "手机",
|
||||
"totpStep1":
|
||||
'在你的设备上安装 <a href="https://fedorahosted.org/freeotp/" target="_blank">FreeOTP</a> 或者 Google Authenticator.两个应用可以从 <a href="https://play.google.com">Google Play</a> 和 Apple App Store下载。',
|
||||
"totpStep2": "打开应用扫描二维码输入验证码",
|
||||
"totpStep3": "输入应用提供的一次性验证码单击保存",
|
||||
"missingUsernameMessage": "请指定用户名",
|
||||
"missingFirstNameMessage": "请指定名",
|
||||
"invalidEmailMessage": "无效的电子邮箱地址",
|
||||
"missingLastNameMessage": "请指定姓",
|
||||
"missingEmailMessage": "请指定邮件地址",
|
||||
"missingPasswordMessage": "请输入密码",
|
||||
"notMatchPasswordMessage": "密码不匹配",
|
||||
"missingTotpMessage": "请指定认证者代码",
|
||||
"invalidPasswordExistingMessage": "无效的旧密码",
|
||||
"invalidPasswordConfirmMessage": "确认密码不相符",
|
||||
"invalidTotpMessage": "无效的认证码",
|
||||
"usernameExistsMessage": "用户名已经存在",
|
||||
"emailExistsMessage": "电子邮箱已经存在",
|
||||
"readOnlyUserMessage": "无法修改账户,因为它是只读的。",
|
||||
"readOnlyPasswordMessage": "不可以更该账户因为它是只读的。",
|
||||
"successTotpMessage": "手机认证者配置完毕",
|
||||
"successTotpRemovedMessage": "手机认证者已删除",
|
||||
"successGrantRevokedMessage": "授权成功回收",
|
||||
"accountUpdatedMessage": "您的账户已经更新",
|
||||
"accountPasswordUpdatedMessage": "您的密码已经修改",
|
||||
"missingIdentityProviderMessage": "身份提供者未指定",
|
||||
"invalidFederatedIdentityActionMessage": "无效或者缺少操作",
|
||||
"identityProviderNotFoundMessage": "指定的身份提供者未找到",
|
||||
"federatedIdentityLinkNotActiveMessage": "这个身份不再使用了。",
|
||||
"federatedIdentityRemovingLastProviderMessage": "你不可以移除最后一个身份提供者因为你没有设置密码",
|
||||
"identityProviderRedirectErrorMessage": "尝试重定向到身份提供商失败",
|
||||
"identityProviderRemovedMessage": "身份提供商成功删除",
|
||||
"identityProviderAlreadyLinkedMessage": "链接的身份 {0} 已经连接到已有用户。",
|
||||
"staleCodeAccountMessage": "页面过期。请再试一次。",
|
||||
"consentDenied": "不同意",
|
||||
"accountDisabledMessage": "账户已经关闭,请联系管理员",
|
||||
"accountTemporarilyDisabledMessage": "账户暂时关闭,请联系管理员或稍后再试。",
|
||||
"invalidPasswordMinLengthMessage": "无效的密码:最短长度 {0}.",
|
||||
"invalidPasswordMinLowerCaseCharsMessage": "无效的密码: 至少包含 {0} 小写字母。",
|
||||
"invalidPasswordMinDigitsMessage": "无效的密码: 至少包含 {0} 数字。",
|
||||
"invalidPasswordMinUpperCaseCharsMessage": "无效的密码: 至少包含 {0} 大写字母",
|
||||
"invalidPasswordMinSpecialCharsMessage": "无效的密码: 至少包含 {0} 个特殊字符",
|
||||
"invalidPasswordNotUsernameMessage": "无效的密码: 不能与用户名相同",
|
||||
"invalidPasswordRegexPatternMessage": "无效的密码: 无法与正则表达式匹配",
|
||||
"invalidPasswordHistoryMessage": "无效的密码: 不能与之前的{0} 个旧密码相同",
|
||||
"locale_ca": "Català",
|
||||
"locale_de": "Deutsch",
|
||||
"locale_en": "English",
|
||||
"locale_es": "Español",
|
||||
"locale_fr": "Français",
|
||||
"locale_it": "Italian",
|
||||
"locale_ja": "日本語",
|
||||
"locale_nl": "Nederlands",
|
||||
"locale_no": "Norsk",
|
||||
"locale_lt": "Lietuvių",
|
||||
"locale_pt-BR": "Português (Brasil)",
|
||||
"locale_ru": "Русский",
|
||||
"locale_zh-CN": "中文简体"
|
||||
};
|
||||
|
||||
export default messages;
|
||||
/* spell-checker: enable */
|
17
src/lib/i18n/generated_messages/11.0.3/admin/ca.ts
Normal file
17
src/lib/i18n/generated_messages/11.0.3/admin/ca.ts
Normal file
@ -0,0 +1,17 @@
|
||||
//This code was automatically generated by running dist/bin/generate-i18n-messages.js
|
||||
//PLEASE DO NOT EDIT MANUALLY
|
||||
|
||||
/* spell-checker: disable */
|
||||
const messages = {
|
||||
"invalidPasswordHistoryMessage": "Contrasenya incorrecta: no pot ser igual a cap de les últimes {0} contrasenyes.",
|
||||
"invalidPasswordMinDigitsMessage": "Contraseña incorrecta: debe contener al menos {0} caracteres numéricos.",
|
||||
"invalidPasswordMinLengthMessage": "Contrasenya incorrecta: longitud mínima {0}.",
|
||||
"invalidPasswordMinLowerCaseCharsMessage": "Contrasenya incorrecta: ha de contenir almenys {0} lletres minúscules.",
|
||||
"invalidPasswordMinSpecialCharsMessage": "Contrasenya incorrecta: ha de contenir almenys {0} caràcters especials.",
|
||||
"invalidPasswordMinUpperCaseCharsMessage": "Contrasenya incorrecta: ha de contenir almenys {0} lletres majúscules.",
|
||||
"invalidPasswordNotUsernameMessage": "Contrasenya incorrecta: no pot ser igual al nom d'usuari.",
|
||||
"invalidPasswordRegexPatternMessage": "Contrasenya incorrecta: no compleix l'expressió regular."
|
||||
};
|
||||
|
||||
export default messages;
|
||||
/* spell-checker: enable */
|
19
src/lib/i18n/generated_messages/11.0.3/admin/de.ts
Normal file
19
src/lib/i18n/generated_messages/11.0.3/admin/de.ts
Normal file
@ -0,0 +1,19 @@
|
||||
//This code was automatically generated by running dist/bin/generate-i18n-messages.js
|
||||
//PLEASE DO NOT EDIT MANUALLY
|
||||
|
||||
/* spell-checker: disable */
|
||||
const messages = {
|
||||
"invalidPasswordMinLengthMessage": "Ungültiges Passwort: muss mindestens {0} Zeichen beinhalten.",
|
||||
"invalidPasswordMinLowerCaseCharsMessage": "Ungültiges Passwort: muss mindestens {0} Kleinbuchstaben beinhalten.",
|
||||
"invalidPasswordMinDigitsMessage": "Ungültiges Passwort: muss mindestens {0} Ziffern beinhalten.",
|
||||
"invalidPasswordMinUpperCaseCharsMessage": "Ungültiges Passwort: muss mindestens {0} Großbuchstaben beinhalten.",
|
||||
"invalidPasswordMinSpecialCharsMessage": "Ungültiges Passwort: muss mindestens {0} Sonderzeichen beinhalten.",
|
||||
"invalidPasswordNotUsernameMessage": "Ungültiges Passwort: darf nicht identisch mit dem Benutzernamen sein.",
|
||||
"invalidPasswordRegexPatternMessage": "Ungültiges Passwort: stimmt nicht mit Regex-Muster überein.",
|
||||
"invalidPasswordHistoryMessage": "Ungültiges Passwort: darf nicht identisch mit einem der letzten {0} Passwörter sein.",
|
||||
"invalidPasswordBlacklistedMessage": "Ungültiges Passwort: Passwort ist zu bekannt und auf der schwarzen Liste.",
|
||||
"invalidPasswordGenericMessage": "Ungültiges Passwort: neues Passwort erfüllt die Passwort-Anforderungen nicht."
|
||||
};
|
||||
|
||||
export default messages;
|
||||
/* spell-checker: enable */
|
44
src/lib/i18n/generated_messages/11.0.3/admin/en.ts
Normal file
44
src/lib/i18n/generated_messages/11.0.3/admin/en.ts
Normal file
@ -0,0 +1,44 @@
|
||||
//This code was automatically generated by running dist/bin/generate-i18n-messages.js
|
||||
//PLEASE DO NOT EDIT MANUALLY
|
||||
|
||||
/* spell-checker: disable */
|
||||
const messages = {
|
||||
"invalidPasswordMinLengthMessage": "Invalid password: minimum length {0}.",
|
||||
"invalidPasswordMinLowerCaseCharsMessage": "Invalid password: must contain at least {0} lower case characters.",
|
||||
"invalidPasswordMinDigitsMessage": "Invalid password: must contain at least {0} numerical digits.",
|
||||
"invalidPasswordMinUpperCaseCharsMessage": "Invalid password: must contain at least {0} upper case characters.",
|
||||
"invalidPasswordMinSpecialCharsMessage": "Invalid password: must contain at least {0} special characters.",
|
||||
"invalidPasswordNotUsernameMessage": "Invalid password: must not be equal to the username.",
|
||||
"invalidPasswordRegexPatternMessage": "Invalid password: fails to match regex pattern(s).",
|
||||
"invalidPasswordHistoryMessage": "Invalid password: must not be equal to any of last {0} passwords.",
|
||||
"invalidPasswordBlacklistedMessage": "Invalid password: password is blacklisted.",
|
||||
"invalidPasswordGenericMessage": "Invalid password: new password does not match password policies.",
|
||||
"ldapErrorInvalidCustomFilter": 'Custom configured LDAP filter does not start with "(" or does not end with ")".',
|
||||
"ldapErrorConnectionTimeoutNotNumber": "Connection Timeout must be a number",
|
||||
"ldapErrorReadTimeoutNotNumber": "Read Timeout must be a number",
|
||||
"ldapErrorMissingClientId": "Client ID needs to be provided in config when Realm Roles Mapping is not used.",
|
||||
"ldapErrorCantPreserveGroupInheritanceWithUIDMembershipType": "Not possible to preserve group inheritance and use UID membership type together.",
|
||||
"ldapErrorCantWriteOnlyForReadOnlyLdap": "Can not set write only when LDAP provider mode is not WRITABLE",
|
||||
"ldapErrorCantWriteOnlyAndReadOnly": "Can not set write-only and read-only together",
|
||||
"ldapErrorCantEnableStartTlsAndConnectionPooling": "Can not enable both StartTLS and connection pooling.",
|
||||
"ldapErrorCantEnableUnsyncedAndImportOff": "Can not disable Importing users when LDAP provider mode is UNSYNCED",
|
||||
"ldapErrorMissingGroupsPathGroup": "Groups path group does not exist - please create the group on specified path first",
|
||||
"clientRedirectURIsFragmentError": "Redirect URIs must not contain an URI fragment",
|
||||
"clientRootURLFragmentError": "Root URL must not contain an URL fragment",
|
||||
"clientRootURLIllegalSchemeError": "Root URL uses an illegal scheme",
|
||||
"clientBaseURLIllegalSchemeError": "Base URL uses an illegal scheme",
|
||||
"clientRedirectURIsIllegalSchemeError": "A redirect URI uses an illegal scheme",
|
||||
"clientBaseURLInvalid": "Base URL is not a valid URL",
|
||||
"clientRootURLInvalid": "Root URL is not a valid URL",
|
||||
"clientRedirectURIsInvalid": "A redirect URI is not a valid URI",
|
||||
"pairwiseMalformedClientRedirectURI": "Client contained an invalid redirect URI.",
|
||||
"pairwiseClientRedirectURIsMissingHost": "Client redirect URIs must contain a valid host component.",
|
||||
"pairwiseClientRedirectURIsMultipleHosts":
|
||||
"Without a configured Sector Identifier URI, client redirect URIs must not contain multiple host components.",
|
||||
"pairwiseMalformedSectorIdentifierURI": "Malformed Sector Identifier URI.",
|
||||
"pairwiseFailedToGetRedirectURIs": "Failed to get redirect URIs from the Sector Identifier URI.",
|
||||
"pairwiseRedirectURIsMismatch": "Client redirect URIs does not match redirect URIs fetched from the Sector Identifier URI."
|
||||
};
|
||||
|
||||
export default messages;
|
||||
/* spell-checker: enable */
|
17
src/lib/i18n/generated_messages/11.0.3/admin/es.ts
Normal file
17
src/lib/i18n/generated_messages/11.0.3/admin/es.ts
Normal file
@ -0,0 +1,17 @@
|
||||
//This code was automatically generated by running dist/bin/generate-i18n-messages.js
|
||||
//PLEASE DO NOT EDIT MANUALLY
|
||||
|
||||
/* spell-checker: disable */
|
||||
const messages = {
|
||||
"invalidPasswordMinLengthMessage": "Contraseña incorrecta: longitud mínima {0}.",
|
||||
"invalidPasswordMinLowerCaseCharsMessage": "Contraseña incorrecta: debe contener al menos {0} letras minúsculas.",
|
||||
"invalidPasswordMinDigitsMessage": "Contraseña incorrecta: debe contener al menos {0} caracteres numéricos.",
|
||||
"invalidPasswordMinUpperCaseCharsMessage": "Contraseña incorrecta: debe contener al menos {0} letras mayúsculas.",
|
||||
"invalidPasswordMinSpecialCharsMessage": "Contraseña incorrecta: debe contener al menos {0} caracteres especiales.",
|
||||
"invalidPasswordNotUsernameMessage": "Contraseña incorrecta: no puede ser igual al nombre de usuario.",
|
||||
"invalidPasswordRegexPatternMessage": "Contraseña incorrecta: no cumple la expresión regular.",
|
||||
"invalidPasswordHistoryMessage": "Contraseña incorrecta: no puede ser igual a ninguna de las últimas {0} contraseñas."
|
||||
};
|
||||
|
||||
export default messages;
|
||||
/* spell-checker: enable */
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user