[ACA-1529] performance fixes for permission checks (#498)

* fix recent files

* fix files component

* fix shared files

* don't evaluate permissions for empty selection

* fix info drawer

* fix viewer

* fix tests

* reduce one more check

* track upload errors on app level

* remove console log

* reduce service dependencies
This commit is contained in:
Denys Vuika 2018-07-08 12:25:20 +01:00 committed by GitHub
parent fe683015c5
commit 718a32a907
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 171 additions and 151 deletions

View File

@ -23,21 +23,27 @@
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>. * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/ */
import {
AlfrescoApiService,
AppConfigService,
AuthenticationService,
FileUploadErrorEvent,
PageTitleService,
UploadService
} from '@alfresco/adf-core';
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { Router, ActivatedRoute, NavigationEnd } from '@angular/router'; import { ActivatedRoute, NavigationEnd, Router } from '@angular/router';
import {
PageTitleService, AppConfigService,
AuthenticationService, AlfrescoApiService } from '@alfresco/adf-core';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { AppStore } from './store/states/app.state';
import {
SetHeaderColorAction,
SetAppNameAction,
SetLogoPathAction,
SetLanguagePickerAction,
SetSharedUrlAction
} from './store/actions';
import { ExtensionService } from './extensions/extension.service'; import { ExtensionService } from './extensions/extension.service';
import {
SetAppNameAction,
SetHeaderColorAction,
SetLanguagePickerAction,
SetLogoPathAction,
SetSharedUrlAction,
SnackbarErrorAction
} from './store/actions';
import { AppStore } from './store/states/app.state';
@Component({ @Component({
selector: 'app-root', selector: 'app-root',
@ -53,11 +59,12 @@ export class AppComponent implements OnInit {
private config: AppConfigService, private config: AppConfigService,
private alfrescoApiService: AlfrescoApiService, private alfrescoApiService: AlfrescoApiService,
private authenticationService: AuthenticationService, private authenticationService: AuthenticationService,
private extensions: ExtensionService) { private uploadService: UploadService,
} private extensions: ExtensionService
) {}
ngOnInit() { ngOnInit() {
this.alfrescoApiService.getInstance().on('error', (error) => { this.alfrescoApiService.getInstance().on('error', error => {
if (error.status === 401) { if (error.status === 401) {
if (!this.authenticationService.isLoggedIn()) { if (!this.authenticationService.isLoggedIn()) {
this.router.navigate(['/login']); this.router.navigate(['/login']);
@ -65,13 +72,11 @@ export class AppComponent implements OnInit {
} }
}); });
this.loadAppSettings(); this.loadAppSettings();
const { router, pageTitle, route } = this; const { router, pageTitle, route } = this;
router router.events
.events
.filter(event => event instanceof NavigationEnd) .filter(event => event instanceof NavigationEnd)
.subscribe(() => { .subscribe(() => {
let currentRoute = route.root; let currentRoute = route.root;
@ -88,8 +93,10 @@ export class AppComponent implements OnInit {
this.extensions.init(); this.extensions.init();
this.router.config.unshift( this.router.config.unshift(...this.extensions.getApplicationRoutes());
...this.extensions.getApplicationRoutes()
this.uploadService.fileUploadError.subscribe(error =>
this.onFileUploadedError(error)
); );
} }
@ -109,7 +116,22 @@ export class AppComponent implements OnInit {
const languagePicker = this.config.get<boolean>('languagePicker'); const languagePicker = this.config.get<boolean>('languagePicker');
this.store.dispatch(new SetLanguagePickerAction(languagePicker)); this.store.dispatch(new SetLanguagePickerAction(languagePicker));
const sharedPreviewUrl = this.config.get<string>('ecmHost') + '/#/preview/s/'; const sharedPreviewUrl =
this.config.get<string>('ecmHost') + '/#/preview/s/';
this.store.dispatch(new SetSharedUrlAction(sharedPreviewUrl)); this.store.dispatch(new SetSharedUrlAction(sharedPreviewUrl));
} }
onFileUploadedError(error: FileUploadErrorEvent) {
let message = 'APP.MESSAGES.UPLOAD.ERROR.GENERIC';
if (error.error.status === 409) {
message = 'APP.MESSAGES.UPLOAD.ERROR.CONFLICT';
}
if (error.error.status === 500) {
message = 'APP.MESSAGES.UPLOAD.ERROR.500';
}
this.store.dispatch(new SnackbarErrorAction(message));
}
} }

View File

@ -30,7 +30,12 @@ import { FolderDialogComponent } from '@alfresco/adf-content-services';
import { SnackbarErrorAction } from '../../store/actions'; import { SnackbarErrorAction } from '../../store/actions';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { AppStore } from '../../store/states'; import { AppStore } from '../../store/states';
import { MinimalNodeEntity, MinimalNodeEntryEntity } from 'alfresco-js-api'; import {
MinimalNodeEntity,
MinimalNodeEntryEntity,
Node
} from 'alfresco-js-api';
import { NodePermissionService } from './node-permission.service';
@Injectable() @Injectable()
export class ContentManagementService { export class ContentManagementService {
@ -43,7 +48,11 @@ export class ContentManagementService {
siteDeleted = new Subject<string>(); siteDeleted = new Subject<string>();
linksUnshared = new Subject<any>(); linksUnshared = new Subject<any>();
constructor(private store: Store<AppStore>, private dialogRef: MatDialog) {} constructor(
private store: Store<AppStore>,
private permission: NodePermissionService,
private dialogRef: MatDialog
) {}
createFolder(parentNodeId: string) { createFolder(parentNodeId: string) {
const dialogInstance = this.dialogRef.open(FolderDialogComponent, { const dialogInstance = this.dialogRef.open(FolderDialogComponent, {
@ -88,4 +97,32 @@ export class ContentManagementService {
} }
}); });
} }
canDeleteNode(node: MinimalNodeEntity | Node): boolean {
return this.permission.check(node, ['delete']);
}
canDeleteNodes(nodes: MinimalNodeEntity[]): boolean {
return this.permission.check(nodes, ['delete']);
}
canUpdateNode(node: MinimalNodeEntity | Node): boolean {
return this.permission.check(node, ['update']);
}
canUploadContent(folderNode: MinimalNodeEntity | Node): boolean {
return this.permission.check(folderNode, ['create']);
}
canDeleteSharedNodes(sharedLinks: MinimalNodeEntity[]): boolean {
return this.permission.check(sharedLinks, ['delete'], {
target: 'allowableOperationsOnTarget'
});
}
canUpdateSharedNode(sharedLink: MinimalNodeEntity): boolean {
return this.permission.check(sharedLink, ['update'], {
target: 'allowableOperationsOnTarget'
});
}
} }

View File

@ -33,7 +33,6 @@ import {
PathInfo PathInfo
} from 'alfresco-js-api'; } from 'alfresco-js-api';
import { ContentManagementService } from '../../common/services/content-management.service'; import { ContentManagementService } from '../../common/services/content-management.service';
import { NodePermissionService } from '../../common/services/node-permission.service';
import { AppStore } from '../../store/states/app.state'; import { AppStore } from '../../store/states/app.state';
import { PageComponent } from '../page.component'; import { PageComponent } from '../page.component';
import { ContentApiService } from '../../services/content-api.service'; import { ContentApiService } from '../../services/content-api.service';
@ -48,10 +47,9 @@ export class FavoritesComponent extends PageComponent implements OnInit {
store: Store<AppStore>, store: Store<AppStore>,
extensions: ExtensionService, extensions: ExtensionService,
private contentApi: ContentApiService, private contentApi: ContentApiService,
private content: ContentManagementService, content: ContentManagementService
public permission: NodePermissionService
) { ) {
super(store, extensions); super(store, extensions, content);
} }
ngOnInit() { ngOnInit() {

View File

@ -48,7 +48,7 @@
<button <button
color="primary" color="primary"
mat-icon-button mat-icon-button
*ngIf="selection.folder && permission.check(selection.folder, ['update'])" *ngIf="canEditFolder"
title="{{ 'APP.ACTIONS.EDIT' | translate }}" title="{{ 'APP.ACTIONS.EDIT' | translate }}"
[acaEditFolder]="selection.folder"> [acaEditFolder]="selection.folder">
<mat-icon>create</mat-icon> <mat-icon>create</mat-icon>
@ -100,7 +100,7 @@
<button <button
mat-menu-item mat-menu-item
*ngIf="permission.check(selection.nodes, ['delete'])" *ngIf="canDelete"
[acaMoveNode]="selection.nodes"> [acaMoveNode]="selection.nodes">
<mat-icon>library_books</mat-icon> <mat-icon>library_books</mat-icon>
<span>{{ 'APP.ACTIONS.MOVE' | translate }}</span> <span>{{ 'APP.ACTIONS.MOVE' | translate }}</span>
@ -108,7 +108,7 @@
<button <button
mat-menu-item mat-menu-item
*ngIf="permission.check(selection.nodes, ['delete'])" *ngIf="canDelete"
[acaDeleteNode]="selection.nodes"> [acaDeleteNode]="selection.nodes">
<mat-icon>delete</mat-icon> <mat-icon>delete</mat-icon>
<span>{{ 'APP.ACTIONS.DELETE' | translate }}</span> <span>{{ 'APP.ACTIONS.DELETE' | translate }}</span>
@ -134,7 +134,7 @@
<div class="inner-layout__panel"> <div class="inner-layout__panel">
<adf-upload-drag-area <adf-upload-drag-area
[parentId]="node?.id" [parentId]="node?.id"
[disabled]="!permission.check(node, ['create'])"> [disabled]="!canUpload">
<adf-document-list acaDocumentList #documentList <adf-document-list acaDocumentList #documentList
[sorting]="[ 'modifiedAt', 'desc' ]" [sorting]="[ 'modifiedAt', 'desc' ]"

View File

@ -31,7 +31,6 @@ import { MinimalNodeEntity, MinimalNodeEntryEntity, NodePaging, PathElement, Pat
import { Observable } from 'rxjs/Rx'; import { Observable } from 'rxjs/Rx';
import { ContentManagementService } from '../../common/services/content-management.service'; import { ContentManagementService } from '../../common/services/content-management.service';
import { NodeActionsService } from '../../common/services/node-actions.service'; import { NodeActionsService } from '../../common/services/node-actions.service';
import { NodePermissionService } from '../../common/services/node-permission.service';
import { AppStore } from '../../store/states/app.state'; import { AppStore } from '../../store/states/app.state';
import { PageComponent } from '../page.component'; import { PageComponent } from '../page.component';
import { ContentApiService } from '../../services/content-api.service'; import { ContentApiService } from '../../services/content-api.service';
@ -53,16 +52,15 @@ export class FilesComponent extends PageComponent implements OnInit, OnDestroy {
store: Store<AppStore>, store: Store<AppStore>,
private nodeActionsService: NodeActionsService, private nodeActionsService: NodeActionsService,
private uploadService: UploadService, private uploadService: UploadService,
private contentManagementService: ContentManagementService, content: ContentManagementService,
public permission: NodePermissionService,
extensions: ExtensionService) { extensions: ExtensionService) {
super(store, extensions); super(store, extensions, content);
} }
ngOnInit() { ngOnInit() {
super.ngOnInit(); super.ngOnInit();
const { route, contentManagementService, nodeActionsService, uploadService } = this; const { route, content, nodeActionsService, uploadService } = this;
const { data } = route.snapshot; const { data } = route.snapshot;
this.title = data.title; this.title = data.title;
@ -89,14 +87,13 @@ export class FilesComponent extends PageComponent implements OnInit, OnDestroy {
this.subscriptions = this.subscriptions.concat([ this.subscriptions = this.subscriptions.concat([
nodeActionsService.contentCopied.subscribe((nodes) => this.onContentCopied(nodes)), nodeActionsService.contentCopied.subscribe((nodes) => this.onContentCopied(nodes)),
contentManagementService.folderCreated.subscribe(() => this.documentList.reload()), content.folderCreated.subscribe(() => this.documentList.reload()),
contentManagementService.folderEdited.subscribe(() => this.documentList.reload()), content.folderEdited.subscribe(() => this.documentList.reload()),
contentManagementService.nodesDeleted.subscribe(() => this.documentList.reload()), content.nodesDeleted.subscribe(() => this.documentList.reload()),
contentManagementService.nodesMoved.subscribe(() => this.documentList.reload()), content.nodesMoved.subscribe(() => this.documentList.reload()),
contentManagementService.nodesRestored.subscribe(() => this.documentList.reload()), content.nodesRestored.subscribe(() => this.documentList.reload()),
uploadService.fileUploadComplete.debounceTime(300).subscribe(file => this.onFileUploadedEvent(file)), uploadService.fileUploadComplete.debounceTime(300).subscribe(file => this.onFileUploadedEvent(file)),
uploadService.fileUploadDeleted.debounceTime(300).subscribe((file) => this.onFileUploadedEvent(file)), uploadService.fileUploadDeleted.debounceTime(300).subscribe((file) => this.onFileUploadedEvent(file)),
uploadService.fileUploadError.subscribe((error) => this.onFileUploadedError(error))
]); ]);
} }

View File

@ -23,11 +23,10 @@
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>. * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/ */
import { AppConfigService, PeopleContentService } from '@alfresco/adf-core'; import { AppConfigService } from '@alfresco/adf-core';
import { NO_ERRORS_SCHEMA } from '@angular/core'; import { NO_ERRORS_SCHEMA } from '@angular/core';
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { Observable } from 'rxjs/Rx';
import { SetAppNameAction, SetHeaderColorAction } from '../../store/actions'; import { SetAppNameAction, SetHeaderColorAction } from '../../store/actions';
import { AppStore } from '../../store/states/app.state'; import { AppStore } from '../../store/states/app.state';
import { AppTestingModule } from '../../testing/app-testing.module'; import { AppTestingModule } from '../../testing/app-testing.module';
@ -46,11 +45,6 @@ describe('HeaderComponent', () => {
HeaderComponent HeaderComponent
], ],
schemas: [ NO_ERRORS_SCHEMA ] schemas: [ NO_ERRORS_SCHEMA ]
})
.overrideProvider(PeopleContentService, {
useValue: {
getCurrentPerson: () => Observable.of({ entry: {} })
}
}); });
store = TestBed.get(Store); store = TestBed.get(Store);

View File

@ -5,8 +5,8 @@
<adf-info-drawer [title]="'APP.INFO_DRAWER.TITLE' | translate"> <adf-info-drawer [title]="'APP.INFO_DRAWER.TITLE' | translate">
<adf-info-drawer-tab [label]="'APP.INFO_DRAWER.TABS.PROPERTIES' | translate"> <adf-info-drawer-tab [label]="'APP.INFO_DRAWER.TABS.PROPERTIES' | translate">
<adf-content-metadata-card <adf-content-metadata-card
[readOnly]="!canUpdateNode()" [readOnly]="!canUpdateNode"
[displayEmpty]="canUpdateNode()" [displayEmpty]="canUpdateNode"
[preset]="'custom'" [preset]="'custom'"
[node]="displayNode"> [node]="displayNode">
</adf-content-metadata-card> </adf-content-metadata-card>

View File

@ -34,19 +34,11 @@ import { ContentApiService } from '../../services/content-api.service';
}) })
export class InfoDrawerComponent implements OnChanges { export class InfoDrawerComponent implements OnChanges {
@Input() nodeId: string; @Input() nodeId: string;
@Input() node: MinimalNodeEntity; @Input() node: MinimalNodeEntity;
isLoading = false; isLoading = false;
displayNode: MinimalNodeEntryEntity; displayNode: MinimalNodeEntryEntity;
canUpdateNode = false;
canUpdateNode(): boolean {
if (this.displayNode) {
return this.permission.check(this.displayNode, ['update']);
}
return false;
}
get isFileSelected(): boolean { get isFileSelected(): boolean {
if (this.node && this.node.entry) { if (this.node && this.node.entry) {
@ -78,7 +70,7 @@ export class InfoDrawerComponent implements OnChanges {
if (this.isTypeImage(entry) && !this.hasAspectNames(entry)) { if (this.isTypeImage(entry) && !this.hasAspectNames(entry)) {
this.loadNodeInfo(this.node.entry.id); this.loadNodeInfo(this.node.entry.id);
} else { } else {
this.displayNode = this.node.entry; this.setDisplayNode(this.node.entry);
} }
} }
} }
@ -101,11 +93,16 @@ export class InfoDrawerComponent implements OnChanges {
this.contentApi.getNodeInfo(nodeId).subscribe( this.contentApi.getNodeInfo(nodeId).subscribe(
entity => { entity => {
this.displayNode = entity; this.setDisplayNode(entity);
this.isLoading = false; this.isLoading = false;
}, },
() => this.isLoading = false () => this.isLoading = false
); );
} }
} }
private setDisplayNode(node: MinimalNodeEntryEntity) {
this.displayNode = node;
this.canUpdateNode = node && this.permission.check(node, ['update']);
}
} }

View File

@ -25,8 +25,7 @@
import { NO_ERRORS_SCHEMA } from '@angular/core'; import { NO_ERRORS_SCHEMA } from '@angular/core';
import { TestBed, ComponentFixture } from '@angular/core/testing'; import { TestBed, ComponentFixture } from '@angular/core/testing';
import { PeopleContentService, AppConfigService, UserPreferencesService } from '@alfresco/adf-core'; import { AppConfigService, UserPreferencesService } from '@alfresco/adf-core';
import { Observable } from 'rxjs/Observable';
import { LayoutComponent } from './layout.component'; import { LayoutComponent } from './layout.component';
import { SidenavViewsManagerDirective } from './sidenav-views-manager.directive'; import { SidenavViewsManagerDirective } from './sidenav-views-manager.directive';
import { AppTestingModule } from '../../testing/app-testing.module'; import { AppTestingModule } from '../../testing/app-testing.module';
@ -44,14 +43,6 @@ describe('LayoutComponent', () => {
LayoutComponent, LayoutComponent,
SidenavViewsManagerDirective SidenavViewsManagerDirective
], ],
providers: [
{
provide: PeopleContentService,
useValue: {
getCurrentPerson: () => Observable.of({ entry: {} })
}
}
],
schemas: [ NO_ERRORS_SCHEMA ] schemas: [ NO_ERRORS_SCHEMA ]
}); });

View File

@ -63,7 +63,7 @@ export class LayoutComponent implements OnInit, OnDestroy {
.pipe(takeUntil(this.onDestroy$)) .pipe(takeUntil(this.onDestroy$))
.subscribe(node => { .subscribe(node => {
this.node = node; this.node = node;
this.canUpload = this.permission.check(node, ['create']); this.canUpload = node && this.permission.check(node, ['create']);
}); });
} }

View File

@ -42,12 +42,12 @@ import { ExtensionService } from '../../extensions/extension.service';
export class LibrariesComponent extends PageComponent implements OnInit { export class LibrariesComponent extends PageComponent implements OnInit {
constructor(private route: ActivatedRoute, constructor(private route: ActivatedRoute,
private content: ContentManagementService, content: ContentManagementService,
private contentApi: ContentApiService, private contentApi: ContentApiService,
store: Store<AppStore>, store: Store<AppStore>,
extensions: ExtensionService, extensions: ExtensionService,
private router: Router) { private router: Router) {
super(store, extensions); super(store, extensions, content);
} }
ngOnInit() { ngOnInit() {

View File

@ -29,7 +29,7 @@ class TestClass extends PageComponent {
node: any; node: any;
constructor() { constructor() {
super(null, null); super(null, null, null);
} }
} }

View File

@ -24,19 +24,20 @@
*/ */
import { DocumentListComponent, ShareDataRow } from '@alfresco/adf-content-services'; import { DocumentListComponent, ShareDataRow } from '@alfresco/adf-content-services';
import { DisplayMode, FileUploadErrorEvent } from '@alfresco/adf-core'; import { DisplayMode } from '@alfresco/adf-core';
import { OnDestroy, OnInit, ViewChild } from '@angular/core'; import { OnDestroy, OnInit, ViewChild } from '@angular/core';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { MinimalNodeEntity, MinimalNodeEntryEntity } from 'alfresco-js-api'; import { MinimalNodeEntity, MinimalNodeEntryEntity } from 'alfresco-js-api';
import { takeUntil } from 'rxjs/operators'; import { takeUntil } from 'rxjs/operators';
import { Subject, Subscription } from 'rxjs/Rx'; import { Subject, Subscription } from 'rxjs/Rx';
import { SnackbarErrorAction, ViewNodeAction, SetSelectedNodesAction } from '../store/actions'; import { ViewNodeAction, SetSelectedNodesAction } from '../store/actions';
import { appSelection, sharedUrl } from '../store/selectors/app.selectors'; import { appSelection, sharedUrl, currentFolder } from '../store/selectors/app.selectors';
import { AppStore } from '../store/states/app.state'; import { AppStore } from '../store/states/app.state';
import { SelectionState } from '../store/states/selection.state'; import { SelectionState } from '../store/states/selection.state';
import { Observable } from 'rxjs/Rx'; import { Observable } from 'rxjs/Rx';
import { ExtensionService } from '../extensions/extension.service'; import { ExtensionService } from '../extensions/extension.service';
import { ContentActionExtension } from '../extensions/content-action.extension'; import { ContentActionExtension } from '../extensions/content-action.extension';
import { ContentManagementService } from '../common/services/content-management.service';
export abstract class PageComponent implements OnInit, OnDestroy { export abstract class PageComponent implements OnInit, OnDestroy {
@ -52,6 +53,11 @@ export abstract class PageComponent implements OnInit, OnDestroy {
displayMode = DisplayMode.List; displayMode = DisplayMode.List;
sharedPreviewUrl$: Observable<string>; sharedPreviewUrl$: Observable<string>;
actions: Array<ContentActionExtension> = []; actions: Array<ContentActionExtension> = [];
canDelete = false;
canEditFolder = false;
canUpload = false;
canDeleteShared = false;
canUpdateShared = false;
protected subscriptions: Subscription[] = []; protected subscriptions: Subscription[] = [];
@ -61,7 +67,8 @@ export abstract class PageComponent implements OnInit, OnDestroy {
constructor( constructor(
protected store: Store<AppStore>, protected store: Store<AppStore>,
protected extensions: ExtensionService) {} protected extensions: ExtensionService,
protected content: ContentManagementService) {}
ngOnInit() { ngOnInit() {
this.sharedPreviewUrl$ = this.store.select(sharedUrl); this.sharedPreviewUrl$ = this.store.select(sharedUrl);
@ -75,6 +82,16 @@ export abstract class PageComponent implements OnInit, OnDestroy {
this.infoDrawerOpened = false; this.infoDrawerOpened = false;
} }
this.actions = this.extensions.getSelectedContentActions(selection, this.node); this.actions = this.extensions.getSelectedContentActions(selection, this.node);
this.canDelete = !this.selection.isEmpty && this.content.canDeleteNodes(selection.nodes);
this.canEditFolder = selection.folder && this.content.canUpdateNode(selection.folder);
this.canDeleteShared = !this.selection.isEmpty && this.content.canDeleteSharedNodes(selection.nodes);
this.canUpdateShared = selection.file && this.content.canUpdateSharedNode(selection.file);
});
this.store.select(currentFolder)
.pipe(takeUntil(this.onDestroy$))
.subscribe(node => {
this.canUpload = node && this.content.canUploadContent(node);
}); });
} }
@ -130,22 +147,6 @@ export abstract class PageComponent implements OnInit, OnDestroy {
} }
} }
onFileUploadedError(error: FileUploadErrorEvent) {
let message = 'APP.MESSAGES.UPLOAD.ERROR.GENERIC';
if (error.error.status === 409) {
message = 'APP.MESSAGES.UPLOAD.ERROR.CONFLICT';
}
if (error.error.status === 500) {
message = 'APP.MESSAGES.UPLOAD.ERROR.500';
}
const action = new SnackbarErrorAction(message);
this.store.dispatch(action);
}
toggleGalleryView(): void { toggleGalleryView(): void {
this.displayMode = this.displayMode === DisplayMode.List ? DisplayMode.Gallery : DisplayMode.List; this.displayMode = this.displayMode === DisplayMode.List ? DisplayMode.Gallery : DisplayMode.List;
this.documentList.display = this.displayMode; this.documentList.display = this.displayMode;

View File

@ -4,8 +4,8 @@
<adf-info-drawer [title]="'APP.INFO_DRAWER.TITLE' | translate"> <adf-info-drawer [title]="'APP.INFO_DRAWER.TITLE' | translate">
<adf-info-drawer-tab [label]="'APP.INFO_DRAWER.TABS.PROPERTIES' | translate"> <adf-info-drawer-tab [label]="'APP.INFO_DRAWER.TABS.PROPERTIES' | translate">
<adf-content-metadata-card <adf-content-metadata-card
[readOnly]="!permission.check(node, ['update'])" [readOnly]="!canUpdatePreview"
[displayEmpty]="permission.check(node, ['update'])" [displayEmpty]="canUpdatePreview"
[preset]="'custom'" [preset]="'custom'"
[node]="node"> [node]="node">
</adf-content-metadata-card> </adf-content-metadata-card>
@ -74,7 +74,7 @@
<button <button
mat-menu-item mat-menu-item
*ngIf="permission.check(node, ['delete'])" *ngIf="canDeletePreview"
[acaMoveNode]="selectedEntities"> [acaMoveNode]="selectedEntities">
<mat-icon>library_books</mat-icon> <mat-icon>library_books</mat-icon>
<span>{{ 'APP.ACTIONS.MOVE' | translate }}</span> <span>{{ 'APP.ACTIONS.MOVE' | translate }}</span>
@ -82,7 +82,7 @@
<button <button
mat-menu-item mat-menu-item
*ngIf="permission.check(node, ['delete'])" *ngIf="canDeletePreview"
(click)="deleteFile()"> (click)="deleteFile()">
<mat-icon>delete</mat-icon> <mat-icon>delete</mat-icon>
<span>{{ 'APP.ACTIONS.DELETE' | translate }}</span> <span>{{ 'APP.ACTIONS.DELETE' | translate }}</span>
@ -90,7 +90,7 @@
<button <button
mat-menu-item mat-menu-item
*ngIf="permission.check(node, ['update'])" *ngIf="canUpdatePreview"
[acaNodeVersions]="node"> [acaNodeVersions]="node">
<mat-icon>history</mat-icon> <mat-icon>history</mat-icon>
<span>{{ 'APP.ACTIONS.VERSIONS' | translate }}</span> <span>{{ 'APP.ACTIONS.VERSIONS' | translate }}</span>

View File

@ -25,9 +25,8 @@
import { Component, OnInit, ViewEncapsulation } from '@angular/core'; import { Component, OnInit, ViewEncapsulation } from '@angular/core';
import { ActivatedRoute, Router, UrlTree, UrlSegmentGroup, UrlSegment, PRIMARY_OUTLET } from '@angular/router'; import { ActivatedRoute, Router, UrlTree, UrlSegmentGroup, UrlSegment, PRIMARY_OUTLET } from '@angular/router';
import { UserPreferencesService, ObjectUtils, UploadService } from '@alfresco/adf-core'; import { UserPreferencesService, ObjectUtils } from '@alfresco/adf-core';
import { Node, MinimalNodeEntity } from 'alfresco-js-api'; import { Node, MinimalNodeEntity } from 'alfresco-js-api';
import { NodePermissionService } from '../../common/services/node-permission.service';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { AppStore } from '../../store/states/app.state'; import { AppStore } from '../../store/states/app.state';
import { DeleteNodesAction } from '../../store/actions'; import { DeleteNodesAction } from '../../store/actions';
@ -35,6 +34,7 @@ import { PageComponent } from '../page.component';
import { ContentApiService } from '../../services/content-api.service'; import { ContentApiService } from '../../services/content-api.service';
import { ExtensionService } from '../../extensions/extension.service'; import { ExtensionService } from '../../extensions/extension.service';
import { OpenWithExtension } from '../../extensions/open-with.extension'; import { OpenWithExtension } from '../../extensions/open-with.extension';
import { ContentManagementService } from '../../common/services/content-management.service';
@Component({ @Component({
selector: 'app-preview', selector: 'app-preview',
templateUrl: 'preview.component.html', templateUrl: 'preview.component.html',
@ -59,16 +59,18 @@ export class PreviewComponent extends PageComponent implements OnInit {
selectedEntities: MinimalNodeEntity[] = []; selectedEntities: MinimalNodeEntity[] = [];
openWith: Array<OpenWithExtension> = []; openWith: Array<OpenWithExtension> = [];
canDeletePreview = false;
canUpdatePreview = false;
constructor( constructor(
private contentApi: ContentApiService, private contentApi: ContentApiService,
private uploadService: UploadService,
private preferences: UserPreferencesService, private preferences: UserPreferencesService,
private route: ActivatedRoute, private route: ActivatedRoute,
private router: Router, private router: Router,
store: Store<AppStore>, store: Store<AppStore>,
public permission: NodePermissionService, extensions: ExtensionService,
extensions: ExtensionService) { content: ContentManagementService) {
super(store, extensions); super(store, extensions, content);
} }
ngOnInit() { ngOnInit() {
@ -97,10 +99,6 @@ export class PreviewComponent extends PageComponent implements OnInit {
} }
}); });
this.subscriptions = this.subscriptions.concat([
this.uploadService.fileUploadError.subscribe((error) => this.onFileUploadedError(error))
]);
this.openWith = this.extensions.openWithActions; this.openWith = this.extensions.openWithActions;
} }
@ -113,6 +111,8 @@ export class PreviewComponent extends PageComponent implements OnInit {
try { try {
this.node = await this.contentApi.getNodeInfo(id).toPromise(); this.node = await this.contentApi.getNodeInfo(id).toPromise();
this.selectedEntities = [{ entry: this.node }]; this.selectedEntities = [{ entry: this.node }];
this.canDeletePreview = this.node && this.content.canDeleteNode(this.node);
this.canUpdatePreview = this.node && this.content.canUpdateNode(this.node);
if (this.node && this.node.isFile) { if (this.node && this.node.isFile) {
const nearest = await this.getNearestNodes(this.node.id, this.node.parentId); const nearest = await this.getNearestNodes(this.node.id, this.node.parentId);

View File

@ -88,7 +88,7 @@
<button <button
mat-menu-item mat-menu-item
*ngIf="permission.check(selection.nodes, ['delete'])" *ngIf="canDelete"
[acaMoveNode]="selection.nodes"> [acaMoveNode]="selection.nodes">
<mat-icon>library_books</mat-icon> <mat-icon>library_books</mat-icon>
<span>{{ 'APP.ACTIONS.MOVE' | translate }}</span> <span>{{ 'APP.ACTIONS.MOVE' | translate }}</span>
@ -96,7 +96,7 @@
<button <button
mat-menu-item mat-menu-item
*ngIf="permission.check(selection.nodes, ['delete'])" *ngIf="canDelete"
[acaDeleteNode]="selection.nodes"> [acaDeleteNode]="selection.nodes">
<mat-icon>delete</mat-icon> <mat-icon>delete</mat-icon>
<span>{{ 'APP.ACTIONS.DELETE' | translate }}</span> <span>{{ 'APP.ACTIONS.DELETE' | translate }}</span>

View File

@ -25,11 +25,8 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { MinimalNodeEntity } from 'alfresco-js-api'; import { MinimalNodeEntity } from 'alfresco-js-api';
import { UploadService } from '@alfresco/adf-core';
import { ContentManagementService } from '../../common/services/content-management.service'; import { ContentManagementService } from '../../common/services/content-management.service';
import { PageComponent } from '../page.component'; import { PageComponent } from '../page.component';
import { NodePermissionService } from '../../common/services/node-permission.service';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { AppStore } from '../../store/states/app.state'; import { AppStore } from '../../store/states/app.state';
import { ExtensionService } from '../../extensions/extension.service'; import { ExtensionService } from '../../extensions/extension.service';
@ -38,14 +35,12 @@ import { ExtensionService } from '../../extensions/extension.service';
templateUrl: './recent-files.component.html' templateUrl: './recent-files.component.html'
}) })
export class RecentFilesComponent extends PageComponent implements OnInit { export class RecentFilesComponent extends PageComponent implements OnInit {
constructor( constructor(
store: Store<AppStore>, store: Store<AppStore>,
extensions: ExtensionService, extensions: ExtensionService,
private uploadService: UploadService, content: ContentManagementService
private content: ContentManagementService, ) {
public permission: NodePermissionService) { super(store, extensions, content);
super(store, extensions);
} }
ngOnInit() { ngOnInit() {
@ -54,8 +49,7 @@ export class RecentFilesComponent extends PageComponent implements OnInit {
this.subscriptions = this.subscriptions.concat([ this.subscriptions = this.subscriptions.concat([
this.content.nodesDeleted.subscribe(() => this.reload()), this.content.nodesDeleted.subscribe(() => this.reload()),
this.content.nodesMoved.subscribe(() => this.reload()), this.content.nodesMoved.subscribe(() => this.reload()),
this.content.nodesRestored.subscribe(() => this.reload()), this.content.nodesRestored.subscribe(() => this.reload())
this.uploadService.fileUploadError.subscribe((error) => this.onFileUploadedError(error))
]); ]);
} }

View File

@ -26,12 +26,13 @@
import { Component, OnInit, ViewChild } from '@angular/core'; import { Component, OnInit, ViewChild } from '@angular/core';
import { NodePaging, Pagination, MinimalNodeEntity } from 'alfresco-js-api'; import { NodePaging, Pagination, MinimalNodeEntity } from 'alfresco-js-api';
import { ActivatedRoute, Params } from '@angular/router'; import { ActivatedRoute, Params } from '@angular/router';
import { SearchQueryBuilderService, SearchComponent as AdfSearchComponent, NodePermissionService } from '@alfresco/adf-content-services'; import { SearchQueryBuilderService, SearchComponent as AdfSearchComponent } from '@alfresco/adf-content-services';
import { PageComponent } from '../../page.component'; import { PageComponent } from '../../page.component';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { AppStore } from '../../../store/states/app.state'; import { AppStore } from '../../../store/states/app.state';
import { NavigateToFolder } from '../../../store/actions'; import { NavigateToFolder } from '../../../store/actions';
import { ExtensionService } from '../../../extensions/extension.service'; import { ExtensionService } from '../../../extensions/extension.service';
import { ContentManagementService } from '../../../common/services/content-management.service';
@Component({ @Component({
selector: 'aca-search-results', selector: 'aca-search-results',
@ -51,13 +52,13 @@ export class SearchResultsComponent extends PageComponent implements OnInit {
sorting = ['name', 'asc']; sorting = ['name', 'asc'];
constructor( constructor(
public permission: NodePermissionService,
private queryBuilder: SearchQueryBuilderService, private queryBuilder: SearchQueryBuilderService,
private route: ActivatedRoute, private route: ActivatedRoute,
store: Store<AppStore>, store: Store<AppStore>,
extensions: ExtensionService extensions: ExtensionService,
content: ContentManagementService
) { ) {
super(store, extensions); super(store, extensions, content);
queryBuilder.paging = { queryBuilder.paging = {
skipCount: 0, skipCount: 0,

View File

@ -77,7 +77,7 @@
<button <button
mat-menu-item mat-menu-item
*ngIf="permission.check(selection.nodes, ['delete'], { target: 'allowableOperationsOnTarget' })" *ngIf="canDeleteShared"
[acaMoveNode]="selection.nodes"> [acaMoveNode]="selection.nodes">
<mat-icon>library_books</mat-icon> <mat-icon>library_books</mat-icon>
<span>{{ 'APP.ACTIONS.MOVE' | translate }}</span> <span>{{ 'APP.ACTIONS.MOVE' | translate }}</span>
@ -85,7 +85,7 @@
<button <button
mat-menu-item mat-menu-item
*ngIf="permission.check(selection.nodes, ['delete'])" *ngIf="canDelete"
[acaUnshareNode]="selection.nodes"> [acaUnshareNode]="selection.nodes">
<mat-icon>stop_screen_share</mat-icon> <mat-icon>stop_screen_share</mat-icon>
<span>{{ 'APP.ACTIONS.UNSHARE' | translate }}</span> <span>{{ 'APP.ACTIONS.UNSHARE' | translate }}</span>
@ -93,7 +93,7 @@
<button <button
mat-menu-item mat-menu-item
*ngIf="permission.check(selection.nodes, ['delete'], { target: 'allowableOperationsOnTarget' })" *ngIf="canDeleteShared"
[acaDeleteNode]="selection.nodes"> [acaDeleteNode]="selection.nodes">
<mat-icon>delete</mat-icon> <mat-icon>delete</mat-icon>
<span>{{ 'APP.ACTIONS.DELETE' | translate }}</span> <span>{{ 'APP.ACTIONS.DELETE' | translate }}</span>
@ -101,7 +101,7 @@
<button <button
mat-menu-item mat-menu-item
*ngIf="selection.file && permission.check(selection.file, ['update'], { target: 'allowableOperationsOnTarget' })" *ngIf="canUpdateShared"
[acaNodeVersions]="selection.file"> [acaNodeVersions]="selection.file">
<mat-icon>history</mat-icon> <mat-icon>history</mat-icon>
<span>{{ 'APP.ACTIONS.VERSIONS' | translate }}</span> <span>{{ 'APP.ACTIONS.VERSIONS' | translate }}</span>

View File

@ -24,10 +24,7 @@
*/ */
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { UploadService } from '@alfresco/adf-core';
import { ContentManagementService } from '../../common/services/content-management.service'; import { ContentManagementService } from '../../common/services/content-management.service';
import { NodePermissionService } from '../../common/services/node-permission.service';
import { PageComponent } from '../page.component'; import { PageComponent } from '../page.component';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { AppStore } from '../../store/states/app.state'; import { AppStore } from '../../store/states/app.state';
@ -40,11 +37,9 @@ export class SharedFilesComponent extends PageComponent implements OnInit {
constructor( constructor(
store: Store<AppStore>, store: Store<AppStore>,
extensions: ExtensionService, extensions: ExtensionService,
private uploadService: UploadService, content: ContentManagementService
private content: ContentManagementService,
public permission: NodePermissionService
) { ) {
super(store, extensions); super(store, extensions, content);
} }
ngOnInit() { ngOnInit() {
@ -55,9 +50,6 @@ export class SharedFilesComponent extends PageComponent implements OnInit {
this.content.nodesMoved.subscribe(() => this.reload()), this.content.nodesMoved.subscribe(() => this.reload()),
this.content.nodesRestored.subscribe(() => this.reload()), this.content.nodesRestored.subscribe(() => this.reload()),
this.content.linksUnshared.subscribe(() => this.reload()), this.content.linksUnshared.subscribe(() => this.reload()),
this.uploadService.fileUploadError.subscribe(error =>
this.onFileUploadedError(error)
)
]); ]);
} }
} }

View File

@ -65,7 +65,7 @@ export class SidenavComponent implements OnInit, OnDestroy {
.subscribe(node => { .subscribe(node => {
this.node = node; this.node = node;
this.createActions = this.extensions.getFolderCreateActions(node); this.createActions = this.extensions.getFolderCreateActions(node);
this.canCreateContent = this.permission.check(node, ['create']); this.canCreateContent = node && this.permission.check(node, ['create']);
}); });
} }

View File

@ -39,10 +39,10 @@ import { Observable } from 'rxjs/Observable';
export class TrashcanComponent extends PageComponent implements OnInit { export class TrashcanComponent extends PageComponent implements OnInit {
user$: Observable<ProfileState>; user$: Observable<ProfileState>;
constructor(private contentManagementService: ContentManagementService, constructor(content: ContentManagementService,
extensions: ExtensionService, extensions: ExtensionService,
store: Store<AppStore>) { store: Store<AppStore>) {
super(store, extensions); super(store, extensions, content);
this.user$ = this.store.select(selectUser); this.user$ = this.store.select(selectUser);
} }
@ -50,9 +50,9 @@ export class TrashcanComponent extends PageComponent implements OnInit {
super.ngOnInit(); super.ngOnInit();
this.subscriptions.push( this.subscriptions.push(
this.contentManagementService.nodesRestored.subscribe(() => this.reload()), this.content.nodesRestored.subscribe(() => this.reload()),
this.contentManagementService.nodesPurged.subscribe(() => this.reload()), this.content.nodesPurged.subscribe(() => this.reload()),
this.contentManagementService.nodesRestored.subscribe(() => this.reload()), this.content.nodesRestored.subscribe(() => this.reload()),
); );
} }
} }

View File

@ -34,7 +34,6 @@ import {
UserPreferencesService, UserPreferencesService,
AppConfigService, AppConfigService,
StorageService, StorageService,
CookieService,
AlfrescoApiService, AlfrescoApiService,
LogService, LogService,
NotificationService, NotificationService,
@ -42,7 +41,6 @@ import {
ContentService, ContentService,
ThumbnailService, ThumbnailService,
UploadService, UploadService,
PeopleContentService,
AlfrescoApiMock AlfrescoApiMock
} from '@alfresco/adf-core'; } from '@alfresco/adf-core';
import { HttpClientModule } from '@angular/common/http'; import { HttpClientModule } from '@angular/common/http';
@ -100,7 +98,6 @@ import { ExtensionService } from '../extensions/extension.service';
UserPreferencesService, UserPreferencesService,
AppConfigService, AppConfigService,
StorageService, StorageService,
CookieService,
AlfrescoApiService, AlfrescoApiService,
LogService, LogService,
NotificationService, NotificationService,
@ -110,7 +107,6 @@ import { ExtensionService } from '../extensions/extension.service';
UploadService, UploadService,
CustomResourcesService, CustomResourcesService,
DocumentListService, DocumentListService,
PeopleContentService,
ContentManagementService, ContentManagementService,
NodeActionsService, NodeActionsService,