Fixed Keycloak v19 themes incompatibility issues with Keycloak v16 themes

This commit is contained in:
Vincenzo Cestone 2022-09-28 18:56:07 +02:00
parent e17cf835ba
commit 905bec8ead
11 changed files with 120 additions and 108 deletions

View File

@ -3,7 +3,7 @@
"id": "personal-info", "id": "personal-info",
"path": "personal-info", "path": "personal-info",
"icon": "pf-icon-user", "icon": "pf-icon-user",
"label": "personalInfoHtmlTitle", "label": "personalInfoSidebarTitle",
"descriptionLabel": "personalInfoIntroMessage", "descriptionLabel": "personalInfoIntroMessage",
"content" : [ "content" : [
{ {
@ -25,27 +25,27 @@
{ {
"id": "security", "id": "security",
"icon": "pf-icon-security", "icon": "pf-icon-security",
"label": "Account Security", "label": "accountSecuritySidebarTitle",
"descriptionLabel": "accountSecurityIntroMessage", "descriptionLabel": "accountSecurityIntroMessage",
"content": [ "content": [
{ {
"id": "signingin", "id": "signingin",
"path": "security/signingin", "path": "security/signingin",
"label": "signingIn", "label": "signingInSidebarTitle",
"modulePath": "/content/signingin-page/SigningInPage.js", "modulePath": "/content/signingin-page/SigningInPage.js",
"componentName": "SigningInPage" "componentName": "SigningInPage"
}, },
{ {
"id": "device-activity", "id": "device-activity",
"path": "security/device-activity", "path": "security/device-activity",
"label": "device-activity", "label": "deviceActivitySidebarTitle",
"modulePath": "/content/device-activity-page/DeviceActivityPage.js", "modulePath": "/content/device-activity-page/DeviceActivityPage.js",
"componentName": "DeviceActivityPage" "componentName": "DeviceActivityPage"
}, },
{ {
"id": "linked-accounts", "id": "linked-accounts",
"path": "security/linked-accounts", "path": "security/linked-accounts",
"label": "linkedAccountsHtmlTitle", "label": "linkedAccountsSidebarTitle",
"modulePath": "/content/linked-accounts-page/LinkedAccountsPage.js", "modulePath": "/content/linked-accounts-page/LinkedAccountsPage.js",
"componentName": "LinkedAccountsPage", "componentName": "LinkedAccountsPage",
"hidden": "!features.isLinkedAccountsEnabled" "hidden": "!features.isLinkedAccountsEnabled"
@ -61,6 +61,16 @@
"modulePath": "/content/applications-page/ApplicationsPage.js", "modulePath": "/content/applications-page/ApplicationsPage.js",
"componentName": "ApplicationsPage" "componentName": "ApplicationsPage"
}, },
{
"id": "groups",
"path": "groups",
"icon": "pf-icon-server-group",
"label": "groupLabel",
"descriptionLabel": "groupDescriptionLabel",
"modulePath": "/content/group-page/GroupsPage.js",
"componentName": "GroupsPage",
"hidden": "!features.isViewGroupsEnabled"
},
{ {
"id": "resources", "id": "resources",
"icon": "pf-icon-repository", "icon": "pf-icon-repository",

View File

@ -1,7 +1,7 @@
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
import * as React from "../../../../common/keycloak/web_modules/react.js"; import * as React from "../../../../common/keycloak/web_modules/react.js";
import { Button, Grid, GridItem, Expandable, Modal } from "../../../../common/keycloak/web_modules/@patternfly/react-core.js"; import { PageSection, PageSectionVariants, Button, Grid, GridItem, ExpandableSection, Modal } from "../../../../common/keycloak/web_modules/@patternfly/react-core.js";
import { AccountServiceContext } from "../../account-service/AccountServiceContext.js"; import { AccountServiceContext } from "../../account-service/AccountServiceContext.js";
import { Msg } from "../../widgets/Msg.js"; import { Msg } from "../../widgets/Msg.js";
import { ContentPage } from "../ContentPage.js"; import { ContentPage } from "../ContentPage.js";
@ -38,62 +38,66 @@ export class AccountExtraPage extends React.Component {
render() { render() {
const accountUrl = this.context["accountUrl"]; const accountUrl = this.context["accountUrl"];
return React.createElement(ContentPage, { return /*#__PURE__*/React.createElement(ContentPage, {
title: "accountExtraInfoHtmlTitle", title: "accountExtraInfoHtmlTitle",
introMessage: "accountExtraSubMessage" introMessage: "accountExtraSubMessage"
}, React.createElement(AvatarForm, { }, /*#__PURE__*/React.createElement(PageSection, {
isFilled: true,
variant: PageSectionVariants.light
}, /*#__PURE__*/React.createElement(AvatarForm, {
accountUrl: accountUrl accountUrl: accountUrl
}), React.createElement("div", { }), /*#__PURE__*/React.createElement("div", {
id: "delete-account", id: "delete-account",
style: { style: {
marginTop: "30px" marginTop: "30px"
} }
}, React.createElement(Expandable, { }, /*#__PURE__*/React.createElement(ExpandableSection, {
toggleText: Msg.localize('deleteAccount') toggleText: Msg.localize('deleteAccount'),
}, React.createElement(Grid, { displaySize: "large"
gutter: "sm" }, /*#__PURE__*/React.createElement(Grid, {
}, React.createElement(GridItem, { hasGutter: true
}, /*#__PURE__*/React.createElement(GridItem, {
span: 8 span: 8
}, React.createElement("p", { }, /*#__PURE__*/React.createElement("p", {
dangerouslySetInnerHTML: { dangerouslySetInnerHTML: {
__html: Msg.localize('deleteAccountInfoMessage') __html: Msg.localize('deleteAccountInfoMessage')
} }
})), React.createElement(GridItem, { })), /*#__PURE__*/React.createElement(GridItem, {
span: 4 span: 4
}, React.createElement(Button, { }, /*#__PURE__*/React.createElement(Button, {
id: "delete-account-btn", id: "delete-account-btn",
variant: "danger", variant: "danger",
onClick: e => this.handleModalToggle(true), onClick: e => this.handleModalToggle(true),
className: "delete-button" className: "delete-button"
}, React.createElement(Msg, { }, /*#__PURE__*/React.createElement(Msg, {
msgKey: "doDelete" msgKey: "doDelete"
}))))), React.createElement(Modal, { }))))), /*#__PURE__*/React.createElement(Modal, {
width: '50%', width: '50%',
title: Msg.localize('deleteAccountDialogHeader'), title: Msg.localize('deleteAccountDialogHeader'),
isOpen: this.state.isModalOpen, isOpen: this.state.isModalOpen,
onClose: () => this.handleModalToggle(false), onClose: () => this.handleModalToggle(false),
actions: [React.createElement(Button, { actions: [/*#__PURE__*/React.createElement(Button, {
key: "confirm", key: "confirm",
variant: "danger", variant: "danger",
onClick: this.modalConfirmDelete onClick: this.modalConfirmDelete
}, React.createElement(Msg, { }, /*#__PURE__*/React.createElement(Msg, {
msgKey: "doDeleteConfirm" msgKey: "doDeleteConfirm"
})), React.createElement(Button, { })), /*#__PURE__*/React.createElement(Button, {
key: "cancel", key: "cancel",
variant: "secondary", variant: "secondary",
onClick: e => this.handleModalToggle(false) onClick: e => this.handleModalToggle(false)
}, React.createElement(Msg, { }, /*#__PURE__*/React.createElement(Msg, {
msgKey: "doCancel" msgKey: "doCancel"
}))] }))]
}, React.createElement("div", { }, /*#__PURE__*/React.createElement("div", {
dangerouslySetInnerHTML: { dangerouslySetInnerHTML: {
__html: Msg.localize('deleteAccountWarningMessage') __html: Msg.localize('deleteAccountWarningMessage')
} }
}), React.createElement("div", { }), /*#__PURE__*/React.createElement("div", {
dangerouslySetInnerHTML: { dangerouslySetInnerHTML: {
__html: Msg.localize('deleteAccountConfirmMessage') __html: Msg.localize('deleteAccountConfirmMessage')
} }
})))); })))));
} }
} }

View File

@ -141,49 +141,48 @@ export class AvatarForm extends React.Component {
border: '1px solid lightgray', border: '1px solid lightgray',
boxShadow: 'lightgray 6px 3px 10px 2px' boxShadow: 'lightgray 6px 3px 10px 2px'
}; };
return React.createElement(Form, { return /*#__PURE__*/React.createElement(Form, {
id: "avatarForm", id: "avatarForm",
method: "post", method: "post",
isHorizontal: true,
action: avatarUrl, action: avatarUrl,
encType: "multipart/form-data", encType: "multipart/form-data",
onSubmit: event => this.handleSubmit(event) onSubmit: event => this.handleSubmit(event)
}, React.createElement(FormGroup, { }, /*#__PURE__*/React.createElement(FormGroup, {
label: Msg.localize('avatarLabel'), label: Msg.localize('avatarLabel'),
fieldId: "avatar-current-or-preview", fieldId: "avatar-current-or-preview",
helperTextInvalid: this.state.errors.avatar, helperTextInvalid: this.state.errors.avatar,
isValid: this.state.errors.avatar === '' validated: this.state.errors.avatar === '' ? 'success' : 'error'
}, avatarSrc !== "" ? React.createElement(Avatar, { }, avatarSrc !== "" ? /*#__PURE__*/React.createElement(Avatar, {
src: avatarSrc, src: avatarSrc,
style: avatarStyle, style: avatarStyle,
alt: "Avatar image preview", alt: "Avatar image preview",
onError: this.handleError onError: this.handleError
}) : React.createElement(Avatar, { }) : /*#__PURE__*/React.createElement(Avatar, {
src: noAvatarSrc, src: noAvatarSrc,
style: avatarStyle, style: avatarStyle,
alt: "No avatar found" alt: "No avatar found"
})), React.createElement(FormGroup, { })), /*#__PURE__*/React.createElement(FormGroup, {
fieldId: "avatar-upload", fieldId: "avatar-upload",
label: React.createElement("span", null, React.createElement(Msg, { label: /*#__PURE__*/React.createElement("span", null, /*#__PURE__*/React.createElement(Msg, {
msgKey: "uploadLabel" msgKey: "uploadLabel"
}), ' ', React.createElement(Tooltip, { }), ' ', /*#__PURE__*/React.createElement(Tooltip, {
content: React.createElement(Msg, { content: /*#__PURE__*/React.createElement(Msg, {
msgKey: "avatarInfo" msgKey: "avatarInfo"
}) })
}, React.createElement(OutlinedQuestionCircleIcon, null))) }, /*#__PURE__*/React.createElement(OutlinedQuestionCircleIcon, null)))
}, React.createElement(FileUpload, { }, /*#__PURE__*/React.createElement(FileUpload, {
id: "simple-file", id: "simple-file",
filename: filename, filename: filename,
filenamePlaceholder: Msg.localize('dragdropInfo'), filenamePlaceholder: Msg.localize('dragdropInfo'),
browseButtonText: Msg.localize('browseButton'), browseButtonText: Msg.localize('browseButton'),
clearButtonText: Msg.localize('clearButton'), clearButtonText: Msg.localize('clearButton'),
onChange: this.handleFileInputChange onChange: this.handleFileInputChange
})), React.createElement(ActionGroup, null, React.createElement(Button, { })), /*#__PURE__*/React.createElement(ActionGroup, null, /*#__PURE__*/React.createElement(Button, {
id: "save-btn", id: "save-btn",
type: "submit", type: "submit",
variant: "primary", variant: "primary",
isDisabled: filename === "" isDisabled: filename === ""
}, React.createElement(Msg, { }, /*#__PURE__*/React.createElement(Msg, {
msgKey: "doSave" msgKey: "doSave"
})))); }))));
} }

View File

@ -1,6 +1,6 @@
import * as React from 'react'; import * as React from 'react';
import { Button, Grid, GridItem, Expandable, Modal, Form } from '@patternfly/react-core'; import { PageSection, PageSectionVariants,Button, Grid, GridItem, ExpandableSection, Modal } from '@patternfly/react-core';
import { AccountServiceContext } from '../../account-service/AccountServiceContext'; import { AccountServiceContext } from '../../account-service/AccountServiceContext';
import { Msg } from '../../widgets/Msg'; import { Msg } from '../../widgets/Msg';
import { ContentPage } from '../ContentPage'; import { ContentPage } from '../ContentPage';
@ -45,42 +45,44 @@ export class AccountExtraPage extends React.Component<AccountExtraPageProps, Acc
<ContentPage title="accountExtraInfoHtmlTitle" <ContentPage title="accountExtraInfoHtmlTitle"
introMessage="accountExtraSubMessage" introMessage="accountExtraSubMessage"
> >
<AvatarForm accountUrl={accountUrl} /> <PageSection isFilled variant={PageSectionVariants.light}>
<AvatarForm accountUrl={accountUrl} />
<div id="delete-account" style={{marginTop:"30px"}}> <div id="delete-account" style={{marginTop:"30px"}}>
<Expandable toggleText={Msg.localize('deleteAccount')}> <ExpandableSection toggleText={Msg.localize('deleteAccount')} displaySize="large">
<Grid gutter={"sm"}> <Grid hasGutter>
<GridItem span={8}> <GridItem span={8}>
<p dangerouslySetInnerHTML={{ __html: Msg.localize('deleteAccountInfoMessage')}} /> <p dangerouslySetInnerHTML={{ __html: Msg.localize('deleteAccountInfoMessage')}} />
</GridItem> </GridItem>
<GridItem span={4}> <GridItem span={4}>
<Button id="delete-account-btn" variant="danger" <Button id="delete-account-btn" variant="danger"
onClick={(e) => this.handleModalToggle(true)} className="delete-button" onClick={(e) => this.handleModalToggle(true)} className="delete-button"
> >
<Msg msgKey="doDelete" /> <Msg msgKey="doDelete" />
</Button>
</GridItem>
</Grid>
</ExpandableSection>
<Modal
width={'50%'}
title={Msg.localize('deleteAccountDialogHeader')}
isOpen={this.state.isModalOpen}
onClose={() => this.handleModalToggle(false)}
actions={[
<Button key="confirm" variant="danger" onClick={this.modalConfirmDelete}>
<Msg msgKey="doDeleteConfirm" />
</Button>,
<Button key="cancel" variant="secondary" onClick={(e) => this.handleModalToggle(false)}>
<Msg msgKey="doCancel" />
</Button> </Button>
</GridItem> ]}
</Grid> >
</Expandable> <div dangerouslySetInnerHTML={{ __html: Msg.localize('deleteAccountWarningMessage')}} />
<div dangerouslySetInnerHTML={{ __html: Msg.localize('deleteAccountConfirmMessage')}} />
<Modal </Modal>
width={'50%'} </div>
title={Msg.localize('deleteAccountDialogHeader')} </PageSection>
isOpen={this.state.isModalOpen}
onClose={() => this.handleModalToggle(false)}
actions={[
<Button key="confirm" variant="danger" onClick={this.modalConfirmDelete}>
<Msg msgKey="doDeleteConfirm" />
</Button>,
<Button key="cancel" variant="secondary" onClick={(e) => this.handleModalToggle(false)}>
<Msg msgKey="doCancel" />
</Button>
]}
>
<div dangerouslySetInnerHTML={{ __html: Msg.localize('deleteAccountWarningMessage')}} />
<div dangerouslySetInnerHTML={{ __html: Msg.localize('deleteAccountConfirmMessage')}} />
</Modal>
</div>
</ContentPage> </ContentPage>
) )

View File

@ -139,14 +139,14 @@ export class AvatarForm extends React.Component<AvatarFormProps, AvatarFormState
boxShadow: 'lightgray 6px 3px 10px 2px' boxShadow: 'lightgray 6px 3px 10px 2px'
} }
return ( return (
<Form id="avatarForm" method="post" isHorizontal <Form id="avatarForm" method="post"
action={avatarUrl} encType="multipart/form-data" action={avatarUrl} encType="multipart/form-data"
onSubmit={event => this.handleSubmit(event)} onSubmit={event => this.handleSubmit(event)}
> >
<FormGroup label={Msg.localize('avatarLabel')} <FormGroup label={Msg.localize('avatarLabel')}
fieldId="avatar-current-or-preview" fieldId="avatar-current-or-preview"
helperTextInvalid={this.state.errors.avatar} helperTextInvalid={this.state.errors.avatar}
isValid={this.state.errors.avatar === ''} validated={this.state.errors.avatar === '' ? 'success' : 'error'}
> >
{ avatarSrc !== "" { avatarSrc !== ""
? <Avatar src={avatarSrc} style={avatarStyle} alt="Avatar image preview" onError={this.handleError}/> ? <Avatar src={avatarSrc} style={avatarStyle} alt="Avatar image preview" onError={this.handleError}/>

View File

@ -1,19 +1,13 @@
# This theme will inherit everything from its parent unless
# it is overridden in the current theme.
parent=keycloak.v2 parent=keycloak.v2
# Look at the styles.css file to see examples of using PatternFly's CSS variables
# for modifying look and feel.
styles=css/styles.css
# This is the logo in upper lefthand corner. # This is the logo in upper lefthand corner.
# It must be a relative path. # It must be a relative path.
logo=/public/d4science-logo.png logo=/public/d4science-logo.png
# This is the link followed when clicking on the logo. # This is the link followed when clicking on the logo.
# It can be any valid URL, including an external site. # It can be any valid URL, including an external site.
logoUrl=./ logoUrl=https://www.d4science.org/
# This is the icon for the account console. # This is the icon for the account console.
# It must be a relative path. # It must be a relative path.
favIcon=/public/favicon.ico favIcon=/public/favicon.ico

View File

@ -26,23 +26,25 @@
</div> </div>
</div> </div>
</#if> </#if>
<div class="${properties.kcFormGroupClass!}"> <#if user.editEmailAllowed>
<div class="${properties.kcLabelWrapperClass!}"> <div class="${properties.kcFormGroupClass!}">
<label for="email" class="${properties.kcLabelClass!}">${msg("email")}</label> <div class="${properties.kcLabelWrapperClass!}">
</div> <label for="email" class="${properties.kcLabelClass!}">${msg("email")}</label>
<div class="${properties.kcInputWrapperClass!}"> </div>
<input type="text" id="email" name="email" value="${(user.email!'')}" <div class="${properties.kcInputWrapperClass!}">
class="${properties.kcInputClass!}" <input type="text" id="email" name="email" value="${(user.email!'')}"
aria-invalid="<#if messagesPerField.existsError('email')>true</#if>" class="${properties.kcInputClass!}"
/> aria-invalid="<#if messagesPerField.existsError('email')>true</#if>"
/>
<#if messagesPerField.existsError('email')> <#if messagesPerField.existsError('email')>
<span id="input-error-email" class="${properties.kcInputErrorMessageClass!}" aria-live="polite"> <span id="input-error-email" class="${properties.kcInputErrorMessageClass!}" aria-live="polite">
${kcSanitize(messagesPerField.get('email'))?no_esc} ${kcSanitize(messagesPerField.get('email'))?no_esc}
</span> </span>
</#if> </#if>
</div>
</div> </div>
</div> </#if>
<div class="${properties.kcFormGroupClass!}"> <div class="${properties.kcFormGroupClass!}">
<div class="${properties.kcLabelWrapperClass!}"> <div class="${properties.kcLabelWrapperClass!}">

View File

@ -1,6 +1,6 @@
<#macro registrationLayout bodyClass="" displayInfo=false displayMessage=true displayRequiredFields=false displayWide=false> <#macro registrationLayout bodyClass="" displayInfo=false displayMessage=true displayRequiredFields=false displayWide=false>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" class="${properties.kcHtmlClass!}"> <html class="${properties.kcHtmlClass!}">
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
@ -47,6 +47,7 @@
class="${properties.kcHeaderWrapperClass!}">${kcSanitize(msg("loginTitleHtml",(realm.displayNameHtml!'')))?no_esc}</div> class="${properties.kcHeaderWrapperClass!}">${kcSanitize(msg("loginTitleHtml",(realm.displayNameHtml!'')))?no_esc}</div>
</div> </div>
--> -->
<div style="${properties.headerReplacementStyle!}"></div>
<div class="${properties.kcFormCardClass!} <#if displayWide>${properties.kcFormCardWideClass!}</#if>"> <div class="${properties.kcFormCardClass!} <#if displayWide>${properties.kcFormCardWideClass!}</#if>">
<!-- D4Science logos header inside formCard --> <!-- D4Science logos header inside formCard -->
@ -114,7 +115,7 @@
<#nested "show-username"> <#nested "show-username">
<div id="kc-username" class="${properties.kcFormGroupClass!}"> <div id="kc-username" class="${properties.kcFormGroupClass!}">
<label id="kc-attempted-username">${auth.attemptedUsername}</label> <label id="kc-attempted-username">${auth.attemptedUsername}</label>
<a id="reset-login" href="${url.loginRestartFlowUrl}"> <a id="reset-login" href="${url.loginRestartFlowUrl}" aria-label="${msg("restartLoginTooltip")}">
<div class="kc-login-tooltip"> <div class="kc-login-tooltip">
<i class="${properties.kcResetFlowIcon!}"></i> <i class="${properties.kcResetFlowIcon!}"></i>
<span class="kc-tooltip-text">${msg("restartLoginTooltip")}</span> <span class="kc-tooltip-text">${msg("restartLoginTooltip")}</span>
@ -127,7 +128,7 @@
<#nested "show-username"> <#nested "show-username">
<div id="kc-username" class="${properties.kcFormGroupClass!}"> <div id="kc-username" class="${properties.kcFormGroupClass!}">
<label id="kc-attempted-username">${auth.attemptedUsername}</label> <label id="kc-attempted-username">${auth.attemptedUsername}</label>
<a id="reset-login" href="${url.loginRestartFlowUrl}"> <a id="reset-login" href="${url.loginRestartFlowUrl}" aria-label="${msg("restartLoginTooltip")}">
<div class="kc-login-tooltip"> <div class="kc-login-tooltip">
<i class="${properties.kcResetFlowIcon!}"></i> <i class="${properties.kcResetFlowIcon!}"></i>
<span class="kc-tooltip-text">${msg("restartLoginTooltip")}</span> <span class="kc-tooltip-text">${msg("restartLoginTooltip")}</span>

View File

@ -1,14 +1,14 @@
parent=keycloak parent=keycloak
import=common/keycloak import=common/keycloak
#styles=node_modules/patternfly/dist/css/patternfly.min.css node_modules/patternfly/dist/css/patternfly-additions.min.css lib/zocial/zocial.css css/d4science.css styles=css/login.css css/d4science.css
styles=css/login.css css/tile.css css/d4science.css
#stylesCommon=web_modules/@patternfly/react-core/dist/styles/base.css web_modules/@patternfly/react-core/dist/styles/app.css node_modules/patternfly/dist/css/patternfly.min.css node_modules/patternfly/dist/css/patternfly-additions.min.css lib/pficon/pficon.css #stylesCommon=web_modules/@patternfly/react-core/dist/styles/base.css web_modules/@patternfly/react-core/dist/styles/app.css node_modules/patternfly/dist/css/patternfly.min.css node_modules/patternfly/dist/css/patternfly-additions.min.css lib/pficon/pficon.css
#titleTag= #titleTag=
#favicon=https://services.d4science.org/favicon.ico #favicon=https://services.d4science.org/favicon.ico
contentBgImg=img/color-triangles.png contentBgImg=img/color-triangles.png
#contentStyle=background: url("https://url-of-background-img"); #contentStyle=background: url("https://url-of-background-img");
headerReplacementStyle=height: 0;
logoHeaderStyle=display: flex; justify-content: space-between; width: 100%; logoHeaderStyle=display: flex; justify-content: space-between; width: 100%;