diff --git a/e2e/resources/extensibility-configs/context-submenus-ext.json b/e2e/resources/extensibility-configs/context-submenus-ext.json
index 9c7df193fd..94bb45d022 100644
--- a/e2e/resources/extensibility-configs/context-submenus-ext.json
+++ b/e2e/resources/extensibility-configs/context-submenus-ext.json
@@ -46,39 +46,52 @@
"features": {
"header": [
{
- "id": "app.header.user",
+ "id": "app.header.notification-center",
"type": "custom",
- "component": "app.user",
- "order": 100
+ "component": "app.notification-center",
+ "order": 50
},
{
"id": "app.header.more",
- "type": "menu",
+ "type": "custom",
"order": 10000,
- "icon": "more_vert",
+ "component": "app.user.menu",
"title": "APP.ACTIONS.MORE",
- "children": [
- {
- "id": "app.languagePicker",
- "order": 100,
- "type": "custom",
- "component": "app.languagePicker"
- },
- {
- "id": "logout.separator",
- "type": "separator",
- "order": 199
- },
- {
- "id": "app.logout",
- "order": 200,
- "type": "custom",
- "component": "app.logout",
- "rules": {
- "visible": "app.canShowLogout"
+ "data": {
+ "items": [
+ {
+ "id": "app.header.user",
+ "type": "custom",
+ "component": "app.user",
+ "order": 100
+ },
+ {
+ "id": "user.separator",
+ "type": "separator",
+ "order": 100
+ },
+ {
+ "id": "app.languagePicker",
+ "order": 100,
+ "type": "custom",
+ "component": "app.languagePicker"
+ },
+ {
+ "id": "logout.separator",
+ "type": "separator",
+ "order": 199
+ },
+ {
+ "id": "app.logout",
+ "order": 200,
+ "type": "custom",
+ "component": "app.logout",
+ "rules": {
+ "visible": "app.canShowLogout"
+ }
}
- }
- ]
+ ]
+ }
}
],
"icons": [
diff --git a/e2e/resources/extensibility-configs/document-presets-ext.json b/e2e/resources/extensibility-configs/document-presets-ext.json
index 3dcb666764..fc3ab4bbce 100644
--- a/e2e/resources/extensibility-configs/document-presets-ext.json
+++ b/e2e/resources/extensibility-configs/document-presets-ext.json
@@ -46,39 +46,52 @@
"features": {
"header": [
{
- "id": "app.header.user",
+ "id": "app.header.notification-center",
"type": "custom",
- "component": "app.user",
- "order": 100
+ "component": "app.notification-center",
+ "order": 50
},
{
"id": "app.header.more",
- "type": "menu",
+ "type": "custom",
"order": 10000,
- "icon": "more_vert",
+ "component": "app.user.menu",
"title": "APP.ACTIONS.MORE",
- "children": [
- {
- "id": "app.languagePicker",
- "order": 100,
- "type": "custom",
- "component": "app.languagePicker"
- },
- {
- "id": "logout.separator",
- "type": "separator",
- "order": 199
- },
- {
- "id": "app.logout",
- "order": 200,
- "type": "custom",
- "component": "app.logout",
- "rules": {
- "visible": "app.canShowLogout"
+ "data": {
+ "items": [
+ {
+ "id": "app.header.user",
+ "type": "custom",
+ "component": "app.user",
+ "order": 100
+ },
+ {
+ "id": "user.separator",
+ "type": "separator",
+ "order": 100
+ },
+ {
+ "id": "app.languagePicker",
+ "order": 100,
+ "type": "custom",
+ "component": "app.languagePicker"
+ },
+ {
+ "id": "logout.separator",
+ "type": "separator",
+ "order": 199
+ },
+ {
+ "id": "app.logout",
+ "order": 200,
+ "type": "custom",
+ "component": "app.logout",
+ "rules": {
+ "visible": "app.canShowLogout"
+ }
}
- }
- ]
+ ]
+ }
}
],
"icons": [
diff --git a/e2e/resources/extensibility-configs/header-ext.json b/e2e/resources/extensibility-configs/header-ext.json
index fb9aa267ba..5b5ea0efa7 100644
--- a/e2e/resources/extensibility-configs/header-ext.json
+++ b/e2e/resources/extensibility-configs/header-ext.json
@@ -46,66 +46,79 @@
"features": {
"header": [
{
- "id": "app.header.user",
+ "id": "app.header.notification-center",
"type": "custom",
- "component": "app.user",
- "order": 100
+ "component": "app.notification-center",
+ "order": 50
},
{
"id": "app.header.more",
- "type": "menu",
+ "type": "custom",
"order": 10000,
- "icon": "more_vert",
+ "component": "app.user.menu",
"title": "APP.ACTIONS.MORE",
- "children": [
- {
- "id": "app.languagePicker",
- "order": 100,
- "type": "custom",
- "component": "app.languagePicker"
- },
- {
- "id": "logout.separator",
- "type": "separator",
- "order": 199
- },
- {
- "id": "settings",
- "title": "App settings",
- "description": "Application settings",
- "icon": "settings",
- "disabled": true,
- "order": 10,
- "actions": {
- "click": "app.actions.settings"
+ "data": {
+ "items": [
+ {
+ "id": "app.header.user",
+ "type": "custom",
+ "component": "app.user",
+ "order": 100
},
- "rules": {
- "visible": "app.navigation.isNotTrashcan"
- }
- },
- {
- "id": "button",
- "title": "New Button",
- "description": "new button description",
- "icon": "alarm_on",
- "order": 20,
- "actions": {
- "click": "app.actions.settings"
+ {
+ "id": "user.separator",
+ "type": "separator",
+ "order": 100
},
- "rules": {
- "visible": "app.navigation.isNotTrashcan"
- }
- },
- {
- "id": "app.logout",
- "order": 200,
- "type": "custom",
- "component": "app.logout",
- "rules": {
- "visible": "app.canShowLogout"
+ {
+ "id": "app.languagePicker",
+ "order": 100,
+ "type": "custom",
+ "component": "app.languagePicker"
+ },
+ {
+ "id": "settings",
+ "title": "App settings",
+ "description": "Application settings",
+ "icon": "settings",
+ "disabled": true,
+ "order": 10,
+ "actions": {
+ "click": "app.actions.settings"
+ },
+ "rules": {
+ "visible": "app.navigation.isNotTrashcan"
+ }
+ },
+ {
+ "id": "button",
+ "title": "New Button",
+ "description": "new button description",
+ "icon": "alarm_on",
+ "order": 20,
+ "actions": {
+ "click": "app.actions.settings"
+ },
+ "rules": {
+ "visible": "app.navigation.isNotTrashcan"
+ }
+ },
+ {
+ "id": "logout.separator",
+ "type": "separator",
+ "order": 199
+ },
+ {
+ "id": "app.logout",
+ "order": 200,
+ "type": "custom",
+ "component": "app.logout",
+ "rules": {
+ "visible": "app.canShowLogout"
+ }
}
- }
- ]
+ ]
+ }
}
],
"icons": [
diff --git a/e2e/resources/extensibility-configs/info-drawer-ext.json b/e2e/resources/extensibility-configs/info-drawer-ext.json
index 89da3b859a..f032057213 100644
--- a/e2e/resources/extensibility-configs/info-drawer-ext.json
+++ b/e2e/resources/extensibility-configs/info-drawer-ext.json
@@ -46,39 +46,52 @@
"features": {
"header": [
{
- "id": "app.header.user",
+ "id": "app.header.notification-center",
"type": "custom",
- "component": "app.user",
- "order": 100
+ "component": "app.notification-center",
+ "order": 50
},
{
"id": "app.header.more",
- "type": "menu",
+ "type": "custom",
"order": 10000,
- "icon": "more_vert",
+ "component": "app.user.menu",
"title": "APP.ACTIONS.MORE",
- "children": [
- {
- "id": "app.languagePicker",
- "order": 100,
- "type": "custom",
- "component": "app.languagePicker"
- },
- {
- "id": "logout.separator",
- "type": "separator",
- "order": 199
- },
- {
- "id": "app.logout",
- "order": 200,
- "type": "custom",
- "component": "app.logout",
- "rules": {
- "visible": "app.canShowLogout"
+ "data": {
+ "items": [
+ {
+ "id": "app.header.user",
+ "type": "custom",
+ "component": "app.user",
+ "order": 100
+ },
+ {
+ "id": "user.separator",
+ "type": "separator",
+ "order": 100
+ },
+ {
+ "id": "app.languagePicker",
+ "order": 100,
+ "type": "custom",
+ "component": "app.languagePicker"
+ },
+ {
+ "id": "logout.separator",
+ "type": "separator",
+ "order": 199
+ },
+ {
+ "id": "app.logout",
+ "order": 200,
+ "type": "custom",
+ "component": "app.logout",
+ "rules": {
+ "visible": "app.canShowLogout"
+ }
}
- }
- ]
+ ]
+ }
}
],
"icons": [
diff --git a/e2e/resources/extensibility-configs/info-drawer-no-tabs-ext.json b/e2e/resources/extensibility-configs/info-drawer-no-tabs-ext.json
index 0cd68e1216..d50b8d7840 100644
--- a/e2e/resources/extensibility-configs/info-drawer-no-tabs-ext.json
+++ b/e2e/resources/extensibility-configs/info-drawer-no-tabs-ext.json
@@ -46,39 +46,52 @@
"features": {
"header": [
{
- "id": "app.header.user",
+ "id": "app.header.notification-center",
"type": "custom",
- "component": "app.user",
- "order": 100
+ "component": "app.notification-center",
+ "order": 50
},
{
"id": "app.header.more",
- "type": "menu",
+ "type": "custom",
"order": 10000,
- "icon": "more_vert",
+ "component": "app.user.menu",
"title": "APP.ACTIONS.MORE",
- "children": [
- {
- "id": "app.languagePicker",
- "order": 100,
- "type": "custom",
- "component": "app.languagePicker"
- },
- {
- "id": "logout.separator",
- "type": "separator",
- "order": 199
- },
- {
- "id": "app.logout",
- "order": 200,
- "type": "custom",
- "component": "app.logout",
- "rules": {
- "visible": "app.canShowLogout"
+ "data": {
+ "items": [
+ {
+ "id": "app.header.user",
+ "type": "custom",
+ "component": "app.user",
+ "order": 100
+ },
+ {
+ "id": "user.separator",
+ "type": "separator",
+ "order": 100
+ },
+ {
+ "id": "app.languagePicker",
+ "order": 100,
+ "type": "custom",
+ "component": "app.languagePicker"
+ },
+ {
+ "id": "logout.separator",
+ "type": "separator",
+ "order": 199
+ },
+ {
+ "id": "app.logout",
+ "order": 200,
+ "type": "custom",
+ "component": "app.logout",
+ "rules": {
+ "visible": "app.canShowLogout"
+ }
}
- }
- ]
+ ]
+ }
}
],
"icons": [
diff --git a/e2e/resources/extensibility-configs/metadata-ext.json b/e2e/resources/extensibility-configs/metadata-ext.json
index 31b94c3ec3..ea508fdd50 100644
--- a/e2e/resources/extensibility-configs/metadata-ext.json
+++ b/e2e/resources/extensibility-configs/metadata-ext.json
@@ -46,39 +46,52 @@
"features": {
"header": [
{
- "id": "app.header.user",
+ "id": "app.header.notification-center",
"type": "custom",
- "component": "app.user",
- "order": 100
+ "component": "app.notification-center",
+ "order": 50
},
{
"id": "app.header.more",
- "type": "menu",
+ "type": "custom",
"order": 10000,
- "icon": "more_vert",
+ "component": "app.user.menu",
"title": "APP.ACTIONS.MORE",
- "children": [
- {
- "id": "app.languagePicker",
- "order": 100,
- "type": "custom",
- "component": "app.languagePicker"
- },
- {
- "id": "logout.separator",
- "type": "separator",
- "order": 199
- },
- {
- "id": "app.logout",
- "order": 200,
- "type": "custom",
- "component": "app.logout",
- "rules": {
- "visible": "app.canShowLogout"
+ "data": {
+ "items": [
+ {
+ "id": "app.header.user",
+ "type": "custom",
+ "component": "app.user",
+ "order": 100
+ },
+ {
+ "id": "user.separator",
+ "type": "separator",
+ "order": 100
+ },
+ {
+ "id": "app.languagePicker",
+ "order": 100,
+ "type": "custom",
+ "component": "app.languagePicker"
+ },
+ {
+ "id": "logout.separator",
+ "type": "separator",
+ "order": 199
+ },
+ {
+ "id": "app.logout",
+ "order": 200,
+ "type": "custom",
+ "component": "app.logout",
+ "rules": {
+ "visible": "app.canShowLogout"
+ }
}
- }
- ]
+ ]
+ }
}
],
"icons": [
diff --git a/e2e/resources/extensibility-configs/viewer-ext.json b/e2e/resources/extensibility-configs/viewer-ext.json
index 0dc53e075c..be6702d8ec 100644
--- a/e2e/resources/extensibility-configs/viewer-ext.json
+++ b/e2e/resources/extensibility-configs/viewer-ext.json
@@ -46,39 +46,52 @@
"features": {
"header": [
{
- "id": "app.header.user",
+ "id": "app.header.notification-center",
"type": "custom",
- "component": "app.user",
- "order": 100
+ "component": "app.notification-center",
+ "order": 50
},
{
"id": "app.header.more",
- "type": "menu",
+ "type": "custom",
"order": 10000,
- "icon": "more_vert",
+ "component": "app.user.menu",
"title": "APP.ACTIONS.MORE",
- "children": [
- {
- "id": "app.languagePicker",
- "order": 100,
- "type": "custom",
- "component": "app.languagePicker"
- },
- {
- "id": "logout.separator",
- "type": "separator",
- "order": 199
- },
- {
- "id": "app.logout",
- "order": 200,
- "type": "custom",
- "component": "app.logout",
- "rules": {
- "visible": "app.canShowLogout"
+ "data": {
+ "items": [
+ {
+ "id": "app.header.user",
+ "type": "custom",
+ "component": "app.user",
+ "order": 100
+ },
+ {
+ "id": "user.separator",
+ "type": "separator",
+ "order": 100
+ },
+ {
+ "id": "app.languagePicker",
+ "order": 100,
+ "type": "custom",
+ "component": "app.languagePicker"
+ },
+ {
+ "id": "logout.separator",
+ "type": "separator",
+ "order": 199
+ },
+ {
+ "id": "app.logout",
+ "order": 200,
+ "type": "custom",
+ "component": "app.logout",
+ "rules": {
+ "visible": "app.canShowLogout"
+ }
}
- }
- ]
+ ]
+ }
}
],
"icons": [
diff --git a/projects/aca-content/about/assets/about.plugin.json b/projects/aca-content/about/assets/about.plugin.json
index eb236cd91e..35ffac1497 100644
--- a/projects/aca-content/about/assets/about.plugin.json
+++ b/projects/aca-content/about/assets/about.plugin.json
@@ -27,18 +27,20 @@
"header": [
{
"id": "app.header.more",
- "children": [
- {
- "id": "app.header.about",
- "order": 100,
- "title": "APP.BROWSE.ABOUT.TITLE",
- "description": "APP.BROWSE.ABOUT.TITLE",
- "icon": "info",
- "actions": {
- "click": "app.actions.about"
+ "data": {
+ "items": [
+ {
+ "id": "app.header.about",
+ "order": 100,
+ "title": "APP.BROWSE.ABOUT.TITLE",
+ "description": "APP.BROWSE.ABOUT.TITLE",
+ "icon": "info",
+ "actions": {
+ "click": "app.actions.about"
+ }
}
- }
- ]
+ ]
+ }
}
]
}
diff --git a/projects/aca-content/assets/app.extensions.json b/projects/aca-content/assets/app.extensions.json
index 5065c62a31..a39d329755 100644
--- a/projects/aca-content/assets/app.extensions.json
+++ b/projects/aca-content/assets/app.extensions.json
@@ -87,38 +87,45 @@
},
{
"id": "app.header.more",
- "type": "menu",
+ "type": "custom",
"order": 10000,
- "icon": "apps",
+ "component": "app.user.menu",
"title": "APP.ACTIONS.MORE",
- "children": [
- {
- "id": "app.header.user",
- "type": "custom",
- "component": "app.user",
- "order": 100
- },
- {
- "id": "app.languagePicker",
- "order": 100,
- "type": "custom",
- "component": "app.languagePicker"
- },
- {
- "id": "logout.separator",
- "type": "separator",
- "order": 199
- },
- {
- "id": "app.logout",
- "order": 200,
- "type": "custom",
- "component": "app.logout",
- "rules": {
- "visible": "app.canShowLogout"
+ "data": {
+ "items": [
+ {
+ "id": "app.header.user",
+ "type": "custom",
+ "component": "app.user",
+ "order": 100
+ },
+ {
+ "id": "user.separator",
+ "type": "separator",
+ "order": 100
+ },
+ {
+ "id": "app.languagePicker",
+ "order": 100,
+ "type": "custom",
+ "component": "app.languagePicker"
+ },
+ {
+ "id": "logout.separator",
+ "type": "separator",
+ "order": 199
+ },
+ {
+ "id": "app.logout",
+ "order": 200,
+ "type": "custom",
+ "component": "app.logout",
+ "rules": {
+ "visible": "app.canShowLogout"
+ }
}
- }
- ]
+ ]
+ }
}
],
"icons": [
diff --git a/projects/aca-content/src/lib/aca-content.module.ts b/projects/aca-content/src/lib/aca-content.module.ts
index ada701a0e1..54a9d082f4 100644
--- a/projects/aca-content/src/lib/aca-content.module.ts
+++ b/projects/aca-content/src/lib/aca-content.module.ts
@@ -118,6 +118,7 @@ import { UserInfoComponent } from './components/common/user-info/user-info.compo
import { SidenavComponent } from './components/sidenav/sidenav.component';
import { ContentManagementService } from './services/content-management.service';
import { ShellLayoutComponent, SHELL_NAVBAR_MIN_WIDTH } from '@alfresco/adf-core/shell';
+import { UserMenuComponent } from './components/sidenav/user-menu/user-menu.component';
registerLocaleData(localeFr);
registerLocaleData(localeDe);
@@ -229,7 +230,8 @@ export class ContentServiceExtensionModule {
'app.languagePicker': LanguagePickerComponent,
'app.logout': LogoutComponent,
'app.user': UserInfoComponent,
- 'app.notification-center': NotificationHistoryComponent
+ 'app.notification-center': NotificationHistoryComponent,
+ 'app.user.menu': UserMenuComponent
});
extensions.setEvaluators({
diff --git a/projects/aca-content/src/lib/components/common/user-info/user-info.component.html b/projects/aca-content/src/lib/components/common/user-info/user-info.component.html
index 7017c6af68..d3e115f5a8 100644
--- a/projects/aca-content/src/lib/components/common/user-info/user-info.component.html
+++ b/projects/aca-content/src/lib/components/common/user-info/user-info.component.html
@@ -1,4 +1,9 @@
-
+
+
+
+
{{ (displayName$ | async)?.firstName }}
+
{{ (displayName$ | async)?.email }}
+
+
diff --git a/projects/aca-content/src/lib/components/common/user-info/user-info.component.scss b/projects/aca-content/src/lib/components/common/user-info/user-info.component.scss
new file mode 100644
index 0000000000..1d168b2ece
--- /dev/null
+++ b/projects/aca-content/src/lib/components/common/user-info/user-info.component.scss
@@ -0,0 +1,25 @@
+.aca-user-info {
+ display: flex;
+ height: 66px;
+ align-items: center;
+
+ &-button {
+ border-radius: 90%;
+ height: 32px;
+ margin-right: 0;
+ min-width: 32px;
+ padding: 0;
+ font-weight: 700;
+ line-height: 32px;
+ text-align: center;
+ vertical-align: middle;
+ background: var(--theme-user-initials-background-color);
+ color: var(--theme-user-intials-text-color);
+ border: none;
+ }
+
+ &-name-email {
+ line-height: 24px;
+ margin-left: 10px;
+ }
+}
diff --git a/projects/aca-content/src/lib/components/common/user-info/user-info.component.spec.ts b/projects/aca-content/src/lib/components/common/user-info/user-info.component.spec.ts
index 2df6b9ec9e..73e33247b9 100644
--- a/projects/aca-content/src/lib/components/common/user-info/user-info.component.spec.ts
+++ b/projects/aca-content/src/lib/components/common/user-info/user-info.component.spec.ts
@@ -23,7 +23,7 @@
*/
import { ComponentFixture, TestBed } from '@angular/core/testing';
-import { AuthenticationService } from '@alfresco/adf-core';
+import { AuthenticationService, IdentityUserService } from '@alfresco/adf-core';
import { PeopleContentService } from '@alfresco/adf-content-services';
import { UserInfoComponent } from './user-info.component';
import { AppTestingModule } from '../../../testing/app-testing.module';
@@ -34,7 +34,7 @@ describe('UserInfoComponent', () => {
let fixture: ComponentFixture;
let authServiceStub: Partial;
let peopleContentServiceStub: Partial;
- let identityUserServiceStub: Partial;
+ let identityUserServiceStub: Partial;
beforeEach(() => {
authServiceStub = {
@@ -67,24 +67,23 @@ describe('UserInfoComponent', () => {
};
identityUserServiceStub = {
- getCurrentUserInfo: () =>
- of({
- firstName: 'John',
- email: 'john@example.com',
- id: 'johnDoe1',
- enabled: true,
- company: {
- organization: 'ABC Organization',
- address1: 'XYZ Road',
- address2: 'Ohio',
- address3: 'Westlake',
- postcode: '44145',
- telephone: '456876',
- fax: '323984',
- email: 'contact.us@abc.com'
- },
- isAdmin: () => true
- })
+ getCurrentUserInfo: () => ({
+ firstName: 'John',
+ email: 'john@example.com',
+ id: 'johnDoe1',
+ enabled: true,
+ company: {
+ organization: 'ABC Organization',
+ address1: 'XYZ Road',
+ address2: 'Ohio',
+ address3: 'Westlake',
+ postcode: '44145',
+ telephone: '456876',
+ fax: '323984',
+ email: 'contact.us@abc.com'
+ },
+ isAdmin: () => true
+ })
};
TestBed.configureTestingModule({
@@ -93,7 +92,7 @@ describe('UserInfoComponent', () => {
providers: [
{ provide: AuthenticationService, useValue: authServiceStub },
{ provide: PeopleContentService, useValue: peopleContentServiceStub },
- { provide: identityUserServiceStub, useValue: identityUserServiceStub }
+ { provide: IdentityUserService, useValue: identityUserServiceStub }
]
}).compileComponents();
@@ -107,15 +106,45 @@ describe('UserInfoComponent', () => {
expect(loggedIn).toBeTrue();
});
- it('should parse display name without email', async () => {
- const model = { firstName: 'John' };
- const displayName = component['parseDisplayName'](model);
- expect(displayName).toBe('John');
+ it('should return an object with empty strings for all properties when the input model is empty', () => {
+ const result = component.parseDisplayName({});
+ expect(result.firstName).toEqual('');
+ expect(result.initials).toEqual('');
+ expect(result.email).toEqual('');
+ });
+
+ it('should return an object with the correct firstName and initials when the input model has only the firstName property', () => {
+ const result = component.parseDisplayName({ firstName: 'John' });
+ expect(result.firstName).toEqual('John');
+ expect(result.initials).toEqual('J');
+ expect(result.email).toEqual('');
+ });
+
+ it('should return an object with the correct firstName and initials when the input model has only the lastName property', () => {
+ const result = component.parseDisplayName({ lastName: 'Doe' });
+ expect(result.firstName).toEqual(' Doe');
+ expect(result.initials).toEqual('D');
+ expect(result.email).toEqual('');
+ });
+
+ it('should return an object with the correct email property when the input model has only the email property', () => {
+ const result = component.parseDisplayName({ email: 'john.doe@example.com' });
+ expect(result.firstName).toEqual('');
+ expect(result.initials).toEqual('');
+ expect(result.email).toEqual('john.doe@example.com');
+ });
+
+ it('should return an object with the correct firstName, initials, and lastName concatenated when the input model has both firstName and lastName properties', () => {
+ const result = component.parseDisplayName({ firstName: 'John', lastName: 'Doe' });
+ expect(result.firstName).toEqual('John Doe');
+ expect(result.initials).toEqual('JD');
+ expect(result.email).toEqual('');
});
- it('should parse display name with email', async () => {
- const model = { firstName: 'John', email: 'john@example.com' };
- const displayName = component['parseDisplayName'](model);
- expect(displayName).toBe('John (john@example.com)');
+ it('should return an object with all properties correctly parsed when the input model has all three properties', () => {
+ const result = component.parseDisplayName({ firstName: 'John', lastName: 'Doe', email: 'john.doe@example.com' });
+ expect(result.firstName).toEqual('John Doe');
+ expect(result.initials).toEqual('JD');
+ expect(result.email).toEqual('john.doe@example.com');
});
});
diff --git a/projects/aca-content/src/lib/components/common/user-info/user-info.component.ts b/projects/aca-content/src/lib/components/common/user-info/user-info.component.ts
index afacbf7097..43f3ce806d 100644
--- a/projects/aca-content/src/lib/components/common/user-info/user-info.component.ts
+++ b/projects/aca-content/src/lib/components/common/user-info/user-info.component.ts
@@ -23,17 +23,19 @@
*/
import { IdentityUserService, AuthenticationService } from '@alfresco/adf-core';
-import { Component, OnInit } from '@angular/core';
+import { Component, OnInit, ViewEncapsulation } from '@angular/core';
import { Observable, of } from 'rxjs';
import { PeopleContentService } from '@alfresco/adf-content-services';
import { map } from 'rxjs/operators';
@Component({
selector: 'app-user-info',
- templateUrl: './user-info.component.html'
+ templateUrl: './user-info.component.html',
+ styleUrls: ['./user-info.component.scss'],
+ encapsulation: ViewEncapsulation.None
})
export class UserInfoComponent implements OnInit {
- displayName$: Observable;
+ displayName$: Observable<{ firstName: string; initials: string; email: string }>;
constructor(
private peopleContentService: PeopleContentService,
@@ -72,13 +74,19 @@ export class UserInfoComponent implements OnInit {
this.displayName$ = of(this.identityUserService.getCurrentUserInfo()).pipe(map((model) => this.parseDisplayName(model)));
}
- private parseDisplayName(model: { firstName?: string; email?: string }): string {
- let result = model.firstName;
-
+ parseDisplayName(model: { firstName?: string; lastName?: string; email?: string }): { firstName: string; initials: string; email: string } {
+ const result = { firstName: '', initials: '', email: '' };
+ if (model.firstName) {
+ result.firstName = model.firstName;
+ result.initials = model.firstName.charAt(0).toUpperCase();
+ }
+ if (model.lastName) {
+ result.firstName += ' ' + model.lastName;
+ result.initials += model.lastName.charAt(0).toUpperCase();
+ }
if (model.email) {
- result = `${model.firstName} (${model.email})`;
+ result.email = `${model.email}`;
}
-
return result;
}
diff --git a/projects/aca-content/src/lib/components/sidenav/sidenav.module.ts b/projects/aca-content/src/lib/components/sidenav/sidenav.module.ts
index 00b4b9f07f..218e71a80d 100644
--- a/projects/aca-content/src/lib/components/sidenav/sidenav.module.ts
+++ b/projects/aca-content/src/lib/components/sidenav/sidenav.module.ts
@@ -36,6 +36,7 @@ import { ButtonMenuComponent } from './components/button-menu.component';
import { ActionDirective } from './directives/action.directive';
import { SidenavHeaderComponent } from './components/sidenav-header.component';
import { SharedToolbarModule } from '@alfresco/aca-shared';
+import { UserMenuComponent } from './user-menu/user-menu.component';
@NgModule({
imports: [CoreModule.forChild(), ExtensionsModule.forChild(), RouterModule, AppCreateMenuModule, SharedToolbarModule],
@@ -47,7 +48,8 @@ import { SharedToolbarModule } from '@alfresco/aca-shared';
ExpandMenuComponent,
ButtonMenuComponent,
SidenavComponent,
- SidenavHeaderComponent
+ SidenavHeaderComponent,
+ UserMenuComponent
],
exports: [
MenuPanelDirective,
diff --git a/projects/aca-content/src/lib/components/sidenav/user-menu/user-menu.component.html b/projects/aca-content/src/lib/components/sidenav/user-menu/user-menu.component.html
new file mode 100644
index 0000000000..67c4e9d644
--- /dev/null
+++ b/projects/aca-content/src/lib/components/sidenav/user-menu/user-menu.component.html
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
diff --git a/projects/aca-content/src/lib/components/sidenav/user-menu/user-menu.component.scss b/projects/aca-content/src/lib/components/sidenav/user-menu/user-menu.component.scss
new file mode 100644
index 0000000000..01066a8eac
--- /dev/null
+++ b/projects/aca-content/src/lib/components/sidenav/user-menu/user-menu.component.scss
@@ -0,0 +1,14 @@
+.aca-user-menu-button {
+ border-radius: 90%;
+ height: 32px;
+ margin-right: 0;
+ min-width: 32px;
+ padding: 0;
+ font-weight: 700;
+ line-height: 32px;
+ text-align: center;
+ vertical-align: middle;
+ background: var(--theme-user-initials-background-color);
+ color: var(--theme-user-intials-text-color);
+ border: none;
+}
diff --git a/projects/aca-content/src/lib/components/sidenav/user-menu/user-menu.component.spec.ts b/projects/aca-content/src/lib/components/sidenav/user-menu/user-menu.component.spec.ts
new file mode 100644
index 0000000000..4b6c00ff34
--- /dev/null
+++ b/projects/aca-content/src/lib/components/sidenav/user-menu/user-menu.component.spec.ts
@@ -0,0 +1,143 @@
+/*!
+ * Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved.
+ *
+ * Alfresco Example Content Application
+ *
+ * This file is part of the Alfresco Example Content Application.
+ * If the software was purchased under a paid Alfresco license, the terms of
+ * the paid license agreement will prevail. Otherwise, the software is
+ * provided under the following open source license terms:
+ *
+ * The Alfresco Example Content Application is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * The Alfresco Example Content Application is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * from Hyland Software. If not, see .
+ */
+
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { AuthenticationService, IdentityUserService } from '@alfresco/adf-core';
+import { PeopleContentService } from '@alfresco/adf-content-services';
+import { AppTestingModule } from '../../../testing/app-testing.module';
+import { UserMenuComponent } from './user-menu.component';
+import { of } from 'rxjs';
+
+describe('UserMenuComponent', () => {
+ let component: UserMenuComponent;
+ let fixture: ComponentFixture;
+ let authServiceStub: Partial;
+ let peopleContentServiceStub: Partial;
+ let identityUserServiceStub: Partial;
+
+ const menuItems = [
+ {
+ id: 'menu1',
+ title: 'Menu Item 1',
+ icon: 'icon1',
+ actions: {
+ click: 'action1'
+ }
+ },
+ {
+ id: 'menu2',
+ title: 'Menu Item 2',
+ icon: 'icon2',
+ actions: {
+ click: 'action2'
+ }
+ }
+ ];
+
+ beforeEach(() => {
+ authServiceStub = {
+ isOauth: () => true,
+ isECMProvider: () => true,
+ isEcmLoggedIn: () => true,
+ isKerberosEnabled: () => false,
+ isLoggedIn: () => true
+ };
+
+ peopleContentServiceStub = {
+ getCurrentUserInfo: () =>
+ of({
+ firstName: 'John',
+ email: 'john@example.com',
+ id: 'johnDoe1',
+ enabled: true,
+ company: {
+ organization: 'ABC Organization',
+ address1: 'XYZ Road',
+ address2: 'Ohio',
+ address3: 'Westlake',
+ postcode: '44145',
+ telephone: '456876',
+ fax: '323984',
+ email: 'contact.us@abc.com'
+ },
+ isAdmin: () => true
+ })
+ };
+
+ identityUserServiceStub = {
+ getCurrentUserInfo: () => ({
+ firstName: 'John',
+ email: 'john@example.com',
+ id: 'johnDoe1',
+ enabled: true,
+ company: {
+ organization: 'ABC Organization',
+ address1: 'XYZ Road',
+ address2: 'Ohio',
+ address3: 'Westlake',
+ postcode: '44145',
+ telephone: '456876',
+ fax: '323984',
+ email: 'contact.us@abc.com'
+ },
+ isAdmin: () => true
+ })
+ };
+
+ TestBed.configureTestingModule({
+ imports: [AppTestingModule],
+ declarations: [UserMenuComponent],
+ providers: [
+ { provide: AuthenticationService, useValue: authServiceStub },
+ { provide: PeopleContentService, useValue: peopleContentServiceStub },
+ { provide: identityUserServiceStub, useValue: identityUserServiceStub }
+ ]
+ }).compileComponents();
+
+ fixture = TestBed.createComponent(UserMenuComponent);
+ component = fixture.componentInstance;
+ component.data = { items: menuItems };
+ fixture.detectChanges();
+ });
+
+ it('should return an object with empty strings for all properties when the input model is empty', () => {
+ const result = component.parseDisplayName({});
+ expect(result.initials).toEqual('');
+ });
+
+ it('should return an object with the correct initials when the input model has only the firstName property', () => {
+ const result = component.parseDisplayName({ firstName: 'John' });
+ expect(result.initials).toEqual('J');
+ });
+
+ it('should return an object with the correct initials when the input model has only the lastName property', () => {
+ const result = component.parseDisplayName({ lastName: 'Doe' });
+ expect(result.initials).toEqual('D');
+ });
+
+ it('should return an object with the correct initials concatenated when the input model has both firstName and lastName properties', () => {
+ const result = component.parseDisplayName({ firstName: 'John', lastName: 'Doe' });
+ expect(result.initials).toEqual('JD');
+ });
+});
diff --git a/projects/aca-content/src/lib/components/sidenav/user-menu/user-menu.component.ts b/projects/aca-content/src/lib/components/sidenav/user-menu/user-menu.component.ts
new file mode 100644
index 0000000000..f4e6167c7d
--- /dev/null
+++ b/projects/aca-content/src/lib/components/sidenav/user-menu/user-menu.component.ts
@@ -0,0 +1,99 @@
+/*!
+ * Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved.
+ *
+ * Alfresco Example Content Application
+ *
+ * This file is part of the Alfresco Example Content Application.
+ * If the software was purchased under a paid Alfresco license, the terms of
+ * the paid license agreement will prevail. Otherwise, the software is
+ * provided under the following open source license terms:
+ *
+ * The Alfresco Example Content Application is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * The Alfresco Example Content Application is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * from Hyland Software. If not, see .
+ */
+
+import { PeopleContentService } from '@alfresco/adf-content-services';
+import { AuthenticationService, IdentityUserService } from '@alfresco/adf-core';
+import { Component, Input, OnInit, ViewEncapsulation } from '@angular/core';
+import { Observable, of } from 'rxjs';
+import { map } from 'rxjs/operators';
+import { ContentActionRef } from '@alfresco/adf-extensions';
+
+@Component({
+ selector: 'aca-user-menu',
+ templateUrl: './user-menu.component.html',
+ styleUrls: ['./user-menu.component.scss'],
+ encapsulation: ViewEncapsulation.None
+})
+export class UserMenuComponent implements OnInit {
+ displayName$: Observable<{ firstName: string; initials: string }>;
+ @Input()
+ actionRef: ContentActionRef;
+ @Input()
+ data: { items: any[] };
+
+ constructor(
+ private peopleContentService: PeopleContentService,
+ private identityUserService: IdentityUserService,
+ private authService: AuthenticationService
+ ) {}
+
+ ngOnInit() {
+ this.getUserInfo();
+ if (this.data && this.data.items) {
+ this.data.items.sort((a, b) => a.order - b.order);
+ }
+ }
+
+ getUserInfo() {
+ if (this.authService.isOauth()) {
+ this.loadIdentityUserInfo();
+
+ if (this.authService.isECMProvider() && this.authService.isEcmLoggedIn()) {
+ this.loadEcmUserInfo();
+ }
+ } else if (this.isEcmLoggedIn()) {
+ this.loadEcmUserInfo();
+ }
+ }
+
+ get isLoggedIn(): boolean {
+ if (this.authService.isKerberosEnabled()) {
+ return true;
+ }
+ return this.authService.isLoggedIn();
+ }
+
+ private loadEcmUserInfo(): void {
+ this.displayName$ = this.peopleContentService.getCurrentUserInfo().pipe(map((model) => this.parseDisplayName(model)));
+ }
+
+ private loadIdentityUserInfo() {
+ this.displayName$ = of(this.identityUserService.getCurrentUserInfo()).pipe(map((model) => this.parseDisplayName(model)));
+ }
+
+ parseDisplayName(model: { firstName?: string; lastName?: string }): { firstName: string; initials: string } {
+ const result = { firstName: '', initials: '' };
+ if (model.firstName) {
+ result.initials = model.firstName.charAt(0).toUpperCase();
+ }
+ if (model.lastName) {
+ result.initials += model.lastName.charAt(0).toUpperCase();
+ }
+ return result;
+ }
+
+ private isEcmLoggedIn() {
+ return this.authService.isEcmLoggedIn() || (this.authService.isECMProvider() && this.authService.isKerberosEnabled());
+ }
+}
diff --git a/projects/aca-content/src/lib/ui/variables/variables.scss b/projects/aca-content/src/lib/ui/variables/variables.scss
index 8489d71da3..8205b50c6f 100644
--- a/projects/aca-content/src/lib/ui/variables/variables.scss
+++ b/projects/aca-content/src/lib/ui/variables/variables.scss
@@ -37,6 +37,8 @@ $selected-background-color: rgba(31, 116, 219, 0.24);
$action-button-text-color: rgba(33, 35, 40, 0.7);
$page-layout-header-background-color: #fff;
$aca-toolbar-button-background-color: rgba(33, 33, 33, 0.05);
+$aca-user-initials-background: rgba(33, 33, 33, 0.12);
+$aca-user-initials-text-color: #212121;
// CSS Variables
$defaults: (
@@ -81,7 +83,9 @@ $defaults: (
--theme-action-button-text-color: $action-button-text-color,
--theme-header-border-color: $grey-background,
--theme-page-layout-header-background-color: $page-layout-header-background-color,
- --theme-app-toolbar-button-background-color: $aca-toolbar-button-background-color
+ --theme-app-toolbar-button-background-color: $aca-toolbar-button-background-color,
+ --theme-user-initials-background-color: $aca-user-initials-background,
+ --theme-user-intials-text-color: $aca-user-initials-text-color
);
// propagates SCSS variables into the CSS variables scope
diff --git a/projects/aca-testing-shared/src/components/header/header.ts b/projects/aca-testing-shared/src/components/header/header.ts
index bd98cd1690..b7394da566 100755
--- a/projects/aca-testing-shared/src/components/header/header.ts
+++ b/projects/aca-testing-shared/src/components/header/header.ts
@@ -33,7 +33,7 @@ import { BrowserActions } from '@alfresco/adf-testing';
export class Header extends Component {
logoLink = this.byCss('.app-menu__title');
- moreActions = browser.element(by.id('app.header.more'));
+ userMenuButton = this.byCss(`.aca-user-menu-button`);
sidenavToggle = this.byCss(`.sidenav-header-title-logo`);
userInfo = new UserInfo();
@@ -46,12 +46,12 @@ export class Header extends Component {
}
async openMoreMenu(): Promise {
- await BrowserActions.click(this.moreActions);
+ await BrowserActions.click(this.userMenuButton);
await this.menu.waitForMenuToOpen();
}
async closeMoreMenu(): Promise {
- await BrowserActions.click(this.moreActions);
+ await BrowserActions.click(this.userMenuButton);
await this.menu.waitForMenuToClose();
}