[ACA-1273] Preview - node metadata (#272)

This commit is contained in:
Cilibiu Bogdan
2018-04-04 11:24:26 +03:00
committed by Denys Vuika
parent b63f6c0197
commit 142b179841
9 changed files with 326 additions and 35 deletions

View File

@@ -63,6 +63,7 @@ import { VersionManagerDialogAdapterComponent } from './components/versions-dial
import { BrowsingFilesService } from './common/services/browsing-files.service';
import { ContentManagementService } from './common/services/content-management.service';
import { NodeActionsService } from './common/services/node-actions.service';
import { NodePermissionService } from './common/services/node-permission.service';
import { MatMenuModule, MatIconModule, MatButtonModule, MatDialogModule, MatInputModule } from '@angular/material';
import { SearchComponent } from './components/search/search.component';
@@ -125,7 +126,8 @@ import { SearchComponent } from './components/search/search.component';
},
BrowsingFilesService,
ContentManagementService,
NodeActionsService
NodeActionsService,
NodePermissionService
],
entryComponents: [
VersionManagerDialogAdapterComponent

View File

@@ -0,0 +1,190 @@
/*!
* @license
* Alfresco Example Content Application
*
* Copyright (C) 2005 - 2018 Alfresco Software Limited
*
* This file is part of the Alfresco Example Content Application.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { NodePermissionService } from './node-permission.service';
describe('NodePermissionService', () => {
let permission: NodePermissionService;
beforeEach(() => {
permission = new NodePermissionService();
});
it('should return false when source is null', () => {
const source = null;
expect(permission.check(source, ['update'])).toBe(false);
});
describe('Multiple source permission', () => {
it('should return true when source has allowableOperations permission', () => {
const source = [
{ entry: { allowableOperations: ['update'] } },
{ entry: { allowableOperations: ['update'] } },
{ entry: { allowableOperations: ['update'] } }
];
expect(permission.check(source, ['update'])).toBe(true);
});
it('should return true when source has allowableOperationsOnTarget permission', () => {
const source = [
{ entry: { allowableOperationsOnTarget: ['update'] } },
{ entry: { allowableOperationsOnTarget: ['update'] } },
{ entry: { allowableOperationsOnTarget: ['update'] } }
];
expect(permission.check(source, ['update'])).toBe(true);
});
it('should return false when source does not have allowableOperations permission', () => {
const source = [
{ entry: { allowableOperations: ['update'] } },
{ entry: { allowableOperations: ['update'] } },
{ entry: { allowableOperations: ['delete'] } }
];
expect(permission.check(source, ['update'])).toBe(false);
});
it('should return false when source does not have allowableOperationsOnTarget permission', () => {
const source = [
{ entry: { allowableOperationsOnTarget: ['update'] } },
{ entry: { allowableOperationsOnTarget: ['update'] } },
{ entry: { allowableOperationsOnTarget: ['delete'] } }
];
expect(permission.check(source, ['update'])).toBe(false);
});
it('should return true when source has `OR` allowableOperations permission', () => {
const source = [
{ entry: { allowableOperations: ['update' , 'delete'] } },
{ entry: { allowableOperations: ['update', 'create'] } },
{ entry: { allowableOperations: ['update', 'updatePermissions'] } }
];
expect(permission.check(source, ['update', 'create'])).toBe(true);
});
it('should return true when source has `AND` allowableOperations permission', () => {
const source = [
{ entry: { allowableOperations: ['update' , 'delete', 'other'] } },
{ entry: { allowableOperations: ['update', 'create', 'other'] } },
{ entry: { allowableOperations: ['update', 'updatePermissions', 'other'] } }
];
expect(permission.check(source, ['update', 'other'], 'AND')).toBe(true);
});
it('should return false when source has no `AND` allowableOperations permission', () => {
const source = [
{ entry: { allowableOperations: ['update' , 'delete', 'other'] } },
{ entry: { allowableOperations: ['update', 'create', 'other'] } },
{ entry: { allowableOperations: ['update', 'updatePermissions', 'other'] } }
];
expect(permission.check(source, ['update', 'bogus'], 'AND')).toBe(false);
});
it('should return false when source has no allowableOperations', () => {
const source = [
{ entry: { allowableOperations: [] } },
{ entry: { allowableOperations: [] } },
{ entry: { allowableOperations: ['update'] } }
];
expect(permission.check(source, ['update'])).toBe(false);
});
it('should return false when source has no allowableOperations property', () => {
const source = [
{ entry: { } },
{ entry: { } },
{ entry: { allowableOperations: ['update'] } }
];
expect(permission.check(source, ['update'])).toBe(false);
});
});
describe('Single source permission', () => {
it('should return true when source has allowableOperations permission', () => {
const source = { entry: { allowableOperations: ['update'] } };
expect(permission.check(source, ['update'])).toBe(true);
});
it('should return true when source has allowableOperationsOnTarget permission', () => {
const source = { entry: { allowableOperationsOnTarget: ['update'] } };
expect(permission.check(source, ['update'])).toBe(true);
});
it('should return false when source does not have allowableOperations permission', () => {
const source = { entry: { allowableOperations: ['delete'] } };
expect(permission.check(source, ['update'])).toBe(false);
});
it('should return false when source does not have allowableOperationsOnTarget permission', () => {
const source = { entry: { allowableOperationsOnTarget: ['delete'] } };
expect(permission.check(source, ['update'])).toBe(false);
});
it('should return true when source has `OR` allowableOperations permission', () => {
const source = { entry: { allowableOperations: ['update'] } };
expect(permission.check(source, ['update', 'create'])).toBe(true);
});
it('should return true when source has `AND` allowableOperations permission', () => {
const source = { entry: { allowableOperations: ['update', 'other'] } };
expect(permission.check(source, ['update', 'other'], 'AND')).toBe(true);
});
it('should return false when source has no `AND` allowableOperations permission', () => {
const source = { entry: { allowableOperations: ['update', 'updatePermissions', 'other'] } };
expect(permission.check(source, ['update', 'bogus'], 'AND')).toBe(false);
});
it('should return false when source has no allowableOperations', () => {
const source = { entry: { allowableOperations: [] } };
expect(permission.check(source, ['update'])).toBe(false);
});
it('should return false when source has no allowableOperations property', () => {
const source = { entry: { } };
expect(permission.check(source, ['update'])).toBe(false);
});
});
});

View File

@@ -0,0 +1,77 @@
/*!
* @license
* Alfresco Example Content Application
*
* Copyright (C) 2005 - 2018 Alfresco Software Limited
*
* This file is part of the Alfresco Example Content Application.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { Injectable } from '@angular/core';
@Injectable()
export class NodePermissionService {
static DEFAULT_OPERATION = 'OR';
check(source: any, permissions: string[], operation: string = NodePermissionService.DEFAULT_OPERATION): boolean {
if (source) {
if (Array.isArray(source) && source.length) {
const arr = this.sanitize(source);
return !!arr.length && source.every(node => this.hasPermission(node, permissions, operation));
}
return this.hasPermission(source, permissions, operation);
}
return false;
}
private hasPermission(node, permissions, operation): boolean {
const allowableOperations = this.getAllowableOperations(node);
if (allowableOperations.length) {
if (operation === NodePermissionService.DEFAULT_OPERATION) {
return permissions.some(permission => allowableOperations.includes(permission));
} else {
return permissions.every(permission => allowableOperations.includes(permission));
}
}
return false;
}
private getAllowableOperations(node): string[] {
const entry = node.entry || node;
if (entry.allowableOperationsOnTarget) {
return entry.allowableOperationsOnTarget;
}
if (entry.allowableOperations) {
return entry.allowableOperations;
}
return [];
}
private sanitize(selection): any[] {
return (selection || []).filter(item => item);
}
}

View File

@@ -1,7 +1,23 @@
<ng-container *ngIf="nodeId">
<ng-template #sidebarTemplate>
<adf-info-drawer [title]="'APP.INFO_DRAWER.TITLE' | translate">
<adf-info-drawer-tab [label]="'APP.INFO_DRAWER.TABS.PROPERTIES' | translate">
<adf-content-metadata-card
[readOnly]="!permission.check(node, ['update'])"
[displayEmpty]="permission.check(node, ['update'])"
[preset]="'custom'"
[node]="node">
</adf-content-metadata-card>
</adf-info-drawer-tab>
</adf-info-drawer>
</ng-template>
<adf-viewer
[fileNodeId]="nodeId"
[allowNavigate]="navigateMultiple"
[allowSidebar]="true"
[sidebarTemplate]="sidebarTemplate"
[canNavigateBefore]="previousNodeId"
[canNavigateNext]="nextNodeId"
[overlayMode]="true"
@@ -15,7 +31,10 @@
mat-menu-item
#selection="adfFavorite"
[adf-node-favorite]="selectedEntities">
<mat-icon [ngClass]="{ 'icon-highlight': selection.hasFavorites() }">
<mat-icon [ngClass]="{
'toolbar__option--active': selection.hasFavorites(),
'toolbar__option--default': !selection.hasFavorites()
}">
{{ selection.hasFavorites() ? 'star' :'star_border' }}
</mat-icon>
<span>{{ 'APP.ACTIONS.FAVORITE' | translate }}</span>
@@ -23,33 +42,32 @@
<button
mat-menu-item
*ngIf="canCopyFile()"
[app-copy-node]="selectedEntities">
<mat-icon>content_copy</mat-icon>
<mat-icon color="primary">content_copy</mat-icon>
<span>{{ 'APP.ACTIONS.COPY' | translate }}</span>
</button>
<button
mat-menu-item
*ngIf="canMoveFile()"
*ngIf="permission.check(node, ['delete'])"
[app-move-node]="selectedEntities">
<mat-icon>library_books</mat-icon>
<mat-icon color="primary">library_books</mat-icon>
<span>{{ 'APP.ACTIONS.MOVE' | translate }}</span>
</button>
<button
mat-menu-item
*ngIf="canDeleteFile()"
*ngIf="permission.check(node, ['delete'])"
(click)="deleteFile()">
<mat-icon>delete</mat-icon>
<mat-icon color="primary">delete</mat-icon>
<span>{{ 'APP.ACTIONS.DELETE' | translate }}</span>
</button>
<button
mat-menu-item
*ngIf="canManageVersions()"
*ngIf="permission.check(node, ['update'])"
[app-node-versions]="selectedEntities">
<mat-icon>storage</mat-icon>
<mat-icon color="primary">storage</mat-icon>
<span>{{ 'APP.ACTIONS.VERSIONS' | translate }}</span>
</button>
</adf-viewer-more-actions>

View File

@@ -36,7 +36,7 @@ import { HttpClientModule } from '@angular/common/http';
import { PreviewComponent } from './preview.component';
import { Observable } from 'rxjs/Rx';
import { ContentManagementService } from '../../common/services/content-management.service';
import { NodePermissionService } from '../../common/services/node-permission.service';
import { MatSnackBarModule } from '@angular/material';
describe('PreviewComponent', () => {
@@ -64,7 +64,7 @@ describe('PreviewComponent', () => {
CookieService,
NotificationService,
UserPreferencesService,
ContentManagementService
NodePermissionService
],
declarations: [
PreviewComponent,

View File

@@ -27,7 +27,7 @@ import { Component, OnInit, ViewEncapsulation } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { AlfrescoApiService, UserPreferencesService, ObjectUtils } from '@alfresco/adf-core';
import { Node, MinimalNodeEntity } from 'alfresco-js-api';
import { ContentManagementService } from '../../common/services/content-management.service';
import { NodePermissionService } from '../../common/services/node-permission.service';
@Component({
selector: 'app-preview',
@@ -57,7 +57,7 @@ export class PreviewComponent implements OnInit {
private route: ActivatedRoute,
private apiService: AlfrescoApiService,
private preferences: UserPreferencesService,
private content: ContentManagementService) {
public permission: NodePermissionService) {
}
ngOnInit() {
@@ -324,28 +324,11 @@ export class PreviewComponent implements OnInit {
return path;
}
canDeleteFile(): boolean {
return this.content.canDeleteNode(this.node);
}
async deleteFile() {
try {
await this.content.deleteNode(this.node);
await this.permission.check(this.node, ['delete']);
this.onVisibilityChanged(false);
} catch {
}
}
canMoveFile(): boolean {
return this.content.canMoveNode(this.node);
}
canCopyFile(): boolean {
return this.content.canCopyNode(this.node);
}
canManageVersions(): boolean {
return this.node.isFile && this.content.nodeHasPermission(this.node, 'update');
}
}

View File

@@ -27,5 +27,6 @@ ng-component {
@import './overrides/alfresco-upload-button';
@import './overrides/alfresco-upload-dialog';
@import './overrides/toolbar';
@import './overrides/adf-viewer-more-actions';
@import './overrides/breadcrumb';
@import './overrides/adf-info-drawer';

View File

@@ -3,6 +3,7 @@
@import '../components/sidenav/sidenav.component.theme';
@import './overrides/toolbar';
@import './overrides/adf-viewer-more-actions';
$grey-scale: (
50 : #e0e0e0,
@@ -47,4 +48,5 @@ $custom-theme: mat-light-theme($custom-theme-primary, $custom-theme-accent);
@mixin custom-theme($theme) {
@include sidenav-component-theme($custom-theme);
@include toolbar-component-theme($custom-theme);
@include viewer-more-actions-component-theme($custom-theme);
}

View File

@@ -0,0 +1,18 @@
@mixin viewer-more-actions-component-theme($theme) {
$primary: map-get($theme, primary);
$accent: map-get($theme, accent);
$background: map-get($theme, background);
.adf-viewer-more-actions {
@include angular-material-theme($theme);
.toolbar__option--active {
color: mat-color($accent) !important;
}
.toolbar__option--default {
color: mat-color($primary, .87) !important;
}
}
}