mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2025-05-12 17:04:57 +00:00
[ACA-4361] permission layout modified (#6937)
* * reusable data table column moved * [ACA-4361] permission layout modified * * build fixed * fix build * * import fixed * * null safety operation * * fixed comments * * fix lint * * wait for reload list * * remove sleep * * add sleep * * fix comments * * fix comments * * floating promises fix * * remove wait
This commit is contained in:
parent
59dc6cb1d1
commit
0635b7fd06
@ -33,6 +33,9 @@
|
||||
<data-column type="text" key="users" title="Users"></data-column>
|
||||
<data-column key="status" title="Status">
|
||||
<ng-template let-value="value">{{value | json }}</ng-template>
|
||||
<adf-data-column-header>
|
||||
<ng-template>STATUS</ng-template>
|
||||
</adf-data-column-header>
|
||||
</data-column>
|
||||
</data-columns>
|
||||
-->
|
||||
|
@ -410,7 +410,7 @@
|
||||
(execute)="onManageMetadata($event)">
|
||||
</content-action>
|
||||
<content-action
|
||||
icon="settings_input_component"
|
||||
icon="supervisor_account"
|
||||
title="DOCUMENT_LIST.ACTIONS.PERMISSION"
|
||||
permission="copy"
|
||||
(error)="onContentActionError($event)"
|
||||
|
@ -1,18 +1,3 @@
|
||||
<div class="app-inherit_permission_button">
|
||||
<button mat-raised-button
|
||||
[color]="toggleStatus?'accent':'primary'"
|
||||
adf-inherit-permission [nodeId]="nodeId"
|
||||
(error)="showErrorMessage($event)"
|
||||
(updated)="onUpdatedPermissions($event)">
|
||||
{{ (toggleStatus?'DEMO_PERMISSION.INHERITED_PERMISSIONS_BUTTON':'DEMO_PERMISSION.INHERIT_PERMISSION_BUTTON') | translate}}</button>
|
||||
<button mat-button color="primary" (click)="openAddPermissionDialog()" data-automation-id="adf-add-permission-button">Add User or Group</button>
|
||||
<div class="app-permission-section">
|
||||
<adf-permission-list [nodeId]="nodeId"></adf-permission-list>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<adf-permission-list #permissionList
|
||||
[nodeId]="nodeId"
|
||||
(update)="reloadList()"
|
||||
(error)="showErrorMessage($event)">
|
||||
</adf-permission-list>
|
||||
</div>
|
||||
|
||||
|
@ -1,6 +1,4 @@
|
||||
.app-inherit_permission_button {
|
||||
padding-top: 20px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
padding-bottom: 20px;
|
||||
.app-permission-section {
|
||||
box-sizing: border-box;
|
||||
height: 100%;
|
||||
}
|
||||
|
@ -15,11 +15,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Component, Optional, OnInit, ViewChild } from '@angular/core';
|
||||
import { Component, OnInit, Optional } from '@angular/core';
|
||||
import { ActivatedRoute, Params } from '@angular/router';
|
||||
import { PermissionListComponent, NodePermissionDialogService } from '@alfresco/adf-content-services';
|
||||
import { MinimalNodeEntryEntity } from '@alfresco/js-api';
|
||||
import { NodesApiService, NotificationService } from '@alfresco/adf-core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-permissions',
|
||||
@ -28,16 +25,9 @@ import { NodesApiService, NotificationService } from '@alfresco/adf-core';
|
||||
})
|
||||
export class DemoPermissionComponent implements OnInit {
|
||||
|
||||
@ViewChild('permissionList', { static: true })
|
||||
displayPermissionComponent: PermissionListComponent;
|
||||
|
||||
nodeId: string;
|
||||
toggleStatus = false;
|
||||
|
||||
constructor(@Optional() private route: ActivatedRoute,
|
||||
private nodeService: NodesApiService,
|
||||
private nodePermissionDialogService: NodePermissionDialogService,
|
||||
private notificationService: NotificationService) {
|
||||
constructor(@Optional() private route: ActivatedRoute) {
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
@ -48,34 +38,5 @@ export class DemoPermissionComponent implements OnInit {
|
||||
}
|
||||
});
|
||||
}
|
||||
this.nodeService
|
||||
.getNode(this.nodeId, {include: ['permissions'] })
|
||||
.subscribe( (currentNode: MinimalNodeEntryEntity) => {
|
||||
this.toggleStatus = currentNode.permissions?.isInheritanceEnabled ?? false;
|
||||
});
|
||||
}
|
||||
|
||||
onUpdatedPermissions(node: MinimalNodeEntryEntity) {
|
||||
this.toggleStatus = node.permissions?.isInheritanceEnabled ?? false;
|
||||
this.displayPermissionComponent.reload();
|
||||
}
|
||||
|
||||
reloadList() {
|
||||
this.displayPermissionComponent.reload();
|
||||
}
|
||||
|
||||
openAddPermissionDialog() {
|
||||
this.nodePermissionDialogService
|
||||
.updateNodePermissionByDialog(this.nodeId)
|
||||
.subscribe(
|
||||
() => this.displayPermissionComponent.reload(),
|
||||
(error) => this.showErrorMessage(error)
|
||||
);
|
||||
}
|
||||
|
||||
showErrorMessage(error) {
|
||||
const message = error.message ? error.message : error;
|
||||
this.notificationService.openSnackMessage(message);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
Title: Permission List Component
|
||||
Added: v2.3.0
|
||||
Status: Active
|
||||
Last reviewed: 2018-11-20
|
||||
Last reviewed: 2021-4-17
|
||||
---
|
||||
|
||||
# [Permission List Component](../../../lib/content-services/src/lib/permission-manager/components/permission-list/permission-list.component.ts "Defined in permission-list.component.ts")
|
||||
@ -14,22 +14,9 @@ Shows node permissions as a table.
|
||||
## Basic Usage
|
||||
|
||||
```html
|
||||
<adf-permission-list [nodeId]="nodeId">
|
||||
</adf-permission-list>
|
||||
<adf-permission-list [nodeId]="nodeId"></adf-permission-list>
|
||||
```
|
||||
|
||||
### [Transclusions](../../user-guide/transclusion.md)
|
||||
|
||||
When the list is empty, the contents will simply say "No permissions" by default,
|
||||
but you can also supply your own content:
|
||||
|
||||
```html
|
||||
<adf-permission-list [nodeId]="nodeId">
|
||||
<adf-no-permission-template>
|
||||
Custom no permission template!
|
||||
</adf-no-permission-template>
|
||||
</adf-permission-list>
|
||||
```
|
||||
|
||||
## Class members
|
||||
|
||||
|
@ -120,7 +120,7 @@ To disable the tooltip your function can return `null` or an empty string.
|
||||
|
||||
### Column Template
|
||||
|
||||
You can provide custom column/cell templates that may contain other Angular components or HTML elements:
|
||||
You can provide custom column/cell header and templates that may contain other Angular components or HTML elements:
|
||||
|
||||
Every cell in the DataTable component is bound to the dynamic data context containing the following properties:
|
||||
|
||||
@ -185,6 +185,23 @@ In the Example below we will prepend `Hi!` to each file and folder name in the l
|
||||
|
||||
<!-- {% endraw %} -->
|
||||
|
||||
In the example below we will show capitalised custom name for a column:
|
||||
|
||||
<!-- {% raw %} -->
|
||||
|
||||
```html
|
||||
<data-column title="Name" key="name" sortable="true" class="full-width ellipsis-cell">
|
||||
<adf-data-column-header>
|
||||
<ng-template let-entry="$implicit">
|
||||
<span>NAME</span>
|
||||
</ng-template>
|
||||
</adf-data-column-header>
|
||||
</data-column>
|
||||
```
|
||||
|
||||
<!-- {% endraw %} -->
|
||||
|
||||
|
||||
In the Example below we will integrate the [adf-tag-node-list](../../content-services/components/tag-node-list.component.md) component
|
||||
within the document list.
|
||||
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 41 KiB After Width: | Height: | Size: 33 KiB |
@ -74,14 +74,13 @@ describe('Permissions Component', () => {
|
||||
});
|
||||
|
||||
const groupBody = {
|
||||
id: StringUtil.generateRandomString(),
|
||||
id: `GROUP_${StringUtil.generateRandomString()}`,
|
||||
displayName: StringUtil.generateRandomString()
|
||||
};
|
||||
|
||||
const fileOwnerUser = new UserModel();
|
||||
const filePermissionUser = new UserModel();
|
||||
|
||||
const duplicateUserPermissionMessage = 'One or more of the permissions you have set is already present : authority -> ' + filePermissionUser.username + ' / role -> Contributor';
|
||||
const roleConsumerFolderModel = new FolderModel({ name: 'roleConsumer' + StringUtil.generateRandomString() });
|
||||
const roleCoordinatorFolderModel = new FolderModel({ name: 'roleCoordinator' + StringUtil.generateRandomString() });
|
||||
const roleCollaboratorFolderModel = new FolderModel({ name: 'roleCollaborator' + StringUtil.generateRandomString() });
|
||||
@ -135,7 +134,7 @@ describe('Permissions Component', () => {
|
||||
|
||||
await contentList.rightClickOnRow(fileModel.name);
|
||||
await contentServicesPage.pressContextMenuActionNamed('Permission');
|
||||
await permissionsPage.addPermissionsDialog.checkPermissionContainerIsDisplayed();
|
||||
await permissionsPage.checkPermissionManagerDisplayed();
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
@ -163,8 +162,11 @@ describe('Permissions Component', () => {
|
||||
await permissionsPage.addPermissionsDialog.checkSearchUserInputIsDisplayed();
|
||||
await permissionsPage.addPermissionsDialog.searchUserOrGroup(groupBody.id);
|
||||
await permissionsPage.addPermissionsDialog.clickUserOrGroup(groupBody.displayName);
|
||||
|
||||
await permissionsPage.addPermissionsDialog.checkGroupIsAdded(groupBody.id);
|
||||
await permissionsPage.addPermissionsDialog.selectRole(groupBody.displayName, 'Consumer');
|
||||
await expect(await permissionsPage.addPermissionsDialog.addButtonIsEnabled()).toBe(true, 'button should be enabled');
|
||||
await permissionsPage.addPermissionsDialog.clickAddButton();
|
||||
await expect(await notificationPage.getSnackBarMessage()).toEqual('Added 0 user(s) 1 group(s)');
|
||||
await permissionsPage.checkUserIsAdded(groupBody.id);
|
||||
});
|
||||
|
||||
it('[C277100] Should display EVERYONE group in the search result set', async () => {
|
||||
@ -179,6 +181,15 @@ describe('Permissions Component', () => {
|
||||
await permissionsPage.addPermissionsDialog.checkResultListIsDisplayed();
|
||||
await permissionsPage.addPermissionsDialog.checkUserOrGroupIsDisplayed('EVERYONE');
|
||||
});
|
||||
|
||||
it('should be able to toggle the inherited permission', async () => {
|
||||
await permissionsPage.checkPermissionListDisplayed();
|
||||
await expect(await permissionsPage.isInherited()).toBe(true, 'Inherited permission should be on');
|
||||
await permissionsPage.toggleInheritPermission();
|
||||
await expect(await notificationPage.getSnackBarMessage()).toContain('Disabled inherited permission', 'Disabled notification not shown');
|
||||
await notificationPage.waitForSnackBarToClose();
|
||||
await expect(await permissionsPage.isInherited()).toBe(false, 'Inherited permission should be off');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Changing and duplicate Permissions', () => {
|
||||
@ -192,13 +203,19 @@ describe('Permissions Component', () => {
|
||||
await contentServicesPage.checkSelectedSiteIsDisplayed('My files');
|
||||
await contentList.rightClickOnRow(fileModel.name);
|
||||
await contentServicesPage.pressContextMenuActionNamed('Permission');
|
||||
await permissionsPage.checkPermissionManagerDisplayed();
|
||||
await permissionsPage.addPermissionButton.waitVisible();
|
||||
await permissionsPage.addPermissionsDialog.clickAddPermissionButton();
|
||||
await permissionsPage.addPermissionsDialog.checkAddPermissionDialogIsDisplayed();
|
||||
await permissionsPage.addPermissionsDialog.checkSearchUserInputIsDisplayed();
|
||||
await permissionsPage.addPermissionsDialog.searchUserOrGroup(filePermissionUser.firstName);
|
||||
await permissionsPage.addPermissionsDialog.clickUserOrGroup(filePermissionUser.firstName);
|
||||
await permissionsPage.addPermissionsDialog.checkUserIsAdded(filePermissionUser.username);
|
||||
await permissionsPage.addPermissionsDialog.selectRole(filePermissionUser.fullName, 'Contributor');
|
||||
await expect(await permissionsPage.addPermissionsDialog.addButtonIsEnabled()).toBe(true, 'button should be enabled');
|
||||
await permissionsPage.addPermissionsDialog.clickAddButton();
|
||||
await expect(await notificationPage.getSnackBarMessage()).toEqual('Added 1 user(s) 0 group(s)');
|
||||
await notificationPage.waitForSnackBarToClose();
|
||||
await permissionsPage.checkUserIsAdded(filePermissionUser.username);
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
@ -207,8 +224,8 @@ describe('Permissions Component', () => {
|
||||
});
|
||||
|
||||
it('[C274691] Should be able to add a new User with permission to the file and also change locally set permissions', async () => {
|
||||
await expect(await permissionsPage.addPermissionsDialog.getRoleCellValue(filePermissionUser.username)).toEqual('Contributor');
|
||||
await permissionsPage.addPermissionsDialog.clickRoleDropdownByUserOrGroupName(filePermissionUser.username);
|
||||
await expect(await permissionsPage.getRoleCellValue(filePermissionUser.username)).toEqual('Contributor');
|
||||
await permissionsPage.clickRoleDropdownByUserOrGroupName(filePermissionUser.username);
|
||||
const roleDropdownOptions = permissionsPage.addPermissionsDialog.getRoleDropdownOptions();
|
||||
await expect(await roleDropdownOptions.count()).toBe(5);
|
||||
|
||||
@ -220,16 +237,20 @@ describe('Permissions Component', () => {
|
||||
|
||||
await BrowserActions.closeMenuAndDialogs();
|
||||
await permissionsPage.changePermission(filePermissionUser.username, 'Collaborator');
|
||||
await expect(await permissionsPage.addPermissionsDialog.getRoleCellValue(filePermissionUser.username)).toEqual('Collaborator');
|
||||
await notificationPage.waitForSnackBarToClose();
|
||||
await expect(await permissionsPage.getRoleCellValue(filePermissionUser.username)).toEqual('Collaborator');
|
||||
|
||||
await permissionsPage.changePermission(filePermissionUser.username, 'Coordinator');
|
||||
await expect(await permissionsPage.addPermissionsDialog.getRoleCellValue(filePermissionUser.username)).toEqual('Coordinator');
|
||||
await notificationPage.waitForSnackBarToClose();
|
||||
await expect(await permissionsPage.getRoleCellValue(filePermissionUser.username)).toEqual('Coordinator');
|
||||
|
||||
await permissionsPage.changePermission(filePermissionUser.username, 'Editor');
|
||||
await expect(await permissionsPage.addPermissionsDialog.getRoleCellValue(filePermissionUser.username)).toEqual('Editor');
|
||||
await notificationPage.waitForSnackBarToClose();
|
||||
await expect(await permissionsPage.getRoleCellValue(filePermissionUser.username)).toEqual('Editor');
|
||||
|
||||
await permissionsPage.changePermission(filePermissionUser.username, 'Consumer');
|
||||
await expect(await permissionsPage.addPermissionsDialog.getRoleCellValue(filePermissionUser.username)).toEqual('Consumer');
|
||||
await notificationPage.waitForSnackBarToClose();
|
||||
await expect(await permissionsPage.getRoleCellValue(filePermissionUser.username)).toEqual('Consumer');
|
||||
});
|
||||
|
||||
it('[C276980] Should not be able to duplicate User or Group to the locally set permissions', async () => {
|
||||
@ -239,15 +260,15 @@ describe('Permissions Component', () => {
|
||||
await permissionsPage.addPermissionsDialog.checkSearchUserInputIsDisplayed();
|
||||
await permissionsPage.addPermissionsDialog.searchUserOrGroup(filePermissionUser.firstName);
|
||||
await permissionsPage.addPermissionsDialog.clickUserOrGroup(filePermissionUser.firstName);
|
||||
|
||||
await expect(await notificationPage.getSnackBarMessage()).toEqual(duplicateUserPermissionMessage);
|
||||
await notificationHistoryPage.checkNotifyContains(duplicateUserPermissionMessage);
|
||||
await expect(await permissionsPage.addPermissionsDialog.getRoleCellValue(filePermissionUser.fullName)).toEqual('Contributor');
|
||||
await expect(await permissionsPage.addPermissionsDialog.addButtonIsEnabled()).toBe(false, 'button should not be enabled');
|
||||
});
|
||||
|
||||
it('[C276982] Should be able to remove User or Group from the locally set permissions', async () => {
|
||||
await expect(await permissionsPage.addPermissionsDialog.getRoleCellValue(filePermissionUser.username)).toEqual('Contributor');
|
||||
await permissionsPage.addPermissionsDialog.clickDeletePermissionButton();
|
||||
await permissionsPage.addPermissionsDialog.checkUserIsDeleted(filePermissionUser.username);
|
||||
await expect(await permissionsPage.getRoleCellValue(filePermissionUser.username)).toEqual('Contributor');
|
||||
await permissionsPage.clickDeletePermissionButton(filePermissionUser.username);
|
||||
await permissionsPage.checkUserIsDeleted(filePermissionUser.username);
|
||||
await expect(await notificationPage.getSnackBarMessage()).toEqual('User/Group deleted');
|
||||
});
|
||||
});
|
||||
|
||||
@ -376,13 +397,9 @@ describe('Permissions Component', () => {
|
||||
await contentServicesPage.checkSelectedSiteIsDisplayed('My files');
|
||||
await contentList.rightClickOnRow('RoleConsumer' + fileModel.name);
|
||||
await contentServicesPage.pressContextMenuActionNamed('Permission');
|
||||
await permissionsPage.addPermissionsDialog.checkPermissionInheritedButtonIsDisplayed();
|
||||
await permissionsPage.addPermissionButton.waitVisible();
|
||||
await permissionsPage.addPermissionsDialog.clickPermissionInheritedButton();
|
||||
await expect(await notificationPage.getSnackBarMessage()).toEqual('You are not allowed to change permissions');
|
||||
await permissionsPage.addPermissionsDialog.clickAddPermissionButton();
|
||||
await expect(await notificationPage.getSnackBarMessage()).toEqual('You are not allowed to change permissions');
|
||||
await notificationHistoryPage.checkNotifyContains('You are not allowed to change permissions');
|
||||
await permissionsPage.checkPermissionManagerDisplayed();
|
||||
await permissionsPage.errorElement.waitPresent();
|
||||
await expect(await permissionsPage.noPermissionContent()).toContain('This item no longer exists or you don\'t have permission to view it.');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -36,6 +36,7 @@ import { NavigationBarPage } from '../../core/pages/navigation-bar.page';
|
||||
import { VersionManagePage } from '../../core/pages/version-manager.page';
|
||||
import CONSTANTS = require('../../util/constants');
|
||||
import { SitesApi } from '@alfresco/js-api';
|
||||
import { NotificationDemoPage } from '../../core/pages/notification.page';
|
||||
|
||||
describe('Permissions Component', () => {
|
||||
|
||||
@ -49,6 +50,7 @@ describe('Permissions Component', () => {
|
||||
const navigationBarPage = new NavigationBarPage();
|
||||
const metadataViewPage = new MetadataViewPage();
|
||||
const notificationHistoryPage = new NotificationHistoryPage();
|
||||
const notificationPage = new NotificationDemoPage();
|
||||
const uploadDialog = new UploadDialogPage();
|
||||
const versionManagePage = new VersionManagePage();
|
||||
|
||||
@ -182,11 +184,9 @@ describe('Permissions Component', () => {
|
||||
|
||||
await contentServicesPage.pressContextMenuActionNamed('Permission');
|
||||
|
||||
await permissionsPage.addPermissionsDialog.checkPermissionInheritedButtonIsDisplayed();
|
||||
await permissionsPage.checkPermissionManagerDisplayed();
|
||||
await permissionsPage.addPermissionButton.waitVisible();
|
||||
|
||||
await browser.sleep(5000);
|
||||
|
||||
await permissionsPage.addPermissionsDialog.clickAddPermissionButton();
|
||||
await permissionsPage.addPermissionsDialog.checkAddPermissionDialogIsDisplayed();
|
||||
await permissionsPage.addPermissionsDialog.checkSearchUserInputIsDisplayed();
|
||||
@ -194,11 +194,18 @@ describe('Permissions Component', () => {
|
||||
await permissionsPage.addPermissionsDialog.searchUserOrGroup(consumerUser.username);
|
||||
|
||||
await permissionsPage.addPermissionsDialog.clickUserOrGroup(consumerUser.firstName);
|
||||
await permissionsPage.addPermissionsDialog.checkUserIsAdded(consumerUser.username);
|
||||
await permissionsPage.addPermissionsDialog.selectRole(consumerUser.fullName, 'Site Collaborator');
|
||||
await expect(await permissionsPage.addPermissionsDialog.getRoleCellValue(consumerUser.fullName)).toEqual('Site Collaborator');
|
||||
await expect(await permissionsPage.addPermissionsDialog.addButtonIsEnabled()).toBe(true, 'Add button should be enabled');
|
||||
await permissionsPage.addPermissionsDialog.clickAddButton();
|
||||
await expect(await notificationPage.getSnackBarMessage()).toEqual('Added 1 user(s) 0 group(s)');
|
||||
await notificationPage.waitForSnackBarToClose();
|
||||
|
||||
await expect(await permissionsPage.addPermissionsDialog.getRoleCellValue(consumerUser.username)).toEqual(CONSTANTS.CS_USER_ROLES_I18N.COLLABORATOR);
|
||||
await permissionsPage.checkUserIsAdded(consumerUser.username);
|
||||
|
||||
await permissionsPage.addPermissionsDialog.clickRoleDropdownByUserOrGroupName(consumerUser.username);
|
||||
await expect(await permissionsPage.getRoleCellValue(consumerUser.username)).toEqual(CONSTANTS.CS_USER_ROLES_I18N.COLLABORATOR);
|
||||
|
||||
await permissionsPage.clickRoleDropdownByUserOrGroupName(consumerUser.username);
|
||||
|
||||
const roleDropdownOptions = permissionsPage.addPermissionsDialog.getRoleDropdownOptions();
|
||||
|
||||
|
@ -15,29 +15,84 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { DataTableComponentPage, AddPermissionsDialogPage, TestElement } from '@alfresco/adf-testing';
|
||||
import { browser, by, element } from 'protractor';
|
||||
import {
|
||||
AddPermissionsDialogPage,
|
||||
BrowserActions,
|
||||
DataTableComponentPage,
|
||||
DropdownPage,
|
||||
TestElement
|
||||
} from '@alfresco/adf-testing';
|
||||
import { browser, by } from 'protractor';
|
||||
|
||||
export class PermissionsPage {
|
||||
|
||||
dataTableComponentPage = new DataTableComponentPage();
|
||||
addPermissionsDialog = new AddPermissionsDialogPage();
|
||||
|
||||
rootElement = 'adf-permission-manager-card';
|
||||
inheritedButton = '[data-automation-id="adf-inherit-toggle-button"]';
|
||||
errorElement = TestElement.byId('adf-permission-manager-error');
|
||||
localPermissionList = TestElement.byCss('[data-automation-id="adf-locally-set-permission"]');
|
||||
addPermissionButton = TestElement.byCss("button[data-automation-id='adf-add-permission-button']");
|
||||
addPermissionDialog = element(by.css('adf-add-permission-dialog'));
|
||||
searchUserInput = element(by.id('searchInput'));
|
||||
searchResults = element(by.css('#adf-add-permission-authority-results #adf-search-results-content'));
|
||||
addButton = element(by.id('add-permission-dialog-confirm-button'));
|
||||
permissionInheritedButton = element.all(by.css('.app-inherit_permission_button button')).first();
|
||||
noPermissions = element(by.id('adf-no-permissions-template'));
|
||||
deletePermissionButton = element(by.css(`button[data-automation-id='adf-delete-permission-button']`));
|
||||
permissionDisplayContainer = element(by.id('adf-permission-display-container'));
|
||||
closeButton = TestElement.byCss('#add-permission-dialog-close-button');
|
||||
|
||||
async changePermission(name: string, role: string): Promise<void> {
|
||||
await this.addPermissionsDialog.clickRoleDropdownByUserOrGroupName(name);
|
||||
await this.addPermissionsDialog.selectOption(role);
|
||||
await browser.sleep(1000);
|
||||
await this.clickRoleDropdownByUserOrGroupName(name);
|
||||
await new DropdownPage().selectOption(role);
|
||||
await this.dataTableComponentPage.checkRowByContentIsNotSelected(name);
|
||||
}
|
||||
|
||||
async checkUserIsAdded(id: string) {
|
||||
const userOrGroupName = TestElement.byCss('div[data-automation-id="' + id + '"]');
|
||||
await userOrGroupName.waitPresent();
|
||||
}
|
||||
|
||||
async getRoleCellValue(username: string): Promise<string> {
|
||||
const locator = this.dataTableComponentPage.getCellByRowContentAndColumn('Users and Groups', username, 'Role');
|
||||
return BrowserActions.getText(locator);
|
||||
}
|
||||
|
||||
async clickRoleDropdownByUserOrGroupName(name: string): Promise<void> {
|
||||
const row = this.dataTableComponentPage.getRow('Users and Groups', name);
|
||||
await row.click();
|
||||
await BrowserActions.click(row.element(by.css('[id="adf-select-role-permission"] .mat-select-trigger')));
|
||||
await TestElement.byCss('.mat-select-panel').waitVisible();
|
||||
}
|
||||
|
||||
async clickDeletePermissionButton(username: string): Promise<void> {
|
||||
const userOrGroupName = TestElement.byCss(`[data-automation-id="adf-delete-permission-button-${username}"]`);
|
||||
await userOrGroupName.waitPresent();
|
||||
await userOrGroupName.click();
|
||||
}
|
||||
|
||||
async checkUserIsDeleted(username: string): Promise<void> {
|
||||
const userOrGroupName = TestElement.byCss('div[data-automation-id="' + username + '"]');
|
||||
await userOrGroupName.waitNotPresent();
|
||||
}
|
||||
|
||||
async noPermissionContent(): Promise<string> {
|
||||
const noPermission = TestElement.byCss('.adf-no-permission__template--text');
|
||||
return noPermission.getText();
|
||||
}
|
||||
|
||||
async checkPermissionManagerDisplayed(): Promise<void> {
|
||||
await TestElement.byId(this.rootElement).waitVisible();
|
||||
}
|
||||
|
||||
async checkPermissionListDisplayed(): Promise<void> {
|
||||
await browser.sleep(500);
|
||||
await this.dataTableComponentPage.checkRowIsNotSelected('Authority ID', name);
|
||||
await this.localPermissionList.waitVisible();
|
||||
}
|
||||
|
||||
async isInherited(): Promise<boolean> {
|
||||
const inheritButton = TestElement.byCss(this.inheritedButton);
|
||||
await inheritButton.waitVisible();
|
||||
const appliedStyles = await inheritButton.getAttribute('class');
|
||||
return appliedStyles.indexOf('mat-checked') !== -1;
|
||||
}
|
||||
|
||||
async toggleInheritPermission(): Promise<void> {
|
||||
const inheritButton = TestElement.byCss(`${this.inheritedButton} label`);
|
||||
await inheritButton.click();
|
||||
}
|
||||
}
|
||||
|
@ -35,7 +35,7 @@ async function checkEnv() {
|
||||
|
||||
await alfrescoJsApi.login(program.username, program.password);
|
||||
} catch (error) {
|
||||
if (error?.error.code === 'ETIMEDOUT') {
|
||||
if (error?.error?.code === 'ETIMEDOUT') {
|
||||
logger.error('The env is not reachable. Terminating');
|
||||
process.exit(1);
|
||||
}
|
||||
|
@ -17,10 +17,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
|
||||
import * as docker from './docker';
|
||||
|
||||
export default function (args: any) {
|
||||
docker.default(args);
|
||||
}
|
||||
|
||||
|
@ -115,8 +115,8 @@ function main(args) {
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if(args.pathProject === undefined) {
|
||||
args.pathProject = resolve('./')
|
||||
if (args.pathProject === undefined) {
|
||||
args.pathProject = resolve('./');
|
||||
}
|
||||
|
||||
if (args.loginCheck === true) {
|
||||
|
@ -17,3 +17,4 @@
|
||||
|
||||
export * from './content-directive.module';
|
||||
export * from './node-lock.directive';
|
||||
export * from './node-counter.directive';
|
||||
|
@ -385,7 +385,8 @@
|
||||
"NO_PERMISSIONS": "No permissions"
|
||||
},
|
||||
"ADD-PERMISSION": {
|
||||
"SEARCH": "Search",
|
||||
"SEARCH": "Search for users or groups to add",
|
||||
"TITLE": "Add user or group to",
|
||||
"TYPE-MESSAGE": "Type something to start searching groups or people",
|
||||
"NO-RESULT": "No result found for this search",
|
||||
"ADD-ACTION": "ADD",
|
||||
@ -393,7 +394,41 @@
|
||||
"BASE-DIALOG-TITLE": "Search a group or people to add...",
|
||||
"EVERYONE": "EVERYONE"
|
||||
},
|
||||
"COLUMN": {
|
||||
"NAME": "Users and Groups ({{ count }})",
|
||||
"LOCATION": "Location",
|
||||
"BULK-ROLE": "Set all role to"
|
||||
},
|
||||
"LABELS": {
|
||||
"ON": "On",
|
||||
"OFF": "Off",
|
||||
"DIRECT-PERMISSIONS": "Direct Applied Permission",
|
||||
"INHERITED-PERMISSIONS": "Inherited Permission",
|
||||
"INHERITED-SUBTITLE": "{{count}} users or groups are inheriting permission from a parent folder",
|
||||
"SELECT-ROLE": "Select role"
|
||||
},
|
||||
"MESSAGE": {
|
||||
"EMPTY-PERMISSION": "No users/groups",
|
||||
"EMPTY-SUBTITLE": "Add user/group to manage permission.",
|
||||
"NO-MEMBERS": "Add groups or people to manage roles",
|
||||
"PERMISSION-BULK-UPDATE-SUCCESS": "Updated {{user}} user(s) {{group}} group(s)",
|
||||
"PERMISSION-UPDATE-SUCCESS": "User/Group updated",
|
||||
"PERMISSION-UPDATE-FAIL": "Failed to update user/group",
|
||||
"PERMISSION-BULK-DELETE-SUCCESS": "Deleted {{user}} user(s_ {{group}} group(s)",
|
||||
"PERMISSION-DELETE-SUCCESS": "User/Group deleted",
|
||||
"PERMISSION-DELETE-FAIL": "Failed to delete user/group",
|
||||
"PERMISSION-ADD-SUCCESS": "Added {{user}} user(s) {{group}} group(s)",
|
||||
"PERMISSION-ADD-FAIL": "Failed to add user/group",
|
||||
"INHERIT-ENABLE-SUCCESS": "Enabled inherited permission",
|
||||
"INHERIT-DISABLE-SUCCESS": "Disabled inherited permission",
|
||||
"TOGGLE-PERMISSION-FAILED": "Failed to toggle inherit Permission"
|
||||
},
|
||||
"ACTION": {
|
||||
"DELETE": "Delete",
|
||||
"ADD-PERMISSION": "Add User or Group"
|
||||
},
|
||||
"ERROR": {
|
||||
"NOT-FOUND": "This item no longer exists or you don't have permission to view it.",
|
||||
"DUPLICATE-PERMISSION": "One or more of the permissions you have set is already present : {{list}}",
|
||||
"NOT-ALLOWED": "You are not allowed to change permissions"
|
||||
}
|
||||
|
@ -100,7 +100,86 @@ export const fakeNodeWithPermissions: any = {
|
||||
}
|
||||
};
|
||||
|
||||
export const fakeNodeInheritedOnly: any = {
|
||||
export const fakeNodeInheritedOnly = {
|
||||
'allowableOperations': [ 'updatePermissions' ],
|
||||
'aspectNames': [
|
||||
'cm:auditable',
|
||||
'cm:taggable',
|
||||
'cm:author',
|
||||
'cm:titled',
|
||||
'app:uifacets'
|
||||
],
|
||||
'createdAt': '2017-11-16T16:29:38.638+0000',
|
||||
'path': {
|
||||
'name': '/Company Home/Sites/testsite/documentLibrary',
|
||||
'isComplete': true,
|
||||
'elements': [
|
||||
{
|
||||
'id': '2be275a1-b00d-4e45-83d8-66af43ac2252',
|
||||
'name': 'Company Home'
|
||||
},
|
||||
{
|
||||
'id': '1be10a97-6eb9-4b60-b6c6-1673900e9631',
|
||||
'name': 'Sites'
|
||||
},
|
||||
{
|
||||
'id': 'e002c740-b8f9-482a-a554-8fff4e4c9dc0',
|
||||
'name': 'testsite'
|
||||
},
|
||||
{
|
||||
'id': '71626fae-0c04-4d0c-a129-20fa4c178716',
|
||||
'name': 'documentLibrary'
|
||||
}
|
||||
]
|
||||
},
|
||||
'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 fakeReadOnlyNodeInherited = {
|
||||
'aspectNames': [
|
||||
'cm:auditable',
|
||||
'cm:taggable',
|
||||
@ -231,7 +310,7 @@ export const fakeNodeWithOnlyLocally: any = {
|
||||
'Editor',
|
||||
'Consumer'
|
||||
],
|
||||
'isInheritanceEnabled': true
|
||||
'isInheritanceEnabled': false
|
||||
},
|
||||
'modifiedByUser': {
|
||||
'id': 'admin',
|
||||
|
@ -15,11 +15,13 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { NodeEntry } from '@alfresco/js-api';
|
||||
import { Node, PermissionElement } from '@alfresco/js-api';
|
||||
import { Subject } from 'rxjs';
|
||||
import { RoleModel } from '../../models/role.model';
|
||||
|
||||
export interface AddPermissionDialogData {
|
||||
title?: string;
|
||||
nodeId: string;
|
||||
confirm: Subject<NodeEntry[]>;
|
||||
node: Node;
|
||||
roles: RoleModel[];
|
||||
confirm: Subject<PermissionElement[]>;
|
||||
}
|
||||
|
@ -1,15 +1,109 @@
|
||||
<h2 mat-dialog-title id="add-permission-dialog-title">
|
||||
{{(data?.title ? data?.title : 'PERMISSION_MANAGER.ADD-PERMISSION.BASE-DIALOG-TITLE') | translate}}
|
||||
{{ (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="adf-choose-action"
|
||||
[disabled]="currentSelection?.length === 0"
|
||||
(click)="onAddClicked()">{{'PERMISSION_MANAGER.ADD-PERMISSION.ADD-ACTION' | translate}}</button>
|
||||
</mat-dialog-actions>
|
||||
|
||||
<ng-container *ngIf="!isSearchActive">
|
||||
<mat-dialog-content>
|
||||
<button mat-button (click)="enableSearch()" class="adf-search-user-button">
|
||||
{{ "PERMISSION_MANAGER.ADD-PERMISSION.SEARCH" | translate }}
|
||||
<span class="adf-toolbar--spacer"></span>
|
||||
<mat-icon>search</mat-icon>
|
||||
</button>
|
||||
|
||||
<adf-datatable [rows]="selectedMembers"
|
||||
selectionMode="none"
|
||||
stickyHeader="true"
|
||||
data-automation-id="adf-user-role-selection-table"
|
||||
*ngIf="selectedMembers.length">
|
||||
<data-columns>
|
||||
<data-column class="adf-key-icon" key="icon" type="icon" [sortable]="false">
|
||||
<ng-template let-context>
|
||||
<adf-user-icon-column [context]="context"></adf-user-icon-column>
|
||||
</ng-template>
|
||||
</data-column>
|
||||
|
||||
<data-column class="adf-authorityId-label adf-ellipsis-cell adf-expand-cell-2"
|
||||
[title]="'Users and Groups (' + selectedMembers.length + ')'"
|
||||
key="id">
|
||||
<ng-template let-context>
|
||||
<adf-user-name-column [context]="context"></adf-user-name-column>
|
||||
</ng-template>
|
||||
</data-column>
|
||||
|
||||
<data-column class="adf-ellipsis-cell adf-expand-cell-1"
|
||||
title="PERMISSION_MANAGER.PERMISSION_DISPLAY.ROLE"
|
||||
key="role">
|
||||
<ng-template let-entry="$implicit">
|
||||
<adf-user-role-column [readonly]="entry.row.obj.readonly"
|
||||
[placeholder]="entry.data.getValue(entry.row, entry.col)"
|
||||
[value]="entry.data.getValue(entry.row, entry.col)"
|
||||
[roles]="data.roles"
|
||||
id="adf-select-role-permission"
|
||||
(roleChanged)="onMemberUpdate($event, entry.row.obj)">
|
||||
</adf-user-role-column>
|
||||
</ng-template>
|
||||
|
||||
<adf-data-column-header>
|
||||
<ng-template>
|
||||
<adf-user-role-column class="adf-permission-role-column-header"
|
||||
placeholder="PERMISSION_MANAGER.COLUMN.BULK-ROLE"
|
||||
[roles]="data.roles"
|
||||
id="adf-bulk-select-role-permission"
|
||||
(roleChanged)="onBulkUpdate($event)">
|
||||
</adf-user-role-column>
|
||||
</ng-template>
|
||||
</adf-data-column-header>
|
||||
</data-column>
|
||||
|
||||
<data-column class="adf-delete-permission" key="" sortable="false">
|
||||
<ng-template let-entry="$implicit">
|
||||
<button mat-icon-button
|
||||
class="adf-add-member-action"
|
||||
[style.display]="entry.row.obj.readonly ? 'none': 'block'"
|
||||
(click)="onMemberDelete(entry.row.obj)"
|
||||
data-automation-id="adf-delete-permission-button">
|
||||
<mat-icon>highlight_off</mat-icon>
|
||||
</button>
|
||||
</ng-template>
|
||||
</data-column>
|
||||
</data-columns>
|
||||
</adf-datatable>
|
||||
|
||||
</mat-dialog-content>
|
||||
|
||||
<mat-dialog-actions>
|
||||
<button mat-button
|
||||
mat-dialog-close
|
||||
data-automation-id="add-permission-dialog-close-button">
|
||||
{{ "PERMISSION_MANAGER.ADD-PERMISSION.CLOSE-ACTION" | translate }}
|
||||
</button>
|
||||
<button mat-button
|
||||
data-automation-id="add-permission-dialog-confirm-button"
|
||||
[mat-dialog-close]="true"
|
||||
class="adf-choose-action"
|
||||
[disabled]="!isValid()"
|
||||
(click)="onAddClicked()">
|
||||
{{ "PERMISSION_MANAGER.ADD-PERMISSION.ADD-ACTION" | translate }}
|
||||
</button>
|
||||
</mat-dialog-actions>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="isSearchActive">
|
||||
<mat-dialog-content>
|
||||
<adf-add-permission-panel (select)="onSelect($event)"></adf-add-permission-panel>
|
||||
</mat-dialog-content>
|
||||
|
||||
<mat-dialog-actions>
|
||||
<button mat-button
|
||||
(click)="canCloseDialog()"
|
||||
data-automation-id="add-permission-dialog-close-button">
|
||||
{{ "PERMISSION_MANAGER.ADD-PERMISSION.CLOSE-ACTION" | translate }}
|
||||
</button>
|
||||
<button mat-button
|
||||
data-automation-id="add-permission-dialog-confirm-button"
|
||||
[disabled]="!currentSelection.length"
|
||||
(click)="onSearchAddClicked()">
|
||||
{{ "PERMISSION_MANAGER.ADD-PERMISSION.ADD-ACTION" | translate }}
|
||||
</button>
|
||||
</mat-dialog-actions>
|
||||
</ng-container>
|
||||
|
@ -25,7 +25,7 @@
|
||||
|
||||
.mat-dialog-content {
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.mat-dialog-actions {
|
||||
@ -51,5 +51,19 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.adf {
|
||||
&-search-user-button {
|
||||
width: 100%;
|
||||
.mat-button-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
&-add-member-action {
|
||||
padding: 0 15px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -15,28 +15,46 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||
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 { NodeEntry } from '@alfresco/js-api';
|
||||
import { Node, PermissionElement } from '@alfresco/js-api';
|
||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { Subject } from 'rxjs';
|
||||
import { AddPermissionPanelComponent } from './add-permission-panel.component';
|
||||
import { ContentTestingModule } from '../../../testing/content.testing.module';
|
||||
import { AddPermissionDialogComponent } from './add-permission-dialog.component';
|
||||
import { AddPermissionDialogData } from './add-permission-dialog-data.interface';
|
||||
import { fakeAuthorityResults } from '../../../mock/add-permission.component.mock';
|
||||
import { AddPermissionPanelComponent } from './add-permission-panel.component';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
|
||||
describe('AddPermissionDialog', () => {
|
||||
|
||||
let fixture: ComponentFixture<AddPermissionDialogComponent>;
|
||||
let component: AddPermissionDialogComponent;
|
||||
let element: HTMLElement;
|
||||
const data: AddPermissionDialogData = {
|
||||
title: 'dead or alive you are coming with me',
|
||||
nodeId: 'fake-node-id',
|
||||
confirm: new Subject<NodeEntry[]> ()
|
||||
node: {
|
||||
id: 'fake-node-id',
|
||||
aspectNames: [],
|
||||
isFile: true,
|
||||
name: 'fake-node.pdf',
|
||||
permissions: {
|
||||
locallySet: []
|
||||
}
|
||||
} as Node,
|
||||
roles: [
|
||||
{
|
||||
label: 'test',
|
||||
role: 'Test'
|
||||
},
|
||||
{
|
||||
label: 'consumer',
|
||||
role: 'Consumer'
|
||||
}
|
||||
],
|
||||
confirm: new Subject<PermissionElement[]> ()
|
||||
};
|
||||
const dialogRef = {
|
||||
close: jasmine.createSpy('close')
|
||||
@ -50,13 +68,12 @@ describe('AddPermissionDialog', () => {
|
||||
providers: [
|
||||
{ provide: MatDialogRef, useValue: dialogRef },
|
||||
{ provide: MAT_DIALOG_DATA, useValue: data }
|
||||
],
|
||||
schemas: [CUSTOM_ELEMENTS_SCHEMA]
|
||||
]
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
|
||||
fixture = TestBed.createComponent(AddPermissionDialogComponent);
|
||||
component = fixture.componentInstance;
|
||||
element = fixture.nativeElement;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
@ -72,44 +89,139 @@ describe('AddPermissionDialog', () => {
|
||||
});
|
||||
|
||||
it('should close the dialog when close button is clicked', () => {
|
||||
const closeButton: HTMLButtonElement = <HTMLButtonElement> element.querySelector('#add-permission-dialog-close-button');
|
||||
const closeButton: HTMLButtonElement = <HTMLButtonElement> element.querySelector('[data-automation-id="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');
|
||||
const confirmButton: HTMLButtonElement = <HTMLButtonElement> element.querySelector('[data-automation-id="add-permission-dialog-confirm-button"]');
|
||||
expect(confirmButton.disabled).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should enable the button when a selection is done', async(() => {
|
||||
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');
|
||||
let confirmButton: HTMLButtonElement = <HTMLButtonElement> element.querySelector('[data-automation-id="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();
|
||||
});
|
||||
}));
|
||||
await fixture.detectChanges();
|
||||
confirmButton = <HTMLButtonElement> element.querySelector('[data-automation-id="add-permission-dialog-confirm-button"]');
|
||||
expect(confirmButton.disabled).toBe(false);
|
||||
});
|
||||
|
||||
it('should stream the confirmed selection on the confirm subject', async(() => {
|
||||
it('should update the role after selection', async (done) => {
|
||||
spyOn(component, 'onMemberUpdate').and.callThrough();
|
||||
const addPermissionPanelComponent: AddPermissionPanelComponent = fixture.debugElement.query(By.directive(AddPermissionPanelComponent)).componentInstance;
|
||||
let confirmButton = <HTMLButtonElement> element.querySelector('[data-automation-id="add-permission-dialog-confirm-button"]');
|
||||
expect(confirmButton.disabled).toBe(true);
|
||||
addPermissionPanelComponent.select.emit([fakeAuthorityResults[0]]);
|
||||
await fixture.detectChanges();
|
||||
expect(confirmButton.disabled).toBe(false);
|
||||
confirmButton.click();
|
||||
await fixture.detectChanges();
|
||||
|
||||
const selectBox = fixture.debugElement.query(By.css(('[id="adf-select-role-permission"] .mat-select-trigger')));
|
||||
selectBox.triggerEventHandler('click', null);
|
||||
fixture.detectChanges();
|
||||
|
||||
const options = fixture.debugElement.queryAll(By.css('mat-option'));
|
||||
expect(options).not.toBeNull();
|
||||
expect(options.length).toBe(2);
|
||||
options[0].triggerEventHandler('click', {});
|
||||
await fixture.detectChanges();
|
||||
expect(component.onMemberUpdate).toHaveBeenCalled();
|
||||
|
||||
data.confirm.subscribe((selection) => {
|
||||
expect(selection.length).toBe(1);
|
||||
done();
|
||||
});
|
||||
|
||||
confirmButton = <HTMLButtonElement> element.querySelector('[data-automation-id="add-permission-dialog-confirm-button"]');
|
||||
expect(confirmButton.disabled).toBe(false);
|
||||
confirmButton.click();
|
||||
});
|
||||
|
||||
it('should update all the user role on header column update', async () => {
|
||||
spyOn(component, 'onBulkUpdate').and.callThrough();
|
||||
const addPermissionPanelComponent: AddPermissionPanelComponent = fixture.debugElement.query(By.directive(AddPermissionPanelComponent)).componentInstance;
|
||||
let confirmButton = <HTMLButtonElement> element.querySelector('[data-automation-id="add-permission-dialog-confirm-button"]');
|
||||
expect(confirmButton.disabled).toBe(true);
|
||||
addPermissionPanelComponent.select.emit(fakeAuthorityResults);
|
||||
await fixture.detectChanges();
|
||||
expect(confirmButton.disabled).toBe(false);
|
||||
confirmButton.click();
|
||||
await fixture.detectChanges();
|
||||
|
||||
const selectBox = fixture.debugElement.query(By.css(('[id="adf-bulk-select-role-permission"] .mat-select-trigger')));
|
||||
selectBox.triggerEventHandler('click', null);
|
||||
fixture.detectChanges();
|
||||
|
||||
const options = fixture.debugElement.queryAll(By.css('mat-option'));
|
||||
expect(options).not.toBeNull();
|
||||
expect(options.length).toBe(2);
|
||||
options[0].triggerEventHandler('click', {});
|
||||
await fixture.detectChanges();
|
||||
expect(component.onBulkUpdate).toHaveBeenCalled();
|
||||
|
||||
data.confirm.subscribe((selection) => {
|
||||
expect(selection.length).toBe(3);
|
||||
});
|
||||
|
||||
confirmButton = <HTMLButtonElement> element.querySelector('[data-automation-id="add-permission-dialog-confirm-button"]');
|
||||
expect(confirmButton.disabled).toBe(false);
|
||||
confirmButton.click();
|
||||
});
|
||||
|
||||
it('should delete the user after selection', async () => {
|
||||
spyOn(component, 'onMemberUpdate').and.callThrough();
|
||||
spyOn(component, 'onMemberDelete').and.callThrough();
|
||||
const addPermissionPanelComponent: AddPermissionPanelComponent = fixture.debugElement.query(By.directive(AddPermissionPanelComponent)).componentInstance;
|
||||
let confirmButton = <HTMLButtonElement> element.querySelector('[data-automation-id="add-permission-dialog-confirm-button"]');
|
||||
expect(confirmButton.disabled).toBe(true);
|
||||
addPermissionPanelComponent.select.emit(fakeAuthorityResults);
|
||||
await fixture.detectChanges();
|
||||
expect(confirmButton.disabled).toBe(false);
|
||||
confirmButton.click();
|
||||
await fixture.detectChanges();
|
||||
|
||||
const selectBox = fixture.debugElement.query(By.css(('[id="adf-select-role-permission"] .mat-select-trigger')));
|
||||
selectBox.triggerEventHandler('click', null);
|
||||
fixture.detectChanges();
|
||||
|
||||
const options = fixture.debugElement.queryAll(By.css('mat-option'));
|
||||
expect(options).not.toBeNull();
|
||||
expect(options.length).toBe(2);
|
||||
options[0].triggerEventHandler('click', {});
|
||||
await fixture.detectChanges();
|
||||
expect(component.onMemberUpdate).toHaveBeenCalled();
|
||||
|
||||
confirmButton = <HTMLButtonElement> element.querySelector('[data-automation-id="add-permission-dialog-confirm-button"]');
|
||||
expect(confirmButton.disabled).toBe(true);
|
||||
const deleteButton = element.querySelectorAll('[data-automation-id="adf-delete-permission-button"]') as any;
|
||||
deleteButton[1].click();
|
||||
deleteButton[2].click();
|
||||
await fixture.detectChanges();
|
||||
|
||||
expect(confirmButton.disabled).toBe(false);
|
||||
expect(component.onMemberDelete).toHaveBeenCalled();
|
||||
data.confirm.subscribe((selection) => {
|
||||
expect(selection.length).toBe(1);
|
||||
});
|
||||
|
||||
confirmButton.click();
|
||||
});
|
||||
|
||||
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);
|
||||
expect(fakeAuthorityResults[0].entry.id).toBe(selection[0].authorityId);
|
||||
});
|
||||
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => {
|
||||
fixture.detectChanges();
|
||||
const confirmButton = <HTMLButtonElement> element.querySelector('#add-permission-dialog-confirm-button');
|
||||
confirmButton.click();
|
||||
});
|
||||
}));
|
||||
await fixture.detectChanges();
|
||||
const confirmButton = <HTMLButtonElement> element.querySelector('[data-automation-id="add-permission-dialog-confirm-button"]');
|
||||
confirmButton.click();
|
||||
});
|
||||
});
|
||||
|
@ -15,11 +15,11 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Component, ViewEncapsulation, Inject, ViewChild } from '@angular/core';
|
||||
import { MAT_DIALOG_DATA } from '@angular/material/dialog';
|
||||
import { NodeEntry } from '@alfresco/js-api';
|
||||
import { Component, Inject, ViewEncapsulation } from '@angular/core';
|
||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||
import { NodeEntry, PermissionElement } from '@alfresco/js-api';
|
||||
import { AddPermissionDialogData } from './add-permission-dialog-data.interface';
|
||||
import { AddPermissionComponent } from '../add-permission/add-permission.component';
|
||||
import { MemberModel } from '../../models/member.model';
|
||||
|
||||
@Component({
|
||||
selector: 'adf-add-permission-dialog',
|
||||
@ -28,13 +28,15 @@ import { AddPermissionComponent } from '../add-permission/add-permission.compone
|
||||
encapsulation: ViewEncapsulation.None
|
||||
})
|
||||
export class AddPermissionDialogComponent {
|
||||
isSearchActive = true;
|
||||
selectedMembers: MemberModel[] = [];
|
||||
|
||||
@ViewChild('addPermission')
|
||||
addPermissionComponent: AddPermissionComponent;
|
||||
|
||||
private existingMembers: PermissionElement[] = [];
|
||||
currentSelection: NodeEntry[] = [];
|
||||
|
||||
constructor(@Inject(MAT_DIALOG_DATA) public data: AddPermissionDialogData) {
|
||||
constructor(@Inject(MAT_DIALOG_DATA) public data: AddPermissionDialogData,
|
||||
private dialogRef: MatDialogRef<AddPermissionDialogComponent>) {
|
||||
this.existingMembers = this.data.node.permissions.locallySet || [];
|
||||
}
|
||||
|
||||
onSelect(items: NodeEntry[]) {
|
||||
@ -42,7 +44,63 @@ export class AddPermissionDialogComponent {
|
||||
}
|
||||
|
||||
onAddClicked() {
|
||||
this.data.confirm.next(this.currentSelection);
|
||||
const selection = this.selectedMembers.filter(member => !member.readonly).map(member => member.toPermissionElement());
|
||||
this.data.confirm.next(selection);
|
||||
this.data.confirm.complete();
|
||||
}
|
||||
|
||||
onSearchAddClicked() {
|
||||
const newMembers = this.currentSelection.map(item => MemberModel.parseFromSearchResult(item))
|
||||
.filter(({id}) => !this.selectedMembers.find((member) => member.id === id));
|
||||
this.selectedMembers = this.selectedMembers.concat(newMembers);
|
||||
|
||||
this.selectedMembers.forEach((member) => {
|
||||
const existingMember = this.existingMembers.find(({authorityId}) => authorityId === member.id);
|
||||
if (!!existingMember) {
|
||||
member.role = existingMember.name;
|
||||
member.accessStatus = existingMember.accessStatus;
|
||||
member.readonly = true; // make role non editable
|
||||
}
|
||||
});
|
||||
this.disableSearch();
|
||||
}
|
||||
|
||||
canCloseDialog() {
|
||||
if (!!this.selectedMembers.length) {
|
||||
this.disableSearch();
|
||||
} else {
|
||||
this.dialogRef.close();
|
||||
}
|
||||
}
|
||||
|
||||
enableSearch() {
|
||||
this.isSearchActive = true;
|
||||
}
|
||||
|
||||
disableSearch() {
|
||||
this.isSearchActive = false;
|
||||
}
|
||||
|
||||
onBulkUpdate(role: string) {
|
||||
this.selectedMembers.filter(member => !member.readonly)
|
||||
.forEach(member => (member.role = role));
|
||||
}
|
||||
|
||||
onMemberDelete({ id }: MemberModel) {
|
||||
const index = this.selectedMembers.findIndex((member) => member.id === id);
|
||||
this.selectedMembers.splice(index, 1);
|
||||
if (this.selectedMembers.length === 0) {
|
||||
this.enableSearch();
|
||||
this.currentSelection = [];
|
||||
}
|
||||
}
|
||||
|
||||
onMemberUpdate(role: string, member: MemberModel) {
|
||||
const _member = this.selectedMembers.find(({ id }) => id === member.id);
|
||||
_member.role = role;
|
||||
}
|
||||
|
||||
isValid(): boolean {
|
||||
return this.selectedMembers.filter(({readonly}) => !readonly).length && this.selectedMembers.every(({role}) => !!role);
|
||||
}
|
||||
}
|
||||
|
@ -33,15 +33,12 @@
|
||||
class="adf-permission-result-list"
|
||||
[class.adf-permission-result-list-search]="searchedWord.length === 0">
|
||||
<ng-template let-data>
|
||||
<mat-selection-list class="adf-permission-result-list-elements">
|
||||
<mat-selection-list class="adf-permission-result-list-elements" (keydown.control.a)="selectAll( data?.list?.entries)">
|
||||
<mat-list-option id="adf-add-permission-group-everyone"
|
||||
class="adf-list-option-item"
|
||||
(click)="elementClicked(EVERYONE)">
|
||||
<mat-icon mat-list-icon
|
||||
id="add-group-icon">
|
||||
group_add
|
||||
</mat-icon>
|
||||
<p>
|
||||
<adf-user-icon-column [node]="EVERYONE" id="add-group-icon"></adf-user-icon-column>
|
||||
<p class="adf-result-name">
|
||||
{{'PERMISSION_MANAGER.ADD-PERMISSION.EVERYONE' | translate}}
|
||||
</p>
|
||||
</mat-list-option>
|
||||
@ -50,26 +47,19 @@
|
||||
(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>
|
||||
<adf-user-icon-column [node]="item"></adf-user-icon-column>
|
||||
<p class="adf-result-name">
|
||||
<ng-container *ngIf="item.entry?.properties['cm:authorityDisplayName']; else authorityName">
|
||||
{{item.entry?.properties['cm:authorityDisplayName']}}
|
||||
{{item.entry.properties['cm:authorityDisplayName']}}
|
||||
</ng-container>
|
||||
<ng-template #authorityName>
|
||||
<ng-container *ngIf="item.entry?.properties['cm:authorityName']; else owner">
|
||||
{{item.entry?.properties['cm:authorityName']}}
|
||||
{{item.entry.properties['cm:authorityName']}}
|
||||
</ng-container>
|
||||
</ng-template>
|
||||
<ng-template #owner>
|
||||
{{item.entry?.properties['cm:owner']?.displayName}}
|
||||
{{item.entry?.properties['cm:firstName'] ? item.entry?.properties['cm:firstName'] : '' }}
|
||||
{{item.entry?.properties['cm:lastName'] ? item.entry?.properties['cm:lastName']: ''}}
|
||||
</ng-template>
|
||||
</p>
|
||||
</mat-list-option>
|
||||
|
@ -58,6 +58,10 @@
|
||||
display: flex;
|
||||
flex-direction: row !important;
|
||||
align-items: center;
|
||||
|
||||
.adf-result-name {
|
||||
padding-left: 16px !important;
|
||||
}
|
||||
}
|
||||
|
||||
&-permission-action {
|
||||
|
@ -15,13 +15,13 @@
|
||||
* 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 { NodeEntry } from '@alfresco/js-api';
|
||||
import { Component, ViewEncapsulation, EventEmitter, Output, ViewChild } from '@angular/core';
|
||||
import { FormControl } from '@angular/forms';
|
||||
import { debounceTime } from 'rxjs/operators';
|
||||
import { NodeEntry } from '@alfresco/js-api';
|
||||
import { SearchPermissionConfigurationService } from './search-config-permission.service';
|
||||
import { SearchComponent } from '../../../search/components/search.component';
|
||||
|
||||
@Component({
|
||||
selector: 'adf-add-permission-panel',
|
||||
@ -48,7 +48,7 @@ export class AddPermissionPanelComponent {
|
||||
|
||||
selectedItems: NodeEntry[] = [];
|
||||
|
||||
EVERYONE: NodeEntry = new NodeEntry({ entry: { properties: {'cm:authorityName': 'GROUP_EVERYONE'}}});
|
||||
EVERYONE: NodeEntry = new NodeEntry({ entry: { nodeType: 'cm:authorityContainer', properties: {'cm:authorityName': 'GROUP_EVERYONE'}}});
|
||||
|
||||
constructor() {
|
||||
this.searchInput.valueChanges
|
||||
@ -72,6 +72,13 @@ export class AddPermissionPanelComponent {
|
||||
this.select.emit(this.selectedItems);
|
||||
}
|
||||
|
||||
selectAll(items: NodeEntry[]) {
|
||||
if (items?.length > 0) {
|
||||
this.selectedItems = items;
|
||||
this.select.emit(this.selectedItems);
|
||||
}
|
||||
}
|
||||
|
||||
private isAlreadySelected(item: NodeEntry): boolean {
|
||||
return this.selectedItems.indexOf(item) >= 0;
|
||||
}
|
||||
|
@ -15,24 +15,23 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { setupTestBed } from '@alfresco/adf-core';
|
||||
import { Node } from '@alfresco/js-api';
|
||||
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, NodesApiService } from '@alfresco/adf-core';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { of, throwError } from 'rxjs';
|
||||
import { fakeAuthorityResults } from '../../../mock/add-permission.component.mock';
|
||||
import { ContentTestingModule } from '../../../testing/content.testing.module';
|
||||
import { NodePermissionService } from '../../services/node-permission.service';
|
||||
import { Node } from '@alfresco/js-api';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { ContentTestingModule } from '../../../testing/content.testing.module';
|
||||
|
||||
describe('AddPermissionComponent', () => {
|
||||
|
||||
let fixture: ComponentFixture<AddPermissionComponent>;
|
||||
let element: HTMLElement;
|
||||
let nodePermissionService: NodePermissionService;
|
||||
let nodeApiService: NodesApiService;
|
||||
|
||||
setupTestBed({
|
||||
imports: [
|
||||
@ -42,11 +41,12 @@ describe('AddPermissionComponent', () => {
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
nodeApiService = TestBed.inject(NodesApiService);
|
||||
spyOn(nodeApiService, 'getNode').and.returnValue(of({ id: 'fake-node', allowableOperations: ['updatePermissions']}));
|
||||
nodePermissionService = TestBed.inject(NodePermissionService);
|
||||
spyOn(nodePermissionService, 'getNodeWithRoles').and.returnValue(
|
||||
of({ node: { id: 'fake-node', allowableOperations: ['updatePermissions']}, roles: [{ label: 'Test' , role: 'test'}] })
|
||||
);
|
||||
fixture = TestBed.createComponent(AddPermissionComponent);
|
||||
element = fixture.nativeElement;
|
||||
nodePermissionService = TestBed.inject(NodePermissionService);
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
@ -84,7 +84,7 @@ describe('AddPermissionComponent', () => {
|
||||
});
|
||||
}));
|
||||
|
||||
it('should emit a success event when the node is updated', (done) => {
|
||||
it('should emit a success event when the node is updated', async (done) => {
|
||||
fixture.componentInstance.selectedItems = fakeAuthorityResults;
|
||||
spyOn(nodePermissionService, 'updateNodePermissions').and.returnValue(of({ id: 'fake-node-id'}));
|
||||
|
||||
@ -93,12 +93,9 @@ describe('AddPermissionComponent', () => {
|
||||
done();
|
||||
});
|
||||
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => {
|
||||
fixture.detectChanges();
|
||||
const addButton: HTMLButtonElement = <HTMLButtonElement> element.querySelector('#adf-add-permission-action-button');
|
||||
addButton.click();
|
||||
});
|
||||
await fixture.detectChanges();
|
||||
const addButton: HTMLButtonElement = <HTMLButtonElement> element.querySelector('#adf-add-permission-action-button');
|
||||
addButton.click();
|
||||
});
|
||||
|
||||
it('should NOT emit a success event when the user does not have permission to update the node', () => {
|
||||
@ -111,7 +108,7 @@ describe('AddPermissionComponent', () => {
|
||||
expect(spySuccess).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should emit an error event when the node update fail', (done) => {
|
||||
it('should emit an error event when the node update fail', async (done) => {
|
||||
fixture.componentInstance.selectedItems = fakeAuthorityResults;
|
||||
spyOn(nodePermissionService, 'updateNodePermissions').and.returnValue(throwError({ error: 'err'}));
|
||||
|
||||
@ -120,11 +117,8 @@ describe('AddPermissionComponent', () => {
|
||||
done();
|
||||
});
|
||||
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => {
|
||||
fixture.detectChanges();
|
||||
const addButton: HTMLButtonElement = <HTMLButtonElement> element.querySelector('#adf-add-permission-action-button');
|
||||
addButton.click();
|
||||
});
|
||||
await fixture.detectChanges();
|
||||
const addButton: HTMLButtonElement = <HTMLButtonElement> element.querySelector('#adf-add-permission-action-button');
|
||||
addButton.click();
|
||||
});
|
||||
});
|
||||
|
@ -15,10 +15,11 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Component, ViewEncapsulation, EventEmitter, Input, Output, OnInit } from '@angular/core';
|
||||
import { NodeEntry, Node } from '@alfresco/js-api';
|
||||
import { AllowableOperationsEnum, ContentService } from '@alfresco/adf-core';
|
||||
import { Node, NodeEntry, PermissionElement } from '@alfresco/js-api';
|
||||
import { Component, EventEmitter, Input, OnInit, Output, ViewEncapsulation } from '@angular/core';
|
||||
import { NodePermissionService } from '../../services/node-permission.service';
|
||||
import { NodesApiService, ContentService, AllowableOperationsEnum } from '@alfresco/adf-core';
|
||||
import { RoleModel } from '../../models/role.model';
|
||||
|
||||
@Component({
|
||||
selector: 'adf-add-permission',
|
||||
@ -26,6 +27,9 @@ import { NodesApiService, ContentService, AllowableOperationsEnum } from '@alfre
|
||||
styleUrls: ['./add-permission.component.scss'],
|
||||
encapsulation: ViewEncapsulation.None
|
||||
})
|
||||
/*
|
||||
* @deprecated in 4.4.0, use adf-add-permission-panel instead.
|
||||
*/
|
||||
export class AddPermissionComponent implements OnInit {
|
||||
|
||||
/** ID of the target node. */
|
||||
@ -42,14 +46,16 @@ export class AddPermissionComponent implements OnInit {
|
||||
|
||||
selectedItems: NodeEntry[] = [];
|
||||
currentNode: Node;
|
||||
currentNodeRoles: string[];
|
||||
currentNodeRoles: RoleModel[];
|
||||
|
||||
constructor(private nodePermissionService: NodePermissionService,
|
||||
private nodeApiService: NodesApiService,
|
||||
private contentService: ContentService) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
this.nodeApiService.getNode(this.nodeId).subscribe((node) => this.currentNode = node);
|
||||
this.nodePermissionService.getNodeWithRoles(this.nodeId).subscribe(({node, roles }) => {
|
||||
this.currentNode = node;
|
||||
this.currentNodeRoles = roles;
|
||||
});
|
||||
}
|
||||
|
||||
onSelect(selection: NodeEntry[]) {
|
||||
@ -63,9 +69,9 @@ export class AddPermissionComponent implements OnInit {
|
||||
|
||||
applySelection() {
|
||||
if (this.contentService.hasAllowableOperations(this.currentNode, AllowableOperationsEnum.UPDATEPERMISSIONS)) {
|
||||
this.nodePermissionService.updateNodePermissions(this.nodeId, this.selectedItems)
|
||||
.subscribe(
|
||||
(node) => {
|
||||
const permissions = this.transformNodeToPermissionElement(this.selectedItems, this.currentNodeRoles[0].role);
|
||||
this.nodePermissionService.updateNodePermissions(this.nodeId, permissions)
|
||||
.subscribe((node) => {
|
||||
this.success.emit(node);
|
||||
},
|
||||
(error) => {
|
||||
@ -74,4 +80,13 @@ export class AddPermissionComponent implements OnInit {
|
||||
}
|
||||
}
|
||||
|
||||
private transformNodeToPermissionElement(nodes: NodeEntry[], role: string): PermissionElement[] {
|
||||
return nodes.map((node) => {
|
||||
return {
|
||||
'authorityId': node.entry.properties['cm:authorityName'] ?? node.entry.properties['cm:userName'],
|
||||
'name': role,
|
||||
'accessStatus': 'ALLOWED'
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -57,7 +57,7 @@ export class SearchPermissionConfigurationService implements SearchConfiguration
|
||||
query = this.queryProvider.query.replace(
|
||||
new RegExp(/\${([^}]+)}/g), searchTerm);
|
||||
} else {
|
||||
query = `authorityName:*${searchTerm}* OR userName:*${searchTerm}*`;
|
||||
query = `(email:*${searchTerm}* OR firstName:*${searchTerm}* OR lastName:*${searchTerm}* OR displayName:*${searchTerm}* OR authorityName:*${searchTerm}* OR authorityDisplayName:*${searchTerm}*) AND ANAME:(\"0/APP.DEFAULT\")`;
|
||||
}
|
||||
return query;
|
||||
}
|
||||
|
@ -0,0 +1,44 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2019 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 { Node } from '@alfresco/js-api';
|
||||
import { Component, Input, OnInit } from '@angular/core';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
|
||||
@Component({
|
||||
selector: 'adf-node-path-column',
|
||||
template: `
|
||||
<span class="adf-user-name-column adf-datatable-cell-value" title="{{ displayText$ | async }}">
|
||||
{{ displayText$ | async }}
|
||||
</span>
|
||||
`,
|
||||
host: { class: 'adf-node-path-column adf-datatable-content-cell' }
|
||||
})
|
||||
export class NodePathColumnComponent implements OnInit {
|
||||
@Input()
|
||||
node: Node;
|
||||
|
||||
displayText$ = new BehaviorSubject<string>('');
|
||||
|
||||
ngOnInit() {
|
||||
this.updateValue();
|
||||
}
|
||||
|
||||
protected updateValue() {
|
||||
this.displayText$.next(this.node.path.name);
|
||||
}
|
||||
}
|
@ -0,0 +1,84 @@
|
||||
<adf-datatable class="adf-datatable-permission"
|
||||
id="adf-permission-display-container"
|
||||
[rows]="permissions"
|
||||
stickyHeader="true"
|
||||
selectionMode="multiple">
|
||||
|
||||
<data-columns>
|
||||
<data-column class="adf-key-icon"
|
||||
key="icon"
|
||||
type="icon"
|
||||
[sortable]="false">
|
||||
<ng-template let-context>
|
||||
<adf-user-icon-column [context]="context"></adf-user-icon-column>
|
||||
</ng-template>
|
||||
</data-column>
|
||||
|
||||
<data-column class="adf-authorityId-label adf-ellipsis-cell adf-expand-cell-2"
|
||||
[title]="'PERMISSION_MANAGER.COLUMN.NAME' | translate:{count:permissions.length}"
|
||||
key="authorityId">
|
||||
<ng-template let-context>
|
||||
<adf-user-name-column [context]="context"></adf-user-name-column>
|
||||
</ng-template>
|
||||
</data-column>
|
||||
|
||||
<data-column class="adf-authorityId-label adf-ellipsis-cell adf-expand-cell-2"
|
||||
title="PERMISSION_MANAGER.COLUMN.LOCATION"
|
||||
key="location"
|
||||
*ngIf="node && showLocation">
|
||||
<ng-template>
|
||||
<adf-node-path-column [node]="node"></adf-node-path-column>
|
||||
</ng-template>
|
||||
</data-column>
|
||||
|
||||
<data-column
|
||||
class="adf-ellipsis-cell adf-expand-cell-1"
|
||||
title="PERMISSION_MANAGER.PERMISSION_DISPLAY.ROLE"
|
||||
key="name"
|
||||
sortable="false">
|
||||
<ng-template let-entry="$implicit">
|
||||
<adf-user-role-column [readonly]="isReadOnly"
|
||||
[placeholder]="entry.data.getValue(entry.row, entry.col)"
|
||||
[value]="entry.data.getValue(entry.row, entry.col)"
|
||||
[roles]="roles"
|
||||
id="adf-select-role-permission"
|
||||
(roleChanged)="updateRole($event, entry.row.obj)">
|
||||
</adf-user-role-column>
|
||||
</ng-template>
|
||||
|
||||
<adf-data-column-header *ngIf="!isReadOnly">
|
||||
<ng-template>
|
||||
<adf-user-role-column class="adf-permission-role-column-header"
|
||||
placeholder="PERMISSION_MANAGER.COLUMN.BULK-ROLE"
|
||||
[roles]="roles"
|
||||
[value]="bulkSelectionRole"
|
||||
id="adf-bulk-select-role-permission"
|
||||
(roleChanged)="bulkRoleUpdate($event)">
|
||||
</adf-user-role-column>
|
||||
</ng-template>
|
||||
</adf-data-column-header>
|
||||
</data-column>
|
||||
|
||||
<data-column class="adf-delete-permission" key="delete" *ngIf="!isReadOnly" sortable="false">
|
||||
<ng-template let-entry="$implicit">
|
||||
<button mat-icon-button
|
||||
(click)="removePermission($event, entry.row.obj)"
|
||||
[attr.data-automation-id]="'adf-delete-permission-button-' + entry.row.obj.authorityId">
|
||||
<mat-icon>delete_outline</mat-icon>
|
||||
</button>
|
||||
</ng-template>
|
||||
</data-column>
|
||||
</data-columns>
|
||||
|
||||
<adf-no-content-template>
|
||||
<ng-template>
|
||||
<adf-empty-content
|
||||
id="adf-no-permissions-template"
|
||||
icon="supervisor_account"
|
||||
title="PERMISSION_MANAGER.MESSAGE.EMPTY-PERMISSION"
|
||||
subtitle="PERMISSION_MANAGER.MESSAGE.EMPTY-SUBTITLE">
|
||||
</adf-empty-content>
|
||||
</ng-template>
|
||||
</adf-no-content-template>
|
||||
|
||||
</adf-datatable>
|
@ -0,0 +1,45 @@
|
||||
@mixin adf-permission-container-theme($theme) {
|
||||
$adf-permission-list-width: 100% !default;
|
||||
|
||||
.adf {
|
||||
&-permission-label {
|
||||
max-width: 130px;
|
||||
min-width: 100px;
|
||||
margin-left: 50px;
|
||||
}
|
||||
|
||||
&-delete-permission {
|
||||
max-width: 50px;
|
||||
}
|
||||
|
||||
&-authorityId-label {
|
||||
min-width: 100px;
|
||||
}
|
||||
|
||||
&-key-icon {
|
||||
max-width: 50px;
|
||||
}
|
||||
|
||||
&-ellipsis-cell {
|
||||
position: sticky;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
&-display-permission-container {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
&-datatable-permission {
|
||||
display: flex;
|
||||
min-width: 450px;
|
||||
width: $adf-permission-list-width;
|
||||
|
||||
&.adf-datatable {
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,99 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2019 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 { setupTestBed } from '@alfresco/adf-core';
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { PermissionContainerComponent } from './permission-container.component';
|
||||
import { ContentTestingModule } from '../../../testing/content.testing.module';
|
||||
|
||||
describe('PermissionContainerComponent', () => {
|
||||
|
||||
let fixture: ComponentFixture<PermissionContainerComponent>;
|
||||
let component: PermissionContainerComponent;
|
||||
let element: HTMLElement;
|
||||
|
||||
setupTestBed({
|
||||
imports: [
|
||||
TranslateModule.forRoot(),
|
||||
ContentTestingModule
|
||||
]
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(PermissionContainerComponent);
|
||||
component = fixture.componentInstance;
|
||||
element = fixture.nativeElement;
|
||||
|
||||
component.permissions = [
|
||||
{
|
||||
authorityId: 'GROUP_EVERYONE',
|
||||
accessStatus: 'ALLOWED',
|
||||
isInherited: true,
|
||||
name: 'consumer',
|
||||
icon: null
|
||||
}
|
||||
];
|
||||
|
||||
component.roles = [
|
||||
{
|
||||
label: 'test',
|
||||
role: 'Test'
|
||||
},
|
||||
{
|
||||
label: 'consumr',
|
||||
role: 'Consumer'
|
||||
}
|
||||
];
|
||||
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
fixture.destroy();
|
||||
});
|
||||
|
||||
it('Should render the layout with details', () => {
|
||||
expect(element.querySelectorAll('.adf-datatable-permission .adf-datatable-row').length).toBe(2);
|
||||
expect(element.querySelector('adf-user-name-column').textContent).toContain('GROUP_EVERYONE');
|
||||
expect(element.querySelector('#adf-select-role-permission').textContent).toContain('consumer');
|
||||
});
|
||||
|
||||
it('should emit update event on role change', () => {
|
||||
spyOn(component.update, 'emit');
|
||||
|
||||
const selectBox = fixture.debugElement.query(By.css(('[id="adf-select-role-permission"] .mat-select-trigger')));
|
||||
selectBox.triggerEventHandler('click', null);
|
||||
fixture.detectChanges();
|
||||
|
||||
const options = fixture.debugElement.queryAll(By.css('mat-option'));
|
||||
expect(options).not.toBeNull();
|
||||
expect(options.length).toBe(2);
|
||||
options[0].triggerEventHandler('click', {});
|
||||
fixture.detectChanges();
|
||||
expect(component.update.emit).toHaveBeenCalledWith({ role: 'Test', permission: component.permissions[0] });
|
||||
});
|
||||
|
||||
it('should delete update event on row delete', () => {
|
||||
spyOn(component.delete, 'emit');
|
||||
const deleteButton: HTMLButtonElement = element.querySelector('[data-automation-id="adf-delete-permission-button-GROUP_EVERYONE"]');
|
||||
deleteButton.click();
|
||||
fixture.detectChanges();
|
||||
expect(component.delete.emit).toHaveBeenCalledWith(component.permissions[0]);
|
||||
});
|
||||
});
|
@ -0,0 +1,78 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2019 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, EventEmitter, Input, OnChanges, Output } from '@angular/core';
|
||||
import { Node, PermissionElement } from '@alfresco/js-api';
|
||||
import { PermissionDisplayModel } from '../../models/permission.model';
|
||||
import { RoleModel } from '../../models/role.model';
|
||||
|
||||
@Component({
|
||||
selector: 'adf-permission-container',
|
||||
templateUrl: './permission-container.component.html',
|
||||
styleUrls: ['./permission-container.component.scss']
|
||||
})
|
||||
export class PermissionContainerComponent implements OnChanges {
|
||||
|
||||
@Input()
|
||||
node: Node;
|
||||
|
||||
@Input()
|
||||
permissions: PermissionDisplayModel[] = [];
|
||||
|
||||
@Input()
|
||||
roles!: RoleModel[];
|
||||
|
||||
@Input()
|
||||
isReadOnly = false;
|
||||
|
||||
@Input()
|
||||
showLocation = false;
|
||||
|
||||
/** Emitted when the permission is updated. */
|
||||
@Output()
|
||||
update = new EventEmitter<{role: string, permission: PermissionDisplayModel}>();
|
||||
|
||||
@Output()
|
||||
updateAll = new EventEmitter<string>();
|
||||
|
||||
/** Emitted when the permission is updated. */
|
||||
@Output()
|
||||
delete = new EventEmitter<PermissionElement>();
|
||||
|
||||
/** Emitted when an error occurs. */
|
||||
@Output()
|
||||
error = new EventEmitter<any>();
|
||||
|
||||
bulkSelectionRole: string;
|
||||
|
||||
ngOnChanges(): void {
|
||||
this.bulkSelectionRole = '';
|
||||
}
|
||||
|
||||
updateRole(role: string, permission: PermissionDisplayModel) {
|
||||
this.update.emit({ role, permission });
|
||||
}
|
||||
|
||||
bulkRoleUpdate(role: string) {
|
||||
this.updateAll.emit(role);
|
||||
}
|
||||
|
||||
removePermission(event: MouseEvent, permissionRow: PermissionDisplayModel) {
|
||||
event.stopPropagation();
|
||||
this.delete.emit(permissionRow);
|
||||
}
|
||||
}
|
@ -1,84 +1,106 @@
|
||||
<div id="adf-permission-display-container" class="adf-display-permission-container">
|
||||
|
||||
<div *ngIf="!permissionList || !permissionList.length" id="adf-no-permissions-template">
|
||||
<div #ref>
|
||||
<ng-content select="adf-no-permission-template"></ng-content>
|
||||
</div>
|
||||
|
||||
<p *ngIf="ref.children.length == 0">
|
||||
{{ 'PERMISSION_MANAGER.PERMISSION_DISPLAY.NO_PERMISSIONS' | translate }}
|
||||
</p>
|
||||
<mat-card class="adf-permission-card" id="adf-permission-manager-card">
|
||||
<div *ngIf="!(permissionList.data$ | async) && permissionList.loading$ | async" class="adf-permission-loader">
|
||||
<mat-progress-spinner [color]="'primary'"
|
||||
[mode]="'indeterminate'">
|
||||
</mat-progress-spinner>
|
||||
</div>
|
||||
|
||||
<adf-datatable [rows]="permissionList" class="adf-datatable-permission" *ngIf="permissionList && permissionList.length">
|
||||
<data-columns>
|
||||
<data-column
|
||||
class="adf-key-icon"
|
||||
key="icon" type="icon"
|
||||
[sortable]="false">
|
||||
</data-column>
|
||||
<data-column
|
||||
class="adf-authorityId-label adf-ellipsis-cell adf-expand-cell-2"
|
||||
title="PERMISSION_MANAGER.PERMISSION_DISPLAY.AUTHORITY_ID"
|
||||
key="authorityId">
|
||||
</data-column>
|
||||
<data-column
|
||||
class="adf-ellipsis-cell adf-expand-cell-1"
|
||||
title="PERMISSION_MANAGER.PERMISSION_DISPLAY.ROLE"
|
||||
key="name">
|
||||
<ng-template let-entry="$implicit">
|
||||
<mat-form-field *ngIf="!entry.row.getValue('isInherited') else show_only_label">
|
||||
<mat-select
|
||||
id="adf-select-role-permission"
|
||||
[placeholder]="entry.data.getValue(entry.row, entry.col)"
|
||||
value="{{entry.data.getValue(entry.row, entry.col)}}"
|
||||
(selectionChange)="saveNewRole($event, entry.row.obj)">
|
||||
<mat-option *ngFor="let role of settableRoles" [value]="role">{{ role | adfLocalizedRole }}</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
<ng-template #show_only_label>
|
||||
<span>{{ entry.data.getValue(entry.row, entry.col) | adfLocalizedRole }}</span>
|
||||
</ng-template>
|
||||
</ng-template>
|
||||
</data-column>
|
||||
<data-column
|
||||
class="adf-permission-label"
|
||||
title="PERMISSION_MANAGER.PERMISSION_DISPLAY.INHERITED"
|
||||
key="isInherited"
|
||||
[focus]="false">
|
||||
<ng-template let-entry="$implicit">
|
||||
<mat-chip-list>
|
||||
<mat-chip
|
||||
*ngIf="!!entry.data.getValue(entry.row, entry.col) else locally_set_chip"
|
||||
class="mat-chip mat-primary mat-standard-chip mat-chip-selected adf-inherited-label"
|
||||
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 class="adf-locallyset-label">
|
||||
<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-column class="adf-delete-permission" key="delete">
|
||||
<ng-template let-entry="$implicit">
|
||||
<button
|
||||
*ngIf="!entry.row.getValue('isInherited')"
|
||||
mat-icon-button
|
||||
color="primary"
|
||||
(click)="removePermission(entry.row.obj)"
|
||||
data-automation-id="adf-delete-permission-button">
|
||||
<mat-icon>highlight_off</mat-icon>
|
||||
</button>
|
||||
</ng-template>
|
||||
</data-column>
|
||||
</data-columns>
|
||||
</adf-datatable>
|
||||
</div>
|
||||
<ng-container *ngIf="permissionList.error$ | async">
|
||||
<div class="adf-no-permission__template" id="adf-permission-manager-error">
|
||||
<mat-icon [color]="'error'">error</mat-icon>
|
||||
<p class="adf-no-permission__template--text">{{ 'PERMISSION_MANAGER.ERROR.NOT-FOUND'| translate }}</p>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="permissionList.data$ | async as model">
|
||||
<div class="adf-permission-container" #target>
|
||||
|
||||
<div class="adf-permission-header">
|
||||
<span class="adf-inherit-container">
|
||||
<h3 class="adf-inherit-container-header">
|
||||
{{'PERMISSION_MANAGER.LABELS.INHERITED-PERMISSIONS' | translate }}
|
||||
|
||||
{{ (model.node.permissions.isInheritanceEnabled ? "PERMISSION_MANAGER.LABELS.ON" : "PERMISSION_MANAGER.LABELS.OFF") | translate }}
|
||||
</h3>
|
||||
|
||||
<mat-slide-toggle
|
||||
class="adf-inherit-toggle"
|
||||
data-automation-id="adf-inherit-toggle-button"
|
||||
[checked]="model.node.permissions.isInheritanceEnabled"
|
||||
(change)="permissionList.toggleInherited($event)">
|
||||
</mat-slide-toggle>
|
||||
</span>
|
||||
|
||||
<span class="adf-inherit-subtitle" title="total">
|
||||
{{'PERMISSION_MANAGER.LABELS.INHERITED-SUBTITLE' | translate: { count: model.inheritedPermissions.length } }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<button
|
||||
mat-button
|
||||
data-automation-id="permission-info-button"
|
||||
[adf-pop-over]="inheritedPermission"
|
||||
[target]="target"
|
||||
#popOver="adfPopOver"
|
||||
*ngIf="model.node.permissions.isInheritanceEnabled">
|
||||
{{ popOver.open ? "Hide" : "Show" }}
|
||||
<mat-icon *ngIf="popOver.open"> keyboard_arrow_up </mat-icon>
|
||||
<mat-icon *ngIf="!popOver.open"> keyboard_arrow_down </mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<ng-template #inheritedPermission>
|
||||
<div class="adf-pop-over-card">
|
||||
<adf-permission-container
|
||||
data-automation-id="adf-inherited-permission"
|
||||
[isReadOnly]="true"
|
||||
[node]="model.node"
|
||||
[permissions]="model.inheritedPermissions"
|
||||
[showLocation]="true"
|
||||
[roles]="model.roles">
|
||||
</adf-permission-container>
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
<mat-card-content style="overflow: hidden">
|
||||
<section class="adf-permission-content-header">
|
||||
<h3>{{'PERMISSION_MANAGER.LABELS.DIRECT-PERMISSIONS' | translate }}</h3>
|
||||
|
||||
<div class="adf-toolbar--spacer"></div>
|
||||
|
||||
<button
|
||||
mat-button
|
||||
[matTooltip]="'PERMISSION_MANAGER.ACTION.DELETE' | translate"
|
||||
[disabled]="!selectedPermissions?.length"
|
||||
(click)="deleteSelection()"
|
||||
data-automation-id="adf-delete-selected-permission">
|
||||
<mat-icon>delete_outline</mat-icon>
|
||||
</button>
|
||||
|
||||
<adf-toolbar-divider></adf-toolbar-divider>
|
||||
|
||||
<button
|
||||
mat-button
|
||||
[matTooltip]="'PERMISSION_MANAGER.ACTION.ADD-PERMISSION' | translate"
|
||||
(click)="openAddPermissionDialog()"
|
||||
data-automation-id="adf-add-permission-button">
|
||||
<mat-icon>person_add_outline</mat-icon>
|
||||
</button>
|
||||
</section>
|
||||
|
||||
<adf-permission-container
|
||||
class="adf-permission-list"
|
||||
data-automation-id="adf-locally-set-permission"
|
||||
[node]="model.node"
|
||||
[permissions]="model.localPermissions"
|
||||
(update)="updatePermission($event)"
|
||||
(delete)="deletePermission($event)"
|
||||
(updateAll)="updateAllPermission($event)"
|
||||
(row-select)="onSelect($event.detail.selection)"
|
||||
(row-unselect)="onSelect($event.detail.selection)"
|
||||
[roles]="model.roles">
|
||||
</adf-permission-container>
|
||||
|
||||
</mat-card-content>
|
||||
</ng-container>
|
||||
</mat-card>
|
||||
|
@ -1,50 +1,92 @@
|
||||
@mixin adf-permission-list-theme($theme) {
|
||||
$adf-permission-list-width: 70% !default;
|
||||
$primary: map-get($theme, primary);
|
||||
$accent: map-get($theme, accent);
|
||||
$warn: map-get($theme, warn);
|
||||
$foreground: map-get($theme, foreground);
|
||||
$background: map-get($theme, background);
|
||||
|
||||
.adf {
|
||||
&-permission-label {
|
||||
max-width: 130px;
|
||||
min-width: 100px;
|
||||
margin-left: 50px;
|
||||
&-permission-card {
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
display: flex !important;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
&-delete-permission {
|
||||
max-width: 50px;
|
||||
&-permission-loader {
|
||||
margin-left: 45%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
&-authorityId-label {
|
||||
min-width: 100px;
|
||||
}
|
||||
|
||||
&-key-icon {
|
||||
max-width: 50px;
|
||||
}
|
||||
|
||||
&-ellipsis-cell {
|
||||
position: sticky;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
&-display-permission-container {
|
||||
&-permission-container {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
flex: 1;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 10px 15px;
|
||||
border: 1px solid mat-color($foreground, divider);
|
||||
}
|
||||
|
||||
&-datatable-permission {
|
||||
&-inherit-container {
|
||||
display: flex;
|
||||
min-width: 450px;
|
||||
width: $adf-permission-list-width;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
|
||||
&-header {
|
||||
margin-bottom: 10px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
&-locallyset-label {
|
||||
padding: 4px;
|
||||
&-inherit-toggle {
|
||||
padding-left: 30px;
|
||||
}
|
||||
|
||||
&-inherited-label {
|
||||
width: 92.13px;
|
||||
justify-content: center;
|
||||
&-inherit-subtitle {
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
|
||||
&-permission-content-header {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
padding: 5px 15px;
|
||||
}
|
||||
|
||||
&-permission-role-column-header {
|
||||
position: relative !important;
|
||||
height: 40px;
|
||||
.mat-form-field-infix {
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
|
||||
&-permission-header {
|
||||
@include flex-column;
|
||||
}
|
||||
|
||||
&-permission-list {
|
||||
display: flex;
|
||||
height: calc(100% - 63px);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[aria-sort='Ascending'] adf-user-role-column,
|
||||
[aria-sort='Descending'] adf-user-role-column {
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
}
|
||||
|
||||
|
||||
.adf-permission-pop-over {
|
||||
padding-right: 15px;
|
||||
width: 100%;
|
||||
|
||||
.adf-pop-over-card {
|
||||
width: 100%;
|
||||
@include mat-elevation(16, mat-color($foreground, divider), 0.8);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -15,21 +15,24 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { PermissionListComponent } from './permission-list.component';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { NodesApiService, SearchService, setupTestBed } from '@alfresco/adf-core';
|
||||
import { of } from 'rxjs';
|
||||
import { NodePermissionService } from '../../services/node-permission.service';
|
||||
import { fakeNodeWithPermissions,
|
||||
fakeNodeInheritedOnly,
|
||||
fakeNodeWithOnlyLocally,
|
||||
fakeSiteNodeResponse,
|
||||
fakeSiteRoles,
|
||||
fakeNodeWithoutPermissions,
|
||||
fakeEmptyResponse } from '../../../mock/permission-list.component.mock';
|
||||
import { ContentTestingModule } from '../../../testing/content.testing.module';
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { of, throwError } from 'rxjs';
|
||||
import { PermissionListComponent } from './permission-list.component';
|
||||
import { NodePermissionService } from '../../services/node-permission.service';
|
||||
import {
|
||||
fakeEmptyResponse,
|
||||
fakeNodeInheritedOnly,
|
||||
fakeNodeWithOnlyLocally,
|
||||
fakeNodeWithoutPermissions,
|
||||
fakeNodeWithPermissions,
|
||||
fakeReadOnlyNodeInherited,
|
||||
fakeSiteNodeResponse,
|
||||
fakeSiteRoles
|
||||
} from '../../../mock/permission-list.component.mock';
|
||||
import { ContentTestingModule } from '../../../testing/content.testing.module';
|
||||
|
||||
describe('PermissionListComponent', () => {
|
||||
|
||||
@ -39,6 +42,9 @@ describe('PermissionListComponent', () => {
|
||||
let nodeService: NodesApiService;
|
||||
let nodePermissionService: NodePermissionService;
|
||||
let searchApiService: SearchService;
|
||||
let getNodeSpy: jasmine.Spy;
|
||||
let searchQuerySpy: jasmine.Spy;
|
||||
const fakeLocalPermission = JSON.parse(JSON.stringify(fakeNodeWithOnlyLocally));
|
||||
|
||||
setupTestBed({
|
||||
imports: [
|
||||
@ -54,142 +60,188 @@ describe('PermissionListComponent', () => {
|
||||
nodeService = TestBed.inject(NodesApiService);
|
||||
nodePermissionService = TestBed.inject(NodePermissionService);
|
||||
searchApiService = TestBed.inject(SearchService);
|
||||
|
||||
spyOn(nodePermissionService, 'getGroupMemberByGroupName').and.returnValue(of(fakeSiteRoles));
|
||||
getNodeSpy = spyOn(nodeService, 'getNode').and.returnValue(of(fakeNodeWithoutPermissions));
|
||||
searchQuerySpy = spyOn(searchApiService, 'searchByQueryBody').and.returnValue(of(fakeEmptyResponse));
|
||||
component.nodeId = 'fake-node-id';
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
fixture.destroy();
|
||||
});
|
||||
|
||||
it('should be able to render the component', () => {
|
||||
spyOn(nodeService, 'getNode').and.returnValue(of(fakeNodeWithOnlyLocally));
|
||||
spyOn(nodePermissionService, 'getNodeRoles').and.returnValue(of([]));
|
||||
fixture.detectChanges();
|
||||
expect(element.querySelector('#adf-permission-display-container')).not.toBeNull();
|
||||
it('should render default layout', async () => {
|
||||
component.nodeId = 'fake-node-id';
|
||||
getNodeSpy.and.returnValue(of(fakeNodeWithoutPermissions));
|
||||
component.ngOnInit();
|
||||
await fixture.detectChanges();
|
||||
expect(element.querySelector('.adf-permission-container')).not.toBeNull();
|
||||
expect(element.querySelector('[data-automation-id="adf-locally-set-permission"]')).not.toBeNull();
|
||||
});
|
||||
|
||||
it('should render default empty template when no permissions', () => {
|
||||
it('should render error template', async () => {
|
||||
component.nodeId = 'fake-node-id';
|
||||
spyOn(nodeService, 'getNode').and.returnValue(of(fakeNodeWithoutPermissions));
|
||||
spyOn(searchApiService, 'searchByQueryBody').and.returnValue(of(fakeEmptyResponse));
|
||||
fixture.detectChanges();
|
||||
getNodeSpy.and.returnValue(throwError(null));
|
||||
component.ngOnInit();
|
||||
await fixture.detectChanges();
|
||||
|
||||
expect(element.querySelector('#adf-no-permissions-template')).not.toBeNull();
|
||||
expect(element.querySelector('#adf-permission-display-container .adf-datatable-permission')).toBeNull();
|
||||
expect(element.querySelector('.adf-no-permission__template')).not.toBeNull();
|
||||
expect(element.querySelector('.adf-no-permission__template p').textContent).toContain('PERMISSION_MANAGER.ERROR.NOT-FOUND');
|
||||
});
|
||||
|
||||
it('should show the node permissions', () => {
|
||||
component.nodeId = 'fake-node-id';
|
||||
spyOn(nodeService, 'getNode').and.returnValue(of(fakeNodeWithPermissions));
|
||||
spyOn(searchApiService, 'searchByQueryBody').and.returnValue(of(fakeEmptyResponse));
|
||||
getNodeSpy.and.returnValue(of(fakeNodeWithPermissions));
|
||||
component.ngOnInit();
|
||||
fixture.detectChanges();
|
||||
expect(element.querySelector('#adf-permission-display-container')).not.toBeNull();
|
||||
expect(element.querySelectorAll('.adf-datatable-row').length).toBe(4);
|
||||
|
||||
expect(element.querySelectorAll('[data-automation-id="adf-locally-set-permission"] .adf-datatable-row').length).toBe(2);
|
||||
|
||||
const showButton: HTMLButtonElement = element.querySelector('[data-automation-id="permission-info-button"]');
|
||||
showButton.click();
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(document.querySelectorAll('[data-automation-id="adf-inherited-permission"] .adf-datatable-row').length).toBe(3);
|
||||
});
|
||||
|
||||
it('should show inherited label for inherited permissions', () => {
|
||||
component.nodeId = 'fake-node-id';
|
||||
spyOn(nodeService, 'getNode').and.returnValue(of(fakeNodeInheritedOnly));
|
||||
spyOn(searchApiService, 'searchByQueryBody').and.returnValue(of(fakeEmptyResponse));
|
||||
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();
|
||||
});
|
||||
describe('Inherited Permission', () => {
|
||||
it('should show inherited details', async() => {
|
||||
getNodeSpy.and.returnValue(of(fakeNodeInheritedOnly));
|
||||
component.ngOnInit();
|
||||
await fixture.detectChanges();
|
||||
|
||||
describe('when it is a locally set permission', () => {
|
||||
|
||||
it('should show locally set label for locally set permissions', () => {
|
||||
component.nodeId = 'fake-node-id';
|
||||
spyOn(nodeService, 'getNode').and.returnValue(of(fakeNodeWithOnlyLocally));
|
||||
spyOn(nodePermissionService, 'getGroupMemberByGroupName').and.returnValue(of(fakeSiteRoles));
|
||||
spyOn(searchApiService, 'searchByQueryBody').and.returnValue(of(fakeSiteNodeResponse));
|
||||
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();
|
||||
expect(element.querySelector('.adf-inherit-container .mat-checked')).toBeDefined();
|
||||
expect(element.querySelector('.adf-inherit-container h3').textContent.trim())
|
||||
.toBe('PERMISSION_MANAGER.LABELS.INHERITED-PERMISSIONS PERMISSION_MANAGER.LABELS.ON');
|
||||
expect(element.querySelector('span[title="total"]').textContent.trim())
|
||||
.toBe('PERMISSION_MANAGER.LABELS.INHERITED-SUBTITLE');
|
||||
});
|
||||
|
||||
it('should show a dropdown with the possible roles', async(() => {
|
||||
component.nodeId = 'fake-node-id';
|
||||
spyOn(nodeService, 'getNode').and.returnValue(of(fakeNodeWithOnlyLocally));
|
||||
spyOn(nodePermissionService, 'getGroupMemberByGroupName').and.returnValue(of(fakeSiteRoles));
|
||||
spyOn(searchApiService, 'searchByQueryBody').and.returnValue(of(fakeSiteNodeResponse));
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => {
|
||||
fixture.detectChanges();
|
||||
expect(element.querySelector('#adf-select-role-permission')).toBeDefined();
|
||||
expect(element.querySelector('#adf-select-role-permission')).not.toBeNull();
|
||||
const selectBox = fixture.debugElement.query(By.css(('#adf-select-role-permission .mat-select-trigger')));
|
||||
selectBox.triggerEventHandler('click', null);
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => {
|
||||
fixture.detectChanges();
|
||||
const options: any = fixture.debugElement.queryAll(By.css('mat-option'));
|
||||
expect(options).not.toBeNull();
|
||||
expect(options.length).toBe(4);
|
||||
expect(options[0].nativeElement.innerText).toContain('ADF.ROLES.SITECOLLABORATOR');
|
||||
expect(options[1].nativeElement.innerText).toContain('ADF.ROLES.SITECONSUMER');
|
||||
expect(options[2].nativeElement.innerText).toContain('ADF.ROLES.SITECONTRIBUTOR');
|
||||
expect(options[3].nativeElement.innerText).toContain('ADF.ROLES.SITEMANAGER');
|
||||
});
|
||||
});
|
||||
}));
|
||||
it('should toggle the inherited button', async() => {
|
||||
getNodeSpy.and.returnValue(of(fakeNodeInheritedOnly));
|
||||
component.ngOnInit();
|
||||
await fixture.detectChanges();
|
||||
|
||||
it('should show the settable roles if the node is not in any site', async(() => {
|
||||
component.nodeId = 'fake-node-id';
|
||||
spyOn(nodeService, 'getNode').and.returnValue(of(fakeNodeWithOnlyLocally));
|
||||
spyOn(searchApiService, 'searchByQueryBody').and.returnValue(of(fakeEmptyResponse));
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => {
|
||||
fixture.detectChanges();
|
||||
expect(element.querySelector('#adf-select-role-permission')).toBeDefined();
|
||||
expect(element.querySelector('#adf-select-role-permission')).not.toBeNull();
|
||||
const selectBox = fixture.debugElement.query(By.css(('#adf-select-role-permission .mat-select-trigger')));
|
||||
selectBox.triggerEventHandler('click', null);
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => {
|
||||
fixture.detectChanges();
|
||||
const options: any = fixture.debugElement.queryAll(By.css('mat-option'));
|
||||
expect(options).not.toBeNull();
|
||||
expect(options.length).toBe(5);
|
||||
expect(options[0].nativeElement.innerText).toContain('ADF.ROLES.CONTRIBUTOR');
|
||||
expect(options[1].nativeElement.innerText).toContain('ADF.ROLES.COLLABORATOR');
|
||||
expect(options[2].nativeElement.innerText).toContain('ADF.ROLES.COORDINATOR');
|
||||
expect(options[3].nativeElement.innerText).toContain('ADF.ROLES.EDITOR');
|
||||
expect(options[4].nativeElement.innerText).toContain('ADF.ROLES.CONSUMER');
|
||||
});
|
||||
});
|
||||
}));
|
||||
expect(element.querySelector('.adf-inherit-container .mat-checked')).toBeDefined();
|
||||
expect(element.querySelector('.adf-inherit-container h3').textContent.trim())
|
||||
.toBe('PERMISSION_MANAGER.LABELS.INHERITED-PERMISSIONS PERMISSION_MANAGER.LABELS.ON');
|
||||
expect(element.querySelector('span[title="total"]').textContent.trim())
|
||||
.toBe('PERMISSION_MANAGER.LABELS.INHERITED-SUBTITLE');
|
||||
|
||||
it('should update the role when another value is chosen', async(() => {
|
||||
component.nodeId = 'fake-node-id';
|
||||
spyOn(nodeService, 'getNode').and.returnValue(of(fakeNodeWithOnlyLocally));
|
||||
spyOn(nodeService, 'updateNode').and.returnValue(of({id: 'fake-updated-node'}));
|
||||
spyOn(searchApiService, 'searchByQueryBody').and.returnValue(of(fakeEmptyResponse));
|
||||
component.update.subscribe((updatedPermission) => {
|
||||
expect(updatedPermission).not.toBeNull();
|
||||
expect(updatedPermission.name).toBe('Editor');
|
||||
expect(updatedPermission.authorityId).toBe('GROUP_EVERYONE');
|
||||
expect(updatedPermission.accessStatus).toBe('ALLOWED');
|
||||
});
|
||||
spyOn(nodeService, 'updateNode').and.returnValue(of(fakeLocalPermission));
|
||||
|
||||
const slider = fixture.debugElement.query(By.css('mat-slide-toggle'));
|
||||
slider.triggerEventHandler('change', { source: { checked: false } });
|
||||
await fixture.detectChanges();
|
||||
|
||||
expect(element.querySelector('.adf-inherit-container .mat-checked')).toBe(null);
|
||||
expect(element.querySelector('.adf-inherit-container h3').textContent.trim())
|
||||
.toBe('PERMISSION_MANAGER.LABELS.INHERITED-PERMISSIONS PERMISSION_MANAGER.LABELS.OFF');
|
||||
expect(element.querySelector('span[title="total"]').textContent.trim())
|
||||
.toBe('PERMISSION_MANAGER.LABELS.INHERITED-SUBTITLE');
|
||||
});
|
||||
|
||||
it('should not toggle inherited button for read only users', async () => {
|
||||
getNodeSpy.and.returnValue(of(fakeReadOnlyNodeInherited));
|
||||
component.ngOnInit();
|
||||
await fixture.detectChanges();
|
||||
|
||||
expect(element.querySelector('.adf-inherit-container .mat-checked')).toBeDefined();
|
||||
expect(element.querySelector('.adf-inherit-container h3').textContent.trim())
|
||||
.toBe('PERMISSION_MANAGER.LABELS.INHERITED-PERMISSIONS PERMISSION_MANAGER.LABELS.ON');
|
||||
expect(element.querySelector('span[title="total"]').textContent.trim())
|
||||
.toBe('PERMISSION_MANAGER.LABELS.INHERITED-SUBTITLE');
|
||||
|
||||
spyOn(nodeService, 'updateNode').and.returnValue(of(fakeLocalPermission));
|
||||
|
||||
const slider = fixture.debugElement.query(By.css('mat-slide-toggle'));
|
||||
slider.triggerEventHandler('change', { source: { checked: false } });
|
||||
await fixture.detectChanges();
|
||||
|
||||
expect(element.querySelector('.adf-inherit-container .mat-checked')).toBeDefined();
|
||||
expect(element.querySelector('.adf-inherit-container h3').textContent.trim())
|
||||
.toBe('PERMISSION_MANAGER.LABELS.INHERITED-PERMISSIONS PERMISSION_MANAGER.LABELS.ON');
|
||||
expect(element.querySelector('span[title="total"]').textContent.trim())
|
||||
.toBe('PERMISSION_MANAGER.LABELS.INHERITED-SUBTITLE');
|
||||
expect(document.querySelector('simple-snack-bar').textContent)
|
||||
.toContain('PERMISSION_MANAGER.ERROR.NOT-ALLOWED');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('locally set permission', () => {
|
||||
beforeEach(() => {
|
||||
getNodeSpy.and.returnValue(of(fakeLocalPermission));
|
||||
});
|
||||
|
||||
it('should show locally set permissions', async() => {
|
||||
searchQuerySpy.and.returnValue(of(fakeSiteNodeResponse));
|
||||
component.ngOnInit();
|
||||
|
||||
await fixture.detectChanges();
|
||||
expect(element.querySelector('adf-user-name-column').textContent).toContain('GROUP_EVERYONE');
|
||||
expect(element.querySelector('#adf-select-role-permission').textContent).toContain('Contributor');
|
||||
});
|
||||
|
||||
it('should see the settable roles if the node is not in any site', async() => {
|
||||
searchQuerySpy.and.returnValue(of(fakeSiteNodeResponse));
|
||||
component.ngOnInit();
|
||||
|
||||
await fixture.detectChanges();
|
||||
expect(element.querySelector('adf-user-name-column').textContent).toContain('GROUP_EVERYONE');
|
||||
expect(element.querySelector('#adf-select-role-permission').textContent).toContain('Contributor');
|
||||
|
||||
const selectBox = fixture.debugElement.query(By.css(('[id="adf-select-role-permission"] .mat-select-trigger')));
|
||||
selectBox.triggerEventHandler('click', null);
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => {
|
||||
fixture.detectChanges();
|
||||
expect(element.querySelector('#adf-select-role-permission')).toBeDefined();
|
||||
expect(element.querySelector('#adf-select-role-permission')).not.toBeNull();
|
||||
const selectBox = fixture.debugElement.query(By.css(('#adf-select-role-permission .mat-select-trigger')));
|
||||
selectBox.triggerEventHandler('click', null);
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => {
|
||||
fixture.detectChanges();
|
||||
const options: any = fixture.debugElement.queryAll(By.css('mat-option'));
|
||||
expect(options).not.toBeNull();
|
||||
expect(options.length).toBe(5);
|
||||
options[3].triggerEventHandler('click', {});
|
||||
fixture.detectChanges();
|
||||
expect(nodeService.updateNode).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
}));
|
||||
});
|
||||
|
||||
const options: any = fixture.debugElement.queryAll(By.css('mat-option'));
|
||||
expect(options).not.toBeNull();
|
||||
expect(options.length).toBe(4);
|
||||
expect(options[0].nativeElement.innerText).toContain('ADF.ROLES.SITECOLLABORATOR');
|
||||
expect(options[1].nativeElement.innerText).toContain('ADF.ROLES.SITECONSUMER');
|
||||
expect(options[2].nativeElement.innerText).toContain('ADF.ROLES.SITECONTRIBUTOR');
|
||||
expect(options[3].nativeElement.innerText).toContain('ADF.ROLES.SITEMANAGER');
|
||||
});
|
||||
|
||||
it('should update the role when another value is chosen', async () => {
|
||||
spyOn(nodeService, 'updateNode').and.returnValue(of({id: 'fake-uwpdated-node'}));
|
||||
searchQuerySpy.and.returnValue(of(fakeEmptyResponse));
|
||||
component.ngOnInit();
|
||||
|
||||
await fixture.detectChanges();
|
||||
|
||||
expect(element.querySelector('adf-user-name-column').textContent).toContain('GROUP_EVERYONE');
|
||||
expect(element.querySelector('#adf-select-role-permission').textContent).toContain('Contributor');
|
||||
|
||||
const selectBox = fixture.debugElement.query(By.css(('[id="adf-select-role-permission"] .mat-select-trigger')));
|
||||
selectBox.triggerEventHandler('click', null);
|
||||
fixture.detectChanges();
|
||||
const options: any = fixture.debugElement.queryAll(By.css('mat-option'));
|
||||
expect(options).not.toBeNull();
|
||||
expect(options.length).toBe(5);
|
||||
options[3].triggerEventHandler('click', {});
|
||||
fixture.detectChanges();
|
||||
expect(nodeService.updateNode).toHaveBeenCalledWith('f472543f-7218-403d-917b-7a5861257244', { permissions: { locallySet: [ { accessStatus: 'ALLOWED', name: 'Editor', authorityId: 'GROUP_EVERYONE' } ] } });
|
||||
});
|
||||
|
||||
it('should delete the person', async () => {
|
||||
spyOn(nodeService, 'updateNode').and.returnValue(of({id: 'fake-uwpdated-node'}));
|
||||
searchQuerySpy.and.returnValue(of(fakeEmptyResponse));
|
||||
component.ngOnInit();
|
||||
await fixture.detectChanges();
|
||||
|
||||
expect(element.querySelector('adf-user-name-column').textContent).toContain('GROUP_EVERYONE');
|
||||
expect(element.querySelector('#adf-select-role-permission').textContent).toContain('Contributor');
|
||||
|
||||
const showButton: HTMLButtonElement = element.querySelector('[data-automation-id="adf-delete-permission-button-GROUP_EVERYONE"]');
|
||||
showButton.click();
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(nodeService.updateNode).toHaveBeenCalledWith('f472543f-7218-403d-917b-7a5861257244', { permissions: { locallySet: [ ] } });
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
@ -15,11 +15,11 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Component, ViewEncapsulation, Input, OnInit, EventEmitter, Output } from '@angular/core';
|
||||
import { NodesApiService } from '@alfresco/adf-core';
|
||||
import { Node, PermissionElement } from '@alfresco/js-api';
|
||||
import { ObjectDataRow } from '@alfresco/adf-core';
|
||||
import { PermissionElement } from '@alfresco/js-api';
|
||||
import { Component, EventEmitter, Input, Output, ViewEncapsulation } from '@angular/core';
|
||||
import { PermissionDisplayModel } from '../../models/permission.model';
|
||||
import { NodePermissionService } from '../../services/node-permission.service';
|
||||
import { PermissionListService } from './permission-list.service';
|
||||
|
||||
@Component({
|
||||
selector: 'adf-permission-list',
|
||||
@ -27,70 +27,54 @@ import { NodePermissionService } from '../../services/node-permission.service';
|
||||
styleUrls: ['./permission-list.component.scss'],
|
||||
encapsulation: ViewEncapsulation.None
|
||||
})
|
||||
export class PermissionListComponent implements OnInit {
|
||||
|
||||
export class PermissionListComponent {
|
||||
/** ID of the node whose permissions you want to show. */
|
||||
@Input()
|
||||
nodeId: string = '';
|
||||
nodeId: string;
|
||||
|
||||
/** Emitted when the permission is updated. */
|
||||
@Output()
|
||||
update = new EventEmitter<PermissionElement>();
|
||||
update: EventEmitter<PermissionElement>;
|
||||
|
||||
/** Emitted when an error occurs. */
|
||||
@Output()
|
||||
error = new EventEmitter<any>();
|
||||
error: EventEmitter<any>;
|
||||
|
||||
permissionList: PermissionDisplayModel[];
|
||||
settableRoles: any[];
|
||||
actualNode: Node;
|
||||
selectedPermissions: PermissionDisplayModel[] = [];
|
||||
|
||||
constructor(private nodeService: NodesApiService,
|
||||
private nodePermissionService: NodePermissionService) {
|
||||
constructor(public readonly permissionList: PermissionListService) {
|
||||
this.error = this.permissionList.errored;
|
||||
this.update = this.permissionList.updated;
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.fetchNodePermissions();
|
||||
ngOnInit(): void {
|
||||
this.permissionList.fetchPermission(this.nodeId);
|
||||
}
|
||||
|
||||
reload() {
|
||||
this.fetchNodePermissions();
|
||||
openAddPermissionDialog() {
|
||||
this.permissionList.updateNodePermissionByDialog();
|
||||
}
|
||||
|
||||
private fetchNodePermissions() {
|
||||
this.nodeService.getNode(this.nodeId).subscribe((node: Node) => {
|
||||
this.actualNode = node;
|
||||
this.permissionList = this.nodePermissionService.getNodePermissions(node);
|
||||
|
||||
this.nodePermissionService.getNodeRoles(node).subscribe((settableList: string[]) => {
|
||||
this.settableRoles = settableList;
|
||||
});
|
||||
});
|
||||
onSelect(selections: ObjectDataRow[]) {
|
||||
this.selectedPermissions = selections.map((selection) => selection['obj']);
|
||||
}
|
||||
|
||||
saveNewRole(event: any, permissionRow: PermissionDisplayModel) {
|
||||
const updatedPermissionRole = this.buildUpdatedPermission(event.value, permissionRow);
|
||||
|
||||
this.nodePermissionService.updatePermissionRole(this.actualNode, updatedPermissionRole)
|
||||
.subscribe(() => {
|
||||
this.update.emit(updatedPermissionRole);
|
||||
});
|
||||
deleteSelection() {
|
||||
this.permissionList.deletePermissions(this.selectedPermissions);
|
||||
this.selectedPermissions = [];
|
||||
}
|
||||
|
||||
private buildUpdatedPermission(newRole: string, permissionRow: PermissionDisplayModel): PermissionElement {
|
||||
return {
|
||||
accessStatus: permissionRow.accessStatus,
|
||||
name: newRole,
|
||||
authorityId: permissionRow.authorityId
|
||||
};
|
||||
updatePermission({role, permission}) {
|
||||
this.permissionList.updateRole(role, permission);
|
||||
}
|
||||
|
||||
removePermission(permissionRow: PermissionDisplayModel) {
|
||||
this.nodePermissionService
|
||||
.removePermission(this.actualNode, permissionRow)
|
||||
.subscribe(
|
||||
node => this.update.emit(node),
|
||||
error => this.error.emit(error)
|
||||
);
|
||||
deletePermission(permission: PermissionDisplayModel) {
|
||||
this.selectedPermissions = [];
|
||||
this.permissionList.deletePermission(permission);
|
||||
}
|
||||
|
||||
updateAllPermission(role: string) {
|
||||
this.permissionList.bulkRoleUpdate(role);
|
||||
this.selectedPermissions = [];
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,149 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2019 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 { NodesApiService, NotificationService, setupTestBed } from '@alfresco/adf-core';
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { of, throwError } from 'rxjs';
|
||||
import { PermissionListService } from './permission-list.service';
|
||||
import { ContentTestingModule } from '../../../testing/content.testing.module';
|
||||
import { NodePermissionService } from '../../services/node-permission.service';
|
||||
import { fakeNodeInheritedOnly, fakeNodeWithOnlyLocally } from '../../../mock/permission-list.component.mock';
|
||||
import { PermissionDisplayModel } from '../../models/permission.model';
|
||||
|
||||
describe('PermissionListService', () => {
|
||||
let service: PermissionListService;
|
||||
let nodePermissionService: NodePermissionService;
|
||||
let notificationService: NotificationService;
|
||||
let nodesApiService: NodesApiService;
|
||||
const localPermission = [new PermissionDisplayModel({
|
||||
authorityId: 'GROUP_EVERYONE',
|
||||
name: 'Contributor',
|
||||
accessStatus: 'ALLOWED'
|
||||
})
|
||||
];
|
||||
|
||||
setupTestBed({
|
||||
imports: [
|
||||
TranslateModule.forRoot(),
|
||||
ContentTestingModule
|
||||
]
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
service = TestBed.inject(PermissionListService);
|
||||
nodePermissionService = TestBed.inject(NodePermissionService);
|
||||
notificationService = TestBed.inject(NotificationService);
|
||||
nodesApiService = TestBed.inject(NodesApiService);
|
||||
spyOn(notificationService, 'showInfo').and.stub();
|
||||
spyOn(notificationService, 'showWarning').and.stub();
|
||||
spyOn(notificationService, 'showError').and.stub();
|
||||
});
|
||||
|
||||
it('fetch Permission', (done) => {
|
||||
spyOn(nodePermissionService, 'getNodeWithRoles').and.returnValue(of({node: fakeNodeWithOnlyLocally , roles: []}));
|
||||
|
||||
const subscription = service.data$.subscribe(({ node, inheritedPermissions, localPermissions, roles }) => {
|
||||
expect(node).toBe(fakeNodeWithOnlyLocally);
|
||||
expect(inheritedPermissions).toEqual([]);
|
||||
expect(roles).toEqual([]);
|
||||
expect(localPermissions).toEqual(localPermission);
|
||||
subscription.unsubscribe();
|
||||
done();
|
||||
});
|
||||
|
||||
service.fetchPermission('fake node');
|
||||
});
|
||||
|
||||
describe('toggle permission', () => {
|
||||
|
||||
it('should show error if user doesn\'t have permission to update node', () => {
|
||||
const node = JSON.parse(JSON.stringify(fakeNodeInheritedOnly)), event = { source: { checked: false } };
|
||||
node.allowableOperations = [];
|
||||
spyOn(nodePermissionService, 'getNodeWithRoles').and.returnValue(of({node , roles: []}));
|
||||
spyOn(nodesApiService, 'updateNode').and.stub();
|
||||
service.fetchPermission('fetch node');
|
||||
service.toggleInherited(event as any);
|
||||
expect(nodesApiService.updateNode).not.toHaveBeenCalled();
|
||||
expect(notificationService.showError).toHaveBeenCalledWith('PERMISSION_MANAGER.ERROR.NOT-ALLOWED');
|
||||
});
|
||||
|
||||
it('should show message after success toggle', () => {
|
||||
const node = JSON.parse(JSON.stringify(fakeNodeInheritedOnly)), event = { source: { checked: false } };
|
||||
const updateNode = JSON.parse(JSON.stringify(fakeNodeInheritedOnly));
|
||||
updateNode.permissions.isInheritanceEnabled = false;
|
||||
spyOn(nodePermissionService, 'getNodeWithRoles').and.returnValue(of({node , roles: []}));
|
||||
spyOn(nodesApiService, 'updateNode').and.returnValue(of(updateNode));
|
||||
service.fetchPermission('fetch node');
|
||||
|
||||
service.toggleInherited(event as any);
|
||||
expect(nodesApiService.updateNode).toHaveBeenCalled();
|
||||
expect(notificationService.showInfo).toHaveBeenCalledWith('PERMISSION_MANAGER.MESSAGE.INHERIT-DISABLE-SUCCESS');
|
||||
});
|
||||
|
||||
it('should show message for errored toggle', () => {
|
||||
const node = JSON.parse(JSON.stringify(fakeNodeInheritedOnly)), event = { source: { checked: false } };
|
||||
spyOn(nodesApiService, 'updateNode').and.returnValue(throwError('Failed to update'));
|
||||
spyOn(nodePermissionService, 'getNodeWithRoles').and.returnValue(of({node , roles: []}));
|
||||
service.fetchPermission('fetch node');
|
||||
|
||||
service.toggleInherited(event as any);
|
||||
expect(nodesApiService.updateNode).toHaveBeenCalled();
|
||||
expect(notificationService.showWarning).toHaveBeenCalledWith('PERMISSION_MANAGER.MESSAGE.TOGGLE-PERMISSION-FAILED');
|
||||
});
|
||||
});
|
||||
|
||||
describe('delete permission', () => {
|
||||
const node = JSON.parse(JSON.stringify(fakeNodeWithOnlyLocally));
|
||||
beforeEach(() => {
|
||||
spyOn(nodePermissionService, 'getNodeWithRoles').and.returnValue(of({node , roles: []}));
|
||||
service.fetchPermission('fetch node');
|
||||
});
|
||||
|
||||
it('should be able to delete a permission', () => {
|
||||
spyOn(nodePermissionService, 'removePermissions').and.returnValue(of(node));
|
||||
service.deletePermissions(localPermission);
|
||||
expect(notificationService.showInfo).toHaveBeenCalledWith('PERMISSION_MANAGER.MESSAGE.PERMISSION-BULK-DELETE-SUCCESS', null, { user: 0, group: 1 });
|
||||
});
|
||||
|
||||
it('should show error message for errored delete operation', () => {
|
||||
spyOn(nodePermissionService, 'removePermissions').and.returnValue(throwError('Failed operation'));
|
||||
service.deletePermissions(localPermission);
|
||||
expect(notificationService.showError).toHaveBeenCalledWith('PERMISSION_MANAGER.MESSAGE.PERMISSION-DELETE-FAIL');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Bulk Role', () => {
|
||||
const node = JSON.parse(JSON.stringify(fakeNodeWithOnlyLocally));
|
||||
beforeEach(() => {
|
||||
spyOn(nodePermissionService, 'getNodeWithRoles').and.returnValue(of({node , roles: []}));
|
||||
service.fetchPermission('fetch node');
|
||||
});
|
||||
|
||||
it('should be able to update bulk permission', () => {
|
||||
spyOn(nodePermissionService, 'updatePermissions').and.returnValue(of(node));
|
||||
service.bulkRoleUpdate('fake-role');
|
||||
expect(notificationService.showInfo).toHaveBeenCalledWith('PERMISSION_MANAGER.MESSAGE.PERMISSION-BULK-UPDATE-SUCCESS', null, { user: 0, group: 1 });
|
||||
});
|
||||
|
||||
it('should show error message for errored operation', () => {
|
||||
spyOn(nodePermissionService, 'updatePermissions').and.returnValue(throwError('Error'));
|
||||
service.bulkRoleUpdate('fake-role');
|
||||
expect(notificationService.showError).toHaveBeenCalledWith('PERMISSION_MANAGER.MESSAGE.PERMISSION-UPDATE-FAIL');
|
||||
});
|
||||
});
|
||||
});
|
@ -0,0 +1,207 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2019 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 { AllowableOperationsEnum, ContentService, NodesApiService, NotificationService } from '@alfresco/adf-core';
|
||||
import { Node, PermissionElement } from '@alfresco/js-api';
|
||||
import { EventEmitter, Injectable } from '@angular/core';
|
||||
import { MatSlideToggleChange } from '@angular/material/slide-toggle';
|
||||
import { BehaviorSubject, forkJoin, Observable, of, Subject } from 'rxjs';
|
||||
import { finalize, map, switchMap } from 'rxjs/operators';
|
||||
import { RoleModel } from '../../models/role.model';
|
||||
import { PermissionDisplayModel } from '../../models/permission.model';
|
||||
import { NodePermissionsModel } from '../../models/member.model';
|
||||
import { NodePermissionService } from '../../services/node-permission.service';
|
||||
import { NodePermissionDialogService } from '../../services/node-permission-dialog.service';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class PermissionListService {
|
||||
updated = new EventEmitter<PermissionDisplayModel>();
|
||||
errored = new EventEmitter<PermissionDisplayModel>();
|
||||
|
||||
loading$: BehaviorSubject<boolean> = new BehaviorSubject(true);
|
||||
error$: Subject<boolean> = new Subject();
|
||||
nodeWithRoles$: Subject<{ node: Node, roles: RoleModel[] }> = new Subject();
|
||||
data$: Observable<NodePermissionsModel> = this.nodeWithRoles$.pipe(
|
||||
map(({ node, roles}) => {
|
||||
return {
|
||||
node,
|
||||
roles,
|
||||
inheritedPermissions: this.nodePermissionService.getInheritedPermission(node),
|
||||
localPermissions: this.nodePermissionService.getLocalPermissions(node),
|
||||
allPermission: this.nodePermissionService.getNodePermissions(node)
|
||||
};
|
||||
})
|
||||
);
|
||||
|
||||
private node: Node;
|
||||
private roles: RoleModel[];
|
||||
|
||||
constructor(
|
||||
private nodeService: NodesApiService,
|
||||
private nodePermissionService: NodePermissionService,
|
||||
private nodePermissionDialogService: NodePermissionDialogService,
|
||||
private contentService: ContentService,
|
||||
private notificationService: NotificationService
|
||||
) {}
|
||||
|
||||
fetchPermission(nodeId: string) {
|
||||
this.loading$.next(true);
|
||||
this.nodePermissionService.getNodeWithRoles(nodeId)
|
||||
.pipe(finalize(() => this.loading$.next(false)))
|
||||
.subscribe(
|
||||
({ node, roles }) => {
|
||||
this.node = node;
|
||||
this.roles = roles;
|
||||
this.nodeWithRoles$.next({ node, roles });
|
||||
},
|
||||
() => this.error$.next(true)
|
||||
);
|
||||
}
|
||||
|
||||
toggleInherited(change: MatSlideToggleChange) {
|
||||
if (this.contentService.hasAllowableOperations(this.node, AllowableOperationsEnum.UPDATEPERMISSIONS)) {
|
||||
const nodeBody = {
|
||||
permissions: {
|
||||
isInheritanceEnabled: !this.node.permissions.isInheritanceEnabled
|
||||
}
|
||||
};
|
||||
this.nodeService.updateNode(this.node.id, nodeBody, {include: ['permissions']})
|
||||
.subscribe(
|
||||
(nodeUpdated: Node) => {
|
||||
const message = nodeUpdated.permissions.isInheritanceEnabled ? 'PERMISSION_MANAGER.MESSAGE.INHERIT-ENABLE-SUCCESS' : 'PERMISSION_MANAGER.MESSAGE.INHERIT-DISABLE-SUCCESS';
|
||||
this.notificationService.showInfo(message);
|
||||
nodeUpdated.permissions.inherited = nodeUpdated.permissions?.inherited ?? [];
|
||||
this.reloadNode(nodeUpdated);
|
||||
},
|
||||
() => {
|
||||
change.source.checked = this.node.permissions.isInheritanceEnabled;
|
||||
this.notificationService.showWarning('PERMISSION_MANAGER.MESSAGE.TOGGLE-PERMISSION-FAILED');
|
||||
}
|
||||
);
|
||||
} else {
|
||||
change.source.checked = this.node.permissions.isInheritanceEnabled;
|
||||
this.notificationService.showError('PERMISSION_MANAGER.ERROR.NOT-ALLOWED');
|
||||
}
|
||||
}
|
||||
|
||||
updateNodePermissionByDialog() {
|
||||
this.nodePermissionDialogService
|
||||
.openAddPermissionDialog(this.node, this.roles)
|
||||
.pipe(
|
||||
switchMap(selection => {
|
||||
const total = selection.length;
|
||||
const group = selection.filter(({authorityId}) => this.isGroup(authorityId)).length;
|
||||
return forkJoin({
|
||||
user: of(total - group),
|
||||
group: of(group),
|
||||
node: this.nodePermissionService.updateNodePermissions(this.node.id, selection)
|
||||
});
|
||||
})
|
||||
)
|
||||
.subscribe(({ user, group, node}) => {
|
||||
this.notificationService.showInfo( 'PERMISSION_MANAGER.MESSAGE.PERMISSION-ADD-SUCCESS', null, { user, group });
|
||||
this.reloadNode(node);
|
||||
},
|
||||
() => {
|
||||
this.notificationService.showError( 'PERMISSION_MANAGER.MESSAGE.PERMISSION-ADD-FAIL');
|
||||
this.reloadNode();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
deletePermissions(permissions: PermissionElement[]) {
|
||||
this.nodePermissionService.removePermissions(this.node, permissions)
|
||||
.subscribe((node) => {
|
||||
const total = permissions.length;
|
||||
const group = permissions.filter(({authorityId}) => this.isGroup(authorityId)).length;
|
||||
this.notificationService.showInfo('PERMISSION_MANAGER.MESSAGE.PERMISSION-BULK-DELETE-SUCCESS', null, {user: total - group, group});
|
||||
this.reloadNode(node);
|
||||
},
|
||||
() => {
|
||||
this.notificationService.showError('PERMISSION_MANAGER.MESSAGE.PERMISSION-DELETE-FAIL');
|
||||
this.reloadNode();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
updateRole(role: string, permission: PermissionDisplayModel) {
|
||||
const updatedPermissionRole = this.buildUpdatedPermission(role, permission);
|
||||
this.nodePermissionService.updatePermissionRole(this.node, updatedPermissionRole)
|
||||
.subscribe((node) => {
|
||||
this.notificationService.showInfo('PERMISSION_MANAGER.MESSAGE.PERMISSION-UPDATE-SUCCESS');
|
||||
this.reloadNode(node);
|
||||
this.updated.emit(permission);
|
||||
},
|
||||
() => {
|
||||
this.notificationService.showError('PERMISSION_MANAGER.MESSAGE.PERMISSION-UPDATE-FAIL');
|
||||
this.reloadNode();
|
||||
this.errored.emit(permission);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
bulkRoleUpdate(role: string) {
|
||||
const permissions = [...this.node.permissions.locallySet] .map((permission) => this.buildUpdatedPermission(role, permission));
|
||||
this.nodePermissionService.updatePermissions(this.node, permissions)
|
||||
.subscribe((node) => {
|
||||
const total = permissions.length;
|
||||
const group = permissions.filter(({authorityId}) => this.isGroup(authorityId)).length;
|
||||
this.notificationService.showInfo('PERMISSION_MANAGER.MESSAGE.PERMISSION-BULK-UPDATE-SUCCESS', null, {user: total - group, group});
|
||||
this.reloadNode(node);
|
||||
},
|
||||
() => {
|
||||
this.notificationService.showError('PERMISSION_MANAGER.MESSAGE.PERMISSION-UPDATE-FAIL');
|
||||
this.reloadNode();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
deletePermission(permission: PermissionDisplayModel) {
|
||||
this.nodePermissionService
|
||||
.removePermission(this.node, permission)
|
||||
.subscribe((node) => {
|
||||
this.notificationService.showInfo('PERMISSION_MANAGER.MESSAGE.PERMISSION-DELETE-SUCCESS');
|
||||
this.reloadNode(node);
|
||||
},
|
||||
() => {
|
||||
this.notificationService.showError('PERMISSION_MANAGER.MESSAGE.PERMISSION-DELETE-FAIL');
|
||||
this.reloadNode();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private buildUpdatedPermission(role: string, permission: PermissionElement): PermissionElement {
|
||||
return {
|
||||
accessStatus: permission.accessStatus,
|
||||
name: role,
|
||||
authorityId: permission.authorityId
|
||||
};
|
||||
}
|
||||
|
||||
private reloadNode(node?: Node) {
|
||||
if (node != null) {
|
||||
Object.assign(this.node.permissions, node.permissions);
|
||||
}
|
||||
this.nodeWithRoles$.next({ node: this.node, roles: this.roles });
|
||||
}
|
||||
|
||||
private isGroup(authorityId) {
|
||||
return authorityId.startsWith('GROUP_') || authorityId.startsWith('ROLE_');
|
||||
}
|
||||
}
|
@ -0,0 +1,104 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2019 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 { AfterViewInit, Directive, ElementRef, Input, OnDestroy, OnInit, TemplateRef, ViewContainerRef } from '@angular/core';
|
||||
import { ConnectionPositionPair, Overlay, OverlayRef } from '@angular/cdk/overlay';
|
||||
import { TemplatePortal } from '@angular/cdk/portal';
|
||||
import { takeUntil } from 'rxjs/operators';
|
||||
import { Subject } from 'rxjs';
|
||||
|
||||
@Directive({
|
||||
selector: '[adf-pop-over]',
|
||||
exportAs: 'adfPopOver'
|
||||
|
||||
})
|
||||
export class PopOverDirective implements OnInit, OnDestroy, AfterViewInit {
|
||||
get open(): boolean {
|
||||
return this._open;
|
||||
}
|
||||
|
||||
@Input('adf-pop-over') popOver!: TemplateRef<object>;
|
||||
@Input() target!: HTMLElement;
|
||||
@Input() panelClass = 'adf-permission-pop-over';
|
||||
|
||||
private _open = false;
|
||||
private destroy$ = new Subject();
|
||||
private overlayRef!: OverlayRef;
|
||||
|
||||
constructor(
|
||||
private element: ElementRef,
|
||||
private overlay: Overlay,
|
||||
private vcr: ViewContainerRef
|
||||
) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
this.createOverlay();
|
||||
}
|
||||
|
||||
ngAfterViewInit(): void {
|
||||
this.element.nativeElement.addEventListener('click', () => this.attachOverlay());
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.detachOverlay();
|
||||
this.destroy$.next();
|
||||
this.destroy$.complete();
|
||||
}
|
||||
|
||||
private createOverlay(): void {
|
||||
const scrollStrategy = this.overlay.scrollStrategies.reposition();
|
||||
const positionStrategy = this.overlay
|
||||
.position()
|
||||
.flexibleConnectedTo(this.target)
|
||||
.withPositions([
|
||||
new ConnectionPositionPair({ originX: 'start', originY: 'bottom' }, { overlayX: 'start', overlayY: 'top' }),
|
||||
new ConnectionPositionPair({ originX: 'start', originY: 'top' }, { overlayX: 'start', overlayY: 'bottom' })
|
||||
])
|
||||
.withPush(false);
|
||||
|
||||
this.overlayRef = this.overlay.create({
|
||||
positionStrategy,
|
||||
scrollStrategy,
|
||||
hasBackdrop: true,
|
||||
backdropClass: 'cdk-overlay-transparent-backdrop',
|
||||
panelClass: this.panelClass
|
||||
});
|
||||
|
||||
this.overlayRef
|
||||
.backdropClick()
|
||||
.pipe(takeUntil(this.destroy$))
|
||||
.subscribe(() => {
|
||||
this._open = false;
|
||||
this.detachOverlay();
|
||||
});
|
||||
}
|
||||
|
||||
private attachOverlay(): void {
|
||||
if (!this.overlayRef.hasAttached()) {
|
||||
const periodSelectorPortal = new TemplatePortal(this.popOver, this.vcr);
|
||||
|
||||
this.overlayRef.attach(periodSelectorPortal);
|
||||
this._open = true;
|
||||
}
|
||||
}
|
||||
|
||||
private detachOverlay(): void {
|
||||
if (this.overlayRef.hasAttached()) {
|
||||
this.overlayRef.detach();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
@mixin adf-user-icon-column-theme($theme) {
|
||||
$primary: map-get($theme, primary);
|
||||
$foreground: map-get($theme, foreground);
|
||||
|
||||
.adf {
|
||||
&-people-initial {
|
||||
background: mat-color($primary);
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
color: mat-color($foreground, text);
|
||||
font-weight: bolder;
|
||||
font-size: 18px;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
&-people-icon {
|
||||
height: 20px !important;
|
||||
width: 20px !important;
|
||||
background: mat-color($primary);
|
||||
border-radius: 50%;
|
||||
padding: 10px;
|
||||
color: mat-color($foreground, text);
|
||||
font-weight: bolder;
|
||||
font-size: 20px;
|
||||
}
|
||||
&-people-select-icon {
|
||||
padding: 10px;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,118 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2019 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 { setupTestBed } from '@alfresco/adf-core';
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { ContentTestingModule } from '../../../testing/content.testing.module';
|
||||
import { UserIconColumnComponent } from './user-icon-column.component';
|
||||
import { NodeEntry } from '@alfresco/js-api';
|
||||
|
||||
describe('UserIconColumnComponent', () => {
|
||||
|
||||
let fixture: ComponentFixture<UserIconColumnComponent>;
|
||||
let component: UserIconColumnComponent;
|
||||
let element: HTMLElement;
|
||||
const person = {
|
||||
firstName: 'fake',
|
||||
lastName: 'user',
|
||||
email: 'fake@test.com'
|
||||
};
|
||||
|
||||
const group = {
|
||||
id: 'fake-id',
|
||||
displayName: 'fake authority'
|
||||
};
|
||||
|
||||
setupTestBed({
|
||||
imports: [
|
||||
TranslateModule.forRoot(),
|
||||
ContentTestingModule
|
||||
]
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(UserIconColumnComponent);
|
||||
component = fixture.componentInstance;
|
||||
element = fixture.nativeElement;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
describe('person initial', () => {
|
||||
it('should render person value from context', () => {
|
||||
component.context = {
|
||||
row: {
|
||||
obj: {
|
||||
person
|
||||
}
|
||||
}
|
||||
};
|
||||
component.ngOnInit();
|
||||
fixture.detectChanges();
|
||||
expect(element.querySelector('[id="user-initials-image"]').textContent).toContain('fu');
|
||||
});
|
||||
|
||||
it('should render person value from node', () => {
|
||||
component.node = {
|
||||
entry: {
|
||||
nodeType: 'cm:person',
|
||||
properties: {
|
||||
'cm:firstName': 'Fake',
|
||||
'cm:lastName': 'User',
|
||||
'cm:email': 'fake-user@test.com',
|
||||
'cm:userName': 'fake-user'
|
||||
}
|
||||
}
|
||||
} as NodeEntry;
|
||||
component.ngOnInit();
|
||||
fixture.detectChanges();
|
||||
expect(element.querySelector('[id="user-initials-image"]').textContent).toContain('FU');
|
||||
});
|
||||
});
|
||||
|
||||
describe('group initial', () => {
|
||||
it('should render group value from context', () => {
|
||||
component.context = {
|
||||
row: {
|
||||
obj: {
|
||||
group
|
||||
}
|
||||
}
|
||||
};
|
||||
component.ngOnInit();
|
||||
fixture.detectChanges();
|
||||
expect(element.querySelector('[id="group-icon"] mat-icon')).toBeDefined();
|
||||
expect(element.querySelector('[id="group-icon"] mat-icon').textContent).toContain('people_alt_outline');
|
||||
});
|
||||
|
||||
it('should render person value from node', () => {
|
||||
component.node = {
|
||||
entry: {
|
||||
nodeType: 'cm:authorityContainer',
|
||||
properties: {
|
||||
'cm:authorityName': 'Fake authorityN'
|
||||
}
|
||||
}
|
||||
} as NodeEntry;
|
||||
component.ngOnInit();
|
||||
fixture.detectChanges();
|
||||
expect(element.querySelector('[id="group-icon"] mat-icon')).toBeDefined();
|
||||
expect(element.querySelector('[id="group-icon"] mat-icon').textContent).toContain('people_alt_outline');
|
||||
});
|
||||
});
|
||||
|
||||
});
|
@ -0,0 +1,69 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2019 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 { User } from '@alfresco/adf-core';
|
||||
import { NodeEntry } from '@alfresco/js-api';
|
||||
import { Component, Input, OnInit } from '@angular/core';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
import { NodePermissionService } from '../../services/node-permission.service';
|
||||
|
||||
@Component({
|
||||
selector: 'adf-user-icon-column',
|
||||
template: `
|
||||
<div class="adf-cell-value" [attr.id]="group ? 'group-icon' : 'person-icon'" *ngIf="!context?.row?.isSelected">
|
||||
<ng-container *ngIf="displayText$ | async as user">
|
||||
<mat-icon *ngIf="group" class="adf-people-icon">people_alt_outline</mat-icon>
|
||||
<div *ngIf="!group" [outerHTML]="user | usernameInitials: 'adf-people-initial'"></div>
|
||||
</ng-container>
|
||||
</div>
|
||||
<div class="adf-cell-value" *ngIf="context?.row?.isSelected">
|
||||
<mat-icon class="adf-people-select-icon" svgIcon="selected"></mat-icon>
|
||||
</div>
|
||||
`,
|
||||
styleUrls: ['./user-icon-column.component.scss'],
|
||||
host: { class: 'adf-user-icon-column adf-datatable-content-cell' }
|
||||
})
|
||||
export class UserIconColumnComponent implements OnInit {
|
||||
@Input()
|
||||
context: any;
|
||||
|
||||
@Input()
|
||||
node: NodeEntry;
|
||||
|
||||
displayText$ = new BehaviorSubject<User>(null);
|
||||
group = false;
|
||||
|
||||
constructor(private nodePermissionService: NodePermissionService) {}
|
||||
|
||||
ngOnInit() {
|
||||
if (this.context) {
|
||||
const { person, group, authorityId } = this.context.row.obj?.entry ?? this.context.row.obj;
|
||||
this.group = this.isGroup(group, authorityId);
|
||||
this.displayText$.next(person || group || { displayName: authorityId });
|
||||
}
|
||||
|
||||
if (this.node) {
|
||||
const { person, group } = this.nodePermissionService.transformNodeToUserPerson(this.node.entry);
|
||||
this.group = this.isGroup(group, null);
|
||||
this.displayText$.next(person || group);
|
||||
}
|
||||
}
|
||||
|
||||
private isGroup(group, authorityId): boolean {
|
||||
return !!group || authorityId?.startsWith('GROUP_') || authorityId?.startsWith('ROLE_');
|
||||
}
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
@mixin member-theme($theme) {
|
||||
$primary: map-get($theme, primary);
|
||||
$foreground: map-get($theme, foreground);
|
||||
|
||||
.adf-user {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
&-name-column {
|
||||
padding: 2px 10px;
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
&-email-column {
|
||||
padding: 2px 10px;
|
||||
font-size: 14px;
|
||||
letter-spacing: -0.2px;
|
||||
line-height: 1.43;
|
||||
color: mat-color($foreground, text, 0.72);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,136 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2019 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 { setupTestBed } from '@alfresco/adf-core';
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { ContentTestingModule } from '../../../testing/content.testing.module';
|
||||
import { UserNameColumnComponent } from './user-name-column.component';
|
||||
import { NodeEntry } from '@alfresco/js-api';
|
||||
|
||||
describe('UserNameColumnComponent', () => {
|
||||
|
||||
let fixture: ComponentFixture<UserNameColumnComponent>;
|
||||
let component: UserNameColumnComponent;
|
||||
let element: HTMLElement;
|
||||
const person = {
|
||||
firstName: 'fake',
|
||||
lastName: 'user',
|
||||
email: 'fake@test.com'
|
||||
};
|
||||
|
||||
const group = {
|
||||
id: 'fake-id',
|
||||
displayName: 'fake authority'
|
||||
};
|
||||
|
||||
setupTestBed({
|
||||
imports: [
|
||||
TranslateModule.forRoot(),
|
||||
ContentTestingModule
|
||||
]
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(UserNameColumnComponent);
|
||||
component = fixture.componentInstance;
|
||||
element = fixture.nativeElement;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
describe('person name', () => {
|
||||
it('should render person value from context', () => {
|
||||
component.context = {
|
||||
row: {
|
||||
obj: {
|
||||
person
|
||||
}
|
||||
}
|
||||
};
|
||||
component.ngOnInit();
|
||||
fixture.detectChanges();
|
||||
expect(element.querySelector('[title="fake user"]').textContent).toContain('fake user');
|
||||
expect(element.querySelector('[title="fake@test.com"]').textContent).toContain('fake@test.com');
|
||||
});
|
||||
|
||||
it('should render person value from node', (done) => {
|
||||
component.node = {
|
||||
entry: {
|
||||
nodeType: 'cm:person',
|
||||
properties: {
|
||||
'cm:firstName': 'Fake',
|
||||
'cm:lastName': 'User',
|
||||
'cm:email': 'fake-user@test.com',
|
||||
'cm:userName': 'fake-user'
|
||||
}
|
||||
}
|
||||
} as NodeEntry;
|
||||
|
||||
const subscription = component.displayText$.subscribe((fullName) => {
|
||||
if (fullName) {
|
||||
expect(fullName).toBe('Fake User');
|
||||
subscription.unsubscribe();
|
||||
done();
|
||||
}
|
||||
});
|
||||
|
||||
component.ngOnInit();
|
||||
});
|
||||
});
|
||||
|
||||
describe('group name', () => {
|
||||
it('should render group value from context', () => {
|
||||
component.context = {
|
||||
row: {
|
||||
obj: {
|
||||
group
|
||||
}
|
||||
}
|
||||
};
|
||||
component.ngOnInit();
|
||||
fixture.detectChanges();
|
||||
expect(element.querySelector('[title="fake authority"]').textContent.trim()).toBe('fake authority');
|
||||
});
|
||||
|
||||
it('should render group for authorityId', () => {
|
||||
component.context = {
|
||||
row: {
|
||||
obj: {
|
||||
authorityId: 'fake-id'
|
||||
}
|
||||
}
|
||||
};
|
||||
component.ngOnInit();
|
||||
fixture.detectChanges();
|
||||
expect(element.querySelector('[title=fake-id]').textContent.trim()).toBe('fake-id');
|
||||
});
|
||||
|
||||
it('should render person value from node', () => {
|
||||
component.node = {
|
||||
entry: {
|
||||
nodeType: 'cm:authorityContainer',
|
||||
properties: {
|
||||
'cm:authorityName': 'Fake authority'
|
||||
}
|
||||
}
|
||||
} as NodeEntry;
|
||||
component.ngOnInit();
|
||||
fixture.detectChanges();
|
||||
expect(element.querySelector('[title="Fake authority"]').textContent.trim()).toBe('Fake authority');
|
||||
});
|
||||
});
|
||||
});
|
@ -0,0 +1,74 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2019 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, Input, OnInit } from '@angular/core';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
import { Group, NodeEntry, Person } from '@alfresco/js-api';
|
||||
import { NodePermissionService } from '../../services/node-permission.service';
|
||||
|
||||
@Component({
|
||||
selector: 'adf-user-name-column',
|
||||
template: `
|
||||
<div class="adf-ellipsis-cell adf-user" [attr.data-automation-id]="displayText$ | async">
|
||||
<span title="{{ displayText$ | async }}"> {{ displayText$ | async }}</span>
|
||||
<br/>
|
||||
<span title="{{ subTitleText$ | async }}">{{ subTitleText$ | async }}</span>
|
||||
</div>
|
||||
`,
|
||||
host: { class: 'adf-user-name-column adf-datatable-content-cell adf-expand-cell-5 adf-ellipsis-cell' },
|
||||
styleUrls: [ './user-name-column.component.scss' ]
|
||||
})
|
||||
export class UserNameColumnComponent implements OnInit {
|
||||
@Input()
|
||||
context: any;
|
||||
|
||||
@Input()
|
||||
node: NodeEntry;
|
||||
|
||||
displayText$ = new BehaviorSubject<string>('');
|
||||
subTitleText$ = new BehaviorSubject<string>('');
|
||||
|
||||
constructor(private nodePermissionService: NodePermissionService) {}
|
||||
|
||||
ngOnInit() {
|
||||
if (this.context != null) {
|
||||
const { person, group, authorityId } = this.context.row.obj?.entry ?? this.context.row.obj;
|
||||
const permissionGroup = authorityId ? { displayName: authorityId } as Group : null;
|
||||
this.updatePerson(person);
|
||||
this.updateGroup(group || permissionGroup);
|
||||
}
|
||||
|
||||
if (this.node) {
|
||||
const { person, group } = this.nodePermissionService.transformNodeToUserPerson(this.node.entry);
|
||||
this.updatePerson(person);
|
||||
this.updateGroup(group);
|
||||
}
|
||||
}
|
||||
|
||||
private updatePerson(person: Person) {
|
||||
if (person) {
|
||||
this.displayText$.next(`${person.firstName ?? ''} ${person.lastName ?? ''}`);
|
||||
this.subTitleText$.next(person.email ?? '');
|
||||
}
|
||||
}
|
||||
|
||||
private updateGroup(group: Group) {
|
||||
if (group) {
|
||||
this.displayText$.next(group.displayName);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,69 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2019 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, EventEmitter, Input, Output } from '@angular/core';
|
||||
import { RoleModel } from '../../models/role.model';
|
||||
|
||||
@Component({
|
||||
selector: 'adf-user-role-column',
|
||||
template: `
|
||||
<mat-form-field floatLabel="never" class="adf-role-selector-field" *ngIf="!readonly">
|
||||
<mat-select
|
||||
(click)="$event.stopPropagation()"
|
||||
[placeholder]="placeholder | translate"
|
||||
[value]="value"
|
||||
(selectionChange)="onRoleChanged($event.value)">
|
||||
<mat-option *ngFor="let role of roles" [value]="role.role">
|
||||
{{ role.label | adfLocalizedRole }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
|
||||
<span class="adf-datatable-cell-value" [title]="value | adfLocalizedRole" *ngIf="readonly">
|
||||
{{value | adfLocalizedRole}}
|
||||
</span>
|
||||
`,
|
||||
host: { class: 'adf-user-role-column adf-datatable-content-cell adf-expand-cell-4' },
|
||||
styles: [
|
||||
`.adf-role-selector-field {
|
||||
width: 100%;
|
||||
}
|
||||
`
|
||||
]
|
||||
})
|
||||
export class UserRoleColumnComponent {
|
||||
|
||||
@Input()
|
||||
roles: RoleModel[];
|
||||
|
||||
@Input()
|
||||
value: string;
|
||||
|
||||
@Input()
|
||||
readonly = false;
|
||||
|
||||
@Input()
|
||||
placeholder: string = 'PERMISSION_MANAGER.LABELS.SELECT-ROLE';
|
||||
|
||||
@Output()
|
||||
roleChanged: EventEmitter<string> = new EventEmitter<string>();
|
||||
|
||||
onRoleChanged(newRole: string) {
|
||||
this.value = newRole;
|
||||
this.roleChanged.emit(newRole);
|
||||
}
|
||||
}
|
@ -0,0 +1,85 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2019 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 { Group, Node, NodeEntry, PermissionElement, Person } from '@alfresco/js-api';
|
||||
import { PermissionDisplayModel } from './permission.model';
|
||||
import { RoleModel } from './role.model';
|
||||
|
||||
export interface NodePermissionsModel {
|
||||
node: Node;
|
||||
roles: RoleModel[];
|
||||
inheritedPermissions: PermissionDisplayModel[];
|
||||
localPermissions: PermissionDisplayModel[];
|
||||
}
|
||||
|
||||
export class MemberModel {
|
||||
id: string;
|
||||
role: string;
|
||||
accessStatus: PermissionElement.AccessStatusEnum | string;
|
||||
entry: {
|
||||
person?: Person;
|
||||
group?: Group;
|
||||
};
|
||||
readonly: boolean = false;
|
||||
|
||||
constructor(input?) {
|
||||
if (input) {
|
||||
Object.assign(this, input);
|
||||
}
|
||||
}
|
||||
|
||||
static parseFromSearchResult({ entry }: NodeEntry): MemberModel {
|
||||
const result = new MemberModel();
|
||||
|
||||
if (entry.nodeType === 'cm:person') {
|
||||
const person = new Person({
|
||||
firstName: entry.properties['cm:firstName'],
|
||||
lastName: entry.properties['cm:lastName'],
|
||||
email: entry.properties['cm:email'],
|
||||
id: entry.properties['cm:userName']
|
||||
});
|
||||
|
||||
result.id = person.id;
|
||||
result.entry = { person };
|
||||
result.accessStatus = 'ALLOWED';
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
if (entry.nodeType === 'cm:authorityContainer') {
|
||||
const group = new Group({
|
||||
id: entry.properties['cm:authorityName'],
|
||||
displayName: entry.properties['cm:authorityDisplayName'] || entry.properties['cm:authorityName']
|
||||
});
|
||||
|
||||
result.id = group.id;
|
||||
result.entry = { group };
|
||||
result.accessStatus = 'ALLOWED';
|
||||
|
||||
return result;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
toPermissionElement(): PermissionElement {
|
||||
return {
|
||||
authorityId: this.id,
|
||||
name: this.role,
|
||||
accessStatus: this.accessStatus
|
||||
};
|
||||
}
|
||||
}
|
@ -15,12 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/* tslint:disable:no-input-rename */
|
||||
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'adf-no-permission-template',
|
||||
template: '<ng-content></ng-content>'
|
||||
})
|
||||
export class NoPermissionTemplateComponent {}
|
||||
export interface RoleModel {
|
||||
label: string;
|
||||
role: string;
|
||||
}
|
@ -15,6 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { CoreModule, PipeModule } from '@alfresco/adf-core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||
@ -22,11 +23,15 @@ 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 { CoreModule, PipeModule } from '@alfresco/adf-core';
|
||||
import { InheritPermissionDirective } from './components/inherited-button.directive';
|
||||
import { NoPermissionTemplateComponent } from './components/permission-list/no-permission.component';
|
||||
import { AddPermissionPanelComponent } from './components/add-permission/add-permission-panel.component';
|
||||
import { SearchModule } from '../search/search.module';
|
||||
import { UserNameColumnComponent } from './components/user-name-column/user-name-column.component';
|
||||
import { UserIconColumnComponent } from './components/user-icon-column/user-icon-column.component';
|
||||
import { UserRoleColumnComponent } from './components/user-role-column/user-role-column.component';
|
||||
import { NodePathColumnComponent } from './components/node-path-column/node-path-column.component';
|
||||
import { PopOverDirective } from './components/pop-over.directive';
|
||||
import { PermissionContainerComponent } from './components/permission-container/permission-container.component';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
@ -40,19 +45,28 @@ import { SearchModule } from '../search/search.module';
|
||||
],
|
||||
declarations: [
|
||||
PermissionListComponent,
|
||||
NoPermissionTemplateComponent,
|
||||
AddPermissionPanelComponent,
|
||||
InheritPermissionDirective,
|
||||
AddPermissionComponent,
|
||||
AddPermissionDialogComponent
|
||||
AddPermissionDialogComponent,
|
||||
UserNameColumnComponent,
|
||||
UserIconColumnComponent,
|
||||
UserRoleColumnComponent,
|
||||
PopOverDirective,
|
||||
NodePathColumnComponent,
|
||||
PermissionContainerComponent
|
||||
],
|
||||
exports: [
|
||||
PermissionListComponent,
|
||||
NoPermissionTemplateComponent,
|
||||
AddPermissionPanelComponent,
|
||||
InheritPermissionDirective,
|
||||
AddPermissionComponent,
|
||||
AddPermissionDialogComponent
|
||||
AddPermissionDialogComponent,
|
||||
UserNameColumnComponent,
|
||||
UserIconColumnComponent,
|
||||
UserRoleColumnComponent,
|
||||
PopOverDirective,
|
||||
NodePathColumnComponent
|
||||
]
|
||||
})
|
||||
export class PermissionManagerModule {}
|
||||
|
@ -16,7 +16,6 @@
|
||||
*/
|
||||
|
||||
export * from './components/permission-list/permission-list.component';
|
||||
export * from './components/permission-list/no-permission.component';
|
||||
export * from './components/inherited-button.directive';
|
||||
export * from './models/permission.model';
|
||||
export * from './services/node-permission-dialog.service';
|
||||
@ -26,5 +25,13 @@ 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 './components/add-permission/search-config-permission.service';
|
||||
export * from './components/user-icon-column/user-icon-column.component';
|
||||
export * from './components/user-name-column/user-name-column.component';
|
||||
export * from './components/user-role-column/user-role-column.component';
|
||||
export * from './components/node-path-column/node-path-column.component';
|
||||
export * from './components/permission-container/permission-container.component';
|
||||
export * from './components/pop-over.directive';
|
||||
export * from './models/member.model';
|
||||
export * from './models/role.model';
|
||||
|
||||
export * from './permission-manager.module';
|
||||
|
@ -16,10 +16,10 @@
|
||||
*/
|
||||
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
import { AppConfigService, setupTestBed, ContentService } from '@alfresco/adf-core';
|
||||
import { AppConfigService, setupTestBed } from '@alfresco/adf-core';
|
||||
import { NodePermissionDialogService } from './node-permission-dialog.service';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { Subject, of, throwError } from 'rxjs';
|
||||
import { of, Subject, throwError } from 'rxjs';
|
||||
import { ContentTestingModule } from '../../testing/content.testing.module';
|
||||
import { NodePermissionService } from './node-permission.service';
|
||||
import { Node } from '@alfresco/js-api';
|
||||
@ -32,7 +32,6 @@ describe('NodePermissionDialogService', () => {
|
||||
let spyOnDialogOpen: jasmine.Spy;
|
||||
let afterOpenObservable: Subject<any>;
|
||||
let nodePermissionService: NodePermissionService;
|
||||
let contentService: ContentService;
|
||||
|
||||
setupTestBed({
|
||||
imports: [
|
||||
@ -48,7 +47,6 @@ describe('NodePermissionDialogService', () => {
|
||||
materialDialog = TestBed.inject(MatDialog);
|
||||
afterOpenObservable = new Subject<any>();
|
||||
nodePermissionService = TestBed.inject(NodePermissionService);
|
||||
contentService = TestBed.inject(ContentService);
|
||||
spyOnDialogOpen = spyOn(materialDialog, 'open').and.returnValue({
|
||||
afterOpen: () => afterOpenObservable,
|
||||
afterClosed: () => of({}),
|
||||
@ -67,14 +65,14 @@ describe('NodePermissionDialogService', () => {
|
||||
});
|
||||
|
||||
it('should be able to open the dialog showing node permissions', () => {
|
||||
service.openAddPermissionDialog(fakePermissionNode, 'fake-title');
|
||||
service.openAddPermissionDialog(fakePermissionNode, [], 'fake-title');
|
||||
expect(spyOnDialogOpen).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should return the updated node', (done) => {
|
||||
spyOn(nodePermissionService, 'updateNodePermissions').and.returnValue(of({id : 'fake-node-updated'}));
|
||||
spyOn(service, 'openAddPermissionDialog').and.returnValue(of({}));
|
||||
spyOn(contentService, 'getNode').and.returnValue(of(fakePermissionNode));
|
||||
spyOn(nodePermissionService, 'getNodeWithRoles').and.returnValue(of({ node: fakePermissionNode, roles: [] }));
|
||||
service.updateNodePermissionByDialog('fake-node-id', 'fake-title').subscribe((node) => {
|
||||
expect(node.id).toBe('fake-node-updated');
|
||||
done();
|
||||
@ -84,7 +82,7 @@ describe('NodePermissionDialogService', () => {
|
||||
it('should throw an error if the update of the node fails', (done) => {
|
||||
spyOn(nodePermissionService, 'updateNodePermissions').and.returnValue(throwError({error : 'error'}));
|
||||
spyOn(service, 'openAddPermissionDialog').and.returnValue(of({}));
|
||||
spyOn(contentService, 'getNode').and.returnValue(of(fakePermissionNode));
|
||||
spyOn(nodePermissionService, 'getNodeWithRoles').and.returnValue(of({ node: fakePermissionNode, roles: [] }));
|
||||
service.updateNodePermissionByDialog('fake-node-id', 'fake-title').subscribe(() => {
|
||||
throwError('This call should fail');
|
||||
}, (error) => {
|
||||
@ -103,12 +101,12 @@ describe('NodePermissionDialogService', () => {
|
||||
});
|
||||
|
||||
it('should not be able to open the dialog showing node permissions', () => {
|
||||
service.openAddPermissionDialog(fakeForbiddenNode, 'fake-title');
|
||||
service.openAddPermissionDialog(fakeForbiddenNode, [], 'fake-title');
|
||||
expect(spyOnDialogOpen).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should return the updated node', (done) => {
|
||||
spyOn(contentService, 'getNode').and.returnValue(of(fakeForbiddenNode));
|
||||
spyOn(nodePermissionService, 'getNodeWithRoles').and.returnValue(of({ node: fakeForbiddenNode, roles: [] }));
|
||||
service.updateNodePermissionByDialog('fake-node-id', 'fake-title').subscribe(() => {
|
||||
throwError('This call should fail');
|
||||
},
|
||||
|
@ -15,15 +15,16 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { AllowableOperationsEnum, ContentService } from '@alfresco/adf-core';
|
||||
import { Node, PermissionElement } from '@alfresco/js-api';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Subject, Observable, throwError } from 'rxjs';
|
||||
import { Observable, Subject, throwError } from 'rxjs';
|
||||
import { switchMap } from 'rxjs/operators';
|
||||
import { NodePermissionService } from './node-permission.service';
|
||||
import { AddPermissionDialogComponent } from '../components/add-permission/add-permission-dialog.component';
|
||||
import { AddPermissionDialogData } from '../components/add-permission/add-permission-dialog-data.interface';
|
||||
import { NodeEntry, Node } from '@alfresco/js-api';
|
||||
import { NodePermissionService } from './node-permission.service';
|
||||
import { ContentService, AllowableOperationsEnum } from '@alfresco/adf-core';
|
||||
import { switchMap } from 'rxjs/operators';
|
||||
import { RoleModel } from '../models/role.model';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
@ -37,25 +38,27 @@ export class NodePermissionDialogService {
|
||||
|
||||
/**
|
||||
* Opens a dialog to add permissions to a node.
|
||||
* @param node ID of the target node
|
||||
* @param node target node
|
||||
* @param roles settable roles for the node
|
||||
* @param title Dialog title
|
||||
* @returns Node with updated permissions
|
||||
*/
|
||||
openAddPermissionDialog(node: Node, title?: string): Observable<NodeEntry[]> {
|
||||
openAddPermissionDialog(node: Node, roles: RoleModel[], title?: string): Observable<PermissionElement[]> {
|
||||
if (this.contentService.hasAllowableOperations(node, AllowableOperationsEnum.UPDATEPERMISSIONS)) {
|
||||
const confirm = new Subject<NodeEntry[]>();
|
||||
const confirm = new Subject<PermissionElement[]>();
|
||||
|
||||
confirm.subscribe({
|
||||
complete: this.close.bind(this)
|
||||
});
|
||||
|
||||
const data: AddPermissionDialogData = {
|
||||
nodeId: node.id,
|
||||
node: node,
|
||||
title: title,
|
||||
confirm: confirm
|
||||
confirm: confirm,
|
||||
roles
|
||||
};
|
||||
|
||||
this.openDialog(data, 'adf-add-permission-dialog', '630px');
|
||||
this.openDialog(data, 'adf-add-permission-dialog', '800px');
|
||||
return confirm;
|
||||
} else {
|
||||
const errors = new Error(JSON.stringify({ error: { statusCode: 403 } }));
|
||||
@ -65,7 +68,7 @@ export class NodePermissionDialogService {
|
||||
}
|
||||
|
||||
private openDialog(data: any, currentPanelClass: string, chosenWidth: string) {
|
||||
this.dialog.open(AddPermissionDialogComponent, { data, panelClass: currentPanelClass, width: chosenWidth });
|
||||
this.dialog.open(AddPermissionDialogComponent, { data, panelClass: currentPanelClass, width: chosenWidth, restoreFocus: true });
|
||||
}
|
||||
|
||||
/**
|
||||
@ -82,10 +85,10 @@ export class NodePermissionDialogService {
|
||||
* @returns Node with updated permissions
|
||||
*/
|
||||
updateNodePermissionByDialog(nodeId?: string, title?: string): Observable<Node> {
|
||||
return this.contentService.getNode(nodeId, { include: ['allowableOperations'] })
|
||||
return this.nodePermissionService.getNodeWithRoles(nodeId)
|
||||
.pipe(
|
||||
switchMap((node) => {
|
||||
return this.openAddPermissionDialog(node.entry, title)
|
||||
switchMap(({node, roles}) => {
|
||||
return this.openAddPermissionDialog(node, roles, title)
|
||||
.pipe(
|
||||
switchMap((selection) => {
|
||||
return this.nodePermissionService.updateNodePermissions(nodeId, selection);
|
||||
|
@ -31,6 +31,23 @@ describe('NodePermissionService', () => {
|
||||
let service: NodePermissionService;
|
||||
let nodeService: NodesApiService;
|
||||
let searchApiService: SearchService;
|
||||
const fakePermissionElements: PermissionElement[] = [
|
||||
{
|
||||
authorityId: fakeAuthorityResults[0].entry.properties['cm:userName'],
|
||||
name: 'Consumer',
|
||||
accessStatus: 'ALLOWED'
|
||||
},
|
||||
{
|
||||
authorityId: fakeAuthorityResults[1].entry.properties['cm:userName'],
|
||||
name: 'Consumer',
|
||||
accessStatus: 'ALLOWED'
|
||||
},
|
||||
{
|
||||
authorityId: fakeAuthorityResults[2].entry.properties['cm:authorityName'],
|
||||
name: 'Consumer',
|
||||
accessStatus: 'ALLOWED'
|
||||
}
|
||||
];
|
||||
|
||||
setupTestBed({
|
||||
imports: [
|
||||
@ -87,7 +104,7 @@ describe('NodePermissionService', () => {
|
||||
|
||||
spyOn(nodeService, 'updateNode').and.callFake((nodeId, permissionBody) => returnUpdatedNode(nodeId, permissionBody));
|
||||
|
||||
service.updatePermissionRole(fakeNodeWithOnlyLocally, fakePermission).subscribe((node: Node) => {
|
||||
service.updatePermissionRole(JSON.parse(JSON.stringify(fakeNodeWithOnlyLocally)), fakePermission).subscribe((node: Node) => {
|
||||
expect(node).not.toBeNull();
|
||||
expect(node.id).toBe('fake-updated-node');
|
||||
expect(node.permissions.locallySet.length).toBe(1);
|
||||
@ -119,10 +136,8 @@ describe('NodePermissionService', () => {
|
||||
const fakeNodeCopy = JSON.parse(JSON.stringify(fakeNodeWithOnlyLocally));
|
||||
spyOn(nodeService, 'getNode').and.returnValue(of(fakeNodeCopy));
|
||||
spyOn(nodeService, 'updateNode').and.callFake((nodeId, permissionBody) => returnUpdatedNode(nodeId, permissionBody));
|
||||
spyOn(searchApiService, 'searchByQueryBody').and.returnValue(of(fakeSiteNodeResponse));
|
||||
spyOn(service, 'getGroupMemberByGroupName').and.returnValue(of(fakeSiteRoles));
|
||||
|
||||
service.updateNodePermissions('fake-node-id', fakeAuthorityResults).subscribe((node: Node) => {
|
||||
service.updateNodePermissions('fake-node-id', fakePermissionElements).subscribe((node: Node) => {
|
||||
expect(node).not.toBeNull();
|
||||
expect(node.id).toBe('fake-updated-node');
|
||||
expect(node.permissions.locallySet.length).toBe(4);
|
||||
@ -136,7 +151,7 @@ describe('NodePermissionService', () => {
|
||||
const fakeNodeCopy = JSON.parse(JSON.stringify(fakeNodeWithOnlyLocally));
|
||||
spyOn(nodeService, 'updateNode').and.callFake((nodeId, permissionBody) => returnUpdatedNode(nodeId, permissionBody));
|
||||
|
||||
service.updateLocallySetPermissions(fakeNodeCopy, fakeAuthorityResults, fakeSiteRoles).subscribe((node: Node) => {
|
||||
service.updateLocallySetPermissions(fakeNodeCopy, fakePermissionElements).subscribe((node: Node) => {
|
||||
expect(node).not.toBeNull();
|
||||
expect(node.id).toBe('fake-updated-node');
|
||||
expect(node.permissions.locallySet.length).toBe(4);
|
||||
@ -150,8 +165,7 @@ describe('NodePermissionService', () => {
|
||||
const fakeNodeCopy = JSON.parse(JSON.stringify(fakeNodeWithoutPermissions));
|
||||
fakeNodeCopy.permissions.locallySet = undefined;
|
||||
spyOn(nodeService, 'updateNode').and.callFake((nodeId, permissionBody) => returnUpdatedNode(nodeId, permissionBody));
|
||||
|
||||
service.updateLocallySetPermissions(fakeNodeCopy, fakeAuthorityResults, fakeSiteRoles).subscribe((node: Node) => {
|
||||
service.updateLocallySetPermissions(fakeNodeCopy, fakePermissionElements).subscribe((node: Node) => {
|
||||
expect(node).not.toBeNull();
|
||||
expect(node.id).toBe('fake-updated-node');
|
||||
expect(node.permissions.locallySet.length).toBe(3);
|
||||
@ -164,31 +178,58 @@ describe('NodePermissionService', () => {
|
||||
it('should fail when user select the same authority and role to add', async(() => {
|
||||
const fakeNodeCopy = JSON.parse(JSON.stringify(fakeNodeWithOnlyLocally));
|
||||
|
||||
const fakeDuplicateAuthority: any = [{
|
||||
'entry': {
|
||||
'isFolder': false,
|
||||
'search': {
|
||||
'score': 0.3541112
|
||||
},
|
||||
'isFile': false,
|
||||
'name': 'GROUP_EVERYONE',
|
||||
'location': 'nodes',
|
||||
'id': 'GROUP_EVERYONE',
|
||||
'nodeType': 'cm:authorityContainer',
|
||||
'properties': {
|
||||
'cm:authorityName': 'GROUP_EVERYONE'
|
||||
},
|
||||
'parentId': '030d833e-da8e-4f5c-8ef9-d809638bd04b'
|
||||
}
|
||||
const fakeDuplicateAuthority: PermissionElement [] = [{
|
||||
authorityId: 'GROUP_EVERYONE',
|
||||
accessStatus: 'ALLOWED',
|
||||
name: 'Contributor'
|
||||
}];
|
||||
|
||||
service.updateLocallySetPermissions(fakeNodeCopy, fakeDuplicateAuthority, ['Contributor'])
|
||||
service.updateLocallySetPermissions(fakeNodeCopy, fakeDuplicateAuthority)
|
||||
.subscribe(() => {
|
||||
|
||||
fail('should throw exception');
|
||||
}, (errorMessage) => {
|
||||
expect(errorMessage).not.toBeNull();
|
||||
expect(errorMessage).toBeDefined();
|
||||
expect(errorMessage).toBe('PERMISSION_MANAGER.ERROR.DUPLICATE-PERMISSION');
|
||||
});
|
||||
}));
|
||||
|
||||
it('should be able to remove the locallyset permission', async(() => {
|
||||
const fakeNodeCopy = JSON.parse(JSON.stringify(fakeNodeWithoutPermissions));
|
||||
fakeNodeCopy.permissions.locallySet = [...fakePermissionElements];
|
||||
spyOn(nodeService, 'updateNode').and.callFake((nodeId, permissionBody) => returnUpdatedNode(nodeId, permissionBody));
|
||||
service.removePermissions(fakeNodeCopy, [fakePermissionElements[2]]).subscribe((node: Node) => {
|
||||
expect(node).not.toBeNull();
|
||||
expect(node.id).toBe('fake-updated-node');
|
||||
expect(node.permissions.locallySet.length).toBe(2);
|
||||
expect(node.permissions.locallySet[0].authorityId).toBe(fakePermissionElements[0].authorityId);
|
||||
expect(node.permissions.locallySet[1].authorityId).toBe(fakePermissionElements[1].authorityId);
|
||||
});
|
||||
}));
|
||||
|
||||
it('should be able to replace the locally set', async(() => {
|
||||
const fakeNodeCopy = JSON.parse(JSON.stringify(fakeNodeWithOnlyLocally));
|
||||
fakeNodeCopy.permissions.locallySet = [];
|
||||
spyOn(nodeService, 'updateNode').and.callFake((nodeId, permissionBody) => returnUpdatedNode(nodeId, permissionBody));
|
||||
service.updatePermissions(fakeNodeCopy, fakePermissionElements).subscribe((node: Node) => {
|
||||
expect(node).not.toBeNull();
|
||||
expect(node.id).toBe('fake-updated-node');
|
||||
expect(node.permissions.locallySet.length).toBe(3);
|
||||
expect(node.permissions.locallySet[0].authorityId).toBe(fakePermissionElements[0].authorityId);
|
||||
expect(node.permissions.locallySet[1].authorityId).toBe(fakePermissionElements[1].authorityId);
|
||||
expect(node.permissions.locallySet[2].authorityId).toBe(fakePermissionElements[2].authorityId);
|
||||
});
|
||||
}));
|
||||
|
||||
it('should be able to get node and it\'s roles', async(() => {
|
||||
const fakeNodeCopy = JSON.parse(JSON.stringify(fakeNodeWithOnlyLocally));
|
||||
spyOn(nodeService, 'getNode').and.returnValue(of(fakeNodeCopy));
|
||||
spyOn(searchApiService, 'searchByQueryBody').and.returnValue(of(fakeSiteNodeResponse));
|
||||
spyOn(service, 'getGroupMemberByGroupName').and.returnValue(of(fakeSiteRoles));
|
||||
service.getNodeWithRoles('node-id').subscribe(({ node, roles }) => {
|
||||
expect(node).toBe(fakeNodeCopy);
|
||||
expect(roles.length).toBe(4);
|
||||
expect(roles[0].role).toBe('SiteCollaborator');
|
||||
});
|
||||
}));
|
||||
});
|
||||
|
@ -15,12 +15,13 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { AlfrescoApiService, NodesApiService, SearchService, TranslationService } from '@alfresco/adf-core';
|
||||
import { Group, GroupMemberEntry, GroupMemberPaging, Node, PathElement, PermissionElement, Person, QueryBody } from '@alfresco/js-api';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Observable, of, from, throwError } from 'rxjs';
|
||||
import { AlfrescoApiService, SearchService, NodesApiService, TranslationService } from '@alfresco/adf-core';
|
||||
import { QueryBody, Node, NodeEntry, PathElement, GroupMemberEntry, GroupMemberPaging, PermissionElement } from '@alfresco/js-api';
|
||||
import { switchMap, map } from 'rxjs/operators';
|
||||
import { forkJoin, from, Observable, of, throwError } from 'rxjs';
|
||||
import { map, switchMap } from 'rxjs/operators';
|
||||
import { PermissionDisplayModel } from '../models/permission.model';
|
||||
import { RoleModel } from '../models/role.model';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
@ -96,27 +97,21 @@ export class NodePermissionService {
|
||||
* @param permissionList New permission settings
|
||||
* @returns Node with updated permissions
|
||||
*/
|
||||
updateNodePermissions(nodeId: string, permissionList: NodeEntry[]): Observable<Node> {
|
||||
updateNodePermissions(nodeId: string, permissionList: PermissionElement[]): Observable<Node> {
|
||||
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))
|
||||
switchMap((node) => this.updateLocallySetPermissions(node, permissionList))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the locally set permissions for a node.
|
||||
* @param node ID of the target node
|
||||
* @param nodes Permission settings
|
||||
* @param nodeRole Permission role
|
||||
* @param permissions Permission settings
|
||||
* @returns Node with updated permissions
|
||||
*/
|
||||
updateLocallySetPermissions(node: Node, nodes: NodeEntry[], nodeRole: string[]): Observable<Node> {
|
||||
updateLocallySetPermissions(node: Node, permissions: PermissionElement[]): Observable<Node> {
|
||||
const permissionBody = { permissions: { locallySet: []} };
|
||||
const permissionList = this.transformNodeToPermissionElement(nodes, nodeRole[0]);
|
||||
const permissionList = permissions;
|
||||
const duplicatedPermissions = this.getDuplicatedPermissions(node.permissions.locallySet, permissionList);
|
||||
if (duplicatedPermissions.length > 0) {
|
||||
const list = duplicatedPermissions.map((permission) => 'authority -> ' + permission.authorityId + ' / role -> ' + permission.name).join(', ');
|
||||
@ -146,18 +141,6 @@ export class NodePermissionService {
|
||||
oldPermission.name === newPermission.name;
|
||||
}
|
||||
|
||||
private transformNodeToPermissionElement(nodes: NodeEntry[], nodeRole: any): PermissionElement[] {
|
||||
return nodes.map((node) => {
|
||||
return {
|
||||
'authorityId': node.entry.properties['cm:authorityName'] ?
|
||||
node.entry.properties['cm:authorityName'] :
|
||||
node.entry.properties['cm:userName'],
|
||||
'name': nodeRole,
|
||||
'accessStatus': 'ALLOWED'
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a permission setting from a node.
|
||||
* @param node ID of the target node
|
||||
@ -227,4 +210,97 @@ export class NodePermissionService {
|
||||
};
|
||||
}
|
||||
|
||||
getLocalPermissions(node: Node): PermissionDisplayModel[] {
|
||||
const result: PermissionDisplayModel[] = [];
|
||||
|
||||
if (node?.permissions?.locallySet) {
|
||||
node.permissions.locallySet.forEach((permissionElement) => {
|
||||
result.push(new PermissionDisplayModel(permissionElement));
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
getInheritedPermission(node: Node): PermissionDisplayModel[] {
|
||||
const result: PermissionDisplayModel[] = [];
|
||||
|
||||
if (node?.permissions?.inherited) {
|
||||
node.permissions.inherited.forEach((permissionElement) => {
|
||||
const permissionInherited = new PermissionDisplayModel(permissionElement);
|
||||
permissionInherited.isInherited = true;
|
||||
result.push(permissionInherited);
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes permissions setting from a node.
|
||||
* @param node target node with permission
|
||||
* @param permissions Permissions to remove
|
||||
* @returns Node with modified permissions
|
||||
*/
|
||||
removePermissions(node: Node, permissions: PermissionElement[]): Observable<Node> {
|
||||
const permissionBody = { permissions: { locallySet: [] } };
|
||||
|
||||
permissions.forEach((permission) => {
|
||||
const index = node.permissions.locallySet.findIndex((locallySet) => locallySet.authorityId === permission.authorityId);
|
||||
if (index !== -1) {
|
||||
node.permissions.locallySet.splice(index, 1);
|
||||
}
|
||||
});
|
||||
permissionBody.permissions.locallySet = node.permissions.locallySet;
|
||||
return this.nodeService.updateNode(node.id, permissionBody);
|
||||
}
|
||||
|
||||
/**
|
||||
* updates permissions setting from a node.
|
||||
* @param node target node with permission
|
||||
* @param permissions Permissions to update
|
||||
* @returns Node with modified permissions
|
||||
*/
|
||||
updatePermissions(node: Node, permissions: PermissionElement[]): Observable<Node> {
|
||||
const permissionBody = { permissions: { locallySet: [] } };
|
||||
permissionBody.permissions.locallySet = permissions;
|
||||
return this.nodeService.updateNode(node.id, permissionBody);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all node detail for nodeId along with settable permissions.
|
||||
* @param nodeId Id of the node
|
||||
* @returns node and it's associated roles { node: Node; roles: RoleModel[] }
|
||||
*/
|
||||
getNodeWithRoles(nodeId: string): Observable<{ node: Node; roles: RoleModel[] }> {
|
||||
return this.nodeService.getNode(nodeId).pipe(
|
||||
switchMap(node => {
|
||||
return forkJoin({
|
||||
node: of(node),
|
||||
roles: this.getNodeRoles(node)
|
||||
.pipe(
|
||||
map(_roles => _roles.map(role => ({ role, label: role }))
|
||||
)
|
||||
)
|
||||
});
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
transformNodeToUserPerson(node: Node): { person: Person, group: Group } {
|
||||
let person = null, group = null;
|
||||
if (node.nodeType === 'cm:person') {
|
||||
const firstName = node.properties['cm:firstName'];
|
||||
const lastName = node.properties['cm:lastName'];
|
||||
const email = node.properties['cm:email'];
|
||||
const id = node.properties['cm:userName'];
|
||||
person = new Person({ id, firstName, lastName, email});
|
||||
}
|
||||
|
||||
if (node.nodeType === 'cm:authorityContainer') {
|
||||
const displayName = node.properties['cm:authorityDisplayName'] || node.properties['cm:authorityName'];
|
||||
const id = node.properties['cm:authorityName'];
|
||||
group = new Group({ displayName, id });
|
||||
}
|
||||
return { person, group };
|
||||
}
|
||||
}
|
||||
|
@ -28,7 +28,9 @@
|
||||
@import '../version-manager/version-comparison.component';
|
||||
@import '../content-type/content-type-dialog.component';
|
||||
@import '../aspect-list/aspect-list.component';
|
||||
@import '../aspect-list//aspect-list-dialog.component';
|
||||
@import '../aspect-list/aspect-list-dialog.component';
|
||||
@import '../permission-manager/components/permission-container/permission-container.component';
|
||||
@import '../permission-manager/components/user-icon-column/user-icon-column.component';
|
||||
|
||||
@mixin adf-content-services-theme($theme) {
|
||||
@include adf-breadcrumb-theme($theme);
|
||||
@ -48,6 +50,8 @@
|
||||
@include adf-content-node-selector-dialog-theme($theme);
|
||||
@include adf-content-metadata-module-theme($theme);
|
||||
@include adf-permission-list-theme($theme);
|
||||
@include adf-permission-container-theme($theme);
|
||||
@include adf-user-icon-column-theme($theme);
|
||||
@include adf-add-permission-theme($theme);
|
||||
@include adf-add-permission-dialog-theme($theme);
|
||||
@include adf-add-permission-panel-theme($theme);
|
||||
|
39
lib/core/data-column/data-column-header.component.ts
Normal file
39
lib/core/data-column/data-column-header.component.ts
Normal file
@ -0,0 +1,39 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2019 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.
|
||||
*/
|
||||
|
||||
/* tslint:disable:component-selector no-input-rename */
|
||||
|
||||
import { Component, ContentChild, TemplateRef } from '@angular/core';
|
||||
import { DataColumnComponent } from './data-column.component';
|
||||
|
||||
@Component({
|
||||
selector: 'adf-data-column-header',
|
||||
template: ''
|
||||
})
|
||||
export class DateColumnHeaderComponent {
|
||||
|
||||
@ContentChild(TemplateRef)
|
||||
public template: TemplateRef<any>;
|
||||
|
||||
constructor(private columnComponent: DataColumnComponent) {}
|
||||
|
||||
ngAfterContentInit() {
|
||||
if (this.columnComponent) {
|
||||
this.columnComponent.header = this.template;
|
||||
}
|
||||
}
|
||||
}
|
@ -82,6 +82,9 @@ export class DataColumnComponent implements OnInit {
|
||||
@Input()
|
||||
sortingKey: string;
|
||||
|
||||
/** Data column header template */
|
||||
header?: TemplateRef<any>;
|
||||
|
||||
ngOnInit() {
|
||||
if (!this.srTitle && this.key === '$thumbnail') {
|
||||
this.srTitle = 'Thumbnail';
|
||||
|
@ -20,6 +20,7 @@ import { NgModule } from '@angular/core';
|
||||
|
||||
import { DataColumnListComponent } from './data-column-list.component';
|
||||
import { DataColumnComponent } from './data-column.component';
|
||||
import { DateColumnHeaderComponent } from './data-column-header.component';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
@ -27,11 +28,13 @@ import { DataColumnComponent } from './data-column.component';
|
||||
],
|
||||
declarations: [
|
||||
DataColumnComponent,
|
||||
DataColumnListComponent
|
||||
DataColumnListComponent,
|
||||
DateColumnHeaderComponent
|
||||
],
|
||||
exports: [
|
||||
DataColumnComponent,
|
||||
DataColumnListComponent
|
||||
DataColumnListComponent,
|
||||
DateColumnHeaderComponent
|
||||
]
|
||||
})
|
||||
export class DataColumnModule {}
|
||||
|
@ -17,5 +17,6 @@
|
||||
|
||||
export * from './data-column-list.component';
|
||||
export * from './data-column.component';
|
||||
export * from './data-column-header.component';
|
||||
|
||||
export * from './data-column.module';
|
||||
|
@ -33,9 +33,12 @@
|
||||
[attr.tabindex]="isHeaderVisible() ? 0 : null"
|
||||
[attr.aria-sort]="col.sortable ? (getAriaSort(col) | translate) : null"
|
||||
adf-drop-zone dropTarget="header" [dropColumn]="col">
|
||||
<span *ngIf="col.title" class="adf-datatable-cell-value">{{ col.title | translate}}</span>
|
||||
<span *ngIf="col.title && col.sortable" class="adf-sr-only" aria-live="polite">{{ getSortLiveAnnouncement(col) | translate: { string: col.title | translate } }}</span>
|
||||
<ng-template *ngIf="allowFiltering" [ngTemplateOutlet]="headerFilterTemplate" [ngTemplateOutletContext]="{$implicit: col}"></ng-template>
|
||||
<ng-container *ngIf="!col.header">
|
||||
<span *ngIf="col.title" class="adf-datatable-cell-value">{{ col.title | translate}}</span>
|
||||
<span *ngIf="col.title && col.sortable" class="adf-sr-only" aria-live="polite">{{ getSortLiveAnnouncement(col) | translate: { string: col.title | translate } }}</span>
|
||||
<ng-template *ngIf="allowFiltering" [ngTemplateOutlet]="headerFilterTemplate" [ngTemplateOutletContext]="{$implicit: col}"></ng-template>
|
||||
</ng-container>
|
||||
<ng-template *ngIf="col.header" [ngTemplateOutlet]="col.header" [ngTemplateOutletContext]="{$implicit: col}"></ng-template>
|
||||
</div>
|
||||
<!-- Actions (right) -->
|
||||
<div *ngIf="actions && actionsPosition === 'right'" class="adf-actions-column adf-datatable-cell-header adf-datatable__actions-cell">
|
||||
|
@ -43,4 +43,5 @@ export interface DataColumn {
|
||||
editable?: boolean;
|
||||
focus?: boolean;
|
||||
sortingKey?: string;
|
||||
header?: TemplateRef<any>;
|
||||
}
|
||||
|
@ -32,6 +32,7 @@ export class ObjectDataColumn implements DataColumn {
|
||||
copyContent?: boolean;
|
||||
focus?: boolean;
|
||||
sortingKey?: string;
|
||||
header?: TemplateRef<any>;
|
||||
|
||||
constructor(input: any) {
|
||||
this.key = input.key;
|
||||
@ -45,5 +46,6 @@ export class ObjectDataColumn implements DataColumn {
|
||||
this.copyContent = input.copyContent;
|
||||
this.focus = input.focus;
|
||||
this.sortingKey = input.sortingKey;
|
||||
this.header = input.template;
|
||||
}
|
||||
}
|
||||
|
@ -28,15 +28,12 @@ const column = {
|
||||
export class AddPermissionsDialogPage {
|
||||
|
||||
dataTableComponentPage: DataTableComponentPage = new DataTableComponentPage();
|
||||
userRoleDataTableComponentPage: DataTableComponentPage = new DataTableComponentPage(element(by.css('[data-automation-id="adf-user-role-selection-table"]')));
|
||||
|
||||
addPermissionDialog = element(by.css('adf-add-permission-dialog'));
|
||||
searchUserInput = element(by.id('searchInput'));
|
||||
searchResults = element(by.css('#adf-add-permission-authority-results #adf-search-results-content'));
|
||||
addButton = element(by.id('add-permission-dialog-confirm-button'));
|
||||
permissionInheritedButton = element.all(by.css('div[class="app-inherit_permission_button"] button')).first();
|
||||
noPermissions = element(by.id('adf-no-permissions-template'));
|
||||
deletePermissionButton = element(by.css('button[data-automation-id="adf-delete-permission-button"]'));
|
||||
permissionDisplayContainer = element(by.id('adf-permission-display-container'));
|
||||
addButton = element(by.css('[data-automation-id="add-permission-dialog-confirm-button"]'));
|
||||
closeButton = element(by.id('add-permission-dialog-close-button'));
|
||||
|
||||
async clickCloseButton(): Promise<void> {
|
||||
@ -70,52 +67,17 @@ export class AddPermissionsDialogPage {
|
||||
await BrowserActions.click(this.addButton);
|
||||
}
|
||||
|
||||
async checkUserIsAdded(name: string): Promise<void> {
|
||||
const userOrGroupName = element(by.css('div[data-automation-id="text_' + name + '"]'));
|
||||
await BrowserVisibility.waitUntilElementIsVisible(userOrGroupName);
|
||||
}
|
||||
|
||||
async checkGroupIsAdded(name: string): Promise<void> {
|
||||
const userOrGroupName = element(by.css('div[data-automation-id="text_GROUP_' + name + '"]'));
|
||||
await BrowserVisibility.waitUntilElementIsVisible(userOrGroupName);
|
||||
}
|
||||
|
||||
async checkUserIsDeleted(name: string): Promise<void> {
|
||||
const userOrGroupName = element(by.css('div[data-automation-id="text_' + name + '"]'));
|
||||
await BrowserVisibility.waitUntilElementIsNotVisible(userOrGroupName);
|
||||
}
|
||||
|
||||
async checkPermissionInheritedButtonIsDisplayed() {
|
||||
await BrowserVisibility.waitUntilElementIsVisible(this.permissionInheritedButton);
|
||||
}
|
||||
|
||||
async clickPermissionInheritedButton(): Promise<void> {
|
||||
await BrowserActions.click(this.permissionInheritedButton);
|
||||
}
|
||||
|
||||
async clickDeletePermissionButton(): Promise<void> {
|
||||
await BrowserActions.click(this.deletePermissionButton);
|
||||
}
|
||||
|
||||
async checkNoPermissionsIsDisplayed(): Promise<void> {
|
||||
await BrowserVisibility.waitUntilElementIsVisible(this.noPermissions);
|
||||
}
|
||||
|
||||
async getPermissionInheritedButtonText(text: string): Promise<void> {
|
||||
await BrowserVisibility.waitUntilElementHasText(this.permissionInheritedButton, text);
|
||||
}
|
||||
|
||||
async checkPermissionsDatatableIsDisplayed(): Promise<void> {
|
||||
await BrowserVisibility.waitUntilElementIsVisible(element(by.css('[class*="adf-datatable-permission"]')));
|
||||
}
|
||||
|
||||
async getRoleCellValue(rowName: string): Promise<string> {
|
||||
const locator = this.dataTableComponentPage.getCellByRowContentAndColumn('Authority ID', rowName, column.role);
|
||||
const locator = this.dataTableComponentPage.getCellByRowContentAndColumn('Users and Groups', rowName, column.role);
|
||||
return BrowserActions.getText(locator);
|
||||
}
|
||||
|
||||
async clickRoleDropdownByUserOrGroupName(name: string): Promise<void> {
|
||||
const row = this.dataTableComponentPage.getRow('Authority ID', name);
|
||||
const row = this.dataTableComponentPage.getRow('Users and Groups', name);
|
||||
await BrowserActions.click(row.element(by.id('adf-select-role-permission')));
|
||||
}
|
||||
|
||||
@ -127,12 +89,23 @@ export class AddPermissionsDialogPage {
|
||||
await new DropdownPage().selectOption(name);
|
||||
}
|
||||
|
||||
async checkPermissionContainerIsDisplayed(): Promise<void> {
|
||||
await BrowserVisibility.waitUntilElementIsVisible(this.permissionDisplayContainer);
|
||||
}
|
||||
|
||||
async checkUserOrGroupIsDisplayed(name: string): Promise<void> {
|
||||
const userOrGroupName = element(by.cssContainingText('mat-list-option .mat-list-text', name));
|
||||
await BrowserVisibility.waitUntilElementIsVisible(userOrGroupName);
|
||||
}
|
||||
|
||||
async addButtonIsEnabled(): Promise<boolean> {
|
||||
return this.addButton.isEnabled();
|
||||
}
|
||||
|
||||
async clickAddButton(): Promise<void> {
|
||||
await BrowserActions.click(this.addButton);
|
||||
}
|
||||
|
||||
async selectRole(name: string, role) {
|
||||
const row = this.userRoleDataTableComponentPage.getRow('Users and Groups', name);
|
||||
await BrowserActions.click(row.element(by.css('[id="adf-select-role-permission"] .mat-select-trigger')));
|
||||
await this.getRoleDropdownOptions();
|
||||
await this.selectOption(role);
|
||||
}
|
||||
}
|
||||
|
@ -49,6 +49,10 @@ export class UserModel {
|
||||
this.id = details.id ? details.id : this.id;
|
||||
}
|
||||
|
||||
get fullName() {
|
||||
return `${this.firstName ?? ''} ${this.lastName ?? ''}`;
|
||||
}
|
||||
|
||||
getAPSModel() {
|
||||
return new UserRepresentation({
|
||||
firstName: this.firstName,
|
||||
|
@ -345,7 +345,7 @@ export class DataTableComponentPage {
|
||||
}
|
||||
|
||||
getRow(columnName: string, columnValue: string): ElementFinder {
|
||||
return this.rootElement.all(by.xpath(`//div[@title='${columnName}']//div[contains(@data-automation-id, '${columnValue}')]//ancestor::adf-datatable-row[contains(@class, 'adf-datatable-row')]`)).first();
|
||||
return this.rootElement.all(by.xpath(`//div[starts-with(@title, '${columnName}')]//div[contains(@data-automation-id, '${columnValue}')]//ancestor::adf-datatable-row[contains(@class, 'adf-datatable-row')]`)).first();
|
||||
}
|
||||
|
||||
getRowByIndex(index: number): ElementFinder {
|
||||
|
Loading…
x
Reference in New Issue
Block a user