[AAE-10533] Generic App shell for HxP applications (#2679)

* [AAE-10533] Generic App shell for HxP applications

* refactor

* fix scss mixin path

* remove forRoot in content-plugin

* remove provided routers

* revert router service

* revert template usage

* Added shell markdown

* Move login component to content-plugin

* Moved logic from app.component to app.service

* remove upload-area from shell

* cleaning

* cleaning

* update md

* abstract preferences

* allow to set shell parent route

* fix preferencesService name

* Fix for sidenav

* Fix CR comments

* [ci:force]

* move translation service mock to aca-shared

* fix e2e

* Fix page title

* remove drop area wrapper from whole application

* Fix e2e

* [ci:force]

* Remove blank page from shell

* Add upload files dialog

* [ci:force]

* Remove ExtensionsDataLoaderGuard from shell
This commit is contained in:
Bartosz Sekuła 2022-11-23 14:45:32 +01:00 committed by GitHub
parent 3650aff589
commit 456454fee1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
271 changed files with 1959 additions and 1167 deletions

View File

@ -36,6 +36,7 @@
"stylePreprocessorOptions": {
"includePaths": [
"app/src/app/ui",
"app/src/app/content-plugin/ui",
"node_modules"
]
},
@ -252,7 +253,7 @@
"polyfills": "app/src/polyfills.ts",
"stylePreprocessorOptions": {
"includePaths": [
"app/src/app/ui",
"app/src/app/content-plugin/ui",
"node_modules"
]
},

View File

@ -0,0 +1,72 @@
/*
* Copyright © 2005 - 2021 Alfresco Software, Ltd. All rights reserved.
*
* License rights for this program may be obtained from Alfresco Software, Ltd.
* pursuant to a written agreement and any use of this program without such an
* agreement is prohibited.
*/
import { CommonModule } from '@angular/common';
import { ModuleWithProviders, NgModule } from '@angular/core';
import { Routes, provideRoutes, RouterModule, Route } from '@angular/router';
import { SHELL_LAYOUT_ROUTE } from './app-shell.routes';
import { SidenavLayoutModule } from '@alfresco/adf-core';
import { ExtensionsModule } from '@alfresco/adf-extensions';
import { ShellLayoutComponent } from './components/shell/shell.component';
export interface AppShellRoutesConfig {
shellParentRoute?: Route;
shellChildren: Routes;
}
@NgModule({
imports: [SidenavLayoutModule, ExtensionsModule, RouterModule.forChild([]), CommonModule],
exports: [ShellLayoutComponent],
declarations: [ShellLayoutComponent]
})
export class AppShellModule {
static withRoutes(routes: Routes | AppShellRoutesConfig): ModuleWithProviders<AppShellModule> {
if (Array.isArray(routes)) {
return getModuleForRoutes(routes);
}
return getModuleForRouteConfig(routes);
}
}
function getModuleForRoutes(routes: Routes): ModuleWithProviders<AppShellModule> {
const shellLayoutRoute = SHELL_LAYOUT_ROUTE;
routes.forEach((childRoute) => {
shellLayoutRoute.children.push(childRoute);
});
return {
ngModule: AppShellModule,
providers: provideRoutes([shellLayoutRoute])
};
}
function getModuleForRouteConfig(config: AppShellRoutesConfig): ModuleWithProviders<AppShellModule> {
const shellLayoutRoute = SHELL_LAYOUT_ROUTE;
const shellParentRoute = config.shellParentRoute;
const shellChildrenRoutes = config.shellChildren;
shellLayoutRoute.children.push(...shellChildrenRoutes);
const rootRoute = shellParentRoute ? shellParentRoute : shellLayoutRoute;
if (config.shellParentRoute) {
if (rootRoute.children === undefined) {
rootRoute.children = [];
}
rootRoute.children.push(shellLayoutRoute);
}
return {
ngModule: AppShellModule,
providers: provideRoutes([rootRoute])
};
}

View File

@ -0,0 +1,20 @@
/*
* Copyright © 2005 - 2021 Alfresco Software, Ltd. All rights reserved.
*
* License rights for this program may be obtained from Alfresco Software, Ltd.
* pursuant to a written agreement and any use of this program without such an
* agreement is prohibited.
*/
import { InjectionToken } from '@angular/core';
import { CanActivate, CanActivateChild, Route } from '@angular/router';
import { ShellLayoutComponent } from './components/shell/shell.component';
export const SHELL_AUTH_TOKEN = new InjectionToken<CanActivate & CanActivateChild>('SHELL_AUTH_TOKEN');
export const SHELL_LAYOUT_ROUTE: Route = {
path: '',
component: ShellLayoutComponent,
canActivate: [SHELL_AUTH_TOKEN],
children: []
};

View File

@ -0,0 +1,48 @@
<adf-sidenav-layout
#layout
[sidenavMin]="70"
[sidenavMax]="320"
[stepOver]="600"
[hideSidenav]="hideSidenav"
[expandedSidenav]="expandedSidenav"
(expanded)="onExpanded($event)"
>
<adf-sidenav-layout-header>
<ng-template let-isMenuMinimized="isMenuMinimized">
<div
role="heading"
aria-level="1"
*ngIf="!hideSidenav"
>
<adf-dynamic-component id="app.layout.header" [data]="{ layout }">
</adf-dynamic-component>
</div>
</ng-template>
</adf-sidenav-layout-header>
<adf-sidenav-layout-navigation>
<ng-template let-isMenuMinimized="isMenuMinimized">
<div
(swipeleft)="hideMenu($event)"
[attr.data-automation-id]="isMenuMinimized() ? 'collapsed' : 'expanded'"
>
<adf-dynamic-component
id="app.layout.sidenav"
[data]="{ mode: layout.isMenuMinimized ? 'collapsed' : 'expanded'}"
>
</adf-dynamic-component>
</div>
</ng-template>
</adf-sidenav-layout-navigation>
<adf-sidenav-layout-content>
<ng-template>
<router-outlet></router-outlet>
</ng-template>
</adf-sidenav-layout-content>
</adf-sidenav-layout>
<adf-dynamic-component id="app.shell.sibling">
</adf-dynamic-component>
<router-outlet name="viewer"></router-outlet>

View File

@ -0,0 +1,34 @@
.app-shell {
display: flex;
flex-direction: column;
flex: 1;
height: 100%;
overflow: hidden;
min-height: 0;
router-outlet[name='viewer'] + * {
width: 100%;
height: 100%;
z-index: 999;
position: absolute;
top: 0;
right: 0;
background-color: white;
}
adf-file-uploading-dialog {
z-index: 1000;
}
}
@media screen and (max-width: 599px) {
.adf-app-title {
display: none;
}
}
@media screen and (max-width: 719px) {
.adf-app-logo {
display: none;
}
}

View File

@ -0,0 +1,169 @@
/*
* Copyright © 2005 - 2021 Alfresco Software, Ltd. All rights reserved.
*
* License rights for this program may be obtained from Alfresco Software, Ltd.
* pursuant to a written agreement and any use of this program without such an
* agreement is prohibited.
*/
import { NO_ERRORS_SCHEMA } from '@angular/core';
import { TestBed, ComponentFixture } from '@angular/core/testing';
import { AppConfigService, SidenavLayoutModule } from '@alfresco/adf-core';
import { ShellLayoutComponent } from './shell.component';
import { Store } from '@ngrx/store';
import { Router, NavigationStart, RouterModule } from '@angular/router';
import { of, Subject } from 'rxjs';
import { ExtensionsModule } from '@alfresco/adf-extensions';
import { CommonModule } from '@angular/common';
import { ShellAppService, SHELL_APP_SERVICE } from '../../services/shell-app.service';
import { HttpClientModule } from '@angular/common/http';
import { TranslateService } from '@ngx-translate/core';
import { TranslateServiceMock } from '@alfresco/aca-shared';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
class MockRouter {
private url = 'some-url';
private subject = new Subject();
events = this.subject.asObservable();
routerState = { snapshot: { url: this.url } };
navigateByUrl(url: string) {
const navigationStart = new NavigationStart(0, url);
this.subject.next(navigationStart);
}
}
describe('AppLayoutComponent', () => {
let fixture: ComponentFixture<ShellLayoutComponent>;
let component: ShellLayoutComponent;
let appConfig: AppConfigService;
let shellAppService: ShellAppService;
beforeEach(() => {
const shellService: ShellAppService = {
pageHeading$: of('Title'),
hideSidenavConditions: [],
minimizeSidenavConditions: [],
preferencesService: {
get: (_key: string) => 'true',
set: (_key: string, _value: any) => {}
}
};
TestBed.configureTestingModule({
imports: [CommonModule, NoopAnimationsModule, HttpClientModule, SidenavLayoutModule, ExtensionsModule, RouterModule.forChild([])],
providers: [
Store,
{
provide: Router,
useClass: MockRouter
},
{
provide: SHELL_APP_SERVICE,
useValue: shellService
},
{ provide: TranslateService, useClass: TranslateServiceMock }
],
declarations: [ShellLayoutComponent],
schemas: [NO_ERRORS_SCHEMA]
});
fixture = TestBed.createComponent(ShellLayoutComponent);
component = fixture.componentInstance;
appConfig = TestBed.inject(AppConfigService);
shellAppService = TestBed.inject(SHELL_APP_SERVICE);
});
beforeEach(() => {
appConfig.config.languages = [];
appConfig.config.locale = 'en';
});
describe('sidenav state', () => {
it('should get state from configuration', () => {
appConfig.config.sideNav = {
expandedSidenav: false,
preserveState: false
};
fixture.detectChanges();
expect(component.expandedSidenav).toBe(false);
});
it('should resolve state to true is no configuration', () => {
appConfig.config.sidenav = {};
fixture.detectChanges();
expect(component.expandedSidenav).toBe(true);
});
it('should get state from user settings as true', () => {
appConfig.config.sideNav = {
expandedSidenav: false,
preserveState: true
};
spyOn(shellAppService.preferencesService, 'get').and.callFake((key) => {
if (key === 'expandedSidenav') {
return 'true';
}
return 'false';
});
fixture.detectChanges();
expect(component.expandedSidenav).toBe(true);
});
it('should get state from user settings as false', () => {
appConfig.config.sidenav = {
expandedSidenav: false,
preserveState: true
};
spyOn(shellAppService.preferencesService, 'get').and.callFake((key) => {
if (key === 'expandedSidenav') {
return 'false';
}
return 'true';
});
fixture.detectChanges();
expect(component.expandedSidenav).toBe(false);
});
});
it('should close menu on mobile screen size', () => {
component.minimizeSidenav = false;
component.layout.container = {
isMobileScreenSize: true,
toggleMenu: () => {}
};
spyOn(component.layout.container, 'toggleMenu');
fixture.detectChanges();
component.hideMenu({ preventDefault: () => {} } as any);
expect(component.layout.container.toggleMenu).toHaveBeenCalled();
});
it('should close menu on mobile screen size also when minimizeSidenav true', () => {
fixture.detectChanges();
component.minimizeSidenav = true;
component.layout.container = {
isMobileScreenSize: true,
toggleMenu: () => {}
};
spyOn(component.layout.container, 'toggleMenu');
fixture.detectChanges();
component.hideMenu({ preventDefault: () => {} } as any);
expect(component.layout.container.toggleMenu).toHaveBeenCalled();
});
});

View File

@ -0,0 +1,123 @@
/*
* Copyright © 2005 - 2021 Alfresco Software, Ltd. All rights reserved.
*
* License rights for this program may be obtained from Alfresco Software, Ltd.
* pursuant to a written agreement and any use of this program without such an
* agreement is prohibited.
*/
import { AppConfigService, SidenavLayoutComponent } from '@alfresco/adf-core';
import { Component, Inject, OnDestroy, OnInit, ViewChild, ViewEncapsulation } from '@angular/core';
import { NavigationEnd, Router } from '@angular/router';
import { Subject, Observable } from 'rxjs';
import { filter, takeUntil, map, withLatestFrom } from 'rxjs/operators';
import { BreakpointObserver } from '@angular/cdk/layout';
import { Directionality } from '@angular/cdk/bidi';
import { SHELL_APP_SERVICE, ShellAppService } from '../../services/shell-app.service';
@Component({
selector: 'app-shell',
templateUrl: './shell.component.html',
styleUrls: ['./shell.component.scss'],
encapsulation: ViewEncapsulation.None,
host: { class: 'app-shell' }
})
export class ShellLayoutComponent implements OnInit, OnDestroy {
@ViewChild('layout', { static: true })
layout: SidenavLayoutComponent;
onDestroy$: Subject<boolean> = new Subject<boolean>();
isSmallScreen$: Observable<boolean>;
expandedSidenav: boolean;
minimizeSidenav = false;
hideSidenav = false;
direction: Directionality;
constructor(
private router: Router,
private appConfigService: AppConfigService,
private breakpointObserver: BreakpointObserver,
@Inject(SHELL_APP_SERVICE) private shellService: ShellAppService
) {}
ngOnInit() {
this.isSmallScreen$ = this.breakpointObserver.observe(['(max-width: 600px)']).pipe(map((result) => result.matches));
this.hideSidenav = this.shellService.hideSidenavConditions.some((el) => this.router.routerState.snapshot.url.includes(el));
this.minimizeSidenav = this.shellService.minimizeSidenavConditions.some((el) => this.router.routerState.snapshot.url.includes(el));
if (!this.minimizeSidenav) {
this.expandedSidenav = this.getSidenavState();
} else {
this.expandedSidenav = false;
}
this.router.events
.pipe(
withLatestFrom(this.isSmallScreen$),
filter(([event, isSmallScreen]) => isSmallScreen && event instanceof NavigationEnd),
takeUntil(this.onDestroy$)
)
.subscribe(() => {
this.layout.container.sidenav.close();
});
this.router.events
.pipe(
filter((event) => event instanceof NavigationEnd),
takeUntil(this.onDestroy$)
)
.subscribe((event: NavigationEnd) => {
this.minimizeSidenav = this.shellService.minimizeSidenavConditions.some((el) => event.urlAfterRedirects.includes(el));
this.hideSidenav = this.shellService.hideSidenavConditions.some((el) => event.urlAfterRedirects.includes(el));
this.updateState();
});
}
ngOnDestroy() {
this.onDestroy$.next(true);
this.onDestroy$.complete();
}
hideMenu(event: Event) {
if (this.layout.container.isMobileScreenSize) {
event.preventDefault();
this.layout.container.toggleMenu();
}
}
private updateState() {
if (this.minimizeSidenav && !this.layout.isMenuMinimized) {
this.layout.isMenuMinimized = true;
if (!this.layout.container.isMobileScreenSize) {
this.layout.container.toggleMenu();
}
}
if (!this.minimizeSidenav) {
if (this.getSidenavState() && this.layout.isMenuMinimized) {
this.layout.isMenuMinimized = false;
this.layout.container.toggleMenu();
}
}
}
onExpanded(state: boolean) {
if (!this.minimizeSidenav && this.appConfigService.get('sideNav.preserveState')) {
this.shellService.preferencesService.set('expandedSidenav', state);
}
}
private getSidenavState(): boolean {
const expand = this.appConfigService.get<boolean>('sideNav.expandedSidenav', true);
const preserveState = this.appConfigService.get<boolean>('sideNav.preserveState', true);
if (preserveState) {
return this.shellService.preferencesService.get('expandedSidenav', expand.toString()) === 'true';
}
return expand;
}
}

View File

@ -0,0 +1,10 @@
/*
* Copyright © 2005 - 2021 Alfresco Software, Ltd. All rights reserved.
*
* License rights for this program may be obtained from Alfresco Software, Ltd.
* pursuant to a written agreement and any use of this program without such an
* agreement is prohibited.
*/
export * from './app-shell.module';
export * from './services/shell-app.service';

View File

@ -0,0 +1,24 @@
/*
* Copyright © 2005 - 2021 Alfresco Software, Ltd. All rights reserved.
*
* License rights for this program may be obtained from Alfresco Software, Ltd.
* pursuant to a written agreement and any use of this program without such an
* agreement is prohibited.
*/
import { InjectionToken } from '@angular/core';
import { Observable } from 'rxjs';
export interface ShellPreferencesService {
set(preferenceKey: string, value: any): void;
get(preferenceKey: string, defaultValue: string): string;
}
export interface ShellAppService {
pageHeading$: Observable<string>;
hideSidenavConditions: string[];
minimizeSidenavConditions: string[];
preferencesService: ShellPreferencesService;
}
export const SHELL_APP_SERVICE = new InjectionToken<ShellAppService>('SHELL_APP_SERVICE');

View File

@ -1,2 +1,2 @@
<h1 class="sr-only" title="{{pageHeading | translate}}">{{ pageHeading | translate }}</h1>
<h1 class="sr-only" title="{{pageHeading | async | translate}}">{{ pageHeading | async | translate }}</h1>
<router-outlet></router-outlet>

View File

@ -1,105 +0,0 @@
/*!
* @license
* Alfresco Example Content Application
*
* Copyright (C) 2005 - 2020 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 <http://www.gnu.org/licenses/>.
*/
import { AppComponent } from './app.component';
import { SetInitialStateAction } from '@alfresco/aca-shared/store';
import { Router } from '@angular/router';
import { TestBed } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
describe('AppComponent', () => {
let component: AppComponent;
let router: Router;
const storeMock: any = {
dispatch: jasmine.createSpy('dispatch')
};
const overlayContainerMock: any = {
getContainerElement: jasmine.createSpy('getContainerElement')
};
const configMock: any = {
get: (key: string) => {
if (key === 'baseShareUrl') {
return 'http://localhost:4200/#/preview/s';
}
return null;
}
};
beforeEach(() => {
TestBed.configureTestingModule({
imports: [RouterTestingModule.withRoutes([{ path: 'fake-path', children: [] }])]
});
router = TestBed.inject(Router);
component = new AppComponent(null, router, null, storeMock, configMock, null, null, null, null, null, null, null, null, overlayContainerMock);
storeMock.dispatch = jasmine.createSpy('dispatch');
});
it('should setup baseShareUrl as per config', (done) => {
storeMock.dispatch.and.callFake((action: SetInitialStateAction) => {
expect(action.payload.sharedUrl).toBe('http://localhost:4200/#/preview/s/');
done();
});
component.loadAppSettings();
});
describe('onFileUploadedError', () => {
it('should dispatch 403 error message', () => {
component.onFileUploadedError({ error: { status: 403 } } as any);
expect(storeMock.dispatch['calls'].argsFor(0)[0].payload).toBe('APP.MESSAGES.UPLOAD.ERROR.403');
});
it('should dispatch 404 error message', () => {
component.onFileUploadedError({ error: { status: 404 } } as any);
expect(storeMock.dispatch['calls'].argsFor(0)[0].payload).toBe('APP.MESSAGES.UPLOAD.ERROR.404');
});
it('should dispatch 409 error message', () => {
component.onFileUploadedError({ error: { status: 409 } } as any);
expect(storeMock.dispatch['calls'].argsFor(0)[0].payload).toBe('APP.MESSAGES.UPLOAD.ERROR.CONFLICT');
});
it('should dispatch 500 error message', () => {
component.onFileUploadedError({ error: { status: 500 } } as any);
expect(storeMock.dispatch['calls'].argsFor(0)[0].payload).toBe('APP.MESSAGES.UPLOAD.ERROR.500');
});
it('should dispatch 504 error message', () => {
component.onFileUploadedError({ error: { status: 504 } } as any);
expect(storeMock.dispatch['calls'].argsFor(0)[0].payload).toBe('APP.MESSAGES.UPLOAD.ERROR.504');
});
it('should dispatch generic error message', () => {
component.onFileUploadedError({ error: { status: 999 } } as any);
expect(storeMock.dispatch['calls'].argsFor(0)[0].payload).toBe('APP.MESSAGES.UPLOAD.ERROR.GENERIC');
});
});
});

View File

@ -1,233 +0,0 @@
/*!
* @license
* Alfresco Example Content Application
*
* Copyright (C) 2005 - 2020 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 <http://www.gnu.org/licenses/>.
*/
import {
AlfrescoApiService,
AppConfigService,
AuthenticationService,
FileUploadErrorEvent,
PageTitleService,
UploadService,
SharedLinksApiService
} from '@alfresco/adf-core';
import { GroupService } from '@alfresco/adf-content-services';
import { Component, OnInit, OnDestroy } from '@angular/core';
import { ActivatedRoute, Router, ActivationEnd } from '@angular/router';
import { Store } from '@ngrx/store';
import {
AppStore,
AppState,
SetCurrentUrlAction,
SetInitialStateAction,
SetUserProfileAction,
SnackbarErrorAction,
CloseModalDialogsAction,
SetRepositoryInfoAction,
getCustomCssPath,
getCustomWebFontPath
} from '@alfresco/aca-shared/store';
import { filter, takeUntil } from 'rxjs/operators';
import { RouterExtensionService, AppService, ContentApiService } from '@alfresco/aca-shared';
import { DiscoveryEntry, GroupEntry, Group } from '@alfresco/js-api';
import { Subject } from 'rxjs';
import { INITIAL_APP_STATE } from './store/initial-state';
import { OverlayContainer } from '@angular/cdk/overlay';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit, OnDestroy {
onDestroy$: Subject<boolean> = new Subject<boolean>();
pageHeading = '';
constructor(
private route: ActivatedRoute,
private router: Router,
private pageTitle: PageTitleService,
private store: Store<AppStore>,
private config: AppConfigService,
private alfrescoApiService: AlfrescoApiService,
private authenticationService: AuthenticationService,
private uploadService: UploadService,
private routerExtensionService: RouterExtensionService,
private contentApi: ContentApiService,
private appService: AppService,
private sharedLinksApiService: SharedLinksApiService,
private groupService: GroupService,
private overlayContainer: OverlayContainer
) {}
ngOnInit() {
this.alfrescoApiService.getInstance().on('error', (error: { status: number; response: any }) => {
if (error.status === 401 && !this.alfrescoApiService.isExcludedErrorListener(error?.response?.req?.url)) {
if (!this.authenticationService.isLoggedIn()) {
this.store.dispatch(new CloseModalDialogsAction());
let redirectUrl = this.route.snapshot.queryParams['redirectUrl'];
if (!redirectUrl) {
redirectUrl = this.router.url;
}
this.router.navigate(['/login'], {
queryParams: { redirectUrl }
});
}
}
});
this.loadAppSettings();
this.loadCustomCss();
this.loadCustomWebFont();
const { router, pageTitle } = this;
this.router.events
.pipe(filter((event) => event instanceof ActivationEnd && event.snapshot.children.length === 0))
.subscribe((event: ActivationEnd) => {
const snapshot: any = event.snapshot || {};
const data: any = snapshot.data || {};
this.pageHeading = data.title || '';
pageTitle.setTitle(data.title || '');
this.store.dispatch(new SetCurrentUrlAction(router.url));
});
this.routerExtensionService.mapExtensionRoutes();
this.uploadService.fileUploadError.subscribe((error) => this.onFileUploadedError(error));
this.sharedLinksApiService.error.pipe(takeUntil(this.onDestroy$)).subscribe((err: { message: string }) => {
this.store.dispatch(new SnackbarErrorAction(err.message));
});
this.appService.ready$.pipe(takeUntil(this.onDestroy$)).subscribe((isReady) => {
if (isReady) {
this.loadRepositoryStatus();
this.loadUserProfile();
}
});
this.overlayContainer.getContainerElement().setAttribute('role', 'region');
}
ngOnDestroy() {
this.onDestroy$.next(true);
this.onDestroy$.complete();
}
private loadRepositoryStatus() {
this.contentApi.getRepositoryInformation().subscribe((response: DiscoveryEntry) => {
this.store.dispatch(new SetRepositoryInfoAction(response.entry.repository));
});
}
private async loadUserProfile() {
const groupsEntries: GroupEntry[] = await this.groupService.listAllGroupMembershipsForPerson('-me-', { maxItems: 250 });
const groups: Group[] = [];
if (groupsEntries) {
groups.push(...groupsEntries.map((obj) => obj.entry));
}
this.contentApi.getPerson('-me-').subscribe((person) => {
this.store.dispatch(new SetUserProfileAction({ person: person.entry, groups }));
});
}
loadAppSettings() {
let baseShareUrl = this.config.get<string>('baseShareUrl');
if (!baseShareUrl.endsWith('/')) {
baseShareUrl += '/';
}
const state: AppState = {
...INITIAL_APP_STATE,
appName: this.config.get<string>('application.name'),
headerColor: this.config.get<string>('headerColor'),
headerTextColor: this.config.get<string>('headerTextColor', '#000000'),
logoPath: this.config.get<string>('application.logo'),
headerImagePath: this.config.get<string>('application.headerImagePath'),
customCssPath: this.config.get<string>('customCssPath'),
webFontPath: this.config.get<string>('webFontPath'),
sharedUrl: baseShareUrl
};
this.store.dispatch(new SetInitialStateAction(state));
}
onFileUploadedError(error: FileUploadErrorEvent) {
let message = 'APP.MESSAGES.UPLOAD.ERROR.GENERIC';
if (error.error.status === 403) {
message = 'APP.MESSAGES.UPLOAD.ERROR.403';
}
if (error.error.status === 404) {
message = 'APP.MESSAGES.UPLOAD.ERROR.404';
}
if (error.error.status === 409) {
message = 'APP.MESSAGES.UPLOAD.ERROR.CONFLICT';
}
if (error.error.status === 500) {
message = 'APP.MESSAGES.UPLOAD.ERROR.500';
}
if (error.error.status === 504) {
message = 'APP.MESSAGES.UPLOAD.ERROR.504';
}
this.store.dispatch(new SnackbarErrorAction(message));
}
private loadCustomCss(): void {
this.store.select(getCustomCssPath).subscribe((cssPath) => {
if (cssPath) {
this.createLink(cssPath);
}
});
}
private loadCustomWebFont(): void {
this.store.select(getCustomWebFontPath).subscribe((fontUrl) => {
if (fontUrl) {
this.createLink(fontUrl);
}
});
}
private createLink(url: string): void {
const cssLinkElement = document.createElement('link');
cssLinkElement.setAttribute('rel', 'stylesheet');
cssLinkElement.setAttribute('type', 'text/css');
cssLinkElement.setAttribute('href', url);
document.head.appendChild(cssLinkElement);
}
}

View File

@ -0,0 +1,43 @@
/*!
* @license
* Alfresco Example Content Application
*
* Copyright (C) 2005 - 2020 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 <http://www.gnu.org/licenses/>.
*/
import { Component } from '@angular/core';
import { Observable, Subject } from 'rxjs';
import { AppService } from '@alfresco/aca-shared';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent {
onDestroy$: Subject<boolean> = new Subject<boolean>();
pageHeading: Observable<string>;
constructor(private appService: AppService) {
this.pageHeading = this.appService.pageHeading$;
this.appService.init();
}
}

View File

@ -23,50 +23,15 @@
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { BrowserModule, HammerModule } from '@angular/platform-browser';
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import { BrowserAnimationsModule, NoopAnimationsModule } from '@angular/platform-browser/animations';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { TRANSLATION_PROVIDER, CoreModule, AppConfigService, DebugAppConfigService } from '@alfresco/adf-core';
import { ContentModule, ContentVersionService } from '@alfresco/adf-content-services';
import { SharedModule } from '@alfresco/aca-shared';
import { AppComponent } from './app.component';
import { APP_ROUTES } from './app.routes';
import { TRANSLATION_PROVIDER, AppConfigService, DebugAppConfigService, CoreModule, AuthGuard } from '@alfresco/adf-core';
import { AppService, SharedModule } from '@alfresco/aca-shared';
import { FilesComponent } from './components/files/files.component';
import { LibrariesComponent } from './components/libraries/libraries.component';
import { FavoriteLibrariesComponent } from './components/favorite-libraries/favorite-libraries.component';
import { ViewProfileModule } from './components/view-profile/view-profile.module';
import { AppStoreModule } from './store/app-store.module';
import { MaterialModule } from './material.module';
import { AppExtensionsModule } from './extensions.module';
import { CoreExtensionsModule } from './extensions/core.extensions.module';
import { AppInfoDrawerModule } from './components/info-drawer/info.drawer.module';
import { DirectivesModule } from './directives/directives.module';
import { ContextMenuModule } from './components/context-menu/context-menu.module';
import { ExtensionsModule } from '@alfresco/adf-extensions';
import { AppToolbarModule } from './components/toolbar/toolbar.module';
import { AppCreateMenuModule } from './components/create-menu/create-menu.module';
import { AppSidenavModule } from './components/sidenav/sidenav.module';
import { AppCommonModule } from './components/common/common.module';
import { AppLayoutModule } from './components/layout/layout.module';
import { AppSearchInputModule } from './components/search/search-input.module';
import { DocumentListCustomComponentsModule } from './components/dl-custom-components/document-list-custom-components.module';
import { AppSearchResultsModule } from './components/search/search-results.module';
import { AppLoginModule } from './components/login/login.module';
import { AppHeaderModule } from './components/header/header.module';
import { AppNodeVersionModule } from './components/node-version/node-version.module';
import { FavoritesComponent } from './components/favorites/favorites.component';
import { RecentFilesComponent } from './components/recent-files/recent-files.component';
import { SharedFilesComponent } from './components/shared-files/shared-files.component';
import { CreateFromTemplateDialogComponent } from './dialogs/node-template/create-from-template.dialog';
import { environment } from '../environments/environment';
import { DetailsComponent } from './components/details/details.component';
import { ContentUrlService } from './services/content-url.service';
import { HomeComponent } from './components/home/home.component';
import { registerLocaleData } from '@angular/common';
import localeFr from '@angular/common/locales/fr';
@ -85,6 +50,18 @@ import localePl from '@angular/common/locales/pl';
import localeFi from '@angular/common/locales/fi';
import localeDa from '@angular/common/locales/da';
import localeSv from '@angular/common/locales/sv';
import { AppShellModule, SHELL_APP_SERVICE } from './app-shell';
import { TranslateModule } from '@ngx-translate/core';
import { RouterModule } from '@angular/router';
import { AppComponent } from './app.components';
import { SHELL_AUTH_TOKEN } from './app-shell/app-shell.routes';
import { CONTENT_LAYOUT_ROUTES } from './content-plugin/content.routes';
import { ContentServiceExtensionModule } from './content-plugin/content-services-extension.module';
import { CoreExtensionsModule } from './extensions/core.extensions.module';
import { INITIAL_APP_STATE } from './content-plugin/store/initial-state';
import { ContentVersionService } from '@alfresco/adf-content-services';
import { ContentUrlService } from './content-plugin/services/content-url.service';
import { STORE_INITIAL_APP_DATA } from '@alfresco/aca-shared/store';
registerLocaleData(localeFr);
registerLocaleData(localeDe);
@ -106,54 +83,38 @@ registerLocaleData(localeSv);
@NgModule({
imports: [
BrowserModule,
TranslateModule.forRoot(),
CoreModule.forRoot(),
SharedModule.forRoot(),
CoreExtensionsModule.forRoot(),
environment.e2e ? NoopAnimationsModule : BrowserAnimationsModule,
FormsModule,
ReactiveFormsModule,
RouterModule.forRoot(APP_ROUTES, {
RouterModule.forRoot([], {
useHash: true,
enableTracing: false, // enable for debug only
relativeLinkResolution: 'legacy'
}),
MaterialModule,
CoreModule.forRoot(),
ContentModule.forRoot(),
SharedModule.forRoot(),
AppStoreModule,
CoreExtensionsModule.forRoot(),
ExtensionsModule.forRoot(),
AppExtensionsModule,
AppLoginModule,
AppCommonModule,
AppLayoutModule,
DirectivesModule,
ContextMenuModule,
AppInfoDrawerModule,
AppToolbarModule,
AppSidenavModule,
AppCreateMenuModule,
DocumentListCustomComponentsModule,
AppSearchInputModule,
AppSearchResultsModule,
AppHeaderModule,
AppNodeVersionModule,
HammerModule,
ViewProfileModule
],
declarations: [
AppComponent,
FilesComponent,
DetailsComponent,
LibrariesComponent,
FavoriteLibrariesComponent,
FavoritesComponent,
RecentFilesComponent,
SharedFilesComponent,
CreateFromTemplateDialogComponent,
HomeComponent
AppShellModule.withRoutes({
shellChildren: [CONTENT_LAYOUT_ROUTES]
}),
ContentServiceExtensionModule
],
providers: [
{ provide: AppService, useClass: AppService },
{ provide: AppConfigService, useClass: DebugAppConfigService },
{ provide: ContentVersionService, useClass: ContentUrlService },
{
provide: SHELL_APP_SERVICE,
useClass: AppService
},
{
provide: SHELL_AUTH_TOKEN,
useClass: AuthGuard
},
{
provide: STORE_INITIAL_APP_DATA,
useValue: INITIAL_APP_STATE
},
{
provide: TRANSLATION_PROVIDER,
multi: true,
@ -163,6 +124,7 @@ registerLocaleData(localeSv);
}
}
],
declarations: [AppComponent],
bootstrap: [AppComponent]
})
export class AppModule {}

View File

@ -1,575 +0,0 @@
/*!
* @license
* Alfresco Example Content Application
*
* Copyright (C) 2005 - 2020 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 <http://www.gnu.org/licenses/>.
*/
import { Routes } from '@angular/router';
import { AppLayoutComponent } from './components/layout/app-layout/app-layout.component';
import { FilesComponent } from './components/files/files.component';
import { LibrariesComponent } from './components/libraries/libraries.component';
import { FavoriteLibrariesComponent } from './components/favorite-libraries/favorite-libraries.component';
import { SearchResultsComponent } from './components/search/search-results/search-results.component';
import { SearchLibrariesResultsComponent } from './components/search/search-libraries-results/search-libraries-results.component';
import { LoginComponent } from './components/login/login.component';
import { AppSharedRuleGuard, GenericErrorComponent, ExtensionsDataLoaderGuard } from '@alfresco/aca-shared';
import { AuthGuard, BlankPageComponent } from '@alfresco/adf-core';
import { FavoritesComponent } from './components/favorites/favorites.component';
import { RecentFilesComponent } from './components/recent-files/recent-files.component';
import { SharedFilesComponent } from './components/shared-files/shared-files.component';
import { DetailsComponent } from './components/details/details.component';
import { HomeComponent } from './components/home/home.component';
import { ViewProfileComponent } from './components/view-profile/view-profile.component';
import { ViewProfileRuleGuard } from './components/view-profile/view-profile.guard';
export const APP_ROUTES: Routes = [
{
path: 'blank',
component: BlankPageComponent
},
{
path: 'login',
component: LoginComponent,
data: {
title: 'APP.SIGN_IN'
}
},
{
path: 'preview/s/:id',
loadChildren: () => import('./components/shared-link-view/shared-link-view.module').then((m) => m.AppSharedLinkViewModule)
},
{
path: 'view',
component: AppLayoutComponent,
children: [
{
path: ':nodeId',
outlet: 'viewer',
children: [
{
path: '',
loadChildren: () => import('./components/viewer/viewer.module').then((m) => m.AppViewerModule)
}
]
}
]
},
{
path: '',
component: AppLayoutComponent,
canActivate: [AuthGuard, ExtensionsDataLoaderGuard],
children: [
{
path: 'profile',
canActivate: [ViewProfileRuleGuard],
component: ViewProfileComponent
},
{
path: '',
component: HomeComponent
},
{
path: 'personal-files',
children: [
{
path: '',
component: FilesComponent,
data: {
sortingPreferenceKey: 'personal-files',
title: 'APP.BROWSE.PERSONAL.TITLE',
defaultNodeId: '-my-'
}
},
{
path: 'details/:nodeId',
children: [
{
path: '',
component: DetailsComponent,
data: {
navigateSource: 'personal-files'
}
},
{
path: ':activeTab',
component: DetailsComponent,
data: {
title: 'APP.BROWSE.PERSONAL.PERMISSIONS.TITLE',
navigateSource: 'personal-files'
}
}
]
},
// deprecated, backwards compatibility with ACA 1.8
{
path: 'preview/:nodeId',
loadChildren: () => import('./components/preview/preview.module').then((m) => m.PreviewModule),
data: {
navigateSource: 'personal-files'
}
},
{
path: 'view/:nodeId',
outlet: 'viewer',
children: [
{
path: '',
data: {
navigateSource: 'personal-files'
},
loadChildren: () => import('./components/viewer/viewer.module').then((m) => m.AppViewerModule)
}
]
},
{
path: 'view/:nodeId/:versionId',
outlet: 'viewer',
children: [
{
path: '',
data: {
navigateSource: 'personal-files'
},
loadChildren: () => import('./components/viewer/viewer.module').then((m) => m.AppViewerModule)
}
]
}
]
},
{
path: 'personal-files/:folderId',
children: [
{
path: '',
component: FilesComponent,
data: {
title: 'APP.BROWSE.PERSONAL.TITLE',
sortingPreferenceKey: 'personal-files'
}
},
{
path: 'view/:nodeId/:versionId',
outlet: 'viewer',
children: [
{
path: '',
data: {
navigateSource: 'personal-files'
},
loadChildren: () => import('./components/viewer/viewer.module').then((m) => m.AppViewerModule)
}
]
},
// deprecated, backwards compatibility with ACA 1.8
{
path: 'preview/:nodeId',
loadChildren: () => import('./components/preview/preview.module').then((m) => m.PreviewModule),
data: {
navigateSource: 'personal-files'
}
},
// deprecated, backwards compatibility with ACA 1.8
{
path: ':folderId/preview/:nodeId',
loadChildren: () => import('./components/preview/preview.module').then((m) => m.PreviewModule),
data: {
navigateSource: 'personal-files'
}
},
{
path: 'view/:nodeId',
outlet: 'viewer',
children: [
{
path: '',
data: {
navigateSource: 'personal-files'
},
loadChildren: () => import('./components/viewer/viewer.module').then((m) => m.AppViewerModule)
}
]
}
]
},
{
path: 'libraries',
children: [
{
path: '',
component: LibrariesComponent,
data: {
title: 'APP.BROWSE.LIBRARIES.MENU.MY_LIBRARIES.TITLE',
sortingPreferenceKey: 'libraries'
}
}
]
},
{
path: 'libraries/:folderId',
children: [
{
path: '',
component: FilesComponent,
data: {
title: 'APP.BROWSE.LIBRARIES.MENU.MY_LIBRARIES.TITLE',
sortingPreferenceKey: 'libraries-files'
}
},
// deprecated, backwards compatibility with ACA 1.8
{
path: 'preview/:nodeId',
loadChildren: () => import('./components/preview/preview.module').then((m) => m.PreviewModule),
data: {
navigateSource: 'libraries'
}
},
{
path: 'view/:nodeId',
outlet: 'viewer',
children: [
{
path: '',
data: {
navigateSource: 'libraries'
},
loadChildren: () => import('./components/viewer/viewer.module').then((m) => m.AppViewerModule)
}
]
},
{
path: 'view/:nodeId/:versionId',
outlet: 'viewer',
children: [
{
path: '',
data: {
navigateSource: 'libraries'
},
loadChildren: () => import('./components/viewer/viewer.module').then((m) => m.AppViewerModule)
}
]
}
]
},
{
path: 'favorite',
children: [
{
path: '',
pathMatch: 'full',
redirectTo: 'libraries'
},
{
path: 'libraries',
component: FavoriteLibrariesComponent,
data: {
title: 'APP.BROWSE.LIBRARIES.MENU.FAVORITE_LIBRARIES.TITLE',
sortingPreferenceKey: 'favorite-libraries'
}
}
]
},
{
path: 'favorite/libraries/:folderId',
children: [
{
path: '',
component: FilesComponent,
data: {
title: 'APP.BROWSE.LIBRARIES.MENU.FAVORITE_LIBRARIES.TITLE',
sortingPreferenceKey: 'libraries-files'
}
},
{
path: 'view/:nodeId',
outlet: 'viewer',
children: [
{
path: '',
data: {
navigateSource: 'libraries'
},
loadChildren: () => import('./components/viewer/viewer.module').then((m) => m.AppViewerModule)
}
]
},
{
path: 'view/:nodeId/:versionId',
outlet: 'viewer',
children: [
{
path: '',
data: {
navigateSource: 'libraries'
},
loadChildren: () => import('./components/viewer/viewer.module').then((m) => m.AppViewerModule)
}
]
}
]
},
{
path: 'favorites',
data: {
sortingPreferenceKey: 'favorites'
},
children: [
{
path: '',
component: FavoritesComponent,
data: {
title: 'APP.BROWSE.FAVORITES.TITLE',
sortingPreferenceKey: 'favorites'
}
// loadChildren:
// './components/favorites/favorites.module#AppFavoritesModule'
},
// deprecated, backwards compatibility with ACA 1.8
{
path: 'preview/:nodeId',
loadChildren: () => import('./components/preview/preview.module').then((m) => m.PreviewModule),
data: {
navigateSource: 'favorites'
}
},
{
path: 'view/:nodeId',
outlet: 'viewer',
children: [
{
path: '',
data: {
navigateSource: 'favorites'
},
loadChildren: () => import('./components/viewer/viewer.module').then((m) => m.AppViewerModule)
}
]
},
{
path: 'view/:nodeId/:versionId',
outlet: 'viewer',
children: [
{
path: '',
data: {
navigateSource: 'favorites'
},
loadChildren: () => import('./components/viewer/viewer.module').then((m) => m.AppViewerModule)
}
]
}
]
},
{
path: 'recent-files',
data: {
sortingPreferenceKey: 'recent-files'
},
children: [
{
path: '',
component: RecentFilesComponent,
data: {
title: 'APP.BROWSE.RECENT.TITLE'
}
// loadChildren:
// './components/recent-files/recent-files.module#AppRecentFilesModule'
},
// deprecated, backwards compatibility with ACA 1.8
{
path: 'preview/:nodeId',
loadChildren: () => import('./components/preview/preview.module').then((m) => m.PreviewModule),
data: {
navigateSource: 'recent-files'
}
},
{
path: 'view/:nodeId',
outlet: 'viewer',
children: [
{
path: '',
data: {
navigateSource: 'recent-files'
},
loadChildren: () => import('./components/viewer/viewer.module').then((m) => m.AppViewerModule)
}
]
},
{
path: 'view/:nodeId/:versionId',
outlet: 'viewer',
children: [
{
path: '',
data: {
navigateSource: 'recent-files'
},
loadChildren: () => import('./components/viewer/viewer.module').then((m) => m.AppViewerModule)
}
]
}
]
},
{
path: 'shared',
children: [
{
path: '',
data: {
title: 'APP.BROWSE.SHARED.TITLE',
sortingPreferenceKey: 'shared-files'
},
component: SharedFilesComponent
// loadChildren:
// './components/shared-files/shared-files.module#AppSharedFilesModule'
},
// deprecated, backwards compatibility with ACA 1.8
{
path: 'preview/:nodeId',
loadChildren: () => import('./components/preview/preview.module').then((m) => m.PreviewModule),
data: {
navigateSource: 'shared'
}
},
{
path: 'view/:nodeId',
outlet: 'viewer',
children: [
{
path: '',
data: {
navigateSource: 'shared'
},
loadChildren: () => import('./components/viewer/viewer.module').then((m) => m.AppViewerModule)
}
]
},
{
path: 'view/:nodeId/:versionId',
outlet: 'viewer',
children: [
{
path: '',
data: {
navigateSource: 'shared'
},
loadChildren: () => import('./components/viewer/viewer.module').then((m) => m.AppViewerModule)
}
]
}
],
canActivateChild: [AppSharedRuleGuard],
canActivate: [AppSharedRuleGuard]
},
{
path: 'trashcan',
loadChildren: () => import('./components/trashcan/trashcan.module').then((m) => m.AppTrashcanModule)
},
{
path: 'search',
children: [
{
path: '',
component: SearchResultsComponent,
data: {
title: 'APP.BROWSE.SEARCH.TITLE'
}
},
// deprecated, backwards compatibility with ACA 1.8
{
path: 'preview/:nodeId',
loadChildren: () => import('./components/preview/preview.module').then((m) => m.PreviewModule),
data: {
navigateSource: 'search'
}
},
{
path: 'view/:nodeId',
outlet: 'viewer',
children: [
{
path: '',
data: {
navigateSource: 'search'
},
loadChildren: () => import('./components/viewer/viewer.module').then((m) => m.AppViewerModule)
}
]
},
{
path: 'view/:nodeId/:versionId',
outlet: 'viewer',
children: [
{
path: '',
data: {
navigateSource: 'search'
},
loadChildren: () => import('./components/viewer/viewer.module').then((m) => m.AppViewerModule)
}
]
}
]
},
{
path: 'search-libraries',
children: [
{
path: '',
component: SearchLibrariesResultsComponent,
data: {
title: 'APP.BROWSE.SEARCH.TITLE'
}
},
{
path: 'view/:nodeId',
outlet: 'viewer',
children: [
{
path: '',
data: {
navigateSource: 'search'
},
loadChildren: () => import('./components/viewer/viewer.module').then((m) => m.AppViewerModule)
}
]
}
]
},
{
path: 'nodes/:nodeId',
children: [
{
path: '',
loadChildren: () => import('@alfresco/aca-folder-rules').then((m) => m.AcaFolderRulesModule)
}
]
},
{
path: '**',
component: GenericErrorComponent
}
],
canActivateChild: [AuthGuard]
}
];

View File

@ -30,7 +30,7 @@ import { MatButtonModule } from '@angular/material/button';
import { MatIconModule } from '@angular/material/icon';
import { MatListModule } from '@angular/material/list';
import { MatMenuModule } from '@angular/material/menu';
import { CoreExtensionsModule } from '../../extensions/core.extensions.module';
import { CoreExtensionsModule } from '../../../extensions/core.extensions.module';
import { AppCommonModule } from '../common/common.module';
import { ContextMenuItemComponent } from './context-menu-item.component';
import { OutsideEventDirective } from './context-menu-outside-event.directive';

View File

@ -26,8 +26,8 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { AppTestingModule } from '../../testing/app-testing.module';
import { DetailsComponent } from './details.component';
import { MetadataTabComponent } from './../info-drawer/metadata-tab/metadata-tab.component';
import { CommentsTabComponent } from './../info-drawer/comments-tab/comments-tab.component';
import { MetadataTabComponent } from '../info-drawer/metadata-tab/metadata-tab.component';
import { CommentsTabComponent } from '../info-drawer/comments-tab/comments-tab.component';
import { ActivatedRoute, Router } from '@angular/router';
import { of, Subject } from 'rxjs';
import { NO_ERRORS_SCHEMA } from '@angular/core';

View File

@ -28,7 +28,7 @@ import { NgModule } from '@angular/core';
import { CustomNameColumnComponent } from './name-column/name-column.component';
import { LockedByModule } from '@alfresco/aca-shared';
import { ContentModule } from '@alfresco/adf-content-services';
import { MaterialModule } from '../../material.module';
import { MaterialModule } from '../../../material.module';
import { CoreModule } from '@alfresco/adf-core';
import { ThumbnailColumnComponent } from './thumbnail-column/thumbnail-column.component';

View File

@ -4,8 +4,8 @@
[tooltip]="appName$ | async"
[color]="headerColor$ | async"
[title]="appName$ | async"
(clicked)="toggleClicked.emit($event)"
[expandedSidenav]="expandedSidenav"
(clicked)="onToggleSidenav($event)"
[expandedSidenav]="isSidenavExpanded"
>
<div class="adf-toolbar--spacer adf-toolbar-divider"></div>

View File

@ -30,7 +30,7 @@ import { ContentActionRef } from '@alfresco/adf-extensions';
import { AppStore, getHeaderColor, getAppName, getLogoPath, getHeaderImagePath, getHeaderTextColor } from '@alfresco/aca-shared/store';
import { AppExtensionService } from '@alfresco/aca-shared';
import { takeUntil } from 'rxjs/operators';
import { AppConfigService } from '@alfresco/adf-core';
import { AppConfigService, SidenavLayoutComponent } from '@alfresco/adf-core';
import { isContentServiceEnabled } from '@alfresco/aca-shared/rules';
@Component({
@ -42,11 +42,18 @@ import { isContentServiceEnabled } from '@alfresco/aca-shared/rules';
})
export class AppHeaderComponent implements OnInit, OnDestroy {
private onDestroy$: Subject<boolean> = new Subject<boolean>();
@Output()
toggleClicked = new EventEmitter();
@Input() expandedSidenav = true;
@Input() data: { layout?: SidenavLayoutComponent; isMenuMinimized?: boolean } = {};
get isSidenavExpanded(): boolean {
return !this.data.isMenuMinimized ?? this.expandedSidenav;
}
appName$: Observable<string>;
headerColor$: Observable<any>;
headerTextColor$: Observable<string>;
@ -55,7 +62,7 @@ export class AppHeaderComponent implements OnInit, OnDestroy {
actions: Array<ContentActionRef> = [];
constructor(store: Store<AppStore>, private appExtensions: AppExtensionService, private appConfigService: AppConfigService) {
constructor(public store: Store<AppStore>, private appExtensions: AppExtensionService, private appConfigService: AppConfigService) {
this.headerColor$ = store.select(getHeaderColor);
this.headerTextColor$ = store.select(getHeaderTextColor);
this.appName$ = store.select(getAppName);
@ -80,6 +87,10 @@ export class AppHeaderComponent implements OnInit, OnDestroy {
});
}
onToggleSidenav(_event: boolean): void {
this.data.layout.toggleMenu();
}
isContentServiceEnabled(): boolean {
return isContentServiceEnabled();
}

View File

@ -29,7 +29,7 @@ import { ExtensionsModule } from '@alfresco/adf-extensions';
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { DirectivesModule } from '../../directives/directives.module';
import { MaterialModule } from '../../material.module';
import { MaterialModule } from '../../../material.module';
import { CommentsTabComponent } from './comments-tab/comments-tab.component';
import { MetadataTabComponent } from './metadata-tab/metadata-tab.component';
import { LibraryMetadataTabComponent } from './library-metadata-tab/library-metadata-tab.component';

View File

@ -27,9 +27,11 @@ import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { CoreModule } from '@alfresco/adf-core';
import { LoginComponent } from './login.component';
import { TranslateModule } from '@ngx-translate/core';
@NgModule({
imports: [CommonModule, CoreModule.forChild()],
imports: [CommonModule, CoreModule.forChild(), TranslateModule.forChild()],
exports: [LoginComponent],
declarations: [LoginComponent]
})
export class AppLoginModule {}

Some files were not shown because too many files have changed in this diff Show More