diff --git a/geoportal/geomapfish_geoportal/static-ngeo/js/apps/Controllerdesktop_alt.js b/geoportal/geomapfish_geoportal/static-ngeo/js/apps/Controllerdesktop_alt.js
index 4ce3f012c..e1842d967 100644
--- a/geoportal/geomapfish_geoportal/static-ngeo/js/apps/Controllerdesktop_alt.js
+++ b/geoportal/geomapfish_geoportal/static-ngeo/js/apps/Controllerdesktop_alt.js
@@ -42,6 +42,8 @@ import ngeoStreetviewModule from 'ngeo/streetview/module';
import ngeoRoutingModule from 'ngeo/routing/module';
import ngeoStatemanagerWfsPermalink from 'ngeo/statemanager/WfsPermalink';
+import {customCssFn} from '../customizationLoader';
+
/**
* @private
*/
@@ -65,6 +67,9 @@ class Controller extends AbstractDesktopController {
const drawLidarprofilePanelActive = new ngeoMiscToolActivate(this, 'drawLidarprofilePanelActive');
this.ngeoToolActivateMgr.registerTool('mapTools', drawLidarprofilePanelActive, false);
+
+ // CUSTOM Override style in the Shadow DOM scope.
+ customCssFn();
}
/**
diff --git a/geoportal/geomapfish_geoportal/static-ngeo/js/apps/desktop_alt.html.ejs b/geoportal/geomapfish_geoportal/static-ngeo/js/apps/desktop_alt.html.ejs
index b50f2fc12..53633e0f4 100644
--- a/geoportal/geomapfish_geoportal/static-ngeo/js/apps/desktop_alt.html.ejs
+++ b/geoportal/geomapfish_geoportal/static-ngeo/js/apps/desktop_alt.html.ejs
@@ -22,6 +22,7 @@
+
@@ -57,6 +58,10 @@
+
+ A custom auth panel under
+
+
diff --git a/geoportal/geomapfish_geoportal/static-ngeo/js/customization/angularDrawPanel.ts b/geoportal/geomapfish_geoportal/static-ngeo/js/customization/angularDrawPanel.ts
new file mode 100644
index 000000000..90d8a7bb5
--- /dev/null
+++ b/geoportal/geomapfish_geoportal/static-ngeo/js/customization/angularDrawPanel.ts
@@ -0,0 +1,12 @@
+import {Controller} from 'gmf/drawing/drawFeatureComponent';
+
+/**
+ * Initialize interactions by setting them inactive and decorating them
+ */
+Controller.prototype.initializeInteractions_ = function () {
+ console.log('Quick test to test AngularJs overriding -> It works.');
+ this.interactions_.forEach((interaction) => {
+ interaction.setActive(false);
+ ngeoMiscDecorateInteraction(interaction);
+ });
+};
diff --git a/geoportal/geomapfish_geoportal/static-ngeo/js/customization/authFormElement.ts b/geoportal/geomapfish_geoportal/static-ngeo/js/customization/authFormElement.ts
new file mode 100644
index 000000000..07966609b
--- /dev/null
+++ b/geoportal/geomapfish_geoportal/static-ngeo/js/customization/authFormElement.ts
@@ -0,0 +1,274 @@
+import {unsafeCSS, html, TemplateResult} from 'lit';
+import i18next from 'i18next';
+import authenticationService from 'ngeo/auth/service';
+import ngeoAuthForm from 'ngeo/auth/FormElement';
+import user from 'gmfapi/store/user';
+
+
+
+/**
+ * CUSTOM
+ * Get custom special role from the user's model.
+ */
+ngeoAuthForm.prototype.getSpecialRole_ = function(): string {
+ return user.getSpecialRole();
+}
+
+/**
+ * Calls the authentication service login method.
+ * CUSTOM: manage special role.
+ * @param evt Event from the form submit action.
+ */
+ngeoAuthForm.prototype.login = function(evt: Event): void {
+ evt.preventDefault();
+
+ this.manualLoginLogout_();
+
+ this.isLoading = true;
+ const errors = [];
+ const form = evt.target as HTMLFormElement;
+ const loginVal = (form.login as HTMLInputElement).value;
+ const pwdVal = (form.password as HTMLInputElement).value;
+
+ if (loginVal === '') {
+ errors.push(i18next.t('The username is required.'));
+ }
+ if (pwdVal === '') {
+ errors.push(i18next.t('The password is required.'));
+ }
+ if (errors.length) {
+ this.isLoading = false;
+ this.setError_(errors);
+ } else {
+ // CUSTOM
+ authenticationService.setSpecialRole(form.specialRole.value);
+ // CUSTOM END
+ authenticationService
+ .login(loginVal, pwdVal)
+ .then(() => {
+ this.resetError_();
+ })
+ .catch(() => {
+ this.setError_([i18next.t('Incorrect credentials or disabled account.')]);
+ })
+ .finally(() => {
+ this.isLoading = false;
+ form.reset();
+ });
+ }
+}
+
+/**
+ * Render the HTML
+ * CUSTOM: Manage special role (new input, new feedback once logged in).
+ */
+ngeoAuthForm.prototype.render = function(): TemplateResult {
+ // CUSTOM
+ this.customCSS_ = `
+ ${this.customCSS_}
+ strong {
+ text-shadow: 1px 1px 2px black;
+ }
+ `
+ // CUSTOM END
+ return html`
+
+
+ ${this.gmfUser.is_intranet
+ ? html`
+
+ ${i18next.t('You are recognized as an intranet user.')}
+
+ `
+ : ''}
+ ${this.gmfUser.username !== null
+ ? html`
+
+
+ ${i18next.t('Logged in as')}
+ ${this.gmfUser.username}.
+
+
+
+ ${i18next.t('With special role')}
+ ${this.getSpecialRole_()}.
+
+
+ ${!this.changingPassword
+ ? html`
+
+ `
+ : ''}
+
+ `
+ : ''}
+ ${this.loginInfoMessage
+ ? html`
+
+ ${this.loginInfoMessage}
+
+ `
+ : ''}
+ ${this.disconnectedShown
+ ? html`
+
+ ${i18next.t('You are not logged in any more. The Interface has been reloaded.')}
+
+ `
+ : ''}
+ ${this.gmfUser.username === null && !this.changingPassword
+ ? html`
+
+
+ ${this.resetPasswordShown
+ ? html`
+ ${i18next.t('A new password has just been sent to you by e-mail.')}
+
`
+ : ''}
+
+ `
+ : ''}
+ ${this.changingPassword
+ ? html`
+
+ ${this.userMustChangeItsPassword
+ ? html`
${i18next.t('You must change your password')}
`
+ : ''}
+
+
+ `
+ : ''}
+
+ `;
+}
diff --git a/geoportal/geomapfish_geoportal/static-ngeo/js/customization/authService.ts b/geoportal/geomapfish_geoportal/static-ngeo/js/customization/authService.ts
new file mode 100644
index 000000000..caf9e09e3
--- /dev/null
+++ b/geoportal/geomapfish_geoportal/static-ngeo/js/customization/authService.ts
@@ -0,0 +1,37 @@
+import * as Sentry from '@sentry/browser';
+import {AuthenticationService} from 'ngeo/auth/service';
+import user from 'gmfapi/store/user';
+
+/**
+ * CUSTOM
+ * Set the special role to use it in the setUser_ methode.
+ */
+AuthenticationService.prototype.setSpecialRole = function(specialRole: string) {
+ this.specialRole_ = specialRole;
+}
+
+/**
+ * CUSTOM
+ * Redefine setUser to add management of a specialRole;
+ * @param respData Response.
+ * @param userState state of the user.
+ * @private
+ */
+AuthenticationService.prototype.setUser_ = function(respData: User, userState: UserState): void {
+ Sentry.setUser({
+ username: respData.username,
+ });
+
+ // CUSTOM part under
+ if (!respData.username) {
+ this.specialRole_ = null;
+ }
+ if (this.specialRole_ === undefined) {
+ this.specialRole_ = localStorage.getItem('gmfUserSpecialRole');
+ } else {
+ localStorage.setItem('gmfUserSpecialRole', this.specialRole_);
+ }
+ // CUSTOM END
+
+ user.setUser(respData, userState, this.specialRole_);
+}
diff --git a/geoportal/geomapfish_geoportal/static-ngeo/js/customization/helloWorld.ts b/geoportal/geomapfish_geoportal/static-ngeo/js/customization/helloWorld.ts
new file mode 100644
index 000000000..187b74511
--- /dev/null
+++ b/geoportal/geomapfish_geoportal/static-ngeo/js/customization/helloWorld.ts
@@ -0,0 +1,32 @@
+// File ...static-ngeo/js/custom/helloWorld.ts;
+import {html, TemplateResult, CSSResult, css} from 'lit';
+import {customElement, property} from 'lit/decorators.js';
+import GmfBaseElement from 'gmfapi/elements/BaseElement';
+import i18next from 'i18next';
+
+@customElement('hello-world')
+export default class GmfAuthForm extends GmfBaseElement {
+ @property({type: String}) name = 'Not set';
+
+ connectedCallback(): void {
+ super.connectedCallback();
+ }
+
+ static styles: CSSResult[] = [
+ ...GmfBaseElement.styles,
+ css`
+ .name {
+ color: green;
+ }
+ `,
+ ];
+
+ protected render(): TemplateResult {
+ return html`
+
+ ${i18next.t('Hello')}
+ ${this.name}
+
+ `;
+ }
+}
diff --git a/geoportal/geomapfish_geoportal/static-ngeo/js/customization/panelElement.ts b/geoportal/geomapfish_geoportal/static-ngeo/js/customization/panelElement.ts
new file mode 100644
index 000000000..e5d859eeb
--- /dev/null
+++ b/geoportal/geomapfish_geoportal/static-ngeo/js/customization/panelElement.ts
@@ -0,0 +1,66 @@
+// The MIT License (MIT)
+//
+// Copyright (c) 2021 Camptocamp SA
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of
+// this software and associated documentation files (the "Software"), to deal in
+// the Software without restriction, including without limitation the rights to
+// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+// the Software, and to permit persons to whom the Software is furnished to do so,
+// subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+import {html, TemplateResult, unsafeCSS} from 'lit';
+import {customElement, property} from 'lit/decorators.js';
+import {unsafeSVG} from 'lit/directives/unsafe-svg.js';
+import loadingSvg from 'gmf/icons/spinner.svg';
+import i18next from 'i18next';
+import 'ngeo/auth/FormElement';
+
+import GmfAuthPanel from 'ngeo/auth/PanelElement';
+
+// CUSTOM element that inherit from GmfAuthPanel.
+@customElement('demo-auth-panel')
+export default class DemoAuthPanel extends GmfAuthPanel {
+ // CUSTOM property
+ @property({type: String}) customMessage: string = 'Not set';
+
+ protected render(): TemplateResult {
+ const spinnerTemplate = this.postLoading
+ ? html`
+
+ ${
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
+ unsafeSVG(loadingSvg)
+ }
+ ${i18next.t('Loading themes, please wait...')}
+
+ `
+ : '';
+ return html`
+
+
+ With my custom message: ${this.customMessage}
+
+ ${this.getTitle(i18next.t('Login'))}
+
+ ${spinnerTemplate}
+ `;
+ }
+}
diff --git a/geoportal/geomapfish_geoportal/static-ngeo/js/customization/user.ts b/geoportal/geomapfish_geoportal/static-ngeo/js/customization/user.ts
new file mode 100644
index 000000000..d35eb4e9a
--- /dev/null
+++ b/geoportal/geomapfish_geoportal/static-ngeo/js/customization/user.ts
@@ -0,0 +1,27 @@
+import {UserModel} from 'gmfapi/store/user';
+
+/**
+ * CUSTOM
+ * Set the current User's properties and state.
+ * @return the custom role.
+ */
+UserModel.prototype.getSpecialRole = function(): string {
+ return this.specialRole_;
+};
+
+/**
+ * Set the current User's properties and state.
+ * @param properties The new user
+ * @param state The new state
+ * @param specialRole A custom very special role.
+ */
+UserModel.prototype.setUser = function(properties: User, state: UserState, specialRole: string): void {
+ const isValid = this.checkUserProperties_(properties);
+ if (!isValid || state === null) {
+ return;
+ }
+ this.state_ = state;
+ this.properties_.next(properties);
+ // CUSTOM part under
+ this.specialRole_ = specialRole;
+}
diff --git a/geoportal/geomapfish_geoportal/static-ngeo/js/customizationLoader.ts b/geoportal/geomapfish_geoportal/static-ngeo/js/customizationLoader.ts
new file mode 100644
index 000000000..dad371d96
--- /dev/null
+++ b/geoportal/geomapfish_geoportal/static-ngeo/js/customizationLoader.ts
@@ -0,0 +1,21 @@
+// Test to override a lit component and a rxjs "store".
+import './customization/user.ts';
+import './customization/authService.ts';
+import './customization/authFormElement.ts';
+import './customization/panelElement.ts';
+// Create a small brand new web-component.
+import './customization/helloWorld.ts';
+// Small test to override an AngularJS component.
+import './customization/angularDrawPanel.ts';
+
+/**
+ * CUSTOM custom function to override style in the Shadow DOM scope.
+ * Here we target the gmf-auth-form of the demo-auth-panel (a Shadow DOM
+ * in another shadow DOM).
+ */
+export const customCssFn = ()=> {
+ const style = document.createElement( 'style' )
+ style.innerHTML = 'strong { color: darkblue; }'
+ const demoAuthPanel = document.querySelector('demo-auth-panel').shadowRoot;
+ demoAuthPanel.querySelector('gmf-auth-form').shadowRoot.appendChild(style);
+}
diff --git a/geoportal/geomapfish_geoportal/static-ngeo/js/geomapfishmodule.js b/geoportal/geomapfish_geoportal/static-ngeo/js/geomapfishmodule.js
index 104cde736..bff195041 100644
--- a/geoportal/geomapfish_geoportal/static-ngeo/js/geomapfishmodule.js
+++ b/geoportal/geomapfish_geoportal/static-ngeo/js/geomapfishmodule.js
@@ -10,9 +10,9 @@ import {decodeQueryString} from 'ngeo/utils.js';
/**
* @type {!angular.IModule}
*/
-const module = angular.module('geomapfish', []);
+const myModule = angular.module('geomapfish', []);
-module.config([
+myModule.config([
'$compileProvider',
function ($compileProvider) {
if (!('debug' in decodeQueryString(window.location.search))) {
@@ -22,4 +22,4 @@ module.config([
},
]);
-export default module;
+export default myModule;
diff --git a/geoportal/vars.yaml b/geoportal/vars.yaml
index 06d0b6ef1..ef4cae1c2 100644
--- a/geoportal/vars.yaml
+++ b/geoportal/vars.yaml
@@ -151,6 +151,8 @@ vars:
extends: desktop
redirect_interface: mobile_alt
constants:
+ gmfCustomCSS:
+ authentication: 'strong {color: red;}'
sentryOptions:
<<: *sentryOptions
tags:
@@ -775,6 +777,7 @@ no_interpreted:
- shortener.email_body
- welcome_email.email_body
- interfaces_config.mobile_alt.constants.gmfMobileMeasurePointOptions.format
+ - interfaces_config.desktop_alt.constants.gmfCustomCSS.authentication
runtime_environment:
- name: SMTP_USER