KEYCLOAK-14531 Welcome cards should be driven by content.json

`content.js` is now `content.json` it's used in freemarker to create the cards
This commit is contained in:
Erik Jan de Wit 2020-06-22 14:42:23 +02:00 committed by Stan Silvert
parent 1b988cc12e
commit 55291bad76
10 changed files with 111 additions and 114 deletions

View file

@ -30,11 +30,14 @@ import javax.ws.rs.Path;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Response;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import java.util.Scanner;
import java.util.function.Function;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.ws.rs.core.UriInfo;
@ -104,7 +107,15 @@ public class AccountConsole {
map.put("msgJSON", messagesToJsonString(messages));
map.put("supportedLocales", supportedLocales(messages));
map.put("properties", theme.getProperties());
map.put("theme", (Function<String, String>) file -> {
try {
final InputStream resource = theme.getResourceAsStream(file);
return new Scanner(resource, "UTF-8").useDelimiter("\\A").next();
} catch (IOException e) {
throw new RuntimeException("could not load file", e);
}
});
EventStoreProvider eventStore = session.getProvider(EventStoreProvider.class);
map.put("isEventsEnabled", eventStore != null && realm.isEventsEnabled());
map.put("isAuthorizationEnabled", true);

View file

@ -64,14 +64,14 @@ public abstract class AbstractLoggedInPage extends AbstractAccountPage {
}
/**
* This is currently used only by navigation menu to identify nav items. See content.js in themes module for IDs.
* This is currently used only by navigation menu to identify nav items. See content.json in themes module for IDs.
*
* @return page ID
*/
public abstract String getPageId();
/**
* In case the page is placed is a subpage, i.e. placed in a subsection. See content.js in themes module for IDs.
* In case the page is placed is a subpage, i.e. placed in a subsection. See content.json in themes module for IDs.
*
* @return parent page ID
*/

View file

@ -41,21 +41,21 @@ public class WelcomeScreen extends AbstractAccountPage {
@FindBy(xpath = "//*[@id='" + ROOT_ELEMENT_ID + "']//header")
private WelcomeScreenHeader header;
@FindBy(xpath = "//*[@id='landingPersonalInfoLink']/a")
@FindBy(xpath = "//*[@id='landingpersonal-infoLink']/a")
private WebElement personalInfoLink;
@FindBy(xpath = "//*[@id='landingChangePasswordLink']/a")
private WebElement changePasswordLink;
@FindBy(xpath = "//*[@id='landingAuthenticatorLink']/a")
private WebElement authenticatorLink;
@FindBy(xpath = "//*[@id='landingDeviceActivityLink']/a")
@FindBy(xpath = "//*[@id='landingdevice-activityLink']/a")
private WebElement deviceActivityLink;
@FindBy(xpath = "//*[@id='landingLinkedAccountsLink']/a")
@FindBy(xpath = "//*[@id='landinglinked-accountsLink']/a")
private WebElement linkedAccountsLink;
@FindBy(xpath = "//*[@id='landingApplicationsLink']/a")
@FindBy(xpath = "//*[@id='landingapplicationsLink']/a")
private WebElement applicationsLink;
@FindBy(id = "landingMyResourcesCard")
@FindBy(id = "landingresourcesCard")
private WebElement myResourcesCard;
@FindBy(xpath = "//*[@id='landingMyResourcesLink']/a")
@FindBy(xpath = "//*[@id='landingresourcesLink']/a")
private WebElement myResourcesLink;
@FindBy(id = "landingLogo")
private WebElement logoLink;

View file

@ -99,6 +99,10 @@
</#list>
</#if>
<script>
var content = <#include "resources/content.json"/>
</script>
<#if properties.styles?has_content>
<#list properties.styles?split(' ') as style>
<link href="${resourceUrl}/${style}" rel="stylesheet"/>
@ -222,52 +226,26 @@
</section>
<section class="pf-c-page__main-section">
<div class="pf-l-gallery pf-m-gutter">
<div class="pf-l-gallery__item">
<div class="pf-c-card">
<div class="pf-c-card__header pf-c-content">
<h2><i class="pf-icon pf-icon-user"></i>&nbsp${msg("personalInfoHtmlTitle")}</h2>
<h6>${msg("personalInfoIntroMessage")}</h6>
</div>
<div class="pf-c-card__body pf-c-content">
<h5 id="landingPersonalInfoLink" onclick="toggleReact()"><a href="#personal-info">${msg("personalInfoHtmlTitle")}</a></h5>
<#assign content=theme.apply("/content.json")?eval>
<#list content as item>
<div class="pf-l-gallery__item">
<div class="pf-c-card" id="landing${item.id}Card">
<div class="pf-c-card__header pf-c-content">
<h2><i class="pf-icon ${item.icon}"></i>&nbsp${msg(item.label)}</h2>
<h6>${msg(item.descriptionLabel)}</h6>
</div>
<div class="pf-c-card__body pf-c-content">
<#if item.content??>
<#list item.content as sub>
<h5 id="landing${sub.id}Link" onclick="toggleReact()"><a href="#${sub.path}">${msg(sub.label)}</a></h5>
</#list>
<#else>
<h5 id="landing${item.id}Link" onclick="toggleReact()"><a href="#${item.path}">${msg(item.label)}</a></h5>
</#if>
</div>
</div>
</div>
</div>
<div class="pf-l-gallery__item">
<div class="pf-c-card">
<div class="pf-c-card__header pf-c-content">
<h2><i class="pf-icon pf-icon-security"></i>&nbsp${msg("accountSecurityTitle")}</h2>
<h6>${msg("accountSecurityIntroMessage")}</h6>
</div>
<div class="pf-c-card__body pf-c-content">
<h5 id="landingSigningInLink" onclick="toggleReact()"><a href="#security/signingin">${msg("signingIn")}</a></h5>
<h5 id="landingDeviceActivityLink" onclick="toggleReact()"><a href="#security/device-activity">${msg("deviceActivityHtmlTitle")}</a></h5>
<h5 id="landingLinkedAccountsLink" style="display:none" onclick="toggleReact()"><a href="#security/linked-accounts">${msg("linkedAccountsHtmlTitle")}</a></h5>
</div>
</div>
</div>
<div class="pf-l-gallery__item">
<div class="pf-c-card">
<div class="pf-c-card__header pf-c-content">
<h2><i class="pf-icon pf-icon-applications"></i>&nbsp${msg("applicationsHtmlTitle")}</h2>
<h6>${msg("applicationsIntroMessage")}</h6>
</div>
<div class="pf-c-card__body pf-c-content">
<h5 id="landingApplicationsLink" onclick="toggleReact()"><a href="#applications">${msg("applicationsHtmlTitle")}</a></h5>
</div>
</div>
</div>
<div class="pf-l-gallery__item" id="landingMyResourcesCard" style="display:none">
<div class="pf-c-card">
<div class="pf-c-card__header pf-c-content">
<h2><i class="pf-icon pf-icon-repository"></i>&nbsp${msg("myResources")}</h2>
<h6>${msg("resourceIntroMessage")}</h6>
</div>
<div class="pf-c-card__body pf-c-content">
<h5 id="landingMyResourcesLink" onclick="toggleReact()"><a href="#resources">${msg("myResources")}</a></h5>
</div>
</div>
</div>
</#list>
</div>
</section>
</main>
@ -275,13 +253,13 @@
</div>
<script>
if (features.isLinkedAccountsEnabled) {
document.getElementById("landingLinkedAccountsLink").style.display='block';
if (!features.isLinkedAccountsEnabled) {
document.getElementById("landinglinked-accountsLink").style.display='none';
};
// Hidden until feature is complete.
if (features.isMyResourcesEnabled) {
document.getElementById("landingMyResourcesCard").style.display='block';
if (!features.isMyResourcesEnabled) {
document.getElementById("landingresourcesCard").style.display='none';
};
</script>

View file

@ -7,7 +7,7 @@
# Don't ignore these
!WelcomePageScripts.js
!content.js
!content.json
public/app.css
public/base.css

View file

@ -1,53 +0,0 @@
var content = [
{
id: 'personal-info',
path: 'personal-info',
label: 'personalInfoHtmlTitle',
modulePath: '/content/account-page/AccountPage.js',
componentName: 'AccountPage'
},
{
id: 'security',
label: 'Account Security',
content: [
{
id: 'signingin',
path: 'security/signingin',
label: 'signingIn',
modulePath: '/content/signingin-page/SigningInPage.js',
componentName: 'SigningInPage',
},
{
id: 'device-activity',
path: 'security/device-activity',
label: 'device-activity',
modulePath: '/content/device-activity-page/DeviceActivityPage.js',
componentName: 'DeviceActivityPage'
},
{
id: 'linked-accounts',
path: 'security/linked-accounts',
label: 'linkedAccountsHtmlTitle',
modulePath: '/content/linked-accounts-page/LinkedAccountsPage.js',
componentName: 'LinkedAccountsPage',
hidden: !features.isLinkedAccountsEnabled
}
]
},
{
id: 'applications',
path: 'applications',
label: 'applications',
modulePath: '/content/applications-page/ApplicationsPage.js',
componentName: 'ApplicationsPage'
},
{
id: 'resources',
path: 'resources',
label: 'resources',
modulePath: '/content/my-resources-page/MyResourcesPage.js',
componentName: 'MyResourcesPage',
hidden: !features.isMyResourcesEnabled
}
];

View file

@ -0,0 +1,60 @@
[
{
"id": "personal-info",
"path": "personal-info",
"icon": "pf-icon-user",
"label": "personalInfoHtmlTitle",
"descriptionLabel": "personalInfoIntroMessage",
"modulePath": "/content/account-page/AccountPage.js",
"componentName": "AccountPage"
},
{
"id": "security",
"icon": "pf-icon-security",
"label": "Account Security",
"descriptionLabel": "accountSecurityIntroMessage",
"content": [
{
"id": "signingin",
"path": "security/signingin",
"label": "signingIn",
"modulePath": "/content/signingin-page/SigningInPage.js",
"componentName": "SigningInPage"
},
{
"id": "device-activity",
"path": "security/device-activity",
"label": "device-activity",
"modulePath": "/content/device-activity-page/DeviceActivityPage.js",
"componentName": "DeviceActivityPage"
},
{
"id": "linked-accounts",
"path": "security/linked-accounts",
"label": "linkedAccountsHtmlTitle",
"modulePath": "/content/linked-accounts-page/LinkedAccountsPage.js",
"componentName": "LinkedAccountsPage",
"hidden": "isLinkedAccountsEnabled"
}
]
},
{
"id": "applications",
"icon": "pf-icon-applications",
"path": "applications",
"label": "applications",
"descriptionLabel": "applicationsIntroMessage",
"modulePath": "/content/applications-page/ApplicationsPage.js",
"componentName": "ApplicationsPage"
},
{
"id": "resources",
"icon": "pf-icon-repository",
"path": "resources",
"label": "resources",
"descriptionLabel": "resourceIntroMessage",
"modulePath": "/content/my-resources-page/MyResourcesPage.js",
"componentName": "MyResourcesPage",
"hidden": "isMyResourcesEnabled"
}
]

View file

@ -24,7 +24,7 @@ export interface ContentItem {
id?: string;
label: string;
labelParams?: string[];
hidden?: boolean;
hidden?: string;
groupId: string; // computed value
itemId: string; // computed value
};

View file

@ -31,6 +31,7 @@ declare const keycloak: KeycloakClient;
declare let isReactLoading: boolean;
declare function toggleReact(): void;
declare const features: { [key: string]: boolean; };
export interface MainProps {}
export class Main extends React.Component<MainProps> {
@ -66,7 +67,7 @@ function removeHidden(items: ContentItem[]): ContentItem[] {
const visible: ContentItem[] = [];
for (let item of items) {
if (item.hidden) continue;
if (item.hidden && !features[item.hidden]) continue;
if (isExpansion(item)) {
visible.push(item);

View file

@ -1,6 +1,6 @@
parent=base
deprecatedMode=false
scripts=welcome-page-scripts.js content.js
scripts=welcome-page-scripts.js
developmentMode=false
# This is the logo in upper lefthand corner.