Compare commits

...

23 Commits

Author SHA1 Message Date
59db202fe4 Update changelog v0.3.23 2021-04-08 15:43:34 +00:00
09927afd43 Merge branch 'main' of https://github.com/InseeFrLab/keycloakify into main 2021-04-08 17:40:54 +02:00
13c6122b9b Bump version (changelog ignore) 2021-04-08 17:06:26 +02:00
1bb19f65a2 Allow to lazily load therms 2021-04-08 17:06:09 +02:00
918a80cfb6 Update changelog v0.3.22 2021-04-08 13:48:41 +00:00
ed7d5eabcb Bump version (changelog ignore) 2021-04-08 15:43:12 +02:00
2795109162 update powerhooks 2021-04-08 15:42:41 +02:00
966f277628 Support terms and condition 2021-04-08 15:41:40 +02:00
36d60411f9 Fix info.ftl 2021-04-08 12:54:29 +02:00
60fe33f3fd Merge branch 'main' of https://github.com/InseeFrLab/keycloakify into main 2021-04-08 12:20:14 +02:00
1df685df92 For useKcMessage we prefer returning callbacks with a changing references 2021-04-08 12:20:06 +02:00
7894d95ace Update changelog v0.3.21 2021-04-04 19:20:27 +00:00
a8b4493aa1 update yarn.lock (changelog ignore) 2021-04-04 21:17:35 +02:00
715a7399cf Merge branch 'main' of https://github.com/InseeFrLab/keycloakify into main 2021-04-04 21:12:39 +02:00
a1e59bae23 Update powerhooks 2021-04-04 21:12:30 +02:00
b0819314a1 Update changelog v0.3.20 2021-04-01 22:37:41 +00:00
0099442543 Merge branch 'main' of https://github.com/InseeFrLab/keycloakify into main 2021-04-01 23:43:14 +02:00
66a0b07228 Bump version (changelog ignore) 2021-04-01 23:43:08 +02:00
85f9544754 Always catch freemarker errors 2021-04-01 23:42:31 +02:00
2f16a09ab8 Update changelog v0.3.19 2021-04-01 15:21:10 +00:00
183ae98c30 Merge branch 'main' of https://github.com/InseeFrLab/keycloakify into main 2021-04-01 17:16:01 +02:00
ba15e63879 Bump version (changelog ignore) 2021-04-01 17:15:55 +02:00
654277feda Fix previous release 2021-04-01 17:15:28 +02:00
24 changed files with 703 additions and 165 deletions

View File

@ -1,3 +1,26 @@
### **0.3.23** (2021-04-08)
- Allow to lazily load therms
### **0.3.22** (2021-04-08)
- update powerhooks
- Support terms and condition
- Fix info.ftl
- For useKcMessage we prefer returning callbacks with a changing references
### **0.3.21** (2021-04-04)
- Update powerhooks
### **0.3.20** (2021-04-01)
- Always catch freemarker errors
### **0.3.19** (2021-04-01)
- Fix previous release
### **0.3.18** (2021-04-01) ### **0.3.18** (2021-04-01)
- Fix error.ftt, Adopt best effort strategy to convert ftl values into JS - Fix error.ftt, Adopt best effort strategy to convert ftl values into JS

View File

@ -1,6 +1,6 @@
{ {
"name": "keycloakify", "name": "keycloakify",
"version": "0.3.18", "version": "0.3.23",
"description": "Keycloak theme generator for Reacts app", "description": "Keycloak theme generator for Reacts app",
"repository": { "repository": {
"type": "git", "type": "git",
@ -53,8 +53,8 @@
"evt": "2.0.0-beta.15", "evt": "2.0.0-beta.15",
"minimal-polyfills": "^2.1.6", "minimal-polyfills": "^2.1.6",
"path": "^0.12.7", "path": "^0.12.7",
"powerhooks": "^0.0.27", "powerhooks": "^0.0.36",
"scripting-tools": "^0.19.13", "scripting-tools": "^0.19.13",
"tss-react": "^0.0.11" "tss-react": "^0.0.12"
} }
} }

View File

@ -1,110 +1,261 @@
<script>const _= <script>const _=
{ {
"url": (function (){ "url": {
"loginAction": (function (){
<#if !url?has_content> <#attempt>
return undefined; return "${url.loginAction?no_esc}";
</#if> <#recover>
</#attempt>
return { })(),
"loginAction": "${(url.loginAction!'')?no_esc}", "resourcesPath": (function (){
"resourcesPath": "${(url.resourcesPath!'')?no_esc}",
"resourcesCommonPath": "${(url.resourcesCommonPath!'')?no_esc}",
"loginRestartFlowUrl": "${(url.loginRestartFlowUrl!'')?no_esc}",
"loginUrl": "${(url.loginUrl!'')?no_esc}"
};
})(), <#attempt>
return "${url.resourcesPath?no_esc}";
<#recover>
</#attempt>
})(),
"resourcesCommonPath": (function (){
<#attempt>
return "${url.resourcesCommonPath?no_esc}";
<#recover>
</#attempt>
})(),
"loginRestartFlowUrl": (function (){
<#attempt>
return "${url.loginRestartFlowUrl?no_esc}";
<#recover>
</#attempt>
})(),
"loginUrl": (function (){
<#attempt>
return "${url.loginUrl?no_esc}";
<#recover>
</#attempt>
})()
},
"realm": { "realm": {
"displayName": "${realm.displayName!''}" || undefined, "displayName": (function (){
"displayNameHtml": "${realm.displayNameHtml!''}" || undefined,
"internationalizationEnabled": ${(realm.internationalizationEnabled!false)?c}, <#attempt>
"registrationEmailAsUsername": ${(realm.registrationEmailAsUsername!false)?c}, return "${realm.displayName!''}" || undefined;
<#recover>
</#attempt>
})(),
"displayNameHtml": (function (){
<#attempt>
return "${realm.displayNameHtml!''}" || undefined;
<#recover>
</#attempt>
})(),
"internationalizationEnabled": (function (){
<#attempt>
return ${realm.internationalizationEnabled?c};
<#recover>
</#attempt>
})(),
"registrationEmailAsUsername": (function (){
<#attempt>
return ${realm.registrationEmailAsUsername?c};
<#recover>
</#attempt>
})()
}, },
"locale": (function (){ "locale": (function (){
<#if !realm.internationalizationEnabled> <#attempt>
return undefined; <#if realm.internationalizationEnabled>
</#if>
return { return {
"supported": (function(){ "supported": (function(){
<#if !realm.internationalizationEnabled> var out= [];
return undefined;
</#if>
var out= []; <#attempt>
<#list locale.supported as lng>
out.push({
"url": (function (){
<#list locale.supported as lng> <#attempt>
out.push({ return "${lng.url?no_esc}";
"url": "${lng.url?no_esc}", <#recover>
"label": "${lng.label}", </#attempt>
"languageTag": "${lng.languageTag}"
});
</#list>
return out; })(),
"label": (function (){
})(), <#attempt>
"current": "${locale.current}" return "${lng.label}";
}; <#recover>
</#attempt>
})(),
"languageTag": (function (){
<#attempt>
return "${lng.languageTag}";
<#recover>
</#attempt>
})()
});
</#list>
<#recover>
</#attempt>
return out;
})(),
"current": (function (){
<#attempt>
return "${locale.current}";
<#recover>
</#attempt>
})()
};
</#if>
<#recover>
</#attempt>
})(), })(),
"auth": (function (){ "auth": (function (){
<#if !auth?has_content> <#attempt>
return undefined; <#if auth?has_content>
var out= {
"showUsername": (function (){
<#attempt>
return ${auth.showUsername()?c};
<#recover>
</#attempt>
})(),
"showResetCredentials": (function (){
<#attempt>
return ${auth.showResetCredentials()?c};
<#recover>
</#attempt>
})(),
"showTryAnotherWayLink": (function(){
<#attempt>
return ${auth.showTryAnotherWayLink()?c};
<#recover>
</#attempt>
})()
};
<#attempt>
<#if auth.showUsername() && !auth.showResetCredentials()>
Object.assign(
out,
{
"attemptedUsername": (function (){
<#attempt>
return "${auth.attemptedUsername}";
<#recover>
</#attempt>
})()
}
);
</#if>
<#recover>
</#attempt>
return out;
</#if> </#if>
<#recover>
var out= { </#attempt>
"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;
})(), })(),
"scripts": (function(){ "scripts": (function(){
var out = []; var out = [];
<#if scripts??> <#attempt>
<#list scripts as script> <#if scripts??>
out.push("${script}"); <#attempt>
</#list> <#list scripts as script>
</#if> out.push((function (){
<#attempt>
return "${script}";
<#recover>
</#attempt>
})());
</#list>
<#recover>
</#attempt>
</#if>
<#recover>
</#attempt>
return out; return out;
})(), })(),
"message": (function (){ "message": (function (){
<#if !message?has_content> <#attempt>
return undefined; <#if message?has_content>
</#if>
return {  return { 
"type": "${message.type}", "type": (function (){
"summary": String.htmlUnescape("${message.summary}")
}; <#attempt>
return "${message.type}";
<#recover>
</#attempt>
})(),
"summary": (function (){
<#attempt>
return String.htmlUnescape("${message.summary}");
<#recover>
</#attempt>
})()
};
</#if>
<#recover>
</#attempt>
})(), })(),
"isAppInitiatedAction": (function (){ "isAppInitiatedAction": (function (){
<#if isAppInitiatedAction??> <#attempt>
return true; <#if isAppInitiatedAction??>
</#if> return true;
</#if>
<#recover>
</#attempt>
return false; return false;
})() })()

View File

@ -2,13 +2,21 @@
{ {
"client": (function (){ "client": (function (){
<#attempt>
<#if client??> <#if client??>
return { return {
"baseUrl": "${(client.baseUrl!'')?no_esc}" || undefined "baseUrl": (function (){
<#attempt>
return "${(client.baseUrl!'')?no_esc}" || undefined;
<#recover>
</#attempt>
})()
}; };
</#if> </#if>
<#recover>
return undefined; </#attempt>
})() })()
} }

View File

@ -10,7 +10,11 @@ import fs from "fs";
import { join as pathJoin } from "path"; import { join as pathJoin } from "path";
import { objectKeys } from "evt/tools/typeSafety/objectKeys"; import { objectKeys } from "evt/tools/typeSafety/objectKeys";
export const pageIds = ["login.ftl", "register.ftl", "info.ftl", "error.ftl", "login-reset-password.ftl", "login-verify-email.ftl"] as const; export const pageIds = [
"login.ftl", "register.ftl", "info.ftl",
"error.ftl", "login-reset-password.ftl",
"login-verify-email.ftl", "terms.ftl"
] as const;
export type PageId = typeof pageIds[number]; export type PageId = typeof pageIds[number];

View File

@ -1,37 +1,82 @@
<script>const _= <script>const _=
{ {
"messageHeader": "${messageHeader!''}" || undefined, "messageHeader": (function (){
<#attempt>
return "${messageHeader!''}" || undefined;
<#recover>
</#attempt>
})(),
"requiredActions": (function (){ "requiredActions": (function (){
<#attempt>
<#if requiredActions??> <#if requiredActions??>
var out =[]; var out =[];
<#attempt>
<#list requiredActions> <#list requiredActions>
<#attempt>
<#items as reqActionItem> <#items as reqActionItem>
out.push("${reqActionItem}"); out.push((function (){
</#items></b>
<#attempt>
return "${reqActionItem}";
<#recover>
</#attempt>
})());
</#items>
<#recover>
</#attempt>
</#list> </#list>
<#recover>
</#attempt>
return out; return out;
<#else> </#if>
<#recover>
return undefined; </#attempt>
})(), })(),
"skipLink": (function (){ "skipLink": (function (){
<#attempt>
<#if skipLink??> <#if skipLink??>
return true; return true;
</#if> </#if>
<#recover>
</#attempt>
return false; return false;
})(), })(),
"pageRedirectUri": "${(pageRedirectUri!'')?no_esc}" || undefined, "pageRedirectUri": (function (){
"actionUri": "${(actionUri!'')?no_esc}" || undefined,
<#attempt>
return "${(pageRedirectUri!'')?no_esc}" || undefined;
<#recover>
</#attempt>
})(),
"actionUri": (function (){
<#attempt>
return "${(actionUri!'')?no_esc}" || undefined;
<#recover>
</#attempt>
})(),
"client": { "client": {
"baseUrl": "${(client.baseUrl!'')?no_esc}" || undefined "baseUrl": (function(){
<#attempt>
return "${(client.baseUrl!'')?no_esc}" || undefined;
<#recover>
</#attempt>
})()
} }
} }
</script> </script>

View File

@ -1,7 +1,14 @@
<script>const _= <script>const _=
{ {
"realm": { "realm": {
"loginWithEmailAllowed": ${realm.loginWithEmailAllowed?c} "loginWithEmailAllowed": (function (){
},
<#attempt>
return ${realm.loginWithEmailAllowed?c};
<#recover>
</#attempt>
})()
}
} }
</script> </script>

View File

@ -1,83 +1,160 @@
<script>const _= <script>const _=
{ {
"url": { "url": {
"loginResetCredentialsUrl": "${url.loginResetCredentialsUrl?no_esc}", "loginResetCredentialsUrl": (function (){
"registrationUrl": "${url.registrationUrl?no_esc}" <#attempt>
return "${url.loginResetCredentialsUrl?no_esc}";
<#recover>
</#attempt>
})(),
"registrationUrl": (function (){
<#attempt>
return "${url.registrationUrl?no_esc}";
<#recover>
</#attempt>
})()
}, },
"realm": { "realm": {
"loginWithEmailAllowed": ${realm.loginWithEmailAllowed?c}, "loginWithEmailAllowed": (function(){
"rememberMe": ${realm.rememberMe?c}, <#attempt>
"password": ${realm.password?c}, return ${realm.loginWithEmailAllowed?c};
"resetPasswordAllowed": ${realm.resetPasswordAllowed?c}, <#recover>
"registrationAllowed": ${realm.registrationAllowed?c} </#attempt>
})(),
"rememberMe": (function (){
<#attempt>
return ${realm.rememberMe?c};
<#recover>
</#attempt>
})(),
"password": (function (){
<#attempt>
return ${realm.password?c};
<#recover>
</#attempt>
})(),
"resetPasswordAllowed": (function (){
<#attempt>
return ${realm.resetPasswordAllowed?c};
<#recover>
</#attempt>
})(),
"registrationAllowed": (function (){
<#attempt>
return ${realm.registrationAllowed?c};
<#recover>
</#attempt>
})()
}, },
"auth": (function (){ "auth": (function (){
<#attempt>
<#if auth?has_content> <#if auth?has_content>
var out= { return {
"selectedCredential": "${auth.selectedCredential!''}" || undefined "selectedCredential": (function (){
<#attempt>
return "${auth.selectedCredential!''}" || undefined;
<#recover>
</#attempt>
})()
}; };
return out;
</#if> </#if>
<#recover>
return undefined; </#attempt>
})(), })(),
"social": { "social": {
"displayInfo": ${social.displayInfo?c}, "displayInfo": (function (){
<#attempt>
return ${social.displayInfo?c};
<#recover>
</#attempt>
})(),
"providers": (()=>{ "providers": (()=>{
<#attempt>
<#if social.providers??> <#if social.providers??>
var out= []; var out= [];
<#attempt>
<#list social.providers as p> <#list social.providers as p>
out.push({ out.push({
"loginUrl": "${p.loginUrl?no_esc}", "loginUrl": (function (){
"alias": "${p.alias}", <#attempt>
"providerId": "${p.providerId}", return "${p.loginUrl?no_esc}";
"displayName": "${p.displayName}" <#recover>
</#attempt>
})(),
"alias": (function (){
<#attempt>
return "${p.alias}";
<#recover>
</#attempt>
})(),
"providerId": (function (){
<#attempt>
return "${p.providerId}";
<#recover>
</#attempt>
})(),
"displayName": (function (){
<#attempt>
return "${p.displayName}";
<#recover>
</#attempt>
})()
}); });
</#list> </#list>
<#recover>
</#attempt>
return out; return out;
</#if> </#if>
<#recover>
return undefined; </#attempt>
})() })()
}, },
"usernameEditDisabled": (function () { "usernameEditDisabled": (function () {
<#attempt>
<#if usernameEditDisabled??> <#if usernameEditDisabled??>
return true; return true;
</#if> </#if>
<#recover>
</#attempt>
return false; return false;
})(), })(),
"login": { "login": {
"username": "${login.username!''}" || undefined, "username": (function (){
<#attempt>
return "${login.username!''}" || undefined;
<#recover>
</#attempt>
})(),
"rememberMe": (function (){ "rememberMe": (function (){
<#attempt>
<#if login.rememberMe??> <#if login.rememberMe??>
return true; return true;
</#if> </#if>
<#recover>
</#attempt>
return false; return false;
})() })()
}, },
"registrationDisabled": (function (){ "registrationDisabled": (function (){
<#attempt>
<#if registrationDisabled??> <#if registrationDisabled??>
return true; return true;
</#if> </#if>
<#recover>
</#attempt>
return false; return false;
})() })()
} }
</script> </script>

View File

@ -1,46 +1,152 @@
<script>const _= <script>const _=
{ {
"url": { "url": {
"registrationAction": "${url.registrationAction?no_esc}" "registrationAction": (function (){
<#attempt>
return "${url.registrationAction?no_esc}";
<#recover>
</#attempt>
})()
}, },
"messagesPerField": { "messagesPerField": {
"printIfExists": function (key, x) { "printIfExists": function (key, x) {
switch(key){ switch(key){
case "userLabel": "${messagesPerField.printIfExists('userLabel','1')}" ? x : undefined; case "userLabel": return (function (){
case "username": "${messagesPerField.printIfExists('username','1')}" ? x : undefined;
case "email": "${messagesPerField.printIfExists('email','1')}" ? x : undefined; <#attempt>
case "firstName": "${messagesPerField.printIfExists('firstName','1')}" ? x : undefined; return "${messagesPerField.printIfExists('userLabel','1')}" ? x : undefined;
case "lastName": "${messagesPerField.printIfExists('lastName','1')}" ? x : undefined; <#recover>
case "password": "${messagesPerField.printIfExists('password','1')}" ? x : undefined; </#attempt>
case "password-confirm": "${messagesPerField.printIfExists('password-confirm','1')}" ? x : undefined;
})();
case "username": return (function (){
<#attempt>
return "${messagesPerField.printIfExists('username','1')}" ? x : undefined;
<#recover>
</#attempt>
})();
case "email": return (function (){
<#attempt>
return "${messagesPerField.printIfExists('email','1')}" ? x : undefined;
<#recover>
</#attempt>
})();
case "firstName": return (function (){
<#attempt>
return "${messagesPerField.printIfExists('firstName','1')}" ? x : undefined;
<#recover>
</#attempt>
})();
case "lastName": return (function (){
<#attempt>
return "${messagesPerField.printIfExists('lastName','1')}" ? x : undefined;
<#recover>
</#attempt>
})();
case "password": return (function (){
<#attempt>
return "${messagesPerField.printIfExists('password','1')}" ? x : undefined;
<#recover>
</#attempt>
})();
case "password-confirm": return (function (){
<#attempt>
return "${messagesPerField.printIfExists('password-confirm','1')}" ? x : undefined;
<#recover>
</#attempt>
})();
} }
} }
}, },
"register": { "register": {
"formData": { "formData": {
"firstName": "${register.formData.firstName!''}" || undefined, "firstName": (function (){
"displayName": "${register.formData.displayName!''}" || undefined,
"lastName": "${register.formData.lastName!''}" || undefined, <#attempt>
"email": "${register.formData.email!''}" || undefined, return "${register.formData.firstName!''}" || undefined;
"username": "${register.formData.username!''}" || undefined <#recover>
</#attempt>
})(),
"displayName": (function (){
<#attempt>
return "${register.formData.displayName!''}" || undefined;
<#recover>
</#attempt>
})(),
"lastName": (function (){
<#attempt>
return "${register.formData.lastName!''}" || undefined;
<#recover>
</#attempt>
})(),
"email": (function(){
<#attempt>
return "${register.formData.email!''}" || undefined;
<#recover>
</#attempt>
})(),
"username": (function (){
<#attempt>
return "${register.formData.username!''}" || undefined;
<#recover>
</#attempt>
})()
} }
}, },
"passwordRequired": (function (){ "passwordRequired": (function (){
<#attempt>
<#if passwordRequired??> <#if passwordRequired??>
return true; return true;
</#if> </#if>
<#recover>
</#attempt>
return false; return false;
})(), })(),
"recaptchaRequired": (function (){ "recaptchaRequired": (function (){
<#attempt>
<#if passwordRequired??> <#if passwordRequired??>
return true; return true;
</#if> </#if>
<#recover>
</#attempt>
return false; return false;
})(), })(),
"recaptchaSiteKey": "${recaptchaSiteKey!''}" || undefined "recaptchaSiteKey": (function (){
<#attempt>
return "${recaptchaSiteKey!''}" || undefined;
<#recover>
</#attempt>
})()
} }
</script> </script>

View File

@ -49,7 +49,7 @@ crawl(".").forEach(filePath => {
child_process.execSync(`rm -r ${tmpDirPath}`); child_process.execSync(`rm -r ${tmpDirPath}`);
const targetDirPath = pathJoin(getProjectRoot(), "src", "lib", "i18n", "generated_messages"); const targetDirPath = pathJoin(getProjectRoot(), "src", "lib", "i18n", "generated_kcMessages");
fs.mkdirSync(targetDirPath, { "recursive": true }); fs.mkdirSync(targetDirPath, { "recursive": true });
@ -65,7 +65,7 @@ Object.keys(record).forEach(pageType => {
'//PLEASE DO NOT EDIT MANUALLY', '//PLEASE DO NOT EDIT MANUALLY',
'', '',
'/* spell-checker: disable */', '/* spell-checker: disable */',
`export const messages= ${JSON.stringify(record[pageType], null, 2)} as const;`, `export const kcMessages= ${JSON.stringify(record[pageType], null, 2)};`,
'/* spell-checker: enable */' '/* spell-checker: enable */'
].join("\n"), "utf8") ].join("\n"), "utf8")
); );

View File

@ -53,13 +53,13 @@ export const Info = memo(({ kcContext, ...props }: { kcContext: KcContext.Info;
{ {
!skipLink && !skipLink &&
pageRedirectUri !== undefined ? pageRedirectUri !== undefined ?
<p><a href="${pageRedirectUri}">${(msg("backToApplication"))}</a></p> <p><a href={pageRedirectUri}>{(msg("backToApplication"))}</a></p>
: :
actionUri !== undefined ? actionUri !== undefined ?
<p><a href="${actionUri}">${msg("proceedWithAction")}</a></p> <p><a href={actionUri}>{msg("proceedWithAction")}</a></p>
: :
client.baseUrl !== undefined && client.baseUrl !== undefined &&
<p><a href="${client.baseUrl}">${msg("backToApplication")}</a></p> <p><a href={client.baseUrl}>{msg("backToApplication")}</a></p>
} }
</div> </div>

View File

@ -8,6 +8,7 @@ import { Info } from "./Info";
import { Error } from "./Error"; import { Error } from "./Error";
import { LoginResetPassword } from "./LoginResetPassword"; import { LoginResetPassword } from "./LoginResetPassword";
import { LoginVerifyEmail } from "./LoginVerifyEmail"; import { LoginVerifyEmail } from "./LoginVerifyEmail";
import { Terms } from "./Terms";
export const KcApp = memo(({ kcContext, ...props }: { kcContext: KcContext; } & KcProps ) => { export const KcApp = memo(({ kcContext, ...props }: { kcContext: KcContext; } & KcProps ) => {
switch (kcContext.pageId) { switch (kcContext.pageId) {
@ -17,5 +18,6 @@ export const KcApp = memo(({ kcContext, ...props }: { kcContext: KcContext; } &
case "error.ftl": return <Error {...{ kcContext, ...props }} />; case "error.ftl": return <Error {...{ kcContext, ...props }} />;
case "login-reset-password.ftl": return <LoginResetPassword {...{ kcContext, ...props }} />; case "login-reset-password.ftl": return <LoginResetPassword {...{ kcContext, ...props }} />;
case "login-verify-email.ftl": return <LoginVerifyEmail {...{ kcContext, ...props }} />; case "login-verify-email.ftl": return <LoginVerifyEmail {...{ kcContext, ...props }} />;
case "terms.ftl": return <Terms {...{ kcContext, ...props }}/>;
} }
}); });

View File

@ -0,0 +1,57 @@
import { memo } from "react";
import { Template } from "./Template";
import type { KcProps } from "./KcProps";
import type { KcContext } from "../KcContext";
import { useKcMessage } from "../i18n/useKcMessage";
import { cx } from "tss-react";
export const Terms = memo(({ kcContext, ...props }: { kcContext: KcContext.Terms; } & KcProps) => {
const { msg, msgStr } = useKcMessage();
const { url } = kcContext;
return (
<Template
{...{ kcContext, ...props }}
displayMessage={false}
headerNode={msg("termsTitle")}
formNode={
<>
<div id="kc-terms-text">
{msg("termsText")}
</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" />
</>
}
/>
);
});

View File

@ -1,8 +1,8 @@
import { objectKeys } from "evt/tools/typeSafety/objectKeys"; import { objectKeys } from "evt/tools/typeSafety/objectKeys";
import { messages } from "./generated_messages/login"; import { kcMessages } from "./kcMessages/login";
export type KcLanguageTag = keyof typeof messages; export type KcLanguageTag = keyof typeof kcMessages;
export type LanguageLabel = export type LanguageLabel =
/* spell-checker: disable */ /* spell-checker: disable */
@ -40,7 +40,7 @@ export function getKcLanguageTagLabel(language: KcLanguageTag): LanguageLabel {
} }
const availableLanguages = objectKeys(messages); const availableLanguages = objectKeys(kcMessages);
/** /**
* Pass in "fr-FR" or "français" for example, it will return the AvailableLanguage * Pass in "fr-FR" or "français" for example, it will return the AvailableLanguage

View File

@ -2,7 +2,7 @@
//PLEASE DO NOT EDIT MANUALLY //PLEASE DO NOT EDIT MANUALLY
/* spell-checker: disable */ /* spell-checker: disable */
export const messages= { export const kcMessages= {
"ca": { "ca": {
"doSave": "Desa", "doSave": "Desa",
"doCancel": "Cancel·la", "doCancel": "Cancel·la",
@ -3060,5 +3060,5 @@ export const messages= {
"locale_ru": "Русский", "locale_ru": "Русский",
"locale_zh-CN": "中文简体" "locale_zh-CN": "中文简体"
} }
} as const; };
/* spell-checker: enable */ /* spell-checker: enable */

View File

@ -2,7 +2,7 @@
//PLEASE DO NOT EDIT MANUALLY //PLEASE DO NOT EDIT MANUALLY
/* spell-checker: disable */ /* spell-checker: disable */
export const messages= { export const kcMessages= {
"ca": { "ca": {
"invalidPasswordHistoryMessage": "Contrasenya incorrecta: no pot ser igual a cap de les últimes {0} contrasenyes.", "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.", "invalidPasswordMinDigitsMessage": "Contraseña incorrecta: debe contener al menos {0} caracteres numéricos.",
@ -240,5 +240,5 @@ export const messages= {
"pairwiseFailedToGetRedirectURIs": "无法从服务器获得重定向URL", "pairwiseFailedToGetRedirectURIs": "无法从服务器获得重定向URL",
"pairwiseRedirectURIsMismatch": "客户端的重定向URI与服务器端获取的URI配置不匹配。" "pairwiseRedirectURIsMismatch": "客户端的重定向URI与服务器端获取的URI配置不匹配。"
} }
} as const; };
/* spell-checker: enable */ /* spell-checker: enable */

View File

@ -2,7 +2,7 @@
//PLEASE DO NOT EDIT MANUALLY //PLEASE DO NOT EDIT MANUALLY
/* spell-checker: disable */ /* spell-checker: disable */
export const messages= { export const kcMessages= {
"ca": { "ca": {
"emailVerificationSubject": "Verificació d'email", "emailVerificationSubject": "Verificació d'email",
"emailVerificationBody": "Algú ha creat un compte de {2} amb aquesta adreça de correu electrònic. Si has estat tu, fes clic a l'enllaç següent per verificar la teva adreça de correu electrònic.\n\n{0}\n\nAquest enllaç expirarà en {1} minuts.\n\nSi tu no has creat aquest compte, simplement ignora aquest missatge.", "emailVerificationBody": "Algú ha creat un compte de {2} amb aquesta adreça de correu electrònic. Si has estat tu, fes clic a l'enllaç següent per verificar la teva adreça de correu electrònic.\n\n{0}\n\nAquest enllaç expirarà en {1} minuts.\n\nSi tu no has creat aquest compte, simplement ignora aquest missatge.",
@ -634,5 +634,5 @@ export const messages= {
"eventUpdateTotpBody": "您账户的OTP 配置在{0} 由 {1}更改. 如非本人操作,请联系管理员。", "eventUpdateTotpBody": "您账户的OTP 配置在{0} 由 {1}更改. 如非本人操作,请联系管理员。",
"eventUpdateTotpBodyHtml": "<p>您账户的OTP 配置在{0} 由 {1}更改. 如非本人操作,请联系管理员。</p>" "eventUpdateTotpBodyHtml": "<p>您账户的OTP 配置在{0} 由 {1}更改. 如非本人操作,请联系管理员。</p>"
} }
} as const; };
/* spell-checker: enable */ /* spell-checker: enable */

View File

@ -2,7 +2,7 @@
//PLEASE DO NOT EDIT MANUALLY //PLEASE DO NOT EDIT MANUALLY
/* spell-checker: disable */ /* spell-checker: disable */
export const messages= { export const kcMessages= {
"ca": { "ca": {
"doLogIn": "Inicia sessió", "doLogIn": "Inicia sessió",
"doRegister": "Registra't", "doRegister": "Registra't",
@ -4360,5 +4360,5 @@ export const messages= {
"invalidParameterMessage": "无效的参数 : {0}", "invalidParameterMessage": "无效的参数 : {0}",
"alreadyLoggedIn": "您已经登录" "alreadyLoggedIn": "您已经登录"
} }
} as const; };
/* spell-checker: enable */ /* spell-checker: enable */

View File

@ -0,0 +1,33 @@
import { kcMessages } from "../generated_kcMessages/login";
import { Evt } from "evt";
import { objectKeys } from "evt/tools/typeSafety/objectKeys";
export const evtTermsUpdated = Evt.asNonPostable(Evt.create<void>());
objectKeys(kcMessages).forEach(kcLanguage =>
Object.defineProperty(
kcMessages[kcLanguage],
"termsText",
(() => {
let value = kcMessages[kcLanguage].termsText;
return {
"enumerable": true,
"get": () => value,
"set": (newValue: string) => {
Evt.asPostable(evtTermsUpdated).post();
value = newValue;
}
};
})()
)
);
export { kcMessages };

View File

@ -1,20 +1,26 @@
import { useCallback, useReducer } from "react";
import { useKcLanguageTag } from "./useKcLanguageTag"; import { useKcLanguageTag } from "./useKcLanguageTag";
import { messages } from "./generated_messages/login"; import { kcMessages, evtTermsUpdated } from "./kcMessages/login";
import { useConstCallback } from "powerhooks";
import type { ReactNode } from "react"; import type { ReactNode } from "react";
import { id } from "evt/tools/typeSafety/id"; import { useEvt } from "evt/hooks";
//@ts-ignore
import * as markdown from "markdown";
export type MessageKey = keyof typeof messages["en"]; export type MessageKey = keyof typeof kcMessages["en"];
export function useKcMessage() { export function useKcMessage() {
const { kcLanguageTag } = useKcLanguageTag(); const { kcLanguageTag } = useKcLanguageTag();
const msgStr = useConstCallback( const [trigger, forceUpdate] = useReducer((counter: number) => counter + 1, 0);
useEvt(ctx => evtTermsUpdated.attach(ctx, forceUpdate), []);
const msgStr = useCallback(
(key: MessageKey, ...args: (string | undefined)[]): string => { (key: MessageKey, ...args: (string | undefined)[]): string => {
let str: string = messages[kcLanguageTag as any as "en"][key] ?? messages["en"][key]; let str: string = kcMessages[kcLanguageTag as any as "en"][key] ?? kcMessages["en"][key];
args.forEach((arg, i) => { args.forEach((arg, i) => {
@ -28,14 +34,21 @@ export function useKcMessage() {
return str; return str;
} },
[kcLanguageTag, trigger]
); );
const msg = useConstCallback( const msg = useCallback<(...args: Parameters<typeof msgStr>) => ReactNode>(
id<(...args: Parameters<typeof msgStr>) => ReactNode>( (key, ...args) =>
(key, ...args) => <span
<span className={key} dangerouslySetInnerHTML={{ "__html": msgStr(key, ...args) }} /> className={key}
) dangerouslySetInnerHTML={{
"__html":
markdown.toHTML(msgStr(key, ...args))
}}
/>
,
[kcLanguageTag, trigger]
); );
return { msg, msgStr }; return { msg, msgStr };

View File

@ -3,6 +3,7 @@ export * from "./KcContext";
export * from "./i18n/KcLanguageTag"; export * from "./i18n/KcLanguageTag";
export * from "./i18n/useKcLanguageTag"; export * from "./i18n/useKcLanguageTag";
export * from "./i18n/useKcMessage"; export * from "./i18n/useKcMessage";
export * from "./i18n/kcMessages/login";
export * from "./components/KcProps"; export * from "./components/KcProps";
export * from "./components/Login"; export * from "./components/Login";
@ -16,4 +17,5 @@ export * from "./keycloakJsAdapter";
export * from "./tools/assert"; export * from "./tools/assert";
export * as kcContextMocks from "./kcContextMocks"; export * as kcContextMocks from "./kcContextMocks";

View File

@ -16,7 +16,8 @@ type ExtractAfterStartingWith<Prefix extends string, StrEnum> =
*/ */
export type KcContext = export type KcContext =
KcContext.Login | KcContext.Register | KcContext.Info | KcContext.Login | KcContext.Register | KcContext.Info |
KcContext.Error | KcContext.LoginResetPassword | KcContext.LoginVerifyEmail; KcContext.Error | KcContext.LoginResetPassword | KcContext.LoginVerifyEmail |
KcContext.Terms;
export declare namespace KcContext { export declare namespace KcContext {
@ -155,6 +156,10 @@ export declare namespace KcContext {
pageId: "login-verify-email.ftl"; pageId: "login-verify-email.ftl";
}; };
export type Terms = Common & {
pageId: "terms.ftl";
};
} }
doExtends<KcContext["pageId"], PageId>(); doExtends<KcContext["pageId"], PageId>();

View File

@ -199,3 +199,8 @@ export const kcLoginVerifyEmailContext: KcContext.LoginVerifyEmail = {
"pageId": "login-verify-email.ftl" "pageId": "login-verify-email.ftl"
}; };
export const kcTermContext: KcContext.Terms = {
...kcCommonContext,
"pageId": "terms.ftl"
};

View File

@ -747,10 +747,10 @@ path@^0.12.7:
process "^0.11.1" process "^0.11.1"
util "^0.10.3" util "^0.10.3"
powerhooks@^0.0.27: powerhooks@^0.0.36:
version "0.0.27" version "0.0.36"
resolved "https://registry.yarnpkg.com/powerhooks/-/powerhooks-0.0.27.tgz#e9dc29258860d2f6bf32b249d9cba07c6f53f393" resolved "https://registry.yarnpkg.com/powerhooks/-/powerhooks-0.0.36.tgz#d973d339ad8ca7ce52ea9d288ebe8e138df7d769"
integrity sha512-ohayWhtIEdLqiC2th/GEhaRfOhqekFg2uFo0JZ8Dn7oTnAZybs618QJeq5ag9oy3lFVzl+kbROpVa8Ch5zrkaA== integrity sha512-0fEGKLfJmuFeEYDGsAlTuvGKKdqOH059xezosmYRoGurfC1bbUlaNJD+TuzOT5cTGURi88DCLcDnhk/2eLyCyA==
dependencies: dependencies:
evt "2.0.0-beta.15" evt "2.0.0-beta.15"
memoizee "^0.4.15" memoizee "^0.4.15"
@ -927,10 +927,10 @@ to-fast-properties@^2.0.0:
resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e"
integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4= integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=
tss-react@^0.0.11: tss-react@^0.0.12:
version "0.0.11" version "0.0.12"
resolved "https://registry.yarnpkg.com/tss-react/-/tss-react-0.0.11.tgz#1cd061927744cd4fc9b7346e2fd1cfcf896a18d5" resolved "https://registry.yarnpkg.com/tss-react/-/tss-react-0.0.12.tgz#6463617ae5e7f670742e48e497d8825d59e2a2e9"
integrity sha512-j8CDpHHIl6S6/mX+AmK08v7waPqwgNA7urHOD3qknCgbY79LRlS7he5DF4NUNE/5B4/Btc3F25w+KqgChNbyGw== integrity sha512-oHekukqdaE71uhHx4XEdHy6aMnDYhoHLWB94iy2Fy9X8btH2lJH1joPj0zS1q7+1Xy2TydkLEZsTq3ElVd7ZqA==
dependencies: dependencies:
"@emotion/css" "^11.1.3" "@emotion/css" "^11.1.3"