Browse Source

Merge branch 'master' of github.com:thingsboard/thingsboard

pull/945/head
Andrew Shvayka 8 years ago
parent
commit
5faf9df11f
  1. 18
      application/src/main/java/org/thingsboard/server/actors/rpc/RpcManagerActor.java
  2. 7
      application/src/main/resources/thingsboard.yml
  3. 3
      dao/src/main/java/org/thingsboard/server/dao/relation/BaseRelationService.java
  4. 4
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/delay/TbMsgDelayNode.java
  5. 54
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbOriginatorTypeFilterNode.java
  6. 38
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbOriginatorTypeFilterNodeConfiguration.java
  7. 6
      rule-engine/rule-engine-components/src/main/resources/public/static/rulenode/rulenode-core-config.js
  8. 4
      ui/package.json
  9. 2
      ui/src/app/api/dashboard.service.js
  10. 7
      ui/src/app/api/user.service.js
  11. 7
      ui/src/app/app.run.js
  12. 68
      ui/src/app/common/types.constant.js
  13. 4
      ui/src/app/components/json-content.directive.js
  14. 2
      ui/src/app/components/json-object-edit.directive.js
  15. 6
      ui/src/app/components/widget/widget.controller.js
  16. 11
      ui/src/app/dashboard/dashboard.controller.js
  17. 18
      ui/src/app/dashboard/dashboard.routes.js
  18. 2
      ui/src/app/dashboard/dashboard.tpl.html
  19. 21
      ui/src/app/entity/entity-select.directive.js
  20. 2
      ui/src/app/entity/entity-select.tpl.html
  21. 57
      ui/src/app/entity/entity-type-list.directive.js
  22. 15
      ui/src/app/entity/relation/relation-filters.directive.js
  23. 8
      ui/src/app/help/help-links.constant.js
  24. 148
      ui/src/app/locale/locale.constant-it.js
  25. 5
      ui/src/app/locale/locale.constant.js
  26. 2
      ui/src/app/rulechain/index.js
  27. 100
      ui/src/app/rulechain/message-type-autocomplete.directive.js
  28. 25
      ui/src/app/rulechain/message-type-autocomplete.scss
  29. 41
      ui/src/app/rulechain/message-type-autocomplete.tpl.html
  30. 17
      ui/src/app/rulechain/script/node-script-test.tpl.html
  31. 11
      ui/src/app/widget/widget-editor.controller.js
  32. 8
      ui/webpack.config.prod.js

18
application/src/main/java/org/thingsboard/server/actors/rpc/RpcManagerActor.java

@ -29,11 +29,7 @@ import org.thingsboard.server.common.msg.cluster.ServerAddress;
import org.thingsboard.server.gen.cluster.ClusterAPIProtos; import org.thingsboard.server.gen.cluster.ClusterAPIProtos;
import org.thingsboard.server.service.cluster.discovery.ServerInstance; import org.thingsboard.server.service.cluster.discovery.ServerInstance;
import java.util.HashMap; import java.util.*;
import java.util.LinkedList;
import java.util.Map;
import java.util.Queue;
import java.util.UUID;
/** /**
* @author Andrew Shvayka * @author Andrew Shvayka
@ -88,7 +84,17 @@ public class RpcManagerActor extends ContextAwareActor {
private void onMsg(RpcBroadcastMsg msg) { private void onMsg(RpcBroadcastMsg msg) {
log.debug("Forwarding msg to session actors {}", msg); log.debug("Forwarding msg to session actors {}", msg);
sessionActors.keySet().forEach(address -> onMsg(msg.getMsg())); sessionActors.keySet().forEach(address -> {
ClusterAPIProtos.ClusterMessage msgWithServerAddress = msg.getMsg()
.toBuilder()
.setServerAddress(ClusterAPIProtos.ServerAddress
.newBuilder()
.setHost(address.getHost())
.setPort(address.getPort())
.build())
.build();
onMsg(msgWithServerAddress);
});
pendingMsgs.values().forEach(queue -> queue.add(msg.getMsg())); pendingMsgs.values().forEach(queue -> queue.add(msg.getMsg()));
} }

7
application/src/main/resources/thingsboard.yml

@ -328,6 +328,13 @@ spring.mvc.cors:
max-age: "1800" max-age: "1800"
allow-credentials: "true" allow-credentials: "true"
# spring serve gzip compressed static resources
spring.resources.chain:
gzipped: "true"
strategy:
content:
enabled: "true"
# HSQLDB DAO Configuration # HSQLDB DAO Configuration
spring: spring:
data: data:

3
dao/src/main/java/org/thingsboard/server/dao/relation/BaseRelationService.java

@ -555,6 +555,9 @@ public class BaseRelationService implements RelationService {
private ListenableFuture<List<EntityRelation>> findRelations(final EntityId rootId, final EntitySearchDirection direction, RelationTypeGroup relationTypeGroup) { private ListenableFuture<List<EntityRelation>> findRelations(final EntityId rootId, final EntitySearchDirection direction, RelationTypeGroup relationTypeGroup) {
ListenableFuture<List<EntityRelation>> relations; ListenableFuture<List<EntityRelation>> relations;
if (relationTypeGroup == null) {
relationTypeGroup = RelationTypeGroup.COMMON;
}
if (direction == EntitySearchDirection.FROM) { if (direction == EntitySearchDirection.FROM) {
relations = findByFromAsync(rootId, relationTypeGroup); relations = findByFromAsync(rootId, relationTypeGroup);
} else { } else {

4
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/delay/TbMsgDelayNode.java

@ -41,7 +41,9 @@ import static org.thingsboard.rule.engine.api.TbRelationTypes.SUCCESS;
configClazz = TbMsgDelayNodeConfiguration.class, configClazz = TbMsgDelayNodeConfiguration.class,
nodeDescription = "Delays incoming message", nodeDescription = "Delays incoming message",
nodeDetails = "Delays messages for configurable period.", nodeDetails = "Delays messages for configurable period.",
icon = "repeat" icon = "pause",
uiResources = {"static/rulenode/rulenode-core-config.js"},
configDirective = "tbActionNodeMsgDelayConfig"
) )
public class TbMsgDelayNode implements TbNode { public class TbMsgDelayNode implements TbNode {

54
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbOriginatorTypeFilterNode.java

@ -0,0 +1,54 @@
/**
* Copyright © 2016-2018 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.
*/
package org.thingsboard.rule.engine.filter;
import lombok.extern.slf4j.Slf4j;
import org.thingsboard.rule.engine.api.util.TbNodeUtils;
import org.thingsboard.rule.engine.api.*;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.plugin.ComponentType;
import org.thingsboard.server.common.msg.TbMsg;
@Slf4j
@RuleNode(
type = ComponentType.FILTER,
name = "originator type",
configClazz = TbOriginatorTypeFilterNodeConfiguration.class,
relationTypes = {"True", "False"},
nodeDescription = "Filter incoming messages by message Originator Type",
nodeDetails = "If Originator Type of incoming message is expected - send Message via <b>True</b> chain, otherwise <b>False</b> chain is used.",
uiResources = {"static/rulenode/rulenode-core-config.js", "static/rulenode/rulenode-core-config.css"},
configDirective = "tbFilterNodeOriginatorTypeConfig")
public class TbOriginatorTypeFilterNode implements TbNode {
TbOriginatorTypeFilterNodeConfiguration config;
@Override
public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException {
this.config = TbNodeUtils.convert(configuration, TbOriginatorTypeFilterNodeConfiguration.class);
}
@Override
public void onMsg(TbContext ctx, TbMsg msg) throws TbNodeException {
EntityType originatorType = msg.getOriginator().getEntityType();
ctx.tellNext(msg, config.getOriginatorTypes().contains(originatorType) ? "True" : "False");
}
@Override
public void destroy() {
}
}

38
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbOriginatorTypeFilterNodeConfiguration.java

@ -0,0 +1,38 @@
/**
* Copyright © 2016-2018 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.
*/
package org.thingsboard.rule.engine.filter;
import lombok.Data;
import org.thingsboard.rule.engine.api.NodeConfiguration;
import org.thingsboard.server.common.data.EntityType;
import java.util.Arrays;
import java.util.List;
@Data
public class TbOriginatorTypeFilterNodeConfiguration implements NodeConfiguration<TbOriginatorTypeFilterNodeConfiguration> {
private List<EntityType> originatorTypes;
@Override
public TbOriginatorTypeFilterNodeConfiguration defaultConfiguration() {
TbOriginatorTypeFilterNodeConfiguration configuration = new TbOriginatorTypeFilterNodeConfiguration();
configuration.setOriginatorTypes(Arrays.asList(
EntityType.DEVICE
));
return configuration;
}
}

6
rule-engine/rule-engine-components/src/main/resources/public/static/rulenode/rulenode-core-config.js

File diff suppressed because one or more lines are too long

4
ui/package.json

@ -15,7 +15,6 @@
}, },
"dependencies": { "dependencies": {
"@flowjs/ng-flow": "^2.7.1", "@flowjs/ng-flow": "^2.7.1",
"ace-builds": "1.3.1",
"angular": "1.5.8", "angular": "1.5.8",
"angular-animate": "1.5.8", "angular-animate": "1.5.8",
"angular-aria": "1.5.8", "angular-aria": "1.5.8",
@ -47,7 +46,7 @@
"angular-ui-router": "^0.3.1", "angular-ui-router": "^0.3.1",
"angular-websocket": "^2.0.1", "angular-websocket": "^2.0.1",
"base64-js": "^1.2.1", "base64-js": "^1.2.1",
"brace": "^0.8.0", "brace": "^0.10.0",
"canvas-gauges": "^2.0.9", "canvas-gauges": "^2.0.9",
"clipboard": "^1.5.15", "clipboard": "^1.5.15",
"compass-sass-mixins": "^0.12.7", "compass-sass-mixins": "^0.12.7",
@ -96,6 +95,7 @@
"babel-loader": "^6.2.5", "babel-loader": "^6.2.5",
"babel-preset-es2015": "^6.14.0", "babel-preset-es2015": "^6.14.0",
"babel-preset-react": "^6.16.0", "babel-preset-react": "^6.16.0",
"compression-webpack-plugin": "^1.1.11",
"connect-history-api-fallback": "^1.3.0", "connect-history-api-fallback": "^1.3.0",
"copy-webpack-plugin": "^3.0.1", "copy-webpack-plugin": "^3.0.1",
"cross-env": "^3.2.4", "cross-env": "^3.2.4",

2
ui/src/app/api/dashboard.service.js

@ -252,7 +252,7 @@ function DashboardService($rootScope, $http, $q, $location, $filter) {
if (port != 80 && port != 443) { if (port != 80 && port != 443) {
url += ":" + port; url += ":" + port;
} }
url += "/dashboards/" + dashboard.id.id + "?publicId=" + dashboard.publicCustomerId; url += "/dashboard/" + dashboard.id.id + "?publicId=" + dashboard.publicCustomerId;
return url; return url;
} }

7
ui/src/app/api/user.service.js

@ -488,7 +488,8 @@ function UserService($http, $q, $rootScope, adminService, dashboardService, logi
} else { } else {
return true; return true;
} }
} else if (to.name === 'home.dashboards.dashboard' && allowedDashboardIds.indexOf(params.dashboardId) > -1) { } else if ((to.name === 'home.dashboards.dashboard' || to.name === 'dashboard')
&& allowedDashboardIds.indexOf(params.dashboardId) > -1) {
return false; return false;
} else { } else {
return true; return true;
@ -504,10 +505,10 @@ function UserService($http, $q, $rootScope, adminService, dashboardService, logi
var place = 'home.links'; var place = 'home.links';
if (currentUser.authority === 'TENANT_ADMIN' || currentUser.authority === 'CUSTOMER_USER') { if (currentUser.authority === 'TENANT_ADMIN' || currentUser.authority === 'CUSTOMER_USER') {
if (userHasDefaultDashboard()) { if (userHasDefaultDashboard()) {
place = 'home.dashboards.dashboard'; place = $rootScope.forceFullscreen ? 'dashboard' : 'home.dashboards.dashboard';
params = {dashboardId: currentUserDetails.additionalInfo.defaultDashboardId}; params = {dashboardId: currentUserDetails.additionalInfo.defaultDashboardId};
} else if (isPublic()) { } else if (isPublic()) {
place = 'home.dashboards.dashboard'; place = 'dashboard';
params = {dashboardId: lastPublicDashboardId}; params = {dashboardId: lastPublicDashboardId};
} }
} else if (currentUser.authority === 'SYS_ADMIN') { } else if (currentUser.authority === 'SYS_ADMIN') {

7
ui/src/app/app.run.js

@ -113,7 +113,10 @@ export default function AppRun($rootScope, $window, $injector, $location, $log,
showForbiddenDialog(); showForbiddenDialog();
} else if (to.redirectTo) { } else if (to.redirectTo) {
evt.preventDefault(); evt.preventDefault();
$state.go(to.redirectTo, params) $state.go(to.redirectTo, params);
} else if (to.name === 'home.dashboards.dashboard' && $rootScope.forceFullscreen) {
evt.preventDefault();
$state.go('dashboard', params);
} }
} }
} else { } else {
@ -138,7 +141,7 @@ export default function AppRun($rootScope, $window, $injector, $location, $log,
$rootScope.pageTitle = 'ThingsBoard'; $rootScope.pageTitle = 'ThingsBoard';
$rootScope.stateChangeSuccessHandle = $rootScope.$on('$stateChangeSuccess', function (evt, to, params) { $rootScope.stateChangeSuccessHandle = $rootScope.$on('$stateChangeSuccess', function (evt, to, params) {
if (userService.isPublic() && to.name === 'home.dashboards.dashboard') { if (userService.isPublic() && to.name === 'dashboard') {
$location.search('publicId', userService.getPublicId()); $location.search('publicId', userService.getPublicId());
userService.updateLastPublicDashboardId(params.dashboardId); userService.updateLastPublicDashboardId(params.dashboardId);
} }

68
ui/src/app/common/types.constant.js

@ -366,6 +366,12 @@ export default angular.module('thingsboard.types', [])
list: 'entity.list-of-rulechains', list: 'entity.list-of-rulechains',
nameStartsWith: 'entity.rulechain-name-starts-with' nameStartsWith: 'entity.rulechain-name-starts-with'
}, },
"RULE_NODE": {
type: 'entity.type-rulenode',
typePlural: 'entity.type-rulenodes',
list: 'entity.list-of-rulenodes',
nameStartsWith: 'entity.rulenode-name-starts-with'
},
"CURRENT_CUSTOMER": { "CURRENT_CUSTOMER": {
type: 'entity.type-current-customer', type: 'entity.type-current-customer',
list: 'entity.type-current-customer' list: 'entity.type-current-customer'
@ -590,6 +596,68 @@ export default angular.module('thingsboard.types', [])
icon: "help_outline" icon: "help_outline"
} }
}, },
messageType: {
'POST_ATTRIBUTES_REQUEST': {
name: 'Post attributes',
value: 'POST_ATTRIBUTES_REQUEST'
},
'POST_TELEMETRY_REQUEST': {
name: 'Post telemetry',
value: 'POST_TELEMETRY_REQUEST'
},
'TO_SERVER_RPC_REQUEST': {
name: 'RPC Request from Device',
value: 'TO_SERVER_RPC_REQUEST'
},
'RPC_CALL_FROM_SERVER_TO_DEVICE': {
name: 'RPC Request to Device',
value: 'RPC_CALL_FROM_SERVER_TO_DEVICE'
},
'ACTIVITY_EVENT': {
name: 'Activity Event',
value: 'ACTIVITY_EVENT'
},
'INACTIVITY_EVENT': {
name: 'Inactivity Event',
value: 'INACTIVITY_EVENT'
},
'CONNECT_EVENT': {
name: 'Connect Event',
value: 'CONNECT_EVENT'
},
'DISCONNECT_EVENT': {
name: 'Disconnect Event',
value: 'DISCONNECT_EVENT'
},
'ENTITY_CREATED': {
name: 'Entity Created',
value: 'ENTITY_CREATED'
},
'ENTITY_UPDATED': {
name: 'Entity Updated',
value: 'ENTITY_UPDATED'
},
'ENTITY_DELETED': {
name: 'Entity Deleted',
value: 'ENTITY_DELETED'
},
'ENTITY_ASSIGNED': {
name: 'Entity Assigned',
value: 'ENTITY_ASSIGNED'
},
'ENTITY_UNASSIGNED': {
name: 'Entity Unassigned',
value: 'ENTITY_UNASSIGNED'
},
'ATTRIBUTES_UPDATED': {
name: 'Attributes Updated',
value: 'ATTRIBUTES_UPDATED'
},
'ATTRIBUTES_DELETED': {
name: 'Attributes Deleted',
value: 'ATTRIBUTES_DELETED'
}
},
valueType: { valueType: {
string: { string: {
value: "string", value: "string",

4
ui/src/app/components/json-content.directive.js

@ -18,8 +18,8 @@ import './json-content.scss';
import 'brace/ext/language_tools'; import 'brace/ext/language_tools';
import 'brace/mode/json'; import 'brace/mode/json';
import 'brace/mode/text'; import 'brace/mode/text';
import 'ace-builds/src-min-noconflict/snippets/json'; import 'brace/snippets/json';
import 'ace-builds/src-min-noconflict/snippets/text'; import 'brace/snippets/text';
import fixAceEditor from './ace-editor-fix'; import fixAceEditor from './ace-editor-fix';

2
ui/src/app/components/json-object-edit.directive.js

@ -17,7 +17,7 @@ import './json-object-edit.scss';
import 'brace/ext/language_tools'; import 'brace/ext/language_tools';
import 'brace/mode/json'; import 'brace/mode/json';
import 'ace-builds/src-min-noconflict/snippets/json'; import 'brace/snippets/json';
import fixAceEditor from './ace-editor-fix'; import fixAceEditor from './ace-editor-fix';

6
ui/src/app/components/widget/widget.controller.js

@ -479,7 +479,11 @@ export default function WidgetController($scope, $state, $timeout, $window, $ele
dashboardId: targetDashboardId, dashboardId: targetDashboardId,
state: utils.objToBase64([ stateObject ]) state: utils.objToBase64([ stateObject ])
} }
$state.go('home.dashboards.dashboard', stateParams); if ($state.current.name === 'dashboard') {
$state.go('dashboard', stateParams);
} else {
$state.go('home.dashboards.dashboard', stateParams);
}
break; break;
case types.widgetActionTypes.custom.value: case types.widgetActionTypes.custom.value:
var customFunction = descriptor.customFunction; var customFunction = descriptor.customFunction;

11
ui/src/app/dashboard/dashboard.controller.js

@ -196,6 +196,7 @@ export default function DashboardController(types, utils, dashboardUtils, widget
vm.displayDashboardTimewindow = displayDashboardTimewindow; vm.displayDashboardTimewindow = displayDashboardTimewindow;
vm.displayDashboardsSelect = displayDashboardsSelect; vm.displayDashboardsSelect = displayDashboardsSelect;
vm.displayEntitiesSelect = displayEntitiesSelect; vm.displayEntitiesSelect = displayEntitiesSelect;
vm.hideFullscreenButton = hideFullscreenButton;
vm.widgetsBundle; vm.widgetsBundle;
@ -258,7 +259,11 @@ export default function DashboardController(types, utils, dashboardUtils, widget
dashboardId: vm.currentDashboardId dashboardId: vm.currentDashboardId
}); });
} else { } else {
$state.go('home.dashboards.dashboard', {dashboardId: vm.currentDashboardId}); if ($state.current.name === 'dashboard') {
$state.go('dashboard', {dashboardId: vm.currentDashboardId});
} else {
$state.go('home.dashboards.dashboard', {dashboardId: vm.currentDashboardId});
}
} }
} }
}); });
@ -805,6 +810,10 @@ export default function DashboardController(types, utils, dashboardUtils, widget
} }
} }
function hideFullscreenButton() {
return vm.widgetEditMode || vm.iframeMode || $rootScope.forceFullscreen || $state.current.name === 'dashboard';
}
function onRevertWidgetEdit(widgetForm) { function onRevertWidgetEdit(widgetForm) {
if (widgetForm.$dirty) { if (widgetForm.$dirty) {
widgetForm.$setPristine(); widgetForm.$setPristine();

18
ui/src/app/dashboard/dashboard.routes.js

@ -86,6 +86,24 @@ export default function DashboardRoutes($stateProvider) {
label: '{"icon": "dashboard", "label": "{{ vm.dashboard.title }}", "translate": "false"}' label: '{"icon": "dashboard", "label": "{{ vm.dashboard.title }}", "translate": "false"}'
} }
}) })
.state('dashboard', {
url: '/dashboard/:dashboardId?state',
reloadOnSearch: false,
module: 'private',
auth: ['TENANT_ADMIN', 'CUSTOMER_USER'],
views: {
"@": {
templateUrl: dashboardTemplate,
controller: 'DashboardController',
controllerAs: 'vm'
}
},
data: {
widgetEditMode: false,
searchEnabled: false,
pageTitle: 'dashboard.dashboard'
}
})
.state('home.customers.dashboards.dashboard', { .state('home.customers.dashboards.dashboard', {
url: '/:dashboardId?state', url: '/:dashboardId?state',
reloadOnSearch: false, reloadOnSearch: false,

2
ui/src/app/dashboard/dashboard.tpl.html

@ -16,7 +16,7 @@
--> -->
<md-content style="padding-top: 150px;" flex tb-expand-fullscreen="vm.widgetEditMode || vm.iframeMode || forceFullscreen" expand-button-id="dashboard-expand-button" <md-content style="padding-top: 150px;" flex tb-expand-fullscreen="vm.widgetEditMode || vm.iframeMode || forceFullscreen" expand-button-id="dashboard-expand-button"
hide-expand-button="vm.widgetEditMode || vm.iframeMode || forceFullscreen" expand-tooltip-direction="bottom" ng-if="vm.dashboard"> hide-expand-button="vm.hideFullscreenButton()" expand-tooltip-direction="bottom" ng-if="vm.dashboard">
<section class="tb-dashboard-toolbar" ng-show="vm.showDashboardToolbar()" <section class="tb-dashboard-toolbar" ng-show="vm.showDashboardToolbar()"
ng-class="{ 'tb-dashboard-toolbar-opened': vm.toolbarOpened, 'tb-dashboard-toolbar-closed': !vm.toolbarOpened }"> ng-class="{ 'tb-dashboard-toolbar-opened': vm.toolbarOpened, 'tb-dashboard-toolbar-closed': !vm.toolbarOpened }">
<tb-dashboard-toolbar ng-show="!vm.widgetEditMode" force-fullscreen="forceFullscreen" <tb-dashboard-toolbar ng-show="!vm.widgetEditMode" force-fullscreen="forceFullscreen"

21
ui/src/app/entity/entity-select.directive.js

@ -22,14 +22,28 @@ import entitySelectTemplate from './entity-select.tpl.html';
/* eslint-enable import/no-unresolved, import/default */ /* eslint-enable import/no-unresolved, import/default */
/*@ngInject*/ /*@ngInject*/
export default function EntitySelect($compile, $templateCache) { export default function EntitySelect($compile, $templateCache, entityService) {
var linker = function (scope, element, attrs, ngModelCtrl) { var linker = function (scope, element, attrs, ngModelCtrl) {
var template = $templateCache.get(entitySelectTemplate); var template = $templateCache.get(entitySelectTemplate);
element.html(template); element.html(template);
scope.tbRequired = angular.isDefined(scope.tbRequired) ? scope.tbRequired : false; scope.tbRequired = angular.isDefined(scope.tbRequired) ? scope.tbRequired : false;
scope.model = {};
var entityTypes = entityService.prepareAllowedEntityTypesList(scope.allowedEntityTypes, scope.useAliasEntityTypes);
var entityTypeKeys = Object.keys(entityTypes);
if (entityTypeKeys.length === 1) {
scope.displayEntityTypeSelect = false;
scope.defaultEntityType = entityTypes[entityTypeKeys[0]];
} else {
scope.displayEntityTypeSelect = true;
}
scope.model = {
entityType: scope.defaultEntityType
};
scope.updateView = function () { scope.updateView = function () {
if (!scope.disabled) { if (!scope.disabled) {
@ -54,7 +68,7 @@ export default function EntitySelect($compile, $templateCache) {
scope.model.entityType = value.entityType; scope.model.entityType = value.entityType;
scope.model.entityId = value.id; scope.model.entityId = value.id;
} else { } else {
scope.model.entityType = null; scope.model.entityType = scope.defaultEntityType;
scope.model.entityId = null; scope.model.entityId = null;
} }
initWatchers(); initWatchers();
@ -106,6 +120,7 @@ export default function EntitySelect($compile, $templateCache) {
theForm: '=?', theForm: '=?',
tbRequired: '=?', tbRequired: '=?',
disabled:'=ngDisabled', disabled:'=ngDisabled',
allowedEntityTypes: "=?",
useAliasEntityTypes: "=?" useAliasEntityTypes: "=?"
} }
}; };

2
ui/src/app/entity/entity-select.tpl.html

@ -17,10 +17,12 @@
--> -->
<div layout='row' class="tb-entity-select"> <div layout='row' class="tb-entity-select">
<tb-entity-type-select style="min-width: 100px;" <tb-entity-type-select style="min-width: 100px;"
ng-if="displayEntityTypeSelect"
the-form="theForm" the-form="theForm"
ng-disabled="disabled" ng-disabled="disabled"
tb-required="tbRequired" tb-required="tbRequired"
use-alias-entity-types="useAliasEntityTypes" use-alias-entity-types="useAliasEntityTypes"
allowed-entity-types="allowedEntityTypes"
ng-model="model.entityType"> ng-model="model.entityType">
</tb-entity-type-select> </tb-entity-type-select>
<tb-entity-autocomplete flex ng-if="model.entityType" <tb-entity-autocomplete flex ng-if="model.entityType"

57
ui/src/app/entity/entity-type-list.directive.js

@ -35,7 +35,30 @@ export default function EntityTypeListDirective($compile, $templateCache, $q, $m
: $translate.instant('entity.any-entity'); : $translate.instant('entity.any-entity');
scope.secondaryPlaceholder = '+' + $translate.instant('entity.entity-type'); scope.secondaryPlaceholder = '+' + $translate.instant('entity.entity-type');
var entityTypes = entityService.prepareAllowedEntityTypesList(scope.allowedEntityTypes); var entityTypes;
if (scope.ignoreAuthorityFilter && scope.allowedEntityTypes
&& scope.allowedEntityTypes.length) {
entityTypes = {};
scope.allowedEntityTypes.forEach((entityTypeValue) => {
var entityType = entityTypeFromValue(entityTypeValue);
if (entityType) {
entityTypes[entityType] = entityTypeValue;
}
});
} else {
entityTypes = entityService.prepareAllowedEntityTypesList(scope.allowedEntityTypes);
}
function entityTypeFromValue(entityTypeValue) {
for (var entityType in types.entityType) {
if (types.entityType[entityType] === entityTypeValue) {
return entityType;
}
}
return null;
}
scope.entityTypesList = []; scope.entityTypesList = [];
for (var type in entityTypes) { for (var type in entityTypes) {
var entityTypeInfo = {}; var entityTypeInfo = {};
@ -62,28 +85,43 @@ export default function EntityTypeListDirective($compile, $templateCache, $q, $m
} }
ngModelCtrl.$render = function () { ngModelCtrl.$render = function () {
scope.entityTypeList = []; if (scope.entityTypeListWatch) {
scope.entityTypeListWatch();
scope.entityTypeListWatch = null;
}
var entityTypeList = [];
var value = ngModelCtrl.$viewValue; var value = ngModelCtrl.$viewValue;
if (value && value.length) { if (value && value.length) {
value.forEach(function(type) { value.forEach(function(type) {
var entityTypeInfo = {}; var entityTypeInfo = {};
entityTypeInfo.value = type; entityTypeInfo.value = type;
entityTypeInfo.name = $translate.instant(types.entityTypeTranslations[entityTypeInfo.value].type) + ''; entityTypeInfo.name = $translate.instant(types.entityTypeTranslations[entityTypeInfo.value].type) + '';
scope.entityTypeList.push(entityTypeInfo); entityTypeList.push(entityTypeInfo);
}); });
} }
scope.entityTypeList = entityTypeList;
scope.entityTypeListWatch = scope.$watch('entityTypeList', function (newVal, prevVal) {
if (!angular.equals(newVal, prevVal)) {
updateEntityTypeList();
}
}, true);
} }
scope.$watch('entityTypeList', function () { function updateEntityTypeList() {
var values = []; var values = ngModelCtrl.$viewValue;
if (!values) {
values = [];
ngModelCtrl.$setViewValue(values);
} else {
values.length = 0;
}
if (scope.entityTypeList && scope.entityTypeList.length) { if (scope.entityTypeList && scope.entityTypeList.length) {
scope.entityTypeList.forEach(function(entityType) { scope.entityTypeList.forEach(function (entityType) {
values.push(entityType.value); values.push(entityType.value);
}); });
} }
ngModelCtrl.$setViewValue(values);
scope.updateValidity(); scope.updateValidity();
}, true); }
$compile(element.contents())(scope); $compile(element.contents())(scope);
@ -103,7 +141,8 @@ export default function EntityTypeListDirective($compile, $templateCache, $q, $m
scope: { scope: {
disabled:'=ngDisabled', disabled:'=ngDisabled',
tbRequired: '=?', tbRequired: '=?',
allowedEntityTypes: '=?' allowedEntityTypes: '=?',
ignoreAuthorityFilter: '=?'
} }
}; };

15
ui/src/app/entity/relation/relation-filters.directive.js

@ -44,6 +44,10 @@ export default function RelationFilters($compile, $templateCache) {
scope.removeFilter = removeFilter; scope.removeFilter = removeFilter;
ngModelCtrl.$render = function () { ngModelCtrl.$render = function () {
if (scope.relationFiltersWatch) {
scope.relationFiltersWatch();
scope.relationFiltersWatch = null;
}
if (ngModelCtrl.$viewValue) { if (ngModelCtrl.$viewValue) {
var value = ngModelCtrl.$viewValue; var value = ngModelCtrl.$viewValue;
scope.relationFilters.length = 0; scope.relationFilters.length = 0;
@ -51,7 +55,7 @@ export default function RelationFilters($compile, $templateCache) {
scope.relationFilters.push(filter); scope.relationFilters.push(filter);
}); });
} }
scope.$watch('relationFilters', function (newVal, prevVal) { scope.relationFiltersWatch = scope.$watch('relationFilters', function (newVal, prevVal) {
if (!angular.equals(newVal, prevVal)) { if (!angular.equals(newVal, prevVal)) {
updateValue(); updateValue();
} }
@ -74,11 +78,16 @@ export default function RelationFilters($compile, $templateCache) {
} }
function updateValue() { function updateValue() {
var value = []; var value = ngModelCtrl.$viewValue;
if (!value) {
value = [];
ngModelCtrl.$setViewValue(value);
} else {
value.length = 0;
}
scope.relationFilters.forEach(function (filter) { scope.relationFilters.forEach(function (filter) {
value.push(filter); value.push(filter);
}); });
ngModelCtrl.$setViewValue(value);
} }
$compile(element.contents())(scope); $compile(element.contents())(scope);
} }

8
ui/src/app/help/help-links.constant.js

@ -20,6 +20,7 @@ var ruleNodeClazzHelpLinkMap = {
'org.thingsboard.rule.engine.filter.TbJsSwitchNode': 'ruleNodeJsSwitch', 'org.thingsboard.rule.engine.filter.TbJsSwitchNode': 'ruleNodeJsSwitch',
'org.thingsboard.rule.engine.filter.TbMsgTypeFilterNode': 'ruleNodeMessageTypeFilter', 'org.thingsboard.rule.engine.filter.TbMsgTypeFilterNode': 'ruleNodeMessageTypeFilter',
'org.thingsboard.rule.engine.filter.TbMsgTypeSwitchNode': 'ruleNodeMessageTypeSwitch', 'org.thingsboard.rule.engine.filter.TbMsgTypeSwitchNode': 'ruleNodeMessageTypeSwitch',
'org.thingsboard.rule.engine.filter.TbOriginatorTypeFilterNode': 'ruleNodeOriginatorTypeFilter',
'org.thingsboard.rule.engine.filter.TbOriginatorTypeSwitchNode': 'ruleNodeOriginatorTypeSwitch', 'org.thingsboard.rule.engine.filter.TbOriginatorTypeSwitchNode': 'ruleNodeOriginatorTypeSwitch',
'org.thingsboard.rule.engine.metadata.TbGetAttributesNode': 'ruleNodeOriginatorAttributes', 'org.thingsboard.rule.engine.metadata.TbGetAttributesNode': 'ruleNodeOriginatorAttributes',
'org.thingsboard.rule.engine.metadata.TbGetOriginatorFieldsNode': 'ruleNodeOriginatorFields', 'org.thingsboard.rule.engine.metadata.TbGetOriginatorFieldsNode': 'ruleNodeOriginatorFields',
@ -31,7 +32,8 @@ var ruleNodeClazzHelpLinkMap = {
'org.thingsboard.rule.engine.transform.TbTransformMsgNode': 'ruleNodeTransformMsg', 'org.thingsboard.rule.engine.transform.TbTransformMsgNode': 'ruleNodeTransformMsg',
'org.thingsboard.rule.engine.mail.TbMsgToEmailNode': 'ruleNodeMsgToEmail', 'org.thingsboard.rule.engine.mail.TbMsgToEmailNode': 'ruleNodeMsgToEmail',
'org.thingsboard.rule.engine.action.TbClearAlarmNode': 'ruleNodeClearAlarm', 'org.thingsboard.rule.engine.action.TbClearAlarmNode': 'ruleNodeClearAlarm',
'org.thingsboard.rule.engine.action.TbCreateAlarmNode': 'ruleNodeCrateAlarm', 'org.thingsboard.rule.engine.action.TbCreateAlarmNode': 'ruleNodeCreateAlarm',
'org.thingsboard.rule.engine.delay.TbMsgDelayNode': 'ruleNodeMsgDelay',
'org.thingsboard.rule.engine.debug.TbMsgGeneratorNode': 'ruleNodeMsgGenerator', 'org.thingsboard.rule.engine.debug.TbMsgGeneratorNode': 'ruleNodeMsgGenerator',
'org.thingsboard.rule.engine.action.TbLogNode': 'ruleNodeLog', 'org.thingsboard.rule.engine.action.TbLogNode': 'ruleNodeLog',
'org.thingsboard.rule.engine.rpc.TbSendRPCReplyNode': 'ruleNodeRpcCallReply', 'org.thingsboard.rule.engine.rpc.TbSendRPCReplyNode': 'ruleNodeRpcCallReply',
@ -61,6 +63,7 @@ export default angular.module('thingsboard.help', [])
ruleNodeJsSwitch: helpBaseUrl + "/docs/user-guide/rule-engine-2-0/filter-nodes/#switch-node", ruleNodeJsSwitch: helpBaseUrl + "/docs/user-guide/rule-engine-2-0/filter-nodes/#switch-node",
ruleNodeMessageTypeFilter: helpBaseUrl + "/docs/user-guide/rule-engine-2-0/filter-nodes/#message-type-filter-node", ruleNodeMessageTypeFilter: helpBaseUrl + "/docs/user-guide/rule-engine-2-0/filter-nodes/#message-type-filter-node",
ruleNodeMessageTypeSwitch: helpBaseUrl + "/docs/user-guide/rule-engine-2-0/filter-nodes/#message-type-switch-node", ruleNodeMessageTypeSwitch: helpBaseUrl + "/docs/user-guide/rule-engine-2-0/filter-nodes/#message-type-switch-node",
ruleNodeOriginatorTypeFilter: helpBaseUrl + "/docs/user-guide/rule-engine-2-0/filter-nodes/#originator-type-filter-node",
ruleNodeOriginatorTypeSwitch: helpBaseUrl + "/docs/user-guide/rule-engine-2-0/filter-nodes/#originator-type-switch-node", ruleNodeOriginatorTypeSwitch: helpBaseUrl + "/docs/user-guide/rule-engine-2-0/filter-nodes/#originator-type-switch-node",
ruleNodeOriginatorAttributes: helpBaseUrl + "/docs/user-guide/rule-engine-2-0/enrichment-nodes/#originator-attributes", ruleNodeOriginatorAttributes: helpBaseUrl + "/docs/user-guide/rule-engine-2-0/enrichment-nodes/#originator-attributes",
ruleNodeOriginatorFields: helpBaseUrl + "/docs/user-guide/rule-engine-2-0/enrichment-nodes/#originator-fields", ruleNodeOriginatorFields: helpBaseUrl + "/docs/user-guide/rule-engine-2-0/enrichment-nodes/#originator-fields",
@ -72,7 +75,8 @@ export default angular.module('thingsboard.help', [])
ruleNodeTransformMsg: helpBaseUrl + "/docs/user-guide/rule-engine-2-0/transformation-nodes/#script-transformation-node", ruleNodeTransformMsg: helpBaseUrl + "/docs/user-guide/rule-engine-2-0/transformation-nodes/#script-transformation-node",
ruleNodeMsgToEmail: helpBaseUrl + "/docs/user-guide/rule-engine-2-0/transformation-nodes/#to-email-node", ruleNodeMsgToEmail: helpBaseUrl + "/docs/user-guide/rule-engine-2-0/transformation-nodes/#to-email-node",
ruleNodeClearAlarm: helpBaseUrl + "/docs/user-guide/rule-engine-2-0/action-nodes/#clear-alarm-node", ruleNodeClearAlarm: helpBaseUrl + "/docs/user-guide/rule-engine-2-0/action-nodes/#clear-alarm-node",
ruleNodeCrateAlarm: helpBaseUrl + "/docs/user-guide/rule-engine-2-0/action-nodes/#create-alarm-node", ruleNodeCreateAlarm: helpBaseUrl + "/docs/user-guide/rule-engine-2-0/action-nodes/#create-alarm-node",
ruleNodeMsgDelay: helpBaseUrl + "/docs/user-guide/rule-engine-2-0/action-nodes/#delay-node",
ruleNodeMsgGenerator: helpBaseUrl + "/docs/user-guide/rule-engine-2-0/action-nodes/#generator-node", ruleNodeMsgGenerator: helpBaseUrl + "/docs/user-guide/rule-engine-2-0/action-nodes/#generator-node",
ruleNodeLog: helpBaseUrl + "/docs/user-guide/rule-engine-2-0/action-nodes/#log-node", ruleNodeLog: helpBaseUrl + "/docs/user-guide/rule-engine-2-0/action-nodes/#log-node",
ruleNodeRpcCallReply: helpBaseUrl + "/docs/user-guide/rule-engine-2-0/action-nodes/#rpc-call-reply-node", ruleNodeRpcCallReply: helpBaseUrl + "/docs/user-guide/rule-engine-2-0/action-nodes/#rpc-call-reply-node",

148
ui/src/app/locale/locale.constant-it.js

@ -278,7 +278,7 @@ export default function addLocaleItalian(locales) {
"prev-widget": "Widget precedente", "prev-widget": "Widget precedente",
"add-to-dashboard": "Aggiungi alla dashboard", "add-to-dashboard": "Aggiungi alla dashboard",
"add-widget-to-dashboard": "Aggiungi widget alla dashboard", "add-widget-to-dashboard": "Aggiungi widget alla dashboard",
"selected-attributes": "{ count, select, 1 {1 attributo selezionato} other {# attributi selezioati} }", "selected-attributes": "{ count, select, 1 {1 attributo selezionato} other {# attributi selezionati} }",
"selected-telemetry": "{ count, select, 1 {1 unità di telemetria selezionata} other {# unità di telemetria selezionate} }" "selected-telemetry": "{ count, select, 1 {1 unità di telemetria selezionata} other {# unità di telemetria selezionate} }"
}, },
"audit-log": { "audit-log": {
@ -412,7 +412,7 @@ export default function addLocaleItalian(locales) {
"assign-to-customers": "Assegna Dashboard ai Clienti", "assign-to-customers": "Assegna Dashboard ai Clienti",
"assign-to-customers-text": "Seleziona i clienti da assegnare alla/alle dashboard", "assign-to-customers-text": "Seleziona i clienti da assegnare alla/alle dashboard",
"unassign-from-customers": "Unassign Dashboard(s) From Customers", "unassign-from-customers": "Unassign Dashboard(s) From Customers",
"unassign-from-customers-text": "Please select the customers to unassign from the dashboard(s)", "unassign-from-customers-text": "Seleziona i clienti di cui annullare l'assegnazione alla/alle dashboard",
"no-dashboards-text": "Nessuna dashboard trovata", "no-dashboards-text": "Nessuna dashboard trovata",
"no-widgets": "Nessun widget configurato", "no-widgets": "Nessun widget configurato",
"add-widget": "Aggiungi nuovo widget", "add-widget": "Aggiungi nuovo widget",
@ -428,10 +428,10 @@ export default function addLocaleItalian(locales) {
"assign-dashboards": "Assegna dashboard", "assign-dashboards": "Assegna dashboard",
"assign-new-dashboard": "Assegna nuova dashboard", "assign-new-dashboard": "Assegna nuova dashboard",
"assign-dashboards-text": "Assegna { count, select, 1 {1 dashboard} other {# dashboard} } ai clienti", "assign-dashboards-text": "Assegna { count, select, 1 {1 dashboard} other {# dashboard} } ai clienti",
"unassign-dashboards-action-text": "Unassign { count, select, 1 {1 dashboard} other {# dashboards} } from customers", "unassign-dashboards-action-text": "Annulla assegnazione { count, select, 1 {1 dashboard} other {# dashboards} } ai clienti",
"delete-dashboards": "Elimina dashboard", "delete-dashboards": "Elimina dashboard",
"unassign-dashboards": "Annulla assegnazione dashboard", "unassign-dashboards": "Annulla assegnazione dashboard",
"unassign-dashboards-action-title": "Unassign { count, select, 1 {1 dashboard} other {# dashboards} } from customer", "unassign-dashboards-action-title": "Annulla assegnazione { count, select, 1 {1 dashboard} other {# dashboards} } al cliente",
"delete-dashboard-title": "Sei sicuro di voler cancellare la dashboard '{{dashboardTitle}}'?", "delete-dashboard-title": "Sei sicuro di voler cancellare la dashboard '{{dashboardTitle}}'?",
"delete-dashboard-text": "Attenzione, dopo la conferma la dashboard e tutti i suoi dati non saranno più recuperabili.", "delete-dashboard-text": "Attenzione, dopo la conferma la dashboard e tutti i suoi dati non saranno più recuperabili.",
"delete-dashboards-title": "Sei sicuro di voler eliminare { count, select, 1 {1 dashboard} other {# dashboard} }?", "delete-dashboards-title": "Sei sicuro di voler eliminare { count, select, 1 {1 dashboard} other {# dashboard} }?",
@ -466,17 +466,17 @@ export default function addLocaleItalian(locales) {
"settings": "Impostazioni", "settings": "Impostazioni",
"columns-count": "Numero colonne", "columns-count": "Numero colonne",
"columns-count-required": "Numero colonne obbligatorio.", "columns-count-required": "Numero colonne obbligatorio.",
"min-columns-count-message": "Only 10 minimum column count is allowed.", "min-columns-count-message": "Ammesso un numero minimo di colonne pari a 10.",
"max-columns-count-message": "Only 1000 maximum column count is allowed.", "max-columns-count-message": "Ammesso un numero massimo di colonne pari a 1000.",
"widgets-margins": "Margine tra i widget", "widgets-margins": "Margine tra i widget",
"horizontal-margin": "Margine orizzontale", "horizontal-margin": "Margine orizzontale",
"horizontal-margin-required": "Margine orizzontale obbligatorio.", "horizontal-margin-required": "Margine orizzontale obbligatorio.",
"min-horizontal-margin-message": "Only 0 is allowed as minimum horizontal margin value.", "min-horizontal-margin-message": "Ammesso un margine orizzontale minimo pari a 0.",
"max-horizontal-margin-message": "Only 50 is allowed as maximum horizontal margin value.", "max-horizontal-margin-message": "Ammesso un margine orizzontale massimo pari a 50.",
"vertical-margin": "Margine verticale", "vertical-margin": "Margine verticale",
"vertical-margin-required": "Margine verticale obbligatorio.", "vertical-margin-required": "Margine verticale obbligatorio.",
"min-vertical-margin-message": "Only 0 is allowed as minimum vertical margin value.", "min-vertical-margin-message": "Ammesso un margine verticale minimo pari a 0.",
"max-vertical-margin-message": "Only 50 is allowed as maximum vertical margin value.", "max-vertical-margin-message": "Ammesso un margine verticale massimo pari a 50.",
"autofill-height": "Auto fill layout height", "autofill-height": "Auto fill layout height",
"mobile-layout": "Impostazioni layout mobile", "mobile-layout": "Impostazioni layout mobile",
"mobile-row-height": "Mobile row height, px", "mobile-row-height": "Mobile row height, px",
@ -484,12 +484,12 @@ export default function addLocaleItalian(locales) {
"min-mobile-row-height-message": "Only 5 pixels is allowed as minimum mobile row height value.", "min-mobile-row-height-message": "Only 5 pixels is allowed as minimum mobile row height value.",
"max-mobile-row-height-message": "Only 200 pixels is allowed as maximum mobile row height value.", "max-mobile-row-height-message": "Only 200 pixels is allowed as maximum mobile row height value.",
"display-title": "Mostra titolo dashboard", "display-title": "Mostra titolo dashboard",
"toolbar-always-open": "Keep toolbar opened", "toolbar-always-open": "Mantieni aperta la barra degli strumenti",
"title-color": "Colore titolo", "title-color": "Colore titolo",
"display-dashboards-selection": "Mostra selezione dashboard", "display-dashboards-selection": "Mostra selezione dashboard",
"display-entities-selection": "Display entities selection", "display-entities-selection": "Mostra selezione entità",
"display-dashboard-timewindow": "Display timewindow", "display-dashboard-timewindow": "Display timewindow",
"display-dashboard-export": "Display export", "display-dashboard-export": "Mostra esportazione",
"import": "Importa dashboard", "import": "Importa dashboard",
"export": "Esporta dashboard", "export": "Esporta dashboard",
"export-failed-error": "Impossibile esportare la dashboard: {{error}}", "export-failed-error": "Impossibile esportare la dashboard: {{error}}",
@ -576,7 +576,7 @@ export default function addLocaleItalian(locales) {
"aliases": "Alias dispositivo", "aliases": "Alias dispositivo",
"no-alias-matching": "'{{alias}}' non trovato.", "no-alias-matching": "'{{alias}}' non trovato.",
"no-aliases-found": "Nessun alias trovato.", "no-aliases-found": "Nessun alias trovato.",
"no-key-matching": "'{{key}}' non trovato.", "no-key-matching": "'{{key}}' non trovata.",
"no-keys-found": "Nessuna chiave trovata.", "no-keys-found": "Nessuna chiave trovata.",
"create-new-alias": "Create a new one!", "create-new-alias": "Create a new one!",
"create-new-key": "Create a new one!", "create-new-key": "Create a new one!",
@ -609,14 +609,14 @@ export default function addLocaleItalian(locales) {
"assign-devices": "Assegna dispositivi", "assign-devices": "Assegna dispositivi",
"assign-devices-text": "Assegna { count, select, 1 {1 dispositivo} other {# dispositivi} } al cliente", "assign-devices-text": "Assegna { count, select, 1 {1 dispositivo} other {# dispositivi} } al cliente",
"delete-devices": "Elimina dispositivi", "delete-devices": "Elimina dispositivi",
"unassign-from-customer": "Unassign from customer", "unassign-from-customer": "Annulla assegnazione al cliente",
"unassign-devices": "Annulla assegnazione dispositivi", "unassign-devices": "Annulla assegnazione dispositivi",
"unassign-devices-action-title": "Unassign { count, select, 1 {1 device} other {# devices} } from customer", "unassign-devices-action-title": "Annulla assegnazione { count, select, 1 {1 dispositivo} other {# dispositivi} } al cliente",
"assign-new-device": "Assegna nuovo dispositivo", "assign-new-device": "Assegna nuovo dispositivo",
"make-public-device-title": "Sei sicuro di voler rendere pubblico il dispositivo '{{deviceName}}'?", "make-public-device-title": "Sei sicuro di voler rendere pubblico il dispositivo '{{deviceName}}'?",
"make-public-device-text": "Dopo la conferma il dispositivo e tutti i suoi dati saranno resi pubblici e accessibili dagli altri.", "make-public-device-text": "Dopo la conferma il dispositivo e tutti i suoi dati saranno resi pubblici e accessibili dagli altri.",
"make-private-device-title": "Sei sicuro di voler rendere privato il dispositivo '{{deviceName}}'?", "make-private-device-title": "Sei sicuro di voler rendere privato il dispositivo '{{deviceName}}'?",
"make-private-device-text": "After the confirmation the device and all its data will be made private and won't be accessible by others.", "make-private-device-text": "Dopo la conferma il dispositivo e tutti i suoi dati saranno resi privati e non più accessibili da altri utenti.",
"view-credentials": "Visualizza credenziali", "view-credentials": "Visualizza credenziali",
"delete-device-title": "Sei sicuro di voler eliminare il dispositivo '{{deviceName}}'?", "delete-device-title": "Sei sicuro di voler eliminare il dispositivo '{{deviceName}}'?",
"delete-device-text": "Attenzione, dopo la conferma il dispositivo e tutti i suoi dati non saranno più recuperabili.", "delete-device-text": "Attenzione, dopo la conferma il dispositivo e tutti i suoi dati non saranno più recuperabili.",
@ -632,11 +632,11 @@ export default function addLocaleItalian(locales) {
"credentials-type": "Tipo credenziali", "credentials-type": "Tipo credenziali",
"access-token": "Token di accesso", "access-token": "Token di accesso",
"access-token-required": "Token di accesso obbligatorio.", "access-token-required": "Token di accesso obbligatorio.",
"access-token-invalid": "Access token length must be from 1 to 20 characters.", "access-token-invalid": "Il token di accesso deve avere una lunghezza compresa tra 1 e 20 caratteri.",
"rsa-key": "Chiave pubblica RSA", "rsa-key": "Chiave pubblica RSA",
"rsa-key-required": "Chiave pubblica RSA obbligatoria.", "rsa-key-required": "Chiave pubblica RSA obbligatoria.",
"secret": "Secret", "secret": "Secret",
"secret-required": "Secret is required.", "secret-required": "Secret obbligatorio.",
"device-type": "Tipo dispositivo", "device-type": "Tipo dispositivo",
"device-type-required": "Tipo dispositivo obbligatorio.", "device-type-required": "Tipo dispositivo obbligatorio.",
"select-device-type": "Seleziona tipo dispositivo", "select-device-type": "Seleziona tipo dispositivo",
@ -657,7 +657,7 @@ export default function addLocaleItalian(locales) {
"assignedToCustomer": "Assegnato al cliente", "assignedToCustomer": "Assegnato al cliente",
"unable-delete-device-alias-title": "Impossibile rimuovere l'alias del dispositivo", "unable-delete-device-alias-title": "Impossibile rimuovere l'alias del dispositivo",
"unable-delete-device-alias-text": "L'alias del dispositivo '{{deviceAlias}}' non può essere eliminato perchè utilizzato dai seguenti widget:<br/>{{widgetsList}}", "unable-delete-device-alias-text": "L'alias del dispositivo '{{deviceAlias}}' non può essere eliminato perchè utilizzato dai seguenti widget:<br/>{{widgetsList}}",
"is-gateway": "Is gateway", "is-gateway": "E' un gateway",
"public": "Pubblico", "public": "Pubblico",
"device-public": "Il dispositivo è pubblico", "device-public": "Il dispositivo è pubblico",
"select-device": "Seleziona dispositivo" "select-device": "Seleziona dispositivo"
@ -667,7 +667,7 @@ export default function addLocaleItalian(locales) {
}, },
"error": { "error": {
"unable-to-connect": "Impossibile connettersi al server! Controlla la connessione ad Internet.", "unable-to-connect": "Impossibile connettersi al server! Controlla la connessione ad Internet.",
"unhandled-error-code": "Unhandled error code: {{errorCode}}", "unhandled-error-code": "Codice errore non gestito: {{errorCode}}",
"unknown-error": "Errore sconosciuto" "unknown-error": "Errore sconosciuto"
}, },
"entity": { "entity": {
@ -696,7 +696,7 @@ export default function addLocaleItalian(locales) {
"use-entity-name-filter": "Usa filtro", "use-entity-name-filter": "Usa filtro",
"entity-list-empty": "Nessuna entità selezionata.", "entity-list-empty": "Nessuna entità selezionata.",
"entity-type-list-empty": "Nessun tipo di entità selezionato.", "entity-type-list-empty": "Nessun tipo di entità selezionato.",
"entity-name-filter-required": "Entity name filter is required.", "entity-name-filter-required": "Filtro nome entità obbligatorio.",
"entity-name-filter-no-entity-matched": "No entities starting with '{{entity}}' were found.", "entity-name-filter-no-entity-matched": "No entities starting with '{{entity}}' were found.",
"all-subtypes": "Tutte", "all-subtypes": "Tutte",
"select-entities": "Seleziona entità", "select-entities": "Seleziona entità",
@ -768,7 +768,7 @@ export default function addLocaleItalian(locales) {
"no-events-prompt": "Nessun evento trovato", "no-events-prompt": "Nessun evento trovato",
"error": "Errore", "error": "Errore",
"alarm": "Allarme", "alarm": "Allarme",
"event-time": "Event time", "event-time": "Orario evento",
"server": "Server", "server": "Server",
"body": "Body", "body": "Body",
"method": "Metodo", "method": "Metodo",
@ -797,7 +797,7 @@ export default function addLocaleItalian(locales) {
"extension-id": "Id Estensione", "extension-id": "Id Estensione",
"extension-type": "Tipo Estensione", "extension-type": "Tipo Estensione",
"transformer-json": "JSON *", "transformer-json": "JSON *",
"unique-id-required": "Current extension id already exists.", "unique-id-required": "Id estensione corrente già esistente.",
"delete": "Elimina estensione", "delete": "Elimina estensione",
"add": "Aggiungi estensione", "add": "Aggiungi estensione",
"edit": "Modifica estensione", "edit": "Modifica estensione",
@ -842,9 +842,9 @@ export default function addLocaleItalian(locales) {
"private-key": "File chiave privata *", "private-key": "File chiave privata *",
"cert": "File certificato *", "cert": "File certificato *",
"no-file": "Nessun file selezionato.", "no-file": "Nessun file selezionato.",
"drop-file": "Drop a file or click to select a file to upload.", "drop-file": "Trascina un file o fai clic per selezionare un file da caricare.",
"mapping": "Mapping", "mapping": "Mapping",
"topic-filter": "Topic filter", "topic-filter": "Filtro topic",
"converter-type": "Converter type", "converter-type": "Converter type",
"converter-json": "Json", "converter-json": "Json",
"json-name-expression": "Device name json expression", "json-name-expression": "Device name json expression",
@ -896,7 +896,7 @@ export default function addLocaleItalian(locales) {
"opc-keystore-location": "Location *", "opc-keystore-location": "Location *",
"opc-keystore-password": "Password", "opc-keystore-password": "Password",
"opc-keystore-alias": "Alias", "opc-keystore-alias": "Alias",
"opc-keystore-key-password": "Key password", "opc-keystore-key-password": "Chiave password",
"opc-device-node-pattern": "Device node pattern", "opc-device-node-pattern": "Device node pattern",
"opc-device-name-pattern": "Device name pattern", "opc-device-name-pattern": "Device name pattern",
"modbus-server": "Servers/slaves", "modbus-server": "Servers/slaves",
@ -917,11 +917,11 @@ export default function addLocaleItalian(locales) {
"modbus-poll-period": "Intervallo di polling (ms)", "modbus-poll-period": "Intervallo di polling (ms)",
"modbus-attributes-poll-period": "Attributes poll period (ms)", "modbus-attributes-poll-period": "Attributes poll period (ms)",
"modbus-timeseries-poll-period": "Timeseries poll period (ms)", "modbus-timeseries-poll-period": "Timeseries poll period (ms)",
"modbus-poll-period-range": "Poll period should be positive value.", "modbus-poll-period-range": "L'intervallo di polling deve essere un valore positivo.",
"modbus-tag": "Tag", "modbus-tag": "Tag",
"modbus-function": "Funzione", "modbus-function": "Funzione",
"modbus-register-address": "Indirizzo registro", "modbus-register-address": "Indirizzo registro",
"modbus-register-address-range": "Register address should be in a range from 0 to 65535.", "modbus-register-address-range": "L'indirizzo del registro deve essere compreso tra 0 e 65535.",
"modbus-register-bit-index": "Bit index", "modbus-register-bit-index": "Bit index",
"modbus-register-bit-index-range": "Bit index should be in a range from 0 to 15.", "modbus-register-bit-index-range": "Bit index should be in a range from 0 to 15.",
"modbus-register-count": "Register count", "modbus-register-count": "Register count",
@ -929,11 +929,11 @@ export default function addLocaleItalian(locales) {
"modbus-byte-order": "Byte order", "modbus-byte-order": "Byte order",
"sync": { "sync": {
"status": "Status", "status": "Stato",
"sync": "Sync", "sync": "Sincronizzato",
"not-sync": "Not sync", "not-sync": "Non sincronizzato",
"last-sync-time": "Last sync time", "last-sync-time": "Ultima sincronizzazione",
"not-available": "Not available" "not-available": "Non disponibile"
}, },
"export-extensions-configuration": "Export extensions configuration", "export-extensions-configuration": "Export extensions configuration",
@ -1031,7 +1031,7 @@ export default function addLocaleItalian(locales) {
"password-reset": "Password reset", "password-reset": "Password reset",
"new-password": "Nuova password", "new-password": "Nuova password",
"new-password-again": "Ripeti nuova password", "new-password-again": "Ripeti nuova password",
"password-link-sent-message": "Password reset link was successfully sent!", "password-link-sent-message": "Link azzeramento password inviato con successo!",
"email": "Email" "email": "Email"
}, },
"position": { "position": {
@ -1062,18 +1062,18 @@ export default function addLocaleItalian(locales) {
"type": "Tipo", "type": "Tipo",
"to-entity-type": "A tipo entità", "to-entity-type": "A tipo entità",
"to-entity-name": "A nome entità", "to-entity-name": "A nome entità",
"from-entity-type": "From entity type", "from-entity-type": "Da tipo entità",
"from-entity-name": "From entity name", "from-entity-name": "Da nome entità",
"to-entity": "To entity", "to-entity": "A entità",
"from-entity": "From entity", "from-entity": "Da entità",
"delete": "Delete relation", "delete": "Delete relation",
"relation-type": "Relation type", "relation-type": "Relation type",
"relation-type-required": "Relation type is required.", "relation-type-required": "Relation type is required.",
"any-relation-type": "Any type", "any-relation-type": "Ogni tipo",
"add": "Add relation", "add": "Add relation",
"edit": "Edit relation", "edit": "Edit relation",
"delete-to-relation-title": "Are you sure you want to delete relation to the entity '{{entityName}}'?", "delete-to-relation-title": "Are you sure you want to delete relation to the entity '{{entityName}}'?",
"delete-to-relation-text": "Be careful, after the confirmation the entity '{{entityName}}' will be unrelated from the current entity.", "delete-to-relation-text": "Attenzione, dopo la conferma l'entità '{{entityName}}' sarà scollegata dall'entità corrente.",
"delete-to-relations-title": "Are you sure you want to delete { count, select, 1 {1 relation} other {# relations} }?", "delete-to-relations-title": "Are you sure you want to delete { count, select, 1 {1 relation} other {# relations} }?",
"delete-to-relations-text": "Be careful, after the confirmation all selected relations will be removed and corresponding entities will be unrelated from the current entity.", "delete-to-relations-text": "Be careful, after the confirmation all selected relations will be removed and corresponding entities will be unrelated from the current entity.",
"delete-from-relation-title": "Are you sure you want to delete relation from the entity '{{entityName}}'?", "delete-from-relation-title": "Are you sure you want to delete relation from the entity '{{entityName}}'?",
@ -1128,7 +1128,7 @@ export default function addLocaleItalian(locales) {
"details": "Dettagli", "details": "Dettagli",
"events": "Eventi", "events": "Eventi",
"search": "Ricerca nodi", "search": "Ricerca nodi",
"open-node-library": "Open node library", "open-node-library": "Apri libreria nodi",
"add": "Add rule node", "add": "Add rule node",
"name": "Nome", "name": "Nome",
"name-required": "Nome obbligatorio.", "name-required": "Nome obbligatorio.",
@ -1189,7 +1189,7 @@ export default function addLocaleItalian(locales) {
"delete": "Cancella tenant", "delete": "Cancella tenant",
"add-tenant-text": "Aggiungi nuovo tenant", "add-tenant-text": "Aggiungi nuovo tenant",
"no-tenants-text": "Nessun tenant trovato", "no-tenants-text": "Nessun tenant trovato",
"tenant-details": "Dettagli tenenat", "tenant-details": "Dettagli tenant",
"delete-tenant-title": "Sei sicuro di voler eliminare il tenant '{{tenantTitle}}'?", "delete-tenant-title": "Sei sicuro di voler eliminare il tenant '{{tenantTitle}}'?",
"delete-tenant-text": "Attenzione, dopo la conferma il tenant e tutti i suoi dati non saranno più recuperabili.", "delete-tenant-text": "Attenzione, dopo la conferma il tenant e tutti i suoi dati non saranno più recuperabili.",
"delete-tenants-title": "Sei sicuro di voler eliminare { count, select, 1 {1 tenant} other {# tenant} }?", "delete-tenants-title": "Sei sicuro di voler eliminare { count, select, 1 {1 tenant} other {# tenant} }?",
@ -1246,8 +1246,8 @@ export default function addLocaleItalian(locales) {
"no-users-text": "Nessun utente trovato", "no-users-text": "Nessun utente trovato",
"user-details": "Dettagli utente", "user-details": "Dettagli utente",
"delete-user-title": "Sei sicuro di voler eliminare l'utente '{{userEmail}}'?", "delete-user-title": "Sei sicuro di voler eliminare l'utente '{{userEmail}}'?",
"delete-user-text": "Be careful, after the confirmation the user and all related data will become unrecoverable.", "delete-user-text": "Attenzione, dopo la conferma l'utente e tutti i suoi dati non saranno più recuperabili.",
"delete-users-title": "Sei sicuro di voler cancellare { count, select, 1 {1 utente} other {# utenti} }?", "delete-users-title": "Sei sicuro di voler eliminare { count, select, 1 {1 utente} other {# utenti} }?",
"delete-users-action-title": "Elimina { count, select, 1 {1 utente} other {# utenti} }", "delete-users-action-title": "Elimina { count, select, 1 {1 utente} other {# utenti} }",
"delete-users-text": "Attenzione, dopo la conferma tutti gli utenti selezionati saranno eliminati e tutti i relativi dati non saranno più recuperabili.", "delete-users-text": "Attenzione, dopo la conferma tutti gli utenti selezionati saranno eliminati e tutti i relativi dati non saranno più recuperabili.",
"activation-email-sent-message": "Email di attivazione inviata con successo!", "activation-email-sent-message": "Email di attivazione inviata con successo!",
@ -1289,12 +1289,12 @@ export default function addLocaleItalian(locales) {
}, },
"widget": { "widget": {
"widget-library": "Libreria Widget", "widget-library": "Libreria Widget",
"widget-bundle": "Widgets Bundle", "widget-bundle": "Bundle widget",
"select-widgets-bundle": "Select widgets bundle", "select-widgets-bundle": "Seleziona bundle widget",
"management": "Gestione widget", "management": "Gestione widget",
"editor": "Editor Widget", "editor": "Editor Widget",
"widget-type-not-found": "Problem loading widget configuration.<br>Probably associated\n widget type was removed.", "widget-type-not-found": "Problem loading widget configuration.<br>Probably associated\n widget type was removed.",
"widget-type-load-error": "Widget wasn't loaded due to the following errors:", "widget-type-load-error": "Widget non caricato a causa dei seguenti errori:",
"remove": "Elimina widget", "remove": "Elimina widget",
"edit": "Modifica widget", "edit": "Modifica widget",
"remove-widget-title": "sei sicuro di voler eliminare il widget '{{widgetTitle}}'?", "remove-widget-title": "sei sicuro di voler eliminare il widget '{{widgetTitle}}'?",
@ -1329,12 +1329,12 @@ export default function addLocaleItalian(locales) {
"settings-schema": "Impostazioni schema", "settings-schema": "Impostazioni schema",
"datakey-settings-schema": "Data key settings schema", "datakey-settings-schema": "Data key settings schema",
"javascript": "Javascript", "javascript": "Javascript",
"remove-widget-type-title": "Are you sure you want to remove the widget type '{{widgetName}}'?", "remove-widget-type-title": "Sei sicuro di voler rimuovere il tipo di widget '{{widgetName}}'?",
"remove-widget-type-text": "After the confirmation the widget type and all related data will become unrecoverable.", "remove-widget-type-text": "Dopo la conferma il tipo di widget e tutti i suoi dati non saranno più recuperabili.",
"remove-widget-type": "Rimuovi tipo widget", "remove-widget-type": "Rimuovi tipo widget",
"add-widget-type": "Aggiungi nuovo tipo widget", "add-widget-type": "Aggiungi nuovo tipo widget",
"widget-type-load-failed-error": "Failed to load widget type!", "widget-type-load-failed-error": "Caricamento tipo widget fallito!",
"widget-template-load-failed-error": "Failed to load widget template!", "widget-template-load-failed-error": "Caricamento template widget fallito!",
"add": "Aggiungi Widget", "add": "Aggiungi Widget",
"undo": "Annulla modifiche widget", "undo": "Annulla modifiche widget",
"export": "Esporta widget" "export": "Esporta widget"
@ -1352,31 +1352,31 @@ export default function addLocaleItalian(locales) {
"open-right-layout": "Open right dashboard layout (mobile view)" "open-right-layout": "Open right dashboard layout (mobile view)"
}, },
"widgets-bundle": { "widgets-bundle": {
"current": "Current bundle", "current": "Bundle corrente",
"widgets-bundles": "Widgets Bundles", "widgets-bundles": "Bundle Widget",
"add": "Add Widgets Bundle", "add": "Aggiungi Bundle Widget",
"delete": "Delete widgets bundle", "delete": "Cancella bundle widget",
"title": "Titolo", "title": "Titolo",
"title-required": "Titolo obbligatorio.", "title-required": "Titolo obbligatorio.",
"add-widgets-bundle-text": "Add new widgets bundle", "add-widgets-bundle-text": "Aggiungi nuovo bundle widget",
"no-widgets-bundles-text": "No widgets bundles found", "no-widgets-bundles-text": "Nessun bundle widget trovato",
"empty": "Widgets bundle is empty", "empty": "Bundle widget vuoto",
"details": "Details", "details": "Dettagli",
"widgets-bundle-details": "Widgets bundle details", "widgets-bundle-details": "Dettagli bundle widget",
"delete-widgets-bundle-title": "Are you sure you want to delete the widgets bundle '{{widgetsBundleTitle}}'?", "delete-widgets-bundle-title": "Sei sicuro di voler eliminare il bundle widget '{{widgetsBundleTitle}}'?",
"delete-widgets-bundle-text": "Be careful, after the confirmation the widgets bundle and all related data will become unrecoverable.", "delete-widgets-bundle-text": "Attenzione, dopo la conferma il bundle widget e tutti i suoi dati non saranno più recuperabili.",
"delete-widgets-bundles-title": "Are you sure you want to delete { count, select, 1 {1 widgets bundle} other {# widgets bundles} }?", "delete-widgets-bundles-title": "Sei sicuro di voler eliminare { count, select, 1 {1 bundle widget} other {# bundle widget} }?",
"delete-widgets-bundles-action-title": "Delete { count, select, 1 {1 widgets bundle} other {# widgets bundles} }", "delete-widgets-bundles-action-title": "Elimina { count, select, 1 {1 bundle widget} other {# bundle widget} }",
"delete-widgets-bundles-text": "Be careful, after the confirmation all selected widgets bundles will be removed and all related data will become unrecoverable.", "delete-widgets-bundles-text": "Attenzione, dopo la conferma tutti i bundle widget selezionati saranno rimossi e tutti i loro dati non saranno più recuperabili.",
"no-widgets-bundles-matching": "No widgets bundles matching '{{widgetsBundle}}' were found.", "no-widgets-bundles-matching": "Nessun bundle widget corrispondente a '{{widgetsBundle}}' è stato trovato.",
"widgets-bundle-required": "Widgets bundle is required.", "widgets-bundle-required": "Bundle widget obbligatorio.",
"system": "Sistema", "system": "Sistema",
"import": "Import widgets bundle", "import": "Importa bundle widget",
"export": "Export widgets bundle", "export": "Esporta bundle widget",
"export-failed-error": "Unable to export widgets bundle: {{error}}", "export-failed-error": "Impossibile esportare bundle widget: {{error}}",
"create-new-widgets-bundle": "Create new widgets bundle", "create-new-widgets-bundle": "Crea nuovo bundle widget",
"widgets-bundle-file": "Widgets bundle file", "widgets-bundle-file": "File bundle widget",
"invalid-widgets-bundle-file-error": "Unable to import widgets bundle: Invalid widgets bundle data structure." "invalid-widgets-bundle-file-error": "Impossibile importare bundle widget: struttura dati non valida."
}, },
"widget-config": { "widget-config": {
"data": "Dati", "data": "Dati",

5
ui/src/app/locale/locale.constant.js

@ -755,6 +755,10 @@ export default angular.module('thingsboard.locale', [])
"type-rulechains": "Rule chains", "type-rulechains": "Rule chains",
"list-of-rulechains": "{ count, select, 1 {One rule chain} other {List of # rule chains} }", "list-of-rulechains": "{ count, select, 1 {One rule chain} other {List of # rule chains} }",
"rulechain-name-starts-with": "Rule chains whose names start with '{{prefix}}'", "rulechain-name-starts-with": "Rule chains whose names start with '{{prefix}}'",
"type-rulenode": "Rule node",
"type-rulenodes": "Rule nodes",
"list-of-rulenodes": "{ count, select, 1 {One rule node} other {List of # rule nodes} }",
"rulenode-name-starts-with": "Rule nodes whose names start with '{{prefix}}'",
"type-current-customer": "Current Customer", "type-current-customer": "Current Customer",
"search": "Search entities", "search": "Search entities",
"selected-entities": "{ count, select, 1 {1 entity} other {# entities} } selected", "selected-entities": "{ count, select, 1 {1 entity} other {# entities} } selected",
@ -1184,6 +1188,7 @@ export default angular.module('thingsboard.locale', [])
"test-script-function": "Test script function", "test-script-function": "Test script function",
"message": "Message", "message": "Message",
"message-type": "Message type", "message-type": "Message type",
"select-message-type": "Select message type",
"message-type-required": "Message type is required", "message-type-required": "Message type is required",
"metadata": "Metadata", "metadata": "Metadata",
"metadata-required": "Metadata entries can't be empty.", "metadata-required": "Metadata entries can't be empty.",

2
ui/src/app/rulechain/index.js

@ -23,6 +23,7 @@ import RuleNodeDefinedConfigDirective from './rulenode-defined-config.directive'
import RuleNodeConfigDirective from './rulenode-config.directive'; import RuleNodeConfigDirective from './rulenode-config.directive';
import RuleNodeDirective from './rulenode.directive'; import RuleNodeDirective from './rulenode.directive';
import LinkDirective from './link.directive'; import LinkDirective from './link.directive';
import MessageTypeAutocompleteDirective from './message-type-autocomplete.directive';
import NodeScriptTest from './script/node-script-test.service'; import NodeScriptTest from './script/node-script-test.service';
export default angular.module('thingsboard.ruleChain', []) export default angular.module('thingsboard.ruleChain', [])
@ -37,5 +38,6 @@ export default angular.module('thingsboard.ruleChain', [])
.directive('tbRuleNodeConfig', RuleNodeConfigDirective) .directive('tbRuleNodeConfig', RuleNodeConfigDirective)
.directive('tbRuleNode', RuleNodeDirective) .directive('tbRuleNode', RuleNodeDirective)
.directive('tbRuleNodeLink', LinkDirective) .directive('tbRuleNodeLink', LinkDirective)
.directive('tbMessageTypeAutocomplete', MessageTypeAutocompleteDirective)
.factory('ruleNodeScriptTest', NodeScriptTest) .factory('ruleNodeScriptTest', NodeScriptTest)
.name; .name;

100
ui/src/app/rulechain/message-type-autocomplete.directive.js

@ -0,0 +1,100 @@
/*
* Copyright © 2016-2018 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 './message-type-autocomplete.scss';
/* eslint-disable import/no-unresolved, import/default */
import messageTypeAutocompleteTemplate from './message-type-autocomplete.tpl.html';
/* eslint-enable import/no-unresolved, import/default */
/*@ngInject*/
export default function MessageTypeAutocomplete($compile, $templateCache, $q, $filter, types) {
var linker = function (scope, element, attrs, ngModelCtrl) {
var template = $templateCache.get(messageTypeAutocompleteTemplate);
element.html(template);
var messageTypeList = [];
for (var t in types.messageType) {
var type = types.messageType[t];
messageTypeList.push(type);
}
scope.messageType = null;
scope.messageTypeSearchText = '';
scope.fetchMessageTypes = function(searchText) {
var deferred = $q.defer();
var result = $filter('filter')(messageTypeList, {'name': searchText});
if (result && result.length) {
deferred.resolve(result);
} else {
deferred.resolve([{name: searchText, value: searchText}]);
}
return deferred.promise;
};
scope.messageTypeSearchTextChanged = function() {
};
scope.updateView = function () {
if (!scope.disabled) {
var value = null;
if (scope.messageType) {
value = scope.messageType.value;
}
ngModelCtrl.$setViewValue(value);
}
};
ngModelCtrl.$render = function () {
var value = ngModelCtrl.$viewValue;
if (value) {
var result = $filter('filter')(messageTypeList, {'value': value}, true);
if (result && result.length) {
scope.messageType = result[0];
} else {
scope.messageType = {
name: value,
value: value
};
}
} else {
scope.messageType = null;
}
};
scope.$watch('messageType', function (newValue, prevValue) {
if (!angular.equals(newValue, prevValue)) {
scope.updateView();
}
});
$compile(element.contents())(scope);
}
return {
restrict: "E",
require: "^ngModel",
link: linker,
scope: {
theForm: '=?',
disabled:'=ngDisabled',
required:'=ngRequired'
}
};
}

25
ui/src/app/rulechain/message-type-autocomplete.scss

@ -0,0 +1,25 @@
/**
* Copyright © 2016-2018 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.
*/
.tb-message-type-autocomplete {
.tb-message-type-item {
display: block;
height: 48px;
}
li {
height: auto !important;
white-space: normal !important;
}
}

41
ui/src/app/rulechain/message-type-autocomplete.tpl.html

@ -0,0 +1,41 @@
<!--
Copyright © 2016-2018 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-autocomplete ng-required="required"
ng-disabled="disabled"
md-no-cache="true"
md-input-name="messageType"
ng-model="messageType"
md-selected-item="messageType"
md-search-text="messageTypeSearchText"
md-search-text-change="messageTypeSearchTextChanged()"
md-items="item in fetchMessageTypes(messageTypeSearchText)"
md-item-text="item.name"
md-min-length="0"
placeholder="{{ 'rulenode.select-message-type' | translate }}"
md-floating-label="{{ 'rulenode.message-type' | translate }}"
md-select-on-match="false"
md-menu-class="tb-message-type-autocomplete">
<md-item-template>
<div class="tb-message-type-item">
<span md-highlight-text="messageTypeSearchText" md-highlight-flags="^i">{{item.name}}</span>
</div>
</md-item-template>
<div ng-messages="theForm.messageType.$error">
<div translate ng-message="required">rulenode.message-type-required</div>
</div>
</md-autocomplete>

17
ui/src/app/rulechain/script/node-script-test.tpl.html

@ -38,13 +38,12 @@
<ng-form name="payloadForm"> <ng-form name="payloadForm">
<div layout="column" style="height: 100%;"> <div layout="column" style="height: 100%;">
<div layout="row"> <div layout="row">
<md-input-container class="md-block" style="margin-bottom: 0px; min-width: 300px;"> <tb-message-type-autocomplete
<label translate>rulenode.message-type</label> style="margin-bottom: 0px; min-width: 300px;"
<input required name="msgType" ng-model="vm.inputParams.msgType"> ng-required="true"
<div ng-messages="payloadForm.msgType.$error"> ng-model="vm.inputParams.msgType"
<div translate ng-message="required">rulenode.message-type-required</div> the-form="payloadForm">
</div> </tb-message-type-autocomplete>
</md-input-container>
</div> </div>
<tb-json-content flex <tb-json-content flex
ng-model="vm.inputParams.msg" ng-model="vm.inputParams.msg"
@ -99,14 +98,14 @@
validate-content="false" validate-content="false"
ng-readonly="true" ng-readonly="true"
fill-height="true"> fill-height="true">
</tb-json-content> </tb-json-content>generateReport
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</md-dialog-content> </md-dialog-content>
<md-dialog-actions layout="row"> <md-dialog-actions layout="row">
<md-button ng-disabled="$root.loading" ng-click="vm.test()" class="md-raised md-primary"> <md-button ng-disabled="$root.loading || theForm.$invalid" ng-click="vm.test()" class="md-raised md-primary">
{{ 'rulenode.test' | translate }} {{ 'rulenode.test' | translate }}
</md-button> </md-button>
<span flex></span> <span flex></span>

11
ui/src/app/widget/widget-editor.controller.js

@ -20,12 +20,11 @@ import 'brace/mode/javascript';
import 'brace/mode/html'; import 'brace/mode/html';
import 'brace/mode/css'; import 'brace/mode/css';
import 'brace/mode/json'; import 'brace/mode/json';
import 'ace-builds/src-min-noconflict/ace'; import 'brace/snippets/javascript';
import 'ace-builds/src-min-noconflict/snippets/javascript'; import 'brace/snippets/text';
import 'ace-builds/src-min-noconflict/snippets/text'; import 'brace/snippets/html';
import 'ace-builds/src-min-noconflict/snippets/html'; import 'brace/snippets/css';
import 'ace-builds/src-min-noconflict/snippets/css'; import 'brace/snippets/json';
import 'ace-builds/src-min-noconflict/snippets/json';
/* eslint-disable import/no-unresolved, import/default */ /* eslint-disable import/no-unresolved, import/default */

8
ui/webpack.config.prod.js

@ -18,6 +18,7 @@
const HtmlWebpackPlugin = require('html-webpack-plugin'); const HtmlWebpackPlugin = require('html-webpack-plugin');
const ExtractTextPlugin = require('extract-text-webpack-plugin'); const ExtractTextPlugin = require('extract-text-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin'); const CopyWebpackPlugin = require('copy-webpack-plugin');
const CompressionPlugin = require('compression-webpack-plugin');
const webpack = require('webpack'); const webpack = require('webpack');
const path = require('path'); const path = require('path');
@ -64,6 +65,13 @@ module.exports = {
NODE_ENV: JSON.stringify('production'), NODE_ENV: JSON.stringify('production'),
}, },
}), }),
new CompressionPlugin({
asset: "[path].gz[query]",
algorithm: "gzip",
test: /\.js$|\.css$|\.svg$|\.ttf$|\.woff$|\.woff2|\.eot$/,
threshold: 10240,
minRatio: 0.8
})
], ],
node: { node: {
tls: "empty", tls: "empty",

Loading…
Cancel
Save