Import/Export. Bug fixes.
This commit is contained in:
		
							parent
							
								
									f78b8e55f1
								
							
						
					
					
						commit
						9c616bd3a3
					
				@ -54,6 +54,7 @@
 | 
			
		||||
    "json-schema-defaults": "^0.2.0",
 | 
			
		||||
    "justgage": "^1.2.2",
 | 
			
		||||
    "material-ui": "^0.16.1",
 | 
			
		||||
    "material-ui-number-input": "^5.0.16",
 | 
			
		||||
    "md-color-picker": "^0.2.6",
 | 
			
		||||
    "mdPickers": "git://github.com/alenaksu/mdPickers.git#0.7.5",
 | 
			
		||||
    "moment": "^2.15.0",
 | 
			
		||||
 | 
			
		||||
@ -88,10 +88,10 @@ function DeviceService($http, $q, $filter, telemetryWebsocketService, types) {
 | 
			
		||||
        return deferred.promise;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function getDevice(deviceId) {
 | 
			
		||||
    function getDevice(deviceId, ignoreErrors) {
 | 
			
		||||
        var deferred = $q.defer();
 | 
			
		||||
        var url = '/api/device/' + deviceId;
 | 
			
		||||
        $http.get(url, null).then(function success(response) {
 | 
			
		||||
        $http.get(url, { ignoreErrors: ignoreErrors }).then(function success(response) {
 | 
			
		||||
            deferred.resolve(response.data);
 | 
			
		||||
        }, function fail(response) {
 | 
			
		||||
            deferred.reject(response.data);
 | 
			
		||||
 | 
			
		||||
@ -58,8 +58,10 @@ function Dashboard() {
 | 
			
		||||
            isMobile: '=',
 | 
			
		||||
            isMobileDisabled: '=?',
 | 
			
		||||
            isEditActionEnabled: '=',
 | 
			
		||||
            isExportActionEnabled: '=',
 | 
			
		||||
            isRemoveActionEnabled: '=',
 | 
			
		||||
            onEditWidget: '&?',
 | 
			
		||||
            onExportWidget: '&?',
 | 
			
		||||
            onRemoveWidget: '&?',
 | 
			
		||||
            onWidgetMouseDown: '&?',
 | 
			
		||||
            onWidgetClicked: '&?',
 | 
			
		||||
@ -139,6 +141,7 @@ function DashboardController($scope, $rootScope, $element, $timeout, $log, toast
 | 
			
		||||
    vm.showWidgetTitle = showWidgetTitle;
 | 
			
		||||
    vm.hasTimewindow = hasTimewindow;
 | 
			
		||||
    vm.editWidget = editWidget;
 | 
			
		||||
    vm.exportWidget = exportWidget;
 | 
			
		||||
    vm.removeWidget = removeWidget;
 | 
			
		||||
    vm.loading = loading;
 | 
			
		||||
 | 
			
		||||
@ -413,6 +416,16 @@ function DashboardController($scope, $rootScope, $element, $timeout, $log, toast
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function exportWidget ($event, widget) {
 | 
			
		||||
        resetWidgetClick();
 | 
			
		||||
        if ($event) {
 | 
			
		||||
            $event.stopPropagation();
 | 
			
		||||
        }
 | 
			
		||||
        if (vm.isExportActionEnabled && vm.onExportWidget) {
 | 
			
		||||
            vm.onExportWidget({event: $event, widget: widget});
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function removeWidget($event, widget) {
 | 
			
		||||
        resetWidgetClick();
 | 
			
		||||
        if ($event) {
 | 
			
		||||
 | 
			
		||||
@ -62,6 +62,18 @@
 | 
			
		||||
											edit
 | 
			
		||||
										</md-icon>
 | 
			
		||||
									</md-button>
 | 
			
		||||
									<md-button ng-show="vm.isExportActionEnabled && !vm.isWidgetExpanded"
 | 
			
		||||
											   ng-disabled="vm.loading()"
 | 
			
		||||
											   class="md-icon-button md-primary"
 | 
			
		||||
											   ng-click="vm.exportWidget($event, widget)"
 | 
			
		||||
											   aria-label="{{ 'widget.export' | translate }}">
 | 
			
		||||
										<md-tooltip md-direction="top">
 | 
			
		||||
											{{ 'widget.export' | translate }}
 | 
			
		||||
										</md-tooltip>
 | 
			
		||||
										<md-icon class="material-icons">
 | 
			
		||||
											file_download
 | 
			
		||||
										</md-icon>
 | 
			
		||||
									</md-button>
 | 
			
		||||
									<md-button ng-show="vm.isRemoveActionEnabled && !vm.isWidgetExpanded"
 | 
			
		||||
										ng-disabled="vm.loading()"
 | 
			
		||||
										class="md-icon-button md-primary"
 | 
			
		||||
 | 
			
		||||
@ -327,6 +327,10 @@ function GridController($scope, $state, $mdDialog, $document, $q, $timeout, $tra
 | 
			
		||||
                icon: "add"
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
        vm.addItemActionsOpen = false;
 | 
			
		||||
 | 
			
		||||
        vm.addItemActions = vm.config.addItemActions || [];
 | 
			
		||||
 | 
			
		||||
        vm.onGridInited = vm.config.onGridInited || function () {
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -79,7 +79,7 @@
 | 
			
		||||
    </tb-details-sidenav>
 | 
			
		||||
</section>
 | 
			
		||||
 | 
			
		||||
<section layout="row" layout-wrap class="tb-footer-buttons md-fab ">
 | 
			
		||||
<section layout="row" layout-wrap class="tb-footer-buttons md-fab " layout-align="start end">
 | 
			
		||||
    <md-button ng-disabled="loading" ng-show="vm.items.selectedCount > 0" class="tb-btn-footer md-accent md-hue-2 md-fab" ng-repeat="groupAction in vm.groupActionsList"
 | 
			
		||||
               ng-click="groupAction.onAction($event, vm.items)" aria-label="{{ groupAction.name() }}">
 | 
			
		||||
        <md-tooltip md-direction="top">
 | 
			
		||||
@ -93,10 +93,29 @@
 | 
			
		||||
        </md-tooltip>
 | 
			
		||||
        <ng-md-icon icon="arrow_drop_up"></ng-md-icon>
 | 
			
		||||
    </md-button>
 | 
			
		||||
    <md-button ng-disabled="loading" ng-if="vm.addItemAction.name()" class="tb-btn-footer md-accent md-hue-2 md-fab" ng-click="vm.addItemAction.onAction($event)" aria-label="{{ vm.addItemAction.name() }}" >
 | 
			
		||||
    <md-button ng-disabled="loading" ng-if="vm.addItemAction.name() && vm.addItemActions.length == 0" class="tb-btn-footer md-accent md-hue-2 md-fab" ng-click="vm.addItemAction.onAction($event)" aria-label="{{ vm.addItemAction.name() }}" >
 | 
			
		||||
        <md-tooltip md-direction="top">
 | 
			
		||||
            {{ vm.addItemAction.details() }}
 | 
			
		||||
        </md-tooltip>
 | 
			
		||||
        <ng-md-icon icon="{{ vm.addItemAction.icon }}"></ng-md-icon>
 | 
			
		||||
    </md-button>
 | 
			
		||||
    <md-fab-speed-dial ng-disabled="loading" ng-if="vm.addItemAction.name() && vm.addItemActions.length > 0" md-open="vm.addItemActionsOpen" class="md-scale" md-direction="up" ng-if="vm.addItemAction.name()">
 | 
			
		||||
        <md-fab-trigger>
 | 
			
		||||
            <md-button ng-disabled="loading" class="tb-btn-footer md-accent md-hue-2 md-fab" aria-label="{{ vm.addItemAction.name() }}" >
 | 
			
		||||
                <md-tooltip md-direction="top">
 | 
			
		||||
                    {{ vm.addItemAction.details() }}
 | 
			
		||||
                </md-tooltip>
 | 
			
		||||
                <ng-md-icon icon="{{ vm.addItemAction.icon }}"></ng-md-icon>
 | 
			
		||||
            </md-button>
 | 
			
		||||
        </md-fab-trigger>
 | 
			
		||||
        <md-fab-actions>
 | 
			
		||||
            <md-button ng-disabled="loading" class="md-accent md-hue-2 md-fab" ng-repeat="addItemAction in vm.addItemActions"
 | 
			
		||||
                       ng-click="addItemAction.onAction($event)" aria-label="{{ addItemAction.name() }}" >
 | 
			
		||||
                <md-tooltip md-direction="top">
 | 
			
		||||
                    {{ addItemAction.details() }}
 | 
			
		||||
                </md-tooltip>
 | 
			
		||||
                <ng-md-icon icon="{{addItemAction.icon}}"></ng-md-icon>
 | 
			
		||||
            </md-button>
 | 
			
		||||
        </md-fab-actions>
 | 
			
		||||
    </md-fab-speed-dial>
 | 
			
		||||
</section>
 | 
			
		||||
@ -15,7 +15,7 @@
 | 
			
		||||
 */
 | 
			
		||||
import React from 'react';
 | 
			
		||||
import ThingsboardBaseComponent from './json-form-base-component.jsx';
 | 
			
		||||
import TextField from 'material-ui/TextField';
 | 
			
		||||
import NumberInput from 'material-ui-number-input';
 | 
			
		||||
 | 
			
		||||
class ThingsboardNumber extends React.Component {
 | 
			
		||||
 | 
			
		||||
@ -63,16 +63,18 @@ class ThingsboardNumber extends React.Component {
 | 
			
		||||
        if (this.state.focused) {
 | 
			
		||||
            fieldClass += " tb-focused";
 | 
			
		||||
        }
 | 
			
		||||
        var value = this.state.lastSuccessfulValue;
 | 
			
		||||
        value = Number(value);
 | 
			
		||||
 | 
			
		||||
        return (
 | 
			
		||||
            <TextField
 | 
			
		||||
            <NumberInput
 | 
			
		||||
                className={fieldClass}
 | 
			
		||||
                type={this.props.form.type}
 | 
			
		||||
                strategy="allow"
 | 
			
		||||
                floatingLabelText={this.props.form.title}
 | 
			
		||||
                hintText={this.props.form.placeholder}
 | 
			
		||||
                errorText={this.props.error}
 | 
			
		||||
                onChange={this.preValidationCheck}
 | 
			
		||||
                defaultValue={this.state.lastSuccessfulValue}
 | 
			
		||||
                defaultValue={value}
 | 
			
		||||
                ref="numberField"
 | 
			
		||||
                disabled={this.props.form.readonly}
 | 
			
		||||
                onFocus={this.onFocus}
 | 
			
		||||
 | 
			
		||||
@ -102,10 +102,12 @@ export default function AddWidgetController($scope, widgetService, deviceService
 | 
			
		||||
            controllerAs: 'vm',
 | 
			
		||||
            templateUrl: deviceAliasesTemplate,
 | 
			
		||||
            locals: {
 | 
			
		||||
                deviceAliases: angular.copy(vm.dashboard.configuration.deviceAliases),
 | 
			
		||||
                aliasToWidgetsMap: null,
 | 
			
		||||
                isSingleDevice: true,
 | 
			
		||||
                singleDeviceAlias: singleDeviceAlias
 | 
			
		||||
                config: {
 | 
			
		||||
                    deviceAliases: angular.copy(vm.dashboard.configuration.deviceAliases),
 | 
			
		||||
                    widgets: null,
 | 
			
		||||
                    isSingleDevice: true,
 | 
			
		||||
                    singleDeviceAlias: singleDeviceAlias
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
            parent: angular.element($document[0].body),
 | 
			
		||||
            fullscreen: true,
 | 
			
		||||
 | 
			
		||||
@ -17,6 +17,7 @@
 | 
			
		||||
-->
 | 
			
		||||
<md-button ng-click="onAssignToCustomer({event: $event})" ng-show="!isEdit && dashboardScope === 'tenant'" class="md-raised md-primary">{{ 'dashboard.assign-to-customer' | translate }}</md-button>
 | 
			
		||||
<md-button ng-click="onUnassignFromCustomer({event: $event})" ng-show="!isEdit && dashboardScope === 'customer'" class="md-raised md-primary">{{ 'dashboard.unassign-from-customer' | translate }}</md-button>
 | 
			
		||||
<md-button ng-click="onExportDashboard({event: $event})" ng-show="!isEdit && dashboardScope === 'tenant'" class="md-raised md-primary">{{ 'dashboard.export' | translate }}</md-button>
 | 
			
		||||
<md-button ng-click="onDeleteDashboard({event: $event})" ng-show="!isEdit && dashboardScope === 'tenant'" class="md-raised md-primary">{{ 'dashboard.delete' | translate }}</md-button>
 | 
			
		||||
 | 
			
		||||
<md-content class="md-padding" layout="column">
 | 
			
		||||
 | 
			
		||||
@ -23,7 +23,7 @@ import addWidgetTemplate from './add-widget.tpl.html';
 | 
			
		||||
 | 
			
		||||
/*@ngInject*/
 | 
			
		||||
export default function DashboardController(types, widgetService, userService,
 | 
			
		||||
                                            dashboardService, itembuffer, hotkeys, $window, $rootScope,
 | 
			
		||||
                                            dashboardService, itembuffer, importExport, hotkeys, $window, $rootScope,
 | 
			
		||||
                                            $scope, $state, $stateParams, $mdDialog, $timeout, $document, $q, $translate, $filter) {
 | 
			
		||||
 | 
			
		||||
    var user = userService.getCurrentUser();
 | 
			
		||||
@ -53,6 +53,8 @@ export default function DashboardController(types, widgetService, userService,
 | 
			
		||||
    vm.prepareDashboardContextMenu = prepareDashboardContextMenu;
 | 
			
		||||
    vm.prepareWidgetContextMenu = prepareWidgetContextMenu;
 | 
			
		||||
    vm.editWidget = editWidget;
 | 
			
		||||
    vm.exportWidget = exportWidget;
 | 
			
		||||
    vm.importWidget = importWidget;
 | 
			
		||||
    vm.isTenantAdmin = isTenantAdmin;
 | 
			
		||||
    vm.loadDashboard = loadDashboard;
 | 
			
		||||
    vm.noData = noData;
 | 
			
		||||
@ -210,44 +212,17 @@ export default function DashboardController(types, widgetService, userService,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function openDeviceAliases($event) {
 | 
			
		||||
        var aliasToWidgetsMap = {};
 | 
			
		||||
        var widgetsTitleList;
 | 
			
		||||
        for (var w in vm.widgets) {
 | 
			
		||||
            var widget = vm.widgets[w];
 | 
			
		||||
            if (widget.type === types.widgetType.rpc.value) {
 | 
			
		||||
                if (widget.config.targetDeviceAliasIds && widget.config.targetDeviceAliasIds.length > 0) {
 | 
			
		||||
                    var targetDeviceAliasId = widget.config.targetDeviceAliasIds[0];
 | 
			
		||||
                    widgetsTitleList = aliasToWidgetsMap[targetDeviceAliasId];
 | 
			
		||||
                    if (!widgetsTitleList) {
 | 
			
		||||
                        widgetsTitleList = [];
 | 
			
		||||
                        aliasToWidgetsMap[targetDeviceAliasId] = widgetsTitleList;
 | 
			
		||||
                    }
 | 
			
		||||
                    widgetsTitleList.push(widget.config.title);
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                for (var i in widget.config.datasources) {
 | 
			
		||||
                    var datasource = widget.config.datasources[i];
 | 
			
		||||
                    if (datasource.type === types.datasourceType.device && datasource.deviceAliasId) {
 | 
			
		||||
                        widgetsTitleList = aliasToWidgetsMap[datasource.deviceAliasId];
 | 
			
		||||
                        if (!widgetsTitleList) {
 | 
			
		||||
                            widgetsTitleList = [];
 | 
			
		||||
                            aliasToWidgetsMap[datasource.deviceAliasId] = widgetsTitleList;
 | 
			
		||||
                        }
 | 
			
		||||
                        widgetsTitleList.push(widget.config.title);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $mdDialog.show({
 | 
			
		||||
            controller: 'DeviceAliasesController',
 | 
			
		||||
            controllerAs: 'vm',
 | 
			
		||||
            templateUrl: deviceAliasesTemplate,
 | 
			
		||||
            locals: {
 | 
			
		||||
                deviceAliases: angular.copy(vm.dashboard.configuration.deviceAliases),
 | 
			
		||||
                aliasToWidgetsMap: aliasToWidgetsMap,
 | 
			
		||||
                isSingleDevice: false,
 | 
			
		||||
                singleDeviceAlias: null
 | 
			
		||||
                config: {
 | 
			
		||||
                    deviceAliases: angular.copy(vm.dashboard.configuration.deviceAliases),
 | 
			
		||||
                    widgets: vm.widgets,
 | 
			
		||||
                    isSingleDevice: false,
 | 
			
		||||
                    singleDeviceAlias: null
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
            parent: angular.element($document[0].body),
 | 
			
		||||
            skipHide: true,
 | 
			
		||||
@ -300,6 +275,16 @@ export default function DashboardController(types, widgetService, userService,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function exportWidget($event, widget) {
 | 
			
		||||
        $event.stopPropagation();
 | 
			
		||||
        importExport.exportWidget(vm.dashboard, widget);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function importWidget($event) {
 | 
			
		||||
        $event.stopPropagation();
 | 
			
		||||
        importExport.importWidget($event, vm.dashboard);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function widgetMouseDown($event, widget) {
 | 
			
		||||
        if (vm.isEdit && !vm.isEditingWidget) {
 | 
			
		||||
            vm.dashboardContainer.selectWidget(widget, 0);
 | 
			
		||||
@ -438,48 +423,7 @@ export default function DashboardController(types, widgetService, userService,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function copyWidget($event, widget) {
 | 
			
		||||
        var aliasesInfo = {
 | 
			
		||||
            datasourceAliases: {},
 | 
			
		||||
            targetDeviceAliases: {}
 | 
			
		||||
        };
 | 
			
		||||
        var originalColumns = 24;
 | 
			
		||||
        if (vm.dashboard.configuration.gridSettings &&
 | 
			
		||||
            vm.dashboard.configuration.gridSettings.columns) {
 | 
			
		||||
            originalColumns = vm.dashboard.configuration.gridSettings.columns;
 | 
			
		||||
        }
 | 
			
		||||
        if (widget.config && vm.dashboard.configuration
 | 
			
		||||
            && vm.dashboard.configuration.deviceAliases) {
 | 
			
		||||
            var deviceAlias;
 | 
			
		||||
            if (widget.config.datasources) {
 | 
			
		||||
                for (var i=0;i<widget.config.datasources.length;i++) {
 | 
			
		||||
                    var datasource = widget.config.datasources[i];
 | 
			
		||||
                    if (datasource.type === types.datasourceType.device && datasource.deviceAliasId) {
 | 
			
		||||
                        deviceAlias = vm.dashboard.configuration.deviceAliases[datasource.deviceAliasId];
 | 
			
		||||
                        if (deviceAlias) {
 | 
			
		||||
                            aliasesInfo.datasourceAliases[i] = {
 | 
			
		||||
                                aliasName: deviceAlias.alias,
 | 
			
		||||
                                deviceId: deviceAlias.deviceId
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            if (widget.config.targetDeviceAliasIds) {
 | 
			
		||||
                for (i=0;i<widget.config.targetDeviceAliasIds.length;i++) {
 | 
			
		||||
                    var targetDeviceAliasId = widget.config.targetDeviceAliasIds[i];
 | 
			
		||||
                    if (targetDeviceAliasId) {
 | 
			
		||||
                        deviceAlias = vm.dashboard.configuration.deviceAliases[targetDeviceAliasId];
 | 
			
		||||
                        if (deviceAlias) {
 | 
			
		||||
                            aliasesInfo.targetDeviceAliases[i] = {
 | 
			
		||||
                                aliasName: deviceAlias.alias,
 | 
			
		||||
                                deviceId: deviceAlias.deviceId
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        itembuffer.copyWidget(widget, aliasesInfo, originalColumns);
 | 
			
		||||
        itembuffer.copyWidget(vm.dashboard, widget);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function helpLinkIdForWidgetType() {
 | 
			
		||||
 | 
			
		||||
@ -36,6 +36,7 @@ export default function DashboardDirective($compile, $templateCache) {
 | 
			
		||||
            theForm: '=',
 | 
			
		||||
            onAssignToCustomer: '&',
 | 
			
		||||
            onUnassignFromCustomer: '&',
 | 
			
		||||
            onExportDashboard: '&',
 | 
			
		||||
            onDeleteDashboard: '&'
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
@ -80,8 +80,10 @@
 | 
			
		||||
                is-mobile="vm.forceDashboardMobileMode"
 | 
			
		||||
                is-mobile-disabled="vm.widgetEditMode"
 | 
			
		||||
                is-edit-action-enabled="vm.isEdit || vm.widgetEditMode"
 | 
			
		||||
                is-export-action-enabled="vm.isEdit && !vm.widgetEditMode"
 | 
			
		||||
                is-remove-action-enabled="vm.isEdit && !vm.widgetEditMode"
 | 
			
		||||
                on-edit-widget="vm.editWidget(event, widget)"
 | 
			
		||||
                on-export-widget="vm.exportWidget(event, widget)"
 | 
			
		||||
                on-widget-mouse-down="vm.widgetMouseDown(event, widget)"
 | 
			
		||||
                on-widget-clicked="vm.widgetClicked(event, widget)"
 | 
			
		||||
                on-widget-context-menu="vm.widgetContextMenu(event, widget)"
 | 
			
		||||
@ -180,15 +182,38 @@
 | 
			
		||||
        </div>
 | 
			
		||||
    </tb-details-sidenav>
 | 
			
		||||
    <!-- </section> -->
 | 
			
		||||
    <section layout="row" layout-wrap class="tb-footer-buttons md-fab">
 | 
			
		||||
        <md-button ng-disabled="loading" ng-show="!vm.isAddingWidget && vm.isEdit && !vm.widgetEditMode"
 | 
			
		||||
                   class="tb-btn-footer md-accent md-hue-2 md-fab" ng-click="vm.addWidget($event)"
 | 
			
		||||
                   aria-label="{{ 'dashboard.add-widget' | translate }}">
 | 
			
		||||
            <md-tooltip md-direction="top">
 | 
			
		||||
                {{ 'dashboard.add-widget' | translate }}
 | 
			
		||||
            </md-tooltip>
 | 
			
		||||
            <ng-md-icon icon="add"></ng-md-icon>
 | 
			
		||||
        </md-button>
 | 
			
		||||
    <section layout="row" layout-wrap class="tb-footer-buttons md-fab" layout-align="start end">
 | 
			
		||||
        <md-fab-speed-dial ng-disabled="loading" ng-show="!vm.isAddingWidget && vm.isEdit && !vm.widgetEditMode"
 | 
			
		||||
                           md-open="vm.addItemActionsOpen" class="md-scale" md-direction="up">
 | 
			
		||||
            <md-fab-trigger>
 | 
			
		||||
                <md-button ng-disabled="loading"
 | 
			
		||||
                           class="tb-btn-footer md-accent md-hue-2 md-fab"
 | 
			
		||||
                           aria-label="{{ 'dashboard.add-widget' | translate }}">
 | 
			
		||||
                    <md-tooltip md-direction="top">
 | 
			
		||||
                        {{ 'dashboard.add-widget' | translate }}
 | 
			
		||||
                    </md-tooltip>
 | 
			
		||||
                    <ng-md-icon icon="add"></ng-md-icon>
 | 
			
		||||
                </md-button>
 | 
			
		||||
            </md-fab-trigger>
 | 
			
		||||
            <md-fab-actions>
 | 
			
		||||
                <md-button ng-disabled="loading"
 | 
			
		||||
                           class="tmd-accent md-hue-2 md-fab" ng-click="vm.addWidget($event)"
 | 
			
		||||
                           aria-label="{{ 'action.create' | translate }}">
 | 
			
		||||
                    <md-tooltip md-direction="top">
 | 
			
		||||
                        {{ 'dashboard.create-new-widget' | translate }}
 | 
			
		||||
                    </md-tooltip>
 | 
			
		||||
                    <ng-md-icon icon="insert_drive_file"></ng-md-icon>
 | 
			
		||||
                </md-button>
 | 
			
		||||
                <md-button ng-disabled="loading"
 | 
			
		||||
                           class="tmd-accent md-hue-2 md-fab" ng-click="vm.importWidget($event)"
 | 
			
		||||
                           aria-label="{{ 'action.import' | translate }}">
 | 
			
		||||
                    <md-tooltip md-direction="top">
 | 
			
		||||
                        {{ 'dashboard.import-widget' | translate }}
 | 
			
		||||
                    </md-tooltip>
 | 
			
		||||
                    <ng-md-icon icon="file_upload"></ng-md-icon>
 | 
			
		||||
                </md-button>
 | 
			
		||||
            </md-fab-actions>
 | 
			
		||||
        </md-fab-speed-dial>
 | 
			
		||||
        <md-button ng-if="vm.isTenantAdmin()" ng-show="vm.isEdit && !vm.isAddingWidget && !loading && !vm.widgetEditMode" ng-disabled="loading"
 | 
			
		||||
                   class="tb-btn-footer md-accent md-hue-2 md-fab"
 | 
			
		||||
                   aria-label="{{ 'action.apply' | translate }}"
 | 
			
		||||
 | 
			
		||||
@ -23,7 +23,7 @@ import addDashboardsToCustomerTemplate from './add-dashboards-to-customer.tpl.ht
 | 
			
		||||
/* eslint-enable import/no-unresolved, import/default */
 | 
			
		||||
 | 
			
		||||
/*@ngInject*/
 | 
			
		||||
export default function DashboardsController(userService, dashboardService, customerService, $scope, $controller, $state, $stateParams, $mdDialog, $document, $q, $translate) {
 | 
			
		||||
export default function DashboardsController(userService, dashboardService, customerService, importExport, $scope, $controller, $state, $stateParams, $mdDialog, $document, $q, $translate) {
 | 
			
		||||
 | 
			
		||||
    var customerId = $stateParams.customerId;
 | 
			
		||||
 | 
			
		||||
@ -86,6 +86,7 @@ export default function DashboardsController(userService, dashboardService, cust
 | 
			
		||||
 | 
			
		||||
    vm.assignToCustomer = assignToCustomer;
 | 
			
		||||
    vm.unassignFromCustomer = unassignFromCustomer;
 | 
			
		||||
    vm.exportDashboard = exportDashboard;
 | 
			
		||||
 | 
			
		||||
    initController();
 | 
			
		||||
 | 
			
		||||
@ -113,6 +114,14 @@ export default function DashboardsController(userService, dashboardService, cust
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            dashboardActionsList.push(
 | 
			
		||||
                {
 | 
			
		||||
                    onAction: function ($event, item) {
 | 
			
		||||
                        exportDashboard($event, item);
 | 
			
		||||
                    },
 | 
			
		||||
                    name: function() { $translate.instant('action.export') },
 | 
			
		||||
                    details: function() { return $translate.instant('dashboard.export') },
 | 
			
		||||
                    icon: "file_download"
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    onAction: function ($event, item) {
 | 
			
		||||
                        assignToCustomer($event, [ item.id.id ]);
 | 
			
		||||
@ -158,7 +167,27 @@ export default function DashboardsController(userService, dashboardService, cust
 | 
			
		||||
                }
 | 
			
		||||
            );
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            vm.dashboardGridConfig.addItemActions = [];
 | 
			
		||||
            vm.dashboardGridConfig.addItemActions.push({
 | 
			
		||||
                onAction: function ($event) {
 | 
			
		||||
                    vm.grid.addItem($event);
 | 
			
		||||
                },
 | 
			
		||||
                name: function() { return $translate.instant('action.create') },
 | 
			
		||||
                details: function() { return $translate.instant('dashboard.create-new-dashboard') },
 | 
			
		||||
                icon: "insert_drive_file"
 | 
			
		||||
            });
 | 
			
		||||
            vm.dashboardGridConfig.addItemActions.push({
 | 
			
		||||
                onAction: function ($event) {
 | 
			
		||||
                    importExport.importDashboard($event).then(
 | 
			
		||||
                        function() {
 | 
			
		||||
                            vm.grid.refreshList();
 | 
			
		||||
                        }
 | 
			
		||||
                    );
 | 
			
		||||
                },
 | 
			
		||||
                name: function() { return $translate.instant('action.import') },
 | 
			
		||||
                details: function() { return $translate.instant('dashboard.import') },
 | 
			
		||||
                icon: "file_upload"
 | 
			
		||||
            });
 | 
			
		||||
        } else if (vm.dashboardsScope === 'customer' || vm.dashboardsScope === 'customer_user') {
 | 
			
		||||
            fetchDashboardsFunction = function (pageLink) {
 | 
			
		||||
                return dashboardService.getCustomerDashboards(customerId, pageLink);
 | 
			
		||||
@ -344,6 +373,11 @@ export default function DashboardsController(userService, dashboardService, cust
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function exportDashboard($event, dashboard) {
 | 
			
		||||
        $event.stopPropagation();
 | 
			
		||||
        importExport.exportDashboard(dashboard.id.id);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function unassignDashboardsFromCustomer($event, items) {
 | 
			
		||||
        var confirm = $mdDialog.confirm()
 | 
			
		||||
            .targetEvent($event)
 | 
			
		||||
 | 
			
		||||
@ -25,5 +25,6 @@
 | 
			
		||||
						  the-form="vm.grid.detailsForm"
 | 
			
		||||
						  on-assign-to-customer="vm.assignToCustomer(event, [ vm.grid.detailsConfig.currentItem.id.id ])"
 | 
			
		||||
						  on-unassign-from-customer="vm.unassignFromCustomer(event, vm.grid.detailsConfig.currentItem)"
 | 
			
		||||
						  on-export-dashboard="vm.exportDashboard(event, vm.grid.detailsConfig.currentItem)"
 | 
			
		||||
						  on-delete-dashboard="vm.grid.deleteItem(event, vm.grid.detailsConfig.currentItem)"></tb-dashboard-details>
 | 
			
		||||
</tb-grid>
 | 
			
		||||
 | 
			
		||||
@ -17,16 +17,18 @@ import './device-aliases.scss';
 | 
			
		||||
 | 
			
		||||
/*@ngInject*/
 | 
			
		||||
export default function DeviceAliasesController(deviceService, toast, $scope, $mdDialog, $document, $q, $translate,
 | 
			
		||||
                                                deviceAliases, aliasToWidgetsMap, isSingleDevice, singleDeviceAlias) {
 | 
			
		||||
                                                types, config) {
 | 
			
		||||
 | 
			
		||||
    var vm = this;
 | 
			
		||||
 | 
			
		||||
    vm.isSingleDevice = isSingleDevice;
 | 
			
		||||
    vm.singleDeviceAlias = singleDeviceAlias;
 | 
			
		||||
    vm.isSingleDevice = config.isSingleDevice;
 | 
			
		||||
    vm.singleDeviceAlias = config.singleDeviceAlias;
 | 
			
		||||
    vm.deviceAliases = [];
 | 
			
		||||
    vm.aliasToWidgetsMap = aliasToWidgetsMap;
 | 
			
		||||
    vm.singleDevice = null;
 | 
			
		||||
    vm.singleDeviceSearchText = '';
 | 
			
		||||
    vm.title = config.customTitle ? config.customTitle : 'device.aliases';
 | 
			
		||||
    vm.disableAdd = config.disableAdd;
 | 
			
		||||
    vm.aliasToWidgetsMap = {};
 | 
			
		||||
 | 
			
		||||
    vm.addAlias = addAlias;
 | 
			
		||||
    vm.cancel = cancel;
 | 
			
		||||
@ -39,9 +41,48 @@ export default function DeviceAliasesController(deviceService, toast, $scope, $m
 | 
			
		||||
    initController();
 | 
			
		||||
 | 
			
		||||
    function initController() {
 | 
			
		||||
        for (var aliasId in deviceAliases) {
 | 
			
		||||
            var alias = deviceAliases[aliasId].alias;
 | 
			
		||||
            var deviceId = deviceAliases[aliasId].deviceId;
 | 
			
		||||
        var aliasId;
 | 
			
		||||
        if (config.widgets) {
 | 
			
		||||
            var widgetsTitleList, widget;
 | 
			
		||||
            if (config.isSingleWidget && config.widgets.length == 1) {
 | 
			
		||||
                widget = config.widgets[0];
 | 
			
		||||
                widgetsTitleList = [widget.config.title];
 | 
			
		||||
                for (aliasId in config.deviceAliases) {
 | 
			
		||||
                    vm.aliasToWidgetsMap[aliasId] = widgetsTitleList;
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                for (var w in config.widgets) {
 | 
			
		||||
                    widget = config.widgets[w];
 | 
			
		||||
                    if (widget.type === types.widgetType.rpc.value) {
 | 
			
		||||
                        if (widget.config.targetDeviceAliasIds && widget.config.targetDeviceAliasIds.length > 0) {
 | 
			
		||||
                            var targetDeviceAliasId = widget.config.targetDeviceAliasIds[0];
 | 
			
		||||
                            widgetsTitleList = vm.aliasToWidgetsMap[targetDeviceAliasId];
 | 
			
		||||
                            if (!widgetsTitleList) {
 | 
			
		||||
                                widgetsTitleList = [];
 | 
			
		||||
                                vm.aliasToWidgetsMap[targetDeviceAliasId] = widgetsTitleList;
 | 
			
		||||
                            }
 | 
			
		||||
                            widgetsTitleList.push(widget.config.title);
 | 
			
		||||
                        }
 | 
			
		||||
                    } else {
 | 
			
		||||
                        for (var i in widget.config.datasources) {
 | 
			
		||||
                            var datasource = widget.config.datasources[i];
 | 
			
		||||
                            if (datasource.type === types.datasourceType.device && datasource.deviceAliasId) {
 | 
			
		||||
                                widgetsTitleList = vm.aliasToWidgetsMap[datasource.deviceAliasId];
 | 
			
		||||
                                if (!widgetsTitleList) {
 | 
			
		||||
                                    widgetsTitleList = [];
 | 
			
		||||
                                    vm.aliasToWidgetsMap[datasource.deviceAliasId] = widgetsTitleList;
 | 
			
		||||
                                }
 | 
			
		||||
                                widgetsTitleList.push(widget.config.title);
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        for (aliasId in config.deviceAliases) {
 | 
			
		||||
            var alias = config.deviceAliases[aliasId].alias;
 | 
			
		||||
            var deviceId = config.deviceAliases[aliasId].deviceId;
 | 
			
		||||
            var deviceAlias = {id: aliasId, alias: alias, device: null, changed: false, searchText: ''};
 | 
			
		||||
            if (deviceId) {
 | 
			
		||||
                fetchAliasDevice(deviceAlias, deviceId);
 | 
			
		||||
 | 
			
		||||
@ -15,11 +15,11 @@
 | 
			
		||||
    limitations under the License.
 | 
			
		||||
 | 
			
		||||
-->
 | 
			
		||||
<md-dialog style="width: 700px;" aria-label="{{ 'device.aliases' | translate }}">
 | 
			
		||||
<md-dialog style="width: 700px;" aria-label="{{ vm.title | translate }}">
 | 
			
		||||
	<form name="theForm" ng-submit="vm.save()">
 | 
			
		||||
	    <md-toolbar>
 | 
			
		||||
	      <div class="md-toolbar-tools">
 | 
			
		||||
	        <h2>{{ vm.isSingleDevice ? ('device.select-device-for-alias' | translate:vm.singleDeviceAlias ) : ('device.aliases' | translate) }}</h2>
 | 
			
		||||
	        <h2>{{ vm.isSingleDevice ? ('device.select-device-for-alias' | translate:vm.singleDeviceAlias ) : (vm.title | translate) }}</h2>
 | 
			
		||||
	        <span flex></span>
 | 
			
		||||
	        <md-button class="md-icon-button" ng-click="vm.cancel()">
 | 
			
		||||
	          <ng-md-icon icon="close" aria-label="{{ 'dialog.close' | translate }}"></ng-md-icon>
 | 
			
		||||
@ -109,7 +109,7 @@
 | 
			
		||||
				          </div>						
 | 
			
		||||
					</div>					
 | 
			
		||||
				</div>
 | 
			
		||||
				<div ng-show="!vm.isSingleDevice" style="padding-bottom: 10px;">
 | 
			
		||||
				<div ng-show="!vm.isSingleDevice && !vm.disableAdd" style="padding-bottom: 10px;">
 | 
			
		||||
			         <md-button ng-disabled="loading" class="md-primary md-raised" ng-click="vm.addAlias($event)" aria-label="{{ 'action.add' | translate }}">
 | 
			
		||||
			         		<md-tooltip md-direction="top">
 | 
			
		||||
			      						{{ 'device.add-alias' | translate }}
 | 
			
		||||
 | 
			
		||||
@ -76,10 +76,12 @@ export default function EditWidgetDirective($compile, $templateCache, widgetServ
 | 
			
		||||
                controllerAs: 'vm',
 | 
			
		||||
                templateUrl: deviceAliasesTemplate,
 | 
			
		||||
                locals: {
 | 
			
		||||
                    deviceAliases: angular.copy(scope.dashboard.configuration.deviceAliases),
 | 
			
		||||
                    aliasToWidgetsMap: null,
 | 
			
		||||
                    isSingleDevice: true,
 | 
			
		||||
                    singleDeviceAlias: singleDeviceAlias
 | 
			
		||||
                    config: {
 | 
			
		||||
                        deviceAliases: angular.copy(scope.dashboard.configuration.deviceAliases),
 | 
			
		||||
                        widgets: null,
 | 
			
		||||
                        isSingleDevice: true,
 | 
			
		||||
                        singleDeviceAlias: singleDeviceAlias
 | 
			
		||||
                    }
 | 
			
		||||
                },
 | 
			
		||||
                parent: angular.element($document[0].body),
 | 
			
		||||
                fullscreen: true,
 | 
			
		||||
 | 
			
		||||
@ -30,6 +30,7 @@ import thingsboardExpandFullscreen from '../components/expand-fullscreen.directi
 | 
			
		||||
import thingsboardWidgetsBundleSelect from '../components/widgets-bundle-select.directive';
 | 
			
		||||
import thingsboardTypes from '../common/types.constant';
 | 
			
		||||
import thingsboardItemBuffer from '../services/item-buffer.service';
 | 
			
		||||
import thingsboardImportExport from '../import-export';
 | 
			
		||||
 | 
			
		||||
import DashboardRoutes from './dashboard.routes';
 | 
			
		||||
import DashboardsController from './dashboards.controller';
 | 
			
		||||
@ -47,6 +48,7 @@ export default angular.module('thingsboard.dashboard', [
 | 
			
		||||
    gridster.name,
 | 
			
		||||
    thingsboardTypes,
 | 
			
		||||
    thingsboardItemBuffer,
 | 
			
		||||
    thingsboardImportExport,
 | 
			
		||||
    thingsboardGrid,
 | 
			
		||||
    thingsboardApiWidget,
 | 
			
		||||
    thingsboardApiUser,
 | 
			
		||||
 | 
			
		||||
@ -148,6 +148,7 @@ export default function GlobalInterceptor($rootScope, $q, $injector) {
 | 
			
		||||
            $rootScope.loading = false;
 | 
			
		||||
        }
 | 
			
		||||
        var unhandled = false;
 | 
			
		||||
        var ignoreErrors = rejection.config.ignoreErrors;
 | 
			
		||||
        if (rejection.refreshTokenPending || rejection.status === 401) {
 | 
			
		||||
            var errorCode = rejectionErrorCode(rejection);
 | 
			
		||||
            if (rejection.refreshTokenPending || (errorCode && errorCode === getTypes().serverErrorCode.jwtTokenExpired)) {
 | 
			
		||||
@ -156,13 +157,17 @@ export default function GlobalInterceptor($rootScope, $q, $injector) {
 | 
			
		||||
                unhandled = true;
 | 
			
		||||
            }
 | 
			
		||||
        } else if (rejection.status === 403) {
 | 
			
		||||
            $rootScope.$broadcast('forbidden');
 | 
			
		||||
            if (!ignoreErrors) {
 | 
			
		||||
                $rootScope.$broadcast('forbidden');
 | 
			
		||||
            }
 | 
			
		||||
        } else if (rejection.status === 0 || rejection.status === -1) {
 | 
			
		||||
            getToast().showError(getTranslate().instant('error.unable-to-connect'));
 | 
			
		||||
        } else if (!rejection.config.url.startsWith('/api/plugins/rpc')) {
 | 
			
		||||
            if (rejection.status === 404) {
 | 
			
		||||
                getToast().showError(rejection.config.method + ": " + rejection.config.url + "<br/>" +
 | 
			
		||||
                    rejection.status + ": " + rejection.statusText);
 | 
			
		||||
                if (!ignoreErrors) {
 | 
			
		||||
                    getToast().showError(rejection.config.method + ": " + rejection.config.url + "<br/>" +
 | 
			
		||||
                        rejection.status + ": " + rejection.statusText);
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                unhandled = true;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										71
									
								
								ui/src/app/import-export/import-dialog.controller.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								ui/src/app/import-export/import-dialog.controller.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,71 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright © 2016 The Thingsboard Authors
 | 
			
		||||
 *
 | 
			
		||||
 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
 * you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 *
 | 
			
		||||
 *     http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
 * See the License for the specific language governing permissions and
 | 
			
		||||
 * limitations under the License.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
import './import-dialog.scss';
 | 
			
		||||
 | 
			
		||||
/*@ngInject*/
 | 
			
		||||
export default function ImportDialogController($scope, $mdDialog, toast, importTitle, importFileLabel) {
 | 
			
		||||
 | 
			
		||||
    var vm = this;
 | 
			
		||||
 | 
			
		||||
    vm.cancel = cancel;
 | 
			
		||||
    vm.importFromJson = importFromJson;
 | 
			
		||||
    vm.fileAdded = fileAdded;
 | 
			
		||||
    vm.clearFile = clearFile;
 | 
			
		||||
 | 
			
		||||
    vm.importTitle = importTitle;
 | 
			
		||||
    vm.importFileLabel = importFileLabel;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    function cancel() {
 | 
			
		||||
        $mdDialog.cancel();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function fileAdded($file) {
 | 
			
		||||
        if ($file.getExtension() === 'json') {
 | 
			
		||||
            var reader = new FileReader();
 | 
			
		||||
            reader.onload = function(event) {
 | 
			
		||||
                $scope.$apply(function() {
 | 
			
		||||
                    if (event.target.result) {
 | 
			
		||||
                        $scope.theForm.$setDirty();
 | 
			
		||||
                        var importJson = event.target.result;
 | 
			
		||||
                        if (importJson && importJson.length > 0) {
 | 
			
		||||
                            try {
 | 
			
		||||
                                vm.importData = angular.fromJson(importJson);
 | 
			
		||||
                                vm.fileName = $file.name;
 | 
			
		||||
                            } catch (err) {
 | 
			
		||||
                                vm.fileName = null;
 | 
			
		||||
                                toast.showError(err.message);
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                });
 | 
			
		||||
            };
 | 
			
		||||
            reader.readAsText($file.file);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function clearFile() {
 | 
			
		||||
        $scope.theForm.$setDirty();
 | 
			
		||||
        vm.fileName = null;
 | 
			
		||||
        vm.importData = null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function importFromJson() {
 | 
			
		||||
        $scope.theForm.$setPristine();
 | 
			
		||||
        $mdDialog.hide(vm.importData);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										70
									
								
								ui/src/app/import-export/import-dialog.scss
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								ui/src/app/import-export/import-dialog.scss
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,70 @@
 | 
			
		||||
/**
 | 
			
		||||
 * Copyright © 2016 The Thingsboard Authors
 | 
			
		||||
 *
 | 
			
		||||
 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
 * you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 *
 | 
			
		||||
 *     http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
 * See the License for the specific language governing permissions and
 | 
			
		||||
 * limitations under the License.
 | 
			
		||||
 */
 | 
			
		||||
$previewSize: 100px;
 | 
			
		||||
 | 
			
		||||
.file-input {
 | 
			
		||||
  display: none;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.tb-container {
 | 
			
		||||
  position: relative;
 | 
			
		||||
  margin-top: 32px;
 | 
			
		||||
  padding: 10px 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.tb-file-select-container {
 | 
			
		||||
  position: relative;
 | 
			
		||||
  height: $previewSize;
 | 
			
		||||
  width: 100%;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.tb-file-preview {
 | 
			
		||||
  max-width: $previewSize;
 | 
			
		||||
  max-height: $previewSize;
 | 
			
		||||
  width: auto;
 | 
			
		||||
  height: auto;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.tb-flow-drop {
 | 
			
		||||
  position: relative;
 | 
			
		||||
  border: dashed 2px;
 | 
			
		||||
  height: $previewSize;
 | 
			
		||||
  vertical-align: top;
 | 
			
		||||
  padding: 0 8px;
 | 
			
		||||
  overflow: hidden;
 | 
			
		||||
  min-width: 300px;
 | 
			
		||||
  label {
 | 
			
		||||
    width: 100%;
 | 
			
		||||
    font-size: 24px;
 | 
			
		||||
    text-align: center;
 | 
			
		||||
    position: absolute;
 | 
			
		||||
    top: 50%;
 | 
			
		||||
    left: 50%;
 | 
			
		||||
    transform: translate(-50%,-50%);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.tb-file-clear-container {
 | 
			
		||||
  width: 48px;
 | 
			
		||||
  height: $previewSize;
 | 
			
		||||
  position: relative;
 | 
			
		||||
  float: right;
 | 
			
		||||
}
 | 
			
		||||
.tb-file-clear-btn {
 | 
			
		||||
  position: absolute !important;
 | 
			
		||||
  top: 50%;
 | 
			
		||||
  transform: translate(0%,-50%) !important;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										72
									
								
								ui/src/app/import-export/import-dialog.tpl.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								ui/src/app/import-export/import-dialog.tpl.html
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,72 @@
 | 
			
		||||
<!--
 | 
			
		||||
 | 
			
		||||
    Copyright © 2016 The Thingsboard Authors
 | 
			
		||||
 | 
			
		||||
    Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
    you may not use this file except in compliance with the License.
 | 
			
		||||
    You may obtain a copy of the License at
 | 
			
		||||
 | 
			
		||||
        http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 | 
			
		||||
    Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
    distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
    See the License for the specific language governing permissions and
 | 
			
		||||
    limitations under the License.
 | 
			
		||||
 | 
			
		||||
-->
 | 
			
		||||
<md-dialog aria-label="{{ vm.importTitle | translate }}">
 | 
			
		||||
    <form name="theForm" ng-submit="vm.importFromJson()">
 | 
			
		||||
        <md-toolbar>
 | 
			
		||||
            <div class="md-toolbar-tools">
 | 
			
		||||
                <h2 translate>{{ vm.importTitle }}</h2>
 | 
			
		||||
                <span flex></span>
 | 
			
		||||
                <md-button class="md-icon-button" ng-click="vm.cancel()">
 | 
			
		||||
                    <ng-md-icon icon="close" aria-label="{{ 'dialog.close' | translate }}"></ng-md-icon>
 | 
			
		||||
                </md-button>
 | 
			
		||||
            </div>
 | 
			
		||||
        </md-toolbar>
 | 
			
		||||
        <md-progress-linear class="md-warn" md-mode="indeterminate" ng-show="loading"></md-progress-linear>
 | 
			
		||||
        <span style="min-height: 5px;" flex="" ng-show="!loading"></span>
 | 
			
		||||
        <md-dialog-content>
 | 
			
		||||
            <div class="md-dialog-content">
 | 
			
		||||
                <fieldset ng-disabled="loading">
 | 
			
		||||
                    <div layout="column" layout-padding>
 | 
			
		||||
                        <div class="tb-container">
 | 
			
		||||
                            <label class="tb-label" translate>{{ vm.importFileLabel }}</label>
 | 
			
		||||
                            <div flow-init="{singleFile:true}"
 | 
			
		||||
                                 flow-file-added="vm.fileAdded( $file )" class="tb-file-select-container">
 | 
			
		||||
                                <div class="tb-file-clear-container">
 | 
			
		||||
                                    <md-button ng-click="vm.clearFile()"
 | 
			
		||||
                                               class="tb-file-clear-btn md-icon-button md-primary" aria-label="{{ 'action.remove' | translate }}">
 | 
			
		||||
                                        <md-tooltip md-direction="top">
 | 
			
		||||
                                            {{ 'action.remove' | translate }}
 | 
			
		||||
                                        </md-tooltip>
 | 
			
		||||
                                        <md-icon aria-label="{{ 'action.remove' | translate }}" class="material-icons">
 | 
			
		||||
                                            close
 | 
			
		||||
                                        </md-icon>
 | 
			
		||||
                                    </md-button>
 | 
			
		||||
                                </div>
 | 
			
		||||
                                <div class="alert tb-flow-drop" flow-drop>
 | 
			
		||||
                                    <label for="select" translate>import.drop-file</label>
 | 
			
		||||
                                    <input class="file-input" flow-btn flow-attrs="{accept:'.json,application/json'}" id="select">
 | 
			
		||||
                                </div>
 | 
			
		||||
                            </div>
 | 
			
		||||
                        </div>
 | 
			
		||||
                        <div>
 | 
			
		||||
                            <div ng-show="!vm.fileName" translate>import.no-file</div>
 | 
			
		||||
                            <div ng-show="vm.fileName">{{ vm.fileName }}</div>
 | 
			
		||||
                        </div>
 | 
			
		||||
                    </div>
 | 
			
		||||
                </fieldset>
 | 
			
		||||
            </div>
 | 
			
		||||
        </md-dialog-content>
 | 
			
		||||
        <md-dialog-actions layout="row">
 | 
			
		||||
            <span flex></span>
 | 
			
		||||
            <md-button ng-disabled="loading || !theForm.$dirty || !theForm.$valid || !vm.importData" type="submit" class="md-raised md-primary">
 | 
			
		||||
                {{ 'action.import' | translate }}
 | 
			
		||||
            </md-button>
 | 
			
		||||
            <md-button ng-disabled="loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | translate }}</md-button>
 | 
			
		||||
        </md-dialog-actions>
 | 
			
		||||
    </form>
 | 
			
		||||
</md-dialog>
 | 
			
		||||
							
								
								
									
										373
									
								
								ui/src/app/import-export/import-export.service.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										373
									
								
								ui/src/app/import-export/import-export.service.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,373 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright © 2016 The Thingsboard Authors
 | 
			
		||||
 *
 | 
			
		||||
 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
 * you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 *
 | 
			
		||||
 *     http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
 * See the License for the specific language governing permissions and
 | 
			
		||||
 * limitations under the License.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/* eslint-disable import/no-unresolved, import/default */
 | 
			
		||||
 | 
			
		||||
import importDialogTemplate from './import-dialog.tpl.html';
 | 
			
		||||
import deviceAliasesTemplate from '../dashboard/device-aliases.tpl.html';
 | 
			
		||||
 | 
			
		||||
/* eslint-enable import/no-unresolved, import/default */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* eslint-disable no-undef, angular/window-service, angular/document-service */
 | 
			
		||||
 | 
			
		||||
/*@ngInject*/
 | 
			
		||||
export default function ImportExport($log, $translate, $q, $mdDialog, $document, itembuffer, deviceService, dashboardService, toast) {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    var service = {
 | 
			
		||||
        exportDashboard: exportDashboard,
 | 
			
		||||
        importDashboard: importDashboard,
 | 
			
		||||
        exportWidget: exportWidget,
 | 
			
		||||
        importWidget: importWidget
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return service;
 | 
			
		||||
 | 
			
		||||
    // Widget functions
 | 
			
		||||
 | 
			
		||||
    function exportWidget(dashboard, widget) {
 | 
			
		||||
        var widgetItem = itembuffer.prepareWidgetItem(dashboard, widget);
 | 
			
		||||
        var name = widgetItem.widget.config.title;
 | 
			
		||||
        name = name.toLowerCase().replace(/\W/g,"_");
 | 
			
		||||
        exportToPc(prepareExport(widgetItem), name + '.json');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function importWidget($event, dashboard) {
 | 
			
		||||
        openImportDialog($event, 'dashboard.import-widget', 'dashboard.widget-file').then(
 | 
			
		||||
            function success(widgetItem) {
 | 
			
		||||
                if (!validateImportedWidget(widgetItem)) {
 | 
			
		||||
                    toast.showError($translate.instant('dashboard.invalid-widget-file-error'));
 | 
			
		||||
                } else {
 | 
			
		||||
                    var widget = widgetItem.widget;
 | 
			
		||||
                    var aliasesInfo = widgetItem.aliasesInfo;
 | 
			
		||||
                    var originalColumns = widgetItem.originalColumns;
 | 
			
		||||
 | 
			
		||||
                    var datasourceAliases = aliasesInfo.datasourceAliases;
 | 
			
		||||
                    var targetDeviceAliases = aliasesInfo.targetDeviceAliases;
 | 
			
		||||
                    if (datasourceAliases || targetDeviceAliases) {
 | 
			
		||||
                        var deviceAliases = {};
 | 
			
		||||
                        var datasourceAliasesMap = {};
 | 
			
		||||
                        var targetDeviceAliasesMap = {};
 | 
			
		||||
                        var aliasId = 1;
 | 
			
		||||
                        var datasourceIndex;
 | 
			
		||||
                        if (datasourceAliases) {
 | 
			
		||||
                            for (datasourceIndex in datasourceAliases) {
 | 
			
		||||
                                datasourceAliasesMap[aliasId] = datasourceIndex;
 | 
			
		||||
                                deviceAliases[aliasId] = {
 | 
			
		||||
                                    alias: datasourceAliases[datasourceIndex].aliasName,
 | 
			
		||||
                                    deviceId: datasourceAliases[datasourceIndex].deviceId
 | 
			
		||||
                                };
 | 
			
		||||
                                aliasId++;
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                        if (targetDeviceAliases) {
 | 
			
		||||
                            for (datasourceIndex in targetDeviceAliases) {
 | 
			
		||||
                                targetDeviceAliasesMap[aliasId] = datasourceIndex;
 | 
			
		||||
                                deviceAliases[aliasId] = {
 | 
			
		||||
                                    alias: targetDeviceAliases[datasourceIndex].aliasName,
 | 
			
		||||
                                    deviceId: targetDeviceAliases[datasourceIndex].deviceId
 | 
			
		||||
                                };
 | 
			
		||||
                                aliasId++;
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        var aliasIds = Object.keys(deviceAliases);
 | 
			
		||||
                        if (aliasIds.length > 0) {
 | 
			
		||||
                            processDeviceAliases(deviceAliases, aliasIds).then(
 | 
			
		||||
                                function(missingDeviceAliases) {
 | 
			
		||||
                                    if (Object.keys(missingDeviceAliases).length > 0) {
 | 
			
		||||
                                        editMissingAliases($event, [ widget ],
 | 
			
		||||
                                              true, 'dashboard.widget-import-missing-aliases-title', missingDeviceAliases).then(
 | 
			
		||||
                                            function success(updatedDeviceAliases) {
 | 
			
		||||
                                                for (var aliasId in updatedDeviceAliases) {
 | 
			
		||||
                                                    var deviceAlias = updatedDeviceAliases[aliasId];
 | 
			
		||||
                                                    var datasourceIndex;
 | 
			
		||||
                                                    if (datasourceAliasesMap[aliasId]) {
 | 
			
		||||
                                                        datasourceIndex = datasourceAliasesMap[aliasId];
 | 
			
		||||
                                                        datasourceAliases[datasourceIndex].deviceId = deviceAlias.deviceId;
 | 
			
		||||
                                                    } else if (targetDeviceAliasesMap[aliasId]) {
 | 
			
		||||
                                                        datasourceIndex = targetDeviceAliasesMap[aliasId];
 | 
			
		||||
                                                        targetDeviceAliases[datasourceIndex].deviceId = deviceAlias.deviceId;
 | 
			
		||||
                                                    }
 | 
			
		||||
                                                }
 | 
			
		||||
                                                addImportedWidget(dashboard, widget, aliasesInfo, originalColumns);
 | 
			
		||||
                                            },
 | 
			
		||||
                                            function fail() {}
 | 
			
		||||
                                        );
 | 
			
		||||
                                    } else {
 | 
			
		||||
                                        addImportedWidget(dashboard, widget, aliasesInfo, originalColumns);
 | 
			
		||||
                                    }
 | 
			
		||||
                                }
 | 
			
		||||
                            );
 | 
			
		||||
                        } else {
 | 
			
		||||
                            addImportedWidget(dashboard, widget, aliasesInfo, originalColumns);
 | 
			
		||||
                        }
 | 
			
		||||
                    } else {
 | 
			
		||||
                        addImportedWidget(dashboard, widget, aliasesInfo, originalColumns);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
            function fail() {}
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function validateImportedWidget(widgetItem) {
 | 
			
		||||
        if (angular.isUndefined(widgetItem.widget)
 | 
			
		||||
            || angular.isUndefined(widgetItem.aliasesInfo)
 | 
			
		||||
            || angular.isUndefined(widgetItem.originalColumns)) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
        var widget = widgetItem.widget;
 | 
			
		||||
        if (angular.isUndefined(widget.isSystemType) ||
 | 
			
		||||
            angular.isUndefined(widget.bundleAlias) ||
 | 
			
		||||
            angular.isUndefined(widget.typeAlias) ||
 | 
			
		||||
            angular.isUndefined(widget.type)) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function addImportedWidget(dashboard, widget, aliasesInfo, originalColumns) {
 | 
			
		||||
        itembuffer.addWidgetToDashboard(dashboard, widget, aliasesInfo, originalColumns, -1, -1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Dashboard functions
 | 
			
		||||
 | 
			
		||||
    function exportDashboard(dashboardId) {
 | 
			
		||||
        dashboardService.getDashboard(dashboardId).then(
 | 
			
		||||
            function success(dashboard) {
 | 
			
		||||
                var name = dashboard.title;
 | 
			
		||||
                name = name.toLowerCase().replace(/\W/g,"_");
 | 
			
		||||
                exportToPc(prepareExport(dashboard), name + '.json');
 | 
			
		||||
            },
 | 
			
		||||
            function fail(rejection) {
 | 
			
		||||
                var message = rejection;
 | 
			
		||||
                if (!message) {
 | 
			
		||||
                    message = $translate.instant('error.unknown-error');
 | 
			
		||||
                }
 | 
			
		||||
                toast.showError($translate.instant('dashboard.export-failed-error', {error: message}));
 | 
			
		||||
            }
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function importDashboard($event) {
 | 
			
		||||
        var deferred = $q.defer();
 | 
			
		||||
        openImportDialog($event, 'dashboard.import', 'dashboard.dashboard-file').then(
 | 
			
		||||
            function success(dashboard) {
 | 
			
		||||
                if (!validateImportedDashboard(dashboard)) {
 | 
			
		||||
                    toast.showError($translate.instant('dashboard.invalid-dashboard-file-error'));
 | 
			
		||||
                    deferred.reject();
 | 
			
		||||
                } else {
 | 
			
		||||
                    var deviceAliases = dashboard.configuration.deviceAliases;
 | 
			
		||||
                    if (deviceAliases) {
 | 
			
		||||
                        var aliasIds = Object.keys( deviceAliases );
 | 
			
		||||
                        if (aliasIds.length > 0) {
 | 
			
		||||
                            processDeviceAliases(deviceAliases, aliasIds).then(
 | 
			
		||||
                                function(missingDeviceAliases) {
 | 
			
		||||
                                    if (Object.keys( missingDeviceAliases ).length > 0) {
 | 
			
		||||
                                        editMissingAliases($event, dashboard.configuration.widgets,
 | 
			
		||||
                                                false, 'dashboard.dashboard-import-missing-aliases-title', missingDeviceAliases).then(
 | 
			
		||||
                                            function success(updatedDeviceAliases) {
 | 
			
		||||
                                                for (var aliasId in updatedDeviceAliases) {
 | 
			
		||||
                                                    deviceAliases[aliasId] = updatedDeviceAliases[aliasId];
 | 
			
		||||
                                                }
 | 
			
		||||
                                                saveImportedDashboard(dashboard, deferred);
 | 
			
		||||
                                            },
 | 
			
		||||
                                            function fail() {
 | 
			
		||||
                                                deferred.reject();
 | 
			
		||||
                                            }
 | 
			
		||||
                                        );
 | 
			
		||||
                                    } else {
 | 
			
		||||
                                        saveImportedDashboard(dashboard, deferred);
 | 
			
		||||
                                    }
 | 
			
		||||
                                }
 | 
			
		||||
                            )
 | 
			
		||||
                        } else {
 | 
			
		||||
                            saveImportedDashboard(dashboard, deferred);
 | 
			
		||||
                        }
 | 
			
		||||
                    } else {
 | 
			
		||||
                        saveImportedDashboard(dashboard, deferred);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
            function fail() {
 | 
			
		||||
                deferred.reject();
 | 
			
		||||
            }
 | 
			
		||||
        );
 | 
			
		||||
        return deferred.promise;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function saveImportedDashboard(dashboard, deferred) {
 | 
			
		||||
        dashboardService.saveDashboard(dashboard).then(
 | 
			
		||||
            function success() {
 | 
			
		||||
                deferred.resolve();
 | 
			
		||||
            },
 | 
			
		||||
            function fail() {
 | 
			
		||||
                deferred.reject();
 | 
			
		||||
            }
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function validateImportedDashboard(dashboard) {
 | 
			
		||||
        if (angular.isUndefined(dashboard.title) || angular.isUndefined(dashboard.configuration)) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function processDeviceAliases(deviceAliases, aliasIds) {
 | 
			
		||||
        var deferred = $q.defer();
 | 
			
		||||
        var missingDeviceAliases = {};
 | 
			
		||||
        var index = -1;
 | 
			
		||||
        checkNextDeviceAliasOrComplete(index, aliasIds, deviceAliases, missingDeviceAliases, deferred);
 | 
			
		||||
        return deferred.promise;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function checkNextDeviceAliasOrComplete(index, aliasIds, deviceAliases, missingDeviceAliases, deferred) {
 | 
			
		||||
        index++;
 | 
			
		||||
        if (index == aliasIds.length) {
 | 
			
		||||
            deferred.resolve(missingDeviceAliases);
 | 
			
		||||
        } else {
 | 
			
		||||
            checkDeviceAlias(index, aliasIds, deviceAliases, missingDeviceAliases, deferred);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function checkDeviceAlias(index, aliasIds, deviceAliases, missingDeviceAliases, deferred) {
 | 
			
		||||
        var aliasId = aliasIds[index];
 | 
			
		||||
        var deviceAlias = deviceAliases[aliasId];
 | 
			
		||||
        if (deviceAlias.deviceId) {
 | 
			
		||||
            deviceService.getDevice(deviceAlias.deviceId, true).then(
 | 
			
		||||
                function success() {
 | 
			
		||||
                    checkNextDeviceAliasOrComplete(index, aliasIds, deviceAliases, missingDeviceAliases, deferred);
 | 
			
		||||
                },
 | 
			
		||||
                function fail() {
 | 
			
		||||
                    var missingDeviceAlias = angular.copy(deviceAlias);
 | 
			
		||||
                    missingDeviceAlias.deviceId = null;
 | 
			
		||||
                    missingDeviceAliases[aliasId] = missingDeviceAlias;
 | 
			
		||||
                    checkNextDeviceAliasOrComplete(index, aliasIds, deviceAliases, missingDeviceAliases, deferred);
 | 
			
		||||
                }
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function editMissingAliases($event, widgets, isSingleWidget, customTitle, missingDeviceAliases) {
 | 
			
		||||
        var deferred = $q.defer();
 | 
			
		||||
        $mdDialog.show({
 | 
			
		||||
            controller: 'DeviceAliasesController',
 | 
			
		||||
            controllerAs: 'vm',
 | 
			
		||||
            templateUrl: deviceAliasesTemplate,
 | 
			
		||||
            locals: {
 | 
			
		||||
                config: {
 | 
			
		||||
                    deviceAliases: missingDeviceAliases,
 | 
			
		||||
                    widgets: widgets,
 | 
			
		||||
                    isSingleWidget: isSingleWidget,
 | 
			
		||||
                    isSingleDevice: false,
 | 
			
		||||
                    singleDeviceAlias: null,
 | 
			
		||||
                    customTitle: customTitle,
 | 
			
		||||
                    disableAdd: true
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
            parent: angular.element($document[0].body),
 | 
			
		||||
            skipHide: true,
 | 
			
		||||
            fullscreen: true,
 | 
			
		||||
            targetEvent: $event
 | 
			
		||||
        }).then(function (updatedDeviceAliases) {
 | 
			
		||||
            deferred.resolve(updatedDeviceAliases);
 | 
			
		||||
        }, function () {
 | 
			
		||||
            deferred.reject();
 | 
			
		||||
        });
 | 
			
		||||
        return deferred.promise;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Common functions
 | 
			
		||||
 | 
			
		||||
    function prepareExport(data) {
 | 
			
		||||
        var exportedData = angular.copy(data);
 | 
			
		||||
        if (angular.isDefined(exportedData.id)) {
 | 
			
		||||
            delete exportedData.id;
 | 
			
		||||
        }
 | 
			
		||||
        if (angular.isDefined(exportedData.createdTime)) {
 | 
			
		||||
            delete exportedData.createdTime;
 | 
			
		||||
        }
 | 
			
		||||
        if (angular.isDefined(exportedData.tenantId)) {
 | 
			
		||||
            delete exportedData.tenantId;
 | 
			
		||||
        }
 | 
			
		||||
        if (angular.isDefined(exportedData.customerId)) {
 | 
			
		||||
            delete exportedData.customerId;
 | 
			
		||||
        }
 | 
			
		||||
        return exportedData;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function exportToPc(data, filename) {
 | 
			
		||||
        if (!data) {
 | 
			
		||||
            $log.error('No data');
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!filename) {
 | 
			
		||||
            filename = 'download.json';
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (angular.isObject(data)) {
 | 
			
		||||
            data = angular.toJson(data, 2);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        var blob = new Blob([data], {type: 'text/json'});
 | 
			
		||||
 | 
			
		||||
        // FOR IE:
 | 
			
		||||
 | 
			
		||||
        if (window.navigator && window.navigator.msSaveOrOpenBlob) {
 | 
			
		||||
            window.navigator.msSaveOrOpenBlob(blob, filename);
 | 
			
		||||
        }
 | 
			
		||||
        else{
 | 
			
		||||
            var e = document.createEvent('MouseEvents'),
 | 
			
		||||
                a = document.createElement('a');
 | 
			
		||||
 | 
			
		||||
            a.download = filename;
 | 
			
		||||
            a.href = window.URL.createObjectURL(blob);
 | 
			
		||||
            a.dataset.downloadurl = ['text/json', a.download, a.href].join(':');
 | 
			
		||||
            e.initEvent('click', true, false, window,
 | 
			
		||||
                0, 0, 0, 0, 0, false, false, false, false, 0, null);
 | 
			
		||||
            a.dispatchEvent(e);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function openImportDialog($event, importTitle, importFileLabel) {
 | 
			
		||||
        var deferred = $q.defer();
 | 
			
		||||
        $mdDialog.show({
 | 
			
		||||
            controller: 'ImportDialogController',
 | 
			
		||||
            controllerAs: 'vm',
 | 
			
		||||
            templateUrl: importDialogTemplate,
 | 
			
		||||
            locals: {
 | 
			
		||||
                importTitle: importTitle,
 | 
			
		||||
                importFileLabel: importFileLabel
 | 
			
		||||
            },
 | 
			
		||||
            parent: angular.element($document[0].body),
 | 
			
		||||
            skipHide: true,
 | 
			
		||||
            fullscreen: true,
 | 
			
		||||
            targetEvent: $event
 | 
			
		||||
        }).then(function (importData) {
 | 
			
		||||
            deferred.resolve(importData);
 | 
			
		||||
        }, function () {
 | 
			
		||||
            deferred.reject();
 | 
			
		||||
        });
 | 
			
		||||
        return deferred.promise;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* eslint-enable no-undef, angular/window-service, angular/document-service */
 | 
			
		||||
							
								
								
									
										24
									
								
								ui/src/app/import-export/index.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								ui/src/app/import-export/index.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,24 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright © 2016 The Thingsboard Authors
 | 
			
		||||
 *
 | 
			
		||||
 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
 * you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 *
 | 
			
		||||
 *     http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
 * See the License for the specific language governing permissions and
 | 
			
		||||
 * limitations under the License.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
import ImportExport from './import-export.service';
 | 
			
		||||
import ImportDialogController from './import-dialog.controller';
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
export default angular.module('thingsboard.importexport', [])
 | 
			
		||||
    .factory('importExport', ImportExport)
 | 
			
		||||
    .controller('ImportDialogController', ImportDialogController)
 | 
			
		||||
    .name;
 | 
			
		||||
@ -25,11 +25,12 @@ export default angular.module('thingsboard.itembuffer', [angularStorage])
 | 
			
		||||
    .name;
 | 
			
		||||
 | 
			
		||||
/*@ngInject*/
 | 
			
		||||
function ItemBuffer(bufferStore) {
 | 
			
		||||
function ItemBuffer(bufferStore, types) {
 | 
			
		||||
 | 
			
		||||
    const WIDGET_ITEM = "widget_item";
 | 
			
		||||
 | 
			
		||||
    var service = {
 | 
			
		||||
        prepareWidgetItem: prepareWidgetItem,
 | 
			
		||||
        copyWidget: copyWidget,
 | 
			
		||||
        hasWidget: hasWidget,
 | 
			
		||||
        pasteWidget: pasteWidget,
 | 
			
		||||
@ -56,12 +57,57 @@ function ItemBuffer(bufferStore) {
 | 
			
		||||
     }
 | 
			
		||||
    **/
 | 
			
		||||
 | 
			
		||||
    function copyWidget(widget, aliasesInfo, originalColumns) {
 | 
			
		||||
        var widgetItem = {
 | 
			
		||||
    function prepareWidgetItem(dashboard, widget) {
 | 
			
		||||
        var aliasesInfo = {
 | 
			
		||||
            datasourceAliases: {},
 | 
			
		||||
            targetDeviceAliases: {}
 | 
			
		||||
        };
 | 
			
		||||
        var originalColumns = 24;
 | 
			
		||||
        if (dashboard.configuration.gridSettings &&
 | 
			
		||||
            dashboard.configuration.gridSettings.columns) {
 | 
			
		||||
            originalColumns = dashboard.configuration.gridSettings.columns;
 | 
			
		||||
        }
 | 
			
		||||
        if (widget.config && dashboard.configuration
 | 
			
		||||
            && dashboard.configuration.deviceAliases) {
 | 
			
		||||
            var deviceAlias;
 | 
			
		||||
            if (widget.config.datasources) {
 | 
			
		||||
                for (var i=0;i<widget.config.datasources.length;i++) {
 | 
			
		||||
                    var datasource = widget.config.datasources[i];
 | 
			
		||||
                    if (datasource.type === types.datasourceType.device && datasource.deviceAliasId) {
 | 
			
		||||
                        deviceAlias = dashboard.configuration.deviceAliases[datasource.deviceAliasId];
 | 
			
		||||
                        if (deviceAlias) {
 | 
			
		||||
                            aliasesInfo.datasourceAliases[i] = {
 | 
			
		||||
                                aliasName: deviceAlias.alias,
 | 
			
		||||
                                deviceId: deviceAlias.deviceId
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            if (widget.config.targetDeviceAliasIds) {
 | 
			
		||||
                for (i=0;i<widget.config.targetDeviceAliasIds.length;i++) {
 | 
			
		||||
                    var targetDeviceAliasId = widget.config.targetDeviceAliasIds[i];
 | 
			
		||||
                    if (targetDeviceAliasId) {
 | 
			
		||||
                        deviceAlias = dashboard.configuration.deviceAliases[targetDeviceAliasId];
 | 
			
		||||
                        if (deviceAlias) {
 | 
			
		||||
                            aliasesInfo.targetDeviceAliases[i] = {
 | 
			
		||||
                                aliasName: deviceAlias.alias,
 | 
			
		||||
                                deviceId: deviceAlias.deviceId
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return {
 | 
			
		||||
            widget: widget,
 | 
			
		||||
            aliasesInfo: aliasesInfo,
 | 
			
		||||
            originalColumns: originalColumns
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function copyWidget(dashboard, widget) {
 | 
			
		||||
        var widgetItem = prepareWidgetItem(dashboard, widget);
 | 
			
		||||
        bufferStore.set(WIDGET_ITEM, angular.toJson(widgetItem));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -69,7 +115,7 @@ function ItemBuffer(bufferStore) {
 | 
			
		||||
        return bufferStore.get(WIDGET_ITEM);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function pasteWidget(targetDasgboard, position) {
 | 
			
		||||
    function pasteWidget(targetDashboard, position) {
 | 
			
		||||
        var widgetItemJson = bufferStore.get(WIDGET_ITEM);
 | 
			
		||||
        if (widgetItemJson) {
 | 
			
		||||
            var widgetItem = angular.fromJson(widgetItemJson);
 | 
			
		||||
@ -82,7 +128,7 @@ function ItemBuffer(bufferStore) {
 | 
			
		||||
                targetRow = position.row;
 | 
			
		||||
                targetColumn = position.column;
 | 
			
		||||
            }
 | 
			
		||||
            addWidgetToDashboard(targetDasgboard, widget, aliasesInfo, originalColumns, targetRow, targetColumn);
 | 
			
		||||
            addWidgetToDashboard(targetDashboard, widget, aliasesInfo, originalColumns, targetRow, targetColumn);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -34,7 +34,13 @@ export default class TbAnalogueLinearGauge {
 | 
			
		||||
        var majorTicksCount = settings.majorTicksCount || 10;
 | 
			
		||||
        var total = maxValue-minValue;
 | 
			
		||||
        var step = (total/majorTicksCount);
 | 
			
		||||
        step = parseFloat(parseFloat(step).toPrecision(12));
 | 
			
		||||
 | 
			
		||||
        var valueInt = settings.valueInt || 3;
 | 
			
		||||
 | 
			
		||||
        var valueDec = (angular.isDefined(settings.valueDec) && settings.valueDec !== null)
 | 
			
		||||
            ? settings.valueDec : 2;
 | 
			
		||||
 | 
			
		||||
        step = parseFloat(parseFloat(step).toFixed(valueDec));
 | 
			
		||||
 | 
			
		||||
        var majorTicks = [];
 | 
			
		||||
        var highlights = [];
 | 
			
		||||
@ -44,7 +50,7 @@ export default class TbAnalogueLinearGauge {
 | 
			
		||||
            var majorTick = tick + minValue;
 | 
			
		||||
            majorTicks.push(majorTick);
 | 
			
		||||
            var nextTick = tick+step;
 | 
			
		||||
            nextTick = parseFloat(parseFloat(nextTick).toPrecision(12));
 | 
			
		||||
            nextTick = parseFloat(parseFloat(nextTick).toFixed(valueDec));
 | 
			
		||||
            if (tick<total) {
 | 
			
		||||
                var highlightColor = tinycolor(keyColor);
 | 
			
		||||
                var percent = tick/total;
 | 
			
		||||
@ -89,9 +95,8 @@ export default class TbAnalogueLinearGauge {
 | 
			
		||||
            // borders
 | 
			
		||||
 | 
			
		||||
            // number formats
 | 
			
		||||
            valueInt: settings.valueInt || 3,
 | 
			
		||||
            valueDec: (angular.isDefined(settings.valueDec) && settings.valueDec !== null)
 | 
			
		||||
                ? settings.valueDec : 2,
 | 
			
		||||
            valueInt: valueInt,
 | 
			
		||||
            valueDec: valueDec,
 | 
			
		||||
            majorTicksInt: 1,
 | 
			
		||||
            majorTicksDec: 0,
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -35,7 +35,13 @@ export default class TbAnalogueRadialGauge {
 | 
			
		||||
        var majorTicksCount = settings.majorTicksCount || 10;
 | 
			
		||||
        var total = maxValue-minValue;
 | 
			
		||||
        var step = (total/majorTicksCount);
 | 
			
		||||
        step = parseFloat(parseFloat(step).toPrecision(12));
 | 
			
		||||
 | 
			
		||||
        var valueInt = settings.valueInt || 3;
 | 
			
		||||
 | 
			
		||||
        var valueDec = (angular.isDefined(settings.valueDec) && settings.valueDec !== null)
 | 
			
		||||
            ? settings.valueDec : 2;
 | 
			
		||||
 | 
			
		||||
        step = parseFloat(parseFloat(step).toFixed(valueDec));
 | 
			
		||||
 | 
			
		||||
        var majorTicks = [];
 | 
			
		||||
        var highlights = [];
 | 
			
		||||
@ -44,7 +50,7 @@ export default class TbAnalogueRadialGauge {
 | 
			
		||||
        while(tick<=maxValue) {
 | 
			
		||||
            majorTicks.push(tick);
 | 
			
		||||
            var nextTick = tick+step;
 | 
			
		||||
            nextTick = parseFloat(parseFloat(nextTick).toPrecision(12));
 | 
			
		||||
            nextTick = parseFloat(parseFloat(nextTick).toFixed(valueDec));
 | 
			
		||||
            if (tick<maxValue) {
 | 
			
		||||
                var highlightColor = tinycolor(keyColor);
 | 
			
		||||
                var percent = (tick-minValue)/total;
 | 
			
		||||
@ -86,9 +92,8 @@ export default class TbAnalogueRadialGauge {
 | 
			
		||||
            //borderShadowWidth: (settings.showBorder !== false) ? 3 : 0,
 | 
			
		||||
 | 
			
		||||
            // number formats
 | 
			
		||||
            valueInt: settings.valueInt || 3,
 | 
			
		||||
            valueDec: (angular.isDefined(settings.valueDec) && settings.valueDec !== null)
 | 
			
		||||
                ? settings.valueDec : 2,
 | 
			
		||||
            valueInt: valueInt,
 | 
			
		||||
            valueDec: valueDec,
 | 
			
		||||
            majorTicksInt: 1,
 | 
			
		||||
            majorTicksDec: 0,
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -40,7 +40,9 @@
 | 
			
		||||
    "refresh": "Refresh",
 | 
			
		||||
    "undo": "Undo",
 | 
			
		||||
    "copy": "Copy",
 | 
			
		||||
    "paste": "Paste"
 | 
			
		||||
    "paste": "Paste",
 | 
			
		||||
    "import": "Import",
 | 
			
		||||
    "export": "Export"
 | 
			
		||||
  },
 | 
			
		||||
  "admin": {
 | 
			
		||||
    "general": "General",
 | 
			
		||||
@ -214,7 +216,19 @@
 | 
			
		||||
    "vertical-margin-required": "Vertical margin value is required.",
 | 
			
		||||
    "min-vertical-margin-message": "Only 0 is allowed as minimum vertical margin value.",
 | 
			
		||||
    "max-vertical-margin-message": "Only 50 is allowed as maximum vertical margin value.",
 | 
			
		||||
    "display-title": "Display dashboard title"
 | 
			
		||||
    "display-title": "Display dashboard title",
 | 
			
		||||
    "import": "Import dashboard",
 | 
			
		||||
    "export": "Export dashboard",
 | 
			
		||||
    "export-failed-error": "Unable to export dashboard: {error}",
 | 
			
		||||
    "create-new-dashboard": "Create new dashboard",
 | 
			
		||||
    "dashboard-file": "Dashboard file",
 | 
			
		||||
    "invalid-dashboard-file-error": "Unable to import dashboard: Invalid dashboard data structure.",
 | 
			
		||||
    "dashboard-import-missing-aliases-title": "Select missing devices for dashboard aliases",
 | 
			
		||||
    "create-new-widget": "Create new widget",
 | 
			
		||||
    "import-widget": "Import widget",
 | 
			
		||||
    "widget-file": "Widget file",
 | 
			
		||||
    "invalid-widget-file-error": "Unable to import widget: Invalid widget data structure.",
 | 
			
		||||
    "widget-import-missing-aliases-title": "Select missing devices used by widget"
 | 
			
		||||
  },
 | 
			
		||||
  "datakey": {
 | 
			
		||||
    "settings": "Settings",
 | 
			
		||||
@ -370,6 +384,10 @@
 | 
			
		||||
    "avatar": "Avatar",
 | 
			
		||||
    "open-user-menu": "Open user menu"
 | 
			
		||||
  },
 | 
			
		||||
  "import": {
 | 
			
		||||
    "no-file": "No file selected",
 | 
			
		||||
    "drop-file": "Drop a JSON file or click to select a file to upload."
 | 
			
		||||
  },
 | 
			
		||||
  "item": {
 | 
			
		||||
    "selected": "Selected"
 | 
			
		||||
  },
 | 
			
		||||
@ -612,7 +630,8 @@
 | 
			
		||||
    "widget-type-load-failed-error": "Failed to load widget type!",
 | 
			
		||||
    "widget-template-load-failed-error": "Failed to load widget template!",
 | 
			
		||||
    "add": "Add Widget",
 | 
			
		||||
    "undo": "Undo widget changes"
 | 
			
		||||
    "undo": "Undo widget changes",
 | 
			
		||||
    "export": "Export widget"
 | 
			
		||||
  },
 | 
			
		||||
  "widgets-bundle": {
 | 
			
		||||
    "current": "Current bundle",
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user