[MNT-24923] legal hold hold capabilities are not verified properly (#4616)

* [MNT-24923] Added possibility to configure readonly for metadata sidebar by json config

* [MNT-24923] Unit tests

* [MNT-24923] Fixed sonar issues
This commit is contained in:
AleksanderSklorz
2025-06-12 14:30:11 +02:00
committed by GitHub
parent 690acfb64b
commit 3a51c19bc6
3 changed files with 201 additions and 12 deletions

View File

@@ -883,6 +883,20 @@
"description": "Sidebar extensions",
"type": "object",
"properties": {
"rules": {
"description": "Element rules",
"type": "object",
"properties": {
"enabled": {
"description": "Rule to evaluate the enabled state",
"type": "array",
"items": {
"type": "string"
},
"minItems": 0
}
}
},
"toolbar": {
"description": "Toolbar entries",
"type": "array",

View File

@@ -32,7 +32,7 @@ import { By } from '@angular/platform-browser';
import { AppExtensionService, NodePermissionService } from '@alfresco/aca-shared';
import { Actions } from '@ngrx/effects';
import { of, Subject } from 'rxjs';
import { ContentActionType } from '@alfresco/adf-extensions';
import { ContentActionType, ExtensionService } from '@alfresco/adf-extensions';
import { CategoryService, ContentMetadataComponent, ContentMetadataService, TagService } from '@alfresco/adf-content-services';
import { MatDialogModule } from '@angular/material/dialog';
@@ -70,6 +70,7 @@ describe('MetadataTabComponent', () => {
return permissions.some((permission) => source.allowableOperations.includes(permission));
});
spyOn(contentMetadataService, 'getGroupedProperties').and.returnValue(of());
extensions = TestBed.inject(AppExtensionService);
});
afterEach(() => {
@@ -79,7 +80,6 @@ describe('MetadataTabComponent', () => {
describe('content-metadata configuration', () => {
beforeEach(() => {
appConfig = TestBed.inject(AppConfigService);
extensions = TestBed.inject(AppExtensionService);
appConfig.config['content-metadata'] = { presets };
});
@@ -102,9 +102,12 @@ describe('MetadataTabComponent', () => {
});
describe('readOnly', () => {
let extensionService: ExtensionService;
beforeEach(() => {
fixture = TestBed.createComponent(MetadataTabComponent);
component = fixture.componentInstance;
extensionService = TestBed.inject(ExtensionService);
});
it('should return false if node is not locked and has update permission', async () => {
@@ -146,6 +149,170 @@ describe('MetadataTabComponent', () => {
expect(component.readOnly).toBe(true);
});
it('should set readOnly to false if node is defined, is not locked and enabled rule for sidebar returns true, has update permission for node', () => {
component.node = {
id: 'some id',
isLocked: false,
allowableOperations: ['update']
} as Node;
const rule = 'someRule';
spyOn(extensionService, 'getFeature').and.returnValue({
rules: {
enabled: [rule]
}
});
spyOn(extensionService, 'evaluateRule').and.returnValue(true);
component.ngOnInit();
expect(component.readOnly).toBeFalse();
expect(extensionService.getFeature).toHaveBeenCalledWith('sidebar');
expect(extensionService.evaluateRule).toHaveBeenCalledWith(rule, extensions);
});
it('should set readOnly to false if node is defined, is not locked and there is nothing for sidebar in configuration, has update permission for node', () => {
component.node = {
id: 'some id',
isLocked: false,
allowableOperations: ['update']
} as Node;
spyOn(extensionService, 'getFeature').and.returnValue(undefined);
spyOn(extensionService, 'evaluateRule').and.returnValue(true);
component.ngOnInit();
expect(component.readOnly).toBeFalse();
expect(extensionService.getFeature).toHaveBeenCalledWith('sidebar');
expect(extensionService.evaluateRule).not.toHaveBeenCalled();
});
it('should set readOnly to true if node is undefined and enabled rule for sidebar returns true, has update permission for node', () => {
component.node = undefined;
spyOn(extensionService, 'getFeature').and.returnValue({
rules: {
enabled: ['someRule']
}
});
spyOn(extensionService, 'evaluateRule').and.returnValue(true);
component.ngOnInit();
expect(component.readOnly).toBeTrue();
expect(extensionService.getFeature).not.toHaveBeenCalled();
expect(extensionService.evaluateRule).not.toHaveBeenCalled();
});
it('should set readOnly to true if node is undefined and there is nothing for sidebar in configuration, has update permission for node', () => {
component.node = undefined;
spyOn(extensionService, 'getFeature').and.returnValue(undefined);
spyOn(extensionService, 'evaluateRule').and.returnValue(true);
component.ngOnInit();
expect(component.readOnly).toBeTrue();
expect(extensionService.getFeature).not.toHaveBeenCalled();
expect(extensionService.evaluateRule).not.toHaveBeenCalled();
});
it('should set readOnly to true if node is defined, is locked and enabled rule for sidebar returns true, has update permission for node', () => {
component.node = {
id: 'some id',
isLocked: true,
allowableOperations: ['update']
} as Node;
spyOn(extensionService, 'getFeature').and.returnValue({
rules: {
enabled: ['someRule']
}
});
spyOn(extensionService, 'evaluateRule').and.returnValue(true);
component.ngOnInit();
expect(component.readOnly).toBeTrue();
expect(extensionService.getFeature).not.toHaveBeenCalled();
expect(extensionService.evaluateRule).not.toHaveBeenCalled();
});
it('should set readOnly to true if node is defined, is locked and there is nothing for sidebar in configuration, has update permission for node', () => {
component.node = {
id: 'some id',
isLocked: true,
allowableOperations: ['update']
} as Node;
spyOn(extensionService, 'getFeature').and.returnValue(undefined);
spyOn(extensionService, 'evaluateRule').and.returnValue(true);
component.ngOnInit();
expect(component.readOnly).toBeTrue();
expect(extensionService.getFeature).not.toHaveBeenCalled();
expect(extensionService.evaluateRule).not.toHaveBeenCalled();
});
it('should set readOnly to true if node is defined, is not locked and enabled rule for sidebar returns true, has not update permission for node', () => {
component.node = {
id: 'some id',
isLocked: false,
allowableOperations: []
} as Node;
const rule = 'someRule';
spyOn(extensionService, 'getFeature').and.returnValue({
rules: {
enabled: [rule]
}
});
spyOn(extensionService, 'evaluateRule').and.returnValue(true);
component.ngOnInit();
expect(component.readOnly).toBeTrue();
expect(extensionService.getFeature).toHaveBeenCalledWith('sidebar');
expect(extensionService.evaluateRule).toHaveBeenCalledWith(rule, extensions);
});
it('should set readOnly to true if node is defined, is not locked and there is nothing for sidebar in configuration, has not update permission for node', () => {
component.node = {
id: 'some id',
isLocked: false,
allowableOperations: ['']
} as Node;
spyOn(extensionService, 'getFeature').and.returnValue(undefined);
spyOn(extensionService, 'evaluateRule').and.returnValue(true);
component.ngOnInit();
expect(component.readOnly).toBeTrue();
expect(extensionService.getFeature).toHaveBeenCalledWith('sidebar');
expect(extensionService.evaluateRule).not.toHaveBeenCalled();
});
it('should set readOnly to true if node is defined, is locked and enabled rule for sidebar returns true, has not update permission for node', () => {
component.node = {
id: 'some id',
isLocked: true,
allowableOperations: []
} as Node;
spyOn(extensionService, 'getFeature').and.returnValue({
rules: {
enabled: ['someRule']
}
});
spyOn(extensionService, 'evaluateRule').and.returnValue(true);
component.ngOnInit();
expect(component.readOnly).toBeTrue();
expect(extensionService.getFeature).not.toHaveBeenCalled();
expect(extensionService.evaluateRule).not.toHaveBeenCalled();
});
it('should set readOnly to true if node is defined, is locked and there is nothing for sidebar in configuration, has not update permission for node', () => {
component.node = {
id: 'some id',
isLocked: true,
allowableOperations: []
} as Node;
spyOn(extensionService, 'getFeature').and.returnValue(undefined);
spyOn(extensionService, 'evaluateRule').and.returnValue(true);
component.ngOnInit();
expect(component.readOnly).toBeTrue();
expect(extensionService.getFeature).not.toHaveBeenCalled();
expect(extensionService.evaluateRule).not.toHaveBeenCalled();
});
describe('set by triggering EditOfflineAction', () => {
let editOfflineAction: EditOfflineAction;

View File

@@ -40,6 +40,7 @@ import { CommonModule } from '@angular/common';
import { Actions, ofType } from '@ngrx/effects';
import { Store } from '@ngrx/store';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { ExtensionService } from '@alfresco/adf-extensions';
@Component({
standalone: true,
@@ -81,15 +82,16 @@ export class MetadataTabComponent implements OnInit {
private readonly destroyRef = inject(DestroyRef);
constructor(
private permission: NodePermissionService,
protected extensions: AppExtensionService,
private appConfig: AppConfigService,
private notificationService: NotificationService,
private contentMetadataService: ContentMetadataService,
private actions$: Actions,
private tagService: TagService,
private categoryService: CategoryService,
private store: Store<AppStore>
private readonly permission: NodePermissionService,
protected readonly extensions: AppExtensionService,
private readonly appConfig: AppConfigService,
private readonly notificationService: NotificationService,
private readonly contentMetadataService: ContentMetadataService,
private readonly actions$: Actions,
private readonly tagService: TagService,
private readonly categoryService: CategoryService,
private readonly store: Store<AppStore>,
private readonly extensionService: ExtensionService
) {
if (this.extensions.contentMetadata) {
this.appConfig.config['content-metadata'].presets = this.extensions.contentMetadata.presets;
@@ -128,6 +130,12 @@ export class MetadataTabComponent implements OnInit {
}
private checkIfNodeIsUpdatable(node: Node) {
this.readOnly = !(node && !isLocked({ entry: node }) ? this.permission.check(node, ['update']) : false);
this.readOnly = !(node &&
!isLocked({ entry: node }) &&
(this.extensionService.getFeature('sidebar')?.['rules']?.enabled ?? []).every((rule: string) =>
this.extensionService.evaluateRule(rule, this.extensions)
)
? this.permission.check(node, ['update'])
: false);
}
}