From 9650924493751b97046a1ac79fd7056561977c2f Mon Sep 17 00:00:00 2001 From: Anton Ramanovich Date: Wed, 11 Mar 2026 15:46:57 +0100 Subject: [PATCH] [ACS-9765]: adds rma related rules for create/copy/move folders --- .../aca-content/assets/app.extensions.json | 6 +- .../aca-content/src/lib/aca-content.module.ts | 1 + .../aca-shared/rules/src/app.rules.spec.ts | 55 ++++++++++++++----- projects/aca-shared/rules/src/app.rules.ts | 13 ++++- 4 files changed, 58 insertions(+), 17 deletions(-) diff --git a/projects/aca-content/assets/app.extensions.json b/projects/aca-content/assets/app.extensions.json index f1c202ce3..c0684905b 100644 --- a/projects/aca-content/assets/app.extensions.json +++ b/projects/aca-content/assets/app.extensions.json @@ -987,7 +987,8 @@ "!app.navigation.isTrashcan", "!app.navigation.isLibraries", "app.selection.notEmpty", - "app.selection.canDelete" + "app.selection.canDelete", + "!app.selection.isRmaSystemContainer" ] } }, @@ -1003,7 +1004,8 @@ "visible": [ "app.selection.notEmpty", "!app.navigation.isTrashcan", - "!app.navigation.isLibraries" + "!app.navigation.isLibraries", + "!app.selection.isRmaSystemContainer" ] } }, diff --git a/projects/aca-content/src/lib/aca-content.module.ts b/projects/aca-content/src/lib/aca-content.module.ts index 3ff4569a6..1f5c53167 100644 --- a/projects/aca-content/src/lib/aca-content.module.ts +++ b/projects/aca-content/src/lib/aca-content.module.ts @@ -153,6 +153,7 @@ import { IsFeatureSupportedInCurrentAcsPipe } from './pipes/is-feature-supported 'app.selection.folder': rules.hasFolderSelected, 'app.selection.folder.canUpdate': rules.canUpdateSelectedFolder, 'app.selection.displayedKnowledgeRetrievalButton': rules.canDisplayKnowledgeRetrievalButton, + 'app.selection.isRmaSystemContainer': rules.isRmaSystemContainer, 'app.navigation.folder.canCreate': rules.canCreateFolder, 'app.navigation.isTrashcan': rules.isTrashcan, diff --git a/projects/aca-shared/rules/src/app.rules.spec.ts b/projects/aca-shared/rules/src/app.rules.spec.ts index 70f431ea5..712e62d67 100644 --- a/projects/aca-shared/rules/src/app.rules.spec.ts +++ b/projects/aca-shared/rules/src/app.rules.spec.ts @@ -23,7 +23,14 @@ */ import * as app from './app.rules'; -import { createVersionRule, getFileExtension, isPreferencesApiAvailable, isNodeInfoAvailable, isBulkActionsAvailable } from './app.rules'; +import { + createVersionRule, + getFileExtension, + isPreferencesApiAvailable, + isNodeInfoAvailable, + isBulkActionsAvailable, + isRmaSystemContainer +} from './app.rules'; import { TestRuleContext } from './test-rule-context'; import { NodeEntry, RepositoryInfo, StatusInfo } from '@alfresco/js-api'; import { ProfileState, RuleContext } from '@alfresco/adf-extensions'; @@ -858,42 +865,39 @@ describe('app.evaluators', () => { }); describe('canCreateFolder', () => { - it('should return false when content service is disabled', () => { - context.appConfig = { get: () => false } as any; - expect(app.canCreateFolder(context)).toBeFalse(); - }); - it('should return false when user is outside personal files or libraries', () => { - context.appConfig = { get: () => true } as any; context.navigation.url = '/favorite/test'; expect(app.canCreateFolder(context)).toBeFalse(); }); it('should return false when current folder does not exist', () => { - context.appConfig = { get: () => true } as any; context.navigation.url = '/personal-files/test'; context.navigation.currentFolder = null; expect(app.canCreateFolder(context)).toBeFalse(); }); it('should return false when permission check fails', () => { - context.appConfig = { get: () => true } as any; context.navigation.url = '/personal-files/test'; context.navigation.currentFolder = {} as any; context.permissions = { check: () => false }; expect(app.canCreateFolder(context)).toBeFalse(); }); - it('should return true when permission requirements are met', () => { - context.appConfig = { get: () => true } as any; + it('should return true when permission requirements are met and current folder is not rma restricted one', () => { context.navigation.url = '/personal-files/test'; - context.navigation.currentFolder = {} as any; + context.navigation.currentFolder = { nodeType: 'rma:filePlan' } as any; context.permissions = { check: () => true }; expect(app.canCreateFolder(context)).toBeTrue(); }); + it('should return false when permission requirements are met BUT current folder is rma restricted one', () => { + context.navigation.url = '/personal-files/test'; + context.navigation.currentFolder = { nodeType: 'rma:hold' } as any; + context.permissions = { check: () => true }; + expect(app.canCreateFolder(context)).toBeFalse(); + }); + it('should verify is user has create permission on current folder', () => { - context.appConfig = { get: () => true } as any; context.navigation.url = '/personal-files/test'; context.navigation.currentFolder = { allowableOperations: ['create'] } as any; spyOn(context.permissions, 'check'); @@ -1315,6 +1319,31 @@ describe('Versions compatibility', () => { expect(rule(makeContext('25.0.1.1-rc'))).toBe(false); }); }); + + describe('isRmaSystemContainer', () => { + const prepareRuleContext = (nodeType: string | string[]): RuleContext => { + const nodes = Array.isArray(nodeType) ? nodeType.map((type) => ({ entry: { nodeType: type } })) : [{ entry: { nodeType } }]; + return { + selection: { + nodes + } + } as RuleContext; + }; + + it('should return false when node is not rma system container', () => { + expect(isRmaSystemContainer(prepareRuleContext('rma:filePlan'))).toBeFalse(); + }); + + it('should return true when node is rma system container', () => { + expect(isRmaSystemContainer(prepareRuleContext('rma:holdContainer'))).toBeTrue(); + expect(isRmaSystemContainer(prepareRuleContext('rma:transferContainer'))).toBeTrue(); + expect(isRmaSystemContainer(prepareRuleContext('rma:unfiledRecordContainer'))).toBeTrue(); + }); + + it('should return true when at least one selected node is an rma system container', () => { + expect(isRmaSystemContainer(prepareRuleContext(['cm:folder', 'rma:transferContainer']))).toBeTrue(); + }); + }); }); function createTestContext(): TestRuleContext { diff --git a/projects/aca-shared/rules/src/app.rules.ts b/projects/aca-shared/rules/src/app.rules.ts index 369c39627..9309b094c 100644 --- a/projects/aca-shared/rules/src/app.rules.ts +++ b/projects/aca-shared/rules/src/app.rules.ts @@ -27,6 +27,7 @@ import { RuleContext } from '@alfresco/adf-extensions'; import * as navigation from './navigation.rules'; import * as repository from './repository.rules'; import { isAdmin } from './user.rules'; +import { isRmaRestrictedCreateFolder, isRmaSystemFolder } from '@alfresco/aca-shared'; /* cspell:disable */ export const supportedExtensions = { @@ -197,7 +198,7 @@ export function canCreateFolder(context: AcaRuleContext): boolean { const { currentFolder } = context.navigation; if (currentFolder) { - return context.permissions.check(currentFolder, ['create']); + return context.permissions.check(currentFolder, ['create']) && !isRmaRestrictedCreateFolder(currentFolder); } } return false; @@ -364,7 +365,7 @@ export function canUploadVersion(context: RuleContext): boolean { export const canPrintFile = (context: RuleContext): boolean => { const nodeEntry = context.selection.file.entry; const mediaMimeTypes = ['video/mp4', 'video/webm', 'video/ogg', 'audio/mpeg', 'audio/mp3', 'audio/ogg', 'audio/wav']; - return !mediaMimeTypes.includes(nodeEntry.content.mimeType); + return !mediaMimeTypes.includes(nodeEntry.content?.mimeType); }; /** @@ -512,6 +513,14 @@ export function createVersionRule(minimalVersion: string): (context: RuleContext }; } +/** + * Checks if the folder is a Records Management site's system folder + * JSON ref: `app.selection.isRmaSystemContainer` + */ +export const isRmaSystemContainer = (context: RuleContext): boolean => { + return context.selection.nodes.some((node) => isRmaSystemFolder(node.entry)); +}; + function isVersionCompatible(currentVersion: string, minimalVersion: string): boolean { if (!currentVersion || !minimalVersion) { return false;