| /******************************************************************************** |
| * Copyright (c) 2015, 2023 Contributors to the Eclipse Foundation |
| * |
| * See the NOTICE file(s) distributed with this work for additional |
| * information regarding copyright ownership. |
| * |
| * This program and the accompanying materials are made available under the |
| * terms of the Eclipse Public License v. 2.0 which is available at |
| * http://www.eclipse.org/legal/epl-2.0. |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| ********************************************************************************/ |
| |
| import { DatePipe } from "@angular/common"; |
| import { Component, OnDestroy, OnInit } from "@angular/core"; |
| import { ActivatedRoute } from "@angular/router"; |
| import { TRANSLATE } from "@core/mdm-core.module"; |
| import { MDMItem } from "@core/mdm-item"; |
| import { MDMNotificationService } from "@core/mdm-notification.service"; |
| import { SystemParameterService } from "@details/services/systemparameter.service"; |
| import { FileLinkContextWrapper } from "@file-explorer/model/file-explorer.model"; |
| import { ContextFilesService } from "@file-explorer/services/context-files.service"; |
| import { FileService } from "@file-explorer/services/file.service"; |
| import { NavigatorService } from "@navigator/navigator.service"; |
| import { Attribute, ContextGroup, Node } from "@navigator/node"; |
| import { TranslateService } from "@ngx-translate/core"; |
| import { TreeNode } from "primeng/api"; |
| import { TreeTable } from "primeng/treetable"; |
| import { of, Subscription } from "rxjs"; |
| import { catchError, finalize, skip, take, tap } from "rxjs/operators"; |
| import { Role } from "src/app/authentication/authentication.service"; |
| import { EntityCreatorService } from "src/app/entity-creator/entity-creator.service"; |
| import { MDMEntity } from "src/app/entity-creator/model/mdm-entity"; |
| import { TimezoneService } from "src/app/timezone/timezone-service"; |
| import { AuthenticationService } from "../../../authentication/authentication.service"; |
| import { Components, Context, ContextAttributeIdentifier, Sensor } from "../../model/details.model"; |
| import { ContextService } from "../../services/context.service"; |
| |
| @Component({ |
| selector: "mdm-detail-context", |
| templateUrl: "mdm-detail-descriptive-data.component.html", |
| styleUrls: ["mdm-detail-descriptive-data.component.css"], |
| }) |
| export class MDMDescriptiveDataComponent implements OnInit, OnDestroy { |
| private readonly StatusLoading = TRANSLATE("details.mdm-detail-descriptive-data.status-loading"); |
| private readonly StatusSaving = TRANSLATE("details.mdm-detail-descriptive-data.status-saving"); |
| private readonly StatusNoNodes = TRANSLATE("details.mdm-detail-descriptive-data.status-no-nodes-available"); |
| private readonly StatusVirtualNodes = TRANSLATE("details.mdm-detail-descriptive-data.status-virtual-node-selected"); |
| private readonly StatusNoDescriptiveData = TRANSLATE("details.mdm-detail-descriptive-data.status-no-descriptive-data-available"); |
| |
| public treeNodes: { [key: string]: TreeNode[] }; |
| |
| // this holds the type dependant original context data in case of cancelling the edit mode |
| private tmpTreeNodes: Node[]; |
| |
| public selectedNode: Node; |
| public contextType: string; |
| |
| public contextComponents: Components; |
| public sensors: Sensor[]; |
| public status: string; |
| |
| // reference to be able to use enum in html template |
| public contextGroupEnum = ContextGroup; |
| |
| public editMode: boolean; |
| |
| public isTreeTableExpanded = true; |
| |
| private sub = new Subscription(); |
| |
| public tplComponents: MDMEntity[]; |
| public selectedTplComponent: MDMEntity[] = []; |
| public showTplComponents = false; |
| public selectedContext: string[] = []; |
| public showConfirmDelete = false; |
| public selectedContextComponentName: string; |
| ctxCompName = { ctxName: "" }; |
| hasOrdered: boolean; |
| hasMeasured: boolean; |
| |
| constructor( |
| private route: ActivatedRoute, |
| private _contextService: ContextService, |
| private navigatorService: NavigatorService, |
| private notificationService: MDMNotificationService, |
| private translateService: TranslateService, |
| private datePipe: DatePipe, |
| private authenticationService: AuthenticationService, |
| private fileService: FileService, |
| private contextFileService: ContextFilesService, |
| private systemParameterService: SystemParameterService, |
| private entityCreatorService: EntityCreatorService, |
| private timezoneService: TimezoneService, |
| ) {} |
| |
| public canEdit: boolean; |
| public isTest: boolean; |
| |
| public canEdit4Env: boolean; |
| private editAllowed: { [key: string]: boolean } = {}; |
| |
| ngOnInit() { |
| this.authenticationService.isUserInRole([Role.Admin.toString(), Role.User.toString()]).subscribe((b) => (this.canEdit = b)); |
| this.status = this.StatusNoNodes; |
| this.editMode = false; |
| |
| this.timezoneService.timezoneChanged.subscribe(() => { |
| this.reloadData(); |
| }); |
| |
| this.sub.add( |
| this.route.params.subscribe( |
| (params) => this.refreshContextData(params["context"]), |
| (error) => |
| this.notificationService.notifyError( |
| this.translateService.instant("details.mdm-detail-descriptive-data.err-cannot-load-scope"), |
| error, |
| ), |
| ), |
| ); |
| // node changed from the navigation tree |
| this.sub.add( |
| this.navigatorService |
| .onSelectionChanged() |
| .pipe( |
| skip(1), // skip first to avoid double loading, since path param also changes for sure on init. |
| tap((obj) => this.handleSelectionChanged(obj)), |
| finalize(() => (this.status = this.StatusNoDescriptiveData)), |
| catchError((error) => |
| of( |
| this.notificationService.notifyError( |
| this.translateService.instant("details.mdm-detail-descriptive-data.err-cannot-load-data"), |
| error, |
| ), |
| ), |
| ), |
| ) |
| .subscribe(), |
| ); |
| |
| // file changed |
| this.fileService.onFileChanged().subscribe((link) => this.handleFileChanged(link)); |
| } |
| |
| ngOnDestroy() { |
| this.sub.unsubscribe(); |
| } |
| |
| private handleSelectionChanged(obj: Node | MDMItem) { |
| this.isTest = false; |
| if (obj instanceof MDMItem) { |
| this.selectedNode = undefined; |
| this.status = this.StatusVirtualNodes; |
| } else { |
| this.refreshDetailData(obj); |
| this.handleEditMode(obj); |
| } |
| } |
| |
| handleFileChanged(fileLinkContextWrapper: FileLinkContextWrapper) { |
| if (this.treeNodes != undefined) { |
| this.treeNodes[fileLinkContextWrapper.contextType] |
| .filter((tnode) => tnode.label === fileLinkContextWrapper.contextComponent.name) |
| .map((tnode) => tnode.children) |
| .reduce((a, b) => a.concat(b), []) |
| .filter((tnode) => tnode.data.attribute.name === fileLinkContextWrapper.attribute.name) |
| .forEach((tnode) => (tnode.data.attribute = fileLinkContextWrapper.attribute)); |
| // spread is necessary for treeTable changeDetection |
| this.treeNodes[fileLinkContextWrapper.contextType] = [...this.treeNodes[fileLinkContextWrapper.contextType]]; |
| } |
| } |
| |
| setContext(context: string) { |
| switch (context) { |
| case "uut": |
| this.contextType = "UNITUNDERTEST"; |
| break; |
| case "te": |
| this.contextType = "TESTEQUIPMENT"; |
| break; |
| case "ts": |
| this.contextType = "TESTSEQUENCE"; |
| break; |
| } |
| } |
| |
| /** |
| * Listener method to change the context tab |
| * |
| * @param contextType |
| */ |
| refreshContextData(contextType: string) { |
| // revert before changing the context |
| this.reloadData(); |
| this.setContext(contextType); |
| this.editMode = false; |
| } |
| |
| private reloadData() { |
| this.sub.add( |
| this.navigatorService |
| .onSelectionChanged() |
| .pipe( |
| take(1), |
| tap((object) => this.handleSelectionChanged(object)), |
| ) |
| .subscribe(), |
| ); |
| } |
| |
| /** |
| * Listener method to change the node |
| * |
| * @param node |
| */ |
| refreshDetailData(node: Node) { |
| this.status = this.StatusLoading; |
| if (node != undefined) { |
| this.selectedNode = node; |
| this.editMode = false; |
| this.contextComponents = undefined; |
| this.treeNodes = undefined; |
| if (node.type.toLowerCase() === "measurement" || node.type.toLowerCase() === "teststep" || node.type.toLowerCase() === "test") { |
| this.loadContext(node); |
| } else { |
| this.status = this.StatusNoDescriptiveData; |
| } |
| if (node.type.toLowerCase() === "test") { |
| this.isTest = true; |
| } |
| } |
| } |
| |
| /** |
| * Load the context data for the provided node |
| * |
| * @param node |
| */ |
| private loadContext(node: Node) { |
| this._contextService.getContext(node).subscribe((components) => this.setComponents(components)); |
| } |
| |
| getContextAttribute( |
| contextDescribable: Node, |
| contextComponent: Node, |
| attribute: Attribute, |
| contextGroup?: ContextGroup, |
| contextType?: string, |
| ) { |
| return new ContextAttributeIdentifier(contextDescribable, contextComponent, attribute, contextGroup, contextType); |
| } |
| |
| getNodeClass(item: Node) { |
| return "icon " + item.type.toLowerCase(); |
| } |
| |
| /** |
| * Change the tree state to expanded or collapsed |
| * |
| * @param type |
| * @param expand |
| */ |
| toggleTreeNodeState(tt: TreeTable) { |
| this.isTreeTableExpanded = !this.isTreeTableExpanded; |
| const nodeArray = this.treeNodes[this.contextType]; |
| if (nodeArray != undefined) { |
| for (const node of nodeArray) { |
| this.recursiveChangeNodes(node, this.isTreeTableExpanded); |
| } |
| } |
| |
| // invoke table update when pressing the plus or minus buttons from the table header |
| tt.updateSerializedValue(); |
| tt.tableService.onUIUpdate(tt.value); |
| } |
| |
| /** |
| * Change the tree node state recursively |
| * |
| * @param node |
| * @param expand |
| */ |
| recursiveChangeNodes(node: TreeNode, expand: boolean) { |
| node.expanded = expand; |
| if (node.children != undefined && node.children.length > 0) { |
| for (const child of node.children) { |
| this.recursiveChangeNodes(child, expand); |
| } |
| } |
| } |
| |
| /** |
| * Get the nodes from the current context |
| * |
| * @param nodes |
| * @param parentId optional parent id which will get the child nodes |
| */ |
| getNodes(nodes: Node[], parentId: string) { |
| return nodes.filter((n) => this.nodeIsInCurrentContext(n, parentId)); |
| } |
| |
| nodeIsInCurrentContext(node: Node, parentId: string) { |
| const parentNodeId = node.relations != null && node.relations.length > 0 ? node.relations[0].parentId : null; |
| return (parentId === null && parentNodeId === null) || (parentId != null && parentNodeId != null && parentId === parentNodeId); |
| } |
| |
| /** |
| * Create a tree node based on the mdm entity |
| * |
| * @param node |
| * @param context |
| */ |
| createTreeNode(node: Node, children: Node[], contextType: string) { |
| const data = { |
| name: node.name, |
| // 'attribute': node, |
| header: true, |
| mimeType: this.extractMimeType(node), |
| optional: node.optional, |
| id: node.id, |
| }; |
| return <TreeNode>{ |
| label: node.name, |
| data: data, |
| children: this.createTreeChildren(node, children, data, contextType), |
| icon: this.getNodeClass(node), |
| expanded: true, |
| optional: node.optional, |
| defaultActive: node.defaultActive, |
| testStepSeriesVariable: node.testStepSeriesVariable, |
| }; |
| } |
| |
| /** |
| * Create the tree children nodes recursive based on the mdm attributes and subsequent mdm entities |
| * |
| * @param node the current node |
| * @param contexts the complete contexts |
| */ |
| createTreeChildren(node: Node, children: Node[], parentData: any, contextType: string) { |
| const list = node.attributes |
| .filter((attr) => attr.name !== "MimeType" && attr.name !== "Name" && attr.name !== "Sortindex") |
| .map((attribute) => { |
| const patchedAttribute = this.patchAttribute(node, attribute, contextType); |
| return { |
| data: { |
| name: attribute.name, |
| attribute: patchedAttribute, |
| header: false, |
| mimeType: this.extractMimeType(node), |
| orderedContextAttribute: this.getContextAttribute( |
| parentData, |
| this.selectedNode, |
| patchedAttribute, |
| ContextGroup.ORDERED, |
| contextType, |
| ), |
| measuredContextAttribute: this.getContextAttribute( |
| parentData, |
| this.selectedNode, |
| patchedAttribute, |
| ContextGroup.MEASURED, |
| contextType, |
| ), |
| }, |
| expanded: true, |
| } as TreeNode; |
| }); |
| |
| const tplCompRel = node.relations.find((r) => r.entityType === "TemplateComponent"); |
| if (tplCompRel && tplCompRel.ids.length > 0) { |
| const nodes = this.getNodesWithTplCompId(children, tplCompRel.ids[0]); |
| for (const n of nodes) { |
| list.push(this.createTreeNode(n, children, contextType)); |
| } |
| } |
| |
| return list; |
| } |
| |
| /** |
| * Searches for nodes which have a relation to the given TemplateComponent ID. |
| * @param nodes List with nodes to search |
| * @param tplCompId TemplateComponent ID |
| */ |
| getNodesWithTplCompId(nodes: Node[], tplCompId: string) { |
| return nodes.filter((n) => n.relations[0].parentId === tplCompId); |
| } |
| |
| private extractMimeType(node: Node) { |
| if (node != undefined) { |
| const attr = node.attributes.find((attr) => attr.name === "MimeType"); |
| if (attr) { |
| if (typeof attr.value === "string") { |
| return attr.value; |
| } else if (Array.isArray(attr.value) && attr.value.length === 1 && typeof attr.value[0] === "string") { |
| return attr.value[0]; |
| } else if (Array.isArray(attr.value) && attr.value.length > 1 && typeof attr.value[1] === "string") { |
| return attr.value[1]; |
| } |
| } |
| } |
| } |
| |
| /** |
| * Method to create a tree structure from the flat context entity and attribute map |
| * |
| * @param components |
| */ |
| mapComponents(components: Components) { |
| const list: { [key: string]: TreeNode[] } = {}; |
| if (components != undefined) { |
| for (const key of Object.keys(components)) { |
| const nodes: Node[] = this.getNodes(components[key], null); |
| if (nodes != undefined) { |
| list[key] = nodes.map((node) => this.createTreeNode(node, components[key], key)); |
| } |
| } |
| } |
| return list; |
| } |
| |
| /** |
| * Convert, complement or load attribute values |
| * |
| * @param attribute |
| */ |
| patchAttribute(node: Node, attribute: Attribute, contextType: string) { |
| if (attribute.dataType != null && attribute.dataType.length > 0) { |
| const val = attribute.value as any[]; |
| if ("FILE_RELATION" === attribute.dataType) { |
| if (val.length > 0) { |
| this.contextFileService |
| .getMDMFileExts(this.getContextAttribute(node, this.selectedNode, attribute, ContextGroup.ORDERED, contextType)) |
| .subscribe((mdmLinks) => (val[0] = mdmLinks)); |
| } |
| if (val.length > 1) { |
| this.contextFileService |
| .getMDMFileExts(this.getContextAttribute(node, this.selectedNode, attribute, ContextGroup.MEASURED, contextType)) |
| .subscribe((mdmLinks) => (val[1] = mdmLinks)); |
| } |
| } |
| } |
| |
| return attribute; |
| } |
| |
| /** ********************* |
| * Edit functions start |
| ********************** */ |
| |
| convertFixedDateStr(dt: string, convertForUI: boolean) { |
| let newDt = dt; |
| let sourceFormat = ""; |
| |
| /** |
| * Get the translation immediately |
| */ |
| sourceFormat = this.translateService.instant("details.mdm-detail-descriptive-data.transform-dateformat"); |
| |
| if (dt != null && dt.length > 0 && convertForUI && dt.indexOf("T") > -1) { |
| // for display purpose |
| const tmpDt = this.parseISOString(dt); |
| newDt = this.datePipe.transform(tmpDt, sourceFormat, "+0000"); |
| } else if (dt != null && dt.length > 0 && !convertForUI && dt.indexOf("T") === -1) { |
| // re-convert UI date to server date for persistence |
| if (newDt.indexOf("-") === -1) { |
| // find the date patterns dd, MM and yyyy and grab the according index positions |
| const startDay = sourceFormat.indexOf("d"); |
| const endDay = sourceFormat.lastIndexOf("d"); |
| const startMonth = sourceFormat.indexOf("M"); |
| const endMonth = sourceFormat.lastIndexOf("M"); |
| const startYear = sourceFormat.indexOf("y"); |
| const endYear = sourceFormat.lastIndexOf("y"); |
| // manually attach the time as toISOString() will not properly transform the winter/summer time |
| newDt = |
| dt.substring(startYear, endYear + 1) + "-" + dt.substring(startMonth, endMonth + 1) + "-" + dt.substring(startDay, endDay + 1); |
| if (dt.indexOf(" ") > -1) { |
| // use the provided timestamp |
| newDt = newDt + "T" + dt.substring(dt.indexOf(" ") + 1) + ":00Z"; |
| } else { |
| newDt = newDt + "T00:00:00Z"; |
| } |
| if (newDt.length !== 20) { |
| newDt = ""; |
| } |
| } |
| } |
| return newDt; |
| } |
| |
| /** |
| * Fixed parsing for ISO date string |
| * |
| * @param s |
| */ |
| parseISOString(s: string) { |
| const b: any[] = s.split(/\D+/); |
| return new Date(Date.UTC(b[0], --b[1], b[2], b[3], b[4], b[5], b[6])); |
| } |
| |
| /** |
| * Send the changed data to the backend |
| * |
| * @param node |
| * @param type |
| */ |
| private putContext(node: Node, type: string) { |
| if (type === null) { |
| return; |
| } |
| this.status = this.StatusSaving; |
| this.treeNodes = undefined; |
| |
| const data = new Context(); |
| data.ordered = new Components(); |
| data.ordered[type] = this.getNodesWithUpdatedContent(this.contextComponents[type] as Node[], true); |
| |
| data.measured = new Components(); |
| data.measured[type] = this.getNodesWithUpdatedContent(this.contextComponents[type] as Node[], false); |
| |
| if (data.ordered[type].length === 0 && data.measured[type].length === 0) { |
| this.treeNodes = this.mapComponents(this.contextComponents); |
| return; |
| } |
| |
| // clear for status display |
| this.contextComponents = undefined; |
| |
| this._contextService.putContext(node, data).subscribe( |
| (components) => { |
| this.setComponents(components); |
| }, |
| (error) => |
| this.notificationService.notifyError( |
| this.translateService.instant("details.mdm-detail-descriptive-data.err-cannot-load-context"), |
| error, |
| ), |
| ); |
| } |
| |
| onEdit(event: Event) { |
| event.stopPropagation(); |
| this.editMode = true; |
| this.tmpTreeNodes = JSON.parse(JSON.stringify(this.contextComponents[this.contextType])); |
| } |
| |
| onCancelEdit(event: Event) { |
| event.stopPropagation(); |
| this.editMode = false; |
| this.isTreeTableExpanded = true; |
| this.reloadData(); |
| } |
| |
| onSaveChanges(event: Event) { |
| event.stopPropagation(); |
| this.editMode = false; |
| this.isTreeTableExpanded = true; |
| this.putContext(this.selectedNode, this.contextType); |
| this.tmpTreeNodes = undefined; |
| } |
| |
| onAddTempComponent(event: Event) { |
| event.stopPropagation(); |
| this.entityCreatorService.getTemplateTestStep(this.selectedNode).subscribe((r) => { |
| if (r.length > 0 && r[0].relations.filter((rel) => rel.contextType === this.contextType).length > 0) { |
| this.entityCreatorService.getTemplateComponents(r[0], this.contextType).subscribe((co) => { |
| this.setTplComponents(co); |
| this.showTplComponents = true; |
| }); |
| } else { |
| this.notificationService.notifyInfo( |
| this.translateService.instant("details.mdm-detail-descriptive-data.info-no-tempComp"), |
| this.translateService.instant("details.mdm-detail-descriptive-data.msg-no_tplComp"), |
| ); |
| } |
| }); |
| } |
| |
| setTplComponents(entities: MDMEntity[]) { |
| if (entities !== undefined) { |
| let cIds: string[] = []; |
| this.contextComponents[this.contextType].forEach((attr) => { |
| if (attr.attributes[0].value.length === 2 && attr.attributes[0].value[0] !== undefined) { |
| attr.relations.forEach((rel) => { |
| if (rel.contextType === this.contextType) { |
| cIds = cIds.concat(rel.ids); |
| } |
| }); |
| } |
| }); |
| this.tplComponents = []; |
| entities |
| .filter( |
| (ent) => |
| cIds.filter((id) => id === ent.id)[0] === undefined && |
| ent.attributes.filter((attr) => attr.name === "Optional" && attr.value === "true"), |
| ) |
| .forEach((entity) => this.tplComponents.push(entity)); |
| } |
| } |
| |
| onSaveSelection(event: Event) { |
| event.stopPropagation(); |
| const compNames: string[] = []; |
| this.selectedTplComponent.forEach((ent) => compNames.push(ent.name)); |
| this._contextService |
| .addNewComponent(this.selectedNode.sourceName, this.contextType, this.selectedNode.id, compNames, this.selectedContext) |
| .subscribe((components) => this.setComponents(components)); |
| this.selectedTplComponent = []; |
| this.showTplComponents = false; |
| } |
| |
| onDeleteComponent(event: Event, name: string) { |
| event.stopPropagation(); |
| this.hasOrdered = this.contextComponents[this.contextType].filter((cmp) => cmp.name === name)[0].attributes[0].value[0] !== undefined; |
| this.hasMeasured = this.contextComponents[this.contextType].filter((cmp) => cmp.name === name)[0].attributes[0].value.length === 2; |
| this.selectedContext = []; |
| this.selectedContextComponentName = name; |
| this.ctxCompName = { ctxName: name }; |
| this.showConfirmDelete = true; |
| } |
| |
| onNotDeleteSelection(event: Event) { |
| event.stopPropagation(); |
| this.showConfirmDelete = false; |
| } |
| |
| onDeleteSelection(event: Event) { |
| if (this.selectedContext?.length > 0) { |
| event.stopPropagation(); |
| this._contextService |
| .deleteComponent( |
| this.selectedNode.sourceName, |
| this.contextType, |
| this.selectedNode.id, |
| this.selectedContextComponentName, |
| this.selectedContext, |
| ) |
| .subscribe((components) => this.loadContext(this.selectedNode)); |
| this.showConfirmDelete = false; |
| } |
| } |
| |
| private setComponents(components: Components) { |
| if (components["UNITUNDERTEST"] != undefined || components["TESTEQUIPMENT"] != undefined || components["TESTSEQUENCE"] != undefined) { |
| this.contextComponents = components; |
| this.treeNodes = this.mapComponents(components); |
| } else { |
| this.status = this.StatusNoDescriptiveData; |
| } |
| } |
| /** |
| * Get the updated nodes from the current context |
| * |
| * @param contextComponents |
| * @param type the context type |
| * @param ordered true if ordered data, false if measured data |
| */ |
| getNodesWithUpdatedContent(contextComponents: Node[], ordered: boolean) { |
| const list = []; |
| for (const component of contextComponents) { |
| const attrs = []; |
| for (const cAttribute of component.attributes) { |
| const attr = new Attribute(); |
| let addAttr = true; |
| attr.dataType = cAttribute.dataType; |
| attr.name = cAttribute.name; |
| attr.unit = cAttribute.unit; |
| attr.value = ""; |
| |
| if (ordered && cAttribute.value instanceof Array && cAttribute.value.length > 0) { |
| attr.value = cAttribute.value[0]; |
| } else if (!ordered && cAttribute.value instanceof Array && cAttribute.value.length > 1) { |
| attr.value = cAttribute.value[1]; |
| } |
| // lookup new value from treenodes |
| if (attr.dataType === "BOOLEAN" && attr.value != null && attr.value.toString().length > 0) { |
| attr.value = attr.value.toString() === "true" ? "1" : "0"; |
| } |
| |
| // FILE_LINK_SEQUENCE is saved separately |
| if (attr.dataType === "FILE_LINK_SEQUENCE" || attr.dataType === "FILE_LINK" || attr.dataType === "FILE_RELATION") { |
| addAttr = false; |
| } |
| |
| if (addAttr) { |
| if (this.isAttributeValueModified(attr, component, ordered)) { |
| attrs.push(attr); |
| } |
| } |
| } |
| // un-merged list |
| if (attrs.length > 0) { |
| const c = JSON.parse(JSON.stringify(component)); |
| c.attributes = attrs; |
| list.push(c); |
| } |
| } |
| return list; |
| } |
| |
| private isAttributeValueModified(attr: Attribute, component: Node, ordered: boolean) { |
| for (const tmpNode of this.tmpTreeNodes) { |
| if (tmpNode.name === component.name) { |
| for (const attribute of tmpNode.attributes) { |
| if (attribute.name === attr.name && attribute.value instanceof Array) { |
| const orgValue = ordered ? attribute.value[0] : attribute.value.length > 1 ? attribute.value[1] : undefined; |
| if (orgValue != undefined) { |
| if (attr.dataType === "BOOLEAN") { |
| // server value = true or false, UI value = 1 or 0 |
| return ( |
| (attr.value === "0" && orgValue === "true") || |
| (attr.value === "1" && orgValue === "false") || |
| (attr.value !== "" && orgValue === "") |
| ); |
| } else { |
| // plain comparison |
| return attr.value !== orgValue; |
| } |
| } |
| return false; |
| } |
| } |
| } |
| } |
| } |
| |
| /** ********************* |
| * Edit functions end |
| ********************** */ |
| |
| handleEditMode(node: Node) { |
| if (node != undefined) { |
| if (this.editAllowed[node.sourceName] != undefined) { |
| this.canEdit4Env = this.canEdit && this.editAllowed[node.sourceName]; |
| } else { |
| this.systemParameterService.findSystemParameter(node.sourceName, "org.eclipse.mdm.detailview.editable").subscribe((values) => { |
| if (values.length === 1) { |
| this.editAllowed[node.sourceName] = values[0].attributes.find((attr) => attr.name === "Value").value === "false" ? false : true; |
| } else { |
| this.editAllowed[node.sourceName] = true; |
| } |
| this.canEdit4Env = this.canEdit && this.editAllowed[node.sourceName]; |
| }); |
| } |
| } |
| } |
| } |