Compare commits

...

57 Commits

Author SHA1 Message Date
82f34c38f6 Bump version 2024-02-01 06:22:54 +01:00
694b4c8027 Reintroduce doBuildRetrocompatAccountTheme (for now) and fix multiple things in the account default theme 2024-02-01 06:22:33 +01:00
33b7bb6184 Bump version 2024-01-26 04:09:54 +01:00
7d9130b2af Remove the doBuildRetrocompatAccountTheme and the retrocompat_ option in dropdown 2024-01-26 03:39:48 +01:00
482d71743b Bump version 2024-01-26 02:03:06 +01:00
1db37a4727 Do not include retrocompat_ in META-INF when using doBuildRetrocompatAccountTheme false 2024-01-26 02:02:45 +01:00
194d16ff91 Bump version 2024-01-26 00:56:45 +01:00
b1e2284c0e Fix condition for displaying info in login page 2024-01-26 00:55:50 +01:00
70d1aa70a3 Bump version 2024-01-18 18:09:59 +01:00
3b17d6e0ab Merge pull request #496 from keycloakify/all-contributors/add-Moulyy
docs: add Moulyy as a contributor for code
2024-01-18 18:09:10 +01:00
9a5819b93b docs: update .all-contributorsrc [skip ci] 2024-01-18 16:45:36 +00:00
a260cd67b0 docs: update README.md [skip ci] 2024-01-18 16:45:35 +00:00
64111fb0ec Merge pull request #495 from Moulyy/fix/add_password_visibility_classkey
Add "kcInputGroup" in ClassKey type definition and useGetClassName
2024-01-18 17:43:48 +01:00
faf2be23d9 Add "kcInputGroup" in ClassKey type definition and the patternfly class associated in useGetClassName 2024-01-18 17:31:16 +01:00
0eb4a6a315 Merge pull request #494 from alexted/patch-1
Update README.md
2024-01-18 16:59:17 +01:00
85673250ed Update README.md
extra "v" deleted  in several places of changelog highlights
2024-01-18 17:48:10 +02:00
09daa741ce Install pnpm if needed 2024-01-18 01:02:14 +01:00
55e2379aab Bump version 2024-01-18 00:57:02 +01:00
9937977203 Merge branch 'main' of https://github.com/keycloakify/keycloakify 2024-01-18 00:56:26 +01:00
c897e7491a Use message of Keycloak version 23.0.4 #493 2024-01-18 00:54:09 +01:00
0a74a95283 Bump version 2024-01-15 11:02:00 +01:00
74ef2c3dff Merge pull request #489 from law108000/main
refactor: point the import statement to keycloakify instead of internal
2024-01-15 11:01:14 +01:00
9976dfacc0 Merge pull request #491 from keycloakify/all-contributors/add-law108000
docs: add law108000 as a contributor for code
2024-01-15 10:59:17 +01:00
659f8ddc7a docs: update .all-contributorsrc [skip ci] 2024-01-15 09:56:30 +00:00
9e4cc2ae57 docs: update README.md [skip ci] 2024-01-15 09:56:29 +00:00
a27d78fcdf refactor: point the import statement to keycloakify instead of internal
align import statement with other page templates
2024-01-15 14:03:01 +08:00
e507435bcb Bump version 2024-01-10 13:01:23 +01:00
d5f234909f Merge branch 'main' of https://github.com/keycloakify/keycloakify 2024-01-10 12:43:11 +01:00
c17f721625 Bump version 2024-01-10 12:43:04 +01:00
600705130f #483: Enable the Template prop not to be lazy 2024-01-10 12:42:48 +01:00
5c5dce1422 Bump version 2023-12-18 13:19:54 +01:00
53585bf2f0 Merge pull request #476 from BlackVoid/feature/document-title
Fixes #473: Set document title using the "loginTitle" translation
2023-12-18 13:18:51 +01:00
116f88a503 Fixes #473: Set document title using the "loginTitle" translation 2023-12-18 11:56:37 +01:00
aaba8cd2c7 Bump version 2023-12-14 15:34:13 +01:00
b67aeb0d3a Fix tests #471 2023-12-14 15:33:57 +01:00
f620562d68 docs: update .all-contributorsrc [skip ci] 2023-12-14 15:09:10 +01:00
5231d0eaa1 docs: update README.md [skip ci] 2023-12-14 15:09:09 +01:00
cb470e3573 Handle CSS with multiple urls 2023-12-12 14:51:09 +00:00
0a0f90aa2e Bump version 2023-12-04 14:10:25 +01:00
635207d12c Generate a README alongside the jars to indicate why there's two jar file 2023-12-04 14:10:10 +01:00
5e4a829413 Bump version 2023-12-01 00:54:36 +01:00
b13b3fd92e Mention the nessesity to use retrocompat- with older Keycloak versions 2023-12-01 00:54:19 +01:00
564dc8e6f1 Bump version 2023-12-01 00:03:58 +01:00
6e4cced8c6 Rename original- to retrocompat- 2023-12-01 00:03:44 +01:00
29a4a5027c Bump version 2023-11-30 22:37:13 +01:00
ee327448b4 Delete original- jar 2023-11-30 22:36:58 +01:00
d078960c5c Bump version 2023-11-30 18:53:17 +01:00
2e8cd375fc Merge pull request #462 from BlackVoid/fix/client-attributes
Fixes #460: Fixes KcContext to contain attributes for client object
2023-11-30 18:52:26 +01:00
1f6751cb01 Fixes #460: Fixes KcContext to contain attributes for client object 2023-11-30 17:17:58 +01:00
3cca4e31cd Merge pull request #463 from keycloakify/all-contributors/add-BlackVoid
docs: add BlackVoid as a contributor for code
2023-11-30 17:10:29 +01:00
b93902800c docs: update .all-contributorsrc [skip ci] 2023-11-30 16:06:55 +00:00
70f6bb3fda docs: update README.md [skip ci] 2023-11-30 16:06:54 +00:00
c075cb6311 Merge pull request #461 from BlackVoid/fix/template
Fixes #459: Use Template from props
2023-11-30 17:03:36 +01:00
d7db85b062 Fixes #459: Use Template from props 2023-11-30 16:29:53 +01:00
b442e7d958 Merge pull request #457 from keycloakify/all-contributors/add-xgp
docs: add xgp as a contributor for code
2023-11-28 13:06:04 +01:00
a495ae637f docs: update .all-contributorsrc [skip ci] 2023-11-28 12:05:49 +00:00
94748a96a9 docs: update README.md [skip ci] 2023-11-28 12:05:48 +00:00
24 changed files with 213 additions and 67 deletions

View File

@ -186,6 +186,51 @@
"contributions": [ "contributions": [
"code" "code"
] ]
},
{
"login": "xgp",
"name": "Garth",
"avatar_url": "https://avatars.githubusercontent.com/u/244253?v=4",
"profile": "https://github.com/xgp",
"contributions": [
"code"
]
},
{
"login": "BlackVoid",
"name": "Felix Gustavsson",
"avatar_url": "https://avatars.githubusercontent.com/u/673720?v=4",
"profile": "https://github.com/BlackVoid",
"contributions": [
"code"
]
},
{
"login": "msiemens",
"name": "Markus Siemens",
"avatar_url": "https://avatars.githubusercontent.com/u/1873922?v=4",
"profile": "https://m-siemens.de/",
"contributions": [
"code"
]
},
{
"login": "law108000",
"name": "Rlok",
"avatar_url": "https://avatars.githubusercontent.com/u/8112024?v=4",
"profile": "https://github.com/law108000",
"contributions": [
"code"
]
},
{
"login": "Moulyy",
"name": "Moulyy",
"avatar_url": "https://avatars.githubusercontent.com/u/115405804?v=4",
"profile": "https://github.com/Moulyy",
"contributions": [
"code"
]
} }
], ],
"contributorsPerLine": 7, "contributorsPerLine": 7,

View File

@ -107,6 +107,13 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
<td align="center" valign="top" width="14.28%"><a href="https://github.com/zavoloklom"><img src="https://avatars.githubusercontent.com/u/4151869?v=4?s=100" width="100px;" alt="Sergey Kupletsky"/><br /><sub><b>Sergey Kupletsky</b></sub></a><br /><a href="https://github.com/keycloakify/keycloakify/commits?author=zavoloklom" title="Tests">⚠️</a> <a href="https://github.com/keycloakify/keycloakify/commits?author=zavoloklom" title="Code">💻</a></td> <td align="center" valign="top" width="14.28%"><a href="https://github.com/zavoloklom"><img src="https://avatars.githubusercontent.com/u/4151869?v=4?s=100" width="100px;" alt="Sergey Kupletsky"/><br /><sub><b>Sergey Kupletsky</b></sub></a><br /><a href="https://github.com/keycloakify/keycloakify/commits?author=zavoloklom" title="Tests">⚠️</a> <a href="https://github.com/keycloakify/keycloakify/commits?author=zavoloklom" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/rome-user"><img src="https://avatars.githubusercontent.com/u/114131048?v=4?s=100" width="100px;" alt="rome-user"/><br /><sub><b>rome-user</b></sub></a><br /><a href="https://github.com/keycloakify/keycloakify/commits?author=rome-user" title="Code">💻</a></td> <td align="center" valign="top" width="14.28%"><a href="https://github.com/rome-user"><img src="https://avatars.githubusercontent.com/u/114131048?v=4?s=100" width="100px;" alt="rome-user"/><br /><sub><b>rome-user</b></sub></a><br /><a href="https://github.com/keycloakify/keycloakify/commits?author=rome-user" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/celinepelletier"><img src="https://avatars.githubusercontent.com/u/82821620?v=4?s=100" width="100px;" alt="Céline Pelletier"/><br /><sub><b>Céline Pelletier</b></sub></a><br /><a href="https://github.com/keycloakify/keycloakify/commits?author=celinepelletier" title="Code">💻</a></td> <td align="center" valign="top" width="14.28%"><a href="https://github.com/celinepelletier"><img src="https://avatars.githubusercontent.com/u/82821620?v=4?s=100" width="100px;" alt="Céline Pelletier"/><br /><sub><b>Céline Pelletier</b></sub></a><br /><a href="https://github.com/keycloakify/keycloakify/commits?author=celinepelletier" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/xgp"><img src="https://avatars.githubusercontent.com/u/244253?v=4?s=100" width="100px;" alt="Garth"/><br /><sub><b>Garth</b></sub></a><br /><a href="https://github.com/keycloakify/keycloakify/commits?author=xgp" title="Code">💻</a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/BlackVoid"><img src="https://avatars.githubusercontent.com/u/673720?v=4?s=100" width="100px;" alt="Felix Gustavsson"/><br /><sub><b>Felix Gustavsson</b></sub></a><br /><a href="https://github.com/keycloakify/keycloakify/commits?author=BlackVoid" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://m-siemens.de/"><img src="https://avatars.githubusercontent.com/u/1873922?v=4?s=100" width="100px;" alt="Markus Siemens"/><br /><sub><b>Markus Siemens</b></sub></a><br /><a href="https://github.com/keycloakify/keycloakify/commits?author=msiemens" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/law108000"><img src="https://avatars.githubusercontent.com/u/8112024?v=4?s=100" width="100px;" alt="Rlok"/><br /><sub><b>Rlok</b></sub></a><br /><a href="https://github.com/keycloakify/keycloakify/commits?author=law108000" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Moulyy"><img src="https://avatars.githubusercontent.com/u/115405804?v=4?s=100" width="100px;" alt="Moulyy"/><br /><sub><b>Moulyy</b></sub></a><br /><a href="https://github.com/keycloakify/keycloakify/commits?author=Moulyy" title="Code">💻</a></td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
@ -118,7 +125,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
# Changelog highlights # Changelog highlights
## v9.0 ## 9.0
Bring back support for account themes in Keycloak v23 and up! [See issue](https://github.com/keycloakify/keycloakify/issues/389). Bring back support for account themes in Keycloak v23 and up! [See issue](https://github.com/keycloakify/keycloakify/issues/389).
@ -251,79 +258,79 @@ Checkout [the migration guide](https://docs.keycloakify.dev/v5-to-v6)
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) 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)
## v5.6.0 ## 5.6.0
Add support for `login-config-totp.ftl` page [#127](https://github.com/keycloakify/keycloakify/pull/127). Add support for `login-config-totp.ftl` page [#127](https://github.com/keycloakify/keycloakify/pull/127).
## v5.3.0 ## 5.3.0
Rename `keycloak_theme_email` to `keycloak_email`. Rename `keycloak_theme_email` to `keycloak_email`.
If you already had a `keycloak_theme_email` you should rename it `keycloak_email`. If you already had a `keycloak_theme_email` you should rename it `keycloak_email`.
## v5.0.0 ## 5.0.0
[Migration guide](https://github.com/garronej/keycloakify-demo-app/blob/a5b6a50f24bc25e082931f5ad9ebf47492acd12a/src/index.tsx#L46-L63) [Migration guide](https://github.com/garronej/keycloakify-demo-app/blob/a5b6a50f24bc25e082931f5ad9ebf47492acd12a/src/index.tsx#L46-L63)
New i18n system. 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). Import of terms and services have changed. [See example](https://github.com/garronej/keycloakify-demo-app/blob/a5b6a50f24bc25e082931f5ad9ebf47492acd12a/src/index.tsx#L46-L63).
## v4.10.0 ## 4.10.0
Add `login-idp-link-email.ftl` page [See PR](https://github.com/keycloakify/keycloakify/pull/92). Add `login-idp-link-email.ftl` page [See PR](https://github.com/keycloakify/keycloakify/pull/92).
## v4.8.0 ## 4.8.0
[Email template customization.](#email-template-customization) [Email template customization.](#email-template-customization)
## v4.7.4 ## 4.7.4
**M1 Mac** support (for testing locally with a dockerized Keycloak). **M1 Mac** support (for testing locally with a dockerized Keycloak).
## v4.7.2 ## 4.7.2
> WARNING: This is broken. > WARNING: This is broken.
> Testing with local Keycloak container working with M1 Mac. Thanks to [@eduardosanzb](https://github.com/keycloakify/keycloakify/issues/43#issuecomment-975699658). > Testing with local Keycloak container working with M1 Mac. Thanks to [@eduardosanzb](https://github.com/keycloakify/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. > Be aware: When running M1s you are testing with Keycloak v15 else the local container spun will be a Keycloak v16.1.0.
## v4.7.0 ## 4.7.0
Register with user profile enabled: Out of the box `options` validator support. 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) [Example](https://user-images.githubusercontent.com/6702424/158911163-81e6bbe8-feb0-4dc8-abff-de199d7a678e.mov)
## v4.6.0 ## 4.6.0
`tss-react` and `powerhooks` are no longer peer dependencies of `keycloakify`. `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. After updating Keycloakify you can remove `tss-react` and `powerhooks` from your dependencies if you don't use them explicitly.
## v4.5.3 ## 4.5.3
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). 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), 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. with this new method your theme wont break on minor Keycloakify update.
## v4.3.0 ## 4.3.0
Feature [`login-update-password.ftl`](https://user-images.githubusercontent.com/6702424/147517600-6191cf72-93dd-437b-a35c-47180142063e.png). 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. 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. 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.
## v4 ## 4
- Out of the box [frontend form validation](#user-profile-and-frontend-form-validation) 🥳 - Out of the box [frontend form validation](#user-profile-and-frontend-form-validation) 🥳
- Improvements (and breaking changes in `import { useKcMessage } from "keycloakify"`. - Improvements (and breaking changes in `import { useKcMessage } from "keycloakify"`.
## v3 ## 3
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. 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 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/keycloakify/keycloakify#implement-context-persistence-optional). [when passing params from the app to the login page](https://github.com/keycloakify/keycloakify#implement-context-persistence-optional).
## v2.5 ## 2.5
- Feature [Use advanced message](https://github.com/keycloakify/keycloakify/blob/59f106bf9e210b63b190826da2bf5f75fc8b7644/src/lib/i18n/useKcMessage.tsx#L53-L66) - Feature [Use advanced message](https://github.com/keycloakify/keycloakify/blob/59f106bf9e210b63b190826da2bf5f75fc8b7644/src/lib/i18n/useKcMessage.tsx#L53-L66)
and [`messagesPerFields`](https://github.com/keycloakify/keycloakify/blob/59f106bf9e210b63b190826da2bf5f75fc8b7644/src/lib/getKcContext/KcContextBase.ts#L70-L75) (implementation [here](https://github.com/keycloakify/keycloakify/blob/59f106bf9e210b63b190826da2bf5f75fc8b7644/src/bin/build-keycloak-theme/generateFtl/common.ftl#L130-L189)) and [`messagesPerFields`](https://github.com/keycloakify/keycloakify/blob/59f106bf9e210b63b190826da2bf5f75fc8b7644/src/lib/getKcContext/KcContextBase.ts#L70-L75) (implementation [here](https://github.com/keycloakify/keycloakify/blob/59f106bf9e210b63b190826da2bf5f75fc8b7644/src/bin/build-keycloak-theme/generateFtl/common.ftl#L130-L189))
- Test container now uses Keycloak version `15.0.2`. - Test container now uses Keycloak version `15.0.2`.
## v2 ## 2
- It's now possible to implement custom `.ftl` pages. - It's now possible to implement custom `.ftl` pages.
- Support for Keycloak plugins that introduce non standard ftl values. - Support for Keycloak plugins that introduce non standard ftl values.

View File

@ -1,6 +1,6 @@
{ {
"name": "keycloakify", "name": "keycloakify",
"version": "9.0.0", "version": "9.3.0",
"description": "Create Keycloak themes using React", "description": "Create Keycloak themes using React",
"repository": { "repository": {
"type": "git", "type": "git",

View File

@ -17,7 +17,7 @@ const isSilent = true;
const logger = getLogger({ isSilent }); const logger = getLogger({ isSilent });
async function main() { async function main() {
const keycloakVersion = "21.0.1"; const keycloakVersion = "23.0.4";
const tmpDirPath = pathJoin(getProjectRoot(), "tmp_xImOef9dOd44"); const tmpDirPath = pathJoin(getProjectRoot(), "tmp_xImOef9dOd44");

View File

@ -1,9 +1,9 @@
import type { LazyExoticComponent } from "react";
import type { I18n } from "keycloakify/account/i18n"; import type { I18n } from "keycloakify/account/i18n";
import type { TemplateProps, ClassKey } from "keycloakify/account/TemplateProps"; import type { TemplateProps, ClassKey } from "keycloakify/account/TemplateProps";
import type { LazyOrNot } from "keycloakify/tools/LazyOrNot";
export type PageProps<KcContext, I18nExtended extends I18n> = { export type PageProps<KcContext, I18nExtended extends I18n> = {
Template: LazyExoticComponent<(props: TemplateProps<any, any>) => JSX.Element | null>; Template: LazyOrNot<(props: TemplateProps<any, any>) => JSX.Element | null>;
kcContext: KcContext; kcContext: KcContext;
i18n: I18nExtended; i18n: I18nExtended;
doUseDefaultCss: boolean; doUseDefaultCss: boolean;

View File

@ -27,6 +27,51 @@ export async function downloadBuiltinKeycloakTheme(params: { keycloakVersion: st
"preCacheTransform": { "preCacheTransform": {
"actionCacheId": "npm install and build", "actionCacheId": "npm install and build",
"action": async ({ destDirPath }) => { "action": async ({ destDirPath }) => {
fix_account_css: {
const accountCssFilePath = pathJoin(destDirPath, "keycloak", "account", "resources", "css", "account.css");
if (!fs.existsSync(accountCssFilePath)) {
break fix_account_css;
}
fs.writeFileSync(
accountCssFilePath,
Buffer.from(fs.readFileSync(accountCssFilePath).toString("utf8").replace("top: -34px;", "top: -34px !important;"), "utf8")
);
}
fix_account_topt: {
const totpFtlFilePath = pathJoin(destDirPath, "base", "account", "totp.ftl");
if (!fs.existsSync(totpFtlFilePath)) {
break fix_account_topt;
}
fs.writeFileSync(
totpFtlFilePath,
Buffer.from(
fs
.readFileSync(totpFtlFilePath)
.toString("utf8")
.replace(
[
" <#list totp.policy.supportedApplications as app>",
" <li>${app}</li>",
" </#list>"
].join("\n"),
[
" <#if totp.policy.supportedApplications?has_content>",
" <#list totp.policy.supportedApplications as app>",
" <li>${app}</li>",
" </#list>",
" </#if>"
].join("\n")
),
"utf8"
)
);
}
install_common_node_modules: { install_common_node_modules: {
const commonResourcesDirPath = pathJoin(destDirPath, "keycloak", "common", "resources"); const commonResourcesDirPath = pathJoin(destDirPath, "keycloak", "common", "resources");
@ -55,7 +100,18 @@ export async function downloadBuiltinKeycloakTheme(params: { keycloakVersion: st
break install_and_move_to_common_resources_generated_in_keycloak_v2; break install_and_move_to_common_resources_generated_in_keycloak_v2;
} }
child_process.execSync("npm install", { "cwd": accountV2DirSrcDirPath, "stdio": "ignore" }); const packageManager = fs.existsSync(pathJoin(accountV2DirSrcDirPath, "pnpm-lock.yaml")) ? "pnpm" : "npm";
if (packageManager === "pnpm") {
try {
child_process.execSync(`which pnpm`);
} catch {
console.log(`Installing pnpm globally`);
child_process.execSync(`npm install -g pnpm`);
}
}
child_process.execSync(`${packageManager} install`, { "cwd": accountV2DirSrcDirPath, "stdio": "ignore" });
const packageJsonFilePath = pathJoin(accountV2DirSrcDirPath, "package.json"); const packageJsonFilePath = pathJoin(accountV2DirSrcDirPath, "package.json");
@ -64,12 +120,12 @@ export async function downloadBuiltinKeycloakTheme(params: { keycloakVersion: st
const parsedPackageJson = JSON.parse(packageJsonRaw.toString("utf8")); const parsedPackageJson = JSON.parse(packageJsonRaw.toString("utf8"));
parsedPackageJson.scripts.build = parsedPackageJson.scripts.build parsedPackageJson.scripts.build = parsedPackageJson.scripts.build
.replace("npm run check-types", "true") .replace(`${packageManager} run check-types`, "true")
.replace("npm run babel", "true"); .replace(`${packageManager} run babel`, "true");
fs.writeFileSync(packageJsonFilePath, Buffer.from(JSON.stringify(parsedPackageJson, null, 2), "utf8")); fs.writeFileSync(packageJsonFilePath, Buffer.from(JSON.stringify(parsedPackageJson, null, 2), "utf8"));
child_process.execSync("npm run build", { "cwd": accountV2DirSrcDirPath, "stdio": "ignore" }); child_process.execSync(`${packageManager} run build`, { "cwd": accountV2DirSrcDirPath, "stdio": "ignore" });
fs.writeFileSync(packageJsonFilePath, packageJsonRaw); fs.writeFileSync(packageJsonFilePath, packageJsonRaw);

View File

@ -38,7 +38,21 @@ export async function bringInAccountV1(params: { buildOptions: BuildOptionsLike
const commonResourceFilePaths = [ const commonResourceFilePaths = [
"node_modules/patternfly/dist/css/patternfly.min.css", "node_modules/patternfly/dist/css/patternfly.min.css",
"node_modules/patternfly/dist/css/patternfly-additions.min.css" "node_modules/patternfly/dist/css/patternfly-additions.min.css",
...[
"OpenSans-Light-webfont.woff2",
"OpenSans-Regular-webfont.woff2",
"OpenSans-Bold-webfont.woff2",
"OpenSans-Semibold-webfont.woff2",
"OpenSans-Bold-webfont.woff",
"OpenSans-Light-webfont.woff",
"OpenSans-Regular-webfont.woff",
"OpenSans-Semibold-webfont.woff",
"OpenSans-Regular-webfont.ttf",
"OpenSans-Light-webfont.ttf",
"OpenSans-Semibold-webfont.ttf",
"OpenSans-Bold-webfont.ttf"
].map(path => `node_modules/patternfly/dist/fonts/${path}`)
]; ];
for (const relativeFilePath of commonResourceFilePaths.map(path => pathJoin(...path.split("/")))) { for (const relativeFilePath of commonResourceFilePaths.map(path => pathJoin(...path.split("/")))) {
@ -49,7 +63,7 @@ export async function bringInAccountV1(params: { buildOptions: BuildOptionsLike
fs.cpSync(pathJoin(builtinKeycloakThemeTmpDirPath, "keycloak", "common", "resources", relativeFilePath), destFilePath); fs.cpSync(pathJoin(builtinKeycloakThemeTmpDirPath, "keycloak", "common", "resources", relativeFilePath), destFilePath);
} }
const resourceFilePaths = ["css/account.css"]; const resourceFilePaths = ["css/account.css", "img/icon-sidebar-active.png", "img/logo.png"];
for (const relativeFilePath of resourceFilePaths.map(path => pathJoin(...path.split("/")))) { for (const relativeFilePath of resourceFilePaths.map(path => pathJoin(...path.split("/")))) {
const destFilePath = pathJoin(accountV1DirPath, "resources", relativeFilePath); const destFilePath = pathJoin(accountV1DirPath, "resources", relativeFilePath);
@ -69,7 +83,7 @@ export async function bringInAccountV1(params: { buildOptions: BuildOptionsLike
"", "",
"locales=ar,ca,cs,da,de,en,es,fr,fi,hu,it,ja,lt,nl,no,pl,pt-BR,ru,sk,sv,tr,zh-CN", "locales=ar,ca,cs,da,de,en,es,fr,fi,hu,it,ja,lt,nl,no,pl,pt-BR,ru,sk,sv,tr,zh-CN",
"", "",
"styles=" + [...resourceFilePaths, ...commonResourceFilePaths.map(path => `resources_common/${path}`)].join(" "), "styles=" + [...resourceFilePaths, ...commonResourceFilePaths.map(path => `resources-common/${path}`)].join(" "),
"", "",
"##### css classes for form buttons", "##### css classes for form buttons",
"# main class used for all buttons", "# main class used for all buttons",

View File

@ -13,6 +13,7 @@ export type BuildOptionsLike = {
cacheDirPath: string; cacheDirPath: string;
keycloakifyBuildDirPath: string; keycloakifyBuildDirPath: string;
themeNames: string[]; themeNames: string[];
doBuildRetrocompatAccountTheme: boolean;
}; };
{ {
@ -114,7 +115,7 @@ export async function generateJavaStackFiles(params: {
.filter(([, isImplemented]) => isImplemented) .filter(([, isImplemented]) => isImplemented)
.map(([themeType]) => themeType) .map(([themeType]) => themeType)
}, },
...(!implementedThemeTypes.account ...(!implementedThemeTypes.account || !buildOptions.doBuildRetrocompatAccountTheme
? [] ? []
: [ : [
{ {

View File

@ -30,6 +30,7 @@ export function generateStartKeycloakTestingContainer(params: { jarFilePath: str
Buffer.from( Buffer.from(
[ [
"#!/usr/bin/env bash", "#!/usr/bin/env bash",
`# If you want to test with Keycloak version prior to 23 use the retrocompat-${pathBasename(jarFilePath)}`,
"", "",
`docker rm ${containerName} || true`, `docker rm ${containerName} || true`,
"", "",

View File

@ -1,6 +1,6 @@
import { generateTheme } from "./generateTheme"; import { generateTheme } from "./generateTheme";
import { generateJavaStackFiles } from "./generateJavaStackFiles"; import { generateJavaStackFiles } from "./generateJavaStackFiles";
import { join as pathJoin, relative as pathRelative, basename as pathBasename, sep as pathSep } from "path"; import { join as pathJoin, relative as pathRelative, basename as pathBasename, dirname as pathDirname, sep as pathSep } from "path";
import * as child_process from "child_process"; import * as child_process from "child_process";
import { generateStartKeycloakTestingContainer } from "./generateStartKeycloakTestingContainer"; import { generateStartKeycloakTestingContainer } from "./generateStartKeycloakTestingContainer";
import * as fs from "fs"; import * as fs from "fs";
@ -64,6 +64,23 @@ export async function main() {
if (buildOptions.doCreateJar) { if (buildOptions.doCreateJar) {
child_process.execSync("mvn clean install", { "cwd": buildOptions.keycloakifyBuildDirPath }); child_process.execSync("mvn clean install", { "cwd": buildOptions.keycloakifyBuildDirPath });
const jarDirPath = pathDirname(jarFilePath);
const retrocompatJarFilePath = pathJoin(jarDirPath, "retrocompat-" + pathBasename(jarFilePath));
fs.renameSync(pathJoin(jarDirPath, "original-" + pathBasename(jarFilePath)), retrocompatJarFilePath);
fs.writeFileSync(
pathJoin(jarDirPath, "README.md"),
Buffer.from(
[
`- The ${jarFilePath} is to be used in Keycloak 23 and up. `,
`- The ${retrocompatJarFilePath} is to be used in Keycloak 22 and below.`,
` Note that Keycloak 22 is only supported for login and email theme but not for account themes. `
].join("\n"),
"utf8"
)
);
} }
const containerKeycloakVersion = "23.0.0"; const containerKeycloakVersion = "23.0.0";

View File

@ -16,7 +16,7 @@ export function replaceImportsInCssCode(params: { cssCode: string }): {
const cssGlobalsToDefine: Record<string, string> = {}; const cssGlobalsToDefine: Record<string, string> = {};
new Set(cssCode.match(/url\(["']?\/[^/][^)"']+["']?\)[^;}]*/g) ?? []).forEach( new Set(cssCode.match(/url\(["']?\/[^/][^)"']+["']?\)[^;}]*?/g) ?? []).forEach(
match => (cssGlobalsToDefine["url" + crypto.createHash("sha256").update(match).digest("hex").substring(0, 15)] = match) match => (cssGlobalsToDefine["url" + crypto.createHash("sha256").update(match).digest("hex").substring(0, 15)] = match)
); );

View File

@ -1,6 +1,7 @@
import { getLatestsSemVersionedTagFactory } from "./tools/octokit-addons/getLatestsSemVersionedTag"; import { getLatestsSemVersionedTagFactory } from "./tools/octokit-addons/getLatestsSemVersionedTag";
import { Octokit } from "@octokit/rest"; import { Octokit } from "@octokit/rest";
import cliSelect from "cli-select"; import cliSelect from "cli-select";
import { lastKeycloakVersionWithAccountV1 } from "./constants";
export async function promptKeycloakVersion() { export async function promptKeycloakVersion() {
const { getLatestsSemVersionedTag } = (() => { const { getLatestsSemVersionedTag } = (() => {
@ -26,6 +27,7 @@ export async function promptKeycloakVersion() {
"owner": "keycloak", "owner": "keycloak",
"repo": "keycloak" "repo": "keycloak"
}).then(arr => arr.map(({ tag }) => tag))), }).then(arr => arr.map(({ tag }) => tag))),
lastKeycloakVersionWithAccountV1,
"11.0.3" "11.0.3"
]; ];

View File

@ -45,6 +45,8 @@ export default function Template(props: TemplateProps<KcContext, I18n>) {
return null; return null;
} }
document.title = i18n.msgStr("loginTitle", kcContext.realm.displayName);
return ( return (
<div className={getClassName("kcLoginClass")}> <div className={getClassName("kcLoginClass")}>
<div id="kc-header" className={getClassName("kcHeaderClass")}> <div id="kc-header" className={getClassName("kcHeaderClass")}>

View File

@ -95,4 +95,5 @@ export type ClassKey =
| "kcAuthenticatorOtpCircleClass" | "kcAuthenticatorOtpCircleClass"
| "kcSelectOTPItemHeadingClass" | "kcSelectOTPItemHeadingClass"
| "kcFormOptionsWrapperClass" | "kcFormOptionsWrapperClass"
| "kcFormButtonsWrapperClass"; | "kcFormButtonsWrapperClass"
| "kcInputGroup";

View File

@ -82,6 +82,7 @@ export declare namespace KcContext {
clientId: string; clientId: string;
name?: string; name?: string;
description?: string; description?: string;
attributes: Record<string, string>;
}; };
isAppInitiatedAction: boolean; isAppInitiatedAction: boolean;
messagesPerField: { messagesPerField: {

View File

@ -234,7 +234,8 @@ export const kcContextCommonMock: KcContext.Common = {
"showTryAnotherWayLink": false "showTryAnotherWayLink": false
}, },
"client": { "client": {
"clientId": "myApp" "clientId": "myApp",
"attributes": {}
}, },
"scripts": [], "scripts": [],
"isAppInitiatedAction": false "isAppInitiatedAction": false
@ -314,7 +315,8 @@ export const kcContextMocks = [
"actionUri": "#", "actionUri": "#",
"client": { "client": {
"clientId": "myApp", "clientId": "myApp",
"baseUrl": "#" "baseUrl": "#",
"attributes": {}
} }
}), }),
id<KcContext.Error>({ id<KcContext.Error>({
@ -322,7 +324,8 @@ export const kcContextMocks = [
"pageId": "error.ftl", "pageId": "error.ftl",
"client": { "client": {
"clientId": "myApp", "clientId": "myApp",
"baseUrl": "#" "baseUrl": "#",
"attributes": {}
}, },
"message": { "message": {
"type": "error", "type": "error",
@ -496,7 +499,8 @@ export const kcContextMocks = [
}, },
"client": { "client": {
"clientId": "myApp", "clientId": "myApp",
"baseUrl": "#" "baseUrl": "#",
"attributes": {}
}, },
"logoutConfirm": { "code": "123", skipLink: false } "logoutConfirm": { "code": "123", skipLink: false }
}), }),

View File

@ -67,6 +67,7 @@ export const { useGetClassName } = createUseClassName<ClassKey>({
// css classes for input // css classes for input
"kcInputLargeClass": "input-lg", "kcInputLargeClass": "input-lg",
"kcInputGroup": "pf-c-input-group",
// css classes for form accessability // css classes for form accessability
"kcSrOnlyClass": "sr-only", "kcSrOnlyClass": "sr-only",

View File

@ -37,22 +37,18 @@ export default function Login(props: PageProps<Extract<KcContext, { pageId: "log
return ( return (
<Template <Template
{...{ kcContext, i18n, doUseDefaultCss, classes }} {...{ kcContext, i18n, doUseDefaultCss, classes }}
displayInfo={social.displayInfo} displayInfo={realm.password && realm.registrationAllowed && !registrationDisabled}
displayWide={realm.password && social.providers !== undefined} displayWide={realm.password && social.providers !== undefined}
headerNode={msg("doLogIn")} headerNode={msg("doLogIn")}
infoNode={ infoNode={
realm.password && <div id="kc-registration">
realm.registrationAllowed && <span>
!registrationDisabled && ( {msg("noAccount")}
<div id="kc-registration"> <a tabIndex={6} href={url.registrationUrl}>
<span> {msg("doRegister")}
{msg("noAccount")} </a>
<a tabIndex={6} href={url.registrationUrl}> </span>
{msg("doRegister")} </div>
</a>
</span>
</div>
)
} }
> >
<div id="kc-form" className={clsx(realm.password && social.providers !== undefined && getClassName("kcContentWrapperClass"))}> <div id="kc-form" className={clsx(realm.password && social.providers !== undefined && getClassName("kcContentWrapperClass"))}>

View File

@ -1,12 +1,11 @@
import { clsx } from "keycloakify/tools/clsx"; import { clsx } from "keycloakify/tools/clsx";
import Template from "../Template";
import { I18n } from "../i18n"; import { I18n } from "../i18n";
import { KcContext } from "../kcContext"; import { KcContext } from "../kcContext";
import { useGetClassName } from "../lib/useGetClassName"; import { useGetClassName } from "keycloakify/login/lib/useGetClassName";
import { PageProps } from "./PageProps"; import { PageProps } from "./PageProps";
export default function LoginOauthGrant(props: PageProps<Extract<KcContext, { pageId: "login-oauth2-device-verify-user-code.ftl" }>, I18n>) { export default function LoginOauthGrant(props: PageProps<Extract<KcContext, { pageId: "login-oauth2-device-verify-user-code.ftl" }>, I18n>) {
const { kcContext, i18n, doUseDefaultCss, classes } = props; const { kcContext, i18n, doUseDefaultCss, classes, Template } = props;
const { url } = kcContext; const { url } = kcContext;
const { msg, msgStr } = i18n; const { msg, msgStr } = i18n;

View File

@ -2,11 +2,10 @@ import { clsx } from "keycloakify/tools/clsx";
import { PageProps } from "./PageProps"; import { PageProps } from "./PageProps";
import { KcContext } from "../kcContext"; import { KcContext } from "../kcContext";
import { I18n } from "../i18n"; import { I18n } from "../i18n";
import Template from "../Template";
import { useGetClassName } from "keycloakify/login/lib/useGetClassName"; import { useGetClassName } from "keycloakify/login/lib/useGetClassName";
export default function LoginOauthGrant(props: PageProps<Extract<KcContext, { pageId: "login-oauth-grant.ftl" }>, I18n>) { export default function LoginOauthGrant(props: PageProps<Extract<KcContext, { pageId: "login-oauth-grant.ftl" }>, I18n>) {
const { kcContext, i18n, doUseDefaultCss, classes } = props; const { kcContext, i18n, doUseDefaultCss, classes, Template } = props;
const { url, oauth, client } = kcContext; const { url, oauth, client } = kcContext;
const { msg, msgStr, advancedMsg, advancedMsgStr } = i18n; const { msg, msgStr, advancedMsg, advancedMsgStr } = i18n;

View File

@ -1,9 +1,9 @@
import type { LazyExoticComponent } from "react";
import type { I18n } from "keycloakify/login/i18n"; import type { I18n } from "keycloakify/login/i18n";
import { type TemplateProps, type ClassKey } from "keycloakify/login/TemplateProps"; import { type TemplateProps, type ClassKey } from "keycloakify/login/TemplateProps";
import type { LazyOrNot } from "keycloakify/tools/LazyOrNot";
export type PageProps<KcContext, I18nExtended extends I18n> = { export type PageProps<KcContext, I18nExtended extends I18n> = {
Template: LazyExoticComponent<(props: TemplateProps<any, any>) => JSX.Element | null>; Template: LazyOrNot<(props: TemplateProps<any, any>) => JSX.Element | null>;
kcContext: KcContext; kcContext: KcContext;
i18n: I18nExtended; i18n: I18nExtended;
doUseDefaultCss: boolean; doUseDefaultCss: boolean;

3
src/tools/LazyOrNot.ts Normal file
View File

@ -0,0 +1,3 @@
import type { LazyExoticComponent, ComponentType } from "react";
export type LazyOrNot<Component extends ComponentType<any>> = LazyExoticComponent<Component> | Component;

View File

@ -67,9 +67,6 @@ describe("Ensure it's able to extract used Keycloak resources", () => {
` `
}); });
console.log(paths);
console.log(expectedPaths);
expect(same(paths, expectedPaths)).toBe(true); expect(same(paths, expectedPaths)).toBe(true);
}); });

View File

@ -124,7 +124,7 @@ describe("bin/css-transforms", () => {
} }
.my-div2 { .my-div2 {
background: url(/logo192.png) no-repeat center center; background: url(/logo192.png) repeat center center;
} }
.my-div { .my-div {
@ -135,11 +135,11 @@ describe("bin/css-transforms", () => {
const fixedCssCodeExpected = ` const fixedCssCodeExpected = `
.my-div { .my-div {
background: var(--url1f9ef5a892c104c); background: var(--urla882a969fd39473) no-repeat center center;
} }
.my-div2 { .my-div2 {
background: var(--url1f9ef5a892c104c); background: var(--urla882a969fd39473) repeat center center;
} }
.my-div { .my-div {
@ -150,7 +150,7 @@ describe("bin/css-transforms", () => {
expect(isSameCode(fixedCssCode, fixedCssCodeExpected)).toBe(true); expect(isSameCode(fixedCssCode, fixedCssCodeExpected)).toBe(true);
const cssGlobalsToDefineExpected = { const cssGlobalsToDefineExpected = {
"url1f9ef5a892c104c": "url(/logo192.png) no-repeat center center", "urla882a969fd39473": "url(/logo192.png)",
"urldd75cab58377c19": "url(/static/media/something.svg)" "urldd75cab58377c19": "url(/static/media/something.svg)"
}; };
@ -165,7 +165,7 @@ describe("bin/css-transforms", () => {
const cssCodeToPrependInHeadExpected = ` const cssCodeToPrependInHeadExpected = `
:root { :root {
--url1f9ef5a892c104c: url(\${url.resourcesPath}/build/logo192.png) no-repeat center center; --urla882a969fd39473: url(\${url.resourcesPath}/build/logo192.png);
--urldd75cab58377c19: url(\${url.resourcesPath}/build/static/media/something.svg); --urldd75cab58377c19: url(\${url.resourcesPath}/build/static/media/something.svg);
} }
`; `;
@ -191,11 +191,11 @@ describe("bin/css-transforms", () => {
const fixedCssCodeExpected = ` const fixedCssCodeExpected = `
.my-div { .my-div {
background: var(--urlf8277cddaa2be78); background: var(--url749a3139386b2c8) no-repeat center center;
} }
.my-div2 { .my-div2 {
background: var(--urlf8277cddaa2be78); background: var(--url749a3139386b2c8) no-repeat center center;
} }
.my-div { .my-div {
@ -206,7 +206,7 @@ describe("bin/css-transforms", () => {
expect(isSameCode(fixedCssCode, fixedCssCodeExpected)).toBe(true); expect(isSameCode(fixedCssCode, fixedCssCodeExpected)).toBe(true);
const cssGlobalsToDefineExpected = { const cssGlobalsToDefineExpected = {
"urlf8277cddaa2be78": "url(/x/y/z/logo192.png) no-repeat center center", "url749a3139386b2c8": "url(/x/y/z/logo192.png)",
"url8bdc0887b97ac9a": "url(/x/y/z/static/media/something.svg)" "url8bdc0887b97ac9a": "url(/x/y/z/static/media/something.svg)"
}; };
@ -221,7 +221,7 @@ describe("bin/css-transforms", () => {
const cssCodeToPrependInHeadExpected = ` const cssCodeToPrependInHeadExpected = `
:root { :root {
--urlf8277cddaa2be78: url(\${url.resourcesPath}/build/logo192.png) no-repeat center center; --url749a3139386b2c8: url(\${url.resourcesPath}/build/logo192.png);
--url8bdc0887b97ac9a: url(\${url.resourcesPath}/build/static/media/something.svg); --url8bdc0887b97ac9a: url(\${url.resourcesPath}/build/static/media/something.svg);
} }
`; `;