/* * Copyright © 2016-2018 The Thingsboard Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* eslint-disable import/no-unresolved, import/default */ import importDialogTemplate from './import-dialog.tpl.html'; import entityAliasesTemplate from '../entity/alias/entity-aliases.tpl.html'; /* eslint-enable import/no-unresolved, import/default */ /* eslint-disable no-undef, angular/window-service, angular/document-service */ /*@ngInject*/ export default function ImportExport($log, $translate, $q, $mdDialog, $document, $http, itembuffer, utils, types, dashboardUtils, entityService, dashboardService, ruleChainService, widgetService, toast, attributeService) { var service = { exportDashboard: exportDashboard, importDashboard: importDashboard, exportWidget: exportWidget, importWidget: importWidget, exportRuleChain: exportRuleChain, importRuleChain: importRuleChain, exportWidgetType: exportWidgetType, importWidgetType: importWidgetType, exportWidgetsBundle: exportWidgetsBundle, importWidgetsBundle: importWidgetsBundle, exportExtension: exportExtension, importExtension: importExtension, exportToPc: exportToPc }; return service; // Widgets bundle functions function exportWidgetsBundle(widgetsBundleId) { widgetService.getWidgetsBundle(widgetsBundleId).then( function success(widgetsBundle) { var bundleAlias = widgetsBundle.alias; var isSystem = widgetsBundle.tenantId.id === types.id.nullUid; widgetService.getBundleWidgetTypes(bundleAlias, isSystem).then( function success (widgetTypes) { prepareExport(widgetsBundle); var widgetsBundleItem = { widgetsBundle: prepareExport(widgetsBundle), widgetTypes: [] }; for (var t in widgetTypes) { var widgetType = widgetTypes[t]; if (angular.isDefined(widgetType.bundleAlias)) { delete widgetType.bundleAlias; } widgetsBundleItem.widgetTypes.push(prepareExport(widgetType)); } var name = widgetsBundle.title; name = name.toLowerCase().replace(/\W/g,"_"); exportToPc(widgetsBundleItem, name + '.json'); }, function fail (rejection) { var message = rejection; if (!message) { message = $translate.instant('error.unknown-error'); } toast.showError($translate.instant('widgets-bundle.export-failed-error', {error: message})); } ); }, function fail(rejection) { var message = rejection; if (!message) { message = $translate.instant('error.unknown-error'); } toast.showError($translate.instant('widgets-bundle.export-failed-error', {error: message})); } ); } function importNextWidgetType(widgetTypes, bundleAlias, index, deferred) { if (!widgetTypes || widgetTypes.length <= index) { deferred.resolve(); } else { var widgetType = widgetTypes[index]; widgetType.bundleAlias = bundleAlias; widgetService.saveImportedWidgetType(widgetType).then( function success() { index++; importNextWidgetType(widgetTypes, bundleAlias, index, deferred); }, function fail() { deferred.reject(); } ); } } function importWidgetsBundle($event) { var deferred = $q.defer(); openImportDialog($event, 'widgets-bundle.import', 'widgets-bundle.widgets-bundle-file').then( function success(widgetsBundleItem) { if (!validateImportedWidgetsBundle(widgetsBundleItem)) { toast.showError($translate.instant('widgets-bundle.invalid-widgets-bundle-file-error')); deferred.reject(); } else { var widgetsBundle = widgetsBundleItem.widgetsBundle; widgetService.saveWidgetsBundle(widgetsBundle).then( function success(savedWidgetsBundle) { var bundleAlias = savedWidgetsBundle.alias; var widgetTypes = widgetsBundleItem.widgetTypes; importNextWidgetType(widgetTypes, bundleAlias, 0, deferred); }, function fail() { deferred.reject(); } ); } }, function fail() { deferred.reject(); } ); return deferred.promise; } function validateImportedWidgetsBundle(widgetsBundleItem) { if (angular.isUndefined(widgetsBundleItem.widgetsBundle)) { return false; } if (angular.isUndefined(widgetsBundleItem.widgetTypes)) { return false; } var widgetsBundle = widgetsBundleItem.widgetsBundle; if (angular.isUndefined(widgetsBundle.title)) { return false; } var widgetTypes = widgetsBundleItem.widgetTypes; for (var t in widgetTypes) { var widgetType = widgetTypes[t]; if (!validateImportedWidgetType(widgetType)) { return false; } } return true; } // Widget type functions function exportWidgetType(widgetTypeId) { widgetService.getWidgetTypeById(widgetTypeId).then( function success(widgetType) { if (angular.isDefined(widgetType.bundleAlias)) { delete widgetType.bundleAlias; } var name = widgetType.name; name = name.toLowerCase().replace(/\W/g,"_"); exportToPc(prepareExport(widgetType), name + '.json'); }, function fail(rejection) { var message = rejection; if (!message) { message = $translate.instant('error.unknown-error'); } toast.showError($translate.instant('widget-type.export-failed-error', {error: message})); } ); } function importWidgetType($event, bundleAlias) { var deferred = $q.defer(); openImportDialog($event, 'widget-type.import', 'widget-type.widget-type-file').then( function success(widgetType) { if (!validateImportedWidgetType(widgetType)) { toast.showError($translate.instant('widget-type.invalid-widget-type-file-error')); deferred.reject(); } else { widgetType.bundleAlias = bundleAlias; widgetService.saveImportedWidgetType(widgetType).then( function success() { deferred.resolve(); }, function fail() { deferred.reject(); } ); } }, function fail() { deferred.reject(); } ); return deferred.promise; } function validateImportedWidgetType(widgetType) { if (angular.isUndefined(widgetType.name) || angular.isUndefined(widgetType.descriptor)) { return false; } return true; } // Rule chain functions function exportRuleChain(ruleChainId) { ruleChainService.getRuleChain(ruleChainId).then( (ruleChain) => { ruleChainService.getRuleChainMetaData(ruleChainId).then( (ruleChainMetaData) => { var ruleChainExport = { ruleChain: prepareRuleChain(ruleChain), metadata: prepareRuleChainMetaData(ruleChainMetaData) }; var name = ruleChain.name; name = name.toLowerCase().replace(/\W/g,"_"); exportToPc(ruleChainExport, name + '.json'); }, (rejection) => { processExportRuleChainRejection(rejection); } ); }, (rejection) => { processExportRuleChainRejection(rejection); } ); } function prepareRuleChain(ruleChain) { ruleChain = prepareExport(ruleChain); if (ruleChain.firstRuleNodeId) { ruleChain.firstRuleNodeId = null; } ruleChain.root = false; return ruleChain; } function prepareRuleChainMetaData(ruleChainMetaData) { delete ruleChainMetaData.ruleChainId; for (var i=0;i 0) { processEntityAliases(entityAliases, aliasIds).then( function(missingEntityAliases) { if (Object.keys(missingEntityAliases).length > 0) { editMissingAliases($event, [ widget ], true, 'dashboard.widget-import-missing-aliases-title', missingEntityAliases).then( function success(updatedEntityAliases) { for (var aliasId in updatedEntityAliases) { var entityAlias = updatedEntityAliases[aliasId]; var datasourceIndex; if (datasourceAliasesMap[aliasId]) { datasourceIndex = datasourceAliasesMap[aliasId]; datasourceAliases[datasourceIndex] = entityAlias; } else if (targetDeviceAliasesMap[aliasId]) { datasourceIndex = targetDeviceAliasesMap[aliasId]; targetDeviceAliases[datasourceIndex] = entityAlias; } } addImportedWidget(dashboard, targetState, targetLayoutFunction, $event, widget, aliasesInfo, onAliasesUpdateFunction, originalColumns, originalSize, deferred); }, function fail() { deferred.reject(); } ); } else { addImportedWidget(dashboard, targetState, targetLayoutFunction, $event, widget, aliasesInfo, onAliasesUpdateFunction, originalColumns, originalSize, deferred); } } ); } else { addImportedWidget(dashboard, targetState, targetLayoutFunction, $event, widget, aliasesInfo, onAliasesUpdateFunction, originalColumns, originalSize, deferred); } } else { addImportedWidget(dashboard, targetState, targetLayoutFunction, $event, widget, aliasesInfo, onAliasesUpdateFunction, originalColumns, originalSize, deferred); } } }, function fail() { deferred.reject(); } ); return deferred.promise; } function validateImportedWidget(widgetItem) { if (angular.isUndefined(widgetItem.widget) || angular.isUndefined(widgetItem.aliasesInfo) || angular.isUndefined(widgetItem.originalColumns)) { return false; } var widget = widgetItem.widget; if (angular.isUndefined(widget.isSystemType) || angular.isUndefined(widget.bundleAlias) || angular.isUndefined(widget.typeAlias) || angular.isUndefined(widget.type)) { return false; } return true; } function addImportedWidget(dashboard, targetState, targetLayoutFunction, event, widget, aliasesInfo, onAliasesUpdateFunction, originalColumns, originalSize, deferred) { targetLayoutFunction(event).then( function success(targetLayout) { itembuffer.addWidgetToDashboard(dashboard, targetState, targetLayout, widget, aliasesInfo, onAliasesUpdateFunction, originalColumns, originalSize, -1, -1).then( function() { deferred.resolve( { widget: widget, layoutId: targetLayout } ); } ); }, function fail() { deferred.reject(); } ); } // Dashboard functions function exportDashboard(dashboardId) { dashboardService.getDashboard(dashboardId).then( function success(dashboard) { var name = dashboard.title; name = name.toLowerCase().replace(/\W/g,"_"); exportToPc(prepareDashboardExport(dashboard), name + '.json'); }, function fail(rejection) { var message = rejection; if (!message) { message = $translate.instant('error.unknown-error'); } toast.showError($translate.instant('dashboard.export-failed-error', {error: message})); } ); } function prepareDashboardExport(dashboard) { dashboard = prepareExport(dashboard); delete dashboard.assignedCustomers; delete dashboard.publicCustomerId; delete dashboard.assignedCustomersText; delete dashboard.assignedCustomersIds; return dashboard; } function importDashboard($event) { var deferred = $q.defer(); openImportDialog($event, 'dashboard.import', 'dashboard.dashboard-file').then( function success(dashboard) { if (!validateImportedDashboard(dashboard)) { toast.showError($translate.instant('dashboard.invalid-dashboard-file-error')); deferred.reject(); } else { dashboard = dashboardUtils.validateAndUpdateDashboard(dashboard); var entityAliases = dashboard.configuration.entityAliases; if (entityAliases) { var aliasIds = Object.keys( entityAliases ); if (aliasIds.length > 0) { processEntityAliases(entityAliases, aliasIds).then( function(missingEntityAliases) { if (Object.keys( missingEntityAliases ).length > 0) { editMissingAliases($event, dashboard.configuration.widgets, false, 'dashboard.dashboard-import-missing-aliases-title', missingEntityAliases).then( function success(updatedEntityAliases) { for (var aliasId in updatedEntityAliases) { entityAliases[aliasId] = updatedEntityAliases[aliasId]; } saveImportedDashboard(dashboard, deferred); }, function fail() { deferred.reject(); } ); } else { saveImportedDashboard(dashboard, deferred); } } ) } else { saveImportedDashboard(dashboard, deferred); } } else { saveImportedDashboard(dashboard, deferred); } } }, function fail() { deferred.reject(); } ); return deferred.promise; } function saveImportedDashboard(dashboard, deferred) { dashboardService.saveDashboard(dashboard).then( function success() { deferred.resolve(); }, function fail() { deferred.reject(); } ) } function validateImportedDashboard(dashboard) { if (angular.isUndefined(dashboard.title) || angular.isUndefined(dashboard.configuration)) { return false; } return true; } function exportExtension(extensionId) { getExtension(extensionId) .then( function success(extension) { var name = extension.title; name = name.toLowerCase().replace(/\W/g,"_"); exportToPc(prepareExport(extension), name + '.json'); }, function fail(rejection) { var message = rejection; if (!message) { message = $translate.instant('error.unknown-error'); } toast.showError($translate.instant('extension.export-failed-error', {error: message})); } ); function getExtension(extensionId) { var deferred = $q.defer(); var url = '/api/plugins/telemetry/DEVICE/' + extensionId; $http.get(url, null) .then(function success(response) { deferred.resolve(response.data); }, function fail() { deferred.reject(); }); return deferred.promise; } } function importExtension($event, options) { var deferred = $q.defer(); openImportDialog($event, 'extension.import-extensions', 'extension.file') .then( function success(extension) { if (!validateImportedExtension(extension)) { toast.showError($translate.instant('extension.invalid-file-error')); deferred.reject(); } else { attributeService .saveEntityAttributes( options.entityType, options.entityId, types.attributesScope.shared.value, [{ key: "configuration", value: angular.toJson(extension) }] ) .then(function success() { options.successFunc(); }); } }, function fail() { deferred.reject(); } ); return deferred.promise; } function validateImportedExtension(configuration) { if (configuration.length) { for (let i = 0; i < configuration.length; i++) { if (angular.isUndefined(configuration[i].configuration) || angular.isUndefined(configuration[i].id )|| angular.isUndefined(configuration[i].type)) { return false; } } } else { return false; } return true; } function processEntityAliases(entityAliases, aliasIds) { var deferred = $q.defer(); var missingEntityAliases = {}; var index = -1; checkNextEntityAliasOrComplete(index, aliasIds, entityAliases, missingEntityAliases, deferred); return deferred.promise; } function checkNextEntityAliasOrComplete(index, aliasIds, entityAliases, missingEntityAliases, deferred) { index++; if (index == aliasIds.length) { deferred.resolve(missingEntityAliases); } else { checkEntityAlias(index, aliasIds, entityAliases, missingEntityAliases, deferred); } } function checkEntityAlias(index, aliasIds, entityAliases, missingEntityAliases, deferred) { var aliasId = aliasIds[index]; var entityAlias = entityAliases[aliasId]; entityService.checkEntityAlias(entityAlias).then( function(result) { if (result) { checkNextEntityAliasOrComplete(index, aliasIds, entityAliases, missingEntityAliases, deferred); } else { var missingEntityAlias = angular.copy(entityAlias); missingEntityAlias.filter = null; missingEntityAliases[aliasId] = missingEntityAlias; checkNextEntityAliasOrComplete(index, aliasIds, entityAliases, missingEntityAliases, deferred); } } ); } function editMissingAliases($event, widgets, isSingleWidget, customTitle, missingEntityAliases) { var deferred = $q.defer(); $mdDialog.show({ controller: 'EntityAliasesController', controllerAs: 'vm', templateUrl: entityAliasesTemplate, locals: { config: { entityAliases: missingEntityAliases, widgets: widgets, isSingleWidget: isSingleWidget, customTitle: customTitle, disableAdd: true } }, parent: angular.element($document[0].body), multiple: true, fullscreen: true, targetEvent: $event }).then(function (updatedEntityAliases) { deferred.resolve(updatedEntityAliases); }, function () { deferred.reject(); }); return deferred.promise; } // Common functions function prepareExport(data) { var exportedData = angular.copy(data); if (angular.isDefined(exportedData.id)) { delete exportedData.id; } if (angular.isDefined(exportedData.createdTime)) { delete exportedData.createdTime; } if (angular.isDefined(exportedData.tenantId)) { delete exportedData.tenantId; } if (angular.isDefined(exportedData.customerId)) { delete exportedData.customerId; } return exportedData; } function exportToPc(data, filename) { if (!data) { $log.error('No data'); return; } if (!filename) { filename = 'download.json'; } if (angular.isObject(data)) { data = angular.toJson(data, 2); } var blob = new Blob([data], {type: 'text/json'}); // FOR IE: if (window.navigator && window.navigator.msSaveOrOpenBlob) { window.navigator.msSaveOrOpenBlob(blob, filename); } else{ var e = document.createEvent('MouseEvents'), a = document.createElement('a'); a.download = filename; a.href = window.URL.createObjectURL(blob); a.dataset.downloadurl = ['text/json', a.download, a.href].join(':'); e.initEvent('click', true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null); a.dispatchEvent(e); } } function openImportDialog($event, importTitle, importFileLabel) { var deferred = $q.defer(); $mdDialog.show({ controller: 'ImportDialogController', controllerAs: 'vm', templateUrl: importDialogTemplate, locals: { importTitle: importTitle, importFileLabel: importFileLabel }, parent: angular.element($document[0].body), multiple: true, fullscreen: true, targetEvent: $event }).then(function (importData) { deferred.resolve(importData); }, function () { deferred.reject(); }); return deferred.promise; } } /* eslint-enable no-undef, angular/window-service, angular/document-service */