diff --git a/.github/workflows/npm-build.yml b/.github/workflows/npm-build.yml
index c95dd14c8..591b3cd7a 100644
--- a/.github/workflows/npm-build.yml
+++ b/.github/workflows/npm-build.yml
@@ -107,3 +107,7 @@ jobs:
working-directory: ./imxweb
run: npm run build qer-app-pwdportal
+ - name: Build custom-app
+ working-directory: ./imxweb
+ run: npm run build custom-app
+
diff --git a/README.md b/README.md
index 22cc0f7d3..05950b409 100644
--- a/README.md
+++ b/README.md
@@ -4,6 +4,10 @@
## Change log
+### October 15, 2022
+
+- There is a new application in the workspace called `custom-app`. This application is a template that provides the basic building blocks (such as Material integration, session handling, login, and the API client configuration) can be used as a starting point for building new applications. See [`readme.md`](./imxweb/projects/custom-app/readme.md) for more information.
+
### September 27, 2022
- The repository has been updated with the source code for the Identity Manager 9.1 release in the `v91` branch.
@@ -70,7 +74,7 @@ For more information about each project, see the `readme.md` files in each proje
|`qer-app-portal`|Portal|Angular app|`qbm`, `qer`|
|`qer-app-operationssupport`|Operations Support Portal|Angular app|`qbm`, `qer`|
|`qer-app-pwdportal`|Password Reset Portal|Angular app|`qbm`, `qer`|
-|`arc-app-certaccess`|Starling CertAccess|Angular app|various|
+|`custom-app`|Template for custom applications|Angular app|`qbm`|
## Installing node.js
@@ -91,8 +95,6 @@ When changing the code of a _library_, you will need to build and deploy customi
When changing the code of a _plugin library_, you will need to build and deploy customized versions of the plugin library itself, and all plugin libraries depending on it. For example, changing `tsb` will require that you also compile `aad` and `o3t` because these plugins include `tsb`.
-_Note_: Starling CertAccess currently does not support hosting custom HTML5 apps.
-
### Debugging
Running and debugging web applications is possible using the default tools of the Angular CLI toolchain. For example, you can use `ng serve qer-app-portal` to debug the Portal app.
diff --git a/imxweb/angular.json b/imxweb/angular.json
index bbec60e33..35f5b319d 100644
--- a/imxweb/angular.json
+++ b/imxweb/angular.json
@@ -1634,6 +1634,162 @@
}
}
}
+ },
+ "custom-app": {
+ "projectType": "application",
+ "root": "projects/custom-app",
+ "sourceRoot": "projects/custom-app/src",
+ "prefix": "imx",
+ "schematics": {
+ "@schematics/angular:component": {
+ "style": "scss"
+ }
+ },
+ "architect": {
+ "build": {
+ "builder": "@angular-devkit/build-angular:browser",
+ "options": {
+ "outputPath": "dist/custom-app",
+ "index": "projects/custom-app/src/index.html",
+ "main": "projects/custom-app/src/main.ts",
+ "polyfills": "projects/custom-app/src/polyfills.ts",
+ "tsConfig": "projects/custom-app/tsconfig.app.json",
+ "aot": true,
+ "assets": [
+ "projects/custom-app/src/appconfig.json",
+ "projects/custom-app/src/assets",
+ {
+ "glob": "**/*",
+ "input": "./html",
+ "output": "./html"
+ },
+ {
+ "glob": "**/*",
+ "input": "./shared/assets/",
+ "output": "./assets"
+ },
+ {
+ "glob": "**/*",
+ "input": "./node_modules/@elemental-ui/core/assets",
+ "output": "./assets"
+ },
+ {
+ "glob": "**/*",
+ "input": "./node_modules/systemjs-plugin-babel/",
+ "output": "./systemjs-plugin-babel"
+ }
+ ],
+ "styles": [
+ "projects/custom-app/src/styles.scss",
+ "projects/qbm/src/lib/styles/imx-page-title.scss",
+ "shared/assets/styles.scss",
+ "shared/assets/variables.scss"
+ ],
+ "stylePreprocessorOptions": {
+ "includePaths": [
+ "./shared/assets",
+ "./node_modules",
+ "./node_modules/@elemental-ui/cadence-icon",
+ "./node_modules/@elemental-ui/core"
+ ]
+ },
+ "scripts": [
+ "node_modules/systemjs/dist/system.src.js",
+ "node_modules/systemjs-plugin-babel/plugin-babel.js",
+ "node_modules/systemjs-plugin-babel/systemjs-babel-browser.js"
+ ]
+ },
+ "configurations": {
+ "production": {
+ "fileReplacements": [
+ {
+ "replace": "projects/custom-app/src/environments/environment.ts",
+ "with": "projects/custom-app/src/environments/environment.prod.ts"
+ }
+ ],
+ "optimization": true,
+ "outputHashing": "all",
+ "sourceMap": false,
+ "namedChunks": false,
+ "extractLicenses": true,
+ "vendorChunk": false,
+ "buildOptimizer": true,
+ "budgets": [
+ {
+ "type": "initial",
+ "maximumWarning": "20mb",
+ "maximumError": "40mb"
+ },
+ {
+ "type": "anyComponentStyle",
+ "maximumWarning": "6kb"
+ }
+ ]
+ },
+ "development": {
+ "buildOptimizer": false,
+ "optimization": false,
+ "vendorChunk": true,
+ "extractLicenses": false,
+ "sourceMap": true,
+ "namedChunks": true
+ },
+ "es5": {
+ "tsConfig": "./projects/custom-app/tsconfig-es5.app.json"
+ }
+ }
+ },
+ "serve": {
+ "builder": "@angular-devkit/build-angular:dev-server",
+ "options": {
+ "browserTarget": "custom-app:build"
+ },
+ "configurations": {
+ "production": {
+ "browserTarget": "custom-app:build:production"
+ },
+ "development": {
+ "browserTarget": "custom-app:build:development"
+ },
+ "es5": {
+ "browserTarget": "custom-app:build:es5"
+ }
+ }
+ },
+ "extract-i18n": {
+ "builder": "@angular-devkit/build-angular:extract-i18n",
+ "options": {
+ "browserTarget": "custom-app:build"
+ }
+ },
+ "test": {
+ "builder": "@angular-devkit/build-angular:karma",
+ "options": {
+ "main": "projects/custom-app/src/test.ts",
+ "polyfills": "projects/custom-app/src/polyfills.ts",
+ "tsConfig": "projects/custom-app/tsconfig.spec.json",
+ "karmaConfig": "projects/custom-app/karma.conf.js",
+ "assets": ["projects/custom-app/src/appconfig.json", "projects/custom-app/src/assets"],
+ "styles": ["projects/custom-app/src/styles.scss"],
+ "stylePreprocessorOptions": {
+ "includePaths": [
+ "./shared/assets",
+ "./node_modules",
+ "./node_modules/@elemental-ui/cadence-icon",
+ "./node_modules/@elemental-ui/core"
+ ]
+ },
+ "scripts": ["node_modules/systemjs/dist/system.src.js"]
+ }
+ },
+ "lint": {
+ "builder": "@angular-devkit/build-angular:tslint",
+ "options": {
+ "tsConfig": ["projects/custom-app/tsconfig.app.json", "projects/custom-app/tsconfig.spec.json"],
+ "exclude": ["**/node_modules/**"]
+ }
+ }
+ }
}
},
"defaultProject": "qer-app-portal",
diff --git a/imxweb/projects/custom-app/.browserslistrc b/imxweb/projects/custom-app/.browserslistrc
new file mode 100644
index 000000000..80848532e
--- /dev/null
+++ b/imxweb/projects/custom-app/.browserslistrc
@@ -0,0 +1,12 @@
+# This file is used by the build system to adjust CSS and JS output to support the specified browsers below.
+# For additional information regarding the format and rule options, please see:
+# https://github.com/browserslist/browserslist#queries
+
+# You can see what browsers were selected by your queries by running:
+# npx browserslist
+
+> 0.5%
+last 2 versions
+Firefox ESR
+not dead
+not IE 9-11 # For IE 9-11 support, remove 'not'.
\ No newline at end of file
diff --git a/imxweb/projects/custom-app/Build.proj b/imxweb/projects/custom-app/Build.proj
new file mode 100644
index 000000000..dd3484c3f
--- /dev/null
+++ b/imxweb/projects/custom-app/Build.proj
@@ -0,0 +1,15 @@
+
+
+ imxweb\projects\custom-app
+ no
+ custom-app
+
+
+
+
+
+
+
+
+
+
diff --git a/imxweb/projects/custom-app/karma.conf.js b/imxweb/projects/custom-app/karma.conf.js
new file mode 100644
index 000000000..b454fa9d7
--- /dev/null
+++ b/imxweb/projects/custom-app/karma.conf.js
@@ -0,0 +1,47 @@
+// Karma configuration file, see link for more information
+// https://karma-runner.github.io/1.0/config/configuration-file.html
+
+module.exports = function (config) {
+ config.set({
+ basePath: '',
+ frameworks: ['jasmine', '@angular-devkit/build-angular'],
+ plugins: [
+ require('karma-jasmine'),
+ require('karma-chrome-launcher'),
+ require('karma-ie-launcher'),
+ require('karma-jasmine-html-reporter'),
+ require('karma-coverage-istanbul-reporter'),
+ require('@angular-devkit/build-angular/plugins/karma'),
+ require('karma-junit-reporter')
+ ],
+ client: {
+ clearContext: false // leave Jasmine Spec Runner output visible in browser
+ },
+ coverageIstanbulReporter: {
+ dir: require('path').join(__dirname, 'results'),
+ reports: ['html', 'lcovonly', 'text-summary'],
+ thresholds: {
+ statements: 0,
+ branches: 0,
+ functions: 0,
+ lines: 0,
+ },
+ fixWebpackSourcePaths: true
+ },
+ junitReporter: {
+ outputDir: require('path').join(__dirname, 'results'),
+ },
+ reporters: ['progress', 'coverage-istanbul', 'junit'],
+ port: 9876,
+ captureTimeout: 210000,
+ browserDisconnectTolerance: 3,
+ browserDisconnectTimeout : 210000,
+ browserNoActivityTimeout : 210000,
+ colors: true,
+ logLevel: config.LOG_INFO,
+ autoWatch: true,
+ browsers: ['Chrome'],
+ singleRun: false,
+ restartOnFileChange: true
+ });
+};
diff --git a/imxweb/projects/custom-app/package.json b/imxweb/projects/custom-app/package.json
new file mode 100644
index 000000000..7d1519614
--- /dev/null
+++ b/imxweb/projects/custom-app/package.json
@@ -0,0 +1,5 @@
+{
+ "name": "custom-app",
+ "version": "9.0.0",
+ "private": true
+}
\ No newline at end of file
diff --git a/imxweb/projects/custom-app/readme.md b/imxweb/projects/custom-app/readme.md
new file mode 100644
index 000000000..d10e72d05
--- /dev/null
+++ b/imxweb/projects/custom-app/readme.md
@@ -0,0 +1,52 @@
+# custom-app
+
+This package contains the base functionality that you can use as a starting point to build a custom application.
+
+This package has a dependency on the `qbm` package, so make sure that package is compiled using the `ng build qbm` command.
+
+## App service
+
+The [`app.service.ts`](./src/app/app.service.ts) service is responsible for initialization of the app:
+
+- It configures the API client with the correct URL for the API Server.
+- It configures the translation service with the browser's preferred language setting.
+- It shows a splash screen while the application is being loaded.
+
+## Routing
+
+The [`appconfig.json`](./src/appconfig.json) file sets two default routes:
+
+``` json
+ "routeConfig": {
+ "login": "",
+ "start": "start"
+ }
+```
+
+The `login` route is the route called when there is no authenticated session.
+The `start` route is the route called when there is an authenticated session.
+
+These routes are defined in the [`app-routing.module.ts`](./src/app/app-routing.module.ts) file. Add any routes for your application here.
+
+## Login component
+
+The app re-uses the default [login component](../qbm/src/lib/login/login.component.ts) defined in the `qbm` library.
+
+This app uses a session on the `portal` API project; you can change this setting in the [`environment.ts`](./src/environments/environment.ts) file.
+
+## Page layout
+
+The [`app.component.html`](./src/app/app.component.html) file defines the basic layout of the page. This template makes use of the following base components:
+
+- `` shows the mast head including:
+ - the product logo,
+ - the name of the authenticated user,
+ - an option to log out,
+ - and the "about" dialog.
+- `` is the container where error messages are shown.
+
+The app component also shows a splash screen while the application is being initialized.
+
+## Debugging
+
+Run `ng serve custom-app` to serve the custom app.
diff --git a/imxweb/projects/custom-app/src/app/app-routing.module.ts b/imxweb/projects/custom-app/src/app/app-routing.module.ts
new file mode 100644
index 000000000..6a35e0258
--- /dev/null
+++ b/imxweb/projects/custom-app/src/app/app-routing.module.ts
@@ -0,0 +1,66 @@
+/*
+ * ONE IDENTITY LLC. PROPRIETARY INFORMATION
+ *
+ * This software is confidential. One Identity, LLC. or one of its affiliates or
+ * subsidiaries, has supplied this software to you under terms of a
+ * license agreement, nondisclosure agreement or both.
+ *
+ * You may not copy, disclose, or use this software except in accordance with
+ * those terms.
+ *
+ *
+ * Copyright 2022 One Identity LLC.
+ * ALL RIGHTS RESERVED.
+ *
+ * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR
+ * WARRANTIES ABOUT THE SUITABILITY OF THE SOFTWARE,
+ * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+ * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, OR
+ * NON-INFRINGEMENT. ONE IDENTITY LLC. SHALL NOT BE
+ * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE
+ * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING
+ * THIS SOFTWARE OR ITS DERIVATIVES.
+ *
+ */
+
+import { InjectionToken, NgModule } from '@angular/core';
+import { Routes, RouterModule, ActivatedRouteSnapshot } from '@angular/router';
+import { AuthenticationGuardService, LoginComponent, RouteGuardService } from 'qbm';
+import { StartComponent } from './start.component';
+
+const externalUrlProvider = new InjectionToken('externalUrlRedirectResolver');
+
+const routes: Routes = [
+ {
+ path: '',
+ component: LoginComponent,
+ canActivate: [AuthenticationGuardService],
+ resolve: [RouteGuardService]
+ },
+ {
+ path: 'start',
+ component: StartComponent,
+ canActivate: [RouteGuardService],
+ resolve: [RouteGuardService]
+ },
+ { path: '**', redirectTo: 'start' }
+];
+
+@NgModule({
+ imports: [RouterModule.forRoot(routes, { useHash: true, relativeLinkResolution: 'legacy' })],
+ exports: [RouterModule],
+ providers: [
+ {
+ provide: externalUrlProvider,
+ useValue: (route: ActivatedRouteSnapshot) => {
+ const externalUrl = route.paramMap.get('externalUrl');
+ if (externalUrl && externalUrl.toLocaleLowerCase() !== 'undefined') {
+ window.open(externalUrl, '_self');
+ }
+ }
+ },
+ ],
+})
+
+export class AppRoutingModule { }
diff --git a/imxweb/projects/custom-app/src/app/app.component.html b/imxweb/projects/custom-app/src/app/app.component.html
new file mode 100644
index 000000000..ae5e1f2c2
--- /dev/null
+++ b/imxweb/projects/custom-app/src/app/app.component.html
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/imxweb/projects/custom-app/src/app/app.component.scss b/imxweb/projects/custom-app/src/app/app.component.scss
new file mode 100644
index 000000000..c2f8a644d
--- /dev/null
+++ b/imxweb/projects/custom-app/src/app/app.component.scss
@@ -0,0 +1,14 @@
+.page {
+ display: flex;
+ flex-direction: column;
+ overflow: hidden;
+ height: 100vh;
+}
+
+.pageContent {
+ margin: 20px 30px;
+ display: flex;
+ flex-direction: column;
+ overflow: hidden;
+ height: inherit;
+}
diff --git a/imxweb/projects/custom-app/src/app/app.component.ts b/imxweb/projects/custom-app/src/app/app.component.ts
new file mode 100644
index 000000000..8cb3f8abc
--- /dev/null
+++ b/imxweb/projects/custom-app/src/app/app.component.ts
@@ -0,0 +1,109 @@
+/*
+ * ONE IDENTITY LLC. PROPRIETARY INFORMATION
+ *
+ * This software is confidential. One Identity, LLC. or one of its affiliates or
+ * subsidiaries, has supplied this software to you under terms of a
+ * license agreement, nondisclosure agreement or both.
+ *
+ * You may not copy, disclose, or use this software except in accordance with
+ * those terms.
+ *
+ *
+ * Copyright 2022 One Identity LLC.
+ * ALL RIGHTS RESERVED.
+ *
+ * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR
+ * WARRANTIES ABOUT THE SUITABILITY OF THE SOFTWARE,
+ * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+ * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, OR
+ * NON-INFRINGEMENT. ONE IDENTITY LLC. SHALL NOT BE
+ * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE
+ * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING
+ * THIS SOFTWARE OR ITS DERIVATIVES.
+ *
+ */
+
+import { Component, OnDestroy, OnInit } from '@angular/core';
+import { NavigationCancel, NavigationEnd, NavigationError, NavigationStart, Router, RouterEvent } from '@angular/router';
+import { Subscription } from 'rxjs';
+
+import { AuthenticationService, ISessionState, SplashService } from 'qbm';
+
+@Component({
+ selector: 'imx-root',
+ styleUrls: ['./app.component.scss'],
+ templateUrl: './app.component.html'
+})
+export class AppComponent implements OnInit, OnDestroy {
+ public isLoggedIn = false;
+ public hideUserMessage = false;
+ public showPageContent = true;
+
+ private readonly subscriptions: Subscription[] = [];
+
+ constructor(
+ private readonly authentication: AuthenticationService,
+ private readonly router: Router,
+ private readonly splash: SplashService,
+ ) {
+ this.subscriptions.push(
+ this.authentication.onSessionResponse.subscribe(async (sessionState: ISessionState) => {
+
+ if (sessionState.hasErrorState) {
+ // Needs to close here when there is an error on sessionState
+ this.splash.close();
+ }
+
+ if (sessionState.IsLoggedOut) {
+ this.showPageContent = false;
+ }
+
+ this.isLoggedIn = sessionState.IsLoggedIn;
+ if (this.isLoggedIn) {
+ // Close the splash screen that opened in app service initialisation
+ // Needs to close here when session is already logged in.
+ this.splash.close();
+ }
+ })
+ );
+
+ this.setupRouter();
+ }
+
+ public async ngOnInit(): Promise {
+ // Fetch the session state from the server; this will either
+ // - go to the login page if the user is not logged in
+ // - or go to the start page if the user is already logged in
+ this.authentication.update();
+ }
+
+ public ngOnDestroy(): void {
+ this.subscriptions.forEach(subscription => subscription.unsubscribe());
+ }
+
+ private setupRouter(): void {
+ this.router.events.subscribe(((event: RouterEvent) => {
+ if (event instanceof NavigationStart) {
+ this.hideUserMessage = true;
+ if (this.isLoggedIn && event.url === '/') {
+ // show the splash screen when the user logs out!
+ this.splash.init({ applicationName: 'Custom App' });
+ }
+ }
+
+ if (event instanceof NavigationCancel) {
+ this.hideUserMessage = false;
+ }
+
+ if (event instanceof NavigationEnd) {
+ this.hideUserMessage = false;
+ this.showPageContent = true;
+ }
+
+ if (event instanceof NavigationError) {
+ this.hideUserMessage = false;
+ }
+ }));
+ }
+}
diff --git a/imxweb/projects/custom-app/src/app/app.module.ts b/imxweb/projects/custom-app/src/app/app.module.ts
new file mode 100644
index 000000000..09b6019ea
--- /dev/null
+++ b/imxweb/projects/custom-app/src/app/app.module.ts
@@ -0,0 +1,117 @@
+/*
+ * ONE IDENTITY LLC. PROPRIETARY INFORMATION
+ *
+ * This software is confidential. One Identity, LLC. or one of its affiliates or
+ * subsidiaries, has supplied this software to you under terms of a
+ * license agreement, nondisclosure agreement or both.
+ *
+ * You may not copy, disclose, or use this software except in accordance with
+ * those terms.
+ *
+ *
+ * Copyright 2022 One Identity LLC.
+ * ALL RIGHTS RESERVED.
+ *
+ * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR
+ * WARRANTIES ABOUT THE SUITABILITY OF THE SOFTWARE,
+ * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+ * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, OR
+ * NON-INFRINGEMENT. ONE IDENTITY LLC. SHALL NOT BE
+ * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE
+ * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING
+ * THIS SOFTWARE OR ITS DERIVATIVES.
+ *
+ */
+
+import { HttpClientModule } from '@angular/common/http';
+import { BrowserModule } from '@angular/platform-browser';
+import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
+import { NgModule, APP_INITIALIZER, ErrorHandler } from '@angular/core';
+import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
+import { MatCardModule } from '@angular/material/card';
+import { MatButtonModule } from '@angular/material/button';
+import { MatIconModule } from '@angular/material/icon';
+import { LoggerModule, NgxLoggerLevel } from 'ngx-logger';
+import { EuiCoreModule, EuiMaterialModule } from '@elemental-ui/core';
+import { TranslateModule, TranslateLoader, MissingTranslationHandler, TranslateService } from '@ngx-translate/core';
+
+import {
+ ImxTranslateLoader,
+ ImxMissingTranslationHandler,
+ MastHeadModule,
+ UserMessageModule,
+ GlobalErrorHandler,
+ Paginator,
+ LdsReplacePipe,
+ QbmModule,
+ AuthenticationModule,
+ MenuModule
+} from 'qbm';
+import { AppRoutingModule } from './app-routing.module';
+import { AppComponent } from './app.component';
+import { AppService } from './app.service';
+import { MatPaginatorIntl } from '@angular/material/paginator';
+import { environment } from '../environments/environment';
+import appConfigJson from '../appconfig.json';
+import { StartComponent } from './start.component';
+
+
+@NgModule({
+ declarations: [
+ AppComponent,
+ StartComponent
+ ],
+ imports: [
+ AppRoutingModule,
+ AuthenticationModule,
+ BrowserAnimationsModule,
+ BrowserModule,
+ EuiCoreModule,
+ EuiMaterialModule,
+ MatButtonModule,
+ MatCardModule,
+ MatIconModule,
+ MatProgressSpinnerModule,
+ HttpClientModule,
+ LoggerModule.forRoot({ level: NgxLoggerLevel.DEBUG, serverLogLevel: NgxLoggerLevel.OFF }),
+ MastHeadModule,
+ MenuModule,
+ QbmModule,
+ TranslateModule.forRoot({
+ loader: {
+ provide: TranslateLoader,
+ useClass: ImxTranslateLoader
+ },
+ missingTranslationHandler: {
+ provide: MissingTranslationHandler,
+ useClass: ImxMissingTranslationHandler
+ }
+ }),
+ UserMessageModule
+ ],
+ providers: [
+ { provide: 'environment', useValue: environment },
+ { provide: 'appConfigJson', useValue: appConfigJson },
+ {
+ provide: APP_INITIALIZER,
+ useFactory: AppService.init,
+ deps: [AppService],
+ multi: true
+ },
+ {
+ provide: ErrorHandler,
+ useClass: GlobalErrorHandler
+ },
+ {
+ provide: MatPaginatorIntl,
+ useFactory: Paginator.Create,
+ deps: [
+ TranslateService,
+ LdsReplacePipe
+ ]
+ }
+ ],
+ bootstrap: [AppComponent]
+})
+export class AppModule { }
diff --git a/imxweb/projects/custom-app/src/app/app.service.ts b/imxweb/projects/custom-app/src/app/app.service.ts
new file mode 100644
index 000000000..01827b2cb
--- /dev/null
+++ b/imxweb/projects/custom-app/src/app/app.service.ts
@@ -0,0 +1,104 @@
+/*
+ * ONE IDENTITY LLC. PROPRIETARY INFORMATION
+ *
+ * This software is confidential. One Identity, LLC. or one of its affiliates or
+ * subsidiaries, has supplied this software to you under terms of a
+ * license agreement, nondisclosure agreement or both.
+ *
+ * You may not copy, disclose, or use this software except in accordance with
+ * those terms.
+ *
+ *
+ * Copyright 2022 One Identity LLC.
+ * ALL RIGHTS RESERVED.
+ *
+ * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR
+ * WARRANTIES ABOUT THE SUITABILITY OF THE SOFTWARE,
+ * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+ * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, OR
+ * NON-INFRINGEMENT. ONE IDENTITY LLC. SHALL NOT BE
+ * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE
+ * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING
+ * THIS SOFTWARE OR ITS DERIVATIVES.
+ *
+ */
+
+import { Injectable } from '@angular/core';
+import { Title } from '@angular/platform-browser';
+import { TranslateService } from '@ngx-translate/core';
+
+import { Globals } from 'imx-qbm-dbts';
+import {
+ AppConfigService,
+ AuthenticationService,
+ imx_SessionService,
+ ImxTranslationProviderService,
+ SplashService
+} from 'qbm';
+import { environment } from '../environments/environment';
+import { TypedClient } from 'imx-api-qbm';
+
+@Injectable({
+ providedIn: 'root'
+})
+export class AppService {
+ constructor(
+ private readonly config: AppConfigService,
+ private readonly translateService: TranslateService,
+ private readonly session: imx_SessionService,
+ private readonly translationProvider: ImxTranslationProviderService,
+ private readonly title: Title,
+ private readonly authentication: AuthenticationService,
+ private readonly splash: SplashService,
+ ) { }
+
+ public async init(): Promise {
+ this.showSplash();
+ await this.config.init(environment.clientUrl);
+
+ this.translateService.addLangs(this.config.Config.Translation.Langs);
+ const browserCulture = this.translateService.getBrowserCultureLang();
+ this.translateService.setDefaultLang(browserCulture);
+ await this.translateService.use(browserCulture).toPromise();
+
+ // If the session defines another culture, update the translation provider
+ this.authentication.onSessionResponse.subscribe(sessionState => this.translationProvider.init(sessionState?.culture));
+
+ this.translateService.onLangChange.subscribe(() => {
+ this.setTitle();
+ });
+
+ this.setTitle();
+
+ this.session.TypedClient = new TypedClient(this.config.v2client, this.translationProvider);
+ }
+
+ private async setTitle(): Promise {
+ const imxConfig = await this.config.getImxConfig();
+ const name = imxConfig.ProductName || Globals.QIM_ProductNameFull;
+ const title = `${name} ${this.config.Config.Title}`;
+ this.title.setTitle(title);
+
+ await this.updateSplash(title);
+ }
+
+ public static init(app: AppService): () => Promise {
+ return () =>
+ new Promise(async (resolve: any) => {
+ await app.init();
+ resolve();
+ });
+ }
+
+ private showSplash(): void {
+ // open splash screen with fix values
+ this.splash.init({ applicationName: 'Custom App' });
+ }
+
+ private async updateSplash(title: string): Promise {
+ // update the splash screen and use translated texts and the title from the imxconfig
+ const loadingMsg = await this.translateService.get('#LDS#Loading...').toPromise();
+ this.splash.update({ applicationName: title, message: loadingMsg });
+ }
+}
diff --git a/imxweb/projects/custom-app/src/app/start.component.html b/imxweb/projects/custom-app/src/app/start.component.html
new file mode 100644
index 000000000..c8aa9c84e
--- /dev/null
+++ b/imxweb/projects/custom-app/src/app/start.component.html
@@ -0,0 +1,18 @@
+
+
+
Hello world!
+
+
+
+
+
+
0">
+
There are {{ totalCount }} identities in the address book. The first identities are:
+
+
+ {{ person.GetEntity().GetDisplay() }}
+
+
+
+
+
\ No newline at end of file
diff --git a/imxweb/projects/custom-app/src/app/start.component.ts b/imxweb/projects/custom-app/src/app/start.component.ts
new file mode 100644
index 000000000..beac2ffa6
--- /dev/null
+++ b/imxweb/projects/custom-app/src/app/start.component.ts
@@ -0,0 +1,34 @@
+import { Component } from "@angular/core";
+import { PortalPersonAll } from "imx-api-qer";
+import { QerApiService } from "qer";
+
+@Component({
+ templateUrl: './start.component.html'
+})
+export class StartComponent {
+
+ constructor(private qerApi: QerApiService) { }
+
+ persons: PortalPersonAll[] = [];
+ totalCount = 0;
+ busy = true;
+
+ async loadPersonData() {
+
+ try {
+ this.busy = true;
+
+ // load some address book data from the API Server.
+ // This call uses the default parameters, which will
+ // return the first 20 identities in the database.
+ const personData = await this.qerApi.typedClient.PortalPersonAll.Get();
+
+ this.persons = personData.Data;
+ this.totalCount = personData.totalCount;
+ } finally {
+ // Even if the call fails, reset the busy flag
+ this.busy = false;
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/imxweb/projects/custom-app/src/appconfig.json b/imxweb/projects/custom-app/src/appconfig.json
new file mode 100644
index 000000000..908bd4c3b
--- /dev/null
+++ b/imxweb/projects/custom-app/src/appconfig.json
@@ -0,0 +1,11 @@
+{
+ "Title": "Custom App",
+ "WebAppIndex": "portal",
+ "Translation": {
+ "Langs": ["en-US", "de-DE", "de", "en"]
+ },
+ "routeConfig": {
+ "login": "",
+ "start": "start"
+ }
+}
diff --git a/imxweb/projects/custom-app/src/environments/environment.prod.ts b/imxweb/projects/custom-app/src/environments/environment.prod.ts
new file mode 100644
index 000000000..e81d320c3
--- /dev/null
+++ b/imxweb/projects/custom-app/src/environments/environment.prod.ts
@@ -0,0 +1,32 @@
+/*
+ * ONE IDENTITY LLC. PROPRIETARY INFORMATION
+ *
+ * This software is confidential. One Identity, LLC. or one of its affiliates or
+ * subsidiaries, has supplied this software to you under terms of a
+ * license agreement, nondisclosure agreement or both.
+ *
+ * You may not copy, disclose, or use this software except in accordance with
+ * those terms.
+ *
+ *
+ * Copyright 2022 One Identity LLC.
+ * ALL RIGHTS RESERVED.
+ *
+ * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR
+ * WARRANTIES ABOUT THE SUITABILITY OF THE SOFTWARE,
+ * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+ * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, OR
+ * NON-INFRINGEMENT. ONE IDENTITY LLC. SHALL NOT BE
+ * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE
+ * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING
+ * THIS SOFTWARE OR ITS DERIVATIVES.
+ *
+ */
+
+export const environment = {
+ production: true,
+ clientUrl: '',
+ appName: 'custom-app',
+ appVersion: '1.0.0'
+};
diff --git a/imxweb/projects/custom-app/src/environments/environment.ts b/imxweb/projects/custom-app/src/environments/environment.ts
new file mode 100644
index 000000000..ccd664291
--- /dev/null
+++ b/imxweb/projects/custom-app/src/environments/environment.ts
@@ -0,0 +1,45 @@
+/*
+ * ONE IDENTITY LLC. PROPRIETARY INFORMATION
+ *
+ * This software is confidential. One Identity, LLC. or one of its affiliates or
+ * subsidiaries, has supplied this software to you under terms of a
+ * license agreement, nondisclosure agreement or both.
+ *
+ * You may not copy, disclose, or use this software except in accordance with
+ * those terms.
+ *
+ *
+ * Copyright 2022 One Identity LLC.
+ * ALL RIGHTS RESERVED.
+ *
+ * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR
+ * WARRANTIES ABOUT THE SUITABILITY OF THE SOFTWARE,
+ * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+ * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, OR
+ * NON-INFRINGEMENT. ONE IDENTITY LLC. SHALL NOT BE
+ * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE
+ * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING
+ * THIS SOFTWARE OR ITS DERIVATIVES.
+ *
+ */
+
+// This file can be replaced during build by using the `fileReplacements` array.
+// `ng build --prod` replaces `environment.ts` with `environment.prod.ts`.
+// The list of file replacements can be found in `angular.json`.
+
+export const environment = {
+ production: false,
+ clientUrl: 'http://localhost:8182',
+ appName: 'custom-app',
+ appVersion: '1.0.0'
+};
+
+/*
+ * For easier debugging in development mode, you can import the following file
+ * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`.
+ *
+ * This import should be commented out in production mode because it will have a negative impact
+ * on performance if an error is thrown.
+ */
+// import 'zone.js/dist/zone-error'; // Included with Angular CLI.
diff --git a/imxweb/projects/custom-app/src/index.html b/imxweb/projects/custom-app/src/index.html
new file mode 100644
index 000000000..4b785ba4e
--- /dev/null
+++ b/imxweb/projects/custom-app/src/index.html
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/imxweb/projects/custom-app/src/main.ts b/imxweb/projects/custom-app/src/main.ts
new file mode 100644
index 000000000..0d6be56bc
--- /dev/null
+++ b/imxweb/projects/custom-app/src/main.ts
@@ -0,0 +1,38 @@
+/*
+ * ONE IDENTITY LLC. PROPRIETARY INFORMATION
+ *
+ * This software is confidential. One Identity, LLC. or one of its affiliates or
+ * subsidiaries, has supplied this software to you under terms of a
+ * license agreement, nondisclosure agreement or both.
+ *
+ * You may not copy, disclose, or use this software except in accordance with
+ * those terms.
+ *
+ *
+ * Copyright 2022 One Identity LLC.
+ * ALL RIGHTS RESERVED.
+ *
+ * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR
+ * WARRANTIES ABOUT THE SUITABILITY OF THE SOFTWARE,
+ * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+ * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, OR
+ * NON-INFRINGEMENT. ONE IDENTITY LLC. SHALL NOT BE
+ * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE
+ * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING
+ * THIS SOFTWARE OR ITS DERIVATIVES.
+ *
+ */
+
+import { enableProdMode } from '@angular/core';
+import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
+
+import { AppModule } from './app/app.module';
+import { environment } from './environments/environment';
+
+if (environment.production) {
+ enableProdMode();
+}
+
+platformBrowserDynamic().bootstrapModule(AppModule)
+ .catch(err => console.error(err));
diff --git a/imxweb/projects/custom-app/src/polyfills.ts b/imxweb/projects/custom-app/src/polyfills.ts
new file mode 100644
index 000000000..02b0a61d5
--- /dev/null
+++ b/imxweb/projects/custom-app/src/polyfills.ts
@@ -0,0 +1,81 @@
+/*
+ * ONE IDENTITY LLC. PROPRIETARY INFORMATION
+ *
+ * This software is confidential. One Identity, LLC. or one of its affiliates or
+ * subsidiaries, has supplied this software to you under terms of a
+ * license agreement, nondisclosure agreement or both.
+ *
+ * You may not copy, disclose, or use this software except in accordance with
+ * those terms.
+ *
+ *
+ * Copyright 2022 One Identity LLC.
+ * ALL RIGHTS RESERVED.
+ *
+ * ONE IDENTITY LLC. MAKES NO REPRESENTATIONS OR
+ * WARRANTIES ABOUT THE SUITABILITY OF THE SOFTWARE,
+ * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+ * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, OR
+ * NON-INFRINGEMENT. ONE IDENTITY LLC. SHALL NOT BE
+ * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE
+ * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING
+ * THIS SOFTWARE OR ITS DERIVATIVES.
+ *
+ */
+
+/**
+ * This file includes polyfills needed by Angular and is loaded before the app.
+ * You can add your own extra polyfills to this file.
+ *
+ * This file is divided into 2 sections:
+ * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
+ * 2. Application imports. Files imported after ZoneJS that should be loaded before your main
+ * file.
+ *
+ * The current setup is for so-called "evergreen" browsers; the last versions of browsers that
+ * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
+ * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
+ *
+ * Learn more in https://angular.io/guide/browser-support
+ */
+
+/***************************************************************************************************
+* BROWSER POLYFILLS
+*/
+
+
+/** IE10 and IE11 requires the following for NgClass support on SVG elements */
+// import 'classlist.js'; // Run `npm install --save classlist.js`.
+
+/** IE10 and IE11 requires the following for the Reflect API. */
+// import 'core-js/es6/reflect';
+
+
+/** Evergreen browsers require these. */
+// Used for reflect-metadata in JIT. If you use AOT (and only Angular decorators), you can remove.
+import 'core-js/es7/reflect';
+
+// Used for support array.includes
+import 'core-js/es7/array';
+
+/**
+ * Required to support Web Animations `@angular/platform-browser/animations`.
+ * Needed for: All but Chrome, Firefox and Opera. http://caniuse.com/#feat=web-animation
+ */
+import 'web-animations-js';
+
+
+
+/***************************************************************************************************
+ * Zone JS is required by default for Angular itself.
+ */
+import 'zone.js/dist/zone'; // Included with Angular CLI.
+
+
+
+/***************************************************************************************************
+ * APPLICATION IMPORTS
+ */
+import 'whatwg-fetch';
+import 'url-polyfill';
diff --git a/imxweb/projects/custom-app/src/styles.scss b/imxweb/projects/custom-app/src/styles.scss
new file mode 100644
index 000000000..b2784f9a9
--- /dev/null
+++ b/imxweb/projects/custom-app/src/styles.scss
@@ -0,0 +1,13 @@
+/* You can add global styles to this file, and also import other style files */
+
+@use '@angular/material' as mat;
+
+$material_icons_font_path: "~node_modules/@elemental-ui/core/assets/MaterialIcons";
+$cadence_font_path: "~node_modules/@elemental-ui/core/assets/Cadence";
+$source_sans_pro_font_path: "~node_modules/@elemental-ui/core/assets/Source_Sans_Pro";
+
+@import '@elemental-ui/core/src/styles/core.scss';
+
+.imx-dialog-panel-class > .mat-dialog-container {
+ background-color: mat.get-color-from-palette($asher-gray-palette, 200);
+}
diff --git a/imxweb/projects/custom-app/tsconfig-es5.app.json b/imxweb/projects/custom-app/tsconfig-es5.app.json
new file mode 100644
index 000000000..4cba6b869
--- /dev/null
+++ b/imxweb/projects/custom-app/tsconfig-es5.app.json
@@ -0,0 +1,6 @@
+{
+ "extends": "./tsconfig.app.json",
+ "compilerOptions": {
+ "target": "es5"
+ }
+}
\ No newline at end of file
diff --git a/imxweb/projects/custom-app/tsconfig.app.json b/imxweb/projects/custom-app/tsconfig.app.json
new file mode 100644
index 000000000..d69a5c154
--- /dev/null
+++ b/imxweb/projects/custom-app/tsconfig.app.json
@@ -0,0 +1,14 @@
+{
+ "extends": "../../tsconfig.json",
+ "compilerOptions": {
+ "outDir": "../../out-tsc/app",
+ "types": []
+ },
+ "files": [
+ "src/main.ts",
+ "src/polyfills.ts"
+ ],
+ "include": [
+ "projects/custom-app/src/**/*.d.ts"
+ ]
+}
diff --git a/imxweb/projects/custom-app/tsconfig.spec.json b/imxweb/projects/custom-app/tsconfig.spec.json
new file mode 100644
index 000000000..a809b0a66
--- /dev/null
+++ b/imxweb/projects/custom-app/tsconfig.spec.json
@@ -0,0 +1,18 @@
+{
+ "extends": "../../tsconfig.json",
+ "compilerOptions": {
+ "outDir": "../../out-tsc/spec",
+ "types": [
+ "jasmine",
+ "node"
+ ]
+ },
+ "files": [
+ "src/test.ts",
+ "src/polyfills.ts"
+ ],
+ "include": [
+ "**/*.spec.ts",
+ "**/*.d.ts"
+ ]
+}
diff --git a/imxweb/projects/custom-app/tslint.json b/imxweb/projects/custom-app/tslint.json
new file mode 100644
index 000000000..8bab15f53
--- /dev/null
+++ b/imxweb/projects/custom-app/tslint.json
@@ -0,0 +1,17 @@
+{
+ "extends": "../../tslint.json",
+ "rules": {
+ "directive-selector": [
+ true,
+ "attribute",
+ "imx",
+ "camelCase"
+ ],
+ "component-selector": [
+ true,
+ "element",
+ "imx",
+ "kebab-case"
+ ]
+ }
+}