Commit 5795e7f25919d65f9c5971c3e530aca327cd2c40

Authored by oleg
1 parent 32ece471

Import export extension configuration

@@ -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",