mirror of
https://github.com/Alfresco/alfresco-content-app.git
synced 2025-07-24 17:31:52 +00:00
401 lines
14 KiB
TypeScript
401 lines
14 KiB
TypeScript
/*!
|
|
* Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
|
|
*
|
|
* Alfresco Example Content Application
|
|
*
|
|
* This file is part of the Alfresco Example Content Application.
|
|
* If the software was purchased under a paid Alfresco license, the terms of
|
|
* the paid license agreement will prevail. Otherwise, the software is
|
|
* provided under the following open source license terms:
|
|
*
|
|
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Lesser General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public License
|
|
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
import { Component, DestroyRef, forwardRef, inject, Input, OnChanges, OnInit, SimpleChanges, ViewEncapsulation } from '@angular/core';
|
|
import { ControlValueAccessor, FormControl, FormGroup, NG_VALUE_ACCESSOR, ReactiveFormsModule, Validators } from '@angular/forms';
|
|
import { ActionDefinitionTransformed, RuleAction } from '../../model/rule-action.model';
|
|
import {
|
|
CardViewBoolItemModel,
|
|
CardViewComponent,
|
|
CardViewItem,
|
|
CardViewSelectItemModel,
|
|
CardViewSelectItemOption,
|
|
CardViewTextItemModel,
|
|
CardViewUpdateService,
|
|
UpdateNotification
|
|
} from '@alfresco/adf-core';
|
|
import { ActionParameterDefinition, Category, Node, SecurityMark } from '@alfresco/js-api';
|
|
import { from, of, Subject } from 'rxjs';
|
|
import { map } from 'rxjs/operators';
|
|
import { ActionParameterConstraint, ConstraintValue } from '../../model/action-parameter-constraint.model';
|
|
import {
|
|
CategorySelectorDialogComponent,
|
|
CategorySelectorDialogOptions,
|
|
CategoryService,
|
|
ContentNodeSelectorComponent,
|
|
ContentNodeSelectorComponentData,
|
|
NodeAction,
|
|
SecurityControlsService,
|
|
TagService
|
|
} from '@alfresco/adf-content-services';
|
|
import { MatDialog } from '@angular/material/dialog';
|
|
import { TranslatePipe, TranslateService } from '@ngx-translate/core';
|
|
import { CommonModule } from '@angular/common';
|
|
import { MatFormFieldModule } from '@angular/material/form-field';
|
|
import { MatSelectModule } from '@angular/material/select';
|
|
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
|
|
@Component({
|
|
imports: [CommonModule, TranslatePipe, ReactiveFormsModule, MatFormFieldModule, MatSelectModule, CardViewComponent],
|
|
selector: 'aca-rule-action',
|
|
templateUrl: './rule-action.ui-component.html',
|
|
styleUrls: ['./rule-action.ui-component.scss'],
|
|
encapsulation: ViewEncapsulation.None,
|
|
host: { class: 'aca-rule-action' },
|
|
providers: [
|
|
{
|
|
provide: NG_VALUE_ACCESSOR,
|
|
multi: true,
|
|
useExisting: forwardRef(() => RuleActionUiComponent)
|
|
},
|
|
CardViewUpdateService
|
|
]
|
|
})
|
|
export class RuleActionUiComponent implements ControlValueAccessor, OnInit, OnChanges {
|
|
@Input()
|
|
nodeId = '';
|
|
|
|
@Input()
|
|
actionDefinitions: ActionDefinitionTransformed[];
|
|
|
|
@Input()
|
|
readOnly = false;
|
|
|
|
private _parameterConstraints = [];
|
|
@Input()
|
|
get parameterConstraints(): ActionParameterConstraint[] {
|
|
return this._parameterConstraints;
|
|
}
|
|
|
|
set parameterConstraints(value) {
|
|
this._parameterConstraints = value.map((obj) => ({
|
|
...obj,
|
|
constraints: this.parseConstraintsToSelectOptions(obj.constraints)
|
|
}));
|
|
}
|
|
|
|
private readonly tagsRelatedPropertiesAndAspects = ['cm:tagscope', 'cm:tagScopeCache', 'cm:taggable'];
|
|
private readonly categoriesRelatedPropertiesAndAspects = ['cm:categories', 'cm:generalclassifiable'];
|
|
private readonly paramsToFormatDisplayedValue = ['securityMarkId', 'securityGroupId'];
|
|
|
|
isFullWidth = false;
|
|
|
|
form = new FormGroup({
|
|
actionDefinitionId: new FormControl('', Validators.required)
|
|
});
|
|
|
|
cardViewItems: CardViewItem[] = [];
|
|
parameters: { [key: string]: unknown } = {};
|
|
|
|
get selectedActionDefinitionId(): string {
|
|
return this.form.get('actionDefinitionId').value;
|
|
}
|
|
|
|
get selectedActionDefinition(): ActionDefinitionTransformed {
|
|
return this.actionDefinitions.find((actionDefinition: ActionDefinitionTransformed) => actionDefinition.id === this.selectedActionDefinitionId);
|
|
}
|
|
|
|
onChange: (action: RuleAction) => void = () => undefined;
|
|
onTouch: () => void = () => undefined;
|
|
|
|
private readonly destroyRef = inject(DestroyRef);
|
|
|
|
constructor(
|
|
private cardViewUpdateService: CardViewUpdateService,
|
|
private dialog: MatDialog,
|
|
private translate: TranslateService,
|
|
private tagService: TagService,
|
|
private categoryService: CategoryService,
|
|
private securityControlsService: SecurityControlsService
|
|
) {}
|
|
|
|
writeValue(action: RuleAction) {
|
|
this.form.setValue({
|
|
actionDefinitionId: action.actionDefinitionId
|
|
});
|
|
this.parameters = {
|
|
...this.parameters,
|
|
...action.params
|
|
};
|
|
this.setCardViewProperties();
|
|
if (this.parameters?.securityGroupId) {
|
|
this.loadSecurityMarkOptions();
|
|
}
|
|
}
|
|
|
|
registerOnChange(fn: (action: RuleAction) => void) {
|
|
this.onChange = fn;
|
|
}
|
|
|
|
registerOnTouched(fn: any) {
|
|
this.onTouch = fn;
|
|
}
|
|
|
|
ngOnInit() {
|
|
this.actionDefinitions = this.actionDefinitions.sort((firstActionDefinition, secondActionDefinition) =>
|
|
firstActionDefinition.title.localeCompare(secondActionDefinition.title)
|
|
);
|
|
|
|
this.form.valueChanges.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(() => {
|
|
this.setDefaultParameters();
|
|
this.setCardViewProperties();
|
|
this.onChange({
|
|
actionDefinitionId: this.selectedActionDefinitionId,
|
|
params: this.parameters
|
|
});
|
|
this.onTouch();
|
|
});
|
|
|
|
this.cardViewUpdateService.itemUpdated$.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((updateNotification: UpdateNotification) => {
|
|
const isSecurityGroupUpdated = updateNotification.target.key === 'securityGroupId';
|
|
if (isSecurityGroupUpdated) {
|
|
this.parameters.securityMarkId = null;
|
|
}
|
|
|
|
this.parameters = this.clearEmptyParameters({
|
|
...this.parameters,
|
|
...updateNotification.changed
|
|
});
|
|
this.onChange({
|
|
actionDefinitionId: this.selectedActionDefinitionId,
|
|
params: this.parameters
|
|
});
|
|
this.onTouch();
|
|
|
|
if (isSecurityGroupUpdated) {
|
|
this.setCardViewProperties();
|
|
this.loadSecurityMarkOptions();
|
|
}
|
|
});
|
|
}
|
|
|
|
ngOnChanges(changes: SimpleChanges): void {
|
|
const readOnly = changes['readOnly']?.currentValue;
|
|
if (readOnly !== undefined && readOnly !== null) {
|
|
if (readOnly) {
|
|
this.readOnly = true;
|
|
this.form.disable();
|
|
} else {
|
|
this.readOnly = false;
|
|
this.form.enable();
|
|
}
|
|
}
|
|
}
|
|
|
|
setCardViewProperties(securityMarkOptions?: CardViewSelectItemOption<string>[]) {
|
|
const disabledTags = !this.tagService.areTagsEnabled();
|
|
const disabledCategories = !this.categoryService.areCategoriesEnabled();
|
|
this.cardViewItems = (this.selectedActionDefinition?.parameterDefinitions ?? []).map((paramDef) => {
|
|
const constraintsForDropdownBox =
|
|
paramDef.name === 'securityMarkId'
|
|
? { name: paramDef.name, constraints: securityMarkOptions || [] }
|
|
: this._parameterConstraints.find((obj) => obj.name === paramDef.name);
|
|
const cardViewPropertiesModel = {
|
|
label: paramDef.displayLabel + (paramDef.mandatory ? ' *' : ''),
|
|
key: paramDef.name,
|
|
editable: true,
|
|
...(paramDef.mandatory
|
|
? {
|
|
validators: [
|
|
{
|
|
message: 'ACA_FOLDER_RULES.RULE_DETAILS.ERROR.REQUIRED',
|
|
isValid: (value: unknown) => !!value
|
|
}
|
|
]
|
|
}
|
|
: {})
|
|
};
|
|
switch (paramDef.type) {
|
|
case 'd:boolean':
|
|
return new CardViewBoolItemModel({
|
|
...cardViewPropertiesModel,
|
|
value: this.parameters[paramDef.name] ?? false
|
|
});
|
|
case 'd:noderef':
|
|
if (!constraintsForDropdownBox && !this.readOnly && paramDef.name !== 'category-value') {
|
|
return new CardViewTextItemModel({
|
|
...cardViewPropertiesModel,
|
|
icon: 'folder',
|
|
default: '',
|
|
clickable: true,
|
|
clickCallBack: this.openSelectorDialog.bind(this, paramDef.name),
|
|
value: this.parameters[paramDef.name]
|
|
});
|
|
} else if (paramDef.name === 'category-value' && !this.readOnly) {
|
|
return new CardViewTextItemModel({
|
|
...cardViewPropertiesModel,
|
|
icon: 'library_add',
|
|
default: '',
|
|
clickable: true,
|
|
clickCallBack: this.openCatDialog.bind(this, paramDef.name),
|
|
value: this.parameters[paramDef.name]
|
|
});
|
|
}
|
|
// falls through
|
|
default:
|
|
if (constraintsForDropdownBox) {
|
|
return new CardViewSelectItemModel({
|
|
...cardViewPropertiesModel,
|
|
value: (this.parameters[paramDef.name] as string) ?? '',
|
|
options$: of(constraintsForDropdownBox.constraints).pipe(
|
|
map((options) => {
|
|
return options.filter(
|
|
(option) =>
|
|
!(
|
|
(disabledTags && this.tagsRelatedPropertiesAndAspects.includes(option.key)) ||
|
|
(disabledCategories && this.categoriesRelatedPropertiesAndAspects.includes(option.key))
|
|
)
|
|
);
|
|
})
|
|
)
|
|
});
|
|
}
|
|
return new CardViewTextItemModel({
|
|
...cardViewPropertiesModel,
|
|
value:
|
|
constraintsForDropdownBox && this.readOnly && this.paramsToFormatDisplayedValue.includes(paramDef.name)
|
|
? (constraintsForDropdownBox.constraints.find((constraint) => constraint.key === this.parameters[paramDef.name])?.label ?? '')
|
|
: (this.parameters[paramDef.name] ?? '')
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
private openSelectorDialog(paramDefName) {
|
|
const data: ContentNodeSelectorComponentData = {
|
|
selectionMode: 'single',
|
|
title: this.translate.instant('ACA_FOLDER_RULES.RULE_DETAILS.PLACEHOLDER.CHOOSE_FOLDER'),
|
|
actionName: NodeAction.CHOOSE,
|
|
currentFolderId: this.nodeId,
|
|
select: new Subject<Node[]>()
|
|
};
|
|
|
|
this.dialog.open(ContentNodeSelectorComponent, {
|
|
data,
|
|
panelClass: 'adf-content-node-selector-dialog',
|
|
width: '630px'
|
|
});
|
|
|
|
data.select.subscribe(
|
|
(selections: Node[]) => {
|
|
if (selections[0].id) {
|
|
this.writeValue({
|
|
actionDefinitionId: this.selectedActionDefinitionId,
|
|
params: {
|
|
...this.parameters,
|
|
[paramDefName]: selections[0].id
|
|
}
|
|
});
|
|
this.onChange({
|
|
actionDefinitionId: this.selectedActionDefinitionId,
|
|
params: this.parameters
|
|
});
|
|
this.onTouch();
|
|
}
|
|
},
|
|
(error) => {
|
|
console.error(error);
|
|
},
|
|
() => {
|
|
this.dialog.closeAll();
|
|
}
|
|
);
|
|
}
|
|
|
|
private openCatDialog(paramDefName) {
|
|
const data: CategorySelectorDialogOptions = {
|
|
select: new Subject<Category[]>(),
|
|
multiSelect: false
|
|
};
|
|
|
|
this.dialog.open(CategorySelectorDialogComponent, {
|
|
data,
|
|
width: '630px'
|
|
});
|
|
|
|
data.select.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((selections: Category[]) => {
|
|
if (selections[0].id) {
|
|
this.writeValue({
|
|
actionDefinitionId: this.selectedActionDefinitionId,
|
|
params: {
|
|
...this.parameters,
|
|
[paramDefName]: selections[0].id
|
|
}
|
|
});
|
|
this.onChange({
|
|
actionDefinitionId: this.selectedActionDefinitionId,
|
|
params: this.parameters
|
|
});
|
|
this.onTouch();
|
|
}
|
|
});
|
|
}
|
|
|
|
setDefaultParameters() {
|
|
this.parameters = {};
|
|
(this.selectedActionDefinition?.parameterDefinitions ?? []).forEach((paramDef: ActionParameterDefinition) => {
|
|
if (paramDef.type === 'd:boolean') {
|
|
this.parameters[paramDef.name] = false;
|
|
}
|
|
});
|
|
}
|
|
|
|
private parseConstraintsToSelectOptions(constraints: ConstraintValue[]): CardViewSelectItemOption<unknown>[] {
|
|
return constraints
|
|
.sort((a, b) => {
|
|
if (!a.label && b.label) {
|
|
return 1;
|
|
}
|
|
if (!b.label && a.label) {
|
|
return -1;
|
|
}
|
|
return a.label?.localeCompare(b.label) ?? -1;
|
|
})
|
|
.map((constraint) => ({
|
|
key: constraint.value,
|
|
label: constraint.label ? `${constraint.label} [${constraint.value}]` : constraint.value
|
|
}));
|
|
}
|
|
|
|
private clearEmptyParameters(params: { [key: string]: unknown }): { [key: string]: unknown } {
|
|
Object.keys(params).forEach((key) => (params[key] === null || params[key] === undefined || params[key] === '') && delete params[key]);
|
|
return params;
|
|
}
|
|
|
|
loadSecurityMarkOptions(): void {
|
|
if (this.parameters?.securityGroupId) {
|
|
from(this.securityControlsService.getSecurityMark(this.parameters.securityGroupId as string))
|
|
.pipe(map((securityMarks) => securityMarks.entries.map((entry) => this.formatSecurityMarkConstraint(entry))))
|
|
.subscribe((res) => this.setCardViewProperties(this.parseConstraintsToSelectOptions(res) as CardViewSelectItemOption<string>[]));
|
|
}
|
|
}
|
|
|
|
private formatSecurityMarkConstraint(securityMark: SecurityMark): ConstraintValue {
|
|
return {
|
|
value: securityMark.id,
|
|
label: securityMark.name
|
|
};
|
|
}
|
|
}
|