|
|
|
@ -22,7 +22,6 @@ import { AppState } from '@core/core.state'; |
|
|
|
import { ActionNotificationShow } from '@core/notification/notification.actions'; |
|
|
|
import { BreakpointId, Dashboard, DashboardLayoutId } from '@shared/models/dashboard.models'; |
|
|
|
import { deepClone, guid, isDefined, isNotEmptyStr, isObject, isString, isUndefined } from '@core/utils'; |
|
|
|
import { WINDOW } from '@core/services/window.service'; |
|
|
|
import { DOCUMENT } from '@angular/common'; |
|
|
|
import { |
|
|
|
AliasesInfo, |
|
|
|
@ -100,8 +99,7 @@ type SupportEntityResources = 'includeResourcesInExportWidgetTypes' | 'includeRe |
|
|
|
@Injectable() |
|
|
|
export class ImportExportService { |
|
|
|
|
|
|
|
constructor(@Inject(WINDOW) private window: Window, |
|
|
|
@Inject(DOCUMENT) private document: Document, |
|
|
|
constructor(@Inject(DOCUMENT) private document: Document, |
|
|
|
private store: Store<AppState>, |
|
|
|
private translate: TranslateService, |
|
|
|
private dashboardService: DashboardService, |
|
|
|
@ -177,9 +175,7 @@ export class ImportExportService { |
|
|
|
public exportCalculatedField(calculatedFieldId: string): void { |
|
|
|
this.calculatedFieldsService.getCalculatedFieldById(calculatedFieldId).subscribe({ |
|
|
|
next: (calculatedField) => { |
|
|
|
let name = calculatedField.name; |
|
|
|
name = name.toLowerCase().replace(/\W/g, '_'); |
|
|
|
this.exportToPc(this.prepareCalculatedFieldExport(calculatedField), name); |
|
|
|
this.exportToPc(this.prepareCalculatedFieldExport(calculatedField), calculatedField.name, true); |
|
|
|
}, |
|
|
|
error: (e) => { |
|
|
|
this.handleExportError(e, 'calculated-fields.export-failed-error'); |
|
|
|
@ -200,9 +196,7 @@ export class ImportExportService { |
|
|
|
this.updateUserSettingsIncludeResourcesIfNeeded(includeResources, result.include, 'includeResourcesInExportDashboard'); |
|
|
|
this.dashboardService.exportDashboard(dashboardId, result.include).subscribe({ |
|
|
|
next: (dashboard) => { |
|
|
|
let name = dashboard.title; |
|
|
|
name = name.toLowerCase().replace(/\W/g, '_'); |
|
|
|
this.exportToPc(this.prepareDashboardExport(dashboard), name); |
|
|
|
this.exportToPc(this.prepareDashboardExport(dashboard), dashboard.title, true); |
|
|
|
}, |
|
|
|
error: (e) => { |
|
|
|
this.handleExportError(e, 'dashboard.export-failed-error'); |
|
|
|
@ -261,9 +255,8 @@ export class ImportExportService { |
|
|
|
widgetTitle: string, breakpoint: BreakpointId) { |
|
|
|
const widgetItem = this.itembuffer.prepareWidgetItem(dashboard, sourceState, sourceLayout, widget, breakpoint); |
|
|
|
const widgetDefaultName = this.widgetService.getWidgetInfoFromCache(widget.typeFullFqn).widgetName; |
|
|
|
let fileName = widgetDefaultName + (isNotEmptyStr(widgetTitle) ? `_${widgetTitle}` : ''); |
|
|
|
fileName = fileName.toLowerCase().replace(/\W/g, '_'); |
|
|
|
this.exportToPc(this.prepareExport(widgetItem), fileName); |
|
|
|
const fileName = widgetDefaultName + (isNotEmptyStr(widgetTitle) ? `_${widgetTitle}` : ''); |
|
|
|
this.exportToPc(this.prepareExport(widgetItem), fileName, true); |
|
|
|
} |
|
|
|
|
|
|
|
public importWidget(dashboard: Dashboard, targetState: string, |
|
|
|
@ -360,9 +353,7 @@ export class ImportExportService { |
|
|
|
this.updateUserSettingsIncludeResourcesIfNeeded(includeResources, result.include, 'includeResourcesInExportWidgetTypes'); |
|
|
|
this.widgetService.exportWidgetType(widgetTypeId, result.include).subscribe({ |
|
|
|
next: (widgetTypeDetails) => { |
|
|
|
let name = widgetTypeDetails.name; |
|
|
|
name = name.toLowerCase().replace(/\W/g, '_'); |
|
|
|
this.exportToPc(this.prepareExport(widgetTypeDetails), name); |
|
|
|
this.exportToPc(this.prepareExport(widgetTypeDetails), widgetTypeDetails.name, true); |
|
|
|
}, |
|
|
|
error: (e) => { |
|
|
|
this.handleExportError(e, 'widget-type.export-failed-error'); |
|
|
|
@ -440,7 +431,7 @@ export class ImportExportService { |
|
|
|
public exportEntity(entityData: VersionedEntity): void { |
|
|
|
const id = (entityData as EntityInfoData).id ?? (entityData as RuleChainMetaData).ruleChainId; |
|
|
|
let fileName = (entityData as EntityInfoData).name; |
|
|
|
let preparedData; |
|
|
|
let preparedData: any; |
|
|
|
switch (id.entityType) { |
|
|
|
case EntityType.DEVICE_PROFILE: |
|
|
|
case EntityType.ASSET_PROFILE: |
|
|
|
@ -511,9 +502,7 @@ export class ImportExportService { |
|
|
|
for (const widgetTypeDetails of widgetTypesDetails) { |
|
|
|
widgetsBundleItem.widgetTypes.push(this.prepareExport(widgetTypeDetails)); |
|
|
|
} |
|
|
|
let name = widgetsBundle.title; |
|
|
|
name = name.toLowerCase().replace(/\W/g, '_'); |
|
|
|
this.exportToPc(widgetsBundleItem, name); |
|
|
|
this.exportToPc(widgetsBundleItem, widgetsBundle.title, true); |
|
|
|
}, |
|
|
|
error: (e) => { |
|
|
|
this.handleExportError(e, 'widgets-bundle.export-failed-error'); |
|
|
|
@ -528,9 +517,7 @@ export class ImportExportService { |
|
|
|
widgetsBundle: this.prepareExport(widgetsBundle), |
|
|
|
widgetTypeFqns |
|
|
|
}; |
|
|
|
let name = widgetsBundle.title; |
|
|
|
name = name.toLowerCase().replace(/\W/g, '_'); |
|
|
|
this.exportToPc(widgetsBundleItem, name); |
|
|
|
this.exportToPc(widgetsBundleItem, widgetsBundle.title, true); |
|
|
|
}, |
|
|
|
error: (e) => { |
|
|
|
this.handleExportError(e, 'widgets-bundle.export-failed-error'); |
|
|
|
@ -662,11 +649,9 @@ export class ImportExportService { |
|
|
|
private onRuleChainExported() { |
|
|
|
return { |
|
|
|
next: (ruleChainExport: RuleChainImport) => { |
|
|
|
let name = ruleChainExport.ruleChain.name; |
|
|
|
name = name.toLowerCase().replace(/\W/g, '_'); |
|
|
|
this.exportToPc(ruleChainExport, name); |
|
|
|
this.exportToPc(ruleChainExport, ruleChainExport.ruleChain.name, true); |
|
|
|
}, |
|
|
|
error: (e) => { |
|
|
|
error: (e: any) => { |
|
|
|
this.handleExportError(e, 'rulechain.export-failed-error'); |
|
|
|
} |
|
|
|
}; |
|
|
|
@ -747,9 +732,7 @@ export class ImportExportService { |
|
|
|
public exportDeviceProfile(deviceProfileId: string) { |
|
|
|
this.deviceProfileService.exportDeviceProfile(deviceProfileId).subscribe({ |
|
|
|
next: (deviceProfile) => { |
|
|
|
let name = deviceProfile.name; |
|
|
|
name = name.toLowerCase().replace(/\W/g, '_'); |
|
|
|
this.exportToPc(this.prepareProfileExport(deviceProfile), name); |
|
|
|
this.exportToPc(this.prepareProfileExport(deviceProfile), deviceProfile.name, true); |
|
|
|
}, |
|
|
|
error: (e) => { |
|
|
|
this.handleExportError(e, 'device-profile.export-failed-error'); |
|
|
|
@ -776,9 +759,7 @@ export class ImportExportService { |
|
|
|
public exportAssetProfile(assetProfileId: string) { |
|
|
|
this.assetProfileService.exportAssetProfile(assetProfileId).subscribe({ |
|
|
|
next: (assetProfile) => { |
|
|
|
let name = assetProfile.name; |
|
|
|
name = name.toLowerCase().replace(/\W/g, '_'); |
|
|
|
this.exportToPc(this.prepareProfileExport(assetProfile), name); |
|
|
|
this.exportToPc(this.prepareProfileExport(assetProfile), assetProfile.name, true); |
|
|
|
}, |
|
|
|
error: (e) => { |
|
|
|
this.handleExportError(e, 'asset-profile.export-failed-error'); |
|
|
|
@ -805,9 +786,7 @@ export class ImportExportService { |
|
|
|
public exportTenantProfile(tenantProfileId: string) { |
|
|
|
this.tenantProfileService.getTenantProfile(tenantProfileId).subscribe({ |
|
|
|
next: (tenantProfile) => { |
|
|
|
let name = tenantProfile.name; |
|
|
|
name = name.toLowerCase().replace(/\W/g, '_'); |
|
|
|
this.exportToPc(this.prepareProfileExport(tenantProfile), name); |
|
|
|
this.exportToPc(this.prepareProfileExport(tenantProfile), tenantProfile.name, true); |
|
|
|
}, |
|
|
|
error: (e) => { |
|
|
|
this.handleExportError(e, 'tenant-profile.export-failed-error'); |
|
|
|
@ -842,7 +821,7 @@ export class ImportExportService { |
|
|
|
return cellData; |
|
|
|
} |
|
|
|
|
|
|
|
public exportCsv(data: {[key: string]: any}[], filename: string) { |
|
|
|
public exportCsv(data: {[key: string]: any}[], filename: string, normalizeFileName = false) { |
|
|
|
let colsHead: string; |
|
|
|
let colsData: string; |
|
|
|
if (data && data.length) { |
|
|
|
@ -857,18 +836,18 @@ export class ImportExportService { |
|
|
|
colsData = ''; |
|
|
|
} |
|
|
|
const csvData = `${colsHead}\n${colsData}`; |
|
|
|
this.downloadFile(csvData, filename, CSV_TYPE); |
|
|
|
this.downloadFile(csvData, filename, CSV_TYPE, normalizeFileName); |
|
|
|
} |
|
|
|
|
|
|
|
public exportText(data: string | Array<string>, filename: string) { |
|
|
|
public exportText(data: string | Array<string>, filename: string, normalizeFileName = false) { |
|
|
|
let content = data; |
|
|
|
if (Array.isArray(data)) { |
|
|
|
content = data.join('\n'); |
|
|
|
} |
|
|
|
this.downloadFile(content, filename, TEXT_TYPE); |
|
|
|
this.downloadFile(content, filename, TEXT_TYPE, normalizeFileName); |
|
|
|
} |
|
|
|
|
|
|
|
public exportJSZip(data: object, filename: string): Observable<void> { |
|
|
|
public exportJSZip(data: object, filename: string, normalizeFileName = false): Observable<void> { |
|
|
|
const exportJsSubjectSubject = new Subject<void>(); |
|
|
|
import('jszip').then((JSZip) => { |
|
|
|
try { |
|
|
|
@ -880,9 +859,9 @@ export class ImportExportService { |
|
|
|
} |
|
|
|
} |
|
|
|
jsZip.generateAsync({type: 'blob'}).then(content => { |
|
|
|
this.downloadFile(content, filename, ZIP_TYPE); |
|
|
|
this.downloadFile(content, filename, ZIP_TYPE, normalizeFileName); |
|
|
|
exportJsSubjectSubject.next(null); |
|
|
|
}).catch(e => { |
|
|
|
}).catch((e: any) => { |
|
|
|
exportJsSubjectSubject.error(e); |
|
|
|
}); |
|
|
|
} catch (e) { |
|
|
|
@ -1180,42 +1159,40 @@ export class ImportExportService { |
|
|
|
)); |
|
|
|
} |
|
|
|
|
|
|
|
private exportToPc(data: any, filename: string) { |
|
|
|
private exportToPc(data: any, filename: string, normalizeFileName = false) { |
|
|
|
if (!data) { |
|
|
|
console.error('No data'); |
|
|
|
return; |
|
|
|
} |
|
|
|
this.exportJson(data, filename); |
|
|
|
this.exportJson(data, filename, normalizeFileName); |
|
|
|
} |
|
|
|
|
|
|
|
public exportJson(data: any, filename: string) { |
|
|
|
public exportJson(data: any, filename: string, normalizeFileName = false) { |
|
|
|
if (isObject(data)) { |
|
|
|
data = JSON.stringify(data, null, 2); |
|
|
|
} |
|
|
|
this.downloadFile(data, filename, JSON_TYPE); |
|
|
|
this.downloadFile(data, filename, JSON_TYPE, normalizeFileName); |
|
|
|
} |
|
|
|
|
|
|
|
private downloadFile(data: any, filename: string, fileType: FileType) { |
|
|
|
if (!filename) { |
|
|
|
filename = 'download'; |
|
|
|
private prepareFilename(filename: string, extension: string, normalizeFileName = false): string { |
|
|
|
if (normalizeFileName) { |
|
|
|
filename = filename.toLowerCase().replace(/\s/g, '_'); |
|
|
|
} |
|
|
|
filename += '.' + fileType.extension; |
|
|
|
filename = filename.replace(/[\\/<>:"|?*\s]/g, '_'); |
|
|
|
return `${filename}.${extension}`; |
|
|
|
} |
|
|
|
|
|
|
|
private downloadFile(data: any, filename = 'download', fileType: FileType, normalizeFileName: boolean) { |
|
|
|
filename = this.prepareFilename(filename, fileType.extension, normalizeFileName); |
|
|
|
const blob = new Blob([data], {type: fileType.mimeType}); |
|
|
|
// @ts-ignore
|
|
|
|
if (this.window.navigator && this.window.navigator.msSaveOrOpenBlob) { |
|
|
|
// @ts-ignore
|
|
|
|
this.window.navigator.msSaveOrOpenBlob(blob, filename); |
|
|
|
} else { |
|
|
|
const e = this.document.createEvent('MouseEvents'); |
|
|
|
const a = this.document.createElement('a'); |
|
|
|
a.download = filename; |
|
|
|
a.href = URL.createObjectURL(blob); |
|
|
|
a.dataset.downloadurl = [fileType.mimeType, a.download, a.href].join(':'); |
|
|
|
// @ts-ignore
|
|
|
|
e.initEvent('click', true, false, this.window, |
|
|
|
0, 0, 0, 0, 0, false, false, false, false, 0, null); |
|
|
|
a.dispatchEvent(e); |
|
|
|
} |
|
|
|
const url = URL.createObjectURL(blob); |
|
|
|
|
|
|
|
const a = this.document.createElement('a'); |
|
|
|
a.href = url; |
|
|
|
a.download = filename; |
|
|
|
a.dataset.downloadurl = [fileType.mimeType, a.download, a.href].join(':'); |
|
|
|
a.click(); |
|
|
|
setTimeout(() => URL.revokeObjectURL(url), 0); |
|
|
|
} |
|
|
|
|
|
|
|
private prepareDashboardExport(dashboard: Dashboard): Dashboard { |
|
|
|
|