[ADF-2556] Created component for add group or user to permission (#3242)

* [ADF-2556] first step to create add people or group to permissions

* [ADF-2556] creating a dialog with user results

* [ADF-2556]
integrated service for add and remove permission from node

* [ADF-2556] fixed behaviour and style for add user group

* [ADF-2556] added some refactoring for dialog service

* [ADF-2556] refactoring the dependencies of the components

* [ADF-2556] added some fix and a new key for dialog

* [ADF-2556] start adding test for node permission service

* [ADF-2556] added test for add permission panel component

* [ADf-2556] adding tests for new add permission component

* [ADF-2556] fixed tests and added documentation

* [ADF-2556] fixed documentation for add-node components

* [ADF-2556] added peer review changes
This commit is contained in:
Vito
2018-05-03 15:14:15 +01:00
committed by Eugenio Romano
parent 61a4173ad3
commit 513915b3d9
37 changed files with 1576 additions and 25 deletions

View File

@@ -0,0 +1,25 @@
/*!
* @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 { MinimalNodeEntity } from 'alfresco-js-api';
import { Subject } from 'rxjs/Subject';
export interface AddPermissionDialogData {
title?: string;
nodeId: string;
confirm: Subject<MinimalNodeEntity[]>;
}

View File

@@ -0,0 +1,16 @@
<h2 mat-dialog-title id="add-permission-dialog-title">
{{(data?.title ? data?.title : 'PERMISSION_MANAGER.ADD-PERMISSION.BASE-DIALOG-TITLE') | translate}}
</h2>
<mat-dialog-content>
<adf-add-permission-panel
(select)="onSelect($event)">
</adf-add-permission-panel>
</mat-dialog-content>
<mat-dialog-actions>
<button mat-button mat-dialog-close id="add-permission-dialog-close-button">{{'PERMISSION_MANAGER.ADD-PERMISSION.CLOSE-ACTION' | translate}}</button>
<button mat-button id="add-permission-dialog-confirm-button" [mat-dialog-close]="true"
class="choose-action"
[disabled]="currentSelection?.length === 0"
(click)="onAddClicked()">{{'PERMISSION_MANAGER.ADD-PERMISSION.ADD-ACTION' | translate}}</button>
</mat-dialog-actions>

View File

@@ -0,0 +1,55 @@
@mixin adf-add-permission-dialog-theme($theme) {
$primary: map-get($theme, primary);
$foreground: map-get($theme, foreground);
$background: map-get($theme, background);
.adf-add-permission-dialog {
.mat-dialog-title {
margin-left: 24px;
margin-right: 24px;
font-size: 20px;
font-weight: 600;
font-style: normal;
font-stretch: normal;
line-height: 1.6;
letter-spacing: -0.5px;
color: mat-color($foreground, text, 0.87);
}
.mat-dialog-container {
padding-left: 0;
padding-right: 0;
}
.mat-dialog-content {
margin: 0;
overflow: hidden;
}
.mat-dialog-actions {
padding: 8px;
background-color: mat-color($background, background);
display: flex;
justify-content: flex-end;
color: mat-color($foreground, secondary-text);
button {
text-transform: uppercase;
font-weight: normal;
}
.choose-action {
&[disabled] {
opacity: 0.6;
}
&:enabled {
color: mat-color($primary);
}
}
}
}
}

View File

@@ -0,0 +1,111 @@
/*!
* @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 { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material';
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { ComponentFixture, TestBed, async } from '@angular/core/testing';
import { ContentTestingModule } from '../../../testing/content.testing.module';
import { By } from '@angular/platform-browser';
import { setupTestBed } from '@alfresco/adf-core';
import { AddPermissionDialogComponent } from './add-permission-dialog.component';
import { MinimalNodeEntity } from 'alfresco-js-api';
import { Subject } from 'rxjs/Subject';
import { AddPermissionDialogData } from './add-permission-dialog-data.interface';
import { fakeAuthorityResults } from '../../../mock/add-permission.component.mock';
import { AddPermissionPanelComponent } from './add-permission-panel.component';
describe('AddPermissionDialog', () => {
let fixture: ComponentFixture<AddPermissionDialogComponent>;
let element: HTMLElement;
let data: AddPermissionDialogData = {
title: 'dead or alive you are coming with me',
nodeId: 'fake-node-id',
confirm: new Subject<MinimalNodeEntity[]> ()
};
const dialogRef = {
close: jasmine.createSpy('close')
};
setupTestBed({
imports: [ContentTestingModule],
providers: [
{ provide: MatDialogRef, useValue: dialogRef },
{ provide: MAT_DIALOG_DATA, useValue: data }
],
schemas: [CUSTOM_ELEMENTS_SCHEMA]
});
beforeEach(() => {
fixture = TestBed.createComponent(AddPermissionDialogComponent);
element = fixture.nativeElement;
fixture.detectChanges();
});
afterEach(() => {
fixture.destroy();
});
it('should show the INJECTED title', () => {
const titleElement = fixture.debugElement.query(By.css('#add-permission-dialog-title'));
expect(titleElement).not.toBeNull();
expect(titleElement.nativeElement.innerText).toBe('dead or alive you are coming with me');
});
it('should close the dialog when close button is clicked', () => {
const closeButton: HTMLButtonElement = <HTMLButtonElement> element.querySelector('#add-permission-dialog-close-button');
expect(closeButton).not.toBeNull();
closeButton.click();
expect(dialogRef.close).toHaveBeenCalled();
});
it('should disable the confirm button when no selection is applied', () => {
const confirmButton: HTMLButtonElement = <HTMLButtonElement> element.querySelector('#add-permission-dialog-confirm-button');
expect(confirmButton.disabled).toBeTruthy();
});
it('should enable the button when a selection is done', async(() => {
const addPermissionPanelComponent: AddPermissionPanelComponent = fixture.debugElement.query(By.directive(AddPermissionPanelComponent)).componentInstance;
addPermissionPanelComponent.select.emit(fakeAuthorityResults);
let confirmButton: HTMLButtonElement = <HTMLButtonElement> element.querySelector('#add-permission-dialog-confirm-button');
expect(confirmButton.disabled).toBeTruthy();
fixture.detectChanges();
fixture.whenStable().then(() => {
fixture.detectChanges();
confirmButton = <HTMLButtonElement> element.querySelector('#add-permission-dialog-confirm-button');
expect(confirmButton.disabled).toBeFalsy();
});
}));
it('should stream the confirmed selection on the confirm subject', async(() => {
const addPermissionPanelComponent: AddPermissionPanelComponent = fixture.debugElement.query(By.directive(AddPermissionPanelComponent)).componentInstance;
addPermissionPanelComponent.select.emit(fakeAuthorityResults);
data.confirm.subscribe((selection) => {
expect(selection[0]).not.toBeNull();
expect(selection[0].entry.id).not.toBeNull();
expect(fakeAuthorityResults[0].entry.id).toBe(selection[0].entry.id);
});
fixture.detectChanges();
fixture.whenStable().then(() => {
fixture.detectChanges();
const confirmButton = <HTMLButtonElement> element.querySelector('#add-permission-dialog-confirm-button');
confirmButton.click();
});
}));
});

View File

@@ -0,0 +1,48 @@
/*!
* @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, Inject, ViewChild } from '@angular/core';
import { MAT_DIALOG_DATA } from '@angular/material';
import { MinimalNodeEntity } from 'alfresco-js-api';
import { AddPermissionDialogData } from './add-permission-dialog-data.interface';
import { AddPermissionComponent } from '../add-permission/add-permission.component';
@Component({
selector: 'adf-add-permission-dialog',
templateUrl: './add-permission-dialog.component.html',
styleUrls: ['./add-permission-dialog.component.scss'],
encapsulation: ViewEncapsulation.None
})
export class AddPermissionDialogComponent {
@ViewChild('addPermission')
addPermissionComponent: AddPermissionComponent;
private currentSelection: MinimalNodeEntity[] = [];
constructor(@Inject(MAT_DIALOG_DATA) public data: AddPermissionDialogData) {
}
onSelect(items: MinimalNodeEntity[]) {
this.currentSelection = items;
}
onAddClicked() {
this.data.confirm.next(this.currentSelection);
this.data.confirm.complete();
}
}

View File

@@ -0,0 +1,54 @@
<mat-form-field floatPlaceholder="never" class="adf-permission-search-input">
<input matInput
id="searchInput"
[formControl]="searchInput"
type="text"
placeholder="{{'PERMISSION_MANAGER.ADD-PERMISSION.SEARCH' | translate}}"
[value]="searchedWord">
<mat-icon *ngIf="searchedWord?.length > 0"
class="adf-permission-search-icon"
data-automation-id="adf-permission-clear-input"
id="adf-permission-clear-input"
matSuffix (click)="clearSearch()">clear
</mat-icon>
<mat-icon *ngIf="searchedWord?.length === 0"
class="adf-permission-search-icon"
data-automation-id="adf-permission-search-icon"
matSuffix>search
</mat-icon>
</mat-form-field>
<div *ngIf="searchedWord?.length === 0" id="adf-add-permission-type-search">
<span class="adf-permission-start-message">{{'PERMISSION_MANAGER.ADD-PERMISSION.TYPE-MESSAGE' | translate}}</span>
</div>
<adf-search #search [searchTerm]="searchedWord"
id="adf-add-permission-authority-results"
class="adf-permission-result-list"
*ngIf="searchedWord.length !== 0">
<ng-template let-data>
<mat-selection-list [class.adf-permission-result-list-elements]="data?.list?.entries.length !== 0">
<mat-list-option *ngFor="let item of data?.list?.entries; let idx = index"
(click)="elementClicked(item)"
class="adf-list-option-item"
id="result_option_{{idx}}">
<mat-icon mat-list-icon id="add-group-icon"
*ngIf="item?.entry?.nodeType === 'cm:authorityContainer' else show_person_icon">
group_add
</mat-icon>
<ng-template #show_person_icon>
<mat-icon id="add-person-icon" mat-list-icon>person_add</mat-icon>
</ng-template>
<p>
{{item.entry?.properties['cm:authorityName']?
item.entry?.properties['cm:authorityName'] :
item.entry?.properties['cm:firstName']}}</p>
</mat-list-option>
</mat-selection-list>
<div *ngIf="data?.list?.entries.length === 0" class="adf-permission-no-result" id="adf-add-permission-no-results">
<span>{{'PERMISSION_MANAGER.ADD-PERMISSION.NO-RESULT' | translate}}</span>
</div>
</ng-template>
</adf-search>

View File

@@ -0,0 +1,68 @@
@mixin adf-add-permission-panel-theme($theme) {
$background: map-get($theme, background);
$foreground: map-get($theme, foreground);
$primary: map-get($theme, primary);
$accent: map-get($theme, accent);
$mat-menu-border-radius: 2px !default;
.adf {
&-permission-result-list {
display: flex;
height: 300px;
overflow: auto;
border: 2px solid mat-color($foreground, base, 0.07);
&-elements {
width: 100%;
}
}
&-permission-start-message {
display: flex;
align-items: center;
justify-content: space-around;
height: 300px;
overflow: auto;
border: 2px solid mat-color($foreground, base, 0.07);
}
&-permission-no-result{
display: flex;
align-items: center;
justify-content: space-around;
width: 100%;
}
&-permission-search {
&-input {
width: 100%;
&-icon {
color: mat-color($foreground, disabled-button);
cursor: pointer;
&:hover {
color: mat-color($foreground, base);
}
}
}
}
&-list-option-item .mat-list-text {
display: flex;
flex-direction: row !important;
align-items: center;
}
&-permission-action {
&[disabled] {
opacity: 0.6;
}
&:enabled {
color: mat-color($primary);
}
}
}
}

View File

@@ -0,0 +1,156 @@
/*!
* @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 { AddPermissionPanelComponent } from './add-permission-panel.component';
import { By } from '@angular/platform-browser';
import { SearchService, setupTestBed, SearchConfigurationService } from '@alfresco/adf-core';
import { Observable } from 'rxjs/Observable';
import { fakeAuthorityListResult } from '../../../mock/add-permission.component.mock';
import { ContentTestingModule } from '../../../testing/content.testing.module';
import { DebugElement } from '@angular/core';
describe('AddPermissionPanelComponent', () => {
let fixture: ComponentFixture<AddPermissionPanelComponent>;
let component: AddPermissionPanelComponent;
let element: HTMLElement;
let searchApiService: SearchService;
let debugElement: DebugElement;
setupTestBed({
imports: [ContentTestingModule],
providers: [SearchService, SearchConfigurationService]
});
beforeEach(() => {
fixture = TestBed.createComponent(AddPermissionPanelComponent);
debugElement = fixture.debugElement;
element = fixture.nativeElement;
component = fixture.componentInstance;
fixture.detectChanges();
});
afterEach(() => {
fixture.destroy();
});
function typeWordIntoSearchInput(word: string): void {
let inputDebugElement = debugElement.query(By.css('#searchInput'));
inputDebugElement.nativeElement.value = word;
inputDebugElement.nativeElement.focus();
inputDebugElement.nativeElement.dispatchEvent(new Event('input'));
}
it('should be able to render the component', () => {
expect(element.querySelector('#adf-add-permission-type-search')).not.toBeNull();
expect(element.querySelector('#searchInput')).not.toBeNull();
});
it('should show search results when user types something', async(() => {
searchApiService = fixture.componentRef.injector.get(SearchService);
spyOn(searchApiService, 'search').and.returnValue(Observable.of(fakeAuthorityListResult));
expect(element.querySelector('#adf-add-permission-type-search')).not.toBeNull();
expect(element.querySelector('#searchInput')).not.toBeNull();
typeWordIntoSearchInput('a');
fixture.detectChanges();
fixture.whenStable().then(() => {
fixture.detectChanges();
expect(element.querySelector('#adf-add-permission-authority-results')).not.toBeNull();
expect(element.querySelector('#result_option_0')).not.toBeNull();
});
}));
it('should emit a select event with the selected items when an item is clicked', async(() => {
searchApiService = fixture.componentRef.injector.get(SearchService);
spyOn(searchApiService, 'search').and.returnValue(Observable.of(fakeAuthorityListResult));
component.select.subscribe((items) => {
expect(items).not.toBeNull();
expect(items[0].entry.id).toBeDefined();
expect(items[0].entry.id).not.toBeNull();
expect(items[0].entry.id).toBe(fakeAuthorityListResult.list.entries[0].entry.id);
});
typeWordIntoSearchInput('a');
fixture.detectChanges();
fixture.whenStable().then(() => {
fixture.detectChanges();
const listElement: DebugElement = fixture.debugElement.query(By.css('#result_option_0'));
expect(listElement).not.toBeNull();
listElement.triggerEventHandler('click', {});
});
}));
it('should show the icon related on the nodeType', async(() => {
searchApiService = fixture.componentRef.injector.get(SearchService);
spyOn(searchApiService, 'search').and.returnValue(Observable.of(fakeAuthorityListResult));
expect(element.querySelector('#adf-add-permission-type-search')).not.toBeNull();
expect(element.querySelector('#searchInput')).not.toBeNull();
typeWordIntoSearchInput('a');
fixture.detectChanges();
fixture.whenStable().then(() => {
fixture.detectChanges();
expect(element.querySelector('#adf-add-permission-authority-results')).not.toBeNull();
expect(element.querySelector('#result_option_0 #add-person-icon')).toBeDefined();
expect(element.querySelector('#result_option_0 #add-person-icon')).not.toBeNull();
expect(element.querySelector('#result_option_2 #add-group-icon')).toBeDefined();
expect(element.querySelector('#result_option_2 #add-group-icon')).not.toBeNull();
});
}));
it('should clear the search when user delete the search input field', async(() => {
searchApiService = fixture.componentRef.injector.get(SearchService);
spyOn(searchApiService, 'search').and.returnValue(Observable.of(fakeAuthorityListResult));
expect(element.querySelector('#adf-add-permission-type-search')).not.toBeNull();
expect(element.querySelector('#searchInput')).not.toBeNull();
typeWordIntoSearchInput('a');
fixture.detectChanges();
fixture.whenStable().then(() => {
fixture.detectChanges();
expect(element.querySelector('#adf-add-permission-authority-results')).not.toBeNull();
expect(element.querySelector('#result_option_0')).not.toBeNull();
const clearButton = fixture.debugElement.query(By.css('#adf-permission-clear-input'));
expect(clearButton).not.toBeNull();
clearButton.triggerEventHandler('click', {});
fixture.whenStable().then(() => {
fixture.detectChanges();
expect(element.querySelector('#adf-add-permission-authority-results')).toBeNull();
});
});
}));
it('should remove element from selection when is clicked and already selected', async(() => {
searchApiService = fixture.componentRef.injector.get(SearchService);
spyOn(searchApiService, 'search').and.returnValue(Observable.of(fakeAuthorityListResult));
component.selectedItems.push(fakeAuthorityListResult.list.entries[0]);
component.select.subscribe((items) => {
expect(items).not.toBeNull();
expect(items[0]).toBeUndefined();
expect(component.selectedItems.length).toBe(0);
});
typeWordIntoSearchInput('a');
fixture.detectChanges();
fixture.whenStable().then(() => {
fixture.detectChanges();
const listElement: DebugElement = fixture.debugElement.query(By.css('#result_option_0'));
expect(listElement).not.toBeNull();
listElement.triggerEventHandler('click', {});
});
}));
});

View File

@@ -0,0 +1,82 @@
/*!
* @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, EventEmitter, Output, ViewChild } from '@angular/core';
import { SearchPermissionConfigurationService } from './search-config-permission.service';
import { SearchService, SearchConfigurationService } from '@alfresco/adf-core';
import { SearchComponent } from '../../../search/components/search.component';
import { FormControl } from '@angular/forms';
import { debounceTime } from 'rxjs/operators';
import { MinimalNodeEntity } from 'alfresco-js-api';
@Component({
selector: 'adf-add-permission-panel',
templateUrl: './add-permission-panel.component.html',
styleUrls: ['./add-permission-panel.component.scss'],
encapsulation: ViewEncapsulation.None,
providers: [
{ provide: SearchConfigurationService, useClass: SearchPermissionConfigurationService },
SearchService
]
})
export class AddPermissionPanelComponent {
@ViewChild('search')
search: SearchComponent;
@Output()
select: EventEmitter<any> = new EventEmitter();
searchInput: FormControl = new FormControl();
searchedWord = '';
debounceSearch: number = 200;
selectedItems: MinimalNodeEntity[] = [];
constructor() {
this.searchInput.valueChanges
.pipe(
debounceTime(this.debounceSearch)
)
.subscribe((searchValue) => {
this.searchedWord = searchValue;
if (!searchValue) {
this.search.resetResults();
}
});
}
elementClicked(item: MinimalNodeEntity) {
if (this.isAlreadySelected(item)) {
this.selectedItems.splice(this.selectedItems.indexOf(item), 1);
} else {
this.selectedItems.push(item);
}
this.select.emit(this.selectedItems);
}
private isAlreadySelected(item: MinimalNodeEntity): boolean {
return this.selectedItems.indexOf(item) >= 0;
}
clearSearch() {
this.searchedWord = '';
this.selectedItems.splice(0, this.selectedItems.length);
this.search.resetResults();
}
}

View File

@@ -0,0 +1,14 @@
<adf-add-permission-panel
(select)="onSelect($event)">
</adf-add-permission-panel>
<div id="adf-add-permission-actions">
<button mat-button
id="adf-add-permission-action-button"
class="adf-permission-action"
[disabled]="selectedItems?.length === 0"
(click)="applySelection()">
{{'PERMISSION_MANAGER.ADD-PERMISSION.ADD-ACTION' | translate}}
</button>
</div>

View File

@@ -0,0 +1,16 @@
@mixin adf-add-permission-theme($theme) {
$primary: map-get($theme, primary);
.adf {
&-permission-action {
&[disabled] {
opacity: 0.6;
}
&:enabled {
color: mat-color($primary);
}
}
}
}

View File

@@ -0,0 +1,102 @@
/*!
* @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 { AddPermissionComponent } from './add-permission.component';
import { AddPermissionPanelComponent } from './add-permission-panel.component';
import { By } from '@angular/platform-browser';
import { setupTestBed } from '@alfresco/adf-core';
import { Observable } from 'rxjs/Observable';
import { fakeAuthorityResults } from '../../../mock/add-permission.component.mock';
import { ContentTestingModule } from '../../../testing/content.testing.module';
import { NodePermissionService } from '../../services/node-permission.service';
describe('AddPermissionComponent', () => {
let fixture: ComponentFixture<AddPermissionComponent>;
let element: HTMLElement;
let nodePermissionService: NodePermissionService;
setupTestBed({
imports: [
ContentTestingModule
]
});
beforeEach(() => {
fixture = TestBed.createComponent(AddPermissionComponent);
element = fixture.nativeElement;
nodePermissionService = TestBed.get(NodePermissionService);
fixture.detectChanges();
});
afterEach(() => {
fixture.destroy();
});
it('should be able to render the component', () => {
expect(element.querySelector('#adf-add-permission-type-search')).not.toBeNull();
expect(element.querySelector('#searchInput')).not.toBeNull();
expect(element.querySelector('#adf-add-permission-actions')).not.toBeNull();
const addButton: HTMLButtonElement = <HTMLButtonElement> element.querySelector('#adf-add-permission-action-button');
expect(addButton.disabled).toBeTruthy();
});
it('should enable the ADD button when a selection is sent', async(() => {
const addPermissionPanelComponent: AddPermissionPanelComponent = fixture.debugElement.query(By.directive(AddPermissionPanelComponent)).componentInstance;
addPermissionPanelComponent.select.emit(fakeAuthorityResults);
fixture.detectChanges();
fixture.whenStable().then(() => {
fixture.detectChanges();
const addButton: HTMLButtonElement = <HTMLButtonElement> element.querySelector('#adf-add-permission-action-button');
expect(addButton.disabled).toBeFalsy();
});
}));
it('should emit a success event when the node is updated', async(() => {
fixture.componentInstance.selectedItems = fakeAuthorityResults;
spyOn(nodePermissionService, 'updateNodePermissions').and.returnValue(Observable.of({ id: 'fake-node-id'}));
fixture.componentInstance.success.subscribe((node) => {
expect(node.id).toBe('fake-node-id');
});
fixture.detectChanges();
fixture.whenStable().then(() => {
fixture.detectChanges();
const addButton: HTMLButtonElement = <HTMLButtonElement> element.querySelector('#adf-add-permission-action-button');
addButton.click();
});
}));
it('should emit an error event when the node update fail', async(() => {
fixture.componentInstance.selectedItems = fakeAuthorityResults;
spyOn(nodePermissionService, 'updateNodePermissions').and.returnValue(Observable.throw({ error: 'errored'}));
fixture.componentInstance.error.subscribe((error) => {
expect(error.error).toBe('errored');
});
fixture.detectChanges();
fixture.whenStable().then(() => {
fixture.detectChanges();
const addButton: HTMLButtonElement = <HTMLButtonElement> element.querySelector('#adf-add-permission-action-button');
addButton.click();
});
}));
});

View File

@@ -0,0 +1,61 @@
/*!
* @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, EventEmitter, Input, Output } from '@angular/core';
import { MinimalNodeEntity, MinimalNodeEntryEntity } from 'alfresco-js-api';
import { NodePermissionService } from '../../services/node-permission.service';
@Component({
selector: 'adf-add-permission',
templateUrl: './add-permission.component.html',
styleUrls: ['./add-permission.component.scss'],
encapsulation: ViewEncapsulation.None
})
export class AddPermissionComponent {
@Input()
nodeId: string;
@Output()
success: EventEmitter<MinimalNodeEntryEntity> = new EventEmitter();
@Output()
error: EventEmitter<any> = new EventEmitter();
selectedItems: MinimalNodeEntity[] = [];
currentNode: MinimalNodeEntryEntity;
currentNodeRoles: string[];
constructor(private nodePermissionService: NodePermissionService) {
}
onSelect(selection: MinimalNodeEntity[]) {
this.selectedItems = selection;
}
applySelection() {
this.nodePermissionService.updateNodePermissions(this.nodeId, this.selectedItems)
.subscribe(
(node) => {
this.success.emit(node);
},
(error) => {
this.error.emit(error);
});
}
}

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 { QueryBody } from 'alfresco-js-api';
import { SearchConfigurationInterface } from '@alfresco/adf-core';
export class SearchPermissionConfigurationService implements SearchConfigurationInterface {
constructor() {
}
public generateQueryBody(searchTerm: string, maxResults: number, skipCount: number): QueryBody {
const defaultQueryBody: QueryBody = {
query: {
query: searchTerm ? `authorityName:${searchTerm}* OR userName:${searchTerm}*` : searchTerm
},
include: ['properties', 'aspectNames'],
paging: {
maxItems: maxResults,
skipCount: skipCount
},
filterQueries: [
/*tslint:disable-next-line */
{ query: "TYPE:'cm:authority'" }]
};
return defaultQueryBody;
}
}

View File

@@ -17,7 +17,7 @@
import { SimpleInheritedPermissionTestComponent } from '../../mock/inherited-permission.component.mock';
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { PermissionManagerModule } from '../../index';
import { InheritPermissionDirective } from './inherited-button.directive';
import { NodesApiService, setupTestBed, CoreModule } from '@alfresco/adf-core';
import { Observable } from 'rxjs/Observable';
@@ -33,20 +33,20 @@ describe('InheritPermissionDirective', () => {
setupTestBed({
imports: [
CoreModule.forRoot(),
PermissionManagerModule
CoreModule.forRoot()
],
declarations: [
InheritPermissionDirective,
SimpleInheritedPermissionTestComponent
]
});
beforeEach(() => {
beforeEach(async(() => {
fixture = TestBed.createComponent(SimpleInheritedPermissionTestComponent);
component = fixture.componentInstance;
element = fixture.nativeElement;
nodeService = TestBed.get(NodesApiService);
});
}));
it('should be able to render the simple component', async(() => {
fixture.detectChanges();

View File

@@ -35,15 +35,18 @@ export class InheritPermissionDirective {
@Output()
updated: EventEmitter<MinimalNodeEntryEntity> = new EventEmitter<MinimalNodeEntryEntity>();
@Output()
error: EventEmitter<any> = new EventEmitter<any>();
constructor(private nodeService: NodesApiService) {
}
onInheritPermissionClicked() {
this.nodeService.getNode(this.nodeId).subscribe((node: MinimalNodeEntryEntity) => {
const nodeBody = { permissions : {isInheritanceEnabled : !node.permissions.isInheritanceEnabled} };
this.nodeService.updateNode(this.nodeId, nodeBody, {include: ['permissions'] }).subscribe((nodeUpdated: MinimalNodeEntryEntity) => {
const nodeBody = { permissions: { isInheritanceEnabled: !node.permissions.isInheritanceEnabled } };
this.nodeService.updateNode(this.nodeId, nodeBody, { include: ['permissions'] }).subscribe((nodeUpdated: MinimalNodeEntryEntity) => {
this.updated.emit(nodeUpdated);
});
}, (error) => this.error.emit(error));
});
}

View File

@@ -48,6 +48,13 @@
</ng-template>
</ng-template>
</data-column>
<data-column key="delete">
<ng-template let-entry="$implicit">
<button *ngIf="!entry.row.getValue('isInherited')" mat-icon-button color="primary" (click)="removePermission(entry.row.obj)">
<mat-icon>highlight_off</mat-icon>
</button>
</ng-template>
</data-column>
</data-columns>
</adf-datatable>
</div>

View File

@@ -35,6 +35,9 @@ export class PermissionListComponent implements OnInit {
@Output()
update: EventEmitter<PermissionElement> = new EventEmitter();
@Output()
error: EventEmitter<any> = new EventEmitter();
permissionList: PermissionDisplayModel[];
settableRoles: any[];
actualNode: MinimalNodeEntryEntity;
@@ -82,7 +85,7 @@ export class PermissionListComponent implements OnInit {
saveNewRole(event: any, permissionRow: PermissionDisplayModel) {
let updatedPermissionRole: PermissionElement = this.buildUpdatedPermission(event.value, permissionRow);
this.nodePermissionService.updatePermissionRoles(this.actualNode, updatedPermissionRole)
this.nodePermissionService.updatePermissionRole(this.actualNode, updatedPermissionRole)
.subscribe((node: MinimalNodeEntryEntity) => {
this.update.emit(updatedPermissionRole);
});
@@ -96,4 +99,10 @@ export class PermissionListComponent implements OnInit {
return permissionRole;
}
removePermission(permissionRow: PermissionDisplayModel) {
this.nodePermissionService.removePermission(this.actualNode, permissionRow).subscribe((node) => {
this.update.emit(node);
}, (error) => this.error.emit(error));
}
}

View File

@@ -31,7 +31,7 @@ export class PermissionDisplayModel implements PermissionElement {
this.name = obj.name;
this.accessStatus = obj.accessStatus;
this.isInherited = obj.isInherited !== null && obj.isInherited !== undefined ? obj.isInherited : false;
this.icon = obj.icon ? obj.icon : 'lock_open';
this.icon = obj.icon ? obj.icon : 'vpn_key';
}
}
}

View File

@@ -21,10 +21,15 @@ import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { TranslateModule } from '@ngx-translate/core';
import { MaterialModule } from '../material.module';
import { PermissionListComponent } from './components/permission-list/permission-list.component';
import { AddPermissionComponent } from './components/add-permission/add-permission.component';
import { AddPermissionDialogComponent } from './components/add-permission/add-permission-dialog.component';
import { DataTableModule, DataColumnModule } from '@alfresco/adf-core';
import { InheritPermissionDirective } from './components/inherited-button.directive';
import { NodePermissionService } from './services/node-permission.service';
import { NoPermissionTemplateComponent } from './components/permission-list/no-permission.component';
import { SearchModule } from '..';
import { NodePermissionDialogService } from './services/node-permission-dialog.service';
import { AddPermissionPanelComponent } from './components/add-permission/add-permission-panel.component';
@NgModule({
imports: [
@@ -34,20 +39,29 @@ import { NoPermissionTemplateComponent } from './components/permission-list/no-p
MaterialModule,
TranslateModule,
DataTableModule,
DataColumnModule
DataColumnModule,
SearchModule
],
declarations: [
PermissionListComponent,
NoPermissionTemplateComponent,
InheritPermissionDirective
AddPermissionPanelComponent,
InheritPermissionDirective,
AddPermissionComponent,
AddPermissionDialogComponent
],
providers: [
NodePermissionDialogService,
NodePermissionService
],
entryComponents: [ AddPermissionPanelComponent, AddPermissionComponent, AddPermissionDialogComponent ],
exports: [
PermissionListComponent,
NoPermissionTemplateComponent,
InheritPermissionDirective
AddPermissionPanelComponent,
InheritPermissionDirective,
AddPermissionComponent,
AddPermissionDialogComponent
]
})
export class PermissionManagerModule {}

View File

@@ -18,7 +18,12 @@
export * from './components/permission-list/permission-list.component';
export * from './components/permission-list/no-permission.component';
export * from './components/inherited-button.directive';
export * from './services/node-permission.service';
export * from './models/permission.model';
export * from './services/node-permission-dialog.service';
export * from './services/node-permission.service';
export * from './components/add-permission/add-permission-dialog-data.interface';
export * from './components/add-permission/add-permission-panel.component';
export * from './components/add-permission/add-permission.component';
export * from './components/add-permission/add-permission-dialog.component';
export * from './permission-manager.module';

View File

@@ -0,0 +1,83 @@
/*!
* @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 { TestBed, async } from '@angular/core/testing';
import { AppConfigService, setupTestBed } from '@alfresco/adf-core';
import { NodePermissionDialogService } from './node-permission-dialog.service';
import { MatDialog } from '@angular/material';
import { Observable } from 'rxjs/Observable';
import { Subject } from 'rxjs/Subject';
import { ContentTestingModule } from '../../testing/content.testing.module';
import { NodePermissionService } from './node-permission.service';
describe('NodePermissionDialogService', () => {
let service: NodePermissionDialogService;
let materialDialog: MatDialog;
let spyOnDialogOpen: jasmine.Spy;
let afterOpenObservable: Subject<any>;
let nodePermissionService: NodePermissionService;
setupTestBed({
imports: [ContentTestingModule],
providers: [NodePermissionService]
});
beforeEach(() => {
let appConfig: AppConfigService = TestBed.get(AppConfigService);
appConfig.config.ecmHost = 'http://localhost:9876/ecm';
service = TestBed.get(NodePermissionDialogService);
materialDialog = TestBed.get(MatDialog);
afterOpenObservable = new Subject<any>();
nodePermissionService = TestBed.get(NodePermissionService);
spyOnDialogOpen = spyOn(materialDialog, 'open').and.returnValue({
afterOpen: () => afterOpenObservable,
afterClosed: () => Observable.of({}),
componentInstance: {
error: new Subject<any>()
}
});
});
it('should be able to create the service', () => {
expect(service).not.toBeNull();
});
it('should be able to open the dialog showing node permissions', () => {
service.openAddPermissionDialog('fake-node-id', 'fake-title');
expect(spyOnDialogOpen).toHaveBeenCalled();
});
it('should throw an error if the update of the node fails', async(() => {
spyOn(nodePermissionService, 'updateNodePermissions').and.returnValue(Observable.throw({error : 'error'}));
spyOn(service, 'openAddPermissionDialog').and.returnValue(Observable.of({}));
service.updateNodePermissionByDialog('fake-node-id', 'fake-title').subscribe(() => {
Observable.throw('This call should fail');
}, (error) => {
expect(error.error).toBe('error');
});
}));
it('should return the updated node', async(() => {
spyOn(nodePermissionService, 'updateNodePermissions').and.returnValue(Observable.of({id : 'fake-node'}));
spyOn(service, 'openAddPermissionDialog').and.returnValue(Observable.of({}));
service.updateNodePermissionByDialog('fake-node-id', 'fake-title').subscribe((node) => {
expect(node.id).toBe('fake-node');
});
}));
});

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 { MatDialog } from '@angular/material';
import { Injectable } from '@angular/core';
import { Subject } from 'rxjs/Subject';
import { Observable } from 'rxjs/Observable';
import { AddPermissionDialogComponent } from '../components/add-permission/add-permission-dialog.component';
import { AddPermissionDialogData } from '../components/add-permission/add-permission-dialog-data.interface';
import { MinimalNodeEntity, MinimalNodeEntryEntity } from 'alfresco-js-api';
import { NodePermissionService } from './node-permission.service';
@Injectable()
export class NodePermissionDialogService {
constructor(private dialog: MatDialog,
private nodePermissionService: NodePermissionService) {
}
openAddPermissionDialog(nodeId: string, title?: string): Observable<MinimalNodeEntity[]> {
const confirm = new Subject<MinimalNodeEntity[]>();
confirm.subscribe({
complete: this.close.bind(this)
});
const data: AddPermissionDialogData = {
nodeId: nodeId,
title: title,
confirm : confirm
};
this.openDialog(data, 'adf-add-permission-dialog', '630px');
return confirm;
}
private openDialog(data: any, currentPanelClass: string, chosenWidth: string) {
this.dialog.open(AddPermissionDialogComponent, { data, panelClass: currentPanelClass, width: chosenWidth });
}
close() {
this.dialog.closeAll();
}
updateNodePermissionByDialog(nodeId?: string, title?: string): Observable<MinimalNodeEntryEntity> {
return this.openAddPermissionDialog(nodeId, title).switchMap((selection) => {
return this.nodePermissionService.updateNodePermissions(nodeId, selection);
});
}
}

View File

@@ -20,7 +20,10 @@ import { NodePermissionService } from './node-permission.service';
import { SearchService, NodesApiService, setupTestBed, CoreModule } from '@alfresco/adf-core';
import { MinimalNodeEntryEntity, PermissionElement } from 'alfresco-js-api';
import { Observable } from 'rxjs/Observable';
import { fakeEmptyResponse, fakeNodeWithOnlyLocally, fakeSiteRoles, fakeSiteNodeResponse } from '../../mock/permission-list.component.mock';
import { fakeEmptyResponse, fakeNodeWithOnlyLocally, fakeSiteRoles, fakeSiteNodeResponse,
fakeNodeToRemovePermission } from '../../mock/permission-list.component.mock';
import { fakeAuthorityResults } from '../../mock/add-permission.component.mock';
import { NodePermissionDialogService } from './node-permission-dialog.service';
describe('NodePermissionService', () => {
@@ -33,6 +36,7 @@ describe('NodePermissionService', () => {
CoreModule.forRoot()
],
providers: [
NodePermissionDialogService,
NodePermissionService
]
});
@@ -85,7 +89,7 @@ describe('NodePermissionService', () => {
spyOn(nodeService, 'updateNode').and.callFake((nodeId, permissionBody) => returnUpdatedNode(nodeId, permissionBody));
service.updatePermissionRoles(fakeNodeWithOnlyLocally, fakePermission).subscribe((node: MinimalNodeEntryEntity) => {
service.updatePermissionRole(fakeNodeWithOnlyLocally, fakePermission).subscribe((node: MinimalNodeEntryEntity) => {
expect(node).not.toBeNull();
expect(node.id).toBe('fake-updated-node');
expect(node.permissions.locallySet.length).toBe(1);
@@ -95,4 +99,53 @@ describe('NodePermissionService', () => {
});
}));
it('should be able to remove a locally set permission', async(() => {
const fakePermission: PermissionElement = <PermissionElement> {
'authorityId': 'FAKE_PERSON_1',
'name': 'Contributor',
'accessStatus' : 'ALLOWED'
};
spyOn(nodeService, 'updateNode').and.callFake((nodeId, permissionBody) => returnUpdatedNode(nodeId, permissionBody));
const fakeNodeCopy = Object.assign(fakeNodeToRemovePermission);
service.removePermission(fakeNodeCopy, fakePermission).subscribe((node: MinimalNodeEntryEntity) => {
expect(node).not.toBeNull();
expect(node.id).toBe('fake-updated-node');
expect(node.permissions.locallySet.length).toBe(2);
expect(node.permissions.locallySet[0].authorityId).not.toBe(fakePermission.authorityId);
expect(node.permissions.locallySet[1].authorityId).not.toBe(fakePermission.authorityId);
});
}));
it('should be able to update locally set permissions on the node by node id', async(() => {
const fakeNodeCopy = Object.assign(fakeNodeWithOnlyLocally);
spyOn(nodeService, 'getNode').and.returnValue(Observable.of(fakeNodeCopy));
spyOn(nodeService, 'updateNode').and.callFake((nodeId, permissionBody) => returnUpdatedNode(nodeId, permissionBody));
spyOn(searchApiService, 'searchByQueryBody').and.returnValue(Observable.of(fakeSiteNodeResponse));
spyOn(service, 'getGroupMemeberByGroupName').and.returnValue(Observable.of(fakeSiteRoles));
service.updateNodePermissions('fake-node-id', fakeAuthorityResults).subscribe((node: MinimalNodeEntryEntity) => {
expect(node).not.toBeNull();
expect(node.id).toBe('fake-updated-node');
expect(node.permissions.locallySet.length).toBe(4);
expect(node.permissions.locallySet[3].authorityId).not.toBe(fakeAuthorityResults[0].entry['cm:userName']);
expect(node.permissions.locallySet[2].authorityId).not.toBe(fakeAuthorityResults[1].entry['cm:userName']);
expect(node.permissions.locallySet[1].authorityId).not.toBe(fakeAuthorityResults[2].entry['cm:userName']);
});
}));
it('should be able to update locally permissions on the node', async(() => {
const fakeNodeCopy = Object.assign(fakeNodeWithOnlyLocally);
spyOn(nodeService, 'updateNode').and.callFake((nodeId, permissionBody) => returnUpdatedNode(nodeId, permissionBody));
service.updateLocallySetPermissions(fakeNodeCopy, fakeAuthorityResults, fakeSiteRoles).subscribe((node: MinimalNodeEntryEntity) => {
expect(node).not.toBeNull();
expect(node.id).toBe('fake-updated-node');
expect(node.permissions.locallySet.length).toBe(4);
expect(node.permissions.locallySet[3].authorityId).not.toBe(fakeAuthorityResults[0].entry['cm:userName']);
expect(node.permissions.locallySet[2].authorityId).not.toBe(fakeAuthorityResults[1].entry['cm:userName']);
expect(node.permissions.locallySet[1].authorityId).not.toBe(fakeAuthorityResults[2].entry['cm:userName']);
});
}));
});

View File

@@ -18,8 +18,10 @@
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import { AlfrescoApiService, SearchService, NodesApiService } from '@alfresco/adf-core';
import { QueryBody, MinimalNodeEntryEntity, PathElement, GroupMemberEntry, GroupsPaging, GroupMemberPaging, PermissionElement } from 'alfresco-js-api';
import { QueryBody, MinimalNodeEntryEntity, MinimalNodeEntity, PathElement, GroupMemberEntry, GroupsPaging, GroupMemberPaging, PermissionElement } from 'alfresco-js-api';
import 'rxjs/add/operator/switchMap';
import { of } from 'rxjs/observable/of';
import { switchMap } from 'rxjs/operators';
@Injectable()
export class NodePermissionService {
@@ -42,7 +44,7 @@ export class NodePermissionService {
});
}
updatePermissionRoles(node: MinimalNodeEntryEntity, updatedPermissionRole: PermissionElement): Observable<MinimalNodeEntryEntity> {
updatePermissionRole(node: MinimalNodeEntryEntity, updatedPermissionRole: PermissionElement): Observable<MinimalNodeEntryEntity> {
let permissionBody = { permissions: { locallySet: []} };
const index = node.permissions.locallySet.map((permission) => permission.authorityId).indexOf(updatedPermissionRole.authorityId);
permissionBody.permissions.locallySet = permissionBody.permissions.locallySet.concat(node.permissions.locallySet);
@@ -54,6 +56,47 @@ export class NodePermissionService {
return this.nodeService.updateNode(node.id, permissionBody);
}
updateNodePermissions(nodeId: string, permissionList: MinimalNodeEntity[]): Observable<MinimalNodeEntryEntity> {
return this.nodeService.getNode(nodeId).pipe(
switchMap(node => {
return this.getNodeRoles(node).pipe(
switchMap((nodeRoles) => of({node, nodeRoles}) )
);
}),
switchMap(({node, nodeRoles}) => this.updateLocallySetPermissions(node, permissionList, nodeRoles))
);
}
updateLocallySetPermissions(node: MinimalNodeEntryEntity, nodes: MinimalNodeEntity[], nodeRole: string[]): Observable<MinimalNodeEntryEntity> {
let permissionBody = { permissions: { locallySet: []} };
const permissionList = this.transformNodeToPermissionElement(nodes, nodeRole[0]);
permissionBody.permissions.locallySet = node.permissions.locallySet ? node.permissions.locallySet.concat(permissionList) : permissionList;
return this.nodeService.updateNode(node.id, permissionBody);
}
private transformNodeToPermissionElement(nodes: MinimalNodeEntity[], nodeRole: any): PermissionElement[] {
return nodes.map((node) => {
let newPermissionElement: PermissionElement = <PermissionElement> {
'authorityId': node.entry.properties['cm:authorityName'] ?
node.entry.properties['cm:authorityName'] :
node.entry.properties['cm:userName'],
'name': nodeRole,
'accessStatus': 'ALLOWED'
};
return newPermissionElement;
});
}
removePermission(node: MinimalNodeEntryEntity, permissionToRemove: PermissionElement): Observable<MinimalNodeEntryEntity> {
let permissionBody = { permissions: { locallySet: [] } };
const index = node.permissions.locallySet.map((permission) => permission.authorityId).indexOf(permissionToRemove.authorityId);
if (index !== -1) {
node.permissions.locallySet.splice(index, 1);
permissionBody.permissions.locallySet = node.permissions.locallySet;
return this.nodeService.updateNode(node.id, permissionBody);
}
}
private getGroupMembersBySiteName(siteName: string): Observable<string[]> {
const groupName = 'GROUP_site_' + siteName;
return this.getGroupMemeberByGroupName(groupName)