thingsboard-aplcache
Details
ui/src/app/device/devices.tpl.html 1(+1 -0)
diff --git a/ui/src/app/device/devices.tpl.html b/ui/src/app/device/devices.tpl.html
index bc66a79..1ec0134 100644
--- a/ui/src/app/device/devices.tpl.html
+++ b/ui/src/app/device/devices.tpl.html
@@ -70,6 +70,7 @@
<md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode && vm.grid.operatingItem().additionalInfo.gateway" md-on-select="vm.grid.triggerResize()" label="{{ 'extension.extensions' | translate }}">
<tb-extension-table flex
entity-id="vm.grid.operatingItem().id.id"
+ entity-name="vm.grid.operatingItem().name"
entity-type="{{vm.types.entityType.device}}">
</tb-extension-table>
</md-tab>
diff --git a/ui/src/app/extension/extension-table.directive.js b/ui/src/app/extension/extension-table.directive.js
index 7fe7139..5e5b036 100644
--- a/ui/src/app/extension/extension-table.directive.js
+++ b/ui/src/app/extension/extension-table.directive.js
@@ -36,7 +36,8 @@ export default function ExtensionTableDirective() {
entityId: '=',
entityType: '@',
inWidget: '@?',
- ctx: '=?'
+ ctx: '=?',
+ entityName: '='
},
controller: ExtensionTableController,
controllerAs: 'vm',
@@ -45,7 +46,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;
@@ -122,6 +123,16 @@ function ExtensionTableController($scope, $filter, $document, $translate, types,
addExtension();
}
});
+ $scope.$on("exportExtensions", function($event, source) {
+ if(source.entityId == vm.entityId) {
+ vm.exportExtensions(source.entityName);
+ }
+ });
+ $scope.$on("importExtensions", function($event, source) {
+ if(source.entityId == vm.entityId) {
+ vm.importExtensions();
+ }
+ });
function enterFilterMode() {
vm.query.search = '';
@@ -364,4 +375,23 @@ function ExtensionTableController($scope, $filter, $document, $translate, types,
return num;
}
}
+
+ vm.importExtensions = function($event) {
+ importExport.importExtension($event, {"entityType":vm.entityType, "entityId":vm.entityId, "successFunc":reloadExtensions});
+ };
+ vm.exportExtensions = function(widgetSourceEntityName) {
+ if(vm.inWidget) {
+ importExport.exportToPc(vm.extensionsJSON, widgetSourceEntityName + '_configuration.json');
+ } else {
+ importExport.exportToPc(vm.extensionsJSON, vm.entityName + '_configuration.json');
+ }
+ };
+
+ /*change function for widget implementing, like vm.exportExtensions*/
+ 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 ac57d4a..7c0a0d8 100644
--- a/ui/src/app/extension/extension-table.scss
+++ b/ui/src/app/extension/extension-table.scss
@@ -16,11 +16,16 @@
@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;
+ }*/
.sync-widget {
max-height: 90px;
overflow: hidden;
@@ -31,7 +36,6 @@
}
}
-
.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 e155e9e..b86061c 100644
--- a/ui/src/app/extension/extension-table.tpl.html
+++ b/ui/src/app/extension/extension-table.tpl.html
@@ -23,6 +23,19 @@
<div class="md-toolbar-tools">
<span translate>{{ 'extension.extensions' }}</span>
<span flex></span>
+
+ <md-button class="md-icon-button" ng-click="vm.importExtensions($event)">
+ <md-icon>file_upload</md-icon>
+ <md-tooltip md-direction="top">
+ {{ 'extension.import-extensions-configuration' | translate }}
+ </md-tooltip>
+ </md-button>
+ <md-button class="md-icon-button" ng-click="vm.exportExtensions()">
+ <md-icon>file_download</md-icon>
+ <md-tooltip md-direction="top">
+ {{ 'extension.export-extensions-configuration' | translate }}
+ </md-tooltip>
+ </md-button>
<md-button class="md-icon-button" ng-click="vm.addExtension($event)">
<md-icon>add</md-icon>
<md-tooltip md-direction="top">
@@ -111,6 +124,14 @@
<td md-cell>{{ extension.id }}</td>
<td md-cell>{{ extension.type }}</td>
<td md-cell class="tb-action-cell">
+
+ <!--<md-button class="md-icon-button" aria-label="{{ 'action.edit' | translate }}" ng-click="vm.exportExtension($event, extension)">
+ <md-icon aria-label="{{ 'action.edit' | translate }}" class="material-icons">file_download</md-icon>
+ <md-tooltip md-direction="top">
+ {{ 'extension.export-extension' | translate }}
+ </md-tooltip>
+ </md-button>-->
+
<md-button class="md-icon-button" aria-label="{{ 'action.edit' | translate }}" ng-click="vm.editExtension($event, extension)">
<md-icon aria-label="{{ 'action.edit' | translate }}" class="material-icons">edit</md-icon>
<md-tooltip md-direction="top">
diff --git a/ui/src/app/import-export/import-export.service.js b/ui/src/app/import-export/import-export.service.js
index 86d5240..62e1bb3 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,84 @@ 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($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 = {};
ui/src/app/locale/locale.constant.js 8(+8 -0)
diff --git a/ui/src/app/locale/locale.constant.js b/ui/src/app/locale/locale.constant.js
index 089a0f5..ab672de 100644
--- a/ui/src/app/locale/locale.constant.js
+++ b/ui/src/app/locale/locale.constant.js
@@ -847,6 +847,14 @@ export default angular.module('thingsboard.locale', [])
"last-sync-time": "Last sync time",
"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",
diff --git a/ui/src/app/widget/lib/extensions-table-widget.js b/ui/src/app/widget/lib/extensions-table-widget.js
index ded52ff..3e5ac61 100644
--- a/ui/src/app/widget/lib/extensions-table-widget.js
+++ b/ui/src/app/widget/lib/extensions-table-widget.js
@@ -66,7 +66,7 @@ function ExtensionsTableWidgetController($scope, $translate, utils) {
}
vm.ctx.widgetTitle = vm.extensionsTitle;
- vm.ctx.widgetActions = [vm.addAction, vm.searchAction, vm.refreshAction];
+ vm.ctx.widgetActions = [vm.importExtensionsAction, vm.exportExtensionsAction, vm.addAction, vm.searchAction, vm.refreshAction];
}
function updateDatasources() {
@@ -78,7 +78,7 @@ function ExtensionsTableWidgetController($scope, $translate, utils) {
vm.changeSelectedSource = function(source) {
vm.selectedSource = source;
- }
+ };
vm.searchAction = {
name: "action.search",
@@ -96,7 +96,7 @@ function ExtensionsTableWidgetController($scope, $translate, utils) {
$scope.$broadcast("refreshExtensions", vm.selectedSource);
},
icon: "refresh"
- }
+ };
vm.addAction = {
name: "action.add",
@@ -105,7 +105,25 @@ function ExtensionsTableWidgetController($scope, $translate, utils) {
$scope.$broadcast("addExtension", vm.selectedSource);
},
icon: "add"
- }
+ };
+
+ vm.exportExtensionsAction = {
+ name: "extension.export-extensions-configuration",
+ show: true,
+ onAction: function() {
+ $scope.$broadcast("exportExtensions", vm.selectedSource);
+ },
+ icon: "file_download"
+ };
+
+ vm.importExtensionsAction = {
+ name: "extension.import-extensions-configuration",
+ show: true,
+ onAction: function() {
+ $scope.$broadcast("importExtensions", vm.selectedSource);
+ },
+ icon: "file_upload"
+ };
$scope.$on("filterMode", function($event, mode) {
vm.tabsHidden = mode;