mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2025-07-24 17:32:15 +00:00
[AAE-10779] User info component refactor (#8187)
* [AAE-10779] Update documentation * [AAE-10779] Update demo-shell user-info component call * [AAE-10779] Ecm user info component * [AAE-10779] Identity user info component * [AAE-10779] Bpm user info component * [AAE-10779] Remove ecm-panel id references * [AAE-10779] add stories and remove old component * [AAE-10779] Update doc version and remove leftover html tag * trigger travis * [AAE-10779] rename ecm-user-info to content-user-info and bpm-user-info to process-user-info * [AAE-10779] update docs * [AAE-10779] fix demo-shell user-info * [AAE-10779] add docs
This commit is contained in:
@@ -115,6 +115,7 @@ import localeSv from '@angular/common/locales/sv';
|
||||
import { setupAppNotifications } from './services/app-notifications-factory';
|
||||
import { AppNotificationsService } from './services/app-notifications.service';
|
||||
import { SearchFilterChipsComponent } from './components/search/search-filter-chips.component';
|
||||
import { UserInfoComponent } from './components/app-layout/user-info/user-info.component';
|
||||
|
||||
registerLocaleData(localeFr);
|
||||
registerLocaleData(localeDe);
|
||||
@@ -159,6 +160,7 @@ registerLocaleData(localeSv);
|
||||
AppComponent,
|
||||
LogoutComponent,
|
||||
AppLayoutComponent,
|
||||
UserInfoComponent,
|
||||
HomeComponent,
|
||||
SearchBarComponent,
|
||||
SearchResultComponent,
|
||||
|
@@ -22,8 +22,7 @@
|
||||
|
||||
<div class="app-header-delimiexpandedSidenavter"></div>
|
||||
|
||||
<adf-userinfo [menuPositionX]="'before'" [menuPositionY]="'above'">
|
||||
</adf-userinfo>
|
||||
<app-shell-user-info [menuPositionX]="'before'" [menuPositionY]="'above'"></app-shell-user-info>
|
||||
|
||||
<app-theme-picker></app-theme-picker>
|
||||
<button data-automation-id="language-menu-button" mat-icon-button [matMenuTriggerFor]="langMenu">
|
||||
|
@@ -0,0 +1,21 @@
|
||||
<ng-container>
|
||||
<adf-content-user-info
|
||||
*ngIf="mode === userInfoMode.CONTENT || mode === userInfoMode.CONTENT_SSO"
|
||||
[ecmUser]="ecmUser$ | async"
|
||||
[identityUser]="identityUser$ | async"
|
||||
[isLoggedIn]="isLoggedIn"
|
||||
[mode]="mode"
|
||||
></adf-content-user-info>
|
||||
<adf-identity-user-info
|
||||
*ngIf="mode === userInfoMode.SSO"
|
||||
[identityUser]="identityUser$ | async"
|
||||
[isLoggedIn]="isLoggedIn"
|
||||
></adf-identity-user-info>
|
||||
<adf-process-user-info
|
||||
*ngIf="mode === userInfoMode.PROCESS || mode === userInfoMode.ALL"
|
||||
[bpmUser]="bpmUser$ | async"
|
||||
[ecmUser]="ecmUser$ | async"
|
||||
[isLoggedIn]="isLoggedIn"
|
||||
[mode]="mode"
|
||||
></adf-process-user-info>
|
||||
</ng-container>
|
@@ -15,34 +15,16 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Component, Input, OnInit, ViewEncapsulation, ViewChild, OnDestroy } from '@angular/core';
|
||||
import { AuthenticationService } from '../../auth/services/authentication.service';
|
||||
import { BpmUserModel } from '../../models/bpm-user.model';
|
||||
import { EcmUserModel } from '../../models/ecm-user.model';
|
||||
import { IdentityUserModel } from '../../auth/models/identity-user.model';
|
||||
import { BpmUserService } from '../../services/bpm-user.service';
|
||||
import { IdentityUserService } from '../../auth/services/identity-user.service';
|
||||
import { of, Observable, Subject } from 'rxjs';
|
||||
import { MatMenuTrigger, MenuPositionX, MenuPositionY } from '@angular/material/menu';
|
||||
import { PeopleContentService } from '../../services/people-content.service';
|
||||
import { AuthenticationService, BpmUserModel, BpmUserService, EcmUserModel, IdentityUserModel, IdentityUserService, PeopleContentService, UserInfoMode } from '@alfresco/adf-core';
|
||||
import { Component, OnInit, Input } from '@angular/core';
|
||||
import { MenuPositionX, MenuPositionY } from '@angular/material/menu';
|
||||
import { Observable, of } from 'rxjs';
|
||||
|
||||
@Component({
|
||||
selector: 'adf-userinfo',
|
||||
templateUrl: './user-info.component.html',
|
||||
styleUrls: ['./user-info.component.scss'],
|
||||
encapsulation: ViewEncapsulation.None
|
||||
selector: 'app-shell-user-info',
|
||||
templateUrl: './user-info.component.html'
|
||||
})
|
||||
export class UserInfoComponent implements OnInit, OnDestroy {
|
||||
|
||||
@ViewChild(MatMenuTrigger) trigger: MatMenuTrigger;
|
||||
|
||||
/** Custom path for the background banner image for ACS users. */
|
||||
@Input()
|
||||
ecmBackgroundImage: string = './assets/images/ecm-background.png';
|
||||
|
||||
/** Custom path for the background banner image for APS users. */
|
||||
@Input()
|
||||
bpmBackgroundImage: string = './assets/images/bpm-background.png';
|
||||
export class UserInfoComponent implements OnInit {
|
||||
|
||||
/** Custom choice for opening the menu at the bottom. Can be `before` or `after`. */
|
||||
@Input()
|
||||
@@ -52,23 +34,12 @@ export class UserInfoComponent implements OnInit, OnDestroy {
|
||||
@Input()
|
||||
menuPositionY: MenuPositionY = 'below';
|
||||
|
||||
/** Shows/hides the username next to the user info button. */
|
||||
@Input()
|
||||
showName: boolean = true;
|
||||
|
||||
/** When the username is shown, this defines its position relative to the user info button.
|
||||
* Can be `right` or `left`.
|
||||
*/
|
||||
@Input()
|
||||
namePosition: string = 'right';
|
||||
|
||||
mode: string;
|
||||
|
||||
mode: UserInfoMode;
|
||||
ecmUser$: Observable<EcmUserModel>;
|
||||
bpmUser$: Observable<BpmUserModel>;
|
||||
identityUser$: Observable<IdentityUserModel>;
|
||||
selectedIndex: number;
|
||||
private destroy$ = new Subject();
|
||||
userInfoMode = UserInfoMode;
|
||||
|
||||
constructor(private peopleContentService: PeopleContentService,
|
||||
private bpmUserService: BpmUserService,
|
||||
@@ -80,40 +51,26 @@ export class UserInfoComponent implements OnInit, OnDestroy {
|
||||
this.getUserInfo();
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.destroy$.next(true);
|
||||
this.destroy$.complete();
|
||||
}
|
||||
|
||||
getUserInfo() {
|
||||
if (this.authService.isOauth()) {
|
||||
this.loadIdentityUserInfo();
|
||||
this.mode = 'SSO';
|
||||
this.mode = UserInfoMode.SSO;
|
||||
|
||||
if (this.authService.isECMProvider() && this.authService.isEcmLoggedIn()) {
|
||||
this.mode = UserInfoMode.CONTENT_SSO;
|
||||
this.loadEcmUserInfo();
|
||||
}
|
||||
|
||||
} else if (this.isAllLoggedIn()) {
|
||||
this.loadEcmUserInfo();
|
||||
this.loadBpmUserInfo();
|
||||
this.mode = 'ALL';
|
||||
this.mode = UserInfoMode.ALL;
|
||||
} else if (this.isEcmLoggedIn()) {
|
||||
this.loadEcmUserInfo();
|
||||
this.mode = 'CONTENT';
|
||||
this.mode = UserInfoMode.CONTENT;
|
||||
} else if (this.isBpmLoggedIn()) {
|
||||
this.loadBpmUserInfo();
|
||||
this.mode = 'PROCESS';
|
||||
}
|
||||
}
|
||||
|
||||
onKeyPress(event: KeyboardEvent) {
|
||||
this.closeUserModal(event);
|
||||
}
|
||||
|
||||
private closeUserModal($event: KeyboardEvent) {
|
||||
if ($event.keyCode === 27) {
|
||||
this.trigger.closeMenu();
|
||||
this.mode = UserInfoMode.PROCESS;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -147,20 +104,5 @@ export class UserInfoComponent implements OnInit, OnDestroy {
|
||||
private isEcmLoggedIn() {
|
||||
return this.authService.isEcmLoggedIn() || (this.authService.isECMProvider() && this.authService.isKerberosEnabled());
|
||||
}
|
||||
|
||||
stopClosing(event: Event) {
|
||||
event.stopPropagation();
|
||||
}
|
||||
|
||||
getEcmAvatar(avatarId: any): string {
|
||||
return this.peopleContentService.getUserProfileImage(avatarId);
|
||||
}
|
||||
|
||||
getBpmUserImage(): string {
|
||||
return this.bpmUserService.getCurrentUserProfileImage();
|
||||
}
|
||||
|
||||
get showOnRight(): boolean {
|
||||
return this.namePosition === 'right';
|
||||
}
|
||||
}
|
@@ -0,0 +1,38 @@
|
||||
---
|
||||
Title: Content User Info component
|
||||
Added: v6.0.0
|
||||
Status: Active
|
||||
Last reviewed: 2023-01-24
|
||||
---
|
||||
|
||||
# [Content User Info component](../../../lib/content-services/src/lib/content-user-info/content-user-info.component.ts "Defined in content-user-info.component.ts")
|
||||
|
||||
Shows user information for `CONTENT` and `CONTENT_SSO` mode.
|
||||
|
||||
## Basic usage
|
||||
|
||||
```html
|
||||
<adf-content-user-info></adf-content-user-info>
|
||||
```
|
||||
|
||||
## Class members
|
||||
|
||||
### Properties
|
||||
|
||||
| Name | Type | Default value | Description |
|
||||
| ---- | ---- | ------------- | ----------- |
|
||||
| isLoggedIn | `boolean` | Is user logged in |
|
||||
| EcmUser | `EcmUserModel` | Ecm user model. |
|
||||
| identityUser | `IdentityUserModel` | Identity user model. |
|
||||
| mode | `UserInfoMode` | `UserInfoMode.CONTENT` | current mode. |
|
||||
| bpmBackgroundImage | `string` | | Custom path for the background banner image for APS users. |
|
||||
| EcmBackgroundImage | `string` | | Custom path for the background banner image for ACS users. |
|
||||
| menuPositionX | [`MenuPositionX`](https://github.com/angular/components/blob/master/src/material/menu/menu-positions.ts) | "after" | Custom choice for opening the menu at the bottom. Can be `before` or `after`. |
|
||||
| menuPositionY | [`MenuPositionY`](https://github.com/angular/components/blob/master/src/material/menu/menu-positions.ts) | "below" | Custom choice for opening the menu at the bottom. Can be `above` or `below`. |
|
||||
| namePosition | `string` | "right" | When the username is shown, this defines its position relative to the user info button. Can be `right` or `left`. |
|
||||
| showName | `boolean` | true | Shows/hides the username next to the user info button. |
|
||||
|
||||
## Details
|
||||
|
||||
The component shows a round icon for the user and will show extra information about
|
||||
the user when clicked.
|
@@ -1,18 +1,18 @@
|
||||
---
|
||||
Title: User Info component
|
||||
Added: v2.0.0
|
||||
Title: Identity User Info component
|
||||
Added: v6.0.0
|
||||
Status: Active
|
||||
Last reviewed: 2018-11-19
|
||||
Last reviewed: 2023-01-24
|
||||
---
|
||||
|
||||
# [User Info component](../../../lib/core/src/lib/userinfo/components/user-info.component.ts "Defined in user-info.component.ts")
|
||||
# [Identity User Info component](../../../lib/core/src/lib/identity-user-info/identity-user-info.component.ts "Defined in identityuser-info.component.ts")
|
||||
|
||||
Shows user information.
|
||||
Shows user information for SSO mode.
|
||||
|
||||
## Basic usage
|
||||
|
||||
```html
|
||||
<adf-userinfo></adf-userinfo>
|
||||
<adf-identity-user-info></adf-identity-user-info>
|
||||
```
|
||||
|
||||
## Class members
|
||||
@@ -21,6 +21,8 @@ Shows user information.
|
||||
|
||||
| Name | Type | Default value | Description |
|
||||
| ---- | ---- | ------------- | ----------- |
|
||||
| isLoggedIn | `boolean` | Is user logged in |
|
||||
| identityUser | `IdentityUserModel` | Identity user model. |
|
||||
| bpmBackgroundImage | `string` | | Custom path for the background banner image for APS users. |
|
||||
| ecmBackgroundImage | `string` | | Custom path for the background banner image for ACS users. |
|
||||
| menuPositionX | [`MenuPositionX`](https://github.com/angular/components/blob/master/src/material/menu/menu-positions.ts) | "after" | Custom choice for opening the menu at the bottom. Can be `before` or `after`. |
|
||||
@@ -32,5 +34,3 @@ Shows user information.
|
||||
|
||||
The component shows a round icon for the user and will show extra information about
|
||||
the user when clicked.
|
||||
If user is logged in with both ACS and APS, the ACS image will be shown.
|
||||
In case of SSO authentication, the information related to the user like firstname, lastname will be fetched using the Keycloak [`Api`](../../../lib/testing/src/lib/core/structure/api.ts)
|
@@ -0,0 +1,38 @@
|
||||
---
|
||||
Title: Process User Info component
|
||||
Added: v6.0.0
|
||||
Status: Active
|
||||
Last reviewed: 2023-01-24
|
||||
---
|
||||
|
||||
# [Process User Info component](../../../lib/process-services/src/lib/process-user-info/process-user-info.component.ts "Defined in process-user-info.component.ts")
|
||||
|
||||
Shows user information for `PROCESS` and `ALL` mode.
|
||||
|
||||
## Basic usage
|
||||
|
||||
```html
|
||||
<adf-process-user-info></adf-process-user-info>
|
||||
```
|
||||
|
||||
## Class members
|
||||
|
||||
### Properties
|
||||
|
||||
| Name | Type | Default value | Description |
|
||||
| ---- | ---- | ------------- | ----------- |
|
||||
| isLoggedIn | `boolean` | Is user logged in |
|
||||
| bpmUser | `BpmUserModel` | Bpm user model. |
|
||||
| ecmUser | `EpmUserModel` | Ecm user model. |
|
||||
| mode | `UserInfoMode` | `UserInfoMode.PROCESS` | current mode. |
|
||||
| bpmBackgroundImage | `string` | | Custom path for the background banner image for APS users. |
|
||||
| BpmBackgroundImage | `string` | | Custom path for the background banner image for ACS users. |
|
||||
| menuPositionX | [`MenuPositionX`](https://github.com/angular/components/blob/master/src/material/menu/menu-positions.ts) | "after" | Custom choice for opening the menu at the bottom. Can be `before` or `after`. |
|
||||
| menuPositionY | [`MenuPositionY`](https://github.com/angular/components/blob/master/src/material/menu/menu-positions.ts) | "below" | Custom choice for opening the menu at the bottom. Can be `above` or `below`. |
|
||||
| namePosition | `string` | "right" | When the username is shown, this defines its position relative to the user info button. Can be `right` or `left`. |
|
||||
| showName | `boolean` | true | Shows/hides the username next to the user info button. |
|
||||
|
||||
## Details
|
||||
|
||||
The component shows a round icon for the user and will show extra information about
|
||||
the user when clicked.
|
@@ -86,6 +86,7 @@ How to fix it:
|
||||
| Class | Before | After |
|
||||
| --- | -- | --- |
|
||||
| `LoginDialogService` | `@alfresco/adf-core`|
|
||||
| `UserInfoComponent` | `@alfresco/adf-core`|
|
||||
|
||||
### DataColumnModule
|
||||
|
||||
@@ -225,6 +226,16 @@ The ```adf-comments``` has now two specialization in :
|
||||
From v.6.0.0 and after [`ViewerComponent`](../../docs/core/components/viewer.component.md) no longer show content from ACS, so instead of taking `nodeId` as `@Input`, it takes `blobFile` and `urlFile`. For more details check the [`PR`](https://github.com/Alfresco/alfresco-ng2-components/pull/7992).
|
||||
If you need to display content from ACS you can use instead the new [`AlfrescoViewerComponent`](../../docs/content-services/components/alfresco-viewer.component.md)
|
||||
|
||||
### UserInfoComponent
|
||||
From v.6.0.0 and after ```UserInfoComponent``` is no longer active.
|
||||
|
||||
In its place there are now 3 presentational components:
|
||||
- [`IdentityUserInfoComponent`](../../docs/core/components/identity-user-info.component.md) present in core
|
||||
- [`ContentUserInfoComponent`](../../docs/content-services/components/content-user-info.component.md) present in content-services
|
||||
- [`ProcessUserInfoComponent`](../../docs/process-services/components/process-user-info.component.md) present in process-services
|
||||
|
||||
To build a similar logic to the one in ```UserInfoComponent``` check implementation on [`demo-shell`](../../demo-shell/src/app/components/app-layout/user-info/user-info.component.ts)
|
||||
|
||||
## Renamed items
|
||||
|
||||
### New Classes or Services
|
||||
|
@@ -0,0 +1,111 @@
|
||||
<div
|
||||
id="userinfo_container"
|
||||
[class.adf-userinfo-name-right]="showOnRight"
|
||||
(keyup)="onKeyPress($event)"
|
||||
class="adf-userinfo-container"
|
||||
*ngIf="canShow"
|
||||
>
|
||||
<span *ngIf="showName" id="adf-userinfo-ecm-name-display" class="adf-userinfo-name">
|
||||
{{ecmUser | fullName}}
|
||||
</span>
|
||||
<button mat-button [matMenuTriggerFor]="menu" class="adf-userinfo-menu_button"
|
||||
data-automation-id="adf-user-profile">
|
||||
<div class="adf-userinfo-button-profile" id="user-profile">
|
||||
<div *ngIf="identityUser; else showEcmUserImage" id="identity-user-image">
|
||||
<div *ngIf="ecmUser?.avatarId; else initialTemplate">
|
||||
<div class="adf-userinfo-profile-container">
|
||||
<img id="logged-user-img" [src]="getEcmAvatar(ecmUser.avatarId)" alt="user-info-profile-button"
|
||||
class="adf-userinfo-profile-image"/>
|
||||
</div>
|
||||
</div>
|
||||
<ng-template #initialTemplate>
|
||||
<div [innerHTML]="identityUser | usernameInitials:'adf-userinfo-pic'"></div>
|
||||
</ng-template>
|
||||
</div>
|
||||
<ng-template #showEcmUserImage>
|
||||
<div id="ecm-user-image">
|
||||
<div *ngIf="ecmUser.avatarId; else initialTemplate" class="adf-userinfo-profile-container">
|
||||
<img id="logged-user-img" [src]="getEcmAvatar(ecmUser.avatarId)" alt="user-info-profile-button"
|
||||
class="adf-userinfo-profile-image"/>
|
||||
</div>
|
||||
<ng-template #initialTemplate>
|
||||
<div [outerHTML]="ecmUser | usernameInitials:'adf-userinfo-pic'"></div>
|
||||
</ng-template>
|
||||
</div>
|
||||
</ng-template>
|
||||
</div>
|
||||
</button>
|
||||
|
||||
<mat-menu #menu="matMenu" id="user-profile-lists" [xPosition]="menuPositionX" [yPosition]="menuPositionY"
|
||||
[overlapTrigger]="false" class="adf-userinfo-menu">
|
||||
<mat-tab-group id="tab-group-env" (click)="stopClosing($event)" selectedIndex="0" role="menuitem"
|
||||
class="adf-userinfo-tab adf-hide-tab">
|
||||
<mat-tab label="{{ 'USER_PROFILE.TAB.CS' | translate }}" role="dialog"
|
||||
*ngIf="mode === userInfoMode.CONTENT">
|
||||
<mat-card class="adf-userinfo-card">
|
||||
<mat-card-header class="adf-userinfo-card-header"
|
||||
[style.background-image]="'url(' + ecmBackgroundImage + ')'">
|
||||
<div *ngIf="ecmUser.avatarId; else initialTemplate"
|
||||
class="adf-userinfo-profile-container adf-hide-small">
|
||||
<img class="adf-userinfo-profile-picture" id="ecm-user-detail-image"
|
||||
alt="ecm-profile-image" [src]="getEcmAvatar(ecmUser.avatarId)"/>
|
||||
</div>
|
||||
<ng-template #initialTemplate>
|
||||
<div
|
||||
[outerHTML]="ecmUser | usernameInitials:'adf-userinfo-profile-initials adf-hide-small'"></div>
|
||||
</ng-template>
|
||||
|
||||
<div class="adf-userinfo-title" id="ecm-username">{{ecmUser | fullName}}</div>
|
||||
</mat-card-header>
|
||||
<mat-card-content>
|
||||
<div class="adf-userinfo-supporting-text">
|
||||
<div class="adf-userinfo-detail">
|
||||
<span id="ecm-full-name"
|
||||
class="adf-userinfo__detail-title">{{ecmUser | fullName}}</span>
|
||||
<span class="adf-userinfo__detail-profile" id="ecm-email"> {{ecmUser.email}} </span>
|
||||
<a class="adf-userinfo__detail-profile" href="#/profile">
|
||||
{{ 'USER_PROFILE.LABELS.MY_PROFILE' | translate }}</a>
|
||||
</div>
|
||||
<div class="adf-userinfo-detail">
|
||||
<span class="adf-userinfo__secondary-info" id="ecm-job-title-label">
|
||||
{{ 'USER_PROFILE.LABELS.ECM.JOB_TITLE' | translate }}
|
||||
<span id="ecm-job-title"
|
||||
class="adf-userinfo__detail-profile"> {{ ecmUser.jobTitle ? ecmUser.jobTitle : 'N/A' }} </span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
</mat-tab>
|
||||
<mat-tab id="identity-panel" role="dialog" *ngIf="mode === userInfoMode.CONTENT_SSO">
|
||||
<mat-card class="adf-userinfo-card">
|
||||
<mat-card-header class="adf-userinfo-card-header"
|
||||
[style.background-image]="'url(' + bpmBackgroundImage + ')'">
|
||||
<div *ngIf="ecmUser.avatarId; else initialTemplate"
|
||||
class="adf-userinfo-profile-container adf-hide-small">
|
||||
<img class="adf-userinfo-profile-picture" id="ecm-user-detail-image"
|
||||
alt="ecm-profile-image" [src]="getEcmAvatar(ecmUser.avatarId)"/>
|
||||
</div>
|
||||
<ng-template #initialTemplate>
|
||||
<div
|
||||
[outerHTML]="identityUser | usernameInitials:'adf-userinfo-profile-initials adf-hide-small'"></div>
|
||||
</ng-template>
|
||||
<div class="adf-userinfo-title" id="identity-username">{{identityUser | fullName}}</div>
|
||||
</mat-card-header>
|
||||
<mat-card-content>
|
||||
<div class="adf-userinfo-supporting-text">
|
||||
<div class="adf-userinfo-detail">
|
||||
<span id="identity-full-name"
|
||||
class="adf-userinfo__detail-title">{{identityUser | fullName}}</span>
|
||||
<span class="adf-userinfo__detail-profile"
|
||||
id="identity-email"> {{identityUser.email}} </span>
|
||||
<a class="adf-userinfo__detail-profile" href="#/profile">
|
||||
{{ 'USER_PROFILE.LABELS.MY_PROFILE' | translate }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
</mat-tab>
|
||||
</mat-tab-group>
|
||||
</mat-menu>
|
||||
</div>
|
@@ -0,0 +1,272 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2019 Alfresco Software, Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {
|
||||
CoreTestingModule,
|
||||
fakeEcmEditedUser,
|
||||
fakeEcmUser,
|
||||
fakeEcmUserNoImage,
|
||||
IdentityUserModel,
|
||||
InitialUsernamePipe,
|
||||
setupTestBed,
|
||||
UserInfoMode
|
||||
} from '@alfresco/adf-core';
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { MatMenuModule } from '@angular/material/menu';
|
||||
import { By, DomSanitizer } from '@angular/platform-browser';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { ContentTestingModule } from '../testing/content.testing.module';
|
||||
|
||||
import { ContentUserInfoComponent } from './content-user-info.component';
|
||||
|
||||
class FakeSanitizer extends DomSanitizer {
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
sanitize(html) {
|
||||
return html;
|
||||
}
|
||||
|
||||
bypassSecurityTrustHtml(value: string): any {
|
||||
return value;
|
||||
}
|
||||
|
||||
bypassSecurityTrustStyle(): any {
|
||||
return null;
|
||||
}
|
||||
|
||||
bypassSecurityTrustScript(): any {
|
||||
return null;
|
||||
}
|
||||
|
||||
bypassSecurityTrustUrl(): any {
|
||||
return null;
|
||||
}
|
||||
|
||||
bypassSecurityTrustResourceUrl(): any {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
describe('ContentUserInfoComponent', () => {
|
||||
const profilePictureUrl = 'alfresco-logo.svg';
|
||||
|
||||
let component: ContentUserInfoComponent;
|
||||
let fixture: ComponentFixture<ContentUserInfoComponent>;
|
||||
let element: HTMLElement;
|
||||
|
||||
const identityUserMock = { firstName: 'fake-identity-first-name', lastName: 'fake-identity-last-name', email: 'fakeIdentity@email.com' };
|
||||
const identityUserWithOutLastNameMock = { firstName: 'fake-identity-first-name', lastName: null, email: 'fakeIdentity@email.com' };
|
||||
|
||||
const openUserInfo = () => {
|
||||
fixture.detectChanges();
|
||||
const imageButton = element.querySelector<HTMLButtonElement>('#logged-user-img');
|
||||
imageButton.click();
|
||||
fixture.detectChanges();
|
||||
};
|
||||
|
||||
const whenFixtureReady = async () => {
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
fixture.detectChanges();
|
||||
};
|
||||
|
||||
setupTestBed({
|
||||
imports: [
|
||||
TranslateModule.forRoot(),
|
||||
CoreTestingModule,
|
||||
ContentTestingModule,
|
||||
MatMenuModule
|
||||
]
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(ContentUserInfoComponent);
|
||||
component = fixture.componentInstance;
|
||||
element = fixture.nativeElement;
|
||||
|
||||
spyOn(window, 'requestAnimationFrame').and.returnValue(1);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
fixture.destroy();
|
||||
});
|
||||
|
||||
it('should not show any image if the user is not logged in', () => {
|
||||
expect(element.querySelector('#userinfo_container')).toBeDefined();
|
||||
expect(element.querySelector('#logged-user-img')).toBeNull();
|
||||
});
|
||||
|
||||
it('should NOT have users immediately after ngOnInit', () => {
|
||||
expect(element.querySelector('#userinfo_container')).toBeDefined();
|
||||
expect(element.querySelector('#ecm_username')).toBeNull();
|
||||
expect(element.querySelector('#user-profile-lists')).toBeNull();
|
||||
});
|
||||
|
||||
describe('when user is logged on ecm', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
component.ecmUser = fakeEcmUser as any;
|
||||
component.isLoggedIn = true;
|
||||
});
|
||||
|
||||
describe('ui', () => {
|
||||
|
||||
it('should show ecm only last name when user first name is null ', async () => {
|
||||
component.ecmUser = fakeEcmEditedUser as any;
|
||||
await whenFixtureReady();
|
||||
|
||||
openUserInfo();
|
||||
expect(element.querySelector('#userinfo_container')).toBeDefined();
|
||||
const ecmUsername = fixture.debugElement.query(By.css('#ecm-username'));
|
||||
expect(ecmUsername).toBeDefined();
|
||||
expect(ecmUsername).not.toBeNull();
|
||||
expect(ecmUsername.nativeElement.textContent).not.toContain('fake-ecm-first-name');
|
||||
expect(ecmUsername.nativeElement.textContent).not.toContain('null');
|
||||
});
|
||||
|
||||
it('should show the username when showName attribute is true', async () => {
|
||||
await whenFixtureReady();
|
||||
expect(component.showName).toBeTruthy();
|
||||
expect(element.querySelector('#adf-userinfo-ecm-name-display')).not.toBeNull();
|
||||
});
|
||||
|
||||
it('should hide the username when showName attribute is false', async () => {
|
||||
component.showName = false;
|
||||
await whenFixtureReady();
|
||||
expect(element.querySelector('#adf-userinfo-ecm-name-display')).toBeNull();
|
||||
});
|
||||
|
||||
it('should have the defined class to show the name on the right side', async () => {
|
||||
await whenFixtureReady();
|
||||
expect(element.querySelector('#userinfo_container').classList).toContain('adf-userinfo-name-right');
|
||||
});
|
||||
|
||||
it('should not have the defined class to show the name on the left side', async () => {
|
||||
component.namePosition = 'left';
|
||||
await whenFixtureReady();
|
||||
expect(element.querySelector('#userinfo_container').classList).not.toContain('adf-userinfo-name-right');
|
||||
});
|
||||
|
||||
describe('and has image', () => {
|
||||
|
||||
beforeEach(async () => {
|
||||
component.ecmUser = fakeEcmUser as any;
|
||||
component.isLoggedIn = true;
|
||||
spyOn(component, 'getEcmAvatar').and.returnValue(profilePictureUrl);
|
||||
await whenFixtureReady();
|
||||
});
|
||||
|
||||
it('should get the ecm current user image', async () => {
|
||||
openUserInfo();
|
||||
const loggedImage = fixture.debugElement.query(By.css('#logged-user-img'));
|
||||
|
||||
expect(element.querySelector('#userinfo_container')).not.toBeNull();
|
||||
expect(loggedImage).not.toBeNull();
|
||||
expect(loggedImage.properties.src).toContain(profilePictureUrl);
|
||||
});
|
||||
|
||||
it('should display the current user image if user has avatarId', () => {
|
||||
openUserInfo();
|
||||
const loggedImage = fixture.debugElement.query(By.css('#logged-user-img'));
|
||||
expect(component.ecmUser).toBeDefined();
|
||||
expect(component.ecmUser.avatarId).toBe('fake-avatar-id');
|
||||
expect(element.querySelector('#userinfo_container')).not.toBeNull();
|
||||
expect(loggedImage).not.toBeNull();
|
||||
expect(loggedImage.properties.src).toContain(profilePictureUrl);
|
||||
});
|
||||
|
||||
it('should get the ecm user information', async () => {
|
||||
openUserInfo();
|
||||
const ecmImage = fixture.debugElement.query(By.css('#ecm-user-detail-image'));
|
||||
const ecmFullName = fixture.debugElement.query(By.css('#ecm-full-name'));
|
||||
const ecmJobTitle = fixture.debugElement.query(By.css('#ecm-job-title-label'));
|
||||
|
||||
expect(element.querySelector('#userinfo_container')).not.toBeNull();
|
||||
expect(fixture.debugElement.query(By.css('#ecm-username'))).not.toBeNull();
|
||||
expect(ecmImage).not.toBeNull();
|
||||
expect(ecmImage.properties.src).toContain(profilePictureUrl);
|
||||
expect(ecmFullName.nativeElement.textContent).toContain('fake-ecm-first-name fake-ecm-last-name');
|
||||
expect(ecmJobTitle.nativeElement.textContent).toContain('USER_PROFILE.LABELS.ECM.JOB_TITLE');
|
||||
});
|
||||
});
|
||||
|
||||
describe('and has no image', () => {
|
||||
|
||||
beforeEach( async () => {
|
||||
component.ecmUser = fakeEcmUserNoImage as any;
|
||||
component.isLoggedIn = true;
|
||||
await whenFixtureReady();
|
||||
});
|
||||
|
||||
it('should show N/A when the job title is null', () => {
|
||||
const imageButton = element.querySelector<HTMLButtonElement>('[data-automation-id="user-initials-image"]');
|
||||
imageButton.click();
|
||||
fixture.detectChanges();
|
||||
expect(element.querySelector('#userinfo_container')).not.toBeNull();
|
||||
const ecmJobTitle = fixture.debugElement.query(By.css('#ecm-job-title'));
|
||||
expect(ecmJobTitle).not.toBeNull();
|
||||
expect(ecmJobTitle).not.toBeNull();
|
||||
expect(ecmJobTitle.nativeElement.textContent).toContain('N/A');
|
||||
});
|
||||
|
||||
it('should not show the tabs', () => {
|
||||
const imageButton = element.querySelector<HTMLButtonElement>('[data-automation-id="user-initials-image"]');
|
||||
imageButton.click();
|
||||
fixture.detectChanges();
|
||||
const tabHeader = fixture.debugElement.query(By.css('#tab-group-env'));
|
||||
expect(tabHeader.classes['adf-hide-tab']).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should display the current user Initials if the user dose not have avatarId', () => {
|
||||
fixture.detectChanges();
|
||||
const pipe = new InitialUsernamePipe(new FakeSanitizer());
|
||||
const expected = pipe.transform({
|
||||
id: 13,
|
||||
firstName: 'Wilbur',
|
||||
lastName: 'Adams',
|
||||
email: 'wilbur@app.com'
|
||||
});
|
||||
expect(expected).toBe('<div data-automation-id="user-initials-image" class="">WA</div>');
|
||||
expect(component.ecmUser).toBeDefined();
|
||||
expect(component.ecmUser.avatarId).toBeNull();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when identity user is logged in', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
component.ecmUser = fakeEcmUser as any;
|
||||
component.identityUser = identityUserMock as unknown as IdentityUserModel;
|
||||
component.isLoggedIn = true;
|
||||
component.mode = UserInfoMode.CONTENT_SSO;
|
||||
});
|
||||
|
||||
it('should not show initials if the user have avatar and provider is ECM', async () => {
|
||||
component.identityUser = identityUserWithOutLastNameMock as unknown as IdentityUserModel;
|
||||
await whenFixtureReady();
|
||||
|
||||
expect(element.querySelector('.adf-userinfo-pic')).toBeNull();
|
||||
expect(element.querySelector('.adf-userinfo-profile-image')).toBeDefined();
|
||||
expect(element.querySelector('.adf-userinfo-profile-image')).not.toBeNull();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
@@ -0,0 +1,108 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2019 Alfresco Software, Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { EcmUserModel, IdentityUserModel, PeopleContentService, UserInfoMode } from '@alfresco/adf-core';
|
||||
import { Component, Input, OnDestroy, ViewChild, ViewEncapsulation } from '@angular/core';
|
||||
import { MatMenuTrigger, MenuPositionX, MenuPositionY } from '@angular/material/menu';
|
||||
import { Subject } from 'rxjs';
|
||||
|
||||
@Component({
|
||||
selector: 'adf-content-user-info',
|
||||
templateUrl: './content-user-info.component.html',
|
||||
styleUrls: ['./content-user-info.component.scss'],
|
||||
encapsulation: ViewEncapsulation.None
|
||||
})
|
||||
export class ContentUserInfoComponent implements OnDestroy {
|
||||
|
||||
@ViewChild(MatMenuTrigger) trigger: MatMenuTrigger;
|
||||
|
||||
@Input()
|
||||
isLoggedIn: boolean;
|
||||
|
||||
@Input()
|
||||
ecmUser: EcmUserModel;
|
||||
|
||||
@Input()
|
||||
identityUser: IdentityUserModel;
|
||||
|
||||
@Input()
|
||||
mode: UserInfoMode = UserInfoMode.CONTENT;
|
||||
|
||||
/** Custom path for the background banner image for ACS users. */
|
||||
@Input()
|
||||
ecmBackgroundImage: string = './assets/images/ecm-background.png';
|
||||
|
||||
/** Custom path for the background banner image for APS users. */
|
||||
@Input()
|
||||
bpmBackgroundImage: string = './assets/images/bpm-background.png';
|
||||
|
||||
/** Custom choice for opening the menu at the bottom. Can be `before` or `after`. */
|
||||
@Input()
|
||||
menuPositionX: MenuPositionX = 'after';
|
||||
|
||||
/** Custom choice for opening the menu at the bottom. Can be `above` or `below`. */
|
||||
@Input()
|
||||
menuPositionY: MenuPositionY = 'below';
|
||||
|
||||
/** Shows/hides the username next to the user info button. */
|
||||
@Input()
|
||||
showName: boolean = true;
|
||||
|
||||
/** When the username is shown, this defines its position relative to the user info button.
|
||||
* Can be `right` or `left`.
|
||||
*/
|
||||
@Input()
|
||||
namePosition: string = 'right';
|
||||
|
||||
userInfoMode = UserInfoMode;
|
||||
|
||||
private destroy$ = new Subject();
|
||||
|
||||
constructor(private peopleContentService: PeopleContentService) {
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.destroy$.next(true);
|
||||
this.destroy$.complete();
|
||||
}
|
||||
|
||||
onKeyPress(event: KeyboardEvent) {
|
||||
this.closeUserModal(event);
|
||||
}
|
||||
|
||||
private closeUserModal($event: KeyboardEvent) {
|
||||
if ($event.keyCode === 27) {
|
||||
this.trigger.closeMenu();
|
||||
}
|
||||
}
|
||||
|
||||
stopClosing(event: Event) {
|
||||
event.stopPropagation();
|
||||
}
|
||||
|
||||
getEcmAvatar(avatarId: string): string {
|
||||
return this.peopleContentService.getUserProfileImage(avatarId);
|
||||
}
|
||||
|
||||
get showOnRight(): boolean {
|
||||
return this.namePosition === 'right';
|
||||
}
|
||||
|
||||
get canShow(): boolean {
|
||||
return this.isLoggedIn && !!this.ecmUser && !!this.mode;
|
||||
}
|
||||
}
|
@@ -0,0 +1,41 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2019 Alfresco Software, Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { ContentUserInfoComponent } from './content-user-info.component';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { PipeModule } from '@alfresco/adf-core';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatMenuModule } from '@angular/material/menu';
|
||||
import { MatTabsModule } from '@angular/material/tabs';
|
||||
import { MatCardModule } from '@angular/material/card';
|
||||
|
||||
@NgModule({
|
||||
declarations: [ContentUserInfoComponent],
|
||||
imports: [
|
||||
CommonModule,
|
||||
MatButtonModule,
|
||||
MatMenuModule,
|
||||
MatTabsModule,
|
||||
MatCardModule,
|
||||
TranslateModule,
|
||||
PipeModule
|
||||
],
|
||||
exports: [ContentUserInfoComponent]
|
||||
})
|
||||
export class ContentUserInfoModule {}
|
20
lib/content-services/src/lib/content-user-info/public-api.ts
Normal file
20
lib/content-services/src/lib/content-user-info/public-api.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2019 Alfresco Software, Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export * from './content-user-info.component';
|
||||
|
||||
export * from './content-user-info.module';
|
@@ -48,6 +48,7 @@ import { ContentPipeModule } from './pipes/content-pipe.module';
|
||||
import { NodeCommentsModule } from './node-comments/node-comments.module';
|
||||
import { TreeModule } from './tree/tree.module';
|
||||
import { AlfrescoViewerModule } from './viewer/alfresco-viewer.module';
|
||||
import { ContentUserInfoModule } from './content-user-info/content-user-info.module';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
@@ -62,6 +63,7 @@ import { AlfrescoViewerModule } from './viewer/alfresco-viewer.module';
|
||||
DialogModule,
|
||||
SearchModule,
|
||||
DocumentListModule,
|
||||
ContentUserInfoModule,
|
||||
UploadModule,
|
||||
MaterialModule,
|
||||
SitesDropdownModule,
|
||||
@@ -98,6 +100,7 @@ import { AlfrescoViewerModule } from './viewer/alfresco-viewer.module';
|
||||
TagModule,
|
||||
WebScriptModule,
|
||||
DocumentListModule,
|
||||
ContentUserInfoModule,
|
||||
UploadModule,
|
||||
SearchModule,
|
||||
SitesDropdownModule,
|
||||
|
@@ -20,6 +20,7 @@ export * from './lib/social/index';
|
||||
export * from './lib/tag/index';
|
||||
export * from './lib/webscript/index';
|
||||
export * from './lib/document-list/index';
|
||||
export * from './lib/content-user-info/index';
|
||||
export * from './lib/upload/index';
|
||||
export * from './lib/search/index';
|
||||
export * from './lib/site-dropdown/index';
|
||||
|
@@ -27,6 +27,7 @@ export * from './services/thumbnail.service';
|
||||
export * from './services/sort-by-category.service';
|
||||
|
||||
export * from './models/log-levels.model';
|
||||
export * from './models/user-info-mode.enum';
|
||||
|
||||
export * from './interface/search-component.interface';
|
||||
|
||||
|
24
lib/core/src/lib/common/models/user-info-mode.enum.ts
Normal file
24
lib/core/src/lib/common/models/user-info-mode.enum.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2019 Alfresco Software, Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export enum UserInfoMode {
|
||||
ALL = 'ALL',
|
||||
CONTENT = 'CONTENT',
|
||||
PROCESS = 'PROCESS',
|
||||
SSO = 'SSO',
|
||||
CONTENT_SSO = 'CONTENT_SSO'
|
||||
}
|
@@ -31,7 +31,6 @@ import { LanguageMenuModule } from './language-menu/language-menu.module';
|
||||
import { LoginModule } from './login/login.module';
|
||||
import { PaginationModule } from './pagination/pagination.module';
|
||||
import { ToolbarModule } from './toolbar/toolbar.module';
|
||||
import { UserInfoModule } from './userinfo/userinfo.module';
|
||||
import { ViewerModule } from './viewer/viewer.module';
|
||||
import { FormBaseModule } from './form/form-base.module';
|
||||
import { SidenavLayoutModule } from './layout/layout.module';
|
||||
@@ -64,6 +63,7 @@ import { RichTextEditorModule } from './rich-text-editor/rich-text-editor.module
|
||||
import { HttpClientModule, HttpClientXsrfModule, HTTP_INTERCEPTORS } from '@angular/common/http';
|
||||
import { AuthenticationService } from './auth/services/authentication.service';
|
||||
import { MAT_SNACK_BAR_DEFAULT_OPTIONS } from '@angular/material/snack-bar';
|
||||
import { IdentityUserInfoModule } from './identity-user-info/identity-user-info.module';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
@@ -74,11 +74,11 @@ import { MAT_SNACK_BAR_DEFAULT_OPTIONS } from '@angular/material/snack-bar';
|
||||
SidenavLayoutModule,
|
||||
PipeModule,
|
||||
CommonModule,
|
||||
IdentityUserInfoModule,
|
||||
DirectiveModule,
|
||||
DownloadZipDialogModule,
|
||||
FormsModule,
|
||||
ReactiveFormsModule,
|
||||
UserInfoModule,
|
||||
MaterialModule,
|
||||
AppConfigModule,
|
||||
PaginationModule,
|
||||
@@ -118,8 +118,8 @@ import { MAT_SNACK_BAR_DEFAULT_OPTIONS } from '@angular/material/snack-bar';
|
||||
DownloadZipDialogModule,
|
||||
ClipboardModule,
|
||||
FormsModule,
|
||||
IdentityUserInfoModule,
|
||||
ReactiveFormsModule,
|
||||
UserInfoModule,
|
||||
MaterialModule,
|
||||
AppConfigModule,
|
||||
PaginationModule,
|
||||
|
@@ -0,0 +1,45 @@
|
||||
<div
|
||||
id="userinfo_container"
|
||||
[class.adf-userinfo-name-right]="showOnRight"
|
||||
(keyup)="onKeyPress($event)"
|
||||
class="adf-userinfo-container"
|
||||
*ngIf="isLoggedIn"
|
||||
>
|
||||
<span *ngIf="showName" id="adf-userinfo-identity-name-display" class="adf-userinfo-name">
|
||||
{{identityUser | fullName}}
|
||||
</span>
|
||||
<button mat-button [matMenuTriggerFor]="menu" class="adf-userinfo-menu_button"
|
||||
data-automation-id="adf-user-profile">
|
||||
<div class="adf-userinfo-button-profile" id="user-profile">
|
||||
<div id="identity-user-image">
|
||||
<div [innerHTML]="identityUser | usernameInitials:'adf-userinfo-pic'"></div>
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
<mat-menu #menu="matMenu" id="user-profile-lists" [xPosition]="menuPositionX" [yPosition]="menuPositionY"
|
||||
[overlapTrigger]="false" class="adf-userinfo-menu">
|
||||
<mat-tab-group id="tab-group-env" (click)="stopClosing($event)" selectedIndex="0" role="menuitem"
|
||||
class="adf-userinfo-tab adf-hide-tab">
|
||||
<mat-tab id="identity-panel" role="dialog">
|
||||
<mat-card class="adf-userinfo-card">
|
||||
<mat-card-header class="adf-userinfo-card-header"
|
||||
[style.background-image]="'url(' + bpmBackgroundImage + ')'">
|
||||
<div class="adf-userinfo-title" id="identity-username">{{identityUser | fullName}}</div>
|
||||
</mat-card-header>
|
||||
<mat-card-content>
|
||||
<div class="adf-userinfo-supporting-text">
|
||||
<div class="adf-userinfo-detail">
|
||||
<span id="identity-full-name"
|
||||
class="adf-userinfo__detail-title">{{identityUser | fullName}}</span>
|
||||
<span class="adf-userinfo__detail-profile"
|
||||
id="identity-email"> {{identityUser.email}} </span>
|
||||
<a class="adf-userinfo__detail-profile" href="#/profile">
|
||||
{{ 'USER_PROFILE.LABELS.MY_PROFILE' | translate }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
</mat-tab>
|
||||
</mat-tab-group>
|
||||
</mat-menu>
|
||||
</div>
|
@@ -0,0 +1,188 @@
|
||||
.adf {
|
||||
&-userinfo-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 5px;
|
||||
}
|
||||
|
||||
&-userinfo-name-right {
|
||||
flex-direction: row-reverse;
|
||||
}
|
||||
|
||||
&-userinfo-name {
|
||||
padding: 0 5px;
|
||||
|
||||
@media screen and (max-width: 959px) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
&-userinfo-pic {
|
||||
background: var(--adf-user-info-color);
|
||||
display: inline-block;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 100px;
|
||||
text-align: center;
|
||||
font-weight: bolder;
|
||||
font-size: var(--theme-adf-picture-1-font-size);
|
||||
text-transform: uppercase;
|
||||
vertical-align: middle;
|
||||
line-height: 40px;
|
||||
}
|
||||
|
||||
&-userinfo-profile-image {
|
||||
background: var(--adf-user-info-color);
|
||||
text-align: center;
|
||||
border-radius: 90%;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
margin-right: 0;
|
||||
cursor: pointer;
|
||||
vertical-align: middle;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
&-userinfo-profile-container {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
&-userinfo-menu_button.mat-button {
|
||||
margin-right: 0;
|
||||
border-radius: 90%;
|
||||
padding: 0;
|
||||
min-width: 40px;
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
&-userinfo-tab .mat-tab-header {
|
||||
align-self: center;
|
||||
width: 100%;
|
||||
min-width: 250px;
|
||||
}
|
||||
|
||||
&-userinfo-tab .mat-tab-label {
|
||||
flex: auto;
|
||||
font-weight: 500;
|
||||
font-size: var(--theme-body-1-font-size);
|
||||
text-transform: uppercase;
|
||||
line-height: 48px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
&-userinfo-card-header {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
justify-content: stretch;
|
||||
line-height: normal;
|
||||
height: 100px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
&-userinfo-card.mat-card {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
&-userinfo-supporting-text {
|
||||
font-size: var(--theme-body-1-font-size);
|
||||
font-weight: 400;
|
||||
letter-spacing: 0;
|
||||
line-height: 18px;
|
||||
overflow: hidden;
|
||||
padding: 32px;
|
||||
column-count: 2;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
@media screen and (max-width: 599px) {
|
||||
padding: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
&-userinfo-title {
|
||||
font-size: var(--theme-title-font-size);
|
||||
}
|
||||
|
||||
&-userinfo__detail-profile {
|
||||
align-items: flex-start;
|
||||
font-size: var(--theme-body-1-font-size);
|
||||
font-weight: 400;
|
||||
letter-spacing: 0;
|
||||
line-height: 18px;
|
||||
display: block;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
&-userinfo__detail-title {
|
||||
text-overflow: ellipsis;
|
||||
font-size: var(--theme-subheading-2-font-size);
|
||||
font-weight: 400;
|
||||
letter-spacing: 0.04em;
|
||||
line-height: 20px;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
&-userinfo__secondary-info {
|
||||
font-size: var(--theme-body-1-font-size);
|
||||
font-weight: 400;
|
||||
letter-spacing: 0;
|
||||
line-height: 18px;
|
||||
align-items: flex-end;
|
||||
}
|
||||
|
||||
&-userinfo-profile-picture {
|
||||
background: var(--adf-user-info-color);
|
||||
background-size: cover;
|
||||
border-radius: 50%;
|
||||
height: 80px;
|
||||
width: 80px;
|
||||
margin-left: 0;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
&-userinfo-profile-initials {
|
||||
text-transform: uppercase;
|
||||
background-size: cover;
|
||||
background-color: var(--adf-user-info-color);
|
||||
border-radius: 50%;
|
||||
height: 80px;
|
||||
width: 80px;
|
||||
margin-left: 0;
|
||||
margin-right: 8px;
|
||||
font-size: 35px;
|
||||
font-weight: 400;
|
||||
letter-spacing: 0;
|
||||
line-height: 78px;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
}
|
||||
|
||||
&-userinfo-button-profile {
|
||||
display: inline-block;
|
||||
border: 0;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
&-userinfo-detail {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
&-hide-tab .mat-tab-label-active {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (min-device-width: 480px) {
|
||||
.mat-menu-panel.adf-userinfo-menu {
|
||||
max-height: 450px;
|
||||
min-width: 450px;
|
||||
overflow: auto;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.mat-menu-panel.adf-userinfo-menu .mat-menu-content {
|
||||
padding: 0;
|
||||
}
|
@@ -0,0 +1,139 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2019 Alfresco Software, Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { IdentityUserInfoComponent } from './identity-user-info.component';
|
||||
import { setupTestBed } from '../testing/setup-test-bed';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { CoreTestingModule } from '../testing/core.testing.module';
|
||||
import { MatMenuModule } from '@angular/material/menu';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { IdentityUserModel } from '../auth/models/identity-user.model';
|
||||
|
||||
describe('IdentityUserInfoComponent', () => {
|
||||
let component: IdentityUserInfoComponent;
|
||||
let fixture: ComponentFixture<IdentityUserInfoComponent>;
|
||||
let element: HTMLElement;
|
||||
|
||||
const identityUserMock = { firstName: 'fake-identity-first-name', lastName: 'fake-identity-last-name', email: 'fakeIdentity@email.com' } as unknown as IdentityUserModel;
|
||||
const identityUserWithOutFirstNameMock = { firstName: null, lastName: 'fake-identity-last-name', email: 'fakeIdentity@email.com' } as unknown as IdentityUserModel;
|
||||
const identityUserWithOutLastNameMock = { firstName: 'fake-identity-first-name', lastName: null, email: 'fakeIdentity@email.com' } as unknown as IdentityUserModel;
|
||||
|
||||
const whenFixtureReady = async () => {
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
fixture.detectChanges();
|
||||
};
|
||||
|
||||
setupTestBed({
|
||||
imports: [
|
||||
TranslateModule.forRoot(),
|
||||
CoreTestingModule,
|
||||
MatMenuModule
|
||||
]
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(IdentityUserInfoComponent);
|
||||
component = fixture.componentInstance;
|
||||
element = fixture.nativeElement;
|
||||
|
||||
spyOn(window, 'requestAnimationFrame').and.returnValue(1);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
fixture.destroy();
|
||||
});
|
||||
|
||||
it('should not show any image if the user is not logged in', () => {
|
||||
expect(element.querySelector('#userinfo_container')).toBeDefined();
|
||||
expect(element.querySelector('#logged-user-img')).toBeNull();
|
||||
});
|
||||
|
||||
it('should NOT have users immediately after ngOnInit', () => {
|
||||
expect(element.querySelector('#userinfo_container')).toBeDefined();
|
||||
expect(element.querySelector('#ecm_username')).toBeNull();
|
||||
expect(element.querySelector('#bpm_username')).toBeNull();
|
||||
expect(element.querySelector('#user-profile-lists')).toBeNull();
|
||||
});
|
||||
|
||||
describe('when identity user is logged in', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
component.identityUser = identityUserMock;
|
||||
component.isLoggedIn = true;
|
||||
});
|
||||
|
||||
it('should show the identity user initials', async () => {
|
||||
await whenFixtureReady();
|
||||
expect(element.querySelector('#userinfo_container')).toBeDefined();
|
||||
expect(element.querySelector('[data-automation-id="user-initials-image"]')?.textContent).toContain('ff');
|
||||
});
|
||||
|
||||
it('should show full name next to the user image', async () => {
|
||||
await whenFixtureReady();
|
||||
|
||||
const imageButton = element.querySelector<HTMLButtonElement>('#identity-user-image');
|
||||
imageButton?.click();
|
||||
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(element.querySelector('#userinfo_container')).not.toBeNull();
|
||||
|
||||
const identityUserName = fixture.debugElement.query(By.css('#identity-username'));
|
||||
expect(identityUserName).toBeDefined();
|
||||
expect(identityUserName).not.toBeNull();
|
||||
expect(identityUserName.nativeElement.textContent).toContain('fake-identity-first-name fake-identity-last-name');
|
||||
});
|
||||
|
||||
it('should show last name if first name is null', async () => {
|
||||
component.identityUser = identityUserWithOutFirstNameMock;
|
||||
await whenFixtureReady();
|
||||
|
||||
const fullNameElement = element.querySelector('#adf-userinfo-identity-name-display');
|
||||
expect(element.querySelector('#userinfo_container')).toBeDefined();
|
||||
expect(element.querySelector('#adf-userinfo-identity-name-display')).not.toBeNull();
|
||||
expect(fullNameElement?.textContent).toContain('fake-identity-last-name');
|
||||
expect(fullNameElement?.textContent).not.toContain('fake-identity-first-name');
|
||||
});
|
||||
|
||||
it('should not show first name if it is null string', async () => {
|
||||
component.identityUser = identityUserWithOutFirstNameMock;
|
||||
await whenFixtureReady();
|
||||
|
||||
const fullNameElement = element.querySelector('#adf-userinfo-identity-name-display');
|
||||
fixture.detectChanges();
|
||||
expect(element.querySelector('#userinfo_container')).toBeDefined();
|
||||
expect(fullNameElement).toBeDefined();
|
||||
expect(fullNameElement?.textContent).toContain('fake-identity-last-name');
|
||||
expect(fullNameElement?.textContent).not.toContain('null');
|
||||
});
|
||||
|
||||
it('should not show last name if it is null string', async () => {
|
||||
component.identityUser = identityUserWithOutLastNameMock;
|
||||
await whenFixtureReady();
|
||||
|
||||
const fullNameElement = element.querySelector('#adf-userinfo-identity-name-display');
|
||||
fixture.detectChanges();
|
||||
expect(element.querySelector('#userinfo_container')).toBeDefined();
|
||||
expect(fullNameElement).toBeDefined();
|
||||
expect(fullNameElement?.textContent).toContain('fake-identity-first-name');
|
||||
expect(fullNameElement?.textContent).not.toContain('null');
|
||||
});
|
||||
});
|
||||
});
|
@@ -0,0 +1,128 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2019 Alfresco Software, Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {
|
||||
Meta,
|
||||
moduleMetadata,
|
||||
Story
|
||||
} from '@storybook/angular';
|
||||
import { CoreStoryModule } from '../testing/core.story.module';
|
||||
import { IdentityUserInfoComponent } from './identity-user-info.component';
|
||||
import { IdentityUserInfoModule } from './identity-user-info.module';
|
||||
|
||||
const fakeIdentityUser = {
|
||||
familyName: 'Identity',
|
||||
givenName: 'John',
|
||||
email: 'john.identity@gmail.com',
|
||||
username: 'johnyIdentity99'
|
||||
};
|
||||
|
||||
export default {
|
||||
component: IdentityUserInfoComponent,
|
||||
title: 'Core/Identity User Info/Identity User Info',
|
||||
decorators: [
|
||||
moduleMetadata({
|
||||
imports: [CoreStoryModule, IdentityUserInfoModule]
|
||||
})
|
||||
],
|
||||
argTypes: {
|
||||
isLoggedIn: {
|
||||
description:
|
||||
'Determines if user is logged in',
|
||||
control: 'boolean',
|
||||
defaultValue: true,
|
||||
table: {
|
||||
type: { summary: 'boolean' },
|
||||
defaultValue: { summary: true }
|
||||
}
|
||||
},
|
||||
identityUser: {
|
||||
description:
|
||||
'Identity User Info',
|
||||
control: 'object',
|
||||
table: {
|
||||
type: { summary: 'IdentityUserModel' }
|
||||
}
|
||||
},
|
||||
menuPositionX: {
|
||||
description:
|
||||
'Material Angular menu horizontal position in regard to User Info',
|
||||
control: 'radio',
|
||||
options: ['before', 'after'],
|
||||
defaultValue: 'after',
|
||||
table: {
|
||||
type: { summary: 'MenuPositionX' },
|
||||
defaultValue: { summary: 'after' }
|
||||
}
|
||||
},
|
||||
menuPositionY: {
|
||||
description:
|
||||
'Material Angular menu vertical position in regard to User Info',
|
||||
control: 'radio',
|
||||
options: ['above', 'below'],
|
||||
defaultValue: 'below',
|
||||
table: {
|
||||
type: { summary: 'MenuPositionY' },
|
||||
defaultValue: { summary: 'below' }
|
||||
}
|
||||
},
|
||||
showName: {
|
||||
description:
|
||||
'Determines if name should be shown next to user avatar',
|
||||
control: 'boolean',
|
||||
defaultValue: true,
|
||||
table: {
|
||||
type: { summary: 'boolean' },
|
||||
defaultValue: { summary: true }
|
||||
}
|
||||
},
|
||||
namePosition: {
|
||||
description: 'User name position in regard to avatar',
|
||||
control: 'radio',
|
||||
options: ['left', 'right'],
|
||||
defaultValue: 'right',
|
||||
table: {
|
||||
type: { summary: 'string' },
|
||||
defaultValue: { summary: 'right' }
|
||||
}
|
||||
},
|
||||
bpmBackgroundImage: {
|
||||
description: 'Menu background banner image for APS users',
|
||||
control: {
|
||||
disable: true
|
||||
},
|
||||
table: {
|
||||
type: {
|
||||
summary: 'string'
|
||||
},
|
||||
defaultValue: {
|
||||
summary: './assets/images/bpm-background.png'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} as Meta;
|
||||
|
||||
const template: Story<IdentityUserInfoComponent> = (args: IdentityUserInfoComponent) => ({
|
||||
props: args
|
||||
});
|
||||
|
||||
export const loginWithSSO = template.bind({});
|
||||
loginWithSSO.args = {
|
||||
identityUser: fakeIdentityUser
|
||||
};
|
||||
loginWithSSO.parameters = { layout: 'centered' };
|
@@ -0,0 +1,89 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2019 Alfresco Software, Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Component, Input, OnDestroy, ViewChild, ViewEncapsulation } from '@angular/core';
|
||||
import { MatMenuTrigger, MenuPositionX, MenuPositionY } from '@angular/material/menu';
|
||||
import { IdentityUserModel } from '../auth/models/identity-user.model';
|
||||
import { Subject } from 'rxjs';
|
||||
|
||||
@Component({
|
||||
selector: 'adf-identity-user-info',
|
||||
templateUrl: './identity-user-info.component.html',
|
||||
styleUrls: ['./identity-user-info.component.scss'],
|
||||
encapsulation: ViewEncapsulation.None
|
||||
})
|
||||
export class IdentityUserInfoComponent implements OnDestroy {
|
||||
|
||||
@ViewChild(MatMenuTrigger) trigger: MatMenuTrigger;
|
||||
|
||||
@Input()
|
||||
isLoggedIn: boolean;
|
||||
|
||||
@Input()
|
||||
identityUser: IdentityUserModel;
|
||||
|
||||
/** Custom path for the background banner image for APS users. */
|
||||
@Input()
|
||||
bpmBackgroundImage: string = './assets/images/bpm-background.png';
|
||||
|
||||
/** Custom choice for opening the menu at the bottom. Can be `before` or `after`. */
|
||||
@Input()
|
||||
menuPositionX: MenuPositionX = 'after';
|
||||
|
||||
/** Custom choice for opening the menu at the bottom. Can be `above` or `below`. */
|
||||
@Input()
|
||||
menuPositionY: MenuPositionY = 'below';
|
||||
|
||||
/** Shows/hides the username next to the user info button. */
|
||||
@Input()
|
||||
showName: boolean = true;
|
||||
|
||||
/** When the username is shown, this defines its position relative to the user info button.
|
||||
* Can be `right` or `left`.
|
||||
*/
|
||||
@Input()
|
||||
namePosition: string = 'right';
|
||||
|
||||
private destroy$ = new Subject();
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.destroy$.next(true);
|
||||
this.destroy$.complete();
|
||||
}
|
||||
|
||||
onKeyPress(event: KeyboardEvent) {
|
||||
this.closeUserModal(event);
|
||||
}
|
||||
|
||||
private closeUserModal($event: KeyboardEvent) {
|
||||
if ($event.keyCode === 27) {
|
||||
this.trigger.closeMenu();
|
||||
}
|
||||
}
|
||||
|
||||
stopClosing(event: Event) {
|
||||
event.stopPropagation();
|
||||
}
|
||||
|
||||
get showOnRight(): boolean {
|
||||
return this.namePosition === 'right';
|
||||
}
|
||||
|
||||
get canShow(): boolean {
|
||||
return this.isLoggedIn && !!this.identityUser;
|
||||
}
|
||||
}
|
@@ -15,17 +15,18 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { PipeModule } from '../pipes/pipe.module';
|
||||
import { UserInfoComponent } from './components/user-info.component';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { IdentityUserInfoComponent } from './identity-user-info.component';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatCardModule } from '@angular/material/card';
|
||||
import { MatMenuModule } from '@angular/material/menu';
|
||||
import { MatTabsModule } from '@angular/material/tabs';
|
||||
import { MatCardModule } from '@angular/material/card';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { PipeModule } from '../pipes/pipe.module';
|
||||
|
||||
@NgModule({
|
||||
declarations: [IdentityUserInfoComponent],
|
||||
imports: [
|
||||
CommonModule,
|
||||
MatButtonModule,
|
||||
@@ -35,12 +36,6 @@ import { MatCardModule } from '@angular/material/card';
|
||||
TranslateModule,
|
||||
PipeModule
|
||||
],
|
||||
declarations: [
|
||||
UserInfoComponent
|
||||
],
|
||||
exports: [
|
||||
UserInfoComponent
|
||||
]
|
||||
exports: [IdentityUserInfoComponent]
|
||||
})
|
||||
export class UserInfoModule {
|
||||
}
|
||||
export class IdentityUserInfoModule {}
|
@@ -15,5 +15,4 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export * from './components/user-info.component';
|
||||
export * from './userinfo.module';
|
||||
export * from './public-api';
|
20
lib/core/src/lib/identity-user-info/public-api.ts
Normal file
20
lib/core/src/lib/identity-user-info/public-api.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2019 Alfresco Software, Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export * from './identity-user-info.component';
|
||||
|
||||
export * from './identity-user-info.module';
|
@@ -1,76 +0,0 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2019 Alfresco Software, Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Inject, Injectable } from '@angular/core';
|
||||
import { ReplaySubject } from 'rxjs';
|
||||
import { AlfrescoApiService } from './../../../services/alfresco-api.service';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class AuthenticationServiceMock {
|
||||
onLogin: ReplaySubject<any> = new ReplaySubject<any>(1);
|
||||
|
||||
onLogout: ReplaySubject<any> = new ReplaySubject<any>(1);
|
||||
|
||||
constructor(
|
||||
private alfrescoApi: AlfrescoApiService,
|
||||
@Inject('MODE') public loginMode: string
|
||||
) {}
|
||||
|
||||
isLoggedIn(): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
isLoggedInWith(provider: string): boolean {
|
||||
if (provider === 'BPM') {
|
||||
return this.isBpmLoggedIn();
|
||||
} else if (provider === 'ECM') {
|
||||
return this.isEcmLoggedIn();
|
||||
} else {
|
||||
return this.isLoggedIn();
|
||||
}
|
||||
}
|
||||
|
||||
isKerberosEnabled(): boolean {
|
||||
return this.loginMode === 'all';
|
||||
}
|
||||
|
||||
isOauth(): boolean {
|
||||
return this.loginMode === 'default' || this.loginMode === 'defaultEcm';
|
||||
}
|
||||
|
||||
isECMProvider(): boolean {
|
||||
return this.alfrescoApi.getInstance().isEcmConfiguration();
|
||||
}
|
||||
|
||||
isBPMProvider(): boolean {
|
||||
return this.alfrescoApi.getInstance().isBpmConfiguration();
|
||||
}
|
||||
|
||||
isALLProvider(): boolean {
|
||||
return this.loginMode === 'all';
|
||||
}
|
||||
|
||||
isEcmLoggedIn(): boolean {
|
||||
return this.loginMode === 'ecm' || this.loginMode === 'defaultEcm';
|
||||
}
|
||||
|
||||
isBpmLoggedIn(): boolean {
|
||||
return this.loginMode === 'bpm';
|
||||
}
|
||||
}
|
@@ -1,78 +0,0 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2019 Alfresco Software, Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {
|
||||
BpmUserModel,
|
||||
EcmUserModel
|
||||
} from './../../../models';
|
||||
import {
|
||||
IdentityUserModel
|
||||
} from './../../../auth/models/identity-user.model';
|
||||
import { of } from 'rxjs';
|
||||
|
||||
export class PeopleContentServiceMock {
|
||||
private fakeEcmUser = new EcmUserModel({
|
||||
firstName: 'John',
|
||||
lastName: 'Ecm',
|
||||
avatarId: 'fake-avatar-id',
|
||||
email: 'john.ecm@gmail.com',
|
||||
jobTitle: 'Product Manager'
|
||||
});
|
||||
|
||||
getCurrentUserInfo = () => of(this.fakeEcmUser);
|
||||
|
||||
getUserProfileImage = () => './assets/images/alfresco-logo.svg';
|
||||
}
|
||||
|
||||
export class EcmUserServiceMock {
|
||||
private fakeEcmUser = new EcmUserModel({
|
||||
firstName: 'John',
|
||||
lastName: 'Ecm',
|
||||
avatarId: 'fake-avatar-id',
|
||||
email: 'john.ecm@gmail.com',
|
||||
jobTitle: 'Product Manager'
|
||||
});
|
||||
|
||||
getCurrentUserInfo = () => of(this.fakeEcmUser);
|
||||
|
||||
getUserProfileImage = () => './assets/images/alfresco-logo.svg';
|
||||
}
|
||||
|
||||
export class BpmUserServiceMock {
|
||||
private fakeBpmUser = new BpmUserModel({
|
||||
email: 'john.bpm@gmail.com',
|
||||
firstName: 'John',
|
||||
lastName: 'Bpm',
|
||||
pictureId: 12,
|
||||
tenantName: 'Name of Tenant'
|
||||
});
|
||||
|
||||
getCurrentUserInfo = () => of(this.fakeBpmUser);
|
||||
|
||||
getCurrentUserProfileImage = () => './assets/images/alfresco-logo.svg';
|
||||
}
|
||||
|
||||
export class IdentityUserServiceMock {
|
||||
private fakeIdentityUser = {
|
||||
familyName: 'Identity',
|
||||
givenName: 'John',
|
||||
email: 'john.identity@gmail.com',
|
||||
username: 'johnyIdentity99'
|
||||
};
|
||||
|
||||
getCurrentUserInfo = (): IdentityUserModel => this.fakeIdentityUser;
|
||||
}
|
@@ -1,162 +0,0 @@
|
||||
<div id="userinfo_container" [class.adf-userinfo-name-right]="showOnRight" (keyup)="onKeyPress($event)"
|
||||
class="adf-userinfo-container" *ngIf="isLoggedIn">
|
||||
|
||||
<ng-container *ngIf="showName">
|
||||
<span *ngIf="identityUser$ | async as identityUser; else showBpmAndEcmUserFullNames"
|
||||
id="adf-userinfo-identity-name-display"
|
||||
class="adf-userinfo-name">{{identityUser | fullName}}</span>
|
||||
<ng-template #showBpmAndEcmUserFullNames>
|
||||
<span *ngIf="ecmUser$ | async as ecmUser; else showBpmUserFullName" id="adf-userinfo-ecm-name-display"
|
||||
class="adf-userinfo-name">{{ecmUser | fullName}}</span>
|
||||
<ng-template #showBpmUserFullName>
|
||||
<span *ngIf="bpmUser$ | async as bpmUser" id="adf-userinfo-bpm-name-display"
|
||||
class="adf-userinfo-name">{{bpmUser | fullName}}</span>
|
||||
</ng-template>
|
||||
</ng-template>
|
||||
</ng-container>
|
||||
|
||||
<button mat-button [matMenuTriggerFor]="menu" class="adf-userinfo-menu_button"
|
||||
data-automation-id="adf-user-profile">
|
||||
<div class="adf-userinfo-button-profile" id="user-profile">
|
||||
<div *ngIf="identityUser$ | async as identityUser; else showBpmAndEcmUserImage" id="identity-user-image">
|
||||
<div *ngIf="(ecmUser$ | async)?.avatarId as avatarId; else initialTemplate">
|
||||
<div class="adf-userinfo-profile-container">
|
||||
<img id="logged-user-img" [src]="getEcmAvatar(avatarId)" alt="user-info-profile-button"
|
||||
class="adf-userinfo-profile-image"/>
|
||||
</div>
|
||||
</div>
|
||||
<ng-template #initialTemplate>
|
||||
<div [innerHTML]="identityUser | usernameInitials:'adf-userinfo-pic'"></div>
|
||||
</ng-template>
|
||||
</div>
|
||||
<ng-template #showBpmAndEcmUserImage>
|
||||
<div *ngIf="ecmUser$ | async as ecmUser; else showBpmUserImage" id="ecm-user-image">
|
||||
<div *ngIf="ecmUser.avatarId; else initialTemplate" class="adf-userinfo-profile-container">
|
||||
<img id="logged-user-img" [src]="getEcmAvatar(ecmUser.avatarId)" alt="user-info-profile-button"
|
||||
class="adf-userinfo-profile-image"/>
|
||||
</div>
|
||||
<ng-template #initialTemplate>
|
||||
<div [outerHTML]="ecmUser | usernameInitials:'adf-userinfo-pic'"></div>
|
||||
</ng-template>
|
||||
</div>
|
||||
<ng-template #showBpmUserImage>
|
||||
<div *ngIf="bpmUser$ | async as bpmUser" id="bpm-user-image">
|
||||
<div *ngIf="bpmUser.pictureId; else initialTemplate" class="adf-userinfo-profile-container">
|
||||
<img id="logged-user-img" [src]="getBpmUserImage()" alt="user-info-profile-button"
|
||||
class="adf-userinfo-profile-image"/>
|
||||
</div>
|
||||
<ng-template #initialTemplate>
|
||||
<div [outerHTML]="bpmUser | usernameInitials:'adf-userinfo-pic'"></div>
|
||||
</ng-template>
|
||||
</div>
|
||||
</ng-template>
|
||||
</ng-template>
|
||||
</div>
|
||||
</button>
|
||||
<mat-menu #menu="matMenu" id="user-profile-lists" [xPosition]="menuPositionX" [yPosition]="menuPositionY"
|
||||
[overlapTrigger]="false" class="adf-userinfo-menu">
|
||||
<mat-tab-group id="tab-group-env" (click)="stopClosing($event)" selectedIndex="0" role="menuitem"
|
||||
class="adf-userinfo-tab" [class.adf-hide-tab]="!(bpmUser$ | async) || !(ecmUser$ | async)">
|
||||
<mat-tab id="ecm-panel" label="{{ 'USER_PROFILE.TAB.CS' | translate }}" role="dialog"
|
||||
*ngIf="mode==='CONTENT' || mode==='ALL'">
|
||||
<mat-card class="adf-userinfo-card" *ngIf="ecmUser$ | async as ecmUser">
|
||||
<mat-card-header class="adf-userinfo-card-header"
|
||||
[style.background-image]="'url(' + ecmBackgroundImage + ')'">
|
||||
<div *ngIf="ecmUser.avatarId; else initialTemplate"
|
||||
class="adf-userinfo-profile-container adf-hide-small">
|
||||
<img class="adf-userinfo-profile-picture" id="ecm-user-detail-image"
|
||||
alt="ecm-profile-image" [src]="getEcmAvatar(ecmUser.avatarId)"/>
|
||||
</div>
|
||||
<ng-template #initialTemplate>
|
||||
<div
|
||||
[outerHTML]="ecmUser | usernameInitials:'adf-userinfo-profile-initials adf-hide-small'"></div>
|
||||
</ng-template>
|
||||
|
||||
<div class="adf-userinfo-title" id="ecm-username">{{ecmUser | fullName}}</div>
|
||||
</mat-card-header>
|
||||
<mat-card-content>
|
||||
<div class="adf-userinfo-supporting-text">
|
||||
<div class="adf-userinfo-detail">
|
||||
<span id="ecm-full-name"
|
||||
class="adf-userinfo__detail-title">{{ecmUser | fullName}}</span>
|
||||
<span class="adf-userinfo__detail-profile" id="ecm-email"> {{ecmUser.email}} </span>
|
||||
<a *ngIf="mode==='CONTENT'" class="adf-userinfo__detail-profile" href="#/profile">
|
||||
{{ 'USER_PROFILE.LABELS.MY_PROFILE' | translate }}</a>
|
||||
</div>
|
||||
<div class="adf-userinfo-detail">
|
||||
<span class="adf-userinfo__secondary-info" id="ecm-job-title-label">
|
||||
{{ 'USER_PROFILE.LABELS.ECM.JOB_TITLE' | translate }}
|
||||
<span id="ecm-job-title"
|
||||
class="adf-userinfo__detail-profile"> {{ ecmUser.jobTitle ? ecmUser.jobTitle : 'N/A' }} </span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
</mat-tab>
|
||||
<mat-tab id="bpm-panel" label="{{ 'USER_PROFILE.TAB.PS' | translate }}" role="dialog"
|
||||
*ngIf="mode==='PROCESS' || mode==='ALL'">
|
||||
<mat-card class="adf-userinfo-card" *ngIf="bpmUser$ | async as bpmUser">
|
||||
<mat-card-header class="adf-userinfo-card-header"
|
||||
[style.background-image]="'url(' + bpmBackgroundImage + ')'">
|
||||
<img *ngIf="bpmUser.pictureId; else initialTemplate"
|
||||
class="adf-userinfo-profile-picture adf-hide-small" id="bpm-user-detail-image"
|
||||
alt="bpm-profile-image" [src]="getBpmUserImage()"/>
|
||||
<ng-template #initialTemplate>
|
||||
<div
|
||||
[outerHTML]="bpmUser | usernameInitials:'adf-userinfo-profile-initials adf-hide-small'"></div>
|
||||
</ng-template>
|
||||
<div class="adf-userinfo-title" id="bpm-username">{{bpmUser | fullName}}</div>
|
||||
</mat-card-header>
|
||||
<mat-card-content>
|
||||
<div class="adf-userinfo-supporting-text">
|
||||
<div class="adf-userinfo-detail">
|
||||
<span id="bpm-full-name"
|
||||
class="adf-userinfo__detail-title">{{ bpmUser | fullName }}</span>
|
||||
<span class="adf-userinfo__detail-profile" id="bpm-email"> {{bpmUser.email}} </span>
|
||||
</div>
|
||||
<div class="adf-userinfo-detail">
|
||||
<span id="bpm-tenant" class="adf-userinfo__secondary-info">
|
||||
{{ 'USER_PROFILE.LABELS.BPM.TENANT' | translate }}
|
||||
<span
|
||||
class="adf-userinfo__detail-profile">{{ bpmUser.tenantName ? bpmUser.tenantName : '' }}</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
</mat-tab>
|
||||
<mat-tab id="identity-panel" role="dialog" *ngIf="mode==='SSO'">
|
||||
<mat-card class="adf-userinfo-card" *ngIf="identityUser$ | async as identityUser">
|
||||
<mat-card-header class="adf-userinfo-card-header"
|
||||
[style.background-image]="'url(' + bpmBackgroundImage + ')'">
|
||||
<div *ngIf="ecmUser$ | async as ecmUser">
|
||||
<div *ngIf="ecmUser.avatarId; else initialTemplate"
|
||||
class="adf-userinfo-profile-container adf-hide-small">
|
||||
<img class="adf-userinfo-profile-picture" id="ecm-user-detail-image"
|
||||
alt="ecm-profile-image" [src]="getEcmAvatar(ecmUser.avatarId)"/>
|
||||
</div>
|
||||
</div>
|
||||
<ng-template #initialTemplate>
|
||||
<div
|
||||
[outerHTML]="identityUser | usernameInitials:'adf-userinfo-profile-initials adf-hide-small'"></div>
|
||||
</ng-template>
|
||||
<div class="adf-userinfo-title" id="identity-username">{{identityUser | fullName}}</div>
|
||||
</mat-card-header>
|
||||
<mat-card-content>
|
||||
<div class="adf-userinfo-supporting-text">
|
||||
<div class="adf-userinfo-detail">
|
||||
<span id="identity-full-name"
|
||||
class="adf-userinfo__detail-title">{{identityUser | fullName}}</span>
|
||||
<span class="adf-userinfo__detail-profile"
|
||||
id="identity-email"> {{identityUser.email}} </span>
|
||||
<a class="adf-userinfo__detail-profile" href="#/profile">
|
||||
{{ 'USER_PROFILE.LABELS.MY_PROFILE' | translate }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
</mat-tab>
|
||||
</mat-tab-group>
|
||||
</mat-menu>
|
||||
</div>
|
@@ -1,592 +0,0 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2019 Alfresco Software, Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { By, DomSanitizer } from '@angular/platform-browser';
|
||||
import { ContentService, PeopleContentService } from '../../services';
|
||||
import { AuthenticationService } from '../../auth/services/authentication.service';
|
||||
import { IdentityUserService } from '../../auth/services/identity-user.service';
|
||||
import { InitialUsernamePipe } from '../../pipes';
|
||||
import { fakeBpmUser } from '../../mock/bpm-user.service.mock';
|
||||
import { fakeEcmEditedUser, fakeEcmUser, fakeEcmUserNoImage } from '../../mock/ecm-user.service.mock';
|
||||
import { BpmUserService } from '../../services/bpm-user.service';
|
||||
import { BpmUserModel } from '../../models/bpm-user.model';
|
||||
import { EcmUserModel } from '../../models/ecm-user.model';
|
||||
import { UserInfoComponent } from './user-info.component';
|
||||
import { of } from 'rxjs';
|
||||
import { setupTestBed } from '../../testing/setup-test-bed';
|
||||
import { CoreTestingModule } from '../../testing/core.testing.module';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { MatMenuModule } from '@angular/material/menu';
|
||||
|
||||
class FakeSanitizer extends DomSanitizer {
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
sanitize(html) {
|
||||
return html;
|
||||
}
|
||||
|
||||
bypassSecurityTrustHtml(value: string): any {
|
||||
return value;
|
||||
}
|
||||
|
||||
bypassSecurityTrustStyle(): any {
|
||||
return null;
|
||||
}
|
||||
|
||||
bypassSecurityTrustScript(): any {
|
||||
return null;
|
||||
}
|
||||
|
||||
bypassSecurityTrustUrl(): any {
|
||||
return null;
|
||||
}
|
||||
|
||||
bypassSecurityTrustResourceUrl(): any {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
describe('User info component', () => {
|
||||
const profilePictureUrl = 'alfresco-logo.svg';
|
||||
|
||||
let component: UserInfoComponent;
|
||||
let fixture: ComponentFixture<UserInfoComponent>;
|
||||
let element: HTMLElement;
|
||||
let authService: AuthenticationService;
|
||||
let contentService: ContentService;
|
||||
let peopleContentService: PeopleContentService;
|
||||
let bpmUserService: BpmUserService;
|
||||
let identityUserService: IdentityUserService;
|
||||
|
||||
const identityUserMock = { firstName: 'fake-identity-first-name', lastName: 'fake-identity-last-name', email: 'fakeIdentity@email.com' };
|
||||
const identityUserWithOutFirstNameMock = { firstName: null, lastName: 'fake-identity-last-name', email: 'fakeIdentity@email.com' };
|
||||
const identityUserWithOutLastNameMock = { firstName: 'fake-identity-first-name', lastName: null, email: 'fakeIdentity@email.com' };
|
||||
|
||||
const openUserInfo = () => {
|
||||
fixture.detectChanges();
|
||||
const imageButton = element.querySelector<HTMLButtonElement>('#logged-user-img');
|
||||
imageButton.click();
|
||||
fixture.detectChanges();
|
||||
};
|
||||
|
||||
const whenFixtureReady = async () => {
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
fixture.detectChanges();
|
||||
};
|
||||
|
||||
setupTestBed({
|
||||
imports: [
|
||||
TranslateModule.forRoot(),
|
||||
CoreTestingModule,
|
||||
MatMenuModule
|
||||
]
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(UserInfoComponent);
|
||||
component = fixture.componentInstance;
|
||||
element = fixture.nativeElement;
|
||||
|
||||
authService = TestBed.inject(AuthenticationService);
|
||||
peopleContentService = TestBed.inject(PeopleContentService);
|
||||
bpmUserService = TestBed.inject(BpmUserService);
|
||||
contentService = TestBed.inject(ContentService);
|
||||
identityUserService = TestBed.inject(IdentityUserService);
|
||||
|
||||
spyOn(window, 'requestAnimationFrame').and.returnValue(1);
|
||||
spyOn(bpmUserService, 'getCurrentUserProfileImage').and.returnValue(profilePictureUrl);
|
||||
spyOn(contentService, 'getContentUrl').and.returnValue(profilePictureUrl);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
fixture.destroy();
|
||||
});
|
||||
|
||||
it('should not show any image if the user is not logged in', () => {
|
||||
expect(element.querySelector('#userinfo_container')).toBeDefined();
|
||||
expect(element.querySelector('#logged-user-img')).toBeNull();
|
||||
});
|
||||
|
||||
it('should NOT have users immediately after ngOnInit', () => {
|
||||
expect(element.querySelector('#userinfo_container')).toBeDefined();
|
||||
expect(element.querySelector('#ecm_username')).toBeNull();
|
||||
expect(element.querySelector('#bpm_username')).toBeNull();
|
||||
expect(element.querySelector('#user-profile-lists')).toBeNull();
|
||||
});
|
||||
|
||||
describe('when user is logged on ecm', () => {
|
||||
|
||||
let getCurrenEcmtUserInfoStub;
|
||||
let isOauthStub;
|
||||
let isEcmLoggedInStub;
|
||||
let isLoggedInStub;
|
||||
let isBpmLoggedInStub;
|
||||
|
||||
beforeEach(() => {
|
||||
isOauthStub = spyOn(authService, 'isOauth').and.returnValue(false);
|
||||
isEcmLoggedInStub = spyOn(authService, 'isEcmLoggedIn').and.returnValue(true);
|
||||
isLoggedInStub = spyOn(authService, 'isLoggedIn').and.returnValue(true);
|
||||
isBpmLoggedInStub = spyOn(authService, 'isBpmLoggedIn').and.returnValue(false);
|
||||
getCurrenEcmtUserInfoStub = spyOn(peopleContentService, 'getCurrentUserInfo').and.returnValue(of(fakeEcmUser as any));
|
||||
});
|
||||
|
||||
describe('ui ', () => {
|
||||
|
||||
it('should able to fetch ecm userInfo', (done) => {
|
||||
component.getUserInfo();
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => {
|
||||
component.ecmUser$.subscribe((response: EcmUserModel) => {
|
||||
expect(response).toBeDefined();
|
||||
expect(response.firstName).toBe('fake-ecm-first-name');
|
||||
expect(response.lastName).toBe('fake-ecm-last-name');
|
||||
expect(response.email).toBe('fakeEcm@ecmUser.com');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should show ecm only last name when user first name is null ', async () => {
|
||||
getCurrenEcmtUserInfoStub.and.returnValue(of(fakeEcmEditedUser));
|
||||
await whenFixtureReady();
|
||||
|
||||
openUserInfo();
|
||||
expect(element.querySelector('#userinfo_container')).toBeDefined();
|
||||
const ecmUsername = fixture.debugElement.query(By.css('#ecm-username'));
|
||||
expect(ecmUsername).toBeDefined();
|
||||
expect(ecmUsername).not.toBeNull();
|
||||
expect(ecmUsername.nativeElement.textContent).not.toContain('fake-ecm-first-name');
|
||||
expect(ecmUsername.nativeElement.textContent).not.toContain('null');
|
||||
});
|
||||
|
||||
it('should show the username when showName attribute is true', async () => {
|
||||
await whenFixtureReady();
|
||||
expect(component.showName).toBeTruthy();
|
||||
expect(element.querySelector('#adf-userinfo-ecm-name-display')).not.toBeNull();
|
||||
});
|
||||
|
||||
it('should hide the username when showName attribute is false', async () => {
|
||||
component.showName = false;
|
||||
await whenFixtureReady();
|
||||
expect(element.querySelector('#adf-userinfo-ecm-name-display')).toBeNull();
|
||||
});
|
||||
|
||||
it('should have the defined class to show the name on the right side', async () => {
|
||||
await whenFixtureReady();
|
||||
expect(element.querySelector('#userinfo_container').classList).toContain('adf-userinfo-name-right');
|
||||
});
|
||||
|
||||
it('should not have the defined class to show the name on the left side', async () => {
|
||||
component.namePosition = 'left';
|
||||
await whenFixtureReady();
|
||||
expect(element.querySelector('#userinfo_container').classList).not.toContain('adf-userinfo-name-right');
|
||||
});
|
||||
|
||||
describe('and has image', () => {
|
||||
|
||||
beforeEach(async () => {
|
||||
isOauthStub.and.returnValue(false);
|
||||
isEcmLoggedInStub.and.returnValue(true);
|
||||
isLoggedInStub.and.returnValue(true);
|
||||
getCurrenEcmtUserInfoStub.and.returnValue(of(fakeEcmUser));
|
||||
await whenFixtureReady();
|
||||
});
|
||||
|
||||
it('should get the ecm current user image from the service', async () => {
|
||||
openUserInfo();
|
||||
const loggedImage = fixture.debugElement.query(By.css('#logged-user-img'));
|
||||
|
||||
expect(element.querySelector('#userinfo_container')).not.toBeNull();
|
||||
expect(loggedImage).not.toBeNull();
|
||||
expect(loggedImage.properties.src).toContain(profilePictureUrl);
|
||||
});
|
||||
|
||||
it('should display the current user image if user has avatarId', (done) => {
|
||||
openUserInfo();
|
||||
const loggedImage = fixture.debugElement.query(By.css('#logged-user-img'));
|
||||
component.ecmUser$.subscribe((response: EcmUserModel) => {
|
||||
expect(response).toBeDefined();
|
||||
expect(response.avatarId).toBe('fake-avatar-id');
|
||||
done();
|
||||
});
|
||||
expect(element.querySelector('#userinfo_container')).not.toBeNull();
|
||||
expect(loggedImage).not.toBeNull();
|
||||
expect(loggedImage.properties.src).toContain(profilePictureUrl);
|
||||
});
|
||||
|
||||
it('should get the ecm user information from the service', async () => {
|
||||
openUserInfo();
|
||||
const ecmImage = fixture.debugElement.query(By.css('#ecm-user-detail-image'));
|
||||
const ecmFullName = fixture.debugElement.query(By.css('#ecm-full-name'));
|
||||
const ecmJobTitle = fixture.debugElement.query(By.css('#ecm-job-title-label'));
|
||||
|
||||
expect(element.querySelector('#userinfo_container')).not.toBeNull();
|
||||
expect(fixture.debugElement.query(By.css('#ecm-username'))).not.toBeNull();
|
||||
expect(ecmImage).not.toBeNull();
|
||||
expect(ecmImage.properties.src).toContain(profilePictureUrl);
|
||||
expect(ecmFullName.nativeElement.textContent).toContain('fake-ecm-first-name fake-ecm-last-name');
|
||||
expect(ecmJobTitle.nativeElement.textContent).toContain('USER_PROFILE.LABELS.ECM.JOB_TITLE');
|
||||
});
|
||||
});
|
||||
|
||||
describe('and has no image', () => {
|
||||
|
||||
beforeEach( async () => {
|
||||
isOauthStub.and.returnValue(false);
|
||||
isEcmLoggedInStub.and.returnValue(true);
|
||||
isLoggedInStub.and.returnValue(true);
|
||||
isBpmLoggedInStub.and.returnValue(false);
|
||||
getCurrenEcmtUserInfoStub.and.returnValue(of(fakeEcmUserNoImage));
|
||||
await whenFixtureReady();
|
||||
});
|
||||
|
||||
it('should show N/A when the job title is null', () => {
|
||||
const imageButton = element.querySelector<HTMLButtonElement>('[data-automation-id="user-initials-image"]');
|
||||
imageButton.click();
|
||||
fixture.detectChanges();
|
||||
expect(element.querySelector('#userinfo_container')).not.toBeNull();
|
||||
const ecmJobTitle = fixture.debugElement.query(By.css('#ecm-job-title'));
|
||||
expect(ecmJobTitle).not.toBeNull();
|
||||
expect(ecmJobTitle).not.toBeNull();
|
||||
expect(ecmJobTitle.nativeElement.textContent).toContain('N/A');
|
||||
});
|
||||
|
||||
it('should not show the tabs', () => {
|
||||
const imageButton = element.querySelector<HTMLButtonElement>('[data-automation-id="user-initials-image"]');
|
||||
imageButton.click();
|
||||
fixture.detectChanges();
|
||||
const tabHeader = fixture.debugElement.query(By.css('#tab-group-env'));
|
||||
expect(tabHeader.classes['adf-hide-tab']).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should display the current user Initials if the user dose not have avatarId', (done) => {
|
||||
fixture.detectChanges();
|
||||
const pipe = new InitialUsernamePipe(new FakeSanitizer());
|
||||
const expected = pipe.transform({
|
||||
id: 13,
|
||||
firstName: 'Wilbur',
|
||||
lastName: 'Adams',
|
||||
email: 'wilbur@app.com'
|
||||
});
|
||||
expect(expected).toBe('<div data-automation-id="user-initials-image" class="">WA</div>');
|
||||
component.ecmUser$.subscribe((response: EcmUserModel) => {
|
||||
expect(response).toBeDefined();
|
||||
expect(response.avatarId).toBeNull();
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when user is logged on bpm', () => {
|
||||
|
||||
let getCurrentUserInfoStub: jasmine.Spy;
|
||||
|
||||
beforeEach(() => {
|
||||
isOauthStub.and.returnValue(false);
|
||||
isBpmLoggedInStub.and.returnValue(true);
|
||||
isLoggedInStub.and.returnValue(true);
|
||||
isEcmLoggedInStub.and.returnValue(false);
|
||||
getCurrentUserInfoStub = spyOn(bpmUserService, 'getCurrentUserInfo').and.returnValue(of(fakeBpmUser));
|
||||
});
|
||||
|
||||
it('should fetch bpm userInfo', (done) => {
|
||||
getCurrentUserInfoStub.and.returnValue(of(fakeBpmUser));
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => {
|
||||
component.bpmUser$.subscribe((response: BpmUserModel) => {
|
||||
expect(response).toBeDefined();
|
||||
expect(response.firstName).toBe('fake-bpm-first-name');
|
||||
expect(response.lastName).toBe('fake-bpm-last-name');
|
||||
expect(response.email).toBe('fakeBpm@fake.com');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should show full name next the user image', async () => {
|
||||
await whenFixtureReady();
|
||||
openUserInfo();
|
||||
const bpmUserName = fixture.debugElement.query(By.css('#bpm-username'));
|
||||
expect(element.querySelector('#userinfo_container')).not.toBeNull();
|
||||
expect(bpmUserName).toBeDefined();
|
||||
expect(bpmUserName).not.toBeNull();
|
||||
expect(bpmUserName.nativeElement.innerHTML).toContain('fake-bpm-first-name fake-bpm-last-name');
|
||||
});
|
||||
|
||||
it('should get the bpm current user image from the service', async () => {
|
||||
await whenFixtureReady();
|
||||
expect(element.querySelector('#userinfo_container')).not.toBeNull();
|
||||
expect(element.querySelector('#logged-user-img')).not.toBeNull();
|
||||
expect(element.querySelector('#logged-user-img').getAttribute('src')).toContain(profilePictureUrl);
|
||||
});
|
||||
|
||||
it('should show last name if first name is null', async () => {
|
||||
const wrongBpmUser: BpmUserModel = new BpmUserModel({
|
||||
firstName: null,
|
||||
lastName: 'fake-last-name'
|
||||
});
|
||||
getCurrentUserInfoStub.and.returnValue(of(wrongBpmUser));
|
||||
await whenFixtureReady();
|
||||
const fullNameElement = (element.querySelector('#adf-userinfo-bpm-name-display'));
|
||||
fixture.detectChanges();
|
||||
expect(element.querySelector('#userinfo_container')).toBeDefined();
|
||||
expect(element.querySelector('#adf-userinfo-bpm-name-display')).not.toBeNull();
|
||||
expect(fullNameElement.textContent).toContain('fake-last-name');
|
||||
expect(fullNameElement.textContent).not.toContain('fake-first-name');
|
||||
});
|
||||
|
||||
it('should not show the tabs', async () => {
|
||||
await whenFixtureReady();
|
||||
openUserInfo();
|
||||
await fixture.whenStable();
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.query(By.css('#tab-group-env')).classes['adf-hide-tab']).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when user is logged on bpm and ecm', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
isOauthStub.and.returnValue(false);
|
||||
isEcmLoggedInStub.and.returnValue(true);
|
||||
isBpmLoggedInStub.and.returnValue(true);
|
||||
isLoggedInStub.and.returnValue(true);
|
||||
|
||||
getCurrenEcmtUserInfoStub.and.returnValue(of(fakeEcmUser));
|
||||
spyOn(bpmUserService, 'getCurrentUserInfo').and.returnValue(of(fakeBpmUser));
|
||||
});
|
||||
|
||||
it('should get the bpm user information from the service', async () => {
|
||||
await whenFixtureReady();
|
||||
openUserInfo();
|
||||
const bpmTab = fixture.debugElement.queryAll(By.css('#tab-group-env .mat-tab-labels .mat-tab-label'))[1];
|
||||
bpmTab.triggerEventHandler('click', null);
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
const bpmUsername = fixture.debugElement.query(By.css('#bpm-username'));
|
||||
const bpmImage = fixture.debugElement.query(By.css('#bpm-user-detail-image'));
|
||||
expect(element.querySelector('#userinfo_container')).not.toBeNull();
|
||||
expect(bpmUsername).not.toBeNull();
|
||||
expect(bpmImage).not.toBeNull();
|
||||
expect(bpmImage.properties.src).toContain(profilePictureUrl);
|
||||
expect(bpmUsername.nativeElement.textContent).toContain('fake-bpm-first-name fake-bpm-last-name');
|
||||
expect(fixture.debugElement.query(By.css('#bpm-tenant')).nativeElement.textContent).toContain('fake-tenant-name');
|
||||
});
|
||||
|
||||
it('should get the ecm user information from the service', async () => {
|
||||
await whenFixtureReady();
|
||||
openUserInfo();
|
||||
const ecmUsername = fixture.debugElement.query(By.css('#ecm-username'));
|
||||
const ecmImage = fixture.debugElement.query(By.css('#ecm-user-detail-image'));
|
||||
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
expect(element.querySelector('#userinfo_container')).toBeDefined();
|
||||
expect(ecmUsername).not.toBeNull();
|
||||
expect(ecmImage).not.toBeNull();
|
||||
expect(ecmImage.properties.src).toContain(profilePictureUrl);
|
||||
expect(fixture.debugElement.query(By.css('#ecm-full-name')).nativeElement.textContent).toContain('fake-ecm-first-name fake-ecm-last-name');
|
||||
expect(fixture.debugElement.query(By.css('#ecm-job-title')).nativeElement.textContent).toContain('job-ecm-test');
|
||||
});
|
||||
|
||||
it('should show the ecm image if exists', async () => {
|
||||
await whenFixtureReady();
|
||||
openUserInfo();
|
||||
expect(element.querySelector('#userinfo_container')).toBeDefined();
|
||||
expect(element.querySelector('#logged-user-img')).toBeDefined();
|
||||
expect(element.querySelector('#logged-user-img').getAttribute('src')).toEqual(profilePictureUrl);
|
||||
});
|
||||
|
||||
it('should show the ecm initials if the ecm user has no image', async () => {
|
||||
getCurrenEcmtUserInfoStub.and.returnValue(of(fakeEcmUserNoImage));
|
||||
await whenFixtureReady();
|
||||
|
||||
expect(element.querySelector('#userinfo_container')).toBeDefined();
|
||||
expect(element.querySelector('[data-automation-id="user-initials-image"]').textContent).toContain('ff');
|
||||
});
|
||||
|
||||
it('should show the tabs for the env', async () => {
|
||||
await whenFixtureReady();
|
||||
openUserInfo();
|
||||
const tabGroup = fixture.debugElement.query(By.css('#tab-group-env'));
|
||||
const tabs = fixture.debugElement.queryAll(By.css('#tab-group-env .mat-tab-labels .mat-tab-label'));
|
||||
|
||||
expect(tabGroup).not.toBeNull();
|
||||
expect(tabGroup.classes['adf-hide-tab']).toBeFalsy();
|
||||
expect(tabs.length).toBe(2);
|
||||
});
|
||||
|
||||
it('should not close the menu when a tab is clicked', async () => {
|
||||
await whenFixtureReady();
|
||||
openUserInfo();
|
||||
const tabGroup = fixture.debugElement.query(By.css('#tab-group-env'));
|
||||
const tabs = fixture.debugElement.queryAll(By.css('#tab-group-env .mat-tab-labels .mat-tab-label'));
|
||||
|
||||
expect(tabGroup).not.toBeNull();
|
||||
tabs[1].triggerEventHandler('click', null);
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.query(By.css('#user-profile-lists'))).not.toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when identity user is logged in', () => {
|
||||
|
||||
let getCurrentUserInfoStub: jasmine.Spy;
|
||||
|
||||
beforeEach(() => {
|
||||
isOauthStub.and.returnValue(true);
|
||||
isLoggedInStub.and.returnValue(true);
|
||||
isEcmLoggedInStub.and.returnValue(false);
|
||||
getCurrentUserInfoStub = spyOn(identityUserService, 'getCurrentUserInfo').and.returnValue(identityUserMock);
|
||||
});
|
||||
|
||||
it('should show the identity user initials if is not ecm user', async () => {
|
||||
await whenFixtureReady();
|
||||
expect(element.querySelector('#userinfo_container')).toBeDefined();
|
||||
expect(element.querySelector('[data-automation-id="user-initials-image"]').textContent).toContain('ff');
|
||||
});
|
||||
|
||||
it('should able to fetch identity userInfo', (done) => {
|
||||
fixture.detectChanges();
|
||||
|
||||
fixture.whenStable().then(() => {
|
||||
component.identityUser$.subscribe(response => {
|
||||
expect(response).toBeDefined();
|
||||
expect(response.firstName).toBe('fake-identity-first-name');
|
||||
expect(response.lastName).toBe('fake-identity-last-name');
|
||||
expect(response.email).toBe('fakeIdentity@email.com');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should show full name next the user image', async () => {
|
||||
await whenFixtureReady();
|
||||
|
||||
const imageButton = element.querySelector<HTMLButtonElement>('#identity-user-image');
|
||||
imageButton.click();
|
||||
fixture.detectChanges();
|
||||
expect(element.querySelector('#userinfo_container')).not.toBeNull();
|
||||
const bpmUserName = fixture.debugElement.query(By.css('#identity-username'));
|
||||
expect(bpmUserName).toBeDefined();
|
||||
expect(bpmUserName).not.toBeNull();
|
||||
expect(bpmUserName.nativeElement.textContent).toContain('fake-identity-first-name fake-identity-last-name');
|
||||
});
|
||||
|
||||
it('should show last name if first name is null', async () => {
|
||||
getCurrentUserInfoStub.and.returnValue(identityUserWithOutFirstNameMock);
|
||||
await whenFixtureReady();
|
||||
|
||||
const fullNameElement = element.querySelector('#adf-userinfo-identity-name-display');
|
||||
expect(element.querySelector('#userinfo_container')).toBeDefined();
|
||||
expect(element.querySelector('#adf-userinfo-identity-name-display')).not.toBeNull();
|
||||
expect(fullNameElement.textContent).toContain('fake-identity-last-name');
|
||||
expect(fullNameElement.textContent).not.toContain('fake-identity-first-name');
|
||||
});
|
||||
|
||||
it('should not show first name if it is null string', async () => {
|
||||
getCurrentUserInfoStub.and.returnValue(identityUserWithOutFirstNameMock);
|
||||
await whenFixtureReady();
|
||||
|
||||
const fullNameElement = element.querySelector('#adf-userinfo-identity-name-display');
|
||||
fixture.detectChanges();
|
||||
expect(element.querySelector('#userinfo_container')).toBeDefined();
|
||||
expect(fullNameElement).toBeDefined();
|
||||
expect(fullNameElement.textContent).toContain('fake-identity-last-name');
|
||||
expect(fullNameElement.textContent).not.toContain('null');
|
||||
});
|
||||
|
||||
it('should not show last name if it is null string', async () => {
|
||||
getCurrentUserInfoStub.and.returnValue(identityUserWithOutLastNameMock);
|
||||
await whenFixtureReady();
|
||||
|
||||
const fullNameElement = element.querySelector('#adf-userinfo-identity-name-display');
|
||||
fixture.detectChanges();
|
||||
expect(element.querySelector('#userinfo_container')).toBeDefined();
|
||||
expect(fullNameElement).toBeDefined();
|
||||
expect(fullNameElement.textContent).toContain('fake-identity-first-name');
|
||||
expect(fullNameElement.textContent).not.toContain('null');
|
||||
});
|
||||
|
||||
it('should not show initials if the user have avatar and provider is ECM', async () => {
|
||||
getCurrentUserInfoStub.and.returnValue(identityUserWithOutLastNameMock);
|
||||
getCurrenEcmtUserInfoStub.and.returnValue(of(fakeEcmUser));
|
||||
isEcmLoggedInStub.and.returnValue(true);
|
||||
await whenFixtureReady();
|
||||
|
||||
expect(element.querySelector('.adf-userinfo-pic')).toBeNull();
|
||||
expect(element.querySelector('.adf-userinfo-profile-image')).toBeDefined();
|
||||
expect(element.querySelector('.adf-userinfo-profile-image')).not.toBeNull();
|
||||
});
|
||||
|
||||
it('should show initials if the user has avatar but provider is not ECM', async () => {
|
||||
getCurrentUserInfoStub.and.returnValue(identityUserWithOutLastNameMock);
|
||||
getCurrenEcmtUserInfoStub.and.returnValue(of(fakeEcmUser));
|
||||
isEcmLoggedInStub.and.returnValue(false);
|
||||
await whenFixtureReady();
|
||||
|
||||
expect(element.querySelector('.adf-userinfo-pic')).not.toBeNull();
|
||||
expect(element.querySelector('.adf-userinfo-profile-image')).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('kerberos', () => {
|
||||
|
||||
beforeEach(async () => {
|
||||
isOauthStub.and.returnValue(false);
|
||||
isEcmLoggedInStub.and.returnValue(false);
|
||||
isBpmLoggedInStub.and.returnValue(false);
|
||||
isLoggedInStub.and.returnValue(false);
|
||||
spyOn(authService, 'isKerberosEnabled').and.returnValue(true);
|
||||
spyOn(authService, 'isALLProvider').and.returnValue(true);
|
||||
spyOn(bpmUserService, 'getCurrentUserInfo').and.returnValue(of(fakeBpmUser));
|
||||
getCurrenEcmtUserInfoStub.and.returnValue(of(fakeEcmUser));
|
||||
|
||||
await whenFixtureReady();
|
||||
});
|
||||
|
||||
it('should show the bpm user information', async () => {
|
||||
openUserInfo();
|
||||
const bpmTab = fixture.debugElement.queryAll(By.css('#tab-group-env .mat-tab-labels .mat-tab-label'))[1];
|
||||
bpmTab.triggerEventHandler('click', null);
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
const bpmUsername = fixture.debugElement.query(By.css('#bpm-username'));
|
||||
const bpmImage = fixture.debugElement.query(By.css('#bpm-user-detail-image'));
|
||||
expect(bpmImage.properties.src).toContain(profilePictureUrl);
|
||||
expect(bpmUsername.nativeElement.textContent).toContain('fake-bpm-first-name fake-bpm-last-name');
|
||||
expect(fixture.debugElement.query(By.css('#bpm-tenant')).nativeElement.textContent).toContain('fake-tenant-name');
|
||||
});
|
||||
|
||||
it('should show the ecm user information', async () => {
|
||||
openUserInfo();
|
||||
const ecmTab = fixture.debugElement.queryAll(By.css('#tab-group-env .mat-tab-labels .mat-tab-label'))[0];
|
||||
ecmTab.triggerEventHandler('click', null);
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
expect(fixture.debugElement.query(By.css('#ecm-full-name')).nativeElement.textContent).toContain('fake-ecm-first-name fake-ecm-last-name');
|
||||
expect(fixture.debugElement.query(By.css('#ecm-job-title')).nativeElement.textContent).toContain('job-ecm-test');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
@@ -1,200 +0,0 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2019 Alfresco Software, Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {
|
||||
Meta,
|
||||
moduleMetadata,
|
||||
Story
|
||||
} from '@storybook/angular';
|
||||
import { CoreStoryModule } from '../../testing/core.story.module';
|
||||
import { UserInfoComponent } from './user-info.component';
|
||||
import { UserInfoModule } from '../userinfo.module';
|
||||
import { PeopleContentService } from './../../services/people-content.service';
|
||||
import { BpmUserService } from './../../services/bpm-user.service';
|
||||
import { IdentityUserService } from '../../auth/services/identity-user.service';
|
||||
import { AuthenticationService } from '../../auth/services/authentication.service';
|
||||
import { AuthenticationServiceMock } from './mocks/authentication.service.mock';
|
||||
import {
|
||||
BpmUserServiceMock,
|
||||
IdentityUserServiceMock,
|
||||
PeopleContentServiceMock
|
||||
} from './mocks/user.service.mock';
|
||||
|
||||
export default {
|
||||
component: UserInfoComponent,
|
||||
title: 'Core/User Info/User Info',
|
||||
decorators: [
|
||||
moduleMetadata({
|
||||
imports: [CoreStoryModule, UserInfoModule],
|
||||
providers: [
|
||||
{
|
||||
provide: PeopleContentService,
|
||||
useClass: PeopleContentServiceMock
|
||||
},
|
||||
{
|
||||
provide: BpmUserService,
|
||||
useClass: BpmUserServiceMock
|
||||
},
|
||||
{
|
||||
provide: IdentityUserService,
|
||||
useClass: IdentityUserServiceMock
|
||||
},
|
||||
{
|
||||
provide: AuthenticationService,
|
||||
useClass: AuthenticationServiceMock
|
||||
}
|
||||
]
|
||||
})
|
||||
],
|
||||
argTypes: {
|
||||
menuPositionX: {
|
||||
description:
|
||||
'Material Angular menu horizontal position in regard to User Info',
|
||||
control: 'radio',
|
||||
options: ['before', 'after'],
|
||||
defaultValue: 'after',
|
||||
table: {
|
||||
type: { summary: 'MenuPositionX' },
|
||||
defaultValue: { summary: 'after' }
|
||||
}
|
||||
},
|
||||
menuPositionY: {
|
||||
description:
|
||||
'Material Angular menu vertical position in regard to User Info',
|
||||
control: 'radio',
|
||||
options: ['above', 'below'],
|
||||
defaultValue: 'below',
|
||||
table: {
|
||||
type: { summary: 'MenuPositionY' },
|
||||
defaultValue: { summary: 'below' }
|
||||
}
|
||||
},
|
||||
showName: {
|
||||
description:
|
||||
'Determines if name should be shown next to user avatar',
|
||||
control: 'boolean',
|
||||
defaultValue: true,
|
||||
table: {
|
||||
type: { summary: 'boolean' },
|
||||
defaultValue: { summary: true }
|
||||
}
|
||||
},
|
||||
namePosition: {
|
||||
description: 'User name position in regard to avatar',
|
||||
control: 'radio',
|
||||
options: ['left', 'right'],
|
||||
defaultValue: 'right',
|
||||
table: {
|
||||
type: { summary: 'string' },
|
||||
defaultValue: { summary: 'right' }
|
||||
}
|
||||
},
|
||||
ecmBackgroundImage: {
|
||||
description: 'Menu background banner image for ACS users',
|
||||
control: {
|
||||
disable: true
|
||||
},
|
||||
table: {
|
||||
type: { summary: 'string' },
|
||||
defaultValue: { summary: './assets/images/ecm-background.png' }
|
||||
}
|
||||
},
|
||||
bpmBackgroundImage: {
|
||||
description: 'Menu background banner image for APS users',
|
||||
control: {
|
||||
disable: true
|
||||
},
|
||||
table: {
|
||||
type: {
|
||||
summary: 'string'
|
||||
},
|
||||
defaultValue: {
|
||||
summary: './assets/images/bpm-background.png'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} as Meta;
|
||||
|
||||
const template: Story<UserInfoComponent> = (args: UserInfoComponent) => ({
|
||||
props: args
|
||||
});
|
||||
|
||||
export const loginWithSSO = template.bind({});
|
||||
loginWithSSO.decorators = [
|
||||
moduleMetadata({
|
||||
providers: [
|
||||
{
|
||||
provide: 'MODE',
|
||||
useValue: 'default'
|
||||
}
|
||||
]
|
||||
})
|
||||
];
|
||||
loginWithSSO.parameters = { layout: 'centered' };
|
||||
|
||||
export const loginWithSSOAndACS = template.bind({});
|
||||
loginWithSSOAndACS.decorators = [
|
||||
moduleMetadata({
|
||||
providers: [
|
||||
{
|
||||
provide: 'MODE',
|
||||
useValue: 'defaultEcm'
|
||||
}
|
||||
]
|
||||
})
|
||||
];
|
||||
loginWithSSOAndACS.parameters = { layout: 'centered' };
|
||||
|
||||
export const loginWithAPSAndACS = template.bind({});
|
||||
loginWithAPSAndACS.decorators = [
|
||||
moduleMetadata({
|
||||
providers: [
|
||||
{
|
||||
provide: 'MODE',
|
||||
useValue: 'all'
|
||||
}
|
||||
]
|
||||
})
|
||||
];
|
||||
loginWithAPSAndACS.parameters = { layout: 'centered' };
|
||||
|
||||
export const loginWithACS = template.bind({});
|
||||
loginWithACS.decorators = [
|
||||
moduleMetadata({
|
||||
providers: [
|
||||
{
|
||||
provide: 'MODE',
|
||||
useValue: 'ecm'
|
||||
}
|
||||
]
|
||||
})
|
||||
];
|
||||
loginWithACS.parameters = { layout: 'centered' };
|
||||
|
||||
export const loginWithAPS = template.bind({});
|
||||
loginWithAPS.decorators = [
|
||||
moduleMetadata({
|
||||
providers: [
|
||||
{
|
||||
provide: 'MODE',
|
||||
useValue: 'bpm'
|
||||
}
|
||||
]
|
||||
})
|
||||
];
|
||||
loginWithAPS.parameters = { layout: 'centered' };
|
@@ -17,12 +17,12 @@
|
||||
|
||||
export * from './lib/about/index';
|
||||
export * from './lib/viewer/index';
|
||||
export * from './lib/userinfo/index';
|
||||
export * from './lib/toolbar/index';
|
||||
export * from './lib/pagination/index';
|
||||
export * from './lib/login/index';
|
||||
export * from './lib/language-menu/index';
|
||||
export * from './lib/info-drawer/index';
|
||||
export * from './lib/identity-user-info/index';
|
||||
export * from './lib/datatable/data-column/index';
|
||||
export * from './lib/datatable/index';
|
||||
export * from './lib/context-menu/index';
|
||||
|
18
lib/process-services/src/lib/process-user-info/index.ts
Normal file
18
lib/process-services/src/lib/process-user-info/index.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2019 Alfresco Software, Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export * from './public-api';
|
@@ -0,0 +1,109 @@
|
||||
<div
|
||||
id="userinfo_container"
|
||||
[class.adf-userinfo-name-right]="showOnRight"
|
||||
(keyup)="onKeyPress($event)"
|
||||
class="adf-userinfo-container"
|
||||
*ngIf="canShow"
|
||||
>
|
||||
<span *ngIf="showName" id="adf-userinfo-bpm-name-display" class="adf-userinfo-name">
|
||||
{{bpmUser | fullName}}
|
||||
</span>
|
||||
<button mat-button [matMenuTriggerFor]="menu" class="adf-userinfo-menu_button"
|
||||
data-automation-id="adf-user-profile">
|
||||
<div class="adf-userinfo-button-profile" id="user-profile">
|
||||
<div *ngIf="ecmUser; else showBpmUserImage" id="ecm-user-image">
|
||||
<div *ngIf="ecmUser.avatarId; else initialTemplate" class="adf-userinfo-profile-container">
|
||||
<img id="logged-user-img" [src]="getEcmAvatar(ecmUser.avatarId)" alt="user-info-profile-button"
|
||||
class="adf-userinfo-profile-image"/>
|
||||
</div>
|
||||
<ng-template #initialTemplate>
|
||||
<div [outerHTML]="ecmUser | usernameInitials:'adf-userinfo-pic'"></div>
|
||||
</ng-template>
|
||||
</div>
|
||||
<ng-template #showBpmUserImage>
|
||||
<div *ngIf="bpmUser" id="bpm-user-image">
|
||||
<div *ngIf="bpmUser.pictureId; else initialTemplate" class="adf-userinfo-profile-container">
|
||||
<img id="logged-user-img" [src]="getBpmUserImage()" alt="user-info-profile-button"
|
||||
class="adf-userinfo-profile-image"/>
|
||||
</div>
|
||||
<ng-template #initialTemplate>
|
||||
<div [outerHTML]="bpmUser | usernameInitials:'adf-userinfo-pic'"></div>
|
||||
</ng-template>
|
||||
</div>
|
||||
</ng-template>
|
||||
</div>
|
||||
</button>
|
||||
<mat-menu #menu="matMenu" id="user-profile-lists" [xPosition]="menuPositionX" [yPosition]="menuPositionY"
|
||||
[overlapTrigger]="false" class="adf-userinfo-menu">
|
||||
<mat-tab-group id="tab-group-env" (click)="stopClosing($event)" selectedIndex="0" role="menuitem"
|
||||
class="adf-userinfo-tab" [class.adf-hide-tab]="!ecmUser">
|
||||
<mat-tab label="{{ 'USER_PROFILE.TAB.CS' | translate }}" role="dialog"
|
||||
*ngIf="mode===userInfoMode.ALL">
|
||||
<mat-card class="adf-userinfo-card" *ngIf="ecmUser">
|
||||
<mat-card-header class="adf-userinfo-card-header"
|
||||
[style.background-image]="'url(' + ecmBackgroundImage + ')'">
|
||||
<div *ngIf="ecmUser.avatarId; else initialTemplate"
|
||||
class="adf-userinfo-profile-container adf-hide-small">
|
||||
<img class="adf-userinfo-profile-picture" id="ecm-user-detail-image"
|
||||
alt="ecm-profile-image" [src]="getEcmAvatar(ecmUser.avatarId)"/>
|
||||
</div>
|
||||
<ng-template #initialTemplate>
|
||||
<div
|
||||
[outerHTML]="ecmUser | usernameInitials:'adf-userinfo-profile-initials adf-hide-small'"></div>
|
||||
</ng-template>
|
||||
|
||||
<div class="adf-userinfo-title" id="ecm-username">{{ecmUser | fullName}}</div>
|
||||
</mat-card-header>
|
||||
<mat-card-content>
|
||||
<div class="adf-userinfo-supporting-text">
|
||||
<div class="adf-userinfo-detail">
|
||||
<span id="ecm-full-name"
|
||||
class="adf-userinfo__detail-title">{{ecmUser | fullName}}</span>
|
||||
<span class="adf-userinfo__detail-profile" id="ecm-email"> {{ecmUser.email}} </span>
|
||||
</div>
|
||||
<div class="adf-userinfo-detail">
|
||||
<span class="adf-userinfo__secondary-info" id="ecm-job-title-label">
|
||||
{{ 'USER_PROFILE.LABELS.ECM.JOB_TITLE' | translate }}
|
||||
<span id="ecm-job-title"
|
||||
class="adf-userinfo__detail-profile"> {{ ecmUser.jobTitle ? ecmUser.jobTitle : 'N/A' }} </span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
</mat-tab>
|
||||
<mat-tab id="bpm-panel" label="{{ 'USER_PROFILE.TAB.PS' | translate }}" role="dialog"
|
||||
*ngIf="mode===userInfoMode.PROCESS || mode===userInfoMode.ALL">
|
||||
<mat-card class="adf-userinfo-card">
|
||||
<mat-card-header class="adf-userinfo-card-header"
|
||||
[style.background-image]="'url(' + bpmBackgroundImage + ')'">
|
||||
<img *ngIf="bpmUser.pictureId; else initialTemplate"
|
||||
class="adf-userinfo-profile-picture adf-hide-small" id="bpm-user-detail-image"
|
||||
alt="bpm-profile-image" [src]="getBpmUserImage()"/>
|
||||
<ng-template #initialTemplate>
|
||||
<div
|
||||
[outerHTML]="bpmUser | usernameInitials:'adf-userinfo-profile-initials adf-hide-small'"></div>
|
||||
</ng-template>
|
||||
<div class="adf-userinfo-title" id="bpm-username">{{bpmUser | fullName}}</div>
|
||||
</mat-card-header>
|
||||
<mat-card-content>
|
||||
<div class="adf-userinfo-supporting-text">
|
||||
<div class="adf-userinfo-detail">
|
||||
<span id="bpm-full-name"
|
||||
class="adf-userinfo__detail-title">{{ bpmUser | fullName }}</span>
|
||||
<span class="adf-userinfo__detail-profile" id="bpm-email"> {{bpmUser.email}} </span>
|
||||
</div>
|
||||
<div class="adf-userinfo-detail">
|
||||
<span id="bpm-tenant" class="adf-userinfo__secondary-info">
|
||||
{{ 'USER_PROFILE.LABELS.BPM.TENANT' | translate }}
|
||||
<span
|
||||
class="adf-userinfo__detail-profile">{{ bpmUser.tenantName ? bpmUser.tenantName : '' }}</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
</mat-tab>
|
||||
</mat-tab-group>
|
||||
</mat-menu>
|
||||
</div>
|
@@ -0,0 +1,188 @@
|
||||
.adf {
|
||||
&-userinfo-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 5px;
|
||||
}
|
||||
|
||||
&-userinfo-name-right {
|
||||
flex-direction: row-reverse;
|
||||
}
|
||||
|
||||
&-userinfo-name {
|
||||
padding: 0 5px;
|
||||
|
||||
@media screen and (max-width: 959px) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
&-userinfo-pic {
|
||||
background: var(--adf-user-info-color);
|
||||
display: inline-block;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 100px;
|
||||
text-align: center;
|
||||
font-weight: bolder;
|
||||
font-size: var(--theme-adf-picture-1-font-size);
|
||||
text-transform: uppercase;
|
||||
vertical-align: middle;
|
||||
line-height: 40px;
|
||||
}
|
||||
|
||||
&-userinfo-profile-image {
|
||||
background: var(--adf-user-info-color);
|
||||
text-align: center;
|
||||
border-radius: 90%;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
margin-right: 0;
|
||||
cursor: pointer;
|
||||
vertical-align: middle;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
&-userinfo-profile-container {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
&-userinfo-menu_button.mat-button {
|
||||
margin-right: 0;
|
||||
border-radius: 90%;
|
||||
padding: 0;
|
||||
min-width: 40px;
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
&-userinfo-tab .mat-tab-header {
|
||||
align-self: center;
|
||||
width: 100%;
|
||||
min-width: 250px;
|
||||
}
|
||||
|
||||
&-userinfo-tab .mat-tab-label {
|
||||
flex: auto;
|
||||
font-weight: 500;
|
||||
font-size: var(--theme-body-1-font-size);
|
||||
text-transform: uppercase;
|
||||
line-height: 48px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
&-userinfo-card-header {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
justify-content: stretch;
|
||||
line-height: normal;
|
||||
height: 100px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
&-userinfo-card.mat-card {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
&-userinfo-supporting-text {
|
||||
font-size: var(--theme-body-1-font-size);
|
||||
font-weight: 400;
|
||||
letter-spacing: 0;
|
||||
line-height: 18px;
|
||||
overflow: hidden;
|
||||
padding: 32px;
|
||||
column-count: 2;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
@media screen and (max-width: 599px) {
|
||||
padding: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
&-userinfo-title {
|
||||
font-size: var(--theme-title-font-size);
|
||||
}
|
||||
|
||||
&-userinfo__detail-profile {
|
||||
align-items: flex-start;
|
||||
font-size: var(--theme-body-1-font-size);
|
||||
font-weight: 400;
|
||||
letter-spacing: 0;
|
||||
line-height: 18px;
|
||||
display: block;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
&-userinfo__detail-title {
|
||||
text-overflow: ellipsis;
|
||||
font-size: var(--theme-subheading-2-font-size);
|
||||
font-weight: 400;
|
||||
letter-spacing: 0.04em;
|
||||
line-height: 20px;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
&-userinfo__secondary-info {
|
||||
font-size: var(--theme-body-1-font-size);
|
||||
font-weight: 400;
|
||||
letter-spacing: 0;
|
||||
line-height: 18px;
|
||||
align-items: flex-end;
|
||||
}
|
||||
|
||||
&-userinfo-profile-picture {
|
||||
background: var(--adf-user-info-color);
|
||||
background-size: cover;
|
||||
border-radius: 50%;
|
||||
height: 80px;
|
||||
width: 80px;
|
||||
margin-left: 0;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
&-userinfo-profile-initials {
|
||||
text-transform: uppercase;
|
||||
background-size: cover;
|
||||
background-color: var(--adf-user-info-color);
|
||||
border-radius: 50%;
|
||||
height: 80px;
|
||||
width: 80px;
|
||||
margin-left: 0;
|
||||
margin-right: 8px;
|
||||
font-size: 35px;
|
||||
font-weight: 400;
|
||||
letter-spacing: 0;
|
||||
line-height: 78px;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
}
|
||||
|
||||
&-userinfo-button-profile {
|
||||
display: inline-block;
|
||||
border: 0;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
&-userinfo-detail {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
&-hide-tab .mat-tab-label-active {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (min-device-width: 480px) {
|
||||
.mat-menu-panel.adf-userinfo-menu {
|
||||
max-height: 450px;
|
||||
min-width: 450px;
|
||||
overflow: auto;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.mat-menu-panel.adf-userinfo-menu .mat-menu-content {
|
||||
padding: 0;
|
||||
}
|
@@ -0,0 +1,228 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2019 Alfresco Software, Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {
|
||||
CoreTestingModule,
|
||||
fakeBpmUser,
|
||||
BpmUserModel,
|
||||
setupTestBed,
|
||||
fakeEcmUser,
|
||||
fakeEcmUserNoImage,
|
||||
UserInfoMode
|
||||
} from '@alfresco/adf-core';
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { MatMenuModule } from '@angular/material/menu';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
|
||||
import { ProcessUserInfoComponent } from './process-user-info.component';
|
||||
|
||||
describe('ProcessUserInfoComponent', () => {
|
||||
const profilePictureUrl = 'alfresco-logo.svg';
|
||||
|
||||
let component: ProcessUserInfoComponent;
|
||||
let fixture: ComponentFixture<ProcessUserInfoComponent>;
|
||||
let element: HTMLElement;
|
||||
|
||||
const openUserInfo = () => {
|
||||
fixture.detectChanges();
|
||||
const imageButton = element.querySelector<HTMLButtonElement>('#logged-user-img');
|
||||
imageButton.click();
|
||||
fixture.detectChanges();
|
||||
};
|
||||
|
||||
const whenFixtureReady = async () => {
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
fixture.detectChanges();
|
||||
};
|
||||
|
||||
setupTestBed({
|
||||
imports: [
|
||||
TranslateModule.forRoot(),
|
||||
CoreTestingModule,
|
||||
MatMenuModule
|
||||
]
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(ProcessUserInfoComponent);
|
||||
component = fixture.componentInstance;
|
||||
element = fixture.nativeElement;
|
||||
|
||||
spyOn(window, 'requestAnimationFrame').and.returnValue(1);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
fixture.destroy();
|
||||
});
|
||||
|
||||
it('should not show any image if the user is not logged in', () => {
|
||||
expect(element.querySelector('#userinfo_container')).toBeDefined();
|
||||
expect(element.querySelector('#logged-user-img')).toBeNull();
|
||||
});
|
||||
|
||||
it('should NOT have users immediately after ngOnInit', () => {
|
||||
expect(element.querySelector('#userinfo_container')).toBeDefined();
|
||||
expect(element.querySelector('#ecm_username')).toBeNull();
|
||||
expect(element.querySelector('#bpm_username')).toBeNull();
|
||||
expect(element.querySelector('#user-profile-lists')).toBeNull();
|
||||
});
|
||||
|
||||
describe('when user is logged on bpm', () => {
|
||||
|
||||
beforeEach(async () => {
|
||||
component.bpmUser = fakeBpmUser;
|
||||
component.isLoggedIn = true;
|
||||
});
|
||||
|
||||
it('should show full name next the user image', async () => {
|
||||
await whenFixtureReady();
|
||||
openUserInfo();
|
||||
const bpmUserName = fixture.debugElement.query(By.css('#bpm-username'));
|
||||
expect(element.querySelector('#userinfo_container')).not.toBeNull();
|
||||
expect(bpmUserName).toBeDefined();
|
||||
expect(bpmUserName).not.toBeNull();
|
||||
expect(bpmUserName.nativeElement.innerHTML).toContain('fake-bpm-first-name fake-bpm-last-name');
|
||||
});
|
||||
|
||||
it('should get the bpm current user image from the service', async () => {
|
||||
spyOn(component, 'getBpmUserImage').and.returnValue(profilePictureUrl);
|
||||
await whenFixtureReady();
|
||||
expect(element.querySelector('#userinfo_container')).not.toBeNull();
|
||||
expect(element.querySelector('#logged-user-img')).not.toBeNull();
|
||||
expect(element.querySelector('#logged-user-img').getAttribute('src')).toContain(profilePictureUrl);
|
||||
});
|
||||
|
||||
it('should show last name if first name is null', async () => {
|
||||
const wrongBpmUser: BpmUserModel = new BpmUserModel({
|
||||
firstName: null,
|
||||
lastName: 'fake-last-name'
|
||||
});
|
||||
component.bpmUser = wrongBpmUser;
|
||||
await whenFixtureReady();
|
||||
const fullNameElement = (element.querySelector('#adf-userinfo-bpm-name-display'));
|
||||
fixture.detectChanges();
|
||||
expect(element.querySelector('#userinfo_container')).toBeDefined();
|
||||
expect(element.querySelector('#adf-userinfo-bpm-name-display')).not.toBeNull();
|
||||
expect(fullNameElement.textContent).toContain('fake-last-name');
|
||||
expect(fullNameElement.textContent).not.toContain('fake-first-name');
|
||||
});
|
||||
|
||||
it('should not show the tabs', async () => {
|
||||
await whenFixtureReady();
|
||||
openUserInfo();
|
||||
await fixture.whenStable();
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.query(By.css('#tab-group-env')).classes['adf-hide-tab']).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when user is logged on bpm and ecm', () => {
|
||||
|
||||
beforeEach(async () => {
|
||||
component.bpmUser = fakeBpmUser;
|
||||
component.ecmUser = fakeEcmUser as any;
|
||||
component.isLoggedIn = true;
|
||||
component.mode = UserInfoMode.ALL;
|
||||
});
|
||||
|
||||
it('should show the tabs', async () => {
|
||||
await whenFixtureReady();
|
||||
openUserInfo();
|
||||
await fixture.whenStable();
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.query(By.css('#tab-group-env')).classes['adf-hide-tab']).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should get the bpm user information', async () => {
|
||||
spyOn(component, 'getBpmUserImage').and.returnValue(profilePictureUrl);
|
||||
await whenFixtureReady();
|
||||
openUserInfo();
|
||||
const bpmTab = fixture.debugElement.queryAll(By.css('#tab-group-env .mat-tab-labels .mat-tab-label'))[1];
|
||||
bpmTab.triggerEventHandler('click', null);
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
const bpmUsername = fixture.debugElement.query(By.css('#bpm-username'));
|
||||
const bpmImage = fixture.debugElement.query(By.css('#bpm-user-detail-image'));
|
||||
expect(element.querySelector('#userinfo_container')).not.toBeNull();
|
||||
expect(bpmUsername).not.toBeNull();
|
||||
expect(bpmImage).not.toBeNull();
|
||||
expect(bpmImage.properties.src).toContain(profilePictureUrl);
|
||||
expect(bpmUsername.nativeElement.textContent).toContain('fake-bpm-first-name fake-bpm-last-name');
|
||||
expect(fixture.debugElement.query(By.css('#bpm-tenant')).nativeElement.textContent).toContain('fake-tenant-name');
|
||||
});
|
||||
|
||||
it('should get the ecm user information', async () => {
|
||||
spyOn(component, 'getEcmAvatar').and.returnValue(profilePictureUrl);
|
||||
await whenFixtureReady();
|
||||
openUserInfo();
|
||||
const ecmUsername = fixture.debugElement.query(By.css('#ecm-username'));
|
||||
const ecmImage = fixture.debugElement.query(By.css('#ecm-user-detail-image'));
|
||||
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
expect(element.querySelector('#userinfo_container')).toBeDefined();
|
||||
expect(ecmUsername).not.toBeNull();
|
||||
expect(ecmImage).not.toBeNull();
|
||||
expect(ecmImage.properties.src).toContain(profilePictureUrl);
|
||||
expect(fixture.debugElement.query(By.css('#ecm-full-name')).nativeElement.textContent).toContain('fake-ecm-first-name fake-ecm-last-name');
|
||||
expect(fixture.debugElement.query(By.css('#ecm-job-title')).nativeElement.textContent).toContain('job-ecm-test');
|
||||
});
|
||||
|
||||
it('should show the ecm image if exists', async () => {
|
||||
spyOn(component, 'getEcmAvatar').and.returnValue(profilePictureUrl);
|
||||
await whenFixtureReady();
|
||||
openUserInfo();
|
||||
expect(element.querySelector('#userinfo_container')).toBeDefined();
|
||||
expect(element.querySelector('#logged-user-img')).toBeDefined();
|
||||
expect(element.querySelector('#logged-user-img').getAttribute('src')).toEqual(profilePictureUrl);
|
||||
});
|
||||
|
||||
it('should show the ecm initials if the ecm user has no image', async () => {
|
||||
component.ecmUser = fakeEcmUserNoImage as any;
|
||||
await whenFixtureReady();
|
||||
|
||||
expect(element.querySelector('#userinfo_container')).toBeDefined();
|
||||
expect(element.querySelector('[data-automation-id="user-initials-image"]').textContent).toContain('ff');
|
||||
});
|
||||
|
||||
it('should show the tabs for the env', async () => {
|
||||
await whenFixtureReady();
|
||||
openUserInfo();
|
||||
const tabGroup = fixture.debugElement.query(By.css('#tab-group-env'));
|
||||
const tabs = fixture.debugElement.queryAll(By.css('#tab-group-env .mat-tab-labels .mat-tab-label'));
|
||||
|
||||
expect(tabGroup).not.toBeNull();
|
||||
expect(tabGroup.classes['adf-hide-tab']).toBeFalsy();
|
||||
expect(tabs.length).toBe(2);
|
||||
});
|
||||
|
||||
it('should not close the menu when a tab is clicked', async () => {
|
||||
await whenFixtureReady();
|
||||
openUserInfo();
|
||||
const tabGroup = fixture.debugElement.query(By.css('#tab-group-env'));
|
||||
|
||||
const tabs = fixture.debugElement.queryAll(By.css('#tab-group-env .mat-tab-labels .mat-tab-label'));
|
||||
|
||||
expect(tabGroup).not.toBeNull();
|
||||
tabs[1].triggerEventHandler('click', null);
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.query(By.css('#user-profile-lists'))).not.toBeNull();
|
||||
});
|
||||
});
|
||||
});
|
@@ -0,0 +1,112 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2019 Alfresco Software, Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { BpmUserModel, BpmUserService, EcmUserModel, PeopleContentService, UserInfoMode } from '@alfresco/adf-core';
|
||||
import { Component, Input, OnDestroy, ViewChild, ViewEncapsulation } from '@angular/core';
|
||||
import { MatMenuTrigger, MenuPositionX, MenuPositionY } from '@angular/material/menu';
|
||||
import { Subject } from 'rxjs';
|
||||
|
||||
@Component({
|
||||
selector: 'adf-process-user-info',
|
||||
templateUrl: './process-user-info.component.html',
|
||||
styleUrls: ['./process-user-info.component.scss'],
|
||||
encapsulation: ViewEncapsulation.None
|
||||
})
|
||||
export class ProcessUserInfoComponent implements OnDestroy {
|
||||
|
||||
@ViewChild(MatMenuTrigger) trigger: MatMenuTrigger;
|
||||
|
||||
@Input()
|
||||
isLoggedIn: boolean;
|
||||
|
||||
@Input()
|
||||
bpmUser: BpmUserModel;
|
||||
|
||||
@Input()
|
||||
ecmUser: EcmUserModel;
|
||||
|
||||
@Input()
|
||||
mode: UserInfoMode = UserInfoMode.PROCESS;
|
||||
|
||||
/** Custom path for the background banner image for APS users. */
|
||||
@Input()
|
||||
bpmBackgroundImage: string = './assets/images/bpm-background.png';
|
||||
|
||||
/** Custom path for the background banner image for ACS users. */
|
||||
@Input()
|
||||
ecmBackgroundImage: string = './assets/images/ecm-background.png';
|
||||
|
||||
/** Custom choice for opening the menu at the bottom. Can be `before` or `after`. */
|
||||
@Input()
|
||||
menuPositionX: MenuPositionX = 'after';
|
||||
|
||||
/** Custom choice for opening the menu at the bottom. Can be `above` or `below`. */
|
||||
@Input()
|
||||
menuPositionY: MenuPositionY = 'below';
|
||||
|
||||
/** Shows/hides the username next to the user info button. */
|
||||
@Input()
|
||||
showName: boolean = true;
|
||||
|
||||
/** When the username is shown, this defines its position relative to the user info button.
|
||||
* Can be `right` or `left`.
|
||||
*/
|
||||
@Input()
|
||||
namePosition: string = 'right';
|
||||
|
||||
userInfoMode = UserInfoMode;
|
||||
|
||||
private destroy$ = new Subject();
|
||||
|
||||
constructor(private bpmUserService: BpmUserService, private peopleContentService: PeopleContentService) {
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.destroy$.next(true);
|
||||
this.destroy$.complete();
|
||||
}
|
||||
|
||||
onKeyPress(event: KeyboardEvent) {
|
||||
this.closeUserModal(event);
|
||||
}
|
||||
|
||||
private closeUserModal($event: KeyboardEvent) {
|
||||
if ($event.keyCode === 27) {
|
||||
this.trigger.closeMenu();
|
||||
}
|
||||
}
|
||||
|
||||
stopClosing(event: Event) {
|
||||
event.stopPropagation();
|
||||
}
|
||||
|
||||
getBpmUserImage(): string {
|
||||
return this.bpmUserService.getCurrentUserProfileImage();
|
||||
}
|
||||
|
||||
getEcmAvatar(avatarId: string): string {
|
||||
return this.peopleContentService.getUserProfileImage(avatarId);
|
||||
}
|
||||
|
||||
get showOnRight(): boolean {
|
||||
return this.namePosition === 'right';
|
||||
}
|
||||
|
||||
get canShow(): boolean {
|
||||
return this.isLoggedIn && !!this.bpmUser && !!this.mode;
|
||||
}
|
||||
}
|
@@ -0,0 +1,41 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2019 Alfresco Software, Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { ProcessUserInfoComponent } from './process-user-info.component';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatCardModule } from '@angular/material/card';
|
||||
import { MatMenuModule } from '@angular/material/menu';
|
||||
import { MatTabsModule } from '@angular/material/tabs';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { PipeModule } from '@alfresco/adf-core';
|
||||
|
||||
@NgModule({
|
||||
declarations: [ProcessUserInfoComponent],
|
||||
imports: [
|
||||
CommonModule,
|
||||
MatButtonModule,
|
||||
MatMenuModule,
|
||||
MatTabsModule,
|
||||
MatCardModule,
|
||||
TranslateModule,
|
||||
PipeModule
|
||||
],
|
||||
exports: [ProcessUserInfoComponent]
|
||||
})
|
||||
export class ProcessUserInfoModule {}
|
20
lib/process-services/src/lib/process-user-info/public-api.ts
Normal file
20
lib/process-services/src/lib/process-user-info/public-api.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2019 Alfresco Software, Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export * from './process-user-info.component';
|
||||
|
||||
export * from './process-user-info.module';
|
@@ -32,6 +32,7 @@ import { FormModule } from './form/form.module';
|
||||
import { ProcessFormRenderingService } from './form/process-form-rendering.service';
|
||||
import { ProcessServicesPipeModule } from './pipes/process-services-pipe.module';
|
||||
import { TaskCommentsModule } from './task-comments/task-comments.module';
|
||||
import { ProcessUserInfoModule } from './process-user-info/process-user-info.module';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
@@ -45,6 +46,7 @@ import { TaskCommentsModule } from './task-comments/task-comments.module';
|
||||
TaskListModule,
|
||||
TaskCommentsModule,
|
||||
AppsListModule,
|
||||
ProcessUserInfoModule,
|
||||
AttachmentModule,
|
||||
PeopleModule,
|
||||
FormModule,
|
||||
@@ -69,6 +71,7 @@ import { TaskCommentsModule } from './task-comments/task-comments.module';
|
||||
TaskListModule,
|
||||
TaskCommentsModule,
|
||||
AppsListModule,
|
||||
ProcessUserInfoModule,
|
||||
AttachmentModule,
|
||||
PeopleModule,
|
||||
FormModule,
|
||||
|
@@ -19,6 +19,7 @@ export * from './lib/process-list/index';
|
||||
export * from './lib/task-list/index';
|
||||
export * from './lib/app-list/index';
|
||||
export * from './lib/attachment/index';
|
||||
export * from './lib/process-user-info/index';
|
||||
export * from './lib/process-comments/index';
|
||||
export * from './lib/people/index';
|
||||
export * from './lib/form/index';
|
||||
|
Reference in New Issue
Block a user