Commit 5795e7f25919d65f9c5971c3e530aca327cd2c40
1 parent
32ece471
Import export extension configuration
Showing
6 changed files
with
152 additions
and
10 deletions
... | ... | @@ -70,6 +70,7 @@ |
70 | 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 | 71 | <tb-extension-table flex |
72 | 72 | entity-id="vm.grid.operatingItem().id.id" |
73 | + entity-name="vm.grid.operatingItem().name" | |
73 | 74 | entity-type="{{vm.types.entityType.device}}"> |
74 | 75 | </tb-extension-table> |
75 | 76 | </md-tab> | ... | ... |
... | ... | @@ -34,7 +34,8 @@ export default function ExtensionTableDirective() { |
34 | 34 | scope: true, |
35 | 35 | bindToController: { |
36 | 36 | entityId: '=', |
37 | - entityType: '@' | |
37 | + entityType: '@', | |
38 | + entityName: '=' | |
38 | 39 | }, |
39 | 40 | controller: ExtensionTableController, |
40 | 41 | controllerAs: 'vm', |
... | ... | @@ -43,7 +44,7 @@ export default function ExtensionTableDirective() { |
43 | 44 | } |
44 | 45 | |
45 | 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 | 49 | let vm = this; |
49 | 50 | |
... | ... | @@ -328,4 +329,21 @@ function ExtensionTableController($scope, $filter, $document, $translate, types, |
328 | 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 | } |
\ No newline at end of file | ... | ... |
... | ... | @@ -16,10 +16,19 @@ |
16 | 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 | 32 | .extension__syncStatus--black { |
24 | 33 | color: #000000!important; |
25 | 34 | } | ... | ... |
... | ... | @@ -23,6 +23,19 @@ |
23 | 23 | <div class="md-toolbar-tools"> |
24 | 24 | <span translate>{{ 'extension.extensions' }}</span> |
25 | 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 | 39 | <md-button class="md-icon-button" ng-click="vm.addExtension($event)"> |
27 | 40 | <md-icon>add</md-icon> |
28 | 41 | <md-tooltip md-direction="top"> |
... | ... | @@ -44,7 +57,7 @@ |
44 | 57 | </div> |
45 | 58 | </md-toolbar> |
46 | 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 | 61 | <div class="md-toolbar-tools"> |
49 | 62 | <md-button class="md-icon-button" aria-label="{{ 'action.search' | translate }}"> |
50 | 63 | <md-icon aria-label="{{ 'action.search' | translate }}" class="material-icons">search</md-icon> |
... | ... | @@ -111,6 +124,12 @@ |
111 | 124 | <td md-cell>{{ extension.id }}</td> |
112 | 125 | <td md-cell>{{ extension.type }}</td> |
113 | 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 | 133 | <md-button class="md-icon-button" aria-label="{{ 'action.edit' | translate }}" ng-click="vm.editExtension($event, extension)"> |
115 | 134 | <md-icon aria-label="{{ 'action.edit' | translate }}" class="material-icons">edit</md-icon> |
116 | 135 | <md-tooltip md-direction="top"> | ... | ... |
... | ... | @@ -24,8 +24,9 @@ import entityAliasesTemplate from '../entity/alias/entity-aliases.tpl.html'; |
24 | 24 | /* eslint-disable no-undef, angular/window-service, angular/document-service */ |
25 | 25 | |
26 | 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 | 32 | var service = { |
... | ... | @@ -40,8 +41,11 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document, |
40 | 41 | exportWidgetType: exportWidgetType, |
41 | 42 | importWidgetType: importWidgetType, |
42 | 43 | exportWidgetsBundle: exportWidgetsBundle, |
43 | - importWidgetsBundle: importWidgetsBundle | |
44 | - } | |
44 | + importWidgetsBundle: importWidgetsBundle, | |
45 | + exportExtension: exportExtension, | |
46 | + importExtension: importExtension, | |
47 | + exportToPc: exportToPc | |
48 | + }; | |
45 | 49 | |
46 | 50 | return service; |
47 | 51 | |
... | ... | @@ -614,6 +618,89 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document, |
614 | 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 | 704 | function processEntityAliases(entityAliases, aliasIds) { |
618 | 705 | var deferred = $q.defer(); |
619 | 706 | var missingEntityAliases = {}; | ... | ... |
... | ... | @@ -845,8 +845,16 @@ export default angular.module('thingsboard.locale', []) |
845 | 845 | "sync": "Sync", |
846 | 846 | "not-sync": "Not sync", |
847 | 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 | 859 | "fullscreen": { |
852 | 860 | "expand": "Expand to fullscreen", | ... | ... |