added user initials

This commit is contained in:
Yasa-Nataliya
2023-05-01 12:00:48 +00:00
parent b2ba94fd08
commit be1b3fa019
10 changed files with 354 additions and 41 deletions

View File

@@ -24,10 +24,7 @@
],
"features": {
"header": [
{
"id": "app.header.more",
"children": [
"moreMenu": [
{
"id": "app.header.about",
"order": 100,
@@ -39,7 +36,5 @@
}
}
]
}
]
}
}

View File

@@ -86,39 +86,32 @@
"order": 50
},
{
"id": "app.header.more",
"type": "menu",
"order": 10000,
"icon": "apps",
"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"
}
}
]
"id": "app.header.user",
"type": "custom",
"component": "app.user.menu",
"order": 100
}
],
"moreMenu": [
{
"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": [

View File

@@ -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({

View File

@@ -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,

View File

@@ -0,0 +1,20 @@
<button mat-button class="aca-menu-user-details-button" [matMenuTriggerFor]="menu">
<div>{{ (displayName$ | async)?.initials }}</div>
</button>
<mat-menu #menu="matMenu" xPosition="before">
<div mat-menu-item class="aca-menu-user-details" [routerLink]="['/profile']"
title="{{'APP.TOOLTIPS.MY_PROFILE' | translate}}">
<button class="aca-menu-user-details-button">
<div>{{ (displayName$ | async)?.initials }}</div>
</button>
<div class="aca-menu-user-details-name-email">
<div>{{ (displayName$ | async)?.firstName }}</div>
<div>{{ (displayName$ | async)?.email }}</div>
</div>
</div>
<mat-divider></mat-divider>
<ng-container *ngFor="let actionRef of actions; trackBy: trackByActionId">
<app-toolbar-menu-item [actionRef]="actionRef"></app-toolbar-menu-item>
</ng-container>
</mat-menu>

View File

@@ -0,0 +1,25 @@
.aca-menu-user-details {
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;
}
}

View File

@@ -0,0 +1,145 @@
/*!
* 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 <http://www.gnu.org/licenses/>.
*/
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('parseDisplayName', () => {
let component: UserMenuComponent;
let fixture: ComponentFixture<UserMenuComponent>;
let authServiceStub: Partial<AuthenticationService>;
let peopleContentServiceStub: Partial<PeopleContentService>;
let identityUserServiceStub: Partial<IdentityUserService>;
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;
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.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 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');
});
});

View File

@@ -0,0 +1,110 @@
/*!
* 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 <http://www.gnu.org/licenses/>.
*/
import { PeopleContentService } from '@alfresco/adf-content-services';
import { AuthenticationService, IdentityUserService } from '@alfresco/adf-core';
import { Component, OnInit } from '@angular/core';
import { Observable, of, Subject } from 'rxjs';
import { map, takeUntil } from 'rxjs/operators';
import { AppExtensionService } from '@alfresco/aca-shared';
import { ContentActionRef } from '@alfresco/adf-extensions';
@Component({
selector: 'aca-user-menu',
templateUrl: './user-menu.component.html',
styleUrls: ['./user-menu.component.scss']
})
export class UserMenuComponent implements OnInit {
displayName$: Observable<{ firstName: string; initials: string; email: string }>;
private onDestroy$ = new Subject<boolean>();
actions: Array<ContentActionRef> = [];
constructor(
private peopleContentService: PeopleContentService,
private identityUserService: IdentityUserService,
private authService: AuthenticationService,
private appExtensions: AppExtensionService
) {}
ngOnInit() {
this.getUserInfo();
this.appExtensions
.getMoreActions()
.pipe(takeUntil(this.onDestroy$))
.subscribe((actions) => {
this.actions = actions;
});
}
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; 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.email = `${model.email}`;
}
return result;
}
private isEcmLoggedIn() {
return this.authService.isEcmLoggedIn() || (this.authService.isECMProvider() && this.authService.isKerberosEnabled());
}
trackByActionId(_: number, action: ContentActionRef) {
return action.id;
}
}

View File

@@ -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);
$acc-user-initials-background: rgba(33, 33, 33, 0.12);
$acc-user-initials-text-color: #212121;
// CSS Variables
$defaults: (
@@ -82,6 +84,8 @@ $defaults: (
--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-user-initials-background-color: $acc-user-initials-background,
--theme-user-intials-text-color: $acc-user-initials-text-color
);
// propagates SCSS variables into the CSS variables scope

View File

@@ -72,6 +72,7 @@ export class AppExtensionService implements RuleContext {
settingGroups: Array<SettingsGroupRef> = [];
private _headerActions = new BehaviorSubject<Array<ContentActionRef>>([]);
private _moreActions = new BehaviorSubject<Array<ContentActionRef>>([]);
private _toolbarActions = new BehaviorSubject<Array<ContentActionRef>>([]);
private _viewerToolbarActions = new BehaviorSubject<Array<ContentActionRef>>([]);
private _sharedLinkViewerToolbarActions = new BehaviorSubject<Array<ContentActionRef>>([]);
@@ -150,6 +151,7 @@ export class AppExtensionService implements RuleContext {
this.settingGroups = this.loader.getElements<SettingsGroupRef>(config, 'settings');
this._headerActions.next(this.loader.getContentActions(config, 'features.header'));
this._moreActions.next(this.loader.getContentActions(config, 'features.moreMenu'));
this._sidebarActions.next(this.loader.getContentActions(config, 'features.sidebar.toolbar'));
this._toolbarActions.next(this.loader.getContentActions(config, 'features.toolbar'));
this._viewerToolbarActions.next(this.loader.getContentActions(config, 'features.viewer.toolbarActions'));
@@ -456,6 +458,21 @@ export class AppExtensionService implements RuleContext {
);
}
getMoreActions(): Observable<Array<ContentActionRef>> {
return this._moreActions.pipe(
map((moreActions) =>
moreActions
.filter((action) => this.filterVisible(action))
.map((action) => this.copyAction(action))
.map((action) => this.buildMenu(action))
.map((action) => this.setActionDisabledFromRule(action))
.sort(sortByOrder)
.reduce(reduceEmptyMenus, [])
.reduce(reduceSeparators, [])
)
);
}
getAllowedContextMenuActions(): Observable<Array<ContentActionRef>> {
return this._contextMenuActions.pipe(map((contextMenuActions) => (!this.selection.isEmpty ? this.getAllowedActions(contextMenuActions) : [])));
}