diff --git a/ui/src/app/device/devices.tpl.html b/ui/src/app/device/devices.tpl.html index bc66a79b8b..1ec0134444 100644 --- a/ui/src/app/device/devices.tpl.html +++ b/ui/src/app/device/devices.tpl.html @@ -70,6 +70,7 @@ diff --git a/ui/src/app/extension/extension-table.directive.js b/ui/src/app/extension/extension-table.directive.js index 8ad8275c15..ecc7b0fad4 100644 --- a/ui/src/app/extension/extension-table.directive.js +++ b/ui/src/app/extension/extension-table.directive.js @@ -34,7 +34,8 @@ export default function ExtensionTableDirective() { scope: true, bindToController: { entityId: '=', - entityType: '@' + entityType: '@', + entityName: '=' }, controller: ExtensionTableController, controllerAs: 'vm', @@ -43,7 +44,7 @@ export default function ExtensionTableDirective() { } /*@ngInject*/ -function ExtensionTableController($scope, $filter, $document, $translate, types, $mdDialog, attributeService, telemetryWebsocketService) { +function ExtensionTableController($scope, $filter, $document, $translate, types, $mdDialog, attributeService, telemetryWebsocketService, importExport) { let vm = this; @@ -328,4 +329,21 @@ function ExtensionTableController($scope, $filter, $document, $translate, types, return num; } } + + vm.importExtensions = function () { + importExport.importExtension({"entityType":vm.entityType, "entityId":vm.entityId, "successFunc":reloadExtensions}); + }; + vm.exportExtensions = function () { + importExport.exportToPc(vm.extensionsJSON, vm.entityName + '_configuration.json'); + }; + + vm.exportExtension = function ($event, extension) { + if ($event) { + $event.stopPropagation(); + } + importExport.exportToPc(extension, vm.entityName +'_'+ extension.id +'_configuration.json'); + }; + + + } \ No newline at end of file diff --git a/ui/src/app/extension/extension-table.scss b/ui/src/app/extension/extension-table.scss index ee2a57016d..1cedaed6dc 100644 --- a/ui/src/app/extension/extension-table.scss +++ b/ui/src/app/extension/extension-table.scss @@ -16,10 +16,19 @@ @import '../../scss/constants'; -.extension-table md-input-container .md-errors-spacer { - min-height: 0; +.extension-table { + + md-input-container .md-errors-spacer { + min-height: 0; + } + + &.tb-data-table table.md-table tbody tr td.tb-action-cell, + &.tb-data-table table.md-table.md-row-select tbody tr td.tb-action-cell { + width: 114px; + } } + .extension__syncStatus--black { color: #000000!important; } diff --git a/ui/src/app/extension/extension-table.tpl.html b/ui/src/app/extension/extension-table.tpl.html index 428a14666d..3cc322b34f 100644 --- a/ui/src/app/extension/extension-table.tpl.html +++ b/ui/src/app/extension/extension-table.tpl.html @@ -23,6 +23,19 @@
{{ 'extension.extensions' }} + + + file_upload + + {{ 'extension.sync.import-extensions-configuration' | translate }} + + + + file_download + + {{ 'extension.sync.export-extensions-configuration' | translate }} + + add @@ -44,7 +57,7 @@
+ && vm.query.search != null">
search @@ -111,6 +124,12 @@ {{ extension.id }} {{ extension.type }} + + file_download + + {{ 'extension.export-extension' | translate }} + + edit diff --git a/ui/src/app/import-export/import-export.service.js b/ui/src/app/import-export/import-export.service.js index 86d5240953..e660e82222 100644 --- a/ui/src/app/import-export/import-export.service.js +++ b/ui/src/app/import-export/import-export.service.js @@ -24,8 +24,9 @@ import entityAliasesTemplate from '../entity/alias/entity-aliases.tpl.html'; /* eslint-disable no-undef, angular/window-service, angular/document-service */ /*@ngInject*/ -export default function ImportExport($log, $translate, $q, $mdDialog, $document, itembuffer, utils, types, dashboardUtils, - entityService, dashboardService, pluginService, ruleService, widgetService, toast) { +export default function ImportExport($log, $translate, $q, $mdDialog, $document, $http, itembuffer, utils, types, + dashboardUtils, entityService, dashboardService, pluginService, ruleService, + widgetService, toast, attributeService) { var service = { @@ -40,8 +41,11 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document, exportWidgetType: exportWidgetType, importWidgetType: importWidgetType, exportWidgetsBundle: exportWidgetsBundle, - importWidgetsBundle: importWidgetsBundle - } + importWidgetsBundle: importWidgetsBundle, + exportExtension: exportExtension, + importExtension: importExtension, + exportToPc: exportToPc + }; return service; @@ -614,6 +618,89 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document, 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(options) { + var deferred = $q.defer(); + openImportDialog(options, '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 = {}; diff --git a/ui/src/app/locale/locale.constant.js b/ui/src/app/locale/locale.constant.js index 089a0f5c51..0021e3de85 100644 --- a/ui/src/app/locale/locale.constant.js +++ b/ui/src/app/locale/locale.constant.js @@ -845,8 +845,16 @@ export default angular.module('thingsboard.locale', []) "sync": "Sync", "not-sync": "Not sync", "last-sync-time": "Last sync time", - "not-available": "Not available" + "not-available": "Not available", + "export-extensions-configuration":"Export extensions configuration", + "import-extensions-configuration":"Import extensions configuration" }, + + "import-extensions": "Import extensions", + "import-extension": "Import extension", + "export-extension": "Export extension", + "file": "Extensions file", + "invalid-file-error": "Invalid extension file" }, "fullscreen": { "expand": "Expand to fullscreen",