ACS-10914 Remove unused allowable operation directive (#11476)

This commit is contained in:
Denys Vuika
2026-01-05 09:01:30 +00:00
committed by GitHub
parent d384139eb9
commit b13ae23173
21 changed files with 12 additions and 688 deletions
@@ -19,12 +19,11 @@ import { UpdateNotification, CardViewBaseItemModel, CardViewUpdateService } from
import { Node } from '@alfresco/js-api';
import { Injectable } from '@angular/core';
import { Subject } from 'rxjs';
import { BaseCardViewContentUpdate } from '../../interfaces/base-card-view-content-update.interface';
@Injectable({
providedIn: 'root'
})
export class CardViewContentUpdateService implements BaseCardViewContentUpdate {
export class CardViewContentUpdateService {
itemUpdated$ = new Subject<UpdateNotification>();
updatedAspect$ = new Subject<Node>();
@@ -1,78 +0,0 @@
/*!
* @license
* Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* 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.
*/
export const mockGroupProperties = [
{
title: 'EXIF',
properties: [
{
label: 'Image Width',
value: 363,
key: 'properties.exif:pixelXDimension',
default: null,
editable: true,
clickable: false,
icon: '',
data: null,
type: 'int',
multiline: false,
pipes: [],
clickCallBack: null,
displayValue: 400
},
{
label: 'Image Height',
value: 400,
key: 'properties.exif:pixelYDimension',
default: null,
editable: true,
clickable: false,
icon: '',
data: null,
type: 'int',
multiline: false,
pipes: [],
clickCallBack: null,
displayValue: 400
}
],
editable: true,
expanded: true
},
{
title: 'CUSTOM',
properties: [
{
label: 'Height',
value: 400,
key: 'properties.custom:abc',
default: null,
editable: true,
clickable: false,
icon: '',
data: null,
type: 'int',
multiline: false,
pipes: [],
clickCallBack: null,
displayValue: 400
}
],
editable: true,
expanded: true
}
];
@@ -1,160 +0,0 @@
/*!
* @license
* Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* 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 { ChangeDetectorRef, Component, ElementRef, SimpleChange } from '@angular/core';
import { ContentService } from '../common/services/content.service';
import { CheckAllowableOperationDirective } from './check-allowable-operation.directive';
import { TestBed } from '@angular/core/testing';
import { NodeAllowableOperationSubject } from '../interfaces/node-allowable-operation-subject.interface';
import { RedirectAuthService } from '@alfresco/adf-core';
import { EMPTY, of } from 'rxjs';
import { HttpClientTestingModule } from '@angular/common/http/testing';
@Component({
selector: 'adf-text-subject',
template: ''
})
class TestComponent implements NodeAllowableOperationSubject {
disabled: boolean = false;
}
describe('CheckAllowableOperationDirective', () => {
let changeDetectorMock: ChangeDetectorRef;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [HttpClientTestingModule],
providers: [{ provide: RedirectAuthService, useValue: { onLogin: EMPTY, onTokenReceived: of() } }]
});
changeDetectorMock = { detectChanges: () => {} } as ChangeDetectorRef;
});
describe('HTML nativeElement as subject', () => {
it('updates element on nodes change', () => {
const directive = new CheckAllowableOperationDirective(null, null, null, changeDetectorMock);
spyOn(directive, 'updateElement').and.stub();
const nodes = [{}, {}];
const change = new SimpleChange([], nodes, false);
directive.ngOnChanges({ nodes: change });
expect(directive.updateElement).toHaveBeenCalled();
});
it('updates element only on subsequent change', () => {
const directive = new CheckAllowableOperationDirective(null, null, null, changeDetectorMock);
spyOn(directive, 'updateElement').and.stub();
const nodes = [{}, {}];
const change = new SimpleChange([], nodes, true);
directive.ngOnChanges({ nodes: change });
expect(directive.updateElement).not.toHaveBeenCalled();
});
it('enables decorated element', () => {
const renderer = jasmine.createSpyObj('renderer', ['removeAttribute']);
const elementRef = new ElementRef({});
const directive = new CheckAllowableOperationDirective(elementRef, renderer, null, changeDetectorMock);
directive.enableElement();
expect(renderer.removeAttribute).toHaveBeenCalledWith(elementRef.nativeElement, 'disabled');
});
it('disables decorated element', () => {
const renderer = jasmine.createSpyObj('renderer', ['setAttribute']);
const elementRef = new ElementRef({});
const directive = new CheckAllowableOperationDirective(elementRef, renderer, null, changeDetectorMock);
directive.disableElement();
expect(renderer.setAttribute).toHaveBeenCalledWith(elementRef.nativeElement, 'disabled', 'true');
});
it('disables element when nodes not available', () => {
const directive = new CheckAllowableOperationDirective(null, null, null, changeDetectorMock);
spyOn(directive, 'disableElement').and.stub();
directive.nodes = null;
expect(directive.updateElement()).toBeFalsy();
directive.nodes = [];
expect(directive.updateElement()).toBeFalsy();
});
it('enables element when all nodes have expected permission', () => {
const contentService = TestBed.inject(ContentService);
spyOn(contentService, 'hasAllowableOperations').and.returnValue(true);
const directive = new CheckAllowableOperationDirective(null, null, contentService, changeDetectorMock);
spyOn(directive, 'enableElement').and.stub();
directive.nodes = [{}, {}] as any[];
expect(directive.updateElement()).toBeTruthy();
expect(directive.enableElement).toHaveBeenCalled();
});
it('disables element when one of the nodes have no permission', () => {
const contentService = TestBed.inject(ContentService);
spyOn(contentService, 'hasAllowableOperations').and.returnValue(false);
const directive = new CheckAllowableOperationDirective(null, null, contentService, changeDetectorMock);
spyOn(directive, 'disableElement').and.stub();
directive.nodes = [{}, {}] as any[];
expect(directive.updateElement()).toBeFalsy();
expect(directive.disableElement).toHaveBeenCalled();
});
});
describe('Angular component as subject', () => {
it('disables decorated component', () => {
const contentService = TestBed.inject(ContentService);
spyOn(contentService, 'hasAllowableOperations').and.returnValue(false);
spyOn(changeDetectorMock, 'detectChanges');
const testComponent = new TestComponent();
testComponent.disabled = false;
const directive = new CheckAllowableOperationDirective(null, null, contentService, changeDetectorMock, testComponent);
directive.nodes = [{}, {}] as any[];
directive.updateElement();
expect(testComponent.disabled).toBeTruthy();
expect(changeDetectorMock.detectChanges).toHaveBeenCalledTimes(1);
});
it('enables decorated component', () => {
const contentService = TestBed.inject(ContentService);
spyOn(contentService, 'hasAllowableOperations').and.returnValue(true);
spyOn(changeDetectorMock, 'detectChanges');
const testComponent = new TestComponent();
testComponent.disabled = true;
const directive = new CheckAllowableOperationDirective(null, null, contentService, changeDetectorMock, testComponent);
directive.nodes = [{}, {}] as any[];
directive.updateElement();
expect(testComponent.disabled).toBeFalsy();
expect(changeDetectorMock.detectChanges).toHaveBeenCalledTimes(1);
});
});
});
@@ -1,125 +0,0 @@
/*!
* @license
* Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* 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.
*/
/* eslint-disable @angular-eslint/no-input-rename */
import { ChangeDetectorRef, Directive, ElementRef, Host, Inject, Input, OnChanges, Optional, Renderer2, SimpleChanges } from '@angular/core';
import { NodeEntry } from '@alfresco/js-api';
import { EXTENDIBLE_COMPONENT } from '@alfresco/adf-core';
import { ContentService } from '../common/services/content.service';
import { NodeAllowableOperationSubject } from '../interfaces/node-allowable-operation-subject.interface';
@Directive({
standalone: true,
selector: '[adf-check-allowable-operation]'
})
export class CheckAllowableOperationDirective implements OnChanges {
/**
* Node permission to check (create, delete, update, updatePermissions,
* !create, !delete, !update, !updatePermissions).
*/
@Input('adf-check-allowable-operation')
permission: string = null;
/** Nodes to check permission for. */
@Input('adf-nodes')
nodes: NodeEntry[] = [];
constructor(
private elementRef: ElementRef,
private renderer: Renderer2,
private contentService: ContentService,
private changeDetector: ChangeDetectorRef,
@Host()
@Optional()
@Inject(EXTENDIBLE_COMPONENT)
private parentComponent?: NodeAllowableOperationSubject
) {}
ngOnChanges(changes: SimpleChanges) {
if (changes.nodes && !changes.nodes.firstChange) {
this.updateElement();
}
}
/**
* Updates disabled state for the decorated element
*
* @returns the new state
*/
updateElement(): boolean {
const enable = this.hasAllowableOperations(this.nodes, this.permission);
if (enable) {
this.enable();
} else {
this.disable();
}
return enable;
}
private enable(): void {
if (this.parentComponent) {
this.parentComponent.disabled = false;
this.changeDetector.detectChanges();
} else {
this.enableElement();
}
}
private disable(): void {
if (this.parentComponent) {
this.parentComponent.disabled = true;
this.changeDetector.detectChanges();
} else {
this.disableElement();
}
}
/**
* Enables decorated element
*
*/
enableElement(): void {
this.renderer.removeAttribute(this.elementRef.nativeElement, 'disabled');
}
/**
* Disables decorated element
*
*/
disableElement(): void {
this.renderer.setAttribute(this.elementRef.nativeElement, 'disabled', 'true');
}
/**
* Checks whether all nodes have a particular permission
*
* @param nodes Node collection to check
* @param permission Permission to check for each node
* @returns `true` if there are allowable operations, otherwise `false`
*/
hasAllowableOperations(nodes: NodeEntry[], permission: string): boolean {
if (nodes && nodes.length > 0) {
return nodes.every((node) => this.contentService.hasAllowableOperations(node.entry, permission));
}
return false;
}
}
@@ -19,7 +19,6 @@ import { NgModule } from '@angular/core';
import { NodeLockDirective } from './node-lock.directive';
import { NodeCounterComponent, NodeCounterDirective } from './node-counter.directive';
import { AutoFocusDirective } from './auto-focus.directive';
import { CheckAllowableOperationDirective } from './check-allowable-operation.directive';
import { LibraryFavoriteDirective } from './library-favorite.directive';
import { LibraryMembershipDirective } from './library-membership.directive';
import { NodeDeleteDirective } from './node-delete.directive';
@@ -27,12 +26,12 @@ import { NodeFavoriteDirective } from './node-favorite.directive';
import { NodeRestoreDirective } from './node-restore.directive';
import { NodeDownloadDirective } from './node-download.directive';
/* @deprecated import standalone directives instead */
export const CONTENT_DIRECTIVES = [
NodeLockDirective,
NodeCounterDirective,
NodeCounterComponent,
AutoFocusDirective,
CheckAllowableOperationDirective,
LibraryFavoriteDirective,
LibraryMembershipDirective,
NodeDeleteDirective,
@@ -41,7 +40,7 @@ export const CONTENT_DIRECTIVES = [
NodeDownloadDirective
];
/** @deprecated import CONTENT_DIRECTIVES or standalone directives instead */
/** @deprecated import standalone directives instead */
@NgModule({
imports: [...CONTENT_DIRECTIVES],
exports: [...CONTENT_DIRECTIVES]
@@ -21,7 +21,6 @@ import { By } from '@angular/platform-browser';
import { NodeDeleteDirective } from './node-delete.directive';
import { RedirectAuthService } from '@alfresco/adf-core';
import { EMPTY, of, Subscription } from 'rxjs';
import { CheckAllowableOperationDirective } from './check-allowable-operation.directive';
@Component({
imports: [NodeDeleteDirective],
@@ -37,8 +36,8 @@ class TestComponent {
}
@Component({
imports: [NodeDeleteDirective, CheckAllowableOperationDirective],
template: `<div id="delete-component" [adf-check-allowable-operation]="'delete'" [adf-delete]="selection" (delete)="onDelete($event)"></div>`
imports: [NodeDeleteDirective],
template: `<div id="delete-component" [adf-delete]="selection" (delete)="onDelete($event)"></div>`
})
class TestWithPermissionsComponent {
selection: any[] = [];
@@ -19,7 +19,6 @@ export * from './content-directive.module';
export * from './node-lock.directive';
export * from './node-counter.directive';
export * from './auto-focus.directive';
export * from './check-allowable-operation.directive';
export * from './library-favorite.directive';
export * from './library-membership.directive';
export * from './node-delete.directive';
@@ -1,29 +0,0 @@
/*!
* @license
* Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* 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 { CardViewBaseItemModel, UpdateNotification } from '@alfresco/adf-core';
import { Node } from '@alfresco/js-api';
import { Subject } from 'rxjs';
export interface BaseCardViewContentUpdate {
itemUpdated$: Subject<UpdateNotification>;
updatedAspect$: Subject<Node>;
update(property: CardViewBaseItemModel, newValue: any);
updateElement(notification: CardViewBaseItemModel);
updateNodeAspect(node: Node);
}
@@ -1,20 +0,0 @@
/*!
* @license
* Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* 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.
*/
export interface NodeAllowableOperationSubject {
disabled: boolean;
}
@@ -15,10 +15,7 @@
* limitations under the License.
*/
export * from './node-allowable-operation-subject.interface';
export * from './library-entity.interface';
export * from './restore-message-model.interface';
export * from './library-membership-error-event.interface';
export * from './library-membership-toggle-event.interface';
export * from './base-card-view-content-update.interface';
@@ -15,8 +15,8 @@
* limitations under the License.
*/
import { EXTENDIBLE_COMPONENT, FileUtils } from '@alfresco/adf-core';
import { Component, EventEmitter, forwardRef, Input, OnChanges, OnInit, Output, SimpleChanges, ViewEncapsulation, inject } from '@angular/core';
import { FileUtils } from '@alfresco/adf-core';
import { Component, EventEmitter, Input, OnChanges, OnInit, Output, SimpleChanges, ViewEncapsulation, inject } from '@angular/core';
import { NodesApiService } from '../../common/services/nodes-api.service';
import { ContentService } from '../../common/services/content.service';
import { AllowableOperationsEnum } from '../../common/models/allowable-operations.enum';
@@ -24,7 +24,6 @@ import { Node } from '@alfresco/js-api';
import { Subject } from 'rxjs';
import { PermissionModel } from '../../document-list/models/permissions.model';
import { UploadBase } from './base-upload/upload-base';
import { NodeAllowableOperationSubject } from '../../interfaces/node-allowable-operation-subject.interface';
import { CommonModule } from '@angular/common';
import { MatButtonModule } from '@angular/material/button';
import { TranslatePipe } from '@ngx-translate/core';
@@ -35,10 +34,9 @@ import { MatIconModule } from '@angular/material/icon';
imports: [CommonModule, MatButtonModule, TranslatePipe, MatIconModule],
templateUrl: './upload-button.component.html',
styleUrls: ['./upload-button.component.scss'],
viewProviders: [{ provide: EXTENDIBLE_COMPONENT, useExisting: forwardRef(() => UploadButtonComponent) }],
encapsulation: ViewEncapsulation.None
})
export class UploadButtonComponent extends UploadBase implements OnInit, OnChanges, NodeAllowableOperationSubject {
export class UploadButtonComponent extends UploadBase implements OnInit, OnChanges {
private contentService = inject(ContentService);
private nodesApiService = inject(NodesApiService);
@@ -15,9 +15,8 @@
* limitations under the License.
*/
import { EXTENDIBLE_COMPONENT, FileInfo, FileUtils, NotificationService } from '@alfresco/adf-core';
import { Component, forwardRef, ViewEncapsulation, inject } from '@angular/core';
import { NodeAllowableOperationSubject } from '../../interfaces/node-allowable-operation-subject.interface';
import { FileInfo, FileUtils, NotificationService } from '@alfresco/adf-core';
import { Component, ViewEncapsulation, inject } from '@angular/core';
import { UploadBase } from './base-upload/upload-base';
import { AllowableOperationsEnum } from '../../common/models/allowable-operations.enum';
import { ContentService } from '../../common/services/content.service';
@@ -31,10 +30,9 @@ import { FileDraggableDirective } from '../directives/file-draggable.directive';
templateUrl: './upload-drag-area.component.html',
styleUrls: ['./upload-drag-area.component.scss'],
host: { class: 'adf-upload-drag-area' },
viewProviders: [{ provide: EXTENDIBLE_COMPONENT, useExisting: forwardRef(() => UploadDragAreaComponent) }],
encapsulation: ViewEncapsulation.None
})
export class UploadDragAreaComponent extends UploadBase implements NodeAllowableOperationSubject {
export class UploadDragAreaComponent extends UploadBase {
private notificationService = inject(NotificationService);
private contentService = inject(ContentService);
@@ -15,8 +15,7 @@
* limitations under the License.
*/
import { EXTENDIBLE_COMPONENT } from '@alfresco/adf-core';
import { Component, forwardRef, Input, OnChanges, ViewEncapsulation, OnInit } from '@angular/core';
import { Component, Input, OnChanges, ViewEncapsulation, OnInit } from '@angular/core';
import { Node } from '@alfresco/js-api';
import { UploadButtonComponent } from './upload-button.component';
import { AllowableOperationsEnum } from '../../common/models/allowable-operations.enum';
@@ -31,7 +30,6 @@ import { MatIconModule } from '@angular/material/icon';
imports: [CommonModule, MatButtonModule, TranslatePipe, MatIconModule],
templateUrl: './upload-button.component.html',
styleUrls: ['./upload-button.component.scss'],
viewProviders: [{ provide: EXTENDIBLE_COMPONENT, useExisting: forwardRef(() => UploadVersionButtonComponent) }],
encapsulation: ViewEncapsulation.None,
host: { class: 'adf-upload-version-button' }
})