mirror of
https://github.com/Alfresco/alfresco-content-app.git
synced 2026-09-09 18:02:54 +00:00
[MNT-25522] Add support for 'Link document' functionality (#5195)
* [MNT-25522] Add 'Link document' functionality * [MNT-25522] address copilot comments * [MNT-25522] cr fixes * [MNT-25522] address code smell
This commit is contained in:
@@ -71,6 +71,27 @@ describe('app.evaluators', () => {
|
||||
|
||||
expect(app.canDownloadSelection(context)).toBe(false);
|
||||
});
|
||||
|
||||
it('should not allow downloading when selection contains a file link', () => {
|
||||
context.selection.isEmpty = false;
|
||||
context.selection.nodes = [{ entry: { nodeType: 'app:filelink', isFile: true } }] as NodeEntry[];
|
||||
|
||||
expect(app.canDownloadSelection(context)).toBe(false);
|
||||
});
|
||||
|
||||
it('should not allow downloading when selection contains a folder link', () => {
|
||||
context.selection.isEmpty = false;
|
||||
context.selection.nodes = [{ entry: { nodeType: 'app:folderlink', isFolder: true } }] as NodeEntry[];
|
||||
|
||||
expect(app.canDownloadSelection(context)).toBe(false);
|
||||
});
|
||||
|
||||
it('should not allow downloading when any node in multi-selection is a link', () => {
|
||||
context.selection.isEmpty = false;
|
||||
context.selection.nodes = [{ entry: { isFile: true } }, { entry: { nodeType: 'app:filelink', isFile: true } }] as NodeEntry[];
|
||||
|
||||
expect(app.canDownloadSelection(context)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('isWriteLocked', () => {
|
||||
@@ -212,6 +233,22 @@ describe('app.evaluators', () => {
|
||||
|
||||
expect(app.canUploadVersion(context)).toBe(true);
|
||||
});
|
||||
|
||||
it('should return [false] when any selected node is a file link', () => {
|
||||
context.navigation.url = '/personal-files';
|
||||
context.selection.isEmpty = false;
|
||||
context.selection.nodes = [{ entry: { nodeType: 'app:filelink' } }] as NodeEntry[];
|
||||
|
||||
expect(app.canUploadVersion(context)).toBe(false);
|
||||
});
|
||||
|
||||
it('should return [false] when any selected node is a folder link', () => {
|
||||
context.navigation.url = '/personal-files';
|
||||
context.selection.isEmpty = false;
|
||||
context.selection.nodes = [{ entry: { nodeType: 'app:folderlink' } }] as NodeEntry[];
|
||||
|
||||
expect(app.canUploadVersion(context)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('isShared', () => {
|
||||
@@ -397,6 +434,26 @@ describe('app.evaluators', () => {
|
||||
|
||||
expect(app.canOpenWithOffice(context)).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should return [false] when any selected node is a file link', () => {
|
||||
context.appConfig = { get: () => true } as any;
|
||||
context.selection.isEmpty = false;
|
||||
context.selection.nodes = [{ entry: { nodeType: 'app:filelink' } }] as NodeEntry[];
|
||||
context.selection.file = { entry: { name: 'document.docx', isLocked: false, properties: {} } } as NodeEntry;
|
||||
context.permissions = { check: () => true };
|
||||
|
||||
expect(app.canOpenWithOffice(context)).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should return [false] when any selected node is a folder link', () => {
|
||||
context.appConfig = { get: () => true } as any;
|
||||
context.selection.isEmpty = false;
|
||||
context.selection.nodes = [{ entry: { nodeType: 'app:folderlink' } }] as NodeEntry[];
|
||||
context.selection.file = { entry: { name: 'document.docx', isLocked: false, properties: {} } } as NodeEntry;
|
||||
context.permissions = { check: () => true };
|
||||
|
||||
expect(app.canOpenWithOffice(context)).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('canEditAspects', () => {
|
||||
@@ -736,6 +793,22 @@ describe('app.evaluators', () => {
|
||||
context.repository.status.isQuickShareEnabled = true;
|
||||
expect(app.canShareFile(context)).toBeTrue();
|
||||
});
|
||||
|
||||
it('should return false when selection contains a file link node', () => {
|
||||
context.selection.file = { entry: { properties: {} } } as NodeEntry;
|
||||
context.selection.nodes = [{ entry: { nodeType: 'app:filelink' } }] as NodeEntry[];
|
||||
context.navigation.url = '/personal-files';
|
||||
context.repository.status.isQuickShareEnabled = true;
|
||||
expect(app.canShareFile(context)).toBeFalse();
|
||||
});
|
||||
|
||||
it('should return false when selection contains a folder link node', () => {
|
||||
context.selection.file = { entry: { properties: {} } } as NodeEntry;
|
||||
context.selection.nodes = [{ entry: { nodeType: 'app:folderlink' } }] as NodeEntry[];
|
||||
context.navigation.url = '/personal-files';
|
||||
context.repository.status.isQuickShareEnabled = true;
|
||||
expect(app.canShareFile(context)).toBeFalse();
|
||||
});
|
||||
});
|
||||
|
||||
describe('canToggleJoinLibrary', () => {
|
||||
@@ -1133,6 +1206,22 @@ describe('app.evaluators', () => {
|
||||
context.permissions = { check: () => false };
|
||||
expect(app.canToggleFileLock(context)).toBeTrue();
|
||||
});
|
||||
|
||||
it('should return false when any selected node is a file link', () => {
|
||||
context.selection.isEmpty = false;
|
||||
context.selection.nodes = [{ entry: { nodeType: 'app:filelink' } }] as NodeEntry[];
|
||||
context.selection.file = { entry: { properties: {} } } as NodeEntry;
|
||||
context.permissions = { check: () => true };
|
||||
expect(app.canToggleFileLock(context)).toBeFalse();
|
||||
});
|
||||
|
||||
it('should return false when any selected node is a folder link', () => {
|
||||
context.selection.isEmpty = false;
|
||||
context.selection.nodes = [{ entry: { nodeType: 'app:folderlink' } }] as NodeEntry[];
|
||||
context.selection.file = { entry: { properties: {} } } as NodeEntry;
|
||||
context.permissions = { check: () => true };
|
||||
expect(app.canToggleFileLock(context)).toBeFalse();
|
||||
});
|
||||
});
|
||||
|
||||
describe('canPrintFile', () => {
|
||||
@@ -1223,6 +1312,49 @@ describe('app.evaluators', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('isNodeLink', () => {
|
||||
it('should return false when selection is empty', () => {
|
||||
context.selection.isEmpty = true;
|
||||
expect(app.isNodeLink(context)).toBeFalse();
|
||||
});
|
||||
|
||||
it('should return false when selected node has no nodeType', () => {
|
||||
context.selection.isEmpty = false;
|
||||
context.selection.nodes = [{ entry: { isFile: true } }] as NodeEntry[];
|
||||
expect(app.isNodeLink(context)).toBeFalse();
|
||||
});
|
||||
|
||||
it('should return false when selected node has an unrelated nodeType', () => {
|
||||
context.selection.isEmpty = false;
|
||||
context.selection.nodes = [{ entry: { nodeType: 'cm:content' } }] as NodeEntry[];
|
||||
expect(app.isNodeLink(context)).toBeFalse();
|
||||
});
|
||||
|
||||
it('should return true when selected node has nodeType app:filelink', () => {
|
||||
context.selection.isEmpty = false;
|
||||
context.selection.nodes = [{ entry: { nodeType: 'app:filelink' } }] as NodeEntry[];
|
||||
expect(app.isNodeLink(context)).toBeTrue();
|
||||
});
|
||||
|
||||
it('should return true when selected node has nodeType app:folderlink', () => {
|
||||
context.selection.isEmpty = false;
|
||||
context.selection.nodes = [{ entry: { nodeType: 'app:folderlink' } }] as NodeEntry[];
|
||||
expect(app.isNodeLink(context)).toBeTrue();
|
||||
});
|
||||
|
||||
it('should return true when any node in multi-selection is a link', () => {
|
||||
context.selection.isEmpty = false;
|
||||
context.selection.nodes = [{ entry: { nodeType: 'cm:content' } }, { entry: { nodeType: 'app:filelink' } }] as NodeEntry[];
|
||||
expect(app.isNodeLink(context)).toBeTrue();
|
||||
});
|
||||
|
||||
it('should return false when no node in multi-selection is a link', () => {
|
||||
context.selection.isEmpty = false;
|
||||
context.selection.nodes = [{ entry: { nodeType: 'cm:content' } }, { entry: { nodeType: 'cm:folder' } }] as NodeEntry[];
|
||||
expect(app.isNodeLink(context)).toBeFalse();
|
||||
});
|
||||
});
|
||||
|
||||
describe('isSSOEnabled', () => {
|
||||
it('should return true if sso is enabled', () => {
|
||||
context.appConfig = { get: () => 'OAUTH' } as any;
|
||||
|
||||
@@ -121,7 +121,9 @@ export function canRemoveFavorite(context: RuleContext): boolean {
|
||||
* JSON ref: `app.selection.file.canShare`
|
||||
*/
|
||||
export const canShareFile = (context: RuleContext): boolean =>
|
||||
[context.selection.file, !navigation.isTrashcan(context), repository.hasQuickShareEnabled(context), !isShared(context)].every(Boolean);
|
||||
[context.selection.file, !navigation.isTrashcan(context), repository.hasQuickShareEnabled(context), !isShared(context), !isNodeLink(context)].every(
|
||||
Boolean
|
||||
);
|
||||
|
||||
/**
|
||||
* Checks if user can perform "Join" or "Cancel Join Request" on a library.
|
||||
@@ -208,6 +210,9 @@ export function canCreateFolder(context: AcaRuleContext): boolean {
|
||||
* JSON ref: `app.selection.canDownload`
|
||||
*/
|
||||
export function canDownloadSelection(context: RuleContext): boolean {
|
||||
if (isNodeLink(context)) {
|
||||
return false;
|
||||
}
|
||||
return context.selection.nodes.every((node: any) => node.entry && (node.entry.isFile || node.entry.isFolder || !!node.entry.nodeId));
|
||||
}
|
||||
|
||||
@@ -344,6 +349,10 @@ export function canUnlockFile(context: RuleContext): boolean {
|
||||
* JSON ref: `app.selection.file.canUploadVersion`
|
||||
*/
|
||||
export function canUploadVersion(context: RuleContext): boolean {
|
||||
if (isNodeLink(context)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (navigation.isFavorites(context) || navigation.isSharedFiles(context)) {
|
||||
return hasFileSelected(context);
|
||||
}
|
||||
@@ -363,6 +372,9 @@ export function canUploadVersion(context: RuleContext): boolean {
|
||||
*/
|
||||
export const canPrintFile = (context: RuleContext): boolean => {
|
||||
const nodeEntry = context.selection.file.entry;
|
||||
if (!nodeEntry?.content?.mimeType) {
|
||||
return false;
|
||||
}
|
||||
const mediaMimeTypes = ['video/mp4', 'video/webm', 'video/ogg', 'audio/mpeg', 'audio/mp3', 'audio/ogg', 'audio/wav'];
|
||||
return !mediaMimeTypes.includes(nodeEntry.content.mimeType);
|
||||
};
|
||||
@@ -390,7 +402,8 @@ export const canEditAspects = (context: RuleContext): boolean =>
|
||||
repository.isMajorVersionAvailable(context, '7')
|
||||
].every(Boolean);
|
||||
|
||||
export const canToggleFileLock = (context: RuleContext): boolean => [canLockFile(context) || canUnlockFile(context)].some(Boolean);
|
||||
export const canToggleFileLock = (context: RuleContext): boolean =>
|
||||
!isNodeLink(context) && [canLockFile(context) || canUnlockFile(context)].some(Boolean);
|
||||
|
||||
/**
|
||||
* @deprecated Uses workarounds for for recent files and search api issues.
|
||||
@@ -434,6 +447,10 @@ export function canOpenWithOffice(context: AcaRuleContext): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isNodeLink(context)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (context.navigation?.url?.startsWith('/trashcan')) {
|
||||
return false;
|
||||
}
|
||||
@@ -577,3 +594,13 @@ export const isCheckedOut = (context: RuleContext): boolean => {
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks if any of the selected nodes is a link node (app:filelink or app:folderlink).
|
||||
* JSON ref: `app.selection.isNodeLink`
|
||||
*
|
||||
* @param context Rule execution context
|
||||
*/
|
||||
export const isNodeLink = (context: RuleContext): boolean =>
|
||||
!context.selection?.isEmpty &&
|
||||
context.selection.nodes.some((node) => node.entry?.nodeType === 'app:filelink' || node.entry?.nodeType === 'app:folderlink');
|
||||
|
||||
@@ -23,8 +23,8 @@
|
||||
*/
|
||||
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Subject } from 'rxjs';
|
||||
import { SiteEntry } from '@alfresco/js-api';
|
||||
import { BehaviorSubject, Subject } from 'rxjs';
|
||||
import { NodeEntry, SiteEntry } from '@alfresco/js-api';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
@@ -84,4 +84,9 @@ export class AppHookService {
|
||||
* Gets emitted when user mark the favorite library
|
||||
*/
|
||||
favoriteLibraryToggle = new Subject<void>();
|
||||
|
||||
/**
|
||||
* Holds the node that should be pre-selected in the document list after navigation.
|
||||
*/
|
||||
nodeToSelect$ = new BehaviorSubject<NodeEntry | null>(null);
|
||||
}
|
||||
|
||||
@@ -114,7 +114,7 @@ export class ContentApiService {
|
||||
*/
|
||||
getNode(nodeId: string, options: any = {}): Observable<NodeEntry> {
|
||||
const defaults = {
|
||||
include: ['path', 'properties', 'allowableOperations', 'permissions', 'definition']
|
||||
include: ['path', 'properties', 'allowableOperations', 'permissions', 'definition', 'isLink']
|
||||
};
|
||||
const queryOptions = Object.assign(defaults, options);
|
||||
|
||||
@@ -123,7 +123,7 @@ export class ContentApiService {
|
||||
|
||||
getNodeInfo(nodeId: string, options?: any): Observable<Node> {
|
||||
const defaults = {
|
||||
include: ['isFavorite', 'allowableOperations', 'path', 'definition']
|
||||
include: ['isFavorite', 'allowableOperations', 'path', 'definition', 'isLink']
|
||||
};
|
||||
const queryOptions = Object.assign(defaults, options || {});
|
||||
|
||||
|
||||
@@ -39,6 +39,8 @@ export enum NodeActionTypes {
|
||||
Unshare = 'UNSHARE_NODES',
|
||||
Copy = 'COPY_NODES',
|
||||
Move = 'MOVE_NODES',
|
||||
Link = 'CREATE_LINK',
|
||||
Locate = 'LOCATE_LINKED_ITEM',
|
||||
NodeInformation = 'NODE_INFORMATION',
|
||||
ManagePermissions = 'MANAGE_PERMISSIONS',
|
||||
PrintFile = 'PRINT_FILE',
|
||||
@@ -149,6 +151,21 @@ export class MoveNodesAction implements Action {
|
||||
) {}
|
||||
}
|
||||
|
||||
export class LinkNodesAction implements Action {
|
||||
readonly type = NodeActionTypes.Link;
|
||||
|
||||
constructor(
|
||||
public payload: Array<NodeEntry>,
|
||||
public configuration?: ModalConfiguration
|
||||
) {}
|
||||
}
|
||||
|
||||
export class LocateLinkedItemAction implements Action {
|
||||
readonly type = NodeActionTypes.Locate;
|
||||
|
||||
constructor(public payload: Array<NodeEntry>) {}
|
||||
}
|
||||
|
||||
export class ManagePermissionsAction implements Action {
|
||||
readonly type = NodeActionTypes.ManagePermissions;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user