Compare commits

...

18 Commits

Author SHA1 Message Date
f0c4786267 Update changelog v0.3.24 2021-04-08 16:14:10 +00:00
0b16159312 Merge branch 'main' of https://github.com/InseeFrLab/keycloakify into main 2021-04-08 18:00:04 +02:00
ea8a91e069 Add missing dependency: markdown 2021-04-08 17:59:58 +02:00
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
18 changed files with 196 additions and 36 deletions

View File

@ -1,3 +1,22 @@
### **0.3.24** (2021-04-08)
- Add missing dependency: markdown
### **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) ### **0.3.20** (2021-04-01)
- Always catch freemarker errors - Always catch freemarker errors

View File

@ -1,6 +1,6 @@
{ {
"name": "keycloakify", "name": "keycloakify",
"version": "0.3.20", "version": "0.3.24",
"description": "Keycloak theme generator for Reacts app", "description": "Keycloak theme generator for Reacts app",
"repository": { "repository": {
"type": "git", "type": "git",
@ -51,9 +51,10 @@
"dependencies": { "dependencies": {
"cheerio": "^1.0.0-rc.5", "cheerio": "^1.0.0-rc.5",
"evt": "2.0.0-beta.15", "evt": "2.0.0-beta.15",
"markdown": "^0.5.0",
"minimal-polyfills": "^2.1.6", "minimal-polyfills": "^2.1.6",
"path": "^0.12.7", "path": "^0.12.7",
"powerhooks": "^0.0.32", "powerhooks": "^0.0.36",
"scripting-tools": "^0.19.13", "scripting-tools": "^0.19.13",
"tss-react": "^0.0.12" "tss-react": "^0.0.12"
} }

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

@ -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 className={key} dangerouslySetInnerHTML={{ "__html": msgStr(key, ...args) }} /> <span
) 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

@ -168,6 +168,11 @@
resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.1.tgz#18845205e86ff0038517aab7a18a62a6b9f71275" resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.1.tgz#18845205e86ff0038517aab7a18a62a6b9f71275"
integrity sha512-EaCxbanVeyxDRTQBkdLb3Bvl/HK7PBK6UJjsSixB0iHKoWxE5uu2Q/DgtpOhPIojN0Zl1whvOd7PoHs2P0s5eA== integrity sha512-EaCxbanVeyxDRTQBkdLb3Bvl/HK7PBK6UJjsSixB0iHKoWxE5uu2Q/DgtpOhPIojN0Zl1whvOd7PoHs2P0s5eA==
abbrev@1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8"
integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==
ansi-regex@^5.0.0: ansi-regex@^5.0.0:
version "5.0.0" version "5.0.0"
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75"
@ -627,6 +632,13 @@ lru-queue@^0.1.0:
dependencies: dependencies:
es5-ext "~0.10.2" es5-ext "~0.10.2"
markdown@^0.5.0:
version "0.5.0"
resolved "https://registry.yarnpkg.com/markdown/-/markdown-0.5.0.tgz#28205b565a8ae7592de207463d6637dc182722b2"
integrity sha1-KCBbVlqK51kt4gdGPWY33BgnIrI=
dependencies:
nopt "~2.1.1"
memoizee@^0.4.15: memoizee@^0.4.15:
version "0.4.15" version "0.4.15"
resolved "https://registry.yarnpkg.com/memoizee/-/memoizee-0.4.15.tgz#e6f3d2da863f318d02225391829a6c5956555b72" resolved "https://registry.yarnpkg.com/memoizee/-/memoizee-0.4.15.tgz#e6f3d2da863f318d02225391829a6c5956555b72"
@ -676,6 +688,13 @@ noms@0.0.0:
inherits "^2.0.1" inherits "^2.0.1"
readable-stream "~1.0.31" readable-stream "~1.0.31"
nopt@~2.1.1:
version "2.1.2"
resolved "https://registry.yarnpkg.com/nopt/-/nopt-2.1.2.tgz#6cccd977b80132a07731d6e8ce58c2c8303cf9af"
integrity sha1-bMzZd7gBMqB3MdbozljCyDA8+a8=
dependencies:
abbrev "1"
nth-check@^2.0.0: nth-check@^2.0.0:
version "2.0.0" version "2.0.0"
resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.0.0.tgz#1bb4f6dac70072fc313e8c9cd1417b5074c0a125" resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.0.0.tgz#1bb4f6dac70072fc313e8c9cd1417b5074c0a125"
@ -747,10 +766,10 @@ path@^0.12.7:
process "^0.11.1" process "^0.11.1"
util "^0.10.3" util "^0.10.3"
powerhooks@^0.0.32: powerhooks@^0.0.36:
version "0.0.32" version "0.0.36"
resolved "https://registry.yarnpkg.com/powerhooks/-/powerhooks-0.0.32.tgz#a6aa19573b22f38f4f0bb44573c3242116e9eed1" resolved "https://registry.yarnpkg.com/powerhooks/-/powerhooks-0.0.36.tgz#d973d339ad8ca7ce52ea9d288ebe8e138df7d769"
integrity sha512-atxsb/zcN8O72hqtfz6tv4XLlh4X22+iTO96PPKpa//nrdvX8KTpFoGiUTh+RQ9RONdR4hP/RmfsDu1zPp2G8Q== 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"