diff --git a/e2e/suites/list-views/trash.test.ts b/e2e/suites/list-views/trash.test.ts index 8af8827bf..df378e765 100755 --- a/e2e/suites/list-views/trash.test.ts +++ b/e2e/suites/list-views/trash.test.ts @@ -132,10 +132,10 @@ describe('Trash', () => { }); it('has the correct columns', () => { - const labels = [ 'Name', 'Location', 'Size', 'Deleted', 'Deleted by' ]; + const labels = [ 'Name', 'Location', 'Size', 'Deleted']; const elements = labels.map(label => dataTable.getColumnHeaderByLabel(label)); - expect(dataTable.getColumnHeaders().count()).toBe(5 + 1, 'Incorrect number of columns'); + expect(dataTable.getColumnHeaders().count()).toBe(4 + 1, 'Incorrect number of columns'); elements.forEach((element, index) => { expect(element.isPresent()).toBe(true, `"${labels[index]}" is missing`); diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 2a7127cb7..d3b59831a 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -70,6 +70,7 @@ import { SearchComponent } from './components/search/search.component'; import { SettingsComponent } from './components/settings/settings.component'; import { HybridAppConfigService } from './common/services/hybrid-app-config.service'; import { PageTitleService as AcaPageTitleService } from './common/services/page-title.service'; +import { ProfileResolver } from './common/services/profile.resolver'; import { InfoDrawerComponent } from './components/info-drawer/info-drawer.component'; import { EditFolderDirective } from './directives/edit-folder.directive'; @@ -148,7 +149,8 @@ import { MaterialModule } from './material.module'; BrowsingFilesService, ContentManagementService, NodeActionsService, - NodePermissionService + NodePermissionService, + ProfileResolver ], entryComponents: [ NodeVersionsDialogComponent diff --git a/src/app/app.routes.ts b/src/app/app.routes.ts index 595fe7654..0a4b610a0 100644 --- a/src/app/app.routes.ts +++ b/src/app/app.routes.ts @@ -42,6 +42,8 @@ import { GenericErrorComponent } from './components/generic-error/generic-error. import { SearchComponent } from './components/search/search.component'; import { SettingsComponent } from './components/settings/settings.component'; +import { ProfileResolver } from './common/services/profile.resolver'; + export const APP_ROUTES: Routes = [ { path: 'login', @@ -60,6 +62,7 @@ export const APP_ROUTES: Routes = [ { path: '', component: LayoutComponent, + resolve: { profile: ProfileResolver }, children: [ { path: '', diff --git a/src/app/common/services/profile.resolver.ts b/src/app/common/services/profile.resolver.ts new file mode 100644 index 000000000..ec169d83d --- /dev/null +++ b/src/app/common/services/profile.resolver.ts @@ -0,0 +1,56 @@ +/*! + * @license + * Alfresco Example Content Application + * + * Copyright (C) 2005 - 2018 Alfresco Software Limited + * + * This file is part of the Alfresco Example Content Application. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * The Alfresco Example Content Application is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The Alfresco Example Content Application is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ + +import { Store } from '@ngrx/store'; +import { Injectable } from '@angular/core'; +import { Resolve } from '@angular/router'; +import { Observable } from 'rxjs/Observable'; + +import { AppStore } from '../../store/states/app.state'; +import { SetUserAction } from '../../store/actions/user.actions'; +import { selectUser } from '../../store/selectors/app.selectors'; +import { PeopleContentService } from '@alfresco/adf-core'; + +@Injectable() +export class ProfileResolver implements Resolve { + constructor(private store: Store, private peopleApi: PeopleContentService) { } + + resolve(): Observable { + + this.init(); + + return this.profileLoaded(); + } + + profileLoaded(): Observable { + return this.store.select(selectUser).take(1); + } + + init(): void { + this.peopleApi.getCurrentPerson().subscribe((person: any) => { + this.store.dispatch(new SetUserAction(person.entry)); + }); + } +} diff --git a/src/app/components/current-user/current-user.component.html b/src/app/components/current-user/current-user.component.html index c2eeef62e..17d9c756d 100644 --- a/src/app/components/current-user/current-user.component.html +++ b/src/app/components/current-user/current-user.component.html @@ -1,9 +1,9 @@
-
{{ userName }}
+
{{ user?.userName }}
- {{ userInitials }} + {{ user?.initials }}
diff --git a/src/app/components/current-user/current-user.component.spec.ts b/src/app/components/current-user/current-user.component.spec.ts deleted file mode 100644 index 88f025232..000000000 --- a/src/app/components/current-user/current-user.component.spec.ts +++ /dev/null @@ -1,90 +0,0 @@ -/*! - * @license - * Alfresco Example Content Application - * - * Copyright (C) 2005 - 2018 Alfresco Software Limited - * - * This file is part of the Alfresco Example Content Application. - * If the software was purchased under a paid Alfresco license, the terms of - * the paid license agreement will prevail. Otherwise, the software is - * provided under the following open source license terms: - * - * The Alfresco Example Content Application is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * The Alfresco Example Content Application is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Alfresco. If not, see . - */ - -import { NO_ERRORS_SCHEMA } from '@angular/core'; -import { TestBed, async } from '@angular/core/testing'; -import { Observable } from 'rxjs/Rx'; -import { MatMenuModule } from '@angular/material'; -import { - AlfrescoApiService, - AppConfigService, - StorageService, - PeopleContentService, - UserPreferencesService, - AppConfigPipe - } from '@alfresco/adf-core'; - -import { CurrentUserComponent } from './current-user.component'; -import { AppTestingModule } from '../../testing/app-testing.module'; - -describe('CurrentUserComponent', () => { - let fixture; - let component; - let peopleApi: PeopleContentService; - let user; - - beforeEach(() => { - user = { entry: { firstName: 'joe', lastName: 'doe' } }; - }); - - beforeEach(async(() => { - TestBed.configureTestingModule({ - imports: [ - AppTestingModule, - MatMenuModule - ], - declarations: [ - CurrentUserComponent, - AppConfigPipe - ], - providers: [ - AlfrescoApiService, - AppConfigService, - StorageService, - PeopleContentService, - UserPreferencesService - ], - schemas: [ NO_ERRORS_SCHEMA ] - }) - .compileComponents() - .then(() => { - fixture = TestBed.createComponent(CurrentUserComponent); - component = fixture.componentInstance; - peopleApi = TestBed.get(PeopleContentService); - - spyOn(peopleApi, 'getCurrentPerson').and.returnValue(Observable.of(user)); - - fixture.detectChanges(); - }); - })); - - it('updates user data', () => { - expect(component.user).toBe(user.entry); - }); - - it('get user initials', () => { - expect(component.userInitials).toBe('jd'); - }); -}); diff --git a/src/app/components/current-user/current-user.component.ts b/src/app/components/current-user/current-user.component.ts index 996646cf5..014207cc5 100644 --- a/src/app/components/current-user/current-user.component.ts +++ b/src/app/components/current-user/current-user.component.ts @@ -24,9 +24,13 @@ */ import { Component, OnInit, OnDestroy, ViewEncapsulation } from '@angular/core'; -import { PeopleContentService } from '@alfresco/adf-core'; import { Subscription } from 'rxjs/Rx'; +import { Store } from '@ngrx/store'; +import { AppStore } from '../../store/states/app.state'; +import { selectUser } from '../../store/selectors/app.selectors'; +import { ProfileState } from '../../store/states/profile.state'; + @Component({ selector: 'aca-current-user', templateUrl: './current-user.component.html', @@ -36,39 +40,17 @@ import { Subscription } from 'rxjs/Rx'; export class CurrentUserComponent implements OnInit, OnDestroy { private subscriptions: Subscription[] = []; - user: any = null; + user: ProfileState; - constructor( - private peopleApi: PeopleContentService - ) {} + constructor(private store: Store) {} ngOnInit() { this.subscriptions = this.subscriptions.concat([ - this.peopleApi.getCurrentPerson().subscribe((person: any) => this.user = person.entry) + this.store.select(selectUser).subscribe((user) => this.user = user) ]); } ngOnDestroy() { this.subscriptions.forEach(s => s.unsubscribe()); } - - get userFirstName(): string { - const { user } = this; - return user ? (user.firstName || '') : ''; - } - - get userLastName(): string { - const { user } = this; - return user ? (user.lastName || '') : ''; - } - - get userName(): string { - const { userFirstName: first, userLastName: last } = this; - return `${first} ${last}`; - } - - get userInitials(): string { - const { userFirstName: first, userLastName: last } = this; - return [ first[0], last[0] ].join(''); - } } diff --git a/src/app/components/trashcan/trashcan.component.html b/src/app/components/trashcan/trashcan.component.html index 6d081f8d6..b9a6700d1 100644 --- a/src/app/components/trashcan/trashcan.component.html +++ b/src/app/components/trashcan/trashcan.component.html @@ -82,7 +82,7 @@ diff --git a/src/app/components/trashcan/trashcan.component.ts b/src/app/components/trashcan/trashcan.component.ts index b914eeab9..f754cf3f4 100644 --- a/src/app/components/trashcan/trashcan.component.ts +++ b/src/app/components/trashcan/trashcan.component.ts @@ -24,20 +24,20 @@ */ import { Component, OnInit } from '@angular/core'; -import { PeopleContentService } from '@alfresco/adf-core'; import { ContentManagementService } from '../../common/services/content-management.service'; import { PageComponent } from '../page.component'; import { Store } from '@ngrx/store'; +import { selectUser } from '../../store/selectors/app.selectors'; import { AppStore } from '../../store/states/app.state'; +import { ProfileState } from '../../store/states/profile.state'; @Component({ templateUrl: './trashcan.component.html' }) export class TrashcanComponent extends PageComponent implements OnInit { - userIsAdmin: boolean; + user: ProfileState; constructor(private contentManagementService: ContentManagementService, - private peopleApi: PeopleContentService, store: Store) { super(store); } @@ -49,15 +49,7 @@ export class TrashcanComponent extends PageComponent implements OnInit { this.contentManagementService.nodesRestored.subscribe(() => this.reload()), this.contentManagementService.nodesPurged.subscribe(() => this.reload()), this.contentManagementService.nodesRestored.subscribe(() => this.reload()), - this.peopleApi.getCurrentPerson().subscribe((user: any) => this.isUserAdmin(user)) + this.store.select(selectUser).subscribe((user) => this.user = user) ); } - - private isUserAdmin(user) { - if (user && user.capabilities) { - this.userIsAdmin = user.capabilities.isAdmin; - } else { - this.userIsAdmin = true; - } - } } diff --git a/src/app/store/actions.ts b/src/app/store/actions.ts index d3cec5741..cb5ff1eee 100644 --- a/src/app/store/actions.ts +++ b/src/app/store/actions.ts @@ -29,3 +29,4 @@ export * from './actions/snackbar.actions'; export * from './actions/router.actions'; export * from './actions/viewer.actions'; export * from './actions/search.actions'; +export * from './actions/user.actions'; diff --git a/src/app/store/actions/user.actions.ts b/src/app/store/actions/user.actions.ts new file mode 100644 index 000000000..447bf6c39 --- /dev/null +++ b/src/app/store/actions/user.actions.ts @@ -0,0 +1,33 @@ +/*! + * @license + * Alfresco Example Content Application + * + * Copyright (C) 2005 - 2018 Alfresco Software Limited + * + * This file is part of the Alfresco Example Content Application. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * The Alfresco Example Content Application is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The Alfresco Example Content Application is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ + +import { Action } from '@ngrx/store'; + +export const SET_USER = 'SET_USER'; + +export class SetUserAction implements Action { + readonly type = SET_USER; + constructor(public payload: any) { } +} diff --git a/src/app/store/reducers/app.reducer.ts b/src/app/store/reducers/app.reducer.ts index 19b1d3f3e..03e736edc 100644 --- a/src/app/store/reducers/app.reducer.ts +++ b/src/app/store/reducers/app.reducer.ts @@ -33,7 +33,9 @@ import { SET_LOGO_PATH, SetLogoPathAction, SET_SELECTED_NODES, - SetSelectedNodesAction + SetSelectedNodesAction, + SET_USER, + SetUserAction } from '../actions'; export function appReducer( @@ -57,6 +59,11 @@ export function appReducer( action )); break; + case SET_USER: + newState = updateUser(state, ( + action + )); + break; default: newState = Object.assign({}, state); } @@ -85,6 +92,29 @@ function updateLogoPath(state: AppState, action: SetLogoPathAction): AppState { return newState; } +function updateUser(state: AppState, action: SetUserAction): AppState { + const newState = Object.assign({}, state); + const user = action.payload; + + const id = user.id; + const firstName = user.firstName || ''; + const lastName = user.lastName || ''; + const userName = `${firstName} ${lastName}`; + const initials = [ firstName[0], lastName[0] ].join(''); + const isAdmin = user.capabilities ? user.capabilities.isAdmin : true; + + newState.user = { + firstName, + lastName, + userName, + initials, + isAdmin, + id + }; + + return newState; +} + function updateSelectedNodes( state: AppState, action: SetSelectedNodesAction diff --git a/src/app/store/selectors/app.selectors.ts b/src/app/store/selectors/app.selectors.ts index 5d7a7cc64..c5ce779e0 100644 --- a/src/app/store/selectors/app.selectors.ts +++ b/src/app/store/selectors/app.selectors.ts @@ -31,3 +31,4 @@ export const selectHeaderColor = createSelector(selectApp, (state: AppState) => export const selectAppName = createSelector(selectApp, (state: AppState) => state.appName); export const selectLogoPath = createSelector(selectApp, (state: AppState) => state.logoPath); export const appSelection = createSelector(selectApp, (state: AppState) => state.selection); +export const selectUser = createSelector(selectApp, (state: AppState) => state.user); diff --git a/src/app/store/states/app.state.ts b/src/app/store/states/app.state.ts index c10145d21..46848f894 100644 --- a/src/app/store/states/app.state.ts +++ b/src/app/store/states/app.state.ts @@ -24,18 +24,26 @@ */ import { SelectionState } from './selection.state'; +import { ProfileState } from './profile.state'; export interface AppState { appName: string; headerColor: string; logoPath: string; selection: SelectionState; + user: ProfileState; } export const INITIAL_APP_STATE: AppState = { appName: 'Alfresco Example Content Application', headerColor: '#2196F3', logoPath: 'assets/images/alfresco-logo-white.svg', + user: { + isAdmin: true, // 5.2.x + id: null, + firstName: '', + lastName: '' + }, selection: { nodes: [], isEmpty: true, diff --git a/src/app/store/states/profile.state.ts b/src/app/store/states/profile.state.ts new file mode 100644 index 000000000..2113d51c9 --- /dev/null +++ b/src/app/store/states/profile.state.ts @@ -0,0 +1,33 @@ +/*! + * @license + * Alfresco Example Content Application + * + * Copyright (C) 2005 - 2018 Alfresco Software Limited + * + * This file is part of the Alfresco Example Content Application. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * The Alfresco Example Content Application is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The Alfresco Example Content Application is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ + +export interface ProfileState { + id: string; + isAdmin: boolean; + firstName: string; + lastName: string; + userName?: string; + initials?: string; +}