UI: Add support UTF-8 symbols in export file name
This commit is contained in:
		
							parent
							
								
									6b4636ac48
								
							
						
					
					
						commit
						fc9c692e0b
					
				@ -22,7 +22,6 @@ import { AppState } from '@core/core.state';
 | 
				
			|||||||
import { ActionNotificationShow } from '@core/notification/notification.actions';
 | 
					import { ActionNotificationShow } from '@core/notification/notification.actions';
 | 
				
			||||||
import { BreakpointId, Dashboard, DashboardLayoutId } from '@shared/models/dashboard.models';
 | 
					import { BreakpointId, Dashboard, DashboardLayoutId } from '@shared/models/dashboard.models';
 | 
				
			||||||
import { deepClone, guid, isDefined, isNotEmptyStr, isObject, isString, isUndefined } from '@core/utils';
 | 
					import { deepClone, guid, isDefined, isNotEmptyStr, isObject, isString, isUndefined } from '@core/utils';
 | 
				
			||||||
import { WINDOW } from '@core/services/window.service';
 | 
					 | 
				
			||||||
import { DOCUMENT } from '@angular/common';
 | 
					import { DOCUMENT } from '@angular/common';
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
  AliasesInfo,
 | 
					  AliasesInfo,
 | 
				
			||||||
@ -100,8 +99,7 @@ type SupportEntityResources = 'includeResourcesInExportWidgetTypes' | 'includeRe
 | 
				
			|||||||
@Injectable()
 | 
					@Injectable()
 | 
				
			||||||
export class ImportExportService {
 | 
					export class ImportExportService {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  constructor(@Inject(WINDOW) private window: Window,
 | 
					  constructor(@Inject(DOCUMENT) private document: Document,
 | 
				
			||||||
              @Inject(DOCUMENT) private document: Document,
 | 
					 | 
				
			||||||
              private store: Store<AppState>,
 | 
					              private store: Store<AppState>,
 | 
				
			||||||
              private translate: TranslateService,
 | 
					              private translate: TranslateService,
 | 
				
			||||||
              private dashboardService: DashboardService,
 | 
					              private dashboardService: DashboardService,
 | 
				
			||||||
@ -177,9 +175,7 @@ export class ImportExportService {
 | 
				
			|||||||
  public exportCalculatedField(calculatedFieldId: string): void {
 | 
					  public exportCalculatedField(calculatedFieldId: string): void {
 | 
				
			||||||
    this.calculatedFieldsService.getCalculatedFieldById(calculatedFieldId).subscribe({
 | 
					    this.calculatedFieldsService.getCalculatedFieldById(calculatedFieldId).subscribe({
 | 
				
			||||||
      next: (calculatedField) => {
 | 
					      next: (calculatedField) => {
 | 
				
			||||||
        let name = calculatedField.name;
 | 
					        this.exportToPc(this.prepareCalculatedFieldExport(calculatedField), calculatedField.name, true);
 | 
				
			||||||
        name = name.toLowerCase().replace(/\W/g, '_');
 | 
					 | 
				
			||||||
        this.exportToPc(this.prepareCalculatedFieldExport(calculatedField), name);
 | 
					 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      error: (e) => {
 | 
					      error: (e) => {
 | 
				
			||||||
        this.handleExportError(e, 'calculated-fields.export-failed-error');
 | 
					        this.handleExportError(e, 'calculated-fields.export-failed-error');
 | 
				
			||||||
@ -200,9 +196,7 @@ export class ImportExportService {
 | 
				
			|||||||
          this.updateUserSettingsIncludeResourcesIfNeeded(includeResources, result.include, 'includeResourcesInExportDashboard');
 | 
					          this.updateUserSettingsIncludeResourcesIfNeeded(includeResources, result.include, 'includeResourcesInExportDashboard');
 | 
				
			||||||
          this.dashboardService.exportDashboard(dashboardId, result.include).subscribe({
 | 
					          this.dashboardService.exportDashboard(dashboardId, result.include).subscribe({
 | 
				
			||||||
            next: (dashboard) => {
 | 
					            next: (dashboard) => {
 | 
				
			||||||
              let name = dashboard.title;
 | 
					              this.exportToPc(this.prepareDashboardExport(dashboard), dashboard.title, true);
 | 
				
			||||||
              name = name.toLowerCase().replace(/\W/g, '_');
 | 
					 | 
				
			||||||
              this.exportToPc(this.prepareDashboardExport(dashboard), name);
 | 
					 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
            error: (e) => {
 | 
					            error: (e) => {
 | 
				
			||||||
              this.handleExportError(e, 'dashboard.export-failed-error');
 | 
					              this.handleExportError(e, 'dashboard.export-failed-error');
 | 
				
			||||||
@ -261,9 +255,8 @@ export class ImportExportService {
 | 
				
			|||||||
                      widgetTitle: string, breakpoint: BreakpointId) {
 | 
					                      widgetTitle: string, breakpoint: BreakpointId) {
 | 
				
			||||||
    const widgetItem = this.itembuffer.prepareWidgetItem(dashboard, sourceState, sourceLayout, widget, breakpoint);
 | 
					    const widgetItem = this.itembuffer.prepareWidgetItem(dashboard, sourceState, sourceLayout, widget, breakpoint);
 | 
				
			||||||
    const widgetDefaultName = this.widgetService.getWidgetInfoFromCache(widget.typeFullFqn).widgetName;
 | 
					    const widgetDefaultName = this.widgetService.getWidgetInfoFromCache(widget.typeFullFqn).widgetName;
 | 
				
			||||||
    let fileName = widgetDefaultName + (isNotEmptyStr(widgetTitle) ? `_${widgetTitle}` : '');
 | 
					    const fileName = widgetDefaultName + (isNotEmptyStr(widgetTitle) ? `_${widgetTitle}` : '');
 | 
				
			||||||
    fileName = fileName.toLowerCase().replace(/\W/g, '_');
 | 
					    this.exportToPc(this.prepareExport(widgetItem), fileName, true);
 | 
				
			||||||
    this.exportToPc(this.prepareExport(widgetItem), fileName);
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public importWidget(dashboard: Dashboard, targetState: string,
 | 
					  public importWidget(dashboard: Dashboard, targetState: string,
 | 
				
			||||||
@ -360,9 +353,7 @@ export class ImportExportService {
 | 
				
			|||||||
          this.updateUserSettingsIncludeResourcesIfNeeded(includeResources, result.include, 'includeResourcesInExportWidgetTypes');
 | 
					          this.updateUserSettingsIncludeResourcesIfNeeded(includeResources, result.include, 'includeResourcesInExportWidgetTypes');
 | 
				
			||||||
          this.widgetService.exportWidgetType(widgetTypeId, result.include).subscribe({
 | 
					          this.widgetService.exportWidgetType(widgetTypeId, result.include).subscribe({
 | 
				
			||||||
            next: (widgetTypeDetails) => {
 | 
					            next: (widgetTypeDetails) => {
 | 
				
			||||||
              let name = widgetTypeDetails.name;
 | 
					              this.exportToPc(this.prepareExport(widgetTypeDetails), widgetTypeDetails.name, true);
 | 
				
			||||||
              name = name.toLowerCase().replace(/\W/g, '_');
 | 
					 | 
				
			||||||
              this.exportToPc(this.prepareExport(widgetTypeDetails), name);
 | 
					 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
            error: (e) => {
 | 
					            error: (e) => {
 | 
				
			||||||
              this.handleExportError(e, 'widget-type.export-failed-error');
 | 
					              this.handleExportError(e, 'widget-type.export-failed-error');
 | 
				
			||||||
@ -440,7 +431,7 @@ export class ImportExportService {
 | 
				
			|||||||
  public exportEntity(entityData: VersionedEntity): void {
 | 
					  public exportEntity(entityData: VersionedEntity): void {
 | 
				
			||||||
    const id = (entityData as EntityInfoData).id ?? (entityData as RuleChainMetaData).ruleChainId;
 | 
					    const id = (entityData as EntityInfoData).id ?? (entityData as RuleChainMetaData).ruleChainId;
 | 
				
			||||||
    let fileName = (entityData as EntityInfoData).name;
 | 
					    let fileName = (entityData as EntityInfoData).name;
 | 
				
			||||||
    let preparedData;
 | 
					    let preparedData: any;
 | 
				
			||||||
    switch (id.entityType) {
 | 
					    switch (id.entityType) {
 | 
				
			||||||
      case EntityType.DEVICE_PROFILE:
 | 
					      case EntityType.DEVICE_PROFILE:
 | 
				
			||||||
      case EntityType.ASSET_PROFILE:
 | 
					      case EntityType.ASSET_PROFILE:
 | 
				
			||||||
@ -511,9 +502,7 @@ export class ImportExportService {
 | 
				
			|||||||
        for (const widgetTypeDetails of widgetTypesDetails) {
 | 
					        for (const widgetTypeDetails of widgetTypesDetails) {
 | 
				
			||||||
          widgetsBundleItem.widgetTypes.push(this.prepareExport(widgetTypeDetails));
 | 
					          widgetsBundleItem.widgetTypes.push(this.prepareExport(widgetTypeDetails));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        let name = widgetsBundle.title;
 | 
					        this.exportToPc(widgetsBundleItem, widgetsBundle.title, true);
 | 
				
			||||||
        name = name.toLowerCase().replace(/\W/g, '_');
 | 
					 | 
				
			||||||
        this.exportToPc(widgetsBundleItem, name);
 | 
					 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      error: (e) => {
 | 
					      error: (e) => {
 | 
				
			||||||
        this.handleExportError(e, 'widgets-bundle.export-failed-error');
 | 
					        this.handleExportError(e, 'widgets-bundle.export-failed-error');
 | 
				
			||||||
@ -528,9 +517,7 @@ export class ImportExportService {
 | 
				
			|||||||
          widgetsBundle: this.prepareExport(widgetsBundle),
 | 
					          widgetsBundle: this.prepareExport(widgetsBundle),
 | 
				
			||||||
          widgetTypeFqns
 | 
					          widgetTypeFqns
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
        let name = widgetsBundle.title;
 | 
					        this.exportToPc(widgetsBundleItem, widgetsBundle.title, true);
 | 
				
			||||||
        name = name.toLowerCase().replace(/\W/g, '_');
 | 
					 | 
				
			||||||
        this.exportToPc(widgetsBundleItem, name);
 | 
					 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      error: (e) => {
 | 
					      error: (e) => {
 | 
				
			||||||
        this.handleExportError(e, 'widgets-bundle.export-failed-error');
 | 
					        this.handleExportError(e, 'widgets-bundle.export-failed-error');
 | 
				
			||||||
@ -662,11 +649,9 @@ export class ImportExportService {
 | 
				
			|||||||
  private onRuleChainExported() {
 | 
					  private onRuleChainExported() {
 | 
				
			||||||
    return {
 | 
					    return {
 | 
				
			||||||
      next: (ruleChainExport: RuleChainImport) => {
 | 
					      next: (ruleChainExport: RuleChainImport) => {
 | 
				
			||||||
        let name = ruleChainExport.ruleChain.name;
 | 
					        this.exportToPc(ruleChainExport, ruleChainExport.ruleChain.name, true);
 | 
				
			||||||
        name = name.toLowerCase().replace(/\W/g, '_');
 | 
					 | 
				
			||||||
        this.exportToPc(ruleChainExport, name);
 | 
					 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      error: (e) => {
 | 
					      error: (e: any) => {
 | 
				
			||||||
        this.handleExportError(e, 'rulechain.export-failed-error');
 | 
					        this.handleExportError(e, 'rulechain.export-failed-error');
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
@ -747,9 +732,7 @@ export class ImportExportService {
 | 
				
			|||||||
  public exportDeviceProfile(deviceProfileId: string) {
 | 
					  public exportDeviceProfile(deviceProfileId: string) {
 | 
				
			||||||
    this.deviceProfileService.exportDeviceProfile(deviceProfileId).subscribe({
 | 
					    this.deviceProfileService.exportDeviceProfile(deviceProfileId).subscribe({
 | 
				
			||||||
      next: (deviceProfile) => {
 | 
					      next: (deviceProfile) => {
 | 
				
			||||||
        let name = deviceProfile.name;
 | 
					        this.exportToPc(this.prepareProfileExport(deviceProfile), deviceProfile.name, true);
 | 
				
			||||||
        name = name.toLowerCase().replace(/\W/g, '_');
 | 
					 | 
				
			||||||
        this.exportToPc(this.prepareProfileExport(deviceProfile), name);
 | 
					 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      error: (e) => {
 | 
					      error: (e) => {
 | 
				
			||||||
        this.handleExportError(e, 'device-profile.export-failed-error');
 | 
					        this.handleExportError(e, 'device-profile.export-failed-error');
 | 
				
			||||||
@ -776,9 +759,7 @@ export class ImportExportService {
 | 
				
			|||||||
  public exportAssetProfile(assetProfileId: string) {
 | 
					  public exportAssetProfile(assetProfileId: string) {
 | 
				
			||||||
    this.assetProfileService.exportAssetProfile(assetProfileId).subscribe({
 | 
					    this.assetProfileService.exportAssetProfile(assetProfileId).subscribe({
 | 
				
			||||||
      next: (assetProfile) => {
 | 
					      next: (assetProfile) => {
 | 
				
			||||||
        let name = assetProfile.name;
 | 
					        this.exportToPc(this.prepareProfileExport(assetProfile), assetProfile.name, true);
 | 
				
			||||||
        name = name.toLowerCase().replace(/\W/g, '_');
 | 
					 | 
				
			||||||
        this.exportToPc(this.prepareProfileExport(assetProfile), name);
 | 
					 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      error: (e) => {
 | 
					      error: (e) => {
 | 
				
			||||||
        this.handleExportError(e, 'asset-profile.export-failed-error');
 | 
					        this.handleExportError(e, 'asset-profile.export-failed-error');
 | 
				
			||||||
@ -805,9 +786,7 @@ export class ImportExportService {
 | 
				
			|||||||
  public exportTenantProfile(tenantProfileId: string) {
 | 
					  public exportTenantProfile(tenantProfileId: string) {
 | 
				
			||||||
    this.tenantProfileService.getTenantProfile(tenantProfileId).subscribe({
 | 
					    this.tenantProfileService.getTenantProfile(tenantProfileId).subscribe({
 | 
				
			||||||
      next: (tenantProfile) => {
 | 
					      next: (tenantProfile) => {
 | 
				
			||||||
        let name = tenantProfile.name;
 | 
					        this.exportToPc(this.prepareProfileExport(tenantProfile), tenantProfile.name, true);
 | 
				
			||||||
        name = name.toLowerCase().replace(/\W/g, '_');
 | 
					 | 
				
			||||||
        this.exportToPc(this.prepareProfileExport(tenantProfile), name);
 | 
					 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      error: (e) => {
 | 
					      error: (e) => {
 | 
				
			||||||
        this.handleExportError(e, 'tenant-profile.export-failed-error');
 | 
					        this.handleExportError(e, 'tenant-profile.export-failed-error');
 | 
				
			||||||
@ -882,7 +861,7 @@ export class ImportExportService {
 | 
				
			|||||||
        jsZip.generateAsync({type: 'blob'}).then(content => {
 | 
					        jsZip.generateAsync({type: 'blob'}).then(content => {
 | 
				
			||||||
          this.downloadFile(content, filename, ZIP_TYPE);
 | 
					          this.downloadFile(content, filename, ZIP_TYPE);
 | 
				
			||||||
          exportJsSubjectSubject.next(null);
 | 
					          exportJsSubjectSubject.next(null);
 | 
				
			||||||
        }).catch(e => {
 | 
					        }).catch((e: any) => {
 | 
				
			||||||
            exportJsSubjectSubject.error(e);
 | 
					            exportJsSubjectSubject.error(e);
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
      } catch (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) {
 | 
					    if (!data) {
 | 
				
			||||||
      console.error('No data');
 | 
					      console.error('No data');
 | 
				
			||||||
      return;
 | 
					      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)) {
 | 
					    if (isObject(data)) {
 | 
				
			||||||
      data = JSON.stringify(data, null,  2);
 | 
					      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) {
 | 
					  private prepareFilename(filename: string, extension: string, normalizeFileName: boolean): string {
 | 
				
			||||||
    if (!filename) {
 | 
					    if (normalizeFileName) {
 | 
				
			||||||
      filename = 'download';
 | 
					      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 = false) {
 | 
				
			||||||
 | 
					    filename = this.prepareFilename(filename, fileType.extension, normalizeFileName);
 | 
				
			||||||
    const blob = new Blob([data], {type: fileType.mimeType});
 | 
					    const blob = new Blob([data], {type: fileType.mimeType});
 | 
				
			||||||
    // @ts-ignore
 | 
					    const url = URL.createObjectURL(blob);
 | 
				
			||||||
    if (this.window.navigator && this.window.navigator.msSaveOrOpenBlob) {
 | 
					
 | 
				
			||||||
      // @ts-ignore
 | 
					    const a = this.document.createElement('a');
 | 
				
			||||||
      this.window.navigator.msSaveOrOpenBlob(blob, filename);
 | 
					    a.href = url;
 | 
				
			||||||
    } else {
 | 
					    a.download = filename;
 | 
				
			||||||
      const e = this.document.createEvent('MouseEvents');
 | 
					    a.dataset.downloadurl = [fileType.mimeType, a.download, a.href].join(':');
 | 
				
			||||||
      const a = this.document.createElement('a');
 | 
					    a.click();
 | 
				
			||||||
      a.download = filename;
 | 
					    setTimeout(() => URL.revokeObjectURL(url), 0);
 | 
				
			||||||
      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);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private prepareDashboardExport(dashboard: Dashboard): Dashboard {
 | 
					  private prepareDashboardExport(dashboard: Dashboard): Dashboard {
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user