| /******************************************************************************** |
| * Copyright (c) 2015-2020 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 { Component, Input, Output, EventEmitter, OnInit, ViewChild } from '@angular/core'; |
| import { DomSanitizer } from '@angular/platform-browser'; |
| import { ModalDirective } from 'ngx-bootstrap'; |
| |
| import { BasketService, Basket} from './basket.service'; |
| |
| import { MDMItem} from '../core/mdm-item'; |
| import { OverwriteDialogComponent } from '../core/overwrite-dialog.component'; |
| import { NavigatorService } from '../navigator/navigator.service'; |
| import { Node} from '../navigator/node'; |
| import { NodeService } from '../navigator/node.service'; |
| import { TableviewComponent } from '../tableview/tableview.component'; |
| import { ViewComponent } from '../tableview/view.component'; |
| import { View } from '../tableview/tableview.service'; |
| import { QueryService, SearchResult, Row } from '../tableview/query.service'; |
| |
| import { deserialize } from 'class-transformer'; |
| |
| import {MenuItem} from 'primeng/primeng'; |
| import * as FileSaver from 'file-saver'; |
| |
| import {MDMNotificationService} from '../core/mdm-notification.service'; |
| |
| import { TranslateService } from '@ngx-translate/core'; |
| import { streamTranslate, TRANSLATE } from '../core/mdm-core.module'; |
| import {ConfirmationService} from 'primeng/api'; |
| import { Observable } from 'rxjs/Observable'; |
| import { forkJoin, throwError } from 'rxjs'; |
| import { map, flatMap } from 'rxjs/operators'; |
| |
| @Component({ |
| selector: 'mdm-basket', |
| templateUrl: 'mdm-basket.component.html', |
| styleUrls: ['./mdm-basket.component.css'], |
| }) |
| export class MDMBasketComponent implements OnInit { |
| @Output() onActive = new EventEmitter<Node>(); |
| @Output() onSelect = new EventEmitter<Node>(); |
| @Input() activeNode: Node; |
| |
| exportableTypes = ['Project', 'Pool', 'Test', 'TestStep', 'Measurement']; |
| |
| basketName = ''; |
| basketContent: SearchResult = new SearchResult(); |
| basket = 'Warenkorb'; |
| |
| baskets: Basket[] = []; |
| selectedBasket: Basket; |
| environments: Node[]; |
| |
| public selectedRow: string; |
| public lazySelectedRow: string; |
| |
| _currentChange: any; |
| |
| contextMenuItems: MenuItem[] = [ |
| { label: 'Remove selection from basket', icon: 'fa fa-times', command: (event) => this.removeSelected() } |
| ]; |
| |
| fixedLaunchers: MenuItem[] = [ |
| {label: 'ATFX', command: (event) => this.saveBasketAsATFX(event.originalEvent)} |
| ]; |
| launchers: MenuItem[] = []; |
| allowedExtensions: string[] = []; |
| |
| @ViewChild(TableviewComponent) |
| tableViewComponent: TableviewComponent; |
| @ViewChild(ViewComponent) |
| viewComponent: ViewComponent; |
| @ViewChild('lgLoadModal') |
| childLoadModal: ModalDirective; |
| @ViewChild('lgSaveModal') |
| childSaveModal: ModalDirective; |
| @ViewChild(OverwriteDialogComponent) |
| overwriteDialogComponent: OverwriteDialogComponent; |
| |
| constructor(private _basketService: BasketService, |
| private queryService: QueryService, |
| private navigatorService: NavigatorService, |
| private sanitizer: DomSanitizer, |
| private nodeService: NodeService, |
| private notificationService: MDMNotificationService, |
| private translateService: TranslateService, |
| private confirmationService: ConfirmationService) { |
| } |
| |
| removeSelected() { |
| this._basketService.remove(this.tableViewComponent.menuSelectedRow.getItem()); |
| } |
| |
| ngOnInit() { |
| streamTranslate(this.translateService, TRANSLATE('basket.mdm-basket.remove-selection')).subscribe( |
| (msg: string) => this.contextMenuItems[0].label = msg); |
| |
| this.nodeService.getRootNodes().subscribe( |
| envs => this.environments = envs, |
| error => this.translateService.getTranslation(TRANSLATE('basket.mdm-basket.err-cannot-load-sources')) |
| .subscribe(msg => this.notificationService.notifyError(msg, error) |
| )); |
| |
| this._basketService.getFileExtensions() |
| .map(launchers => <{ menuItems: MenuItem[], extensions: string[] }>{ |
| menuItems: launchers.map((l: { label: string; extension: string; }) => <MenuItem> { |
| label: l.label, |
| command: (event) => this.saveBasketWithExtension(event.originalEvent, l.extension) }), |
| extensions: launchers.map((l: { extension: string; }) => '.' + l.extension).join(',') |
| }) |
| .subscribe(({menuItems, extensions}) => { |
| this.launchers = this.fixedLaunchers.concat(menuItems); |
| this.allowedExtensions = [ 'application/xml' ].concat(extensions); |
| }); |
| |
| this.setItems(this._basketService.items); |
| |
| this._basketService.itemsAdded$.subscribe( |
| items => this.addItems(items), |
| error => this.translateService.getTranslation(TRANSLATE('basket.mdm-basket.err-cannot-add-selection')) |
| .subscribe(msg => this.notificationService.notifyError(msg, error) |
| )); |
| |
| this._basketService.itemsRemoved$.subscribe( |
| items => this.removeItems(items), |
| error => this.translateService.getTranslation(TRANSLATE('basket.mdm-basket.err-cannot-remove-selection')) |
| .subscribe(msg => this.notificationService.notifyError(msg, error) |
| )); |
| |
| this.viewComponent.viewChanged$.subscribe( |
| () => this.setItems(this._basketService.items), |
| error => this.translateService.getTranslation(TRANSLATE('basket.mdm-basket.err-cannot-select-view')) |
| .subscribe(msg => this.notificationService.notifyError(msg, error) |
| )); |
| } |
| |
| onViewClick(e: Event) { |
| e.stopPropagation(); |
| } |
| |
| setItems(items: MDMItem[]) { |
| this.basketContent.rows = []; |
| this.addItems(items); |
| } |
| |
| addItems(items: MDMItem[]) { |
| if (this.viewComponent.selectedView) { |
| this.queryService.queryItems(items, this.viewComponent.selectedView.columns.map(c => c.type + '.' + c.name)) |
| .forEach(q => |
| q.subscribe( |
| r => this.addData(r.rows), |
| error => this.notificationService.notifyError( |
| this.translateService.instant('basket.mdm-basket.err-cannot-add-items-to-shopping-basket'), error)) |
| ); |
| } |
| } |
| |
| removeItems(items: MDMItem[]) { |
| // copy and reasign to get new object refference triggering angular change detection in tableview. |
| let tmp = Object.assign({}, this.basketContent); |
| items.forEach(item => |
| tmp.rows = tmp.rows.filter(row => |
| !(row.source === item.source && row.type === item.type && row.id === item.id))); |
| this.basketContent = tmp; |
| } |
| |
| setView(view: View) { |
| console.log('setView', view); |
| } |
| |
| saveBasket(e: Event) { |
| if (e) { |
| e.stopPropagation(); |
| } |
| if (this.baskets.find(f => f.name === this.basketName) != undefined) { |
| this.childSaveModal.hide(); |
| this.overwriteDialogComponent.showOverwriteModal( |
| this.translateService.instant('basket.mdm-basket.item-save-shopping-basket')).subscribe( |
| needSave => this.saveBasket2(needSave), |
| error => { |
| this.saveBasket2(false); |
| this.notificationService.notifyError(this.translateService.instant('basket.mdm-basket.err-save-shopping-basket'), error); |
| }); |
| } else { |
| this.saveBasket2(true); |
| } |
| } |
| |
| saveBasket2(save: boolean) { |
| if (save) { |
| this._basketService.saveBasketWithName(this.basketName); |
| this.childSaveModal.hide(); |
| } else { |
| this.childSaveModal.show(); |
| } |
| } |
| |
| loadBasket(basket?: Basket) { |
| if (basket == undefined) { |
| basket = this.selectedBasket; |
| if (this.selectedBasket == undefined) { |
| return; |
| } |
| } |
| this.basketName = basket.name; |
| this.setItems(basket.items); |
| this._basketService.setItems(basket.items); |
| this.childLoadModal.hide(); |
| } |
| |
| loadBaskets() { |
| this._basketService.getBaskets().subscribe( |
| baskets => this.baskets = baskets, |
| error => this.notificationService.notifyError( |
| this.translateService.instant('basket.mdm-basket.err-cannot-load-shopping-basket'), error)); |
| } |
| |
| clearBasket(e: Event) { |
| e.stopPropagation(); |
| this.basketContent = new SearchResult(); |
| this._basketService.removeAll(); |
| this.basketName = ''; |
| } |
| |
| showLoadModal(e: Event) { |
| e.stopPropagation(); |
| this.selectedBasket = undefined; |
| this.loadBaskets(); |
| this.childLoadModal.show(); |
| } |
| |
| showSaveModal(e: Event) { |
| e.stopPropagation(); |
| this.loadBaskets(); |
| this.basketName = this.selectedBasket ? this.selectedBasket.name : ''; |
| this.childSaveModal.show(); |
| } |
| |
| stopEvent(event: Event) { |
| event.stopPropagation(); |
| } |
| |
| saveBasketWithExtension(event: any, extension: string) { |
| if (event) { |
| event.stopPropagation(); |
| } |
| let downloadContent = new Basket(this.basketName, this._basketService.getItems()); |
| |
| this._basketService.getBasketAsXml(downloadContent) |
| .map(xml => new Blob([xml], { type: 'application/xml' })) |
| .subscribe(blob => this.saveAsFile(blob, extension, 'shoppingbasket')); |
| } |
| |
| isExportable(item: MDMItem) { |
| return this.exportableTypes.indexOf(item.type) >= 0; |
| } |
| |
| saveBasketAsATFX(event: any) { |
| if (event) { |
| event.stopPropagation(); |
| } |
| const notExportableItems = this._basketService.getItems().filter(i => !this.isExportable(i)); |
| const exportableItems = this._basketService.getItems().filter(i => this.isExportable(i)).map(i => i.type).join(', '); |
| |
| if (exportableItems.length === 0) { |
| // No exportable item in shopping basket -> show message and cancel export |
| const msg = this.translateService.instant('basket.mdm-basket.msg-atfx-only-supports-types', |
| { types: this.exportableTypes.join(', ') }) |
| + ' ' + this.translateService.instant('basket.mdm-basket.msg-only-not-exportable-types-selected', |
| { types: notExportableItems.map(i => i.type).join(', ') }); |
| |
| this.confirmationService.confirm({ |
| message: msg, |
| acceptLabel: 'OK', |
| rejectVisible: false, |
| }); |
| } else if (notExportableItems.length > 0) { |
| // shopping basket contains not exportable items -> show message and ask if export should continue |
| const msg = this.translateService.instant('basket.mdm-basket.msg-atfx-only-supports-types', |
| { types: this.exportableTypes.join(', ') }) |
| + ' ' + this.translateService.instant('basket.mdm-basket.msg-not-exportable-types-continue', |
| { types: exportableItems }); |
| |
| this.confirmationService.confirm({ |
| message: msg, |
| acceptLabel: 'Yes', |
| rejectLabel: 'No', |
| icon: 'pi pi-question-circle', |
| accept: () => this.exportItemsToAtfx(this._basketService.getItems().filter(i => this.isExportable(i))), |
| rejectVisible: true, |
| }); |
| } else { |
| this.exportItemsToAtfx(this._basketService.getItems()); |
| } |
| } |
| |
| exportItemsToAtfx(basket: MDMItem[]) { |
| let downloadContent = new Basket(this.basketName, basket); |
| |
| this._basketService.getBasketAsAtfx(downloadContent) |
| .map(atfx => new Blob([atfx], { type: 'application/xml' })) |
| .subscribe(atfx => this.saveAsFile(atfx, '.atfx')); |
| } |
| |
| saveAsFile(blob: Blob, fileExtension: string, defaultFilename = 'export') { |
| let name = 'export.' + fileExtension; |
| if (this.basketName && this.basketName.trim().length !== 0) { |
| name = this.basketName + '.' + fileExtension; |
| } |
| FileSaver.saveAs(blob, name); |
| } |
| |
| onUploadChange(event: Event) { |
| this._currentChange = event.target; |
| this.onUploadEvent(this._currentChange); |
| } |
| |
| onUploadClick(e: Event) { |
| e.stopPropagation(); |
| } |
| |
| toggleSelect(basket: Basket) { |
| this.selectedBasket = this.selectedBasket === basket ? undefined : basket; |
| } |
| |
| isDownloadDisabled() { |
| return this.basketContent.rows.length <= 0; |
| } |
| |
| getSaveBtnTitle() { |
| return this.basketName |
| ? TRANSLATE('basket.mdm-basket.tooltip-save-shopping-basket') |
| : TRANSLATE('basket.mdm-basket.tooltip-no-name-set'); |
| } |
| |
| private onUploadEvent(fileInput: any) { |
| if (fileInput.files[0]) { |
| const readFile = (blob: Blob): Observable<string> => Observable.create(o => { |
| if (!(blob instanceof Blob)) { |
| o.error(new Error('Parameter `blob` must be an instance of Blob.')); |
| return; |
| } |
| |
| const fileReader = new FileReader(); |
| fileReader.onload = () => o.next(fileReader.result); |
| fileReader.onloadend = () => o.complete(); |
| fileReader.onabort = err => o.error(err); |
| fileReader.onerror = err => o.error(err); |
| return fileReader.readAsText(blob); |
| }); |
| |
| readFile(fileInput.files[0]).pipe( |
| flatMap(xml => this.parseXmlShoppingBasket(xml)) |
| ).subscribe( |
| basket => this.loadBasket(basket), |
| err => this.notificationService.notifyWarn('Could not load shopping basket from file.', err) |
| ); |
| } |
| } |
| |
| private parseXmlShoppingBasket(xml: string) { |
| let oParser = new DOMParser(); |
| let oDOM = oParser.parseFromString(xml, 'application/xml'); |
| // print the name of the root element or error message |
| if (oDOM.documentElement.tagName !== 'shoppingbasket') { |
| if (oDOM.documentElement.innerText) { |
| return throwError(new Error('Error while parsing shopping basket: ' + oDOM.documentElement.innerText)); |
| } else { |
| return throwError(new Error('Error while parsing shopping basket: XML is missing shoppingbasket root tag')); |
| } |
| } |
| |
| // load the name of the shopping basket |
| let name = ''; |
| let nameTags = oDOM.documentElement.getElementsByTagName('name'); |
| if (nameTags.length > 0) { |
| name = nameTags[0].textContent; |
| } |
| |
| // load the business objectes defined by the resturi values in the xml file |
| let items: Observable<Node[]>[] = []; |
| let uris = oDOM.documentElement.getElementsByTagName('resturi'); |
| if (uris.length === 0) { |
| return throwError(new Error('No resturis found!')); |
| } |
| for (let uri of <any>uris) { |
| let url = uri.textContent; |
| items.push(this.nodeService.getNodesByAbsoluteUrl(url)); |
| } |
| |
| // map business objects to MDMItems and load basket |
| return forkJoin(items).pipe( |
| map(n => n.map(x => new MDMItem(x[0].sourceName, x[0].type, x[0].id))), |
| map(i => new Basket(name, i))); |
| } |
| |
| private addData(rows: Row[]) { |
| this.basketContent.rows.push(... rows); |
| // copy and reasign to get new object refference triggering angular change detection in tableview. |
| this.basketContent = Object.assign({}, this.basketContent); |
| } |
| |
| onRowSelect(e: any) { |
| if (this.lazySelectedRow !== e.data) { |
| this.selectedRow = e.data; |
| this.basketName = e.data.name; |
| } else { |
| this.selectedRow = undefined; |
| this.basketName = ''; |
| } |
| this.lazySelectedRow = this.selectedRow; |
| } |
| } |