[ADF-2432] Creating component to display node permission (#3106)

* [ADF-2432] start adding demo shell changes for permissions

* [ADF-2432] permission display component - phase 1

* [ADF-2432] added permissions to node query|

* [ADF-2432] display permission table for node - phase 2

* [ADF-2432] fixed layout for display permissions

* [ADF-2432] added test and documentation for permission display

* [ADF-2432] fixed wrong rebase changes

* [ADF-2432] added peer review changes

* [ADF-2432] added license header to mock file
This commit is contained in:
Vito
2018-03-21 22:02:40 +00:00
committed by Eugenio Romano
parent ae8b7419a0
commit 99e694ef98
26 changed files with 602 additions and 8 deletions

View File

@@ -89,6 +89,7 @@
"VERSIONS": "Manage versions",
"METADATA": "Info",
"DOWNLOAD": "Download",
"PERMISSION": "Permission",
"FOLDER": {
"COPY": "Copy",
"MOVE": "Move",

View File

@@ -47,6 +47,7 @@ import { ReactiveFormsModule } from '@angular/forms';
import { TaskAttachmentsComponent } from './components/process-service/task-attachments.component';
import { ProcessAttachmentsComponent } from './components/process-service/process-attachments.component';
import { SharedLinkViewComponent } from './components/shared-link-view/shared-link-view.component';
import { DemoPermissionComponent } from './components/permissions/demo-permissions.component';
@NgModule({
@@ -94,7 +95,8 @@ import { SharedLinkViewComponent } from './components/shared-link-view/shared-li
ProcessAttachmentsComponent,
OverlayViewerComponent,
SharedLinkViewComponent,
FormLoadingComponent
FormLoadingComponent,
DemoPermissionComponent
],
providers: [
{ provide: AppConfigService, useClass: DebugAppConfigService },

View File

@@ -46,6 +46,7 @@ import { FormListComponent } from './components/form/form-list.component';
import { OverlayViewerComponent } from './components/overlay-viewer/overlay-viewer.component';
import { SharedLinkViewComponent } from './components/shared-link-view/shared-link-view.component';
import { FormLoadingComponent } from './components/form/form-loading.component';
import { DemoPermissionComponent } from './components/permissions/demo-permissions.component';
export const appRoutes: Routes = [
{ path: 'login', component: LoginComponent },
@@ -165,6 +166,11 @@ export const appRoutes: Routes = [
component: SocialComponent,
canActivate: [AuthGuardEcm]
},
{
path: 'permissions/:id',
component: DemoPermissionComponent,
canActivate: [AuthGuardEcm]
},
{ path: 'about', component: AboutComponent },
{ path: 'form', component: FormComponent },
{ path: 'form-list', component: FormListComponent },

View File

@@ -178,7 +178,7 @@ export class DataTableComponent {
getRowForNode() {
const opts: any = {
includeSource: true,
include: ['path', 'properties', 'allowableOperations']
include: ['path', 'properties', 'allowableOperations', 'permissions']
};
Observable.fromPromise(this.apiService.getInstance().nodes

View File

@@ -308,6 +308,13 @@
title="DOCUMENT_LIST.ACTIONS.DOCUMENT.PROCESS_ACTION"
(execute)="startProcesAction($event)">
</content-action>
<content-action
icon="settings_input_component"
title="{{'DOCUMENT_LIST.ACTIONS.PERMISSION' | translate}}"
permission="copy"
(error)="onContentActionError($event)"
(execute)="onPermissionRequested($event)">
</content-action>
</content-actions>
</adf-document-list>
<adf-pagination

View File

@@ -344,6 +344,10 @@ export class FilesComponent implements OnInit, OnChanges, OnDestroy {
this.reloadForInfiniteScrolling();
}
onPermissionRequested(node) {
this.router.navigate(['/permissions', node.value.entry.id]);
}
private reloadForInfiniteScrolling() {
if (this.infiniteScrolling) {
this.documentList.skipCount = 0;

View File

@@ -0,0 +1,2 @@
<adf-permission-list [nodeId]="nodeId">
</adf-permission-list>

View File

@@ -0,0 +1,43 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Component, Optional, OnInit } from '@angular/core';
import { ActivatedRoute, Params} from '@angular/router';
@Component({
selector: 'app-permissions',
templateUrl: './demo-permissions.component.html',
styleUrls: ['./demo-permissions.component.scss']
})
export class DemoPermissionComponent implements OnInit {
nodeId: string;
constructor(@Optional() private route: ActivatedRoute) {
}
ngOnInit() {
if (this.route) {
this.route.params.forEach((params: Params) => {
if (params['id']) {
this.nodeId = params['id'];
}
});
}
}
}

View File

@@ -0,0 +1,26 @@
---
Added: v2.3.0
Status: Active
Last reviewed: 2018-03-12
---
# Permission List Component
![Permission List](../docassets/images/adf-permission-list.png)
## Basic Usage
```html
<adf-permission-list [nodeId]="nodeId">
</adf-permission-list>
```
### Properties
| Name | Type | Default value | Description |
| ---- | ---- | ------------- | ----------- |
| nodeId | `string` | `null` | node id which we want to show the permissions |
## Details
This component use the `datatable` to show the permission retrieved from the node service.

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB

View File

@@ -36,6 +36,7 @@ import { ContentDirectiveModule } from './directives/content-directive.module';
import { DialogModule } from './dialogs/dialog.module';
import { FolderDirectiveModule } from './folder-directive/folder-directive.module';
import { ContentMetadataModule } from './content-metadata/content-metadata.module';
import { PermissionManagerModule } from './permission-manager/permission-manager.module';
@NgModule({
imports: [
@@ -57,7 +58,8 @@ import { ContentMetadataModule } from './content-metadata/content-metadata.modul
ContentMetadataModule,
DialogModule,
FolderDirectiveModule,
ContentDirectiveModule
ContentDirectiveModule,
PermissionManagerModule
],
providers: [
{
@@ -84,7 +86,8 @@ import { ContentMetadataModule } from './content-metadata/content-metadata.modul
ContentMetadataModule,
DialogModule,
FolderDirectiveModule,
ContentDirectiveModule
ContentDirectiveModule,
PermissionManagerModule
]
})
export class ContentModule {

View File

@@ -50,7 +50,7 @@ export class DocumentListService {
let params: any = {
includeSource: true,
include: ['path', 'properties', 'allowableOperations']
include: ['path', 'properties', 'allowableOperations', 'permissions']
};
if (folder) {
@@ -127,7 +127,7 @@ export class DocumentListService {
getFolderNode(nodeId: string): Promise<MinimalNodeEntryEntity> {
let opts: any = {
includeSource: true,
include: ['path', 'properties', 'allowableOperations']
include: ['path', 'properties', 'allowableOperations', 'permissions']
};
let nodes: any = this.apiService.getInstance().nodes;

View File

@@ -206,5 +206,13 @@
"CLOSE": "CLOSE",
"COPY-LINK": "COPY LINK"
}
},
"PERMISSION_MANAGER": {
"PERMISSION_DISPLAY": {
"INHERITED" : "Inherited",
"AUTHORITY_ID" : "Authority ID",
"NAME": "Name",
"LOCALLY_SET" : "Locally set"
}
}
}

View File

@@ -31,6 +31,7 @@ export * from './dialogs/dialog.module';
export * from './folder-directive/folder-directive.module';
export * from './content-metadata/content-metadata.module';
export * from './directives/content-directive.module';
export * from './permission-manager/permission-manager.module';
export * from './directives';
export * from './social';
@@ -46,3 +47,4 @@ export * from './content-node-selector';
export * from './dialogs';
export * from './folder-directive';
export * from './content-metadata';
export * from './permission-manager';

View File

@@ -0,0 +1,185 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export const fakeNodeWithPermissions: any = {
'aspectNames': [
'cm:auditable',
'cm:taggable',
'cm:author',
'cm:titled',
'app:uifacets'
],
'createdAt': '2017-11-16T16:29:38.638+0000',
'isFolder': true,
'isFile': false,
'createdByUser': {
'id': 'System',
'displayName': 'System'
},
'modifiedAt': '2018-03-21T03:17:58.783+0000',
'permissions': {
'inherited': [
{
'authorityId': 'guest',
'name': 'Read',
'accessStatus': 'ALLOWED'
},
{
'authorityId': 'GROUP_EVERYONE',
'name': 'Read',
'accessStatus': 'ALLOWED'
}
],
'locallySet': [
{
'authorityId': 'GROUP_EVERYONE',
'name': 'Contributor',
'accessStatus': 'ALLOWED'
}
],
'settable': [
'Contributor',
'Collaborator',
'Coordinator',
'Editor',
'Consumer'
],
'isInheritanceEnabled': true
},
'modifiedByUser': {
'id': 'admin',
'displayName': 'PedroH Hernandez'
},
'name': 'test',
'id': 'f472543f-7218-403d-917b-7a5861257244',
'nodeType': 'cm:folder',
'properties': {
'cm:title': 'test',
'cm:author': 'yagud',
'cm:taggable': [
'e8c8fbba-03ba-4fa6-86b1-f7ad7c296409'
],
'cm:description': 'sleepery',
'app:icon': 'space-icon-default'
}
};
export const fakeNodeInheritedOnly: any = {
'aspectNames': [
'cm:auditable',
'cm:taggable',
'cm:author',
'cm:titled',
'app:uifacets'
],
'createdAt': '2017-11-16T16:29:38.638+0000',
'isFolder': true,
'isFile': false,
'createdByUser': {
'id': 'System',
'displayName': 'System'
},
'modifiedAt': '2018-03-21T03:17:58.783+0000',
'permissions': {
'inherited': [
{
'authorityId': 'guest',
'name': 'Read',
'accessStatus': 'ALLOWED'
},
{
'authorityId': 'GROUP_EVERYONE',
'name': 'Read',
'accessStatus': 'ALLOWED'
}
],
'settable': [
'Contributor',
'Collaborator',
'Coordinator',
'Editor',
'Consumer'
],
'isInheritanceEnabled': true
},
'modifiedByUser': {
'id': 'admin',
'displayName': 'PedroH Hernandez'
},
'name': 'test',
'id': 'f472543f-7218-403d-917b-7a5861257244',
'nodeType': 'cm:folder',
'properties': {
'cm:title': 'test',
'cm:author': 'yagud',
'cm:taggable': [
'e8c8fbba-03ba-4fa6-86b1-f7ad7c296409'
],
'cm:description': 'sleepery',
'app:icon': 'space-icon-default'
}
};
export const fakeNodeWithOnlyLocally: any = {
'aspectNames': [
'cm:auditable',
'cm:taggable',
'cm:author',
'cm:titled',
'app:uifacets'
],
'createdAt': '2017-11-16T16:29:38.638+0000',
'isFolder': true,
'isFile': false,
'createdByUser': {
'id': 'System',
'displayName': 'System'
},
'modifiedAt': '2018-03-21T03:17:58.783+0000',
'permissions': {
'locallySet': [
{
'authorityId': 'GROUP_EVERYONE',
'name': 'Contributor',
'accessStatus': 'ALLOWED'
}
],
'settable': [
'Contributor',
'Collaborator',
'Coordinator',
'Editor',
'Consumer'
],
'isInheritanceEnabled': true
},
'modifiedByUser': {
'id': 'admin',
'displayName': 'PedroH Hernandez'
},
'name': 'test',
'id': 'f472543f-7218-403d-917b-7a5861257244',
'nodeType': 'cm:folder',
'properties': {
'cm:title': 'test',
'cm:author': 'yagud',
'cm:taggable': [
'e8c8fbba-03ba-4fa6-86b1-f7ad7c296409'
],
'cm:description': 'sleepery',
'app:icon': 'space-icon-default'
}
};

View File

@@ -0,0 +1,28 @@
<div id="adf-permission-display-container" class="adf-display-permission-container">
<adf-datatable [rows]="permissionList" class="adf-datatable-permission">
<data-columns>
<data-column key="icon" type="icon" [sortable]="false">
</data-column>
<data-column title="{{'PERMISSION_MANAGER.PERMISSION_DISPLAY.AUTHORITY_ID' | translate}}" key="authorityId"></data-column>
<data-column title="{{'PERMISSION_MANAGER.PERMISSION_DISPLAY.NAME' | translate}}" key="name"></data-column>
<data-column title="{{'PERMISSION_MANAGER.PERMISSION_DISPLAY.INHERITED' | translate}}" key="isInherited">
<ng-template let-entry="$implicit">
<mat-chip-list>
<mat-chip *ngIf="!!entry.data.getValue(entry.row, entry.col) else locally_set_chip"
id="adf-permission-inherited-label"
color="primary" selected="true">{{'PERMISSION_MANAGER.PERMISSION_DISPLAY.INHERITED' | translate}}</mat-chip>
</mat-chip-list>
<ng-template #locally_set_chip>
<mat-chip-list>
<mat-chip id="adf-permission-locallyset-label"
color="accent" selected="true">
{{'PERMISSION_MANAGER.PERMISSION_DISPLAY.LOCALLY_SET' | translate}}
</mat-chip>
</mat-chip-list>
</ng-template>
</ng-template>
</data-column>
</data-columns>
</adf-datatable>
</div>

View File

@@ -0,0 +1,15 @@
@mixin adf-permission-list-theme($theme) {
.adf{
&-display-permission-container {
display: flex;
justify-content: space-around;
}
&-datatable-permission {
display: flex;
flex-basis: 38%;
}
}
}

View File

@@ -0,0 +1,81 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { PermissionListComponent } from './permission-list.component';
import { NodesApiService } from '@alfresco/adf-core';
import { Observable } from 'rxjs/Observable';
import { fakeNodeWithPermissions, fakeNodeInheritedOnly, fakeNodeWithOnlyLocally} from '../../../mock/permission-list.component.mock';
describe('PermissionDisplayComponent', () => {
let fixture: ComponentFixture<PermissionListComponent>;
let component: PermissionListComponent;
let element: HTMLElement;
let nodeService: NodesApiService;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [
PermissionListComponent
],
providers: [NodesApiService]
}).compileComponents().then(() => {
fixture = TestBed.createComponent(PermissionListComponent);
component = fixture.componentInstance;
element = fixture.nativeElement;
nodeService = TestBed.get(NodesApiService);
});
}));
afterEach(async(() => {
fixture.destroy();
TestBed.resetTestingModule();
}));
it('should be able to render the component', async() => {
fixture.detectChanges();
expect(element.querySelector('#adf-permission-display-container')).not.toBeNull();
});
it('should show the node permissions', async() => {
component.nodeId = 'fake-node-id';
spyOn(nodeService, 'getNode').and.returnValue(Observable.of(fakeNodeWithPermissions));
fixture.detectChanges();
expect(element.querySelector('#adf-permission-display-container')).not.toBeNull();
expect(element.querySelectorAll('.adf-datatable-row').length).toBe(4);
});
it('should show inherited label for inherited permissions', async() => {
component.nodeId = 'fake-node-id';
spyOn(nodeService, 'getNode').and.returnValue(Observable.of(fakeNodeInheritedOnly));
fixture.detectChanges();
expect(element.querySelector('#adf-permission-display-container')).not.toBeNull();
expect(element.querySelector('#adf-permission-inherited-label')).toBeDefined();
expect(element.querySelector('#adf-permission-inherited-label')).not.toBeNull();
});
it('should show locally set label for locally set permissions', async() => {
component.nodeId = 'fake-node-id';
spyOn(nodeService, 'getNode').and.returnValue(Observable.of(fakeNodeWithOnlyLocally));
fixture.detectChanges();
expect(element.querySelector('#adf-permission-display-container')).not.toBeNull();
expect(element.querySelector('#adf-permission-locallyset-label')).toBeDefined();
expect(element.querySelector('#adf-permission-locallyset-label')).not.toBeNull();
});
});

View File

@@ -0,0 +1,64 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Component, ViewEncapsulation, Input, OnInit } from '@angular/core';
import { NodesApiService } from '@alfresco/adf-core';
import { MinimalNodeEntryEntity } from 'alfresco-js-api';
import { PermissionDisplayModel } from '../../models/permission.model';
@Component({
selector: 'adf-permission-list',
templateUrl: './permission-list.component.html',
styleUrls: ['./permission-list.component.scss'],
encapsulation: ViewEncapsulation.None
})
export class PermissionListComponent implements OnInit {
@Input()
nodeId: string = '';
permissionList: PermissionDisplayModel[];
constructor(private nodeService: NodesApiService) {
}
ngOnInit() {
this.nodeService.getNode(this.nodeId).subscribe((node: MinimalNodeEntryEntity) => {
this.permissionList = this.getPermissionList(node);
});
}
private getPermissionList(node: MinimalNodeEntryEntity): PermissionDisplayModel[] {
let allPermissions: PermissionDisplayModel[] = [];
if (node.permissions.locallySet) {
node.permissions.locallySet.map((element) => {
let permission = new PermissionDisplayModel(element);
allPermissions.push(permission);
});
}
if (node.permissions.inherited) {
node.permissions.inherited.map((element) => {
let permissionInherited = new PermissionDisplayModel(element);
permissionInherited.isInherited = true;
allPermissions.push(permissionInherited);
});
}
return allPermissions;
}
}

View File

@@ -0,0 +1,18 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export * from './public-api';

View File

@@ -0,0 +1,34 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export class PermissionDisplayModel {
accessStatus: string;
authorityId: string;
name: string;
isInherited: boolean = false;
icon: string;
constructor(obj?: any) {
if (obj) {
this.accessStatus = obj.accessStatus;
this.authorityId = obj.authorityId;
this.name = obj.name;
this.isInherited = obj.isInherited;
this.icon = obj.icon ? obj.icon : 'lock_open';
}
}
}

View File

@@ -0,0 +1,43 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { TranslateModule } from '@ngx-translate/core';
import { MaterialModule } from '../material.module';
import { PermissionListComponent } from './components/permission-display/permission-list.component';
import { DataTableModule, DataColumnModule } from '@alfresco/adf-core';
@NgModule({
imports: [
CommonModule,
FormsModule,
ReactiveFormsModule,
MaterialModule,
TranslateModule,
DataTableModule,
DataColumnModule
],
declarations: [
PermissionListComponent
],
exports: [
PermissionListComponent
]
})
export class PermissionManagerModule {}

View File

@@ -0,0 +1,20 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export * from './components/permission-display/permission-list.component';
export * from './models/permission.model';

View File

@@ -14,6 +14,7 @@
@import '../content-node-selector/content-node-selector.component';
@import '../content-metadata/content-metadata.module';
@import '../permission-manager/components/permission-display/permission-list.component';
@mixin adf-content-services-theme($theme) {
@include adf-breadcrumb-theme($theme);
@@ -28,4 +29,5 @@
@include adf-dialog-theme($theme);
@include adf-content-node-selector-dialog-theme($theme) ;
@include adf-content-metadata-module-theme($theme);
@include adf-permission-list-theme($theme);
}

View File

@@ -46,7 +46,7 @@ export class NodesApiService {
getNode(nodeId: string, options: any = {}): Observable<MinimalNodeEntryEntity> {
const { nodesApi, handleError, getEntryFromEntity } = this;
const defaults = {
include: [ 'path', 'properties', 'allowableOperations' ]
include: [ 'path', 'properties', 'allowableOperations', 'permissions' ]
};
const queryOptions = Object.assign(defaults, options);
const promise = nodesApi
@@ -68,7 +68,7 @@ export class NodesApiService {
const defaults = {
maxItems: this.preferences.paginationSize,
skipCount: 0,
include: [ 'path', 'properties', 'allowableOperations' ]
include: [ 'path', 'properties', 'allowableOperations', 'permissions' ]
};
const queryOptions = Object.assign(defaults, options);
const promise = nodesApi