Commit 5795e7f25919d65f9c5971c3e530aca327cd2c40
1 parent
32ece471
Import export extension configuration
Showing
6 changed files
with
152 additions
and
10 deletions
@@ -70,6 +70,7 @@ | @@ -70,6 +70,7 @@ | ||
70 | <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode && vm.grid.operatingItem().additionalInfo.gateway" md-on-select="vm.grid.triggerResize()" label="{{ 'extension.extensions' | translate }}"> | 70 | <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode && vm.grid.operatingItem().additionalInfo.gateway" md-on-select="vm.grid.triggerResize()" label="{{ 'extension.extensions' | translate }}"> |
71 | <tb-extension-table flex | 71 | <tb-extension-table flex |
72 | entity-id="vm.grid.operatingItem().id.id" | 72 | entity-id="vm.grid.operatingItem().id.id" |
73 | + entity-name="vm.grid.operatingItem().name" | ||
73 | entity-type="{{vm.types.entityType.device}}"> | 74 | entity-type="{{vm.types.entityType.device}}"> |
74 | </tb-extension-table> | 75 | </tb-extension-table> |
75 | </md-tab> | 76 | </md-tab> |
@@ -34,7 +34,8 @@ export default function ExtensionTableDirective() { | @@ -34,7 +34,8 @@ export default function ExtensionTableDirective() { | ||
34 | scope: true, | 34 | scope: true, |
35 | bindToController: { | 35 | bindToController: { |
36 | entityId: '=', | 36 | entityId: '=', |
37 | - entityType: '@' | 37 | + entityType: '@', |
38 | + entityName: '=' | ||
38 | }, | 39 | }, |
39 | controller: ExtensionTableController, | 40 | controller: ExtensionTableController, |
40 | controllerAs: 'vm', | 41 | controllerAs: 'vm', |
@@ -43,7 +44,7 @@ export default function ExtensionTableDirective() { | @@ -43,7 +44,7 @@ export default function ExtensionTableDirective() { | ||
43 | } | 44 | } |
44 | 45 | ||
45 | /*@ngInject*/ | 46 | /*@ngInject*/ |
46 | -function ExtensionTableController($scope, $filter, $document, $translate, types, $mdDialog, attributeService, telemetryWebsocketService) { | 47 | +function ExtensionTableController($scope, $filter, $document, $translate, types, $mdDialog, attributeService, telemetryWebsocketService, importExport) { |
47 | 48 | ||
48 | let vm = this; | 49 | let vm = this; |
49 | 50 | ||
@@ -328,4 +329,21 @@ function ExtensionTableController($scope, $filter, $document, $translate, types, | @@ -328,4 +329,21 @@ function ExtensionTableController($scope, $filter, $document, $translate, types, | ||
328 | return num; | 329 | return num; |
329 | } | 330 | } |
330 | } | 331 | } |
332 | + | ||
333 | + vm.importExtensions = function () { | ||
334 | + importExport.importExtension({"entityType":vm.entityType, "entityId":vm.entityId, "successFunc":reloadExtensions}); | ||
335 | + }; | ||
336 | + vm.exportExtensions = function () { | ||
337 | + importExport.exportToPc(vm.extensionsJSON, vm.entityName + '_configuration.json'); | ||
338 | + }; | ||
339 | + | ||
340 | + vm.exportExtension = function ($event, extension) { | ||
341 | + if ($event) { | ||
342 | + $event.stopPropagation(); | ||
343 | + } | ||
344 | + importExport.exportToPc(extension, vm.entityName +'_'+ extension.id +'_configuration.json'); | ||
345 | + }; | ||
346 | + | ||
347 | + | ||
348 | + | ||
331 | } | 349 | } |
@@ -16,10 +16,19 @@ | @@ -16,10 +16,19 @@ | ||
16 | @import '../../scss/constants'; | 16 | @import '../../scss/constants'; |
17 | 17 | ||
18 | 18 | ||
19 | -.extension-table md-input-container .md-errors-spacer { | ||
20 | - min-height: 0; | 19 | +.extension-table { |
20 | + | ||
21 | + md-input-container .md-errors-spacer { | ||
22 | + min-height: 0; | ||
23 | + } | ||
24 | + | ||
25 | + &.tb-data-table table.md-table tbody tr td.tb-action-cell, | ||
26 | + &.tb-data-table table.md-table.md-row-select tbody tr td.tb-action-cell { | ||
27 | + width: 114px; | ||
28 | + } | ||
21 | } | 29 | } |
22 | 30 | ||
31 | + | ||
23 | .extension__syncStatus--black { | 32 | .extension__syncStatus--black { |
24 | color: #000000!important; | 33 | color: #000000!important; |
25 | } | 34 | } |
@@ -23,6 +23,19 @@ | @@ -23,6 +23,19 @@ | ||
23 | <div class="md-toolbar-tools"> | 23 | <div class="md-toolbar-tools"> |
24 | <span translate>{{ 'extension.extensions' }}</span> | 24 | <span translate>{{ 'extension.extensions' }}</span> |
25 | <span flex></span> | 25 | <span flex></span> |
26 | + | ||
27 | + <md-button class="md-icon-button" ng-click="vm.importExtensions($event)"> | ||
28 | + <md-icon>file_upload</md-icon> | ||
29 | + <md-tooltip md-direction="top"> | ||
30 | + {{ 'extension.sync.import-extensions-configuration' | translate }} | ||
31 | + </md-tooltip> | ||
32 | + </md-button> | ||
33 | + <md-button class="md-icon-button" ng-click="vm.exportExtensions()"> | ||
34 | + <md-icon>file_download</md-icon> | ||
35 | + <md-tooltip md-direction="top"> | ||
36 | + {{ 'extension.sync.export-extensions-configuration' | translate }} | ||
37 | + </md-tooltip> | ||
38 | + </md-button> | ||
26 | <md-button class="md-icon-button" ng-click="vm.addExtension($event)"> | 39 | <md-button class="md-icon-button" ng-click="vm.addExtension($event)"> |
27 | <md-icon>add</md-icon> | 40 | <md-icon>add</md-icon> |
28 | <md-tooltip md-direction="top"> | 41 | <md-tooltip md-direction="top"> |
@@ -44,7 +57,7 @@ | @@ -44,7 +57,7 @@ | ||
44 | </div> | 57 | </div> |
45 | </md-toolbar> | 58 | </md-toolbar> |
46 | <md-toolbar class="md-table-toolbar md-default" ng-show="!vm.selectedExtensions.length | 59 | <md-toolbar class="md-table-toolbar md-default" ng-show="!vm.selectedExtensions.length |
47 | - && vm.query.search != null""> | 60 | + && vm.query.search != null"> |
48 | <div class="md-toolbar-tools"> | 61 | <div class="md-toolbar-tools"> |
49 | <md-button class="md-icon-button" aria-label="{{ 'action.search' | translate }}"> | 62 | <md-button class="md-icon-button" aria-label="{{ 'action.search' | translate }}"> |
50 | <md-icon aria-label="{{ 'action.search' | translate }}" class="material-icons">search</md-icon> | 63 | <md-icon aria-label="{{ 'action.search' | translate }}" class="material-icons">search</md-icon> |
@@ -111,6 +124,12 @@ | @@ -111,6 +124,12 @@ | ||
111 | <td md-cell>{{ extension.id }}</td> | 124 | <td md-cell>{{ extension.id }}</td> |
112 | <td md-cell>{{ extension.type }}</td> | 125 | <td md-cell>{{ extension.type }}</td> |
113 | <td md-cell class="tb-action-cell"> | 126 | <td md-cell class="tb-action-cell"> |
127 | + <md-button class="md-icon-button" aria-label="{{ 'action.edit' | translate }}" ng-click="vm.exportExtension($event, extension)"> | ||
128 | + <md-icon aria-label="{{ 'action.edit' | translate }}" class="material-icons">file_download</md-icon> | ||
129 | + <md-tooltip md-direction="top"> | ||
130 | + {{ 'extension.export-extension' | translate }} | ||
131 | + </md-tooltip> | ||
132 | + </md-button> | ||
114 | <md-button class="md-icon-button" aria-label="{{ 'action.edit' | translate }}" ng-click="vm.editExtension($event, extension)"> | 133 | <md-button class="md-icon-button" aria-label="{{ 'action.edit' | translate }}" ng-click="vm.editExtension($event, extension)"> |
115 | <md-icon aria-label="{{ 'action.edit' | translate }}" class="material-icons">edit</md-icon> | 134 | <md-icon aria-label="{{ 'action.edit' | translate }}" class="material-icons">edit</md-icon> |
116 | <md-tooltip md-direction="top"> | 135 | <md-tooltip md-direction="top"> |
@@ -24,8 +24,9 @@ import entityAliasesTemplate from '../entity/alias/entity-aliases.tpl.html'; | @@ -24,8 +24,9 @@ import entityAliasesTemplate from '../entity/alias/entity-aliases.tpl.html'; | ||
24 | /* eslint-disable no-undef, angular/window-service, angular/document-service */ | 24 | /* eslint-disable no-undef, angular/window-service, angular/document-service */ |
25 | 25 | ||
26 | /*@ngInject*/ | 26 | /*@ngInject*/ |
27 | -export default function ImportExport($log, $translate, $q, $mdDialog, $document, itembuffer, utils, types, dashboardUtils, | ||
28 | - entityService, dashboardService, pluginService, ruleService, widgetService, toast) { | 27 | +export default function ImportExport($log, $translate, $q, $mdDialog, $document, $http, itembuffer, utils, types, |
28 | + dashboardUtils, entityService, dashboardService, pluginService, ruleService, | ||
29 | + widgetService, toast, attributeService) { | ||
29 | 30 | ||
30 | 31 | ||
31 | var service = { | 32 | var service = { |
@@ -40,8 +41,11 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document, | @@ -40,8 +41,11 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document, | ||
40 | exportWidgetType: exportWidgetType, | 41 | exportWidgetType: exportWidgetType, |
41 | importWidgetType: importWidgetType, | 42 | importWidgetType: importWidgetType, |
42 | exportWidgetsBundle: exportWidgetsBundle, | 43 | exportWidgetsBundle: exportWidgetsBundle, |
43 | - importWidgetsBundle: importWidgetsBundle | ||
44 | - } | 44 | + importWidgetsBundle: importWidgetsBundle, |
45 | + exportExtension: exportExtension, | ||
46 | + importExtension: importExtension, | ||
47 | + exportToPc: exportToPc | ||
48 | + }; | ||
45 | 49 | ||
46 | return service; | 50 | return service; |
47 | 51 | ||
@@ -614,6 +618,89 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document, | @@ -614,6 +618,89 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document, | ||
614 | return true; | 618 | return true; |
615 | } | 619 | } |
616 | 620 | ||
621 | + | ||
622 | + | ||
623 | + function exportExtension(extensionId) { | ||
624 | + | ||
625 | + getExtension(extensionId) | ||
626 | + .then( | ||
627 | + function success(extension) { | ||
628 | + var name = extension.title; | ||
629 | + name = name.toLowerCase().replace(/\W/g,"_"); | ||
630 | + exportToPc(prepareExport(extension), name + '.json'); | ||
631 | + }, | ||
632 | + function fail(rejection) { | ||
633 | + var message = rejection; | ||
634 | + if (!message) { | ||
635 | + message = $translate.instant('error.unknown-error'); | ||
636 | + } | ||
637 | + toast.showError($translate.instant('extension.export-failed-error', {error: message})); | ||
638 | + } | ||
639 | + ); | ||
640 | + | ||
641 | + | ||
642 | + function getExtension(extensionId) { | ||
643 | + var deferred = $q.defer(); | ||
644 | + var url = '/api/plugins/telemetry/DEVICE/' + extensionId; | ||
645 | + $http.get(url, null) | ||
646 | + .then(function success(response) { | ||
647 | + deferred.resolve(response.data); | ||
648 | + }, function fail() { | ||
649 | + deferred.reject(); | ||
650 | + }); | ||
651 | + return deferred.promise; | ||
652 | + } | ||
653 | + | ||
654 | + } | ||
655 | + | ||
656 | + function importExtension(options) { | ||
657 | + var deferred = $q.defer(); | ||
658 | + openImportDialog(options, 'extension.import-extensions', 'extension.file') | ||
659 | + .then( | ||
660 | + function success(extension) { | ||
661 | + if (!validateImportedExtension(extension)) { | ||
662 | + toast.showError($translate.instant('extension.invalid-file-error')); | ||
663 | + deferred.reject(); | ||
664 | + } else { | ||
665 | + attributeService | ||
666 | + .saveEntityAttributes( | ||
667 | + options.entityType, | ||
668 | + options.entityId, | ||
669 | + types.attributesScope.shared.value, | ||
670 | + [{ | ||
671 | + key: "configuration", | ||
672 | + value: angular.toJson(extension) | ||
673 | + }] | ||
674 | + ) | ||
675 | + .then(function success() { | ||
676 | + options.successFunc(); | ||
677 | + }); | ||
678 | + | ||
679 | + } | ||
680 | + }, | ||
681 | + function fail() { | ||
682 | + deferred.reject(); | ||
683 | + } | ||
684 | + ); | ||
685 | + return deferred.promise; | ||
686 | + } | ||
687 | + | ||
688 | + | ||
689 | + function validateImportedExtension(configuration) { | ||
690 | + if (configuration.length) { | ||
691 | + for (let i = 0; i < configuration.length; i++) { | ||
692 | + if (angular.isUndefined(configuration[i].configuration) || angular.isUndefined(configuration[i].id )|| angular.isUndefined(configuration[i].type)) { | ||
693 | + return false; | ||
694 | + } | ||
695 | + } | ||
696 | + } else { | ||
697 | + return false; | ||
698 | + } | ||
699 | + return true; | ||
700 | + } | ||
701 | + | ||
702 | + | ||
703 | + | ||
617 | function processEntityAliases(entityAliases, aliasIds) { | 704 | function processEntityAliases(entityAliases, aliasIds) { |
618 | var deferred = $q.defer(); | 705 | var deferred = $q.defer(); |
619 | var missingEntityAliases = {}; | 706 | var missingEntityAliases = {}; |
@@ -845,8 +845,16 @@ export default angular.module('thingsboard.locale', []) | @@ -845,8 +845,16 @@ export default angular.module('thingsboard.locale', []) | ||
845 | "sync": "Sync", | 845 | "sync": "Sync", |
846 | "not-sync": "Not sync", | 846 | "not-sync": "Not sync", |
847 | "last-sync-time": "Last sync time", | 847 | "last-sync-time": "Last sync time", |
848 | - "not-available": "Not available" | 848 | + "not-available": "Not available", |
849 | + "export-extensions-configuration":"Export extensions configuration", | ||
850 | + "import-extensions-configuration":"Import extensions configuration" | ||
849 | }, | 851 | }, |
852 | + | ||
853 | + "import-extensions": "Import extensions", | ||
854 | + "import-extension": "Import extension", | ||
855 | + "export-extension": "Export extension", | ||
856 | + "file": "Extensions file", | ||
857 | + "invalid-file-error": "Invalid extension file" | ||
850 | }, | 858 | }, |
851 | "fullscreen": { | 859 | "fullscreen": { |
852 | "expand": "Expand to fullscreen", | 860 | "expand": "Expand to fullscreen", |