Browse Source

Merge remote-tracking branch 'upstream/master'

pull/585/head
volodymyr-babak 9 years ago
parent
commit
5f478d9e13
  1. 4
      application/pom.xml
  2. 4
      application/src/main/data/json/system/widget_bundles/cards.json
  3. 3
      application/src/main/java/org/thingsboard/server/actors/plugin/PluginProcessingContext.java
  4. 4
      application/src/main/java/org/thingsboard/server/config/WebSocketConfiguration.java
  5. 16
      application/src/main/java/org/thingsboard/server/controller/CustomerController.java
  6. 1
      application/src/main/java/org/thingsboard/server/controller/plugin/PluginWebSocketHandler.java
  7. 2
      application/src/main/java/org/thingsboard/server/service/mail/DefaultMailService.java
  8. 19
      application/src/main/java/org/thingsboard/server/service/security/model/token/JwtTokenFactory.java
  9. 20
      application/src/test/java/org/thingsboard/server/controller/AbstractControllerTest.java
  10. 86
      application/src/test/java/org/thingsboard/server/mqtt/rpc/AbstractMqttServerSideRpcIntegrationTest.java
  11. 8
      dao/src/main/java/org/thingsboard/server/dao/customer/CustomerService.java
  12. 8
      dao/src/main/java/org/thingsboard/server/dao/customer/CustomerServiceImpl.java
  13. 8
      pom.xml
  14. 41
      tools/src/main/java/org/thingsboard/client/tools/RestClient.java
  15. 4
      tools/src/main/shell/client.keygen.sh
  16. 2
      tools/src/main/shell/keygen.properties
  17. 8
      ui/src/app/admin/general-settings.tpl.html
  18. 14
      ui/src/app/admin/outgoing-mail-settings.tpl.html
  19. 6
      ui/src/app/alarm/alarm-details-dialog.tpl.html
  20. 6
      ui/src/app/alarm/alarm-table.tpl.html
  21. 4
      ui/src/app/api/asset.service.js
  22. 4
      ui/src/app/api/attribute.service.js
  23. 8
      ui/src/app/api/customer.service.js
  24. 16
      ui/src/app/api/dashboard.service.js
  25. 4
      ui/src/app/api/device.service.js
  26. 4
      ui/src/app/api/entity-relation.service.js
  27. 76
      ui/src/app/api/entity.service.js
  28. 24
      ui/src/app/api/plugin.service.js
  29. 12
      ui/src/app/api/rule.service.js
  30. 37
      ui/src/app/api/telemetry-websocket.service.js
  31. 8
      ui/src/app/api/tenant.service.js
  32. 8
      ui/src/app/api/user.service.js
  33. 16
      ui/src/app/api/widget.service.js
  34. 8
      ui/src/app/asset/add-asset.tpl.html
  35. 8
      ui/src/app/asset/add-assets-to-customer.tpl.html
  36. 4
      ui/src/app/asset/asset-fieldset.tpl.html
  37. 8
      ui/src/app/asset/assign-to-customer.tpl.html
  38. 35
      ui/src/app/common/utils.service.js
  39. 14
      ui/src/app/component/component-dialog.tpl.html
  40. 4
      ui/src/app/component/component.tpl.html
  41. 6
      ui/src/app/components/dashboard-autocomplete.directive.js
  42. 4
      ui/src/app/components/dashboard-select.directive.js
  43. 2
      ui/src/app/components/datakey-config-dialog.controller.js
  44. 8
      ui/src/app/components/datakey-config-dialog.tpl.html
  45. 4
      ui/src/app/components/details-sidenav.tpl.html
  46. 14
      ui/src/app/components/grid.tpl.html
  47. 2
      ui/src/app/components/legend-config-panel.tpl.html
  48. 8
      ui/src/app/components/material-icons-dialog.tpl.html
  49. 4
      ui/src/app/components/plugin-select.directive.js
  50. 6
      ui/src/app/components/timewindow-panel.tpl.html
  51. 12
      ui/src/app/components/widget/action/widget-action-dialog.tpl.html
  52. 4
      ui/src/app/components/widget/widget-config.tpl.html
  53. 9
      ui/src/app/components/widget/widget.controller.js
  54. 2
      ui/src/app/components/widgets-bundle-select.directive.js
  55. 8
      ui/src/app/customer/add-customer.tpl.html
  56. 2
      ui/src/app/customer/customer-fieldset.tpl.html
  57. 8
      ui/src/app/dashboard/add-dashboard.tpl.html
  58. 8
      ui/src/app/dashboard/add-dashboards-to-customer.tpl.html
  59. 2
      ui/src/app/dashboard/add-widget.controller.js
  60. 10
      ui/src/app/dashboard/add-widget.tpl.html
  61. 8
      ui/src/app/dashboard/assign-to-customer.tpl.html
  62. 2
      ui/src/app/dashboard/dashboard-fieldset.tpl.html
  63. 10
      ui/src/app/dashboard/dashboard-settings.tpl.html
  64. 16
      ui/src/app/dashboard/dashboard.tpl.html
  65. 2
      ui/src/app/dashboard/edit-widget.directive.js
  66. 2
      ui/src/app/dashboard/edit-widget.tpl.html
  67. 2
      ui/src/app/dashboard/layouts/dashboard-layout.tpl.html
  68. 10
      ui/src/app/dashboard/layouts/manage-dashboard-layouts.tpl.html
  69. 6
      ui/src/app/dashboard/layouts/select-target-layout.tpl.html
  70. 10
      ui/src/app/dashboard/states/dashboard-state-dialog.tpl.html
  71. 10
      ui/src/app/dashboard/states/manage-dashboard-states.tpl.html
  72. 10
      ui/src/app/dashboard/states/select-target-state.tpl.html
  73. 8
      ui/src/app/device/add-device.tpl.html
  74. 8
      ui/src/app/device/add-devices-to-customer.tpl.html
  75. 8
      ui/src/app/device/assign-to-customer.tpl.html
  76. 12
      ui/src/app/device/device-credentials.tpl.html
  77. 6
      ui/src/app/device/device-fieldset.tpl.html
  78. 10
      ui/src/app/entity/alias/entity-alias-dialog.tpl.html
  79. 16
      ui/src/app/entity/alias/entity-aliases.tpl.html
  80. 12
      ui/src/app/entity/attribute/add-attribute-dialog.tpl.html
  81. 12
      ui/src/app/entity/attribute/add-widget-to-dashboard-dialog.tpl.html
  82. 2
      ui/src/app/entity/attribute/attribute-table.scss
  83. 2
      ui/src/app/entity/attribute/attribute-table.tpl.html
  84. 2
      ui/src/app/entity/entity-autocomplete.directive.js
  85. 2
      ui/src/app/entity/entity-list.directive.js
  86. 4
      ui/src/app/entity/entity-subtype-autocomplete.directive.js
  87. 4
      ui/src/app/entity/entity-subtype-list.directive.js
  88. 4
      ui/src/app/entity/entity-subtype-select.directive.js
  89. 12
      ui/src/app/entity/relation/relation-dialog.tpl.html
  90. 4
      ui/src/app/entity/relation/relation-filters.tpl.html
  91. 2
      ui/src/app/entity/relation/relation-table.tpl.html
  92. 2
      ui/src/app/event/event-content-dialog.tpl.html
  93. 6
      ui/src/app/event/event-table.tpl.html
  94. 8
      ui/src/app/extension/extension-dialog.tpl.html
  95. 10
      ui/src/app/import-export/import-dialog.tpl.html
  96. 2
      ui/src/app/layout/home.tpl.html
  97. 1
      ui/src/app/locale/locale.constant.js
  98. 2
      ui/src/app/login/create-password.tpl.html
  99. 2
      ui/src/app/login/login.tpl.html
  100. 2
      ui/src/app/login/reset-password-request.tpl.html

4
application/pom.xml

@ -125,10 +125,6 @@
<groupId>io.jsonwebtoken</groupId> <groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId> <artifactId>jjwt</artifactId>
</dependency> </dependency>
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
</dependency>
<dependency> <dependency>
<groupId>org.apache.velocity</groupId> <groupId>org.apache.velocity</groupId>
<artifactId>velocity</artifactId> <artifactId>velocity</artifactId>

4
application/src/main/data/json/system/widget_bundles/cards.json

@ -112,9 +112,9 @@
"templateHtml": "<tb-timeseries-table-widget \n table-id=\"tableId\"\n ctx=\"ctx\">\n</tb-timeseries-table-widget>", "templateHtml": "<tb-timeseries-table-widget \n table-id=\"tableId\"\n ctx=\"ctx\">\n</tb-timeseries-table-widget>",
"templateCss": "", "templateCss": "",
"controllerScript": "self.onInit = function() {\n var scope = self.ctx.$scope;\n var id = self.ctx.$scope.$injector.get('utils').guid();\n scope.tableId = \"table-\"+id;\n scope.ctx = self.ctx;\n}\n\nself.onDataUpdated = function() {\n self.ctx.$scope.$broadcast('timeseries-table-data-updated', self.ctx.$scope.tableId);\n}\n\nself.onDestroy = function() {\n}", "controllerScript": "self.onInit = function() {\n var scope = self.ctx.$scope;\n var id = self.ctx.$scope.$injector.get('utils').guid();\n scope.tableId = \"table-\"+id;\n scope.ctx = self.ctx;\n}\n\nself.onDataUpdated = function() {\n self.ctx.$scope.$broadcast('timeseries-table-data-updated', self.ctx.$scope.tableId);\n}\n\nself.onDestroy = function() {\n}",
"settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"TimeseriesTableSettings\",\n \"properties\": {\n \"showTimestamp\": {\n \"title\": \"Display timestamp column\",\n \"type\": \"boolean\",\n \"default\": true\n }\n },\n \"required\": []\n },\n \"form\": [\n \"showTimestamp\"\n ]\n}", "settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"TimeseriesTableSettings\",\n \"properties\": {\n \"showTimestamp\": {\n \"title\": \"Display timestamp column\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"displayPagination\": {\n \"title\": \"Display pagination\",\n \"type\": \"boolean\",\n \"default\": true\n }, \n \"defaultPageSize\": {\n \"title\": \"Default page size\",\n \"type\": \"number\",\n \"default\": 10\n }\n },\n \"required\": []\n },\n \"form\": [\n \"showTimestamp\",\n \"displayPagination\",\n \"defaultPageSize\"\n ]\n}",
"dataKeySettingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"DataKeySettings\",\n \"properties\": {\n \"useCellStyleFunction\": {\n \"title\": \"Use cell style function\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"cellStyleFunction\": {\n \"title\": \"Cell style function: f(value)\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"useCellContentFunction\": {\n \"title\": \"Use cell content function\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"cellContentFunction\": {\n \"title\": \"Cell content function: f(value, rowData, filter)\",\n \"type\": \"string\",\n \"default\": \"\"\n }\n },\n \"required\": []\n },\n \"form\": [\n \"useCellStyleFunction\",\n {\n \"key\": \"cellStyleFunction\",\n \"type\": \"javascript\"\n },\n \"useCellContentFunction\",\n {\n \"key\": \"cellContentFunction\",\n \"type\": \"javascript\"\n }\n ]\n}", "dataKeySettingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"DataKeySettings\",\n \"properties\": {\n \"useCellStyleFunction\": {\n \"title\": \"Use cell style function\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"cellStyleFunction\": {\n \"title\": \"Cell style function: f(value)\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"useCellContentFunction\": {\n \"title\": \"Use cell content function\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"cellContentFunction\": {\n \"title\": \"Cell content function: f(value, rowData, filter)\",\n \"type\": \"string\",\n \"default\": \"\"\n }\n },\n \"required\": []\n },\n \"form\": [\n \"useCellStyleFunction\",\n {\n \"key\": \"cellStyleFunction\",\n \"type\": \"javascript\"\n },\n \"useCellContentFunction\",\n {\n \"key\": \"cellContentFunction\",\n \"type\": \"javascript\"\n }\n ]\n}",
"defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temperature °C\",\"color\":\"#2196f3\",\"settings\":{\"useCellStyleFunction\":true,\"cellStyleFunction\":\"if (value) {\\n var percent = (value + 60)/120 * 100;\\n var color = tinycolor.mix('blue', 'red', amount = percent);\\n color.setAlpha(.5);\\n return {\\n paddingLeft: '20px',\\n color: '#ffffff',\\n background: color.toRgbString(),\\n fontSize: '18px'\\n };\\n} else {\\n return {};\\n}\"},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nvar multiplier = Math.pow(10, 1 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Humidity, %\",\"color\":\"#ffc107\",\"settings\":{\"useCellStyleFunction\":true,\"cellStyleFunction\":\"if (value) {\\n var percent = value;\\n var backgroundColor = tinycolor('blue');\\n backgroundColor.setAlpha(value/100);\\n var color = 'blue';\\n if (value > 50) {\\n color = 'white';\\n }\\n \\n return {\\n paddingLeft: '20px',\\n color: color,\\n background: backgroundColor.toRgbString(),\\n fontSize: '18px'\\n };\\n} else {\\n return {};\\n}\",\"useCellContentFunction\":false},\"_hash\":0.12775350966079668,\"funcBody\":\"var value = prevValue + Math.random() * 20 - 10;\\nvar multiplier = Math.pow(10, 1 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 5) {\\n\\tvalue = 5;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"interval\":1000,\"timewindowMs\":60000},\"aggregation\":{\"type\":\"NONE\",\"limit\":200}},\"showTitle\":true,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"showTimestamp\":true},\"title\":\"Timeseries table\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":false,\"showLegend\":false}" "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temperature °C\",\"color\":\"#2196f3\",\"settings\":{\"useCellStyleFunction\":true,\"cellStyleFunction\":\"if (value) {\\n var percent = (value + 60)/120 * 100;\\n var color = tinycolor.mix('blue', 'red', amount = percent);\\n color.setAlpha(.5);\\n return {\\n paddingLeft: '20px',\\n color: '#ffffff',\\n background: color.toRgbString(),\\n fontSize: '18px'\\n };\\n} else {\\n return {};\\n}\"},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nvar multiplier = Math.pow(10, 1 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Humidity, %\",\"color\":\"#ffc107\",\"settings\":{\"useCellStyleFunction\":true,\"cellStyleFunction\":\"if (value) {\\n var percent = value;\\n var backgroundColor = tinycolor('blue');\\n backgroundColor.setAlpha(value/100);\\n var color = 'blue';\\n if (value > 50) {\\n color = 'white';\\n }\\n \\n return {\\n paddingLeft: '20px',\\n color: color,\\n background: backgroundColor.toRgbString(),\\n fontSize: '18px'\\n };\\n} else {\\n return {};\\n}\",\"useCellContentFunction\":false},\"_hash\":0.12775350966079668,\"funcBody\":\"var value = prevValue + Math.random() * 20 - 10;\\nvar multiplier = Math.pow(10, 1 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 5) {\\n\\tvalue = 5;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"interval\":1000,\"timewindowMs\":60000},\"aggregation\":{\"type\":\"NONE\",\"limit\":200}},\"showTitle\":true,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"showTimestamp\":true,\"displayPagination\":true,\"defaultPageSize\":10},\"title\":\"Timeseries table\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":false,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{}}"
} }
} }
] ]

3
application/src/main/java/org/thingsboard/server/actors/plugin/PluginProcessingContext.java

@ -62,6 +62,7 @@ public final class PluginProcessingContext implements PluginContext {
private static final Executor executor = Executors.newSingleThreadExecutor(); private static final Executor executor = Executors.newSingleThreadExecutor();
public static final String CUSTOMER_USER_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION = "Customer user is not allowed to perform this operation!"; public static final String CUSTOMER_USER_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION = "Customer user is not allowed to perform this operation!";
public static final String SYSTEM_ADMINISTRATOR_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION = "System administrator is not allowed to perform this operation!"; public static final String SYSTEM_ADMINISTRATOR_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION = "System administrator is not allowed to perform this operation!";
public static final String DEVICE_WITH_REQUESTED_ID_NOT_FOUND = "Device with requested id wasn't found!";
private final SharedPluginProcessingContext pluginCtx; private final SharedPluginProcessingContext pluginCtx;
private final Optional<PluginApiCallSecurityContext> securityCtx; private final Optional<PluginApiCallSecurityContext> securityCtx;
@ -309,7 +310,7 @@ public final class PluginProcessingContext implements PluginContext {
ListenableFuture<Device> deviceFuture = pluginCtx.deviceService.findDeviceByIdAsync(new DeviceId(entityId.getId())); ListenableFuture<Device> deviceFuture = pluginCtx.deviceService.findDeviceByIdAsync(new DeviceId(entityId.getId()));
Futures.addCallback(deviceFuture, getCallback(callback, device -> { Futures.addCallback(deviceFuture, getCallback(callback, device -> {
if (device == null) { if (device == null) {
return ValidationResult.entityNotFound("Device with requested id wasn't found!"); return ValidationResult.entityNotFound(DEVICE_WITH_REQUESTED_ID_NOT_FOUND);
} else { } else {
if (!device.getTenantId().equals(ctx.getTenantId())) { if (!device.getTenantId().equals(ctx.getTenantId())) {
return ValidationResult.accessDenied("Device doesn't belong to the current Tenant!"); return ValidationResult.accessDenied("Device doesn't belong to the current Tenant!");

4
application/src/main/java/org/thingsboard/server/config/WebSocketConfiguration.java

@ -47,8 +47,8 @@ public class WebSocketConfiguration implements WebSocketConfigurer {
@Bean @Bean
public ServletServerContainerFactoryBean createWebSocketContainer() { public ServletServerContainerFactoryBean createWebSocketContainer() {
ServletServerContainerFactoryBean container = new ServletServerContainerFactoryBean(); ServletServerContainerFactoryBean container = new ServletServerContainerFactoryBean();
container.setMaxTextMessageBufferSize(8192); container.setMaxTextMessageBufferSize(32768);
container.setMaxBinaryMessageBufferSize(8192); container.setMaxBinaryMessageBufferSize(32768);
return container; return container;
} }

16
application/src/main/java/org/thingsboard/server/controller/CustomerController.java

@ -82,7 +82,7 @@ public class CustomerController extends BaseController {
@PreAuthorize("hasAuthority('TENANT_ADMIN')") @PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/customer", method = RequestMethod.POST) @RequestMapping(value = "/customer", method = RequestMethod.POST)
@ResponseBody @ResponseBody
public Customer saveCustomer(@RequestBody Customer customer) throws ThingsboardException { public Customer saveCustomer(@RequestBody Customer customer) throws ThingsboardException {
try { try {
customer.setTenantId(getCurrentUser().getTenantId()); customer.setTenantId(getCurrentUser().getTenantId());
@ -107,7 +107,7 @@ public class CustomerController extends BaseController {
} }
@PreAuthorize("hasAuthority('TENANT_ADMIN')") @PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/customers", params = { "limit" }, method = RequestMethod.GET) @RequestMapping(value = "/customers", params = {"limit"}, method = RequestMethod.GET)
@ResponseBody @ResponseBody
public TextPageData<Customer> getCustomers(@RequestParam int limit, public TextPageData<Customer> getCustomers(@RequestParam int limit,
@RequestParam(required = false) String textSearch, @RequestParam(required = false) String textSearch,
@ -122,4 +122,16 @@ public class CustomerController extends BaseController {
} }
} }
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/tenant/customers", params = {"customerTitle"}, method = RequestMethod.GET)
@ResponseBody
public Customer getTenantCustomer(
@RequestParam String customerTitle) throws ThingsboardException {
try {
TenantId tenantId = getCurrentUser().getTenantId();
return checkNotNull(customerService.findCustomerByTenantIdAndTitle(tenantId, customerTitle));
} catch (Exception e) {
throw handleException(e);
}
}
} }

1
application/src/main/java/org/thingsboard/server/controller/plugin/PluginWebSocketHandler.java

@ -77,7 +77,6 @@ public class PluginWebSocketHandler extends TextWebSocketHandler implements Plug
log.warn("[{}] Failed to find session", session.getId()); log.warn("[{}] Failed to find session", session.getId());
session.close(CloseStatus.SERVER_ERROR.withReason("Session not found!")); session.close(CloseStatus.SERVER_ERROR.withReason("Session not found!"));
} }
session.sendMessage(message);
} catch (IOException e) { } catch (IOException e) {
log.warn("IO error", e); log.warn("IO error", e);
} }

2
application/src/main/java/org/thingsboard/server/service/mail/DefaultMailService.java

@ -96,7 +96,7 @@ public class DefaultMailService implements MailService {
javaMailProperties.put(MAIL_PROP + protocol + ".port", jsonConfig.get("smtpPort").asText()); javaMailProperties.put(MAIL_PROP + protocol + ".port", jsonConfig.get("smtpPort").asText());
javaMailProperties.put(MAIL_PROP + protocol + ".timeout", jsonConfig.get("timeout").asText()); javaMailProperties.put(MAIL_PROP + protocol + ".timeout", jsonConfig.get("timeout").asText());
javaMailProperties.put(MAIL_PROP + protocol + ".auth", String.valueOf(StringUtils.isNotEmpty(jsonConfig.get("username").asText()))); javaMailProperties.put(MAIL_PROP + protocol + ".auth", String.valueOf(StringUtils.isNotEmpty(jsonConfig.get("username").asText())));
javaMailProperties.put(MAIL_PROP + protocol + ".starttls.enable", jsonConfig.get("enableTls")); javaMailProperties.put(MAIL_PROP + protocol + ".starttls.enable", jsonConfig.has("enableTls") ? jsonConfig.get("enableTls").asText() : "false");
return javaMailProperties; return javaMailProperties;
} }

19
application/src/main/java/org/thingsboard/server/service/security/model/token/JwtTokenFactory.java

@ -20,7 +20,6 @@ import io.jsonwebtoken.Jws;
import io.jsonwebtoken.Jwts; import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm; import io.jsonwebtoken.SignatureAlgorithm;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.joda.time.DateTime;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.CustomerId;
@ -31,7 +30,9 @@ import org.thingsboard.server.config.JwtSettings;
import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.model.SecurityUser;
import org.thingsboard.server.service.security.model.UserPrincipal; import org.thingsboard.server.service.security.model.UserPrincipal;
import java.util.Arrays; import java.time.ZonedDateTime;
import java.util.Collections;
import java.util.Date;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -81,13 +82,13 @@ public class JwtTokenFactory {
claims.put(CUSTOMER_ID, securityUser.getCustomerId().getId().toString()); claims.put(CUSTOMER_ID, securityUser.getCustomerId().getId().toString());
} }
DateTime currentTime = new DateTime(); ZonedDateTime currentTime = ZonedDateTime.now();
String token = Jwts.builder() String token = Jwts.builder()
.setClaims(claims) .setClaims(claims)
.setIssuer(settings.getTokenIssuer()) .setIssuer(settings.getTokenIssuer())
.setIssuedAt(currentTime.toDate()) .setIssuedAt(Date.from(currentTime.toInstant()))
.setExpiration(currentTime.plusSeconds(settings.getTokenExpirationTime()).toDate()) .setExpiration(Date.from(currentTime.plusSeconds(settings.getTokenExpirationTime()).toInstant()))
.signWith(SignatureAlgorithm.HS512, settings.getTokenSigningKey()) .signWith(SignatureAlgorithm.HS512, settings.getTokenSigningKey())
.compact(); .compact();
@ -129,11 +130,11 @@ public class JwtTokenFactory {
throw new IllegalArgumentException("Cannot create JWT Token without username/email"); throw new IllegalArgumentException("Cannot create JWT Token without username/email");
} }
DateTime currentTime = new DateTime(); ZonedDateTime currentTime = ZonedDateTime.now();
UserPrincipal principal = securityUser.getUserPrincipal(); UserPrincipal principal = securityUser.getUserPrincipal();
Claims claims = Jwts.claims().setSubject(principal.getValue()); Claims claims = Jwts.claims().setSubject(principal.getValue());
claims.put(SCOPES, Arrays.asList(Authority.REFRESH_TOKEN.name())); claims.put(SCOPES, Collections.singletonList(Authority.REFRESH_TOKEN.name()));
claims.put(USER_ID, securityUser.getId().getId().toString()); claims.put(USER_ID, securityUser.getId().getId().toString());
claims.put(IS_PUBLIC, principal.getType() == UserPrincipal.Type.PUBLIC_ID); claims.put(IS_PUBLIC, principal.getType() == UserPrincipal.Type.PUBLIC_ID);
@ -141,8 +142,8 @@ public class JwtTokenFactory {
.setClaims(claims) .setClaims(claims)
.setIssuer(settings.getTokenIssuer()) .setIssuer(settings.getTokenIssuer())
.setId(UUID.randomUUID().toString()) .setId(UUID.randomUUID().toString())
.setIssuedAt(currentTime.toDate()) .setIssuedAt(Date.from(currentTime.toInstant()))
.setExpiration(currentTime.plusSeconds(settings.getRefreshTokenExpTime()).toDate()) .setExpiration(Date.from(currentTime.plusSeconds(settings.getRefreshTokenExpTime()).toInstant()))
.signWith(SignatureAlgorithm.HS512, settings.getTokenSigningKey()) .signWith(SignatureAlgorithm.HS512, settings.getTokenSigningKey())
.compact(); .compact();

20
application/src/test/java/org/thingsboard/server/controller/AbstractControllerTest.java

@ -106,6 +106,11 @@ public abstract class AbstractControllerTest {
protected static final String CUSTOMER_USER_EMAIL = "testcustomer@thingsboard.org"; protected static final String CUSTOMER_USER_EMAIL = "testcustomer@thingsboard.org";
private static final String CUSTOMER_USER_PASSWORD = "customer"; private static final String CUSTOMER_USER_PASSWORD = "customer";
/** See {@link org.springframework.test.web.servlet.DefaultMvcResult#getAsyncResult(long)}
* and {@link org.springframework.mock.web.MockAsyncContext#getTimeout()}
*/
private static final long DEFAULT_TIMEOUT = -1L;
protected MediaType contentType = new MediaType(MediaType.APPLICATION_JSON.getType(), protected MediaType contentType = new MediaType(MediaType.APPLICATION_JSON.getType(),
MediaType.APPLICATION_JSON.getSubtype(), MediaType.APPLICATION_JSON.getSubtype(),
Charset.forName("utf8")); Charset.forName("utf8"));
@ -336,7 +341,7 @@ public abstract class AbstractControllerTest {
} }
protected <T> T doPost(String urlTemplate, T content, Class<T> responseClass, ResultMatcher resultMatcher, String... params) throws Exception { protected <T> T doPost(String urlTemplate, T content, Class<T> responseClass, ResultMatcher resultMatcher, String... params) throws Exception {
return readResponse(doPost(urlTemplate, params).andExpect(resultMatcher), responseClass); return readResponse(doPost(urlTemplate, content, params).andExpect(resultMatcher), responseClass);
} }
protected <T> T doPost(String urlTemplate, T content, Class<T> responseClass, String... params) throws Exception { protected <T> T doPost(String urlTemplate, T content, Class<T> responseClass, String... params) throws Exception {
@ -344,7 +349,11 @@ public abstract class AbstractControllerTest {
} }
protected <T> T doPostAsync(String urlTemplate, T content, Class<T> responseClass, ResultMatcher resultMatcher, String... params) throws Exception { protected <T> T doPostAsync(String urlTemplate, T content, Class<T> responseClass, ResultMatcher resultMatcher, String... params) throws Exception {
return readResponse(doPostAsync(urlTemplate, content, params).andExpect(resultMatcher), responseClass); return readResponse(doPostAsync(urlTemplate, content, DEFAULT_TIMEOUT, params).andExpect(resultMatcher), responseClass);
}
protected <T> T doPostAsync(String urlTemplate, T content, Class<T> responseClass, ResultMatcher resultMatcher, Long timeout, String... params) throws Exception {
return readResponse(doPostAsync(urlTemplate, content, timeout, params).andExpect(resultMatcher), responseClass);
} }
protected <T> T doDelete(String urlTemplate, Class<T> responseClass, String... params) throws Exception { protected <T> T doDelete(String urlTemplate, Class<T> responseClass, String... params) throws Exception {
@ -366,12 +375,13 @@ public abstract class AbstractControllerTest {
return mockMvc.perform(postRequest); return mockMvc.perform(postRequest);
} }
protected <T> ResultActions doPostAsync(String urlTemplate, T content, String... params) throws Exception { protected <T> ResultActions doPostAsync(String urlTemplate, T content, Long timeout, String... params) throws Exception {
MockHttpServletRequestBuilder postRequest = post(urlTemplate); MockHttpServletRequestBuilder postRequest = post(urlTemplate);
setJwtToken(postRequest); setJwtToken(postRequest);
String json = json(content); String json = json(content);
postRequest.contentType(contentType).content(json); postRequest.contentType(contentType).content(json);
MvcResult result = mockMvc.perform(postRequest).andReturn(); MvcResult result = mockMvc.perform(postRequest).andReturn();
result.getAsyncResult(timeout);
return mockMvc.perform(asyncDispatch(result)); return mockMvc.perform(asyncDispatch(result));
} }
@ -384,8 +394,8 @@ public abstract class AbstractControllerTest {
protected void populateParams(MockHttpServletRequestBuilder request, String... params) { protected void populateParams(MockHttpServletRequestBuilder request, String... params) {
if (params != null && params.length > 0) { if (params != null && params.length > 0) {
Assert.assertEquals(params.length % 2, 0); Assert.assertEquals(0, params.length % 2);
MultiValueMap<String, String> paramsMap = new LinkedMultiValueMap<String, String>(); MultiValueMap<String, String> paramsMap = new LinkedMultiValueMap<>();
for (int i = 0; i < params.length; i += 2) { for (int i = 0; i < params.length; i += 2) {
paramsMap.add(params[i], params[i + 1]); paramsMap.add(params[i], params[i + 1]);
} }

86
application/src/test/java/org/thingsboard/server/mqtt/rpc/AbstractMqttServerSideRpcIntegrationTest.java

@ -15,21 +15,23 @@
*/ */
package org.thingsboard.server.mqtt.rpc; package org.thingsboard.server.mqtt.rpc;
import java.util.Arrays;
import com.datastax.driver.core.utils.UUIDs;
import com.fasterxml.jackson.core.type.TypeReference;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.eclipse.paho.client.mqttv3.*; import org.eclipse.paho.client.mqttv3.*;
import org.junit.*; import org.junit.*;
import org.springframework.http.HttpStatus; import org.thingsboard.server.actors.plugin.PluginProcessingContext;
import org.springframework.web.client.HttpClientErrorException;
import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.page.TextPageData;
import org.thingsboard.server.common.data.plugin.PluginMetaData;
import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.common.data.security.DeviceCredentials; import org.thingsboard.server.common.data.security.DeviceCredentials;
import org.thingsboard.server.controller.AbstractControllerTest; import org.thingsboard.server.controller.AbstractControllerTest;
import org.thingsboard.server.dao.service.DaoNoSqlTest;
import java.util.UUID;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
@ -42,15 +44,19 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractControllerTest { public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractControllerTest {
private static final String MQTT_URL = "tcp://localhost:1883"; private static final String MQTT_URL = "tcp://localhost:1883";
private static final String FAIL_MSG_IF_HTTP_CLIENT_ERROR_NOT_ENCOUNTERED = "HttpClientErrorException expected, but not encountered"; private static final Long TIME_TO_HANDLE_REQUEST = 500L;
private Tenant savedTenant; private Tenant savedTenant;
private User tenantAdmin; private User tenantAdmin;
private Long asyncContextTimeoutToUseRpcPlugin;
@Before @Before
public void beforeTest() throws Exception { public void beforeTest() throws Exception {
loginSysAdmin(); loginSysAdmin();
asyncContextTimeoutToUseRpcPlugin = getAsyncContextTimeoutToUseRpcPlugin();
Tenant tenant = new Tenant(); Tenant tenant = new Tenant();
tenant.setTitle("My tenant"); tenant.setTitle("My tenant");
savedTenant = doPost("/api/tenant", tenant, Tenant.class); savedTenant = doPost("/api/tenant", tenant, Tenant.class);
@ -70,8 +76,7 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractC
public void afterTest() throws Exception { public void afterTest() throws Exception {
loginSysAdmin(); loginSysAdmin();
if (savedTenant != null) { if (savedTenant != null) {
doDelete("/api/tenant/" + savedTenant.getId().getId().toString()) doDelete("/api/tenant/" + savedTenant.getId().getId().toString()).andExpect(status().isOk());
.andExpect(status().isOk());
} }
} }
@ -102,7 +107,6 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractC
} }
@Test @Test
@Ignore // TODO: figure out the right error code for this case. Ignored due to failure: expected 408 but was: 200
public void testServerMqttOneWayRpcDeviceOffline() throws Exception { public void testServerMqttOneWayRpcDeviceOffline() throws Exception {
Device device = new Device(); Device device = new Device();
device.setName("Test One-Way Server-Side RPC Device Offline"); device.setName("Test One-Way Server-Side RPC Device Offline");
@ -115,29 +119,19 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractC
String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"23\",\"value\": 1}}"; String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"23\",\"value\": 1}}";
String deviceId = savedDevice.getId().getId().toString(); String deviceId = savedDevice.getId().getId().toString();
try {
doPost("/api/plugins/rpc/oneway/" + deviceId, setGpioRequest, String.class, status().is(408)); doPostAsync("/api/plugins/rpc/oneway/" + deviceId, setGpioRequest, String.class, status().isRequestTimeout(),
Assert.fail(FAIL_MSG_IF_HTTP_CLIENT_ERROR_NOT_ENCOUNTERED); asyncContextTimeoutToUseRpcPlugin);
} catch (HttpClientErrorException e) {
log.error(e.getMessage(), e);
Assert.assertEquals(HttpStatus.REQUEST_TIMEOUT, e.getStatusCode());
Assert.assertEquals("408 null", e.getMessage());
}
} }
@Test @Test
@Ignore // TODO: figure out the right error code for this case. Ignored due to failure: expected 400 (404?) but was: 401
public void testServerMqttOneWayRpcDeviceDoesNotExist() throws Exception { public void testServerMqttOneWayRpcDeviceDoesNotExist() throws Exception {
String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"23\",\"value\": 1}}"; String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"23\",\"value\": 1}}";
String nonExistentDeviceId = UUID.randomUUID().toString(); String nonExistentDeviceId = UUIDs.timeBased().toString();
try {
doPostAsync("/api/plugins/rpc/oneway/" + nonExistentDeviceId, setGpioRequest, String.class, status().is(400)); String result = doPostAsync("/api/plugins/rpc/oneway/" + nonExistentDeviceId, setGpioRequest, String.class,
Assert.fail(FAIL_MSG_IF_HTTP_CLIENT_ERROR_NOT_ENCOUNTERED); status().isNotFound());
} catch (HttpClientErrorException e) { Assert.assertEquals(PluginProcessingContext.DEVICE_WITH_REQUESTED_ID_NOT_FOUND, result);
log.error(e.getMessage(), e);
Assert.assertEquals(HttpStatus.BAD_REQUEST, e.getStatusCode());
Assert.assertEquals("400 null", e.getMessage());
}
} }
@Test @Test
@ -168,7 +162,6 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractC
} }
@Test @Test
@Ignore // TODO: figure out the right error code for this case. Ignored due to failure: expected 408 but was: 200
public void testServerMqttTwoWayRpcDeviceOffline() throws Exception { public void testServerMqttTwoWayRpcDeviceOffline() throws Exception {
Device device = new Device(); Device device = new Device();
device.setName("Test Two-Way Server-Side RPC Device Offline"); device.setName("Test Two-Way Server-Side RPC Device Offline");
@ -181,29 +174,19 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractC
String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"23\",\"value\": 1}}"; String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"23\",\"value\": 1}}";
String deviceId = savedDevice.getId().getId().toString(); String deviceId = savedDevice.getId().getId().toString();
try {
doPost("/api/plugins/rpc/twoway/" + deviceId, setGpioRequest, String.class, status().is(408)); doPostAsync("/api/plugins/rpc/twoway/" + deviceId, setGpioRequest, String.class, status().isRequestTimeout(),
Assert.fail(FAIL_MSG_IF_HTTP_CLIENT_ERROR_NOT_ENCOUNTERED); asyncContextTimeoutToUseRpcPlugin);
} catch (HttpClientErrorException e) {
log.error(e.getMessage(), e);
Assert.assertEquals(HttpStatus.REQUEST_TIMEOUT, e.getStatusCode());
Assert.assertEquals("408 null", e.getMessage());
}
} }
@Test @Test
@Ignore // TODO: figure out the right error code for this case. Ignored due to failure: expected 400 (404?) but was: 401
public void testServerMqttTwoWayRpcDeviceDoesNotExist() throws Exception { public void testServerMqttTwoWayRpcDeviceDoesNotExist() throws Exception {
String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"23\",\"value\": 1}}"; String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"23\",\"value\": 1}}";
String nonExistentDeviceId = UUID.randomUUID().toString(); String nonExistentDeviceId = UUIDs.timeBased().toString();
try {
doPostAsync("/api/plugins/rpc/oneway/" + nonExistentDeviceId, setGpioRequest, String.class, status().is(400)); String result = doPostAsync("/api/plugins/rpc/twoway/" + nonExistentDeviceId, setGpioRequest, String.class,
Assert.fail(FAIL_MSG_IF_HTTP_CLIENT_ERROR_NOT_ENCOUNTERED); status().isNotFound());
} catch (HttpClientErrorException e) { Assert.assertEquals(PluginProcessingContext.DEVICE_WITH_REQUESTED_ID_NOT_FOUND, result);
log.error(e.getMessage(), e);
Assert.assertEquals(HttpStatus.BAD_REQUEST, e.getStatusCode());
Assert.assertEquals("400 null", e.getMessage());
}
} }
private Device getSavedDevice(Device device) throws Exception { private Device getSavedDevice(Device device) throws Exception {
@ -214,6 +197,13 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractC
return doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class); return doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class);
} }
private Long getAsyncContextTimeoutToUseRpcPlugin() throws Exception {
TextPageData<PluginMetaData> plugins = doGetTyped("/api/plugin/system?limit=1&textSearch=system rpc plugin",
new TypeReference<TextPageData<PluginMetaData>>(){});
Long systemRpcPluginTimeout = plugins.getData().iterator().next().getConfiguration().get("defaultTimeout").asLong();
return systemRpcPluginTimeout + TIME_TO_HANDLE_REQUEST;
}
private static class TestMqttCallback implements MqttCallback { private static class TestMqttCallback implements MqttCallback {
private final MqttAsyncClient client; private final MqttAsyncClient client;
@ -228,10 +218,10 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractC
@Override @Override
public void messageArrived(String requestTopic, MqttMessage mqttMessage) throws Exception { public void messageArrived(String requestTopic, MqttMessage mqttMessage) throws Exception {
log.info("Message Arrived: " + mqttMessage.getPayload().toString()); log.info("Message Arrived: " + Arrays.toString(mqttMessage.getPayload()));
MqttMessage message = new MqttMessage(); MqttMessage message = new MqttMessage();
String responseTopic = requestTopic.replace("request", "response"); String responseTopic = requestTopic.replace("request", "response");
message.setPayload("{\"value1\":\"A\", \"value2\":\"B\"}".getBytes()); message.setPayload("{\"value1\":\"A\", \"value2\":\"B\"}".getBytes("UTF-8"));
client.publish(responseTopic, message); client.publish(responseTopic, message);
} }

8
dao/src/main/java/org/thingsboard/server/dao/customer/CustomerService.java

@ -22,20 +22,24 @@ import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.TextPageData; import org.thingsboard.server.common.data.page.TextPageData;
import org.thingsboard.server.common.data.page.TextPageLink; import org.thingsboard.server.common.data.page.TextPageLink;
import java.util.Optional;
public interface CustomerService { public interface CustomerService {
Customer findCustomerById(CustomerId customerId); Customer findCustomerById(CustomerId customerId);
Optional<Customer> findCustomerByTenantIdAndTitle(TenantId tenantId, String title);
ListenableFuture<Customer> findCustomerByIdAsync(CustomerId customerId); ListenableFuture<Customer> findCustomerByIdAsync(CustomerId customerId);
Customer saveCustomer(Customer customer); Customer saveCustomer(Customer customer);
void deleteCustomer(CustomerId customerId); void deleteCustomer(CustomerId customerId);
Customer findOrCreatePublicCustomer(TenantId tenantId); Customer findOrCreatePublicCustomer(TenantId tenantId);
TextPageData<Customer> findCustomersByTenantId(TenantId tenantId, TextPageLink pageLink); TextPageData<Customer> findCustomersByTenantId(TenantId tenantId, TextPageLink pageLink);
void deleteCustomersByTenantId(TenantId tenantId); void deleteCustomersByTenantId(TenantId tenantId);
} }

8
dao/src/main/java/org/thingsboard/server/dao/customer/CustomerServiceImpl.java

@ -52,6 +52,7 @@ public class CustomerServiceImpl extends AbstractEntityService implements Custom
private static final String PUBLIC_CUSTOMER_TITLE = "Public"; private static final String PUBLIC_CUSTOMER_TITLE = "Public";
public static final String INCORRECT_CUSTOMER_ID = "Incorrect customerId "; public static final String INCORRECT_CUSTOMER_ID = "Incorrect customerId ";
public static final String INCORRECT_TENANT_ID = "Incorrect tenantId ";
@Autowired @Autowired
private CustomerDao customerDao; private CustomerDao customerDao;
@ -78,6 +79,13 @@ public class CustomerServiceImpl extends AbstractEntityService implements Custom
return customerDao.findById(customerId.getId()); return customerDao.findById(customerId.getId());
} }
@Override
public Optional<Customer> findCustomerByTenantIdAndTitle(TenantId tenantId, String title) {
log.trace("Executing findCustomerByTenantIdAndTitle [{}] [{}]", tenantId, title);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
return customerDao.findCustomersByTenantIdAndTitle(tenantId.getId(), title);
}
@Override @Override
public ListenableFuture<Customer> findCustomerByIdAsync(CustomerId customerId) { public ListenableFuture<Customer> findCustomerByIdAsync(CustomerId customerId) {
log.trace("Executing findCustomerByIdAsync [{}]", customerId); log.trace("Executing findCustomerByIdAsync [{}]", customerId);

8
pom.xml

@ -33,8 +33,7 @@
<spring.version>4.3.4.RELEASE</spring.version> <spring.version>4.3.4.RELEASE</spring.version>
<spring-security.version>4.2.0.RELEASE</spring-security.version> <spring-security.version>4.2.0.RELEASE</spring-security.version>
<jjwt.version>0.7.0</jjwt.version> <jjwt.version>0.7.0</jjwt.version>
<joda-time.version>2.4</joda-time.version> <json-path.version>2.2.0</json-path.version>
<json-path.version>2.2.0</json-path.version>
<junit.version>4.12</junit.version> <junit.version>4.12</junit.version>
<slf4j.version>1.7.7</slf4j.version> <slf4j.version>1.7.7</slf4j.version>
<logback.version>1.2.3</logback.version> <logback.version>1.2.3</logback.version>
@ -483,11 +482,6 @@
<artifactId>jjwt</artifactId> <artifactId>jjwt</artifactId>
<version>${jjwt.version}</version> <version>${jjwt.version}</version>
</dependency> </dependency>
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>${joda-time.version}</version>
</dependency>
<dependency> <dependency>
<groupId>org.apache.velocity</groupId> <groupId>org.apache.velocity</groupId>
<artifactId>velocity</artifactId> <artifactId>velocity</artifactId>

41
tools/src/main/java/org/thingsboard/client/tools/RestClient.java

@ -29,13 +29,12 @@ import org.springframework.web.client.RestTemplate;
import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.alarm.Alarm;
import org.thingsboard.server.common.data.alarm.AlarmSeverity;
import org.thingsboard.server.common.data.alarm.AlarmStatus;
import org.thingsboard.server.common.data.asset.Asset; import org.thingsboard.server.common.data.asset.Asset;
import org.thingsboard.server.common.data.id.AssetId; import org.thingsboard.server.common.data.id.AssetId;
import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.relation.EntityRelation;
import org.thingsboard.server.common.data.security.DeviceCredentials; import org.thingsboard.server.common.data.security.DeviceCredentials;
import java.io.IOException; import java.io.IOException;
@ -78,6 +77,36 @@ public class RestClient implements ClientHttpRequestInterceptor {
} }
} }
public Optional<Customer> findCustomer(String title) {
Map<String, String> params = new HashMap<String, String>();
params.put("customerTitle", title);
try {
ResponseEntity<Customer> customerEntity = restTemplate.getForEntity(baseURL + "/api/tenant/customers?customerTitle={customerTitle}", Customer.class, params);
return Optional.of(customerEntity.getBody());
} catch (HttpClientErrorException exception) {
if (exception.getStatusCode() == HttpStatus.NOT_FOUND) {
return Optional.empty();
} else {
throw exception;
}
}
}
public Optional<Asset> findAsset(String name) {
Map<String, String> params = new HashMap<String, String>();
params.put("assetName", name);
try {
ResponseEntity<Asset> assetEntity = restTemplate.getForEntity(baseURL + "/api/tenant/assets?assetName={assetName}", Asset.class, params);
return Optional.of(assetEntity.getBody());
} catch (HttpClientErrorException exception) {
if (exception.getStatusCode() == HttpStatus.NOT_FOUND) {
return Optional.empty();
} else {
throw exception;
}
}
}
public Customer createCustomer(String title) { public Customer createCustomer(String title) {
Customer customer = new Customer(); Customer customer = new Customer();
customer.setTitle(title); customer.setTitle(title);
@ -112,6 +141,14 @@ public class RestClient implements ClientHttpRequestInterceptor {
customerId.toString(), assetId.toString()).getBody(); customerId.toString(), assetId.toString()).getBody();
} }
public EntityRelation makeRelation(String relationType, EntityId idFrom, EntityId idTo) {
EntityRelation relation = new EntityRelation();
relation.setFrom(idFrom);
relation.setTo(idTo);
relation.setType(relationType);
return restTemplate.postForEntity(baseURL + "/api/relation", relation, EntityRelation.class).getBody();
}
public DeviceCredentials getCredentials(DeviceId id) { public DeviceCredentials getCredentials(DeviceId id) {
return restTemplate.getForEntity(baseURL + "/api/device/" + id.getId().toString() + "/credentials", DeviceCredentials.class).getBody(); return restTemplate.getForEntity(baseURL + "/api/device/" + id.getId().toString() + "/credentials", DeviceCredentials.class).getBody();
} }

4
tools/src/main/shell/client.keygen.sh

@ -74,13 +74,13 @@ echo "Generating SSL Key Pair..."
keytool -genkeypair -v \ keytool -genkeypair -v \
-alias $CLIENT_KEY_ALIAS \ -alias $CLIENT_KEY_ALIAS \
-dname "CN=$DOMAIN_SUFFIX, OU=$ORGANIZATIONAL_UNIT, O=$ORGANIZATION, L=$CITY, ST=$STATE_OR_PROVINCE, C=$TWO_LETTER_COUNTRY_CODE" \
-keystore $CLIENT_FILE_PREFIX.jks \ -keystore $CLIENT_FILE_PREFIX.jks \
-keypass $CLIENT_KEY_PASSWORD \ -keypass $CLIENT_KEY_PASSWORD \
-storepass $CLIENT_KEYSTORE_PASSWORD \ -storepass $CLIENT_KEYSTORE_PASSWORD \
-keyalg RSA \ -keyalg RSA \
-keysize 2048 \ -keysize 2048 \
-validity 9999 -validity 9999 \
-dname "CN=$DOMAIN_SUFFIX, OU=$ORGANIZATIONAL_UNIT, O=$ORGANIZATION, L=$CITY, ST=$STATE_OR_PROVINCE, C=$TWO_LETTER_COUNTRY_CODE"
echo "Converting keystore to pkcs12" echo "Converting keystore to pkcs12"
keytool -importkeystore \ keytool -importkeystore \

2
tools/src/main/shell/keygen.properties

@ -17,7 +17,7 @@
DOMAIN_SUFFIX="$(hostname)" DOMAIN_SUFFIX="$(hostname)"
ORGANIZATIONAL_UNIT=Thingsboard ORGANIZATIONAL_UNIT=Thingsboard
ORGANIZATION=Thingsboard ORGANIZATION=Thingsboard
CITY=San Francisco CITY=SF
STATE_OR_PROVINCE=CA STATE_OR_PROVINCE=CA
TWO_LETTER_COUNTRY_CODE=US TWO_LETTER_COUNTRY_CODE=US

8
ui/src/app/admin/general-settings.tpl.html

@ -22,11 +22,11 @@
<span translate class="md-headline">admin.general-settings</span> <span translate class="md-headline">admin.general-settings</span>
</md-card-title-text> </md-card-title-text>
</md-card-title> </md-card-title>
<md-progress-linear md-mode="indeterminate" ng-disabled="!loading" ng-show="loading"></md-progress-linear> <md-progress-linear md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
<span style="min-height: 5px;" flex="" ng-show="!loading"></span> <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
<md-card-content> <md-card-content>
<form name="vm.settingsForm" ng-submit="vm.save()" tb-confirm-on-exit confirm-form="vm.settingsForm"> <form name="vm.settingsForm" ng-submit="vm.save()" tb-confirm-on-exit confirm-form="vm.settingsForm">
<fieldset ng-disabled="loading"> <fieldset ng-disabled="$root.loading">
<md-input-container class="md-block"> <md-input-container class="md-block">
<label translate>admin.base-url</label> <label translate>admin.base-url</label>
<input required name="baseUrl" ng-model="vm.settings.jsonValue.baseUrl"> <input required name="baseUrl" ng-model="vm.settings.jsonValue.baseUrl">
@ -35,7 +35,7 @@
</div> </div>
</md-input-container> </md-input-container>
<div layout="row" layout-align="end center" width="100%" layout-wrap> <div layout="row" layout-align="end center" width="100%" layout-wrap>
<md-button ng-disabled="loading || vm.settingsForm.$invalid || !vm.settingsForm.$dirty" type="submit" class="md-raised md-primary">{{'action.save' | translate}}</md-button> <md-button ng-disabled="$root.loading || vm.settingsForm.$invalid || !vm.settingsForm.$dirty" type="submit" class="md-raised md-primary">{{'action.save' | translate}}</md-button>
</div> </div>
</fieldset> </fieldset>
</form> </form>

14
ui/src/app/admin/outgoing-mail-settings.tpl.html

@ -24,11 +24,11 @@
<div id="help-container"></div> <div id="help-container"></div>
</md-card-title-text> </md-card-title-text>
</md-card-title> </md-card-title>
<md-progress-linear md-mode="indeterminate" ng-disabled="!loading" ng-show="loading"></md-progress-linear> <md-progress-linear md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
<span style="min-height: 5px;" flex="" ng-show="!loading"></span> <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
<md-card-content> <md-card-content>
<form name="vm.settingsForm" ng-submit="vm.save()" tb-confirm-on-exit confirm-form="vm.settingsForm"> <form name="vm.settingsForm" ng-submit="vm.save()" tb-confirm-on-exit confirm-form="vm.settingsForm">
<fieldset ng-disabled="loading"> <fieldset ng-disabled="$root.loading">
<md-input-container class="md-block"> <md-input-container class="md-block">
<label translate>admin.mail-from</label> <label translate>admin.mail-from</label>
<input required name="mailFrom" ng-model="vm.settings.jsonValue.mailFrom"> <input required name="mailFrom" ng-model="vm.settings.jsonValue.mailFrom">
@ -38,7 +38,7 @@
</md-input-container> </md-input-container>
<md-input-container class="md-block"> <md-input-container class="md-block">
<label translate>admin.smtp-protocol</label> <label translate>admin.smtp-protocol</label>
<md-select ng-disabled="loading" ng-model="vm.settings.jsonValue.smtpProtocol"> <md-select ng-disabled="$root.loading" ng-model="vm.settings.jsonValue.smtpProtocol">
<md-option ng-repeat="smtpProtocol in vm.smtpProtocols" value="{{smtpProtocol}}"> <md-option ng-repeat="smtpProtocol in vm.smtpProtocols" value="{{smtpProtocol}}">
{{smtpProtocol.toUpperCase()}} {{smtpProtocol.toUpperCase()}}
</md-option> </md-option>
@ -78,7 +78,7 @@
<div translate ng-message="md-maxlength">admin.timeout-invalid</div> <div translate ng-message="md-maxlength">admin.timeout-invalid</div>
</div> </div>
</md-input-container> </md-input-container>
<md-checkbox ng-disabled="loading" ng-true-value="'true'" ng-false-value="'false'" <md-checkbox ng-disabled="$root.loading" ng-true-value="'true'" ng-false-value="'false'"
aria-label="{{ 'admin.enable-tls' | translate }}" ng-model="vm.settings.jsonValue.enableTls">{{ 'admin.enable-tls' | translate }}</md-checkbox> aria-label="{{ 'admin.enable-tls' | translate }}" ng-model="vm.settings.jsonValue.enableTls">{{ 'admin.enable-tls' | translate }}</md-checkbox>
<md-input-container class="md-block"> <md-input-container class="md-block">
<label translate>common.username</label> <label translate>common.username</label>
@ -89,8 +89,8 @@
<input name="password" placeholder="{{ 'common.enter-password' | translate }}" type="password" ng-model="vm.settings.jsonValue.password"> <input name="password" placeholder="{{ 'common.enter-password' | translate }}" type="password" ng-model="vm.settings.jsonValue.password">
</md-input-container> </md-input-container>
<div layout="row" layout-align="end center" width="100%" layout-wrap> <div layout="row" layout-align="end center" width="100%" layout-wrap>
<md-button ng-disabled="loading || vm.settingsForm.$invalid" ng-click="vm.sendTestMail()" class="md-raised">{{'admin.send-test-mail' | translate}}</md-button> <md-button ng-disabled="$root.loading || vm.settingsForm.$invalid" ng-click="vm.sendTestMail()" class="md-raised">{{'admin.send-test-mail' | translate}}</md-button>
<md-button ng-disabled="loading || vm.settingsForm.$invalid || !vm.settingsForm.$dirty" type="submit" class="md-raised md-primary">{{'action.save' | translate}}</md-button> <md-button ng-disabled="$root.loading || vm.settingsForm.$invalid || !vm.settingsForm.$dirty" type="submit" class="md-raised md-primary">{{'action.save' | translate}}</md-button>
</div> </div>
</fieldset> </fieldset>
</form> </form>

6
ui/src/app/alarm/alarm-details-dialog.tpl.html

@ -87,7 +87,7 @@
<md-button ng-if="vm.allowAcknowledgment && (vm.alarm.status==vm.types.alarmStatus.activeUnack || <md-button ng-if="vm.allowAcknowledgment && (vm.alarm.status==vm.types.alarmStatus.activeUnack ||
vm.alarm.status==vm.types.alarmStatus.clearedUnack)" vm.alarm.status==vm.types.alarmStatus.clearedUnack)"
class="md-raised md-primary" class="md-raised md-primary"
ng-disabled="loading" ng-disabled="$root.loading"
ng-click="vm.acknowledge()" ng-click="vm.acknowledge()"
style="margin-right:20px;">{{ 'alarm.acknowledge' | style="margin-right:20px;">{{ 'alarm.acknowledge' |
translate }} translate }}
@ -95,12 +95,12 @@
<md-button ng-if="vm.allowClear && (vm.alarm.status==vm.types.alarmStatus.activeAck || <md-button ng-if="vm.allowClear && (vm.alarm.status==vm.types.alarmStatus.activeAck ||
vm.alarm.status==vm.types.alarmStatus.activeUnack)" vm.alarm.status==vm.types.alarmStatus.activeUnack)"
class="md-raised md-primary" class="md-raised md-primary"
ng-disabled="loading" ng-disabled="$root.loading"
ng-click="vm.clear()">{{ 'alarm.clear' | ng-click="vm.clear()">{{ 'alarm.clear' |
translate }} translate }}
</md-button> </md-button>
<span flex></span> <span flex></span>
<md-button ng-disabled="loading" ng-click="vm.close()" style="margin-right:20px;">{{ 'action.close' | <md-button ng-disabled="$root.loading" ng-click="vm.close()" style="margin-right:20px;">{{ 'action.close' |
translate }} translate }}
</md-button> </md-button>
</md-dialog-actions> </md-dialog-actions>

6
ui/src/app/alarm/alarm-table.tpl.html

@ -19,7 +19,7 @@
<section layout="row"> <section layout="row">
<md-input-container class="md-block" style="width: 200px;"> <md-input-container class="md-block" style="width: 200px;">
<label translate>alarm.alarm-status</label> <label translate>alarm.alarm-status</label>
<md-select ng-model="alarmSearchStatus" ng-disabled="loading()"> <md-select ng-model="alarmSearchStatus" ng-disabled="$root.loading">
<md-option ng-repeat="searchStatus in types.alarmSearchStatus" ng-value="searchStatus"> <md-option ng-repeat="searchStatus in types.alarmSearchStatus" ng-value="searchStatus">
{{ ('alarm.search-status.' + searchStatus) | translate }} {{ ('alarm.search-status.' + searchStatus) | translate }}
</md-option> </md-option>
@ -31,8 +31,8 @@
<md-list flex layout="column" class="tb-alarm-table"> <md-list flex layout="column" class="tb-alarm-table">
<md-list class="tb-row tb-header" layout="row" tb-alarm-header> <md-list class="tb-row tb-header" layout="row" tb-alarm-header>
</md-list> </md-list>
<md-progress-linear style="max-height: 0px;" md-mode="indeterminate" ng-disabled="!loading()" <md-progress-linear style="max-height: 0px;" md-mode="indeterminate" ng-disabled="!$root.loading"
ng-show="loading()"></md-progress-linear> ng-show="$root.loading"></md-progress-linear>
<md-divider></md-divider> <md-divider></md-divider>
<span translate layout-align="center center" <span translate layout-align="center center"
style="margin-top: 25px;" style="margin-top: 25px;"

4
ui/src/app/api/asset.service.js

@ -265,10 +265,10 @@ function AssetService($http, $q, customerService, userService) {
return deferred.promise; return deferred.promise;
} }
function getAssetTypes() { function getAssetTypes(config) {
var deferred = $q.defer(); var deferred = $q.defer();
var url = '/api/asset/types'; var url = '/api/asset/types';
$http.get(url).then(function success(response) { $http.get(url, config).then(function success(response) {
deferred.resolve(response.data); deferred.resolve(response.data);
}, function fail() { }, function fail() {
deferred.reject(); deferred.reject();

4
ui/src/app/api/attribute.service.js

@ -35,7 +35,7 @@ function AttributeService($http, $q, $filter, types, telemetryWebsocketService)
return service; return service;
function getEntityKeys(entityType, entityId, query, type) { function getEntityKeys(entityType, entityId, query, type, config) {
var deferred = $q.defer(); var deferred = $q.defer();
var url = '/api/plugins/telemetry/' + entityType + '/' + entityId + '/keys/'; var url = '/api/plugins/telemetry/' + entityType + '/' + entityId + '/keys/';
if (type === types.dataKeyType.timeseries) { if (type === types.dataKeyType.timeseries) {
@ -43,7 +43,7 @@ function AttributeService($http, $q, $filter, types, telemetryWebsocketService)
} else if (type === types.dataKeyType.attribute) { } else if (type === types.dataKeyType.attribute) {
url += 'attributes'; url += 'attributes';
} }
$http.get(url, null).then(function success(response) { $http.get(url, config).then(function success(response) {
var result = []; var result = [];
if (response.data) { if (response.data) {
if (query) { if (query) {

8
ui/src/app/api/customer.service.js

@ -32,7 +32,7 @@ function CustomerService($http, $q, types) {
return service; return service;
function getCustomers(pageLink) { function getCustomers(pageLink, config) {
var deferred = $q.defer(); var deferred = $q.defer();
var url = '/api/customers?limit=' + pageLink.limit; var url = '/api/customers?limit=' + pageLink.limit;
if (angular.isDefined(pageLink.textSearch)) { if (angular.isDefined(pageLink.textSearch)) {
@ -44,7 +44,7 @@ function CustomerService($http, $q, types) {
if (angular.isDefined(pageLink.textOffset)) { if (angular.isDefined(pageLink.textOffset)) {
url += '&textOffset=' + pageLink.textOffset; url += '&textOffset=' + pageLink.textOffset;
} }
$http.get(url, null).then(function success(response) { $http.get(url, config).then(function success(response) {
deferred.resolve(response.data); deferred.resolve(response.data);
}, function fail() { }, function fail() {
deferred.reject(); deferred.reject();
@ -52,10 +52,10 @@ function CustomerService($http, $q, types) {
return deferred.promise; return deferred.promise;
} }
function getCustomer(customerId) { function getCustomer(customerId, config) {
var deferred = $q.defer(); var deferred = $q.defer();
var url = '/api/customer/' + customerId; var url = '/api/customer/' + customerId;
$http.get(url, null).then(function success(response) { $http.get(url, config).then(function success(response) {
deferred.resolve(response.data); deferred.resolve(response.data);
}, function fail(response) { }, function fail(response) {
deferred.reject(response.data); deferred.reject(response.data);

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

@ -43,7 +43,7 @@ function DashboardService($rootScope, $http, $q, $location, customerService) {
return service; return service;
function getTenantDashboardsByTenantId(tenantId, pageLink) { function getTenantDashboardsByTenantId(tenantId, pageLink, config) {
var deferred = $q.defer(); var deferred = $q.defer();
var url = '/api/tenant/' + tenantId + '/dashboards?limit=' + pageLink.limit; var url = '/api/tenant/' + tenantId + '/dashboards?limit=' + pageLink.limit;
if (angular.isDefined(pageLink.textSearch)) { if (angular.isDefined(pageLink.textSearch)) {
@ -55,7 +55,7 @@ function DashboardService($rootScope, $http, $q, $location, customerService) {
if (angular.isDefined(pageLink.textOffset)) { if (angular.isDefined(pageLink.textOffset)) {
url += '&textOffset=' + pageLink.textOffset; url += '&textOffset=' + pageLink.textOffset;
} }
$http.get(url, null).then(function success(response) { $http.get(url, config).then(function success(response) {
deferred.resolve(response.data); deferred.resolve(response.data);
}, function fail() { }, function fail() {
deferred.reject(); deferred.reject();
@ -63,7 +63,7 @@ function DashboardService($rootScope, $http, $q, $location, customerService) {
return deferred.promise; return deferred.promise;
} }
function getTenantDashboards(pageLink, applyCustomersInfo) { function getTenantDashboards(pageLink, applyCustomersInfo, config) {
var deferred = $q.defer(); var deferred = $q.defer();
var url = '/api/tenant/dashboards?limit=' + pageLink.limit; var url = '/api/tenant/dashboards?limit=' + pageLink.limit;
if (angular.isDefined(pageLink.textSearch)) { if (angular.isDefined(pageLink.textSearch)) {
@ -75,7 +75,7 @@ function DashboardService($rootScope, $http, $q, $location, customerService) {
if (angular.isDefined(pageLink.textOffset)) { if (angular.isDefined(pageLink.textOffset)) {
url += '&textOffset=' + pageLink.textOffset; url += '&textOffset=' + pageLink.textOffset;
} }
$http.get(url, null).then(function success(response) { $http.get(url, config).then(function success(response) {
if (applyCustomersInfo) { if (applyCustomersInfo) {
customerService.applyAssignedCustomersInfo(response.data.data).then( customerService.applyAssignedCustomersInfo(response.data.data).then(
function success(data) { function success(data) {
@ -95,7 +95,7 @@ function DashboardService($rootScope, $http, $q, $location, customerService) {
return deferred.promise; return deferred.promise;
} }
function getCustomerDashboards(customerId, pageLink, applyCustomersInfo) { function getCustomerDashboards(customerId, pageLink, applyCustomersInfo, config) {
var deferred = $q.defer(); var deferred = $q.defer();
var url = '/api/customer/' + customerId + '/dashboards?limit=' + pageLink.limit; var url = '/api/customer/' + customerId + '/dashboards?limit=' + pageLink.limit;
if (angular.isDefined(pageLink.textSearch)) { if (angular.isDefined(pageLink.textSearch)) {
@ -107,7 +107,7 @@ function DashboardService($rootScope, $http, $q, $location, customerService) {
if (angular.isDefined(pageLink.textOffset)) { if (angular.isDefined(pageLink.textOffset)) {
url += '&textOffset=' + pageLink.textOffset; url += '&textOffset=' + pageLink.textOffset;
} }
$http.get(url, null).then(function success(response) { $http.get(url, config).then(function success(response) {
if (applyCustomersInfo) { if (applyCustomersInfo) {
customerService.applyAssignedCustomerInfo(response.data.data, customerId).then( customerService.applyAssignedCustomerInfo(response.data.data, customerId).then(
function success(data) { function success(data) {
@ -158,10 +158,10 @@ function DashboardService($rootScope, $http, $q, $location, customerService) {
return deferred.promise; return deferred.promise;
} }
function getDashboardInfo(dashboardId) { function getDashboardInfo(dashboardId, config) {
var deferred = $q.defer(); var deferred = $q.defer();
var url = '/api/dashboard/info/' + dashboardId; var url = '/api/dashboard/info/' + dashboardId;
$http.get(url, null).then(function success(response) { $http.get(url, config).then(function success(response) {
deferred.resolve(response.data); deferred.resolve(response.data);
}, function fail() { }, function fail() {
deferred.reject(); deferred.reject();

4
ui/src/app/api/device.service.js

@ -293,10 +293,10 @@ function DeviceService($http, $q, attributeService, customerService, types) {
return deferred.promise; return deferred.promise;
} }
function getDeviceTypes() { function getDeviceTypes(config) {
var deferred = $q.defer(); var deferred = $q.defer();
var url = '/api/device/types'; var url = '/api/device/types';
$http.get(url).then(function success(response) { $http.get(url, config).then(function success(response) {
deferred.resolve(response.data); deferred.resolve(response.data);
}, function fail() { }, function fail() {
deferred.reject(); deferred.reject();

4
ui/src/app/api/entity-relation.service.js

@ -175,10 +175,10 @@ function EntityRelationService($http, $q) {
return deferred.promise; return deferred.promise;
} }
function findInfoByQuery(query) { function findInfoByQuery(query, config) {
var deferred = $q.defer(); var deferred = $q.defer();
var url = '/api/relations/info'; var url = '/api/relations/info';
$http.post(url, query).then(function success(response) { $http.post(url, query, config).then(function success(response) {
deferred.resolve(response.data); deferred.resolve(response.data);
}, function fail() { }, function fail() {
deferred.reject(); deferred.reject();

76
ui/src/app/api/entity.service.js

@ -56,22 +56,22 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
promise = assetService.getAsset(entityId, true, config); promise = assetService.getAsset(entityId, true, config);
break; break;
case types.entityType.tenant: case types.entityType.tenant:
promise = tenantService.getTenant(entityId); promise = tenantService.getTenant(entityId, config);
break; break;
case types.entityType.customer: case types.entityType.customer:
promise = customerService.getCustomer(entityId); promise = customerService.getCustomer(entityId, config);
break; break;
case types.entityType.rule: case types.entityType.rule:
promise = ruleService.getRule(entityId); promise = ruleService.getRule(entityId, config);
break; break;
case types.entityType.plugin: case types.entityType.plugin:
promise = pluginService.getPlugin(entityId); promise = pluginService.getPlugin(entityId, config);
break; break;
case types.entityType.dashboard: case types.entityType.dashboard:
promise = dashboardService.getDashboardInfo(entityId); promise = dashboardService.getDashboardInfo(entityId, config);
break; break;
case types.entityType.user: case types.entityType.user:
promise = userService.getUser(entityId); promise = userService.getUser(entityId, true, config);
break; break;
case types.entityType.alarm: case types.entityType.alarm:
$log.error('Get Alarm Entity is not implemented!'); $log.error('Get Alarm Entity is not implemented!');
@ -136,22 +136,28 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
promise = assetService.getAssets(entityIds, config); promise = assetService.getAssets(entityIds, config);
break; break;
case types.entityType.tenant: case types.entityType.tenant:
promise = getEntitiesByIdsPromise(tenantService.getTenant, entityIds); promise = getEntitiesByIdsPromise(
(id) => tenantService.getTenant(id, config), entityIds);
break; break;
case types.entityType.customer: case types.entityType.customer:
promise = getEntitiesByIdsPromise(customerService.getCustomer, entityIds); promise = getEntitiesByIdsPromise(
(id) => customerService.getCustomer(id, config), entityIds);
break; break;
case types.entityType.rule: case types.entityType.rule:
promise = getEntitiesByIdsPromise(ruleService.getRule, entityIds); promise = getEntitiesByIdsPromise(
(id) => ruleService.getRule(id, config), entityIds);
break; break;
case types.entityType.plugin: case types.entityType.plugin:
promise = getEntitiesByIdsPromise(pluginService.getPlugin, entityIds); promise = getEntitiesByIdsPromise(
(id) => pluginService.getPlugin(id, config), entityIds);
break; break;
case types.entityType.dashboard: case types.entityType.dashboard:
promise = getEntitiesByIdsPromise(dashboardService.getDashboardInfo, entityIds); promise = getEntitiesByIdsPromise(
(id) => dashboardService.getDashboardInfo(id, config), entityIds);
break; break;
case types.entityType.user: case types.entityType.user:
promise = getEntitiesByIdsPromise(userService.getUser, entityIds); promise = getEntitiesByIdsPromise(
(id) => userService.getUser(id, true, config), entityIds);
break; break;
case types.entityType.alarm: case types.entityType.alarm:
$log.error('Get Alarm Entity is not implemented!'); $log.error('Get Alarm Entity is not implemented!');
@ -178,11 +184,11 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
return deferred.promise; return deferred.promise;
} }
function getSingleTenantByPageLinkPromise(pageLink) { function getSingleTenantByPageLinkPromise(pageLink, config) {
var user = userService.getCurrentUser(); var user = userService.getCurrentUser();
var tenantId = user.tenantId; var tenantId = user.tenantId;
var deferred = $q.defer(); var deferred = $q.defer();
tenantService.getTenant(tenantId).then( tenantService.getTenant(tenantId, config).then(
function success(tenant) { function success(tenant) {
var tenantName = tenant.name; var tenantName = tenant.name;
var result = { var result = {
@ -202,11 +208,11 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
return deferred.promise; return deferred.promise;
} }
function getSingleCustomerByPageLinkPromise(pageLink) { function getSingleCustomerByPageLinkPromise(pageLink, config) {
var user = userService.getCurrentUser(); var user = userService.getCurrentUser();
var customerId = user.customerId; var customerId = user.customerId;
var deferred = $q.defer(); var deferred = $q.defer();
customerService.getCustomer(customerId).then( customerService.getCustomer(customerId, config).then(
function success(customer) { function success(customer) {
var customerName = customer.name; var customerName = customer.name;
var result = { var result = {
@ -247,29 +253,29 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
break; break;
case types.entityType.tenant: case types.entityType.tenant:
if (user.authority === 'TENANT_ADMIN') { if (user.authority === 'TENANT_ADMIN') {
promise = getSingleTenantByPageLinkPromise(pageLink); promise = getSingleTenantByPageLinkPromise(pageLink, config);
} else { } else {
promise = tenantService.getTenants(pageLink); promise = tenantService.getTenants(pageLink, config);
} }
break; break;
case types.entityType.customer: case types.entityType.customer:
if (user.authority === 'CUSTOMER_USER') { if (user.authority === 'CUSTOMER_USER') {
promise = getSingleCustomerByPageLinkPromise(pageLink); promise = getSingleCustomerByPageLinkPromise(pageLink, config);
} else { } else {
promise = customerService.getCustomers(pageLink); promise = customerService.getCustomers(pageLink, config);
} }
break; break;
case types.entityType.rule: case types.entityType.rule:
promise = ruleService.getAllRules(pageLink); promise = ruleService.getAllRules(pageLink, config);
break; break;
case types.entityType.plugin: case types.entityType.plugin:
promise = pluginService.getAllPlugins(pageLink); promise = pluginService.getAllPlugins(pageLink, config);
break; break;
case types.entityType.dashboard: case types.entityType.dashboard:
if (user.authority === 'CUSTOMER_USER') { if (user.authority === 'CUSTOMER_USER') {
promise = dashboardService.getCustomerDashboards(customerId, pageLink, false); promise = dashboardService.getCustomerDashboards(customerId, pageLink, false, config);
} else { } else {
promise = dashboardService.getTenantDashboards(pageLink, false); promise = dashboardService.getTenantDashboards(pageLink, false, config);
} }
break; break;
case types.entityType.user: case types.entityType.user:
@ -426,7 +432,7 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
var stateEntityId = getStateEntityId(filter, stateParams); var stateEntityId = getStateEntityId(filter, stateParams);
switch (filter.type) { switch (filter.type) {
case types.aliasFilterType.singleEntity.value: case types.aliasFilterType.singleEntity.value:
getEntity(filter.singleEntity.entityType, filter.singleEntity.id).then( getEntity(filter.singleEntity.entityType, filter.singleEntity.id, {ignoreLoading: true}).then(
function success(entity) { function success(entity) {
result.entities = entitiesToEntitiesInfo([entity]); result.entities = entitiesToEntitiesInfo([entity]);
deferred.resolve(result); deferred.resolve(result);
@ -437,7 +443,7 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
); );
break; break;
case types.aliasFilterType.entityList.value: case types.aliasFilterType.entityList.value:
getEntities(filter.entityType, filter.entityList).then( getEntities(filter.entityType, filter.entityList, {ignoreLoading: true}).then(
function success(entities) { function success(entities) {
if (entities && entities.length || !failOnEmpty) { if (entities && entities.length || !failOnEmpty) {
result.entities = entitiesToEntitiesInfo(entities); result.entities = entitiesToEntitiesInfo(entities);
@ -452,7 +458,7 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
); );
break; break;
case types.aliasFilterType.entityName.value: case types.aliasFilterType.entityName.value:
getEntitiesByNameFilter(filter.entityType, filter.entityNameFilter, maxItems).then( getEntitiesByNameFilter(filter.entityType, filter.entityNameFilter, maxItems, {ignoreLoading: true}).then(
function success(entities) { function success(entities) {
if (entities && entities.length || !failOnEmpty) { if (entities && entities.length || !failOnEmpty) {
result.entities = entitiesToEntitiesInfo(entities); result.entities = entitiesToEntitiesInfo(entities);
@ -469,7 +475,7 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
case types.aliasFilterType.stateEntity.value: case types.aliasFilterType.stateEntity.value:
result.stateEntity = true; result.stateEntity = true;
if (stateEntityId) { if (stateEntityId) {
getEntity(stateEntityId.entityType, stateEntityId.id).then( getEntity(stateEntityId.entityType, stateEntityId.id, {ignoreLoading: true}).then(
function success(entity) { function success(entity) {
result.entities = entitiesToEntitiesInfo([entity]); result.entities = entitiesToEntitiesInfo([entity]);
deferred.resolve(result); deferred.resolve(result);
@ -483,7 +489,7 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
} }
break; break;
case types.aliasFilterType.assetType.value: case types.aliasFilterType.assetType.value:
getEntitiesByNameFilter(types.entityType.asset, filter.assetNameFilter, maxItems, null, filter.assetType).then( getEntitiesByNameFilter(types.entityType.asset, filter.assetNameFilter, maxItems, {ignoreLoading: true}, filter.assetType).then(
function success(entities) { function success(entities) {
if (entities && entities.length || !failOnEmpty) { if (entities && entities.length || !failOnEmpty) {
result.entities = entitiesToEntitiesInfo(entities); result.entities = entitiesToEntitiesInfo(entities);
@ -498,7 +504,7 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
); );
break; break;
case types.aliasFilterType.deviceType.value: case types.aliasFilterType.deviceType.value:
getEntitiesByNameFilter(types.entityType.device, filter.deviceNameFilter, maxItems, null, filter.deviceType).then( getEntitiesByNameFilter(types.entityType.device, filter.deviceNameFilter, maxItems, {ignoreLoading: true}, filter.deviceType).then(
function success(entities) { function success(entities) {
if (entities && entities.length || !failOnEmpty) { if (entities && entities.length || !failOnEmpty) {
result.entities = entitiesToEntitiesInfo(entities); result.entities = entitiesToEntitiesInfo(entities);
@ -533,7 +539,7 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
filters: filter.filters filters: filter.filters
}; };
searchQuery.parameters.maxLevel = filter.maxLevel && filter.maxLevel > 0 ? filter.maxLevel : -1; searchQuery.parameters.maxLevel = filter.maxLevel && filter.maxLevel > 0 ? filter.maxLevel : -1;
entityRelationService.findInfoByQuery(searchQuery).then( entityRelationService.findInfoByQuery(searchQuery, {ignoreLoading: true}).then(
function success(allRelations) { function success(allRelations) {
if (allRelations && allRelations.length || !failOnEmpty) { if (allRelations && allRelations.length || !failOnEmpty) {
if (angular.isDefined(maxItems) && maxItems > 0 && allRelations) { if (angular.isDefined(maxItems) && maxItems > 0 && allRelations) {
@ -577,10 +583,10 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
var findByQueryPromise; var findByQueryPromise;
if (filter.type == types.aliasFilterType.assetSearchQuery.value) { if (filter.type == types.aliasFilterType.assetSearchQuery.value) {
searchQuery.assetTypes = filter.assetTypes; searchQuery.assetTypes = filter.assetTypes;
findByQueryPromise = assetService.findByQuery(searchQuery, false); findByQueryPromise = assetService.findByQuery(searchQuery, false, {ignoreLoading: true});
} else if (filter.type == types.aliasFilterType.deviceSearchQuery.value) { } else if (filter.type == types.aliasFilterType.deviceSearchQuery.value) {
searchQuery.deviceTypes = filter.deviceTypes; searchQuery.deviceTypes = filter.deviceTypes;
findByQueryPromise = deviceService.findByQuery(searchQuery, false); findByQueryPromise = deviceService.findByQuery(searchQuery, false, {ignoreLoading: true});
} }
findByQueryPromise.then( findByQueryPromise.then(
function success(entities) { function success(entities) {
@ -762,7 +768,7 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
return deferred.promise; return deferred.promise;
} }
function getEntityKeys(entityType, entityId, query, type) { function getEntityKeys(entityType, entityId, query, type, config) {
var deferred = $q.defer(); var deferred = $q.defer();
var url = '/api/plugins/telemetry/' + entityType + '/' + entityId + '/keys/'; var url = '/api/plugins/telemetry/' + entityType + '/' + entityId + '/keys/';
if (type === types.dataKeyType.timeseries) { if (type === types.dataKeyType.timeseries) {
@ -770,7 +776,7 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
} else if (type === types.dataKeyType.attribute) { } else if (type === types.dataKeyType.attribute) {
url += 'attributes'; url += 'attributes';
} }
$http.get(url, null).then(function success(response) { $http.get(url, config).then(function success(response) {
var result = []; var result = [];
if (response.data) { if (response.data) {
if (query) { if (query) {

24
ui/src/app/api/plugin.service.js

@ -50,11 +50,11 @@ function PluginService($http, $q, $rootScope, $filter, componentDescriptorServic
tenantPlugins = undefined; tenantPlugins = undefined;
} }
function loadPluginsCache() { function loadPluginsCache(config) {
var deferred = $q.defer(); var deferred = $q.defer();
if (!allPlugins) { if (!allPlugins) {
var url = '/api/plugins'; var url = '/api/plugins';
$http.get(url, null).then(function success(response) { $http.get(url, config).then(function success(response) {
componentDescriptorService.getComponentDescriptorsByType(types.componentType.plugin).then( componentDescriptorService.getComponentDescriptorsByType(types.componentType.plugin).then(
function success(pluginComponents) { function success(pluginComponents) {
allPlugins = response.data; allPlugins = response.data;
@ -93,9 +93,9 @@ function PluginService($http, $q, $rootScope, $filter, componentDescriptorServic
return deferred.promise; return deferred.promise;
} }
function getSystemPlugins(pageLink) { function getSystemPlugins(pageLink, config) {
var deferred = $q.defer(); var deferred = $q.defer();
loadPluginsCache().then( loadPluginsCache(config).then(
function success() { function success() {
utils.filterSearchTextEntities(systemPlugins, 'name', pageLink, deferred); utils.filterSearchTextEntities(systemPlugins, 'name', pageLink, deferred);
}, },
@ -106,9 +106,9 @@ function PluginService($http, $q, $rootScope, $filter, componentDescriptorServic
return deferred.promise; return deferred.promise;
} }
function getTenantPlugins(pageLink) { function getTenantPlugins(pageLink, config) {
var deferred = $q.defer(); var deferred = $q.defer();
loadPluginsCache().then( loadPluginsCache(config).then(
function success() { function success() {
utils.filterSearchTextEntities(tenantPlugins, 'name', pageLink, deferred); utils.filterSearchTextEntities(tenantPlugins, 'name', pageLink, deferred);
}, },
@ -119,9 +119,9 @@ function PluginService($http, $q, $rootScope, $filter, componentDescriptorServic
return deferred.promise; return deferred.promise;
} }
function getAllActionPlugins(pageLink) { function getAllActionPlugins(pageLink, config) {
var deferred = $q.defer(); var deferred = $q.defer();
loadPluginsCache().then( loadPluginsCache(config).then(
function success() { function success() {
utils.filterSearchTextEntities(allActionPlugins, 'name', pageLink, deferred); utils.filterSearchTextEntities(allActionPlugins, 'name', pageLink, deferred);
}, },
@ -132,9 +132,9 @@ function PluginService($http, $q, $rootScope, $filter, componentDescriptorServic
return deferred.promise; return deferred.promise;
} }
function getAllPlugins(pageLink) { function getAllPlugins(pageLink, config) {
var deferred = $q.defer(); var deferred = $q.defer();
loadPluginsCache().then( loadPluginsCache(config).then(
function success() { function success() {
utils.filterSearchTextEntities(allPlugins, 'name', pageLink, deferred); utils.filterSearchTextEntities(allPlugins, 'name', pageLink, deferred);
}, },
@ -156,10 +156,10 @@ function PluginService($http, $q, $rootScope, $filter, componentDescriptorServic
return deferred.promise; return deferred.promise;
} }
function getPlugin(pluginId) { function getPlugin(pluginId, config) {
var deferred = $q.defer(); var deferred = $q.defer();
var url = '/api/plugin/' + pluginId; var url = '/api/plugin/' + pluginId;
$http.get(url, null).then(function success(response) { $http.get(url, config).then(function success(response) {
deferred.resolve(response.data); deferred.resolve(response.data);
}, function fail(response) { }, function fail(response) {
deferred.reject(response.data); deferred.reject(response.data);

12
ui/src/app/api/rule.service.js

@ -47,11 +47,11 @@ function RuleService($http, $q, $rootScope, $filter, types, utils) {
tenantRules = undefined; tenantRules = undefined;
} }
function loadRulesCache() { function loadRulesCache(config) {
var deferred = $q.defer(); var deferred = $q.defer();
if (!allRules) { if (!allRules) {
var url = '/api/rules'; var url = '/api/rules';
$http.get(url, null).then(function success(response) { $http.get(url, config).then(function success(response) {
allRules = response.data; allRules = response.data;
systemRules = []; systemRules = [];
tenantRules = []; tenantRules = [];
@ -100,9 +100,9 @@ function RuleService($http, $q, $rootScope, $filter, types, utils) {
return deferred.promise; return deferred.promise;
} }
function getAllRules(pageLink) { function getAllRules(pageLink, config) {
var deferred = $q.defer(); var deferred = $q.defer();
loadRulesCache().then( loadRulesCache(config).then(
function success() { function success() {
utils.filterSearchTextEntities(allRules, 'name', pageLink, deferred); utils.filterSearchTextEntities(allRules, 'name', pageLink, deferred);
}, },
@ -124,10 +124,10 @@ function RuleService($http, $q, $rootScope, $filter, types, utils) {
return deferred.promise; return deferred.promise;
} }
function getRule(ruleId) { function getRule(ruleId, config) {
var deferred = $q.defer(); var deferred = $q.defer();
var url = '/api/rule/' + ruleId; var url = '/api/rule/' + ruleId;
$http.get(url, null).then(function success(response) { $http.get(url, config).then(function success(response) {
deferred.resolve(response.data); deferred.resolve(response.data);
}, function fail(response) { }, function fail(response) {
deferred.reject(response.data); deferred.reject(response.data);

37
ui/src/app/api/telemetry-websocket.service.js

@ -23,6 +23,8 @@ export default angular.module('thingsboard.api.telemetryWebsocket', [thingsboard
const RECONNECT_INTERVAL = 2000; const RECONNECT_INTERVAL = 2000;
const WS_IDLE_TIMEOUT = 90000; const WS_IDLE_TIMEOUT = 90000;
const MAX_PUBLISH_COMMANDS = 10;
/*@ngInject*/ /*@ngInject*/
function TelemetryWebsocketService($rootScope, $websocket, $timeout, $window, types, userService) { function TelemetryWebsocketService($rootScope, $websocket, $timeout, $window, types, userService) {
@ -75,19 +77,40 @@ function TelemetryWebsocketService($rootScope, $websocket, $timeout, $window, ty
return service; return service;
function publishCommands () { function publishCommands () {
if (isOpened && (cmdsWrapper.tsSubCmds.length > 0 || while(isOpened && hasCommands()) {
cmdsWrapper.historyCmds.length > 0 || dataStream.send(preparePublishCommands()).then(function () {
cmdsWrapper.attrSubCmds.length > 0)) {
dataStream.send(angular.copy(cmdsWrapper)).then(function () {
checkToClose(); checkToClose();
}); });
cmdsWrapper.tsSubCmds = [];
cmdsWrapper.historyCmds = [];
cmdsWrapper.attrSubCmds = [];
} }
tryOpenSocket(); tryOpenSocket();
} }
function hasCommands() {
return cmdsWrapper.tsSubCmds.length > 0 ||
cmdsWrapper.historyCmds.length > 0 ||
cmdsWrapper.attrSubCmds.length > 0;
}
function preparePublishCommands() {
var preparedWrapper = {};
var leftCount = MAX_PUBLISH_COMMANDS;
preparedWrapper.tsSubCmds = popCmds(cmdsWrapper.tsSubCmds, leftCount);
leftCount -= preparedWrapper.tsSubCmds.length;
preparedWrapper.historyCmds = popCmds(cmdsWrapper.historyCmds, leftCount);
leftCount -= preparedWrapper.historyCmds.length;
preparedWrapper.attrSubCmds = popCmds(cmdsWrapper.attrSubCmds, leftCount);
return preparedWrapper;
}
function popCmds(cmds, leftCount) {
var toPublish = Math.min(cmds.length, leftCount);
if (toPublish > 0) {
return cmds.splice(0, toPublish);
} else {
return [];
}
}
function onError (/*message*/) { function onError (/*message*/) {
isOpening = false; isOpening = false;
} }

8
ui/src/app/api/tenant.service.js

@ -29,7 +29,7 @@ function TenantService($http, $q) {
return service; return service;
function getTenants (pageLink) { function getTenants (pageLink, config) {
var deferred = $q.defer(); var deferred = $q.defer();
var url = '/api/tenants?limit=' + pageLink.limit; var url = '/api/tenants?limit=' + pageLink.limit;
if (angular.isDefined(pageLink.textSearch)) { if (angular.isDefined(pageLink.textSearch)) {
@ -41,7 +41,7 @@ function TenantService($http, $q) {
if (angular.isDefined(pageLink.textOffset)) { if (angular.isDefined(pageLink.textOffset)) {
url += '&textOffset=' + pageLink.textOffset; url += '&textOffset=' + pageLink.textOffset;
} }
$http.get(url, null).then(function success(response) { $http.get(url, config).then(function success(response) {
deferred.resolve(response.data); deferred.resolve(response.data);
}, function fail() { }, function fail() {
deferred.reject(); deferred.reject();
@ -49,10 +49,10 @@ function TenantService($http, $q) {
return deferred.promise; return deferred.promise;
} }
function getTenant (tenantId) { function getTenant (tenantId, config) {
var deferred = $q.defer(); var deferred = $q.defer();
var url = '/api/tenant/' + tenantId; var url = '/api/tenant/' + tenantId;
$http.get(url, null).then(function success(response) { $http.get(url, config).then(function success(response) {
deferred.resolve(response.data); deferred.resolve(response.data);
}, function fail(response) { }, function fail(response) {
deferred.reject(response.data); deferred.reject(response.data);

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

@ -421,10 +421,14 @@ function UserService($http, $q, $rootScope, adminService, dashboardService, logi
return deferred.promise; return deferred.promise;
} }
function getUser(userId, ignoreErrors) { function getUser(userId, ignoreErrors, config) {
var deferred = $q.defer(); var deferred = $q.defer();
var url = '/api/user/' + userId; var url = '/api/user/' + userId;
$http.get(url, { ignoreErrors: ignoreErrors }).then(function success(response) { if (!config) {
config = {};
}
config = Object.assign(config, { ignoreErrors: ignoreErrors });
$http.get(url, config).then(function success(response) {
deferred.resolve(response.data); deferred.resolve(response.data);
}, function fail() { }, function fail() {
deferred.reject(); deferred.reject();

16
ui/src/app/api/widget.service.js

@ -298,11 +298,11 @@ function WidgetService($rootScope, $http, $q, $filter, $ocLazyLoad, $window, $tr
tenantWidgetsBundles = undefined; tenantWidgetsBundles = undefined;
} }
function loadWidgetsBundleCache() { function loadWidgetsBundleCache(config) {
var deferred = $q.defer(); var deferred = $q.defer();
if (!allWidgetsBundles) { if (!allWidgetsBundles) {
var url = '/api/widgetsBundles'; var url = '/api/widgetsBundles';
$http.get(url, null).then(function success(response) { $http.get(url, config).then(function success(response) {
allWidgetsBundles = response.data; allWidgetsBundles = response.data;
systemWidgetsBundles = []; systemWidgetsBundles = [];
tenantWidgetsBundles = []; tenantWidgetsBundles = [];
@ -326,9 +326,9 @@ function WidgetService($rootScope, $http, $q, $filter, $ocLazyLoad, $window, $tr
} }
function getSystemWidgetsBundles() { function getSystemWidgetsBundles(config) {
var deferred = $q.defer(); var deferred = $q.defer();
loadWidgetsBundleCache().then( loadWidgetsBundleCache(config).then(
function success() { function success() {
deferred.resolve(systemWidgetsBundles); deferred.resolve(systemWidgetsBundles);
}, },
@ -339,9 +339,9 @@ function WidgetService($rootScope, $http, $q, $filter, $ocLazyLoad, $window, $tr
return deferred.promise; return deferred.promise;
} }
function getTenantWidgetsBundles() { function getTenantWidgetsBundles(config) {
var deferred = $q.defer(); var deferred = $q.defer();
loadWidgetsBundleCache().then( loadWidgetsBundleCache(config).then(
function success() { function success() {
deferred.resolve(tenantWidgetsBundles); deferred.resolve(tenantWidgetsBundles);
}, },
@ -352,9 +352,9 @@ function WidgetService($rootScope, $http, $q, $filter, $ocLazyLoad, $window, $tr
return deferred.promise; return deferred.promise;
} }
function getAllWidgetsBundles() { function getAllWidgetsBundles(config) {
var deferred = $q.defer(); var deferred = $q.defer();
loadWidgetsBundleCache().then( loadWidgetsBundleCache(config).then(
function success() { function success() {
deferred.resolve(allWidgetsBundles); deferred.resolve(allWidgetsBundles);
}, },

8
ui/src/app/asset/add-asset.tpl.html

@ -27,8 +27,8 @@
</md-button> </md-button>
</div> </div>
</md-toolbar> </md-toolbar>
<md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!loading" ng-show="loading"></md-progress-linear> <md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
<span style="min-height: 5px;" flex="" ng-show="!loading"></span> <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
<md-dialog-content> <md-dialog-content>
<div class="md-dialog-content"> <div class="md-dialog-content">
<tb-asset asset="vm.item" is-edit="true" the-form="theForm"></tb-asset> <tb-asset asset="vm.item" is-edit="true" the-form="theForm"></tb-asset>
@ -36,10 +36,10 @@
</md-dialog-content> </md-dialog-content>
<md-dialog-actions layout="row"> <md-dialog-actions layout="row">
<span flex></span> <span flex></span>
<md-button ng-disabled="loading || theForm.$invalid || !theForm.$dirty" type="submit" class="md-raised md-primary"> <md-button ng-disabled="$root.loading || theForm.$invalid || !theForm.$dirty" type="submit" class="md-raised md-primary">
{{ 'action.add' | translate }} {{ 'action.add' | translate }}
</md-button> </md-button>
<md-button ng-disabled="loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | translate }}</md-button> <md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | translate }}</md-button>
</md-dialog-actions> </md-dialog-actions>
</form> </form>
</md-dialog> </md-dialog>

8
ui/src/app/asset/add-assets-to-customer.tpl.html

@ -26,8 +26,8 @@
</md-button> </md-button>
</div> </div>
</md-toolbar> </md-toolbar>
<md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!loading" ng-show="loading"></md-progress-linear> <md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
<span style="min-height: 5px;" flex="" ng-show="!loading"></span> <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
<md-dialog-content> <md-dialog-content>
<div class="md-dialog-content"> <div class="md-dialog-content">
<fieldset> <fieldset>
@ -65,11 +65,11 @@
</md-dialog-content> </md-dialog-content>
<md-dialog-actions layout="row"> <md-dialog-actions layout="row">
<span flex></span> <span flex></span>
<md-button ng-disabled="loading || vm.assets.selectedCount == 0" type="submit" <md-button ng-disabled="$root.loading || vm.assets.selectedCount == 0" type="submit"
class="md-raised md-primary"> class="md-raised md-primary">
{{ 'action.assign' | translate }} {{ 'action.assign' | translate }}
</md-button> </md-button>
<md-button ng-disabled="loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | <md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' |
translate }} translate }}
</md-button> </md-button>
</md-dialog-actions> </md-dialog-actions>

4
ui/src/app/asset/asset-fieldset.tpl.html

@ -48,7 +48,7 @@
ng-show="!isEdit && isPublic && (assetScope === 'customer' || assetScope === 'tenant')"> ng-show="!isEdit && isPublic && (assetScope === 'customer' || assetScope === 'tenant')">
{{ 'asset.asset-public' | translate }} {{ 'asset.asset-public' | translate }}
</div> </div>
<fieldset ng-disabled="loading || !isEdit"> <fieldset ng-disabled="$root.loading || !isEdit">
<md-input-container class="md-block"> <md-input-container class="md-block">
<label translate>asset.name</label> <label translate>asset.name</label>
<input required name="name" ng-model="asset.name"> <input required name="name" ng-model="asset.name">
@ -57,7 +57,7 @@
</div> </div>
</md-input-container> </md-input-container>
<tb-entity-subtype-autocomplete <tb-entity-subtype-autocomplete
ng-disabled="loading || !isEdit" ng-disabled="$root.loading || !isEdit"
tb-required="true" tb-required="true"
the-form="theForm" the-form="theForm"
ng-model="asset.type" ng-model="asset.type"

8
ui/src/app/asset/assign-to-customer.tpl.html

@ -26,8 +26,8 @@
</md-button> </md-button>
</div> </div>
</md-toolbar> </md-toolbar>
<md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!loading" ng-show="loading"></md-progress-linear> <md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
<span style="min-height: 5px;" flex="" ng-show="!loading"></span> <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
<md-dialog-content> <md-dialog-content>
<div class="md-dialog-content"> <div class="md-dialog-content">
<fieldset> <fieldset>
@ -65,10 +65,10 @@
</md-dialog-content> </md-dialog-content>
<md-dialog-actions layout="row"> <md-dialog-actions layout="row">
<span flex></span> <span flex></span>
<md-button ng-disabled="loading || vm.customers.selection==null" type="submit" class="md-raised md-primary"> <md-button ng-disabled="$root.loading || vm.customers.selection==null" type="submit" class="md-raised md-primary">
{{ 'action.assign' | translate }} {{ 'action.assign' | translate }}
</md-button> </md-button>
<md-button ng-disabled="loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | <md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' |
translate }} translate }}
</md-button> </md-button>
</md-dialog-actions> </md-dialog-actions>

35
ui/src/app/common/utils.service.js

@ -134,6 +134,8 @@ function Utils($mdColorPalette, $rootScope, $window, $translate, $q, $timeout, t
defaultAlarmDataKeys.push(dataKey); defaultAlarmDataKeys.push(dataKey);
} }
var imageAspectMap = {};
var service = { var service = {
getDefaultDatasource: getDefaultDatasource, getDefaultDatasource: getDefaultDatasource,
generateObjectFromJsonSchema: generateObjectFromJsonSchema, generateObjectFromJsonSchema: generateObjectFromJsonSchema,
@ -159,7 +161,8 @@ function Utils($mdColorPalette, $rootScope, $window, $translate, $q, $timeout, t
insertVariable: insertVariable, insertVariable: insertVariable,
customTranslation: customTranslation, customTranslation: customTranslation,
objToBase64: objToBase64, objToBase64: objToBase64,
base64toObj: base64toObj base64toObj: base64toObj,
loadImageAspect: loadImageAspect
} }
return service; return service;
@ -543,4 +546,34 @@ function Utils($mdColorPalette, $rootScope, $window, $translate, $q, $timeout, t
return obj; return obj;
} }
function loadImageAspect(imageUrl) {
var deferred = $q.defer();
if (imageUrl && imageUrl.length) {
var urlHashCode = hashCode(imageUrl);
var aspect = imageAspectMap[urlHashCode];
if (angular.isUndefined(aspect)) {
var testImage = document.createElement('img'); // eslint-disable-line
testImage.style.visibility = 'hidden';
testImage.onload = function() {
aspect = testImage.width / testImage.height;
document.body.removeChild(testImage); //eslint-disable-line
imageAspectMap[urlHashCode] = aspect;
deferred.resolve(aspect);
};
testImage.onerror = function() {
aspect = 0;
imageAspectMap[urlHashCode] = aspect;
deferred.resolve(aspect);
};
document.body.appendChild(testImage); //eslint-disable-line
testImage.src = imageUrl;
} else {
deferred.resolve(aspect);
}
} else {
deferred.resolve(0);
}
return deferred.promise;
}
} }

14
ui/src/app/component/component-dialog.tpl.html

@ -27,11 +27,11 @@
</md-button> </md-button>
</div> </div>
</md-toolbar> </md-toolbar>
<md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!loading" ng-show="loading"></md-progress-linear> <md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
<span style="min-height: 5px;" flex="" ng-show="!loading"></span> <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
<md-dialog-content> <md-dialog-content>
<div class="md-dialog-content tb-filter"> <div class="md-dialog-content tb-filter">
<fieldset ng-disabled="loading || vm.isReadOnly"> <fieldset ng-disabled="$root.loading || vm.isReadOnly">
<section flex layout="row"> <section flex layout="row">
<md-input-container flex class="md-block"> <md-input-container flex class="md-block">
<label translate>rule.component-name</label> <label translate>rule.component-name</label>
@ -42,7 +42,7 @@
</md-input-container> </md-input-container>
<md-input-container flex class="md-block"> <md-input-container flex class="md-block">
<label translate>rule.component-type</label> <label translate>rule.component-type</label>
<md-select required name="componentType" ng-model="vm.componentInfo.component.clazz" ng-disabled="loading || vm.isReadOnly"> <md-select required name="componentType" ng-model="vm.componentInfo.component.clazz" ng-disabled="$root.loading || vm.isReadOnly">
<md-option ng-repeat="componentDescriptor in vm.componentDescriptors" ng-value="componentDescriptor.clazz"> <md-option ng-repeat="componentDescriptor in vm.componentDescriptors" ng-value="componentDescriptor.clazz">
{{componentDescriptor.name}} {{componentDescriptor.name}}
</md-option> </md-option>
@ -57,7 +57,7 @@
<tb-json-form schema="vm.componentDescriptor.configurationDescriptor.schema" <tb-json-form schema="vm.componentDescriptor.configurationDescriptor.schema"
form="vm.componentDescriptor.configurationDescriptor.form" form="vm.componentDescriptor.configurationDescriptor.form"
model="vm.componentInfo.component.configuration" model="vm.componentInfo.component.configuration"
readonly="loading || vm.isReadOnly" readonly="$root.loading || vm.isReadOnly"
form-control="theForm"> form-control="theForm">
</tb-json-form> </tb-json-form>
</md-card-content> </md-card-content>
@ -67,11 +67,11 @@
</md-dialog-content> </md-dialog-content>
<md-dialog-actions layout="row"> <md-dialog-actions layout="row">
<span flex></span> <span flex></span>
<md-button ng-if="!vm.isReadOnly" ng-disabled="loading || theForm.$invalid || !theForm.$dirty" type="submit" <md-button ng-if="!vm.isReadOnly" ng-disabled="$root.loading || theForm.$invalid || !theForm.$dirty" type="submit"
class="md-raised md-primary"> class="md-raised md-primary">
{{ (vm.isAdd ? 'action.add' : 'action.save') | translate }} {{ (vm.isAdd ? 'action.add' : 'action.save') | translate }}
</md-button> </md-button>
<md-button ng-disabled="loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | <md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' |
translate }} translate }}
</md-button> </md-button>
</md-dialog-actions> </md-dialog-actions>

4
ui/src/app/component/component.tpl.html

@ -24,7 +24,7 @@
{{ componentTypeName }} {{ componentTypeName }}
</span> </span>
<span ng-if="readOnly" style="min-width: 40px; min-height: 40px; margin: 0 6px;"></br></span> <span ng-if="readOnly" style="min-width: 40px; min-height: 40px; margin: 0 6px;"></br></span>
<md-button ng-disabled="loading" class="md-icon-button md-primary" <md-button ng-disabled="$root.loading" class="md-icon-button md-primary"
style="min-width: 40px;" style="min-width: 40px;"
ng-click="openComponent($event)" ng-click="openComponent($event)"
aria-label="{{ (readOnly ? 'action.view' : 'action.edit') | translate }}"> aria-label="{{ (readOnly ? 'action.view' : 'action.edit') | translate }}">
@ -43,7 +43,7 @@
edit edit
</md-icon> </md-icon>
</md-button> </md-button>
<md-button ng-if="!readOnly" ng-disabled="loading" class="md-icon-button md-primary" <md-button ng-if="!readOnly" ng-disabled="$root.loading" class="md-icon-button md-primary"
style="min-width: 40px;" style="min-width: 40px;"
ng-click="onRemoveComponent({event: $event})" ng-click="onRemoveComponent({event: $event})"
aria-label="{{ 'action.remove' | translate }}"> aria-label="{{ 'action.remove' | translate }}">

6
ui/src/app/components/dashboard-autocomplete.directive.js

@ -48,19 +48,19 @@ function DashboardAutocomplete($compile, $templateCache, $q, dashboardService, u
var promise; var promise;
if (scope.dashboardsScope === 'customer' || userService.getAuthority() === 'CUSTOMER_USER') { if (scope.dashboardsScope === 'customer' || userService.getAuthority() === 'CUSTOMER_USER') {
if (scope.customerId) { if (scope.customerId) {
promise = dashboardService.getCustomerDashboards(scope.customerId, pageLink, false); promise = dashboardService.getCustomerDashboards(scope.customerId, pageLink, false, {ignoreLoading: true});
} else { } else {
promise = $q.when({data: []}); promise = $q.when({data: []});
} }
} else { } else {
if (userService.getAuthority() === 'SYS_ADMIN') { if (userService.getAuthority() === 'SYS_ADMIN') {
if (scope.tenantId) { if (scope.tenantId) {
promise = dashboardService.getTenantDashboardsByTenantId(scope.tenantId, pageLink); promise = dashboardService.getTenantDashboardsByTenantId(scope.tenantId, pageLink, {ignoreLoading: true});
} else { } else {
promise = $q.when({data: []}); promise = $q.when({data: []});
} }
} else { } else {
promise = dashboardService.getTenantDashboards(pageLink, false); promise = dashboardService.getTenantDashboards(pageLink, false, {ignoreLoading: true});
} }
} }

4
ui/src/app/components/dashboard-select.directive.js

@ -48,12 +48,12 @@ function DashboardSelect($compile, $templateCache, $q, $mdMedia, $mdPanel, $docu
var promise; var promise;
if (scope.dashboardsScope === 'customer' || userService.getAuthority() === 'CUSTOMER_USER') { if (scope.dashboardsScope === 'customer' || userService.getAuthority() === 'CUSTOMER_USER') {
if (scope.customerId && scope.customerId != types.id.nullUid) { if (scope.customerId && scope.customerId != types.id.nullUid) {
promise = dashboardService.getCustomerDashboards(scope.customerId, pageLink, false); promise = dashboardService.getCustomerDashboards(scope.customerId, pageLink, false, {ignoreLoading: true});
} else { } else {
promise = $q.when({data: []}); promise = $q.when({data: []});
} }
} else { } else {
promise = dashboardService.getTenantDashboards(pageLink, false); promise = dashboardService.getTenantDashboards(pageLink, false, {ignoreLoading: true});
} }
promise.then(function success(result) { promise.then(function success(result) {

2
ui/src/app/components/datakey-config-dialog.controller.js

@ -43,7 +43,7 @@ function DatakeyConfigDialogController($scope, $mdDialog, $q, entityService, dat
function success(aliasInfo) { function success(aliasInfo) {
var entity = aliasInfo.currentEntity; var entity = aliasInfo.currentEntity;
if (entity) { if (entity) {
entityService.getEntityKeys(entity.entityType, entity.id, query, type).then( entityService.getEntityKeys(entity.entityType, entity.id, query, type, {ignoreLoading: true}).then(
function success(keys) { function success(keys) {
deferred.resolve(keys); deferred.resolve(keys);
}, },

8
ui/src/app/components/datakey-config-dialog.tpl.html

@ -26,8 +26,8 @@
</md-button> </md-button>
</div> </div>
</md-toolbar> </md-toolbar>
<md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!loading" ng-show="loading"></md-progress-linear> <md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
<span style="min-height: 5px;" flex="" ng-show="!loading"></span> <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
<md-dialog-content> <md-dialog-content>
<tb-datakey-config ng-model="vm.dataKey" <tb-datakey-config ng-model="vm.dataKey"
fetch-entity-keys="vm.fetchEntityKeys(entityAliasId, query, type)" fetch-entity-keys="vm.fetchEntityKeys(entityAliasId, query, type)"
@ -37,10 +37,10 @@
</md-dialog-content> </md-dialog-content>
<md-dialog-actions layout="row"> <md-dialog-actions layout="row">
<span flex></span> <span flex></span>
<md-button ng-disabled="loading || theForm.$invalid || !theForm.$dirty" type="submit" class="md-raised md-primary"> <md-button ng-disabled="$root.loading || theForm.$invalid || !theForm.$dirty" type="submit" class="md-raised md-primary">
{{ 'action.save' | translate }} {{ 'action.save' | translate }}
</md-button> </md-button>
<md-button ng-disabled="loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | translate }}</md-button> <md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | translate }}</md-button>
</md-dialog-actions> </md-dialog-actions>
</form> </form>
</md-dialog> </md-dialog>

4
ui/src/app/components/details-sidenav.tpl.html

@ -35,7 +35,7 @@
</div> </div>
<section ng-if="!isReadOnly" layout="row" layout-wrap <section ng-if="!isReadOnly" layout="row" layout-wrap
class="tb-header-buttons md-fab"> class="tb-header-buttons md-fab">
<md-button ng-show="isEdit" ng-disabled="loading || theForm.$invalid || !theForm.$dirty" <md-button ng-show="isEdit" ng-disabled="$root.loading || theForm.$invalid || !theForm.$dirty"
class="tb-btn-header md-accent md-hue-2 md-fab md-fab-bottom-right" class="tb-btn-header md-accent md-hue-2 md-fab md-fab-bottom-right"
aria-label="{{ 'action.apply' | translate }}" aria-label="{{ 'action.apply' | translate }}"
ng-click="detailsApply()"> ng-click="detailsApply()">
@ -44,7 +44,7 @@
</md-tooltip> </md-tooltip>
<ng-md-icon icon="done"></ng-md-icon> <ng-md-icon icon="done"></ng-md-icon>
</md-button> </md-button>
<md-button ng-disabled="loading || (isAlwaysEdit && !theForm.$dirty)" class="tb-btn-header md-accent md-hue-2 md-fab md-fab-bottom-right" <md-button ng-disabled="$root.loading || (isAlwaysEdit && !theForm.$dirty)" class="tb-btn-header md-accent md-hue-2 md-fab md-fab-bottom-right"
aria-label="{{ 'details.edit-mode' | translate }}" aria-label="{{ 'details.edit-mode' | translate }}"
ng-click="toggleDetailsEditMode()"> ng-click="toggleDetailsEditMode()">
<md-tooltip md-direction="top"> <md-tooltip md-direction="top">

14
ui/src/app/components/grid.tpl.html

@ -45,7 +45,7 @@
<tb-grid-card-content flex grid-ctl="vm" parent-ctl="vm.parentCtl" item-controller="vm.itemCardController" item-template="vm.itemCardTemplate" item="rowItem[n]"></tb-grid-card-content> <tb-grid-card-content flex grid-ctl="vm" parent-ctl="vm.parentCtl" item-controller="vm.itemCardController" item-template="vm.itemCardTemplate" item="rowItem[n]"></tb-grid-card-content>
</md-card-content> </md-card-content>
<md-card-actions layout="row" layout-align="end end"> <md-card-actions layout="row" layout-align="end end">
<md-button ng-if="action.isEnabled(rowItem[n])" ng-disabled="loading" class="md-icon-button md-primary" ng-repeat="action in vm.actionsList" <md-button ng-if="action.isEnabled(rowItem[n])" ng-disabled="$root.loading" class="md-icon-button md-primary" ng-repeat="action in vm.actionsList"
ng-click="action.onAction($event, rowItem[n])" aria-label="{{ action.name() }}"> ng-click="action.onAction($event, rowItem[n])" aria-label="{{ action.name() }}">
<md-tooltip md-direction="top"> <md-tooltip md-direction="top">
{{ action.details( rowItem[n] ) }} {{ action.details( rowItem[n] ) }}
@ -81,28 +81,28 @@
</section> </section>
<section layout="row" layout-wrap class="tb-footer-buttons md-fab " layout-align="start end"> <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" <md-button ng-disabled="$root.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() }}"> ng-click="groupAction.onAction($event, vm.items)" aria-label="{{ groupAction.name() }}">
<md-tooltip md-direction="top"> <md-tooltip md-direction="top">
{{ groupAction.details(vm.items.selectedCount) }} {{ groupAction.details(vm.items.selectedCount) }}
</md-tooltip> </md-tooltip>
<ng-md-icon icon="{{groupAction.icon}}"></ng-md-icon> <ng-md-icon icon="{{groupAction.icon}}"></ng-md-icon>
</md-button> </md-button>
<md-button ng-disabled="loading" ng-show="vm.topIndex > 0" class="tb-btn-footer md-primary md-hue-1 md-fab" ng-click="vm.moveToTop()" aria-label="{{'grid.scroll-to-top' | translate}}" > <md-button ng-disabled="$root.loading" ng-show="vm.topIndex > 0" class="tb-btn-footer md-primary md-hue-1 md-fab" ng-click="vm.moveToTop()" aria-label="{{'grid.scroll-to-top' | translate}}" >
<md-tooltip md-direction="top"> <md-tooltip md-direction="top">
{{'grid.scroll-to-top' | translate}} {{'grid.scroll-to-top' | translate}}
</md-tooltip> </md-tooltip>
<ng-md-icon icon="arrow_drop_up"></ng-md-icon> <ng-md-icon icon="arrow_drop_up"></ng-md-icon>
</md-button> </md-button>
<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-button ng-disabled="$root.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"> <md-tooltip md-direction="top">
{{ vm.addItemAction.details() }} {{ vm.addItemAction.details() }}
</md-tooltip> </md-tooltip>
<ng-md-icon icon="{{ vm.addItemAction.icon }}"></ng-md-icon> <ng-md-icon icon="{{ vm.addItemAction.icon }}"></ng-md-icon>
</md-button> </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-speed-dial ng-disabled="$root.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-fab-trigger>
<md-button ng-disabled="loading" class="tb-btn-footer md-accent md-hue-2 md-fab" aria-label="{{ vm.addItemAction.name() }}" > <md-button ng-disabled="$root.loading" class="tb-btn-footer md-accent md-hue-2 md-fab" aria-label="{{ vm.addItemAction.name() }}" >
<md-tooltip md-direction="top"> <md-tooltip md-direction="top">
{{ vm.addItemAction.details() }} {{ vm.addItemAction.details() }}
</md-tooltip> </md-tooltip>
@ -110,7 +110,7 @@
</md-button> </md-button>
</md-fab-trigger> </md-fab-trigger>
<md-fab-actions> <md-fab-actions>
<md-button ng-disabled="loading" class="md-accent md-hue-2 md-fab" ng-repeat="addItemAction in vm.addItemActions" <md-button ng-disabled="$root.loading" class="md-accent md-hue-2 md-fab" ng-repeat="addItemAction in vm.addItemActions"
ng-click="addItemAction.onAction($event)" aria-label="{{ addItemAction.name() }}" > ng-click="addItemAction.onAction($event)" aria-label="{{ addItemAction.name() }}" >
<md-tooltip md-direction="top"> <md-tooltip md-direction="top">
{{ addItemAction.details() }} {{ addItemAction.details() }}

2
ui/src/app/components/legend-config-panel.tpl.html

@ -16,7 +16,7 @@
--> -->
<form name="theForm" ng-submit="vm.update()"> <form name="theForm" ng-submit="vm.update()">
<fieldset ng-disabled="loading"> <fieldset ng-disabled="$root.loading">
<md-content style="height: 100%" flex layout="column"> <md-content style="height: 100%" flex layout="column">
<section layout="column"> <section layout="column">
<md-content class="md-padding" layout="column"> <md-content class="md-padding" layout="column">

8
ui/src/app/components/material-icons-dialog.tpl.html

@ -32,15 +32,15 @@
</md-button> </md-button>
</div> </div>
</md-toolbar> </md-toolbar>
<md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!loading" ng-show="loading"></md-progress-linear> <md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
<span style="min-height: 5px;" flex="" ng-show="!loading"></span> <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
<div class="tb-absolute-fill tb-icons-load" ng-show="vm.loadingIcons" layout="column" layout-align="center center"> <div class="tb-absolute-fill tb-icons-load" ng-show="vm.loadingIcons" layout="column" layout-align="center center">
<md-progress-circular md-mode="indeterminate" ng-disabled="!vm.loadingIcons" class="md-accent" md-diameter="40"></md-progress-circular> <md-progress-circular md-mode="indeterminate" ng-disabled="!vm.loadingIcons" class="md-accent" md-diameter="40"></md-progress-circular>
</div> </div>
<md-dialog-content> <md-dialog-content>
<div class="md-dialog-content"> <div class="md-dialog-content">
<md-content class="md-padding" layout="column"> <md-content class="md-padding" layout="column">
<fieldset ng-disabled="loading"> <fieldset ng-disabled="$root.loading">
<md-button ng-class="{'md-primary md-raised': icon == vm.selectedIcon}" class="tb-select-icon-button md-icon-button" <md-button ng-class="{'md-primary md-raised': icon == vm.selectedIcon}" class="tb-select-icon-button md-icon-button"
ng-repeat="icon in vm.icons" ng-click="vm.selectIcon($event, icon)" tb-on-finish-render="iconsLoadFinished"> ng-repeat="icon in vm.icons" ng-click="vm.selectIcon($event, icon)" tb-on-finish-render="iconsLoadFinished">
<md-icon class="material-icons">{{icon}}</md-icon> <md-icon class="material-icons">{{icon}}</md-icon>
@ -54,7 +54,7 @@
</md-dialog-content> </md-dialog-content>
<md-dialog-actions layout="row"> <md-dialog-actions layout="row">
<span flex></span> <span flex></span>
<md-button ng-disabled="loading" ng-click="vm.cancel()"> <md-button ng-disabled="$root.loading" ng-click="vm.cancel()">
{{ 'action.cancel' | translate }} {{ 'action.cancel' | translate }}
</md-button> </md-button>
</md-dialog-actions> </md-dialog-actions>

4
ui/src/app/components/plugin-select.directive.js

@ -56,7 +56,7 @@ function PluginSelect($compile, $templateCache, $q, pluginService, types) {
var deferred = $q.defer(); var deferred = $q.defer();
scope.pluginFetchFunction(pageLink).then(function success(result) { scope.pluginFetchFunction(pageLink, {ignoreLoading: true}).then(function success(result) {
deferred.resolve(result.data); deferred.resolve(result.data);
}, function fail() { }, function fail() {
deferred.reject(); deferred.reject();
@ -89,7 +89,7 @@ function PluginSelect($compile, $templateCache, $q, pluginService, types) {
if (scope.selectFirstPlugin) { if (scope.selectFirstPlugin) {
var pageLink = {limit: 1, textSearch: ''}; var pageLink = {limit: 1, textSearch: ''};
scope.pluginFetchFunction(pageLink).then(function success(result) { scope.pluginFetchFunction(pageLink, {ignoreLoading: true}).then(function success(result) {
var plugins = result.data; var plugins = result.data;
if (plugins.length > 0) { if (plugins.length > 0) {
scope.plugin = plugins[0]; scope.plugin = plugins[0];

6
ui/src/app/components/timewindow-panel.tpl.html

@ -16,7 +16,7 @@
--> -->
<form name="theForm" ng-submit="vm.update()"> <form name="theForm" ng-submit="vm.update()">
<fieldset ng-disabled="loading"> <fieldset ng-disabled="$root.loading">
<md-content style="height: 100%" flex layout="column"> <md-content style="height: 100%" flex layout="column">
<section layout="column"> <section layout="column">
<md-tabs ng-class="{'tb-headless': vm.historyOnly}" md-dynamic-height md-selected="vm.timewindow.selectedTab" md-border-bottom> <md-tabs ng-class="{'tb-headless': vm.historyOnly}" md-dynamic-height md-selected="vm.timewindow.selectedTab" md-border-bottom>
@ -81,10 +81,10 @@
<span flex></span> <span flex></span>
<section layout="row" layout-alignment="start center"> <section layout="row" layout-alignment="start center">
<span flex></span> <span flex></span>
<md-button ng-disabled="loading || theForm.$invalid || !theForm.$dirty" type="submit" class="md-raised md-primary"> <md-button ng-disabled="$root.loading || theForm.$invalid || !theForm.$dirty" type="submit" class="md-raised md-primary">
{{ 'action.update' | translate }} {{ 'action.update' | translate }}
</md-button> </md-button>
<md-button ng-disabled="loading" ng-click="vm.cancel()" style="margin-right:20px;"> <md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">
{{ 'action.cancel' | translate }} {{ 'action.cancel' | translate }}
</md-button> </md-button>
</section> </section>

12
ui/src/app/components/widget/action/widget-action-dialog.tpl.html

@ -26,12 +26,12 @@
</md-button> </md-button>
</div> </div>
</md-toolbar> </md-toolbar>
<md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!loading" ng-show="loading"></md-progress-linear> <md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
<span style="min-height: 5px;" flex="" ng-show="!loading"></span> <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
<md-dialog-content> <md-dialog-content>
<div class="md-dialog-content"> <div class="md-dialog-content">
<md-content class="md-padding" layout="column"> <md-content class="md-padding" layout="column">
<fieldset ng-disabled="loading" layout="column"> <fieldset ng-disabled="$root.loading" layout="column">
<md-input-container class="md-block"> <md-input-container class="md-block">
<label translate>widget-config.action-source</label> <label translate>widget-config.action-source</label>
<md-select name="actionSource" required aria-label="{{ 'widget-config.action-source' | translate }}" ng-model="vm.action.actionSourceId"> <md-select name="actionSource" required aria-label="{{ 'widget-config.action-source' | translate }}" ng-model="vm.action.actionSourceId">
@ -120,7 +120,7 @@
</div> </div>
<tb-js-func ng-if="vm.action.type == vm.types.widgetActionTypes.custom.value" <tb-js-func ng-if="vm.action.type == vm.types.widgetActionTypes.custom.value"
ng-model="vm.action.customFunction" ng-model="vm.action.customFunction"
function-args="{{ ['$event', 'widgetContext', 'entityId'] }}" function-args="{{ ['$event', 'widgetContext', 'entityId', 'entityName', 'additionalParams'] }}"
validation-args="{{ [] }}"> validation-args="{{ [] }}">
</tb-js-func> </tb-js-func>
</fieldset> </fieldset>
@ -129,11 +129,11 @@
</md-dialog-content> </md-dialog-content>
<md-dialog-actions layout="row"> <md-dialog-actions layout="row">
<span flex></span> <span flex></span>
<md-button ng-disabled="loading || theForm.$invalid || !theForm.$dirty" type="submit" <md-button ng-disabled="$root.loading || theForm.$invalid || !theForm.$dirty" type="submit"
class="md-raised md-primary"> class="md-raised md-primary">
{{ (vm.isAdd ? 'action.add' : 'action.save') | translate }} {{ (vm.isAdd ? 'action.add' : 'action.save') | translate }}
</md-button> </md-button>
<md-button ng-disabled="loading" ng-click="vm.cancel()" style="margin-right:20px;"> <md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">
{{ 'action.cancel' | translate }} {{ 'action.cancel' | translate }}
</md-button> </md-button>
</md-dialog-actions> </md-dialog-actions>

4
ui/src/app/components/widget/widget-config.tpl.html

@ -104,7 +104,7 @@
generate-data-key="generateDataKey(chip,type)" generate-data-key="generateDataKey(chip,type)"
fetch-entity-keys="fetchEntityKeys({entityAliasId: entityAliasId, query: query, type: type})" fetch-entity-keys="fetchEntityKeys({entityAliasId: entityAliasId, query: query, type: type})"
on-create-entity-alias="onCreateEntityAlias({event: event, alias: alias})"></tb-datasource> on-create-entity-alias="onCreateEntityAlias({event: event, alias: alias})"></tb-datasource>
<md-button ng-disabled="loading" class="md-icon-button md-primary" <md-button ng-disabled="$root.loading" class="md-icon-button md-primary"
style="min-width: 40px;" style="min-width: 40px;"
ng-click="removeDatasource($event, datasource)" ng-click="removeDatasource($event, datasource)"
aria-label="{{ 'action.remove' | translate }}"> aria-label="{{ 'action.remove' | translate }}">
@ -121,7 +121,7 @@
</div> </div>
</div> </div>
<div flex layout="row" layout-align="start center"> <div flex layout="row" layout-align="start center">
<md-button ng-show="typeParameters.maxDatasources == -1 || datasources.length < typeParameters.maxDatasources" ng-disabled="loading" class="md-primary md-raised" <md-button ng-show="typeParameters.maxDatasources == -1 || datasources.length < typeParameters.maxDatasources" ng-disabled="$root.loading" class="md-primary md-raised"
ng-click="addDatasource($event)" aria-label="{{ 'action.add' | translate }}"> ng-click="addDatasource($event)" aria-label="{{ 'action.add' | translate }}">
<md-tooltip md-direction="top"> <md-tooltip md-direction="top">
{{ 'widget-config.add-datasource' | translate }} {{ 'widget-config.add-datasource' | translate }}

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

@ -444,7 +444,7 @@ export default function WidgetController($scope, $state, $timeout, $window, $ele
} }
} }
function handleWidgetAction($event, descriptor, entityId, entityName) { function handleWidgetAction($event, descriptor, entityId, entityName, additionalParams) {
var type = descriptor.type; var type = descriptor.type;
var targetEntityParamName = descriptor.stateEntityParamName; var targetEntityParamName = descriptor.stateEntityParamName;
var targetEntityId; var targetEntityId;
@ -485,8 +485,11 @@ export default function WidgetController($scope, $state, $timeout, $window, $ele
var customFunction = descriptor.customFunction; var customFunction = descriptor.customFunction;
if (angular.isDefined(customFunction) && customFunction.length > 0) { if (angular.isDefined(customFunction) && customFunction.length > 0) {
try { try {
var customActionFunction = new Function('$event', 'widgetContext', 'entityId', 'entityName', customFunction); if (!additionalParams) {
customActionFunction($event, widgetContext, entityId, entityName); additionalParams = {};
}
var customActionFunction = new Function('$event', 'widgetContext', 'entityId', 'entityName', 'additionalParams', customFunction);
customActionFunction($event, widgetContext, entityId, entityName, additionalParams);
} catch (e) { } catch (e) {
// //
} }

2
ui/src/app/components/widgets-bundle-select.directive.js

@ -48,7 +48,7 @@ function WidgetsBundleSelect($compile, $templateCache, widgetService, types) {
} }
} }
widgetsBundleFetchFunction().then( widgetsBundleFetchFunction({ignoreLoading: true}).then(
function success(widgetsBundles) { function success(widgetsBundles) {
scope.widgetsBundles = widgetsBundles; scope.widgetsBundles = widgetsBundles;
if (scope.selectFirstBundle) { if (scope.selectFirstBundle) {

8
ui/src/app/customer/add-customer.tpl.html

@ -27,8 +27,8 @@
</md-button> </md-button>
</div> </div>
</md-toolbar> </md-toolbar>
<md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!loading" ng-show="loading"></md-progress-linear> <md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
<span style="min-height: 5px;" flex="" ng-show="!loading"></span> <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
<md-dialog-content> <md-dialog-content>
<div class="md-dialog-content"> <div class="md-dialog-content">
<tb-customer customer="vm.item" is-edit="true" the-form="theForm"></tb-customer> <tb-customer customer="vm.item" is-edit="true" the-form="theForm"></tb-customer>
@ -36,10 +36,10 @@
</md-dialog-content> </md-dialog-content>
<md-dialog-actions layout="row"> <md-dialog-actions layout="row">
<span flex></span> <span flex></span>
<md-button ng-disabled="loading || theForm.$invalid || !theForm.$dirty" type="submit" class="md-raised md-primary"> <md-button ng-disabled="$root.loading || theForm.$invalid || !theForm.$dirty" type="submit" class="md-raised md-primary">
{{ 'action.add' | translate }} {{ 'action.add' | translate }}
</md-button> </md-button>
<md-button ng-disabled="loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | translate }}</md-button> <md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | translate }}</md-button>
</md-dialog-actions> </md-dialog-actions>
</form> </form>
</md-dialog> </md-dialog>

2
ui/src/app/customer/customer-fieldset.tpl.html

@ -32,7 +32,7 @@
</div> </div>
<md-content class="md-padding" layout="column"> <md-content class="md-padding" layout="column">
<fieldset ng-show="!isPublic" ng-disabled="loading || !isEdit"> <fieldset ng-show="!isPublic" ng-disabled="$root.loading || !isEdit">
<md-input-container class="md-block"> <md-input-container class="md-block">
<label translate>customer.title</label> <label translate>customer.title</label>
<input required name="title" ng-model="customer.title"> <input required name="title" ng-model="customer.title">

8
ui/src/app/dashboard/add-dashboard.tpl.html

@ -27,8 +27,8 @@
</md-button> </md-button>
</div> </div>
</md-toolbar> </md-toolbar>
<md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!loading" ng-show="loading"></md-progress-linear> <md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
<span style="min-height: 5px;" flex="" ng-show="!loading"></span> <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
<md-dialog-content> <md-dialog-content>
<div class="md-dialog-content"> <div class="md-dialog-content">
<tb-dashboard-details dashboard="vm.item" is-edit="true" the-form="theForm"></tb-dashboard-details> <tb-dashboard-details dashboard="vm.item" is-edit="true" the-form="theForm"></tb-dashboard-details>
@ -36,10 +36,10 @@
</md-dialog-content> </md-dialog-content>
<md-dialog-actions layout="row"> <md-dialog-actions layout="row">
<span flex></span> <span flex></span>
<md-button ng-disabled="loading || theForm.$invalid || !theForm.$dirty" type="submit" class="md-raised md-primary"> <md-button ng-disabled="$root.loading || theForm.$invalid || !theForm.$dirty" type="submit" class="md-raised md-primary">
{{ 'action.add' | translate }} {{ 'action.add' | translate }}
</md-button> </md-button>
<md-button ng-disabled="loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | translate }}</md-button> <md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | translate }}</md-button>
</md-dialog-actions> </md-dialog-actions>
</form> </form>
</md-dialog> </md-dialog>

8
ui/src/app/dashboard/add-dashboards-to-customer.tpl.html

@ -26,8 +26,8 @@
</md-button> </md-button>
</div> </div>
</md-toolbar> </md-toolbar>
<md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!loading" ng-show="loading"></md-progress-linear> <md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
<span style="min-height: 5px;" flex="" ng-show="!loading"></span> <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
<md-dialog-content> <md-dialog-content>
<div class="md-dialog-content"> <div class="md-dialog-content">
<fieldset> <fieldset>
@ -65,11 +65,11 @@
</md-dialog-content> </md-dialog-content>
<md-dialog-actions layout="row"> <md-dialog-actions layout="row">
<span flex></span> <span flex></span>
<md-button ng-disabled="loading || vm.dashboards.selectedCount == 0" type="submit" <md-button ng-disabled="$root.loading || vm.dashboards.selectedCount == 0" type="submit"
class="md-raised md-primary"> class="md-raised md-primary">
{{ 'action.assign' | translate }} {{ 'action.assign' | translate }}
</md-button> </md-button>
<md-button ng-disabled="loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | <md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' |
translate }} translate }}
</md-button> </md-button>
</md-dialog-actions> </md-dialog-actions>

2
ui/src/app/dashboard/add-widget.controller.js

@ -110,7 +110,7 @@ export default function AddWidgetController($scope, widgetService, entityService
function success(aliasInfo) { function success(aliasInfo) {
var entity = aliasInfo.currentEntity; var entity = aliasInfo.currentEntity;
if (entity) { if (entity) {
entityService.getEntityKeys(entity.entityType, entity.id, query, type).then( entityService.getEntityKeys(entity.entityType, entity.id, query, type, {ignoreLoading: true}).then(
function success(keys) { function success(keys) {
deferred.resolve(keys); deferred.resolve(keys);
}, },

10
ui/src/app/dashboard/add-widget.tpl.html

@ -27,11 +27,11 @@
</md-button> </md-button>
</div> </div>
</md-toolbar> </md-toolbar>
<md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!loading" ng-show="loading"></md-progress-linear> <md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
<span style="min-height: 5px;" flex="" ng-show="!loading"></span> <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
<md-dialog-content> <md-dialog-content>
<div class="md-dialog-content" style="padding-top: 0px;"> <div class="md-dialog-content" style="padding-top: 0px;">
<fieldset ng-disabled="loading" style="position: relative; height: 600px;"> <fieldset ng-disabled="$root.loading" style="position: relative; height: 600px;">
<tb-widget-config widget-type="vm.widget.type" <tb-widget-config widget-type="vm.widget.type"
type-parameters="vm.widgetInfo.typeParameters" type-parameters="vm.widgetInfo.typeParameters"
action-sources="vm.widgetInfo.actionSources" action-sources="vm.widgetInfo.actionSources"
@ -50,11 +50,11 @@
</md-dialog-content> </md-dialog-content>
<md-dialog-actions layout="row"> <md-dialog-actions layout="row">
<span flex></span> <span flex></span>
<md-button ng-disabled="loading || theForm.$invalid" type="submit" <md-button ng-disabled="$root.loading || theForm.$invalid" type="submit"
class="md-raised md-primary"> class="md-raised md-primary">
{{ 'action.add' | translate }} {{ 'action.add' | translate }}
</md-button> </md-button>
<md-button ng-disabled="loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | <md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' |
translate }} translate }}
</md-button> </md-button>
</md-dialog-actions> </md-dialog-actions>

8
ui/src/app/dashboard/assign-to-customer.tpl.html

@ -26,8 +26,8 @@
</md-button> </md-button>
</div> </div>
</md-toolbar> </md-toolbar>
<md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!loading" ng-show="loading"></md-progress-linear> <md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
<span style="min-height: 5px;" flex="" ng-show="!loading"></span> <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
<md-dialog-content> <md-dialog-content>
<div class="md-dialog-content"> <div class="md-dialog-content">
<fieldset> <fieldset>
@ -65,10 +65,10 @@
</md-dialog-content> </md-dialog-content>
<md-dialog-actions layout="row"> <md-dialog-actions layout="row">
<span flex></span> <span flex></span>
<md-button ng-disabled="loading || vm.customers.selection==null" type="submit" class="md-raised md-primary"> <md-button ng-disabled="$root.loading || vm.customers.selection==null" type="submit" class="md-raised md-primary">
{{ 'action.assign' | translate }} {{ 'action.assign' | translate }}
</md-button> </md-button>
<md-button ng-disabled="loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | <md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' |
translate }} translate }}
</md-button> </md-button>
</md-dialog-actions> </md-dialog-actions>

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

@ -59,7 +59,7 @@
</md-button> </md-button>
</div> </div>
</div> </div>
<fieldset ng-disabled="loading || !isEdit"> <fieldset ng-disabled="$root.loading || !isEdit">
<md-input-container class="md-block"> <md-input-container class="md-block">
<label translate>dashboard.title</label> <label translate>dashboard.title</label>
<input required name="title" ng-model="dashboard.title"> <input required name="title" ng-model="dashboard.title">

10
ui/src/app/dashboard/dashboard-settings.tpl.html

@ -26,11 +26,11 @@
</md-button> </md-button>
</div> </div>
</md-toolbar> </md-toolbar>
<md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!loading" ng-show="loading"></md-progress-linear> <md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
<span style="min-height: 5px;" flex="" ng-show="!loading"></span> <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
<md-dialog-content> <md-dialog-content>
<div class="md-dialog-content"> <div class="md-dialog-content">
<fieldset ng-disabled="loading"> <fieldset ng-disabled="$root.loading">
<div ng-show="vm.settings"> <div ng-show="vm.settings">
<md-input-container class="md-block"> <md-input-container class="md-block">
<label translate>dashboard.state-controller</label> <label translate>dashboard.state-controller</label>
@ -194,10 +194,10 @@
</md-dialog-content> </md-dialog-content>
<md-dialog-actions layout="row"> <md-dialog-actions layout="row">
<span flex></span> <span flex></span>
<md-button ng-disabled="loading || !theForm.$dirty || !theForm.$valid" type="submit" class="md-raised md-primary"> <md-button ng-disabled="$root.loading || !theForm.$dirty || !theForm.$valid" type="submit" class="md-raised md-primary">
{{ 'action.save' | translate }} {{ 'action.save' | translate }}
</md-button> </md-button>
<md-button ng-disabled="loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | translate }}</md-button> <md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | translate }}</md-button>
</md-dialog-actions> </md-dialog-actions>
</form> </form>
</md-dialog> </md-dialog>

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

@ -110,7 +110,7 @@
</section> </section>
<section class="tb-dashboard-container tb-absolute-fill" <section class="tb-dashboard-container tb-absolute-fill"
ng-class="{ 'is-fullscreen': forceFullscreen, 'tb-dashboard-toolbar-opened': vm.toolbarOpened, 'tb-dashboard-toolbar-closed': !vm.toolbarOpened }"> ng-class="{ 'is-fullscreen': forceFullscreen, 'tb-dashboard-toolbar-opened': vm.toolbarOpened, 'tb-dashboard-toolbar-closed': !vm.toolbarOpened }">
<section ng-show="!loading && vm.dashboardConfigurationError()" layout-align="center center" <section ng-show="!$root.loading && vm.dashboardConfigurationError()" layout-align="center center"
ng-style="{'color': vm.dashboard.configuration.settings.titleColor}" ng-style="{'color': vm.dashboard.configuration.settings.titleColor}"
ng-class="{'tb-padded' : !vm.widgetEditMode}" ng-class="{'tb-padded' : !vm.widgetEditMode}"
style="text-transform: uppercase; display: flex; z-index: 1;" style="text-transform: uppercase; display: flex; z-index: 1;"
@ -277,10 +277,10 @@
</div> </div>
</tb-details-sidenav> </tb-details-sidenav>
<section layout="row" layout-wrap class="tb-footer-buttons md-fab" layout-align="start end"> <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-fab-speed-dial ng-disabled="$root.loading" ng-show="!vm.isAddingWidget && vm.isEdit && !vm.widgetEditMode"
md-open="vm.addItemActionsOpen" class="md-scale" md-direction="up"> md-open="vm.addItemActionsOpen" class="md-scale" md-direction="up">
<md-fab-trigger> <md-fab-trigger>
<md-button ng-disabled="loading" <md-button ng-disabled="$root.loading"
class="tb-btn-footer md-accent md-hue-2 md-fab" class="tb-btn-footer md-accent md-hue-2 md-fab"
aria-label="{{ 'dashboard.add-widget' | translate }}"> aria-label="{{ 'dashboard.add-widget' | translate }}">
<md-tooltip md-direction="top"> <md-tooltip md-direction="top">
@ -290,7 +290,7 @@
</md-button> </md-button>
</md-fab-trigger> </md-fab-trigger>
<md-fab-actions> <md-fab-actions>
<md-button ng-disabled="loading" <md-button ng-disabled="$root.loading"
class="tmd-accent md-hue-2 md-fab" ng-click="vm.addWidget($event)" class="tmd-accent md-hue-2 md-fab" ng-click="vm.addWidget($event)"
aria-label="{{ 'action.create' | translate }}"> aria-label="{{ 'action.create' | translate }}">
<md-tooltip md-direction="top"> <md-tooltip md-direction="top">
@ -298,7 +298,7 @@
</md-tooltip> </md-tooltip>
<ng-md-icon icon="insert_drive_file"></ng-md-icon> <ng-md-icon icon="insert_drive_file"></ng-md-icon>
</md-button> </md-button>
<md-button ng-disabled="loading" <md-button ng-disabled="$root.loading"
class="tmd-accent md-hue-2 md-fab" ng-click="vm.importWidget($event)" class="tmd-accent md-hue-2 md-fab" ng-click="vm.importWidget($event)"
aria-label="{{ 'action.import' | translate }}"> aria-label="{{ 'action.import' | translate }}">
<md-tooltip md-direction="top"> <md-tooltip md-direction="top">
@ -308,7 +308,7 @@
</md-button> </md-button>
</md-fab-actions> </md-fab-actions>
</md-fab-speed-dial> </md-fab-speed-dial>
<md-button ng-if="(vm.isTenantAdmin() || vm.isSystemAdmin()) && !forceFullscreen" ng-show="vm.isEdit && !vm.isAddingWidget && !loading" ng-disabled="loading" <md-button ng-if="(vm.isTenantAdmin() || vm.isSystemAdmin()) && !forceFullscreen" ng-show="vm.isEdit && !vm.isAddingWidget && !$root.loading" ng-disabled="$root.loading"
class="tb-btn-footer md-accent md-hue-2 md-fab" class="tb-btn-footer md-accent md-hue-2 md-fab"
aria-label="{{ 'action.apply' | translate }}" aria-label="{{ 'action.apply' | translate }}"
ng-click="vm.saveDashboard()"> ng-click="vm.saveDashboard()">
@ -317,8 +317,8 @@
</md-tooltip> </md-tooltip>
<ng-md-icon icon="done"></ng-md-icon> <ng-md-icon icon="done"></ng-md-icon>
</md-button> </md-button>
<md-button ng-show="!vm.isAddingWidget && !loading" <md-button ng-show="!vm.isAddingWidget && !$root.loading"
ng-if="(vm.isTenantAdmin() || vm.isSystemAdmin()) && !forceFullscreen" ng-disabled="loading" ng-if="(vm.isTenantAdmin() || vm.isSystemAdmin()) && !forceFullscreen" ng-disabled="$root.loading"
class="tb-btn-footer md-accent md-hue-2 md-fab" class="tb-btn-footer md-accent md-hue-2 md-fab"
aria-label="{{ 'action.edit-mode' | translate }}" aria-label="{{ 'action.edit-mode' | translate }}"
ng-click="vm.toggleDashboardEditMode()"> ng-click="vm.toggleDashboardEditMode()">

2
ui/src/app/dashboard/edit-widget.directive.js

@ -75,7 +75,7 @@ export default function EditWidgetDirective($compile, $templateCache, types, wid
function success(aliasInfo) { function success(aliasInfo) {
var entity = aliasInfo.currentEntity; var entity = aliasInfo.currentEntity;
if (entity) { if (entity) {
entityService.getEntityKeys(entity.entityType, entity.id, query, type).then( entityService.getEntityKeys(entity.entityType, entity.id, query, type, {ignoreLoading: true}).then(
function success(keys) { function success(keys) {
deferred.resolve(keys); deferred.resolve(keys);
}, },

2
ui/src/app/dashboard/edit-widget.tpl.html

@ -15,7 +15,7 @@
limitations under the License. limitations under the License.
--> -->
<fieldset ng-disabled="loading"> <fieldset ng-disabled="$root.loading">
<tb-widget-config widget-type="widget.type" <tb-widget-config widget-type="widget.type"
type-parameters="typeParameters" type-parameters="typeParameters"
action-sources="actionSources" action-sources="actionSources"

2
ui/src/app/dashboard/layouts/dashboard-layout.tpl.html

@ -22,7 +22,7 @@
'background-attachment': 'scroll', 'background-attachment': 'scroll',
'background-size': vm.layoutCtx.gridSettings.backgroundSizeMode || '100%', 'background-size': vm.layoutCtx.gridSettings.backgroundSizeMode || '100%',
'background-position': '0% 0%'}"> 'background-position': '0% 0%'}">
<section ng-show="!loading && vm.noData()" layout-align="center center" <section ng-show="!$root.loading && vm.noData()" layout-align="center center"
ng-style="{'color': vm.layoutCtx.gridSettings.color}" ng-style="{'color': vm.layoutCtx.gridSettings.color}"
style="text-transform: uppercase; display: flex; z-index: 1; pointer-events: none;" style="text-transform: uppercase; display: flex; z-index: 1; pointer-events: none;"
class="md-headline tb-absolute-fill"> class="md-headline tb-absolute-fill">

10
ui/src/app/dashboard/layouts/manage-dashboard-layouts.tpl.html

@ -26,11 +26,11 @@
</md-button> </md-button>
</div> </div>
</md-toolbar> </md-toolbar>
<md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!loading" ng-show="loading"></md-progress-linear> <md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
<span style="min-height: 5px;" flex="" ng-show="!loading"></span> <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
<md-dialog-content> <md-dialog-content>
<div class="md-dialog-content"> <div class="md-dialog-content">
<fieldset ng-disabled="loading"> <fieldset ng-disabled="$root.loading">
<div layout="row" layout-align="start center"> <div layout="row" layout-align="start center">
<md-checkbox ng-disabled="true" flex aria-label="{{ 'layout.main' | translate }}" <md-checkbox ng-disabled="true" flex aria-label="{{ 'layout.main' | translate }}"
ng-model="vm.displayLayouts.main">{{ 'layout.main' | translate }} ng-model="vm.displayLayouts.main">{{ 'layout.main' | translate }}
@ -56,10 +56,10 @@
</md-dialog-content> </md-dialog-content>
<md-dialog-actions layout="row"> <md-dialog-actions layout="row">
<span flex></span> <span flex></span>
<md-button ng-disabled="loading || !theForm.$dirty || !theForm.$valid" type="submit" class="md-raised md-primary"> <md-button ng-disabled="$root.loading || !theForm.$dirty || !theForm.$valid" type="submit" class="md-raised md-primary">
{{ 'action.save' | translate }} {{ 'action.save' | translate }}
</md-button> </md-button>
<md-button ng-disabled="loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | translate }}</md-button> <md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | translate }}</md-button>
</md-dialog-actions> </md-dialog-actions>
</form> </form>
</md-dialog> </md-dialog>

6
ui/src/app/dashboard/layouts/select-target-layout.tpl.html

@ -26,11 +26,11 @@
</md-button> </md-button>
</div> </div>
</md-toolbar> </md-toolbar>
<md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!loading" ng-show="loading"></md-progress-linear> <md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
<span style="min-height: 5px;" flex="" ng-show="!loading"></span> <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
<md-dialog-content> <md-dialog-content>
<div class="md-dialog-content"> <div class="md-dialog-content">
<fieldset ng-disabled="loading"> <fieldset ng-disabled="$root.loading">
<div layout="row" layout-align="start center"> <div layout="row" layout-align="start center">
<md-button flex class="tb-layout-button md-raised md-primary" layout="column" <md-button flex class="tb-layout-button md-raised md-primary" layout="column"
ng-click="vm.selectLayout($event, 'main')"> ng-click="vm.selectLayout($event, 'main')">

10
ui/src/app/dashboard/states/dashboard-state-dialog.tpl.html

@ -26,12 +26,12 @@
</md-button> </md-button>
</div> </div>
</md-toolbar> </md-toolbar>
<md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!loading" ng-show="loading"></md-progress-linear> <md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
<span style="min-height: 5px;" flex="" ng-show="!loading"></span> <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
<md-dialog-content> <md-dialog-content>
<div class="md-dialog-content"> <div class="md-dialog-content">
<md-content class="md-padding" layout="column"> <md-content class="md-padding" layout="column">
<fieldset ng-disabled="loading"> <fieldset ng-disabled="$root.loading">
<md-input-container class="md-block"> <md-input-container class="md-block">
<label translate>dashboard.state-name</label> <label translate>dashboard.state-name</label>
<input name="name" required ng-model="vm.state.name"> <input name="name" required ng-model="vm.state.name">
@ -57,11 +57,11 @@
</md-dialog-content> </md-dialog-content>
<md-dialog-actions layout="row"> <md-dialog-actions layout="row">
<span flex></span> <span flex></span>
<md-button ng-disabled="loading || theForm.$invalid || !theForm.$dirty" type="submit" <md-button ng-disabled="$root.loading || theForm.$invalid || !theForm.$dirty" type="submit"
class="md-raised md-primary"> class="md-raised md-primary">
{{ (vm.isAdd ? 'action.add' : 'action.save') | translate }} {{ (vm.isAdd ? 'action.add' : 'action.save') | translate }}
</md-button> </md-button>
<md-button ng-disabled="loading" ng-click="vm.cancel()" style="margin-right:20px;"> <md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">
{{ 'action.cancel' | translate }} {{ 'action.cancel' | translate }}
</md-button> </md-button>
</md-dialog-actions> </md-dialog-actions>

10
ui/src/app/dashboard/states/manage-dashboard-states.tpl.html

@ -26,11 +26,11 @@
</md-button> </md-button>
</div> </div>
</md-toolbar> </md-toolbar>
<md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!loading" ng-show="loading"></md-progress-linear> <md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
<span style="min-height: 5px;" flex="" ng-show="!loading"></span> <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
<md-dialog-content> <md-dialog-content>
<div class="md-dialog-content"> <div class="md-dialog-content">
<fieldset ng-disabled="loading"> <fieldset ng-disabled="$root.loading">
<div class="manage-dashboard-states" layout="column"> <div class="manage-dashboard-states" layout="column">
<md-toolbar class="md-table-toolbar md-default" ng-show="vm.query.search === null"> <md-toolbar class="md-table-toolbar md-default" ng-show="vm.query.search === null">
<div class="md-toolbar-tools"> <div class="md-toolbar-tools">
@ -118,10 +118,10 @@
</md-dialog-content> </md-dialog-content>
<md-dialog-actions layout="row"> <md-dialog-actions layout="row">
<span flex></span> <span flex></span>
<md-button ng-disabled="loading || !theForm.$dirty || !theForm.$valid" type="submit" class="md-raised md-primary"> <md-button ng-disabled="$root.loading || !theForm.$dirty || !theForm.$valid" type="submit" class="md-raised md-primary">
{{ 'action.save' | translate }} {{ 'action.save' | translate }}
</md-button> </md-button>
<md-button ng-disabled="loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | translate }}</md-button> <md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | translate }}</md-button>
</md-dialog-actions> </md-dialog-actions>
</form> </form>
</md-dialog> </md-dialog>

10
ui/src/app/dashboard/states/select-target-state.tpl.html

@ -26,11 +26,11 @@
</md-button> </md-button>
</div> </div>
</md-toolbar> </md-toolbar>
<md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!loading" ng-show="loading"></md-progress-linear> <md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
<span style="min-height: 5px;" flex="" ng-show="!loading"></span> <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
<md-dialog-content> <md-dialog-content>
<div class="md-dialog-content"> <div class="md-dialog-content">
<fieldset ng-disabled="loading"> <fieldset ng-disabled="$root.loading">
<md-select required aria-label="{{ 'dashboard.state' | translate }}" ng-model="vm.stateId"> <md-select required aria-label="{{ 'dashboard.state' | translate }}" ng-model="vm.stateId">
<md-option ng-repeat="(stateId, state) in vm.states" ng-value="stateId"> <md-option ng-repeat="(stateId, state) in vm.states" ng-value="stateId">
{{state.name}} {{state.name}}
@ -41,10 +41,10 @@
</md-dialog-content> </md-dialog-content>
<md-dialog-actions layout="row"> <md-dialog-actions layout="row">
<span flex></span> <span flex></span>
<md-button ng-disabled="loading || !theForm.$dirty || !theForm.$valid" type="submit" class="md-raised md-primary"> <md-button ng-disabled="$root.loading || !theForm.$dirty || !theForm.$valid" type="submit" class="md-raised md-primary">
{{ 'action.save' | translate }} {{ 'action.save' | translate }}
</md-button> </md-button>
<md-button ng-disabled="loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | translate }}</md-button> <md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | translate }}</md-button>
</md-dialog-actions> </md-dialog-actions>
</form> </form>
</md-dialog> </md-dialog>

8
ui/src/app/device/add-device.tpl.html

@ -27,8 +27,8 @@
</md-button> </md-button>
</div> </div>
</md-toolbar> </md-toolbar>
<md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!loading" ng-show="loading"></md-progress-linear> <md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
<span style="min-height: 5px;" flex="" ng-show="!loading"></span> <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
<md-dialog-content> <md-dialog-content>
<div class="md-dialog-content"> <div class="md-dialog-content">
<tb-device device="vm.item" is-edit="true" the-form="theForm"></tb-device> <tb-device device="vm.item" is-edit="true" the-form="theForm"></tb-device>
@ -36,10 +36,10 @@
</md-dialog-content> </md-dialog-content>
<md-dialog-actions layout="row"> <md-dialog-actions layout="row">
<span flex></span> <span flex></span>
<md-button ng-disabled="loading || theForm.$invalid || !theForm.$dirty" type="submit" class="md-raised md-primary"> <md-button ng-disabled="$root.loading || theForm.$invalid || !theForm.$dirty" type="submit" class="md-raised md-primary">
{{ 'action.add' | translate }} {{ 'action.add' | translate }}
</md-button> </md-button>
<md-button ng-disabled="loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | translate }}</md-button> <md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | translate }}</md-button>
</md-dialog-actions> </md-dialog-actions>
</form> </form>
</md-dialog> </md-dialog>

8
ui/src/app/device/add-devices-to-customer.tpl.html

@ -26,8 +26,8 @@
</md-button> </md-button>
</div> </div>
</md-toolbar> </md-toolbar>
<md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!loading" ng-show="loading"></md-progress-linear> <md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
<span style="min-height: 5px;" flex="" ng-show="!loading"></span> <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
<md-dialog-content> <md-dialog-content>
<div class="md-dialog-content"> <div class="md-dialog-content">
<fieldset> <fieldset>
@ -65,11 +65,11 @@
</md-dialog-content> </md-dialog-content>
<md-dialog-actions layout="row"> <md-dialog-actions layout="row">
<span flex></span> <span flex></span>
<md-button ng-disabled="loading || vm.devices.selectedCount == 0" type="submit" <md-button ng-disabled="$root.loading || vm.devices.selectedCount == 0" type="submit"
class="md-raised md-primary"> class="md-raised md-primary">
{{ 'action.assign' | translate }} {{ 'action.assign' | translate }}
</md-button> </md-button>
<md-button ng-disabled="loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | <md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' |
translate }} translate }}
</md-button> </md-button>
</md-dialog-actions> </md-dialog-actions>

8
ui/src/app/device/assign-to-customer.tpl.html

@ -26,8 +26,8 @@
</md-button> </md-button>
</div> </div>
</md-toolbar> </md-toolbar>
<md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!loading" ng-show="loading"></md-progress-linear> <md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
<span style="min-height: 5px;" flex="" ng-show="!loading"></span> <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
<md-dialog-content> <md-dialog-content>
<div class="md-dialog-content"> <div class="md-dialog-content">
<fieldset> <fieldset>
@ -65,10 +65,10 @@
</md-dialog-content> </md-dialog-content>
<md-dialog-actions layout="row"> <md-dialog-actions layout="row">
<span flex></span> <span flex></span>
<md-button ng-disabled="loading || vm.customers.selection==null" type="submit" class="md-raised md-primary"> <md-button ng-disabled="$root.loading || vm.customers.selection==null" type="submit" class="md-raised md-primary">
{{ 'action.assign' | translate }} {{ 'action.assign' | translate }}
</md-button> </md-button>
<md-button ng-disabled="loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | <md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' |
translate }} translate }}
</md-button> </md-button>
</md-dialog-actions> </md-dialog-actions>

12
ui/src/app/device/device-credentials.tpl.html

@ -26,14 +26,14 @@
</md-button> </md-button>
</div> </div>
</md-toolbar> </md-toolbar>
<md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!loading" ng-show="loading"></md-progress-linear> <md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
<span style="min-height: 5px;" flex="" ng-show="!loading"></span> <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
<md-dialog-content> <md-dialog-content>
<div class="md-dialog-content"> <div class="md-dialog-content">
<fieldset ng-disabled="loading || vm.isReadOnly"> <fieldset ng-disabled="$root.loading || vm.isReadOnly">
<md-input-container class="md-block"> <md-input-container class="md-block">
<label translate>device.credentials-type</label> <label translate>device.credentials-type</label>
<md-select ng-disabled="loading || vm.isReadOnly" ng-model="vm.deviceCredentials.credentialsType" <md-select ng-disabled="$root.loading || vm.isReadOnly" ng-model="vm.deviceCredentials.credentialsType"
ng-change="vm.clear()"> ng-change="vm.clear()">
<md-option ng-repeat="credentialsType in vm.credentialsTypes" value="{{credentialsType.value}}"> <md-option ng-repeat="credentialsType in vm.credentialsTypes" value="{{credentialsType.value}}">
{{credentialsType.name}} {{credentialsType.name}}
@ -62,10 +62,10 @@
</md-dialog-content> </md-dialog-content>
<md-dialog-actions layout="row"> <md-dialog-actions layout="row">
<span flex></span> <span flex></span>
<md-button ng-if="!vm.isReadOnly" ng-disabled="loading || theForm.$invalid || !theForm.$dirty || !vm.valid()" type="submit" class="md-raised md-primary"> <md-button ng-if="!vm.isReadOnly" ng-disabled="$root.loading || theForm.$invalid || !theForm.$dirty || !vm.valid()" type="submit" class="md-raised md-primary">
{{ 'action.save' | translate }} {{ 'action.save' | translate }}
</md-button> </md-button>
<md-button ng-disabled="loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ (vm.isReadOnly ? 'action.close' : 'action.cancel') | translate }}</md-button> <md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ (vm.isReadOnly ? 'action.close' : 'action.cancel') | translate }}</md-button>
</md-dialog-actions> </md-dialog-actions>
</form> </form>
</md-dialog> </md-dialog>

6
ui/src/app/device/device-fieldset.tpl.html

@ -58,7 +58,7 @@
ng-show="!isEdit && isPublic && (deviceScope === 'customer' || deviceScope === 'tenant')"> ng-show="!isEdit && isPublic && (deviceScope === 'customer' || deviceScope === 'tenant')">
{{ 'device.device-public' | translate }} {{ 'device.device-public' | translate }}
</div> </div>
<fieldset ng-disabled="loading || !isEdit"> <fieldset ng-disabled="$root.loading || !isEdit">
<md-input-container class="md-block"> <md-input-container class="md-block">
<label translate>device.name</label> <label translate>device.name</label>
<input required name="name" ng-model="device.name"> <input required name="name" ng-model="device.name">
@ -67,14 +67,14 @@
</div> </div>
</md-input-container> </md-input-container>
<tb-entity-subtype-autocomplete <tb-entity-subtype-autocomplete
ng-disabled="loading || !isEdit" ng-disabled="$root.loading || !isEdit"
tb-required="true" tb-required="true"
the-form="theForm" the-form="theForm"
ng-model="device.type" ng-model="device.type"
entity-type="types.entityType.device"> entity-type="types.entityType.device">
</tb-entity-subtype-autocomplete> </tb-entity-subtype-autocomplete>
<md-input-container class="md-block"> <md-input-container class="md-block">
<md-checkbox ng-disabled="loading || !isEdit" flex aria-label="{{ 'device.is-gateway' | translate }}" <md-checkbox ng-disabled="$root.loading || !isEdit" flex aria-label="{{ 'device.is-gateway' | translate }}"
ng-model="device.additionalInfo.gateway">{{ 'device.is-gateway' | translate }} ng-model="device.additionalInfo.gateway">{{ 'device.is-gateway' | translate }}
</md-checkbox> </md-checkbox>
</md-input-container> </md-input-container>

10
ui/src/app/entity/alias/entity-alias-dialog.tpl.html

@ -26,11 +26,11 @@
</md-button> </md-button>
</div> </div>
</md-toolbar> </md-toolbar>
<md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!loading" ng-show="loading"></md-progress-linear> <md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
<span style="min-height: 5px;" flex="" ng-show="!loading"></span> <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
<md-dialog-content> <md-dialog-content>
<div class="md-dialog-content"> <div class="md-dialog-content">
<fieldset ng-disabled="loading"> <fieldset ng-disabled="$root.loading">
<div flex layout="column"> <div flex layout="column">
<div layout="row"> <div layout="row">
<md-input-container flex class="md-block"> <md-input-container flex class="md-block">
@ -64,10 +64,10 @@
</md-dialog-content> </md-dialog-content>
<md-dialog-actions layout="row"> <md-dialog-actions layout="row">
<span flex></span> <span flex></span>
<md-button ng-disabled="loading || theForm.$invalid || !theForm.$dirty" type="submit" class="md-raised md-primary"> <md-button ng-disabled="$root.loading || theForm.$invalid || !theForm.$dirty" type="submit" class="md-raised md-primary">
{{ (vm.isAdd ? 'action.add' : 'action.save') | translate }} {{ (vm.isAdd ? 'action.add' : 'action.save') | translate }}
</md-button> </md-button>
<md-button ng-disabled="loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | translate }}</md-button> <md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | translate }}</md-button>
</md-dialog-actions> </md-dialog-actions>
</form> </form>
</md-dialog> </md-dialog>

16
ui/src/app/entity/alias/entity-aliases.tpl.html

@ -26,8 +26,8 @@
</md-button> </md-button>
</div> </div>
</md-toolbar> </md-toolbar>
<md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!loading" ng-show="loading"></md-progress-linear> <md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
<span style="min-height: 5px;" flex="" ng-show="!loading"></span> <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
<div class="tb-aliases-header" flex layout="row" layout-align="start center"> <div class="tb-aliases-header" flex layout="row" layout-align="start center">
<span flex="5"></span> <span flex="5"></span>
<div flex layout="row" layout-align="start center"> <div flex layout="row" layout-align="start center">
@ -40,7 +40,7 @@
<md-divider></md-divider> <md-divider></md-divider>
<md-dialog-content> <md-dialog-content>
<div class="md-dialog-content"> <div class="md-dialog-content">
<fieldset ng-disabled="loading"> <fieldset ng-disabled="$root.loading">
<div ng-form name="aliasForm" flex layout="row" layout-align="start center" ng-repeat="entityAlias in vm.entityAliases track by $index"> <div ng-form name="aliasForm" flex layout="row" layout-align="start center" ng-repeat="entityAlias in vm.entityAliases track by $index">
<span flex="5">{{$index + 1}}.</span> <span flex="5">{{$index + 1}}.</span>
<di class="md-whiteframe-4dp tb-alias" flex layout="row" layout-align="start center"> <di class="md-whiteframe-4dp tb-alias" flex layout="row" layout-align="start center">
@ -63,7 +63,7 @@
aria-label="resolve-multiple-switcher"> aria-label="resolve-multiple-switcher">
</md-switch> </md-switch>
</section> </section>
<md-button ng-disabled="loading" class="md-icon-button md-primary" style="min-width: 40px;" <md-button ng-disabled="$root.loading" class="md-icon-button md-primary" style="min-width: 40px;"
ng-click="vm.editAlias($event, entityAlias)" aria-label="{{ 'action.edit' | translate }}"> ng-click="vm.editAlias($event, entityAlias)" aria-label="{{ 'action.edit' | translate }}">
<md-tooltip md-direction="top"> <md-tooltip md-direction="top">
{{ 'alias.edit' | translate }} {{ 'alias.edit' | translate }}
@ -72,7 +72,7 @@
edit edit
</md-icon> </md-icon>
</md-button> </md-button>
<md-button ng-disabled="loading" class="md-icon-button md-primary" style="min-width: 40px;" <md-button ng-disabled="$root.loading" class="md-icon-button md-primary" style="min-width: 40px;"
ng-click="vm.removeAlias($event, entityAlias)" aria-label="{{ 'action.remove' | translate }}"> ng-click="vm.removeAlias($event, entityAlias)" aria-label="{{ 'action.remove' | translate }}">
<md-tooltip md-direction="top"> <md-tooltip md-direction="top">
{{ 'entity.remove-alias' | translate }} {{ 'entity.remove-alias' | translate }}
@ -87,7 +87,7 @@
</div> </div>
</md-dialog-content> </md-dialog-content>
<md-dialog-actions layout="row"> <md-dialog-actions layout="row">
<md-button ng-show="!vm.disableAdd" ng-disabled="loading" class="md-primary md-raised" <md-button ng-show="!vm.disableAdd" ng-disabled="$root.loading" class="md-primary md-raised"
ng-click="vm.addAlias($event)" ng-click="vm.addAlias($event)"
aria-label="{{ 'alias.add' | translate }}"> aria-label="{{ 'alias.add' | translate }}">
<md-tooltip md-direction="top"> <md-tooltip md-direction="top">
@ -96,10 +96,10 @@
<span translate>alias.add</span> <span translate>alias.add</span>
</md-button> </md-button>
<span flex></span> <span flex></span>
<md-button ng-disabled="loading || theForm.$invalid || !theForm.$dirty" type="submit" class="md-raised md-primary"> <md-button ng-disabled="$root.loading || theForm.$invalid || !theForm.$dirty" type="submit" class="md-raised md-primary">
{{ 'action.save' | translate }} {{ 'action.save' | translate }}
</md-button> </md-button>
<md-button ng-disabled="loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | translate }}</md-button> <md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | translate }}</md-button>
</md-dialog-actions> </md-dialog-actions>
</form> </form>
</md-dialog> </md-dialog>

12
ui/src/app/entity/attribute/add-attribute-dialog.tpl.html

@ -26,12 +26,12 @@
</md-button> </md-button>
</div> </div>
</md-toolbar> </md-toolbar>
<md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!loading" ng-show="loading"></md-progress-linear> <md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
<span style="min-height: 5px;" flex="" ng-show="!loading"></span> <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
<md-dialog-content> <md-dialog-content>
<div class="md-dialog-content"> <div class="md-dialog-content">
<md-content class="md-padding" layout="column"> <md-content class="md-padding" layout="column">
<fieldset ng-disabled="loading"> <fieldset ng-disabled="$root.loading">
<md-input-container class="md-block"> <md-input-container class="md-block">
<label translate>attribute.key</label> <label translate>attribute.key</label>
<input required name="key" ng-model="vm.attribute.key"> <input required name="key" ng-model="vm.attribute.key">
@ -42,7 +42,7 @@
<section layout="row"> <section layout="row">
<md-input-container flex="40" class="md-block" style="width: 200px;"> <md-input-container flex="40" class="md-block" style="width: 200px;">
<label translate>value.type</label> <label translate>value.type</label>
<md-select ng-model="vm.valueType" ng-disabled="loading()"> <md-select ng-model="vm.valueType" ng-disabled="$root.loading">
<md-option ng-repeat="type in vm.valueTypes" ng-value="type"> <md-option ng-repeat="type in vm.valueTypes" ng-value="type">
<md-icon md-svg-icon="{{ type.icon }}"></md-icon> <md-icon md-svg-icon="{{ type.icon }}"></md-icon>
<span>{{type.name | translate}}</span> <span>{{type.name | translate}}</span>
@ -83,11 +83,11 @@
</md-dialog-content> </md-dialog-content>
<md-dialog-actions layout="row"> <md-dialog-actions layout="row">
<span flex></span> <span flex></span>
<md-button ng-disabled="loading || theForm.$invalid || !theForm.$dirty" type="submit" <md-button ng-disabled="$root.loading || theForm.$invalid || !theForm.$dirty" type="submit"
class="md-raised md-primary"> class="md-raised md-primary">
{{ 'action.add' | translate }} {{ 'action.add' | translate }}
</md-button> </md-button>
<md-button ng-disabled="loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | <md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' |
translate }} translate }}
</md-button> </md-button>
</md-dialog-actions> </md-dialog-actions>

12
ui/src/app/entity/attribute/add-widget-to-dashboard-dialog.tpl.html

@ -26,18 +26,18 @@
</md-button> </md-button>
</div> </div>
</md-toolbar> </md-toolbar>
<md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!loading" ng-show="loading"></md-progress-linear> <md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
<span style="min-height: 5px;" flex="" ng-show="!loading"></span> <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
<md-dialog-content> <md-dialog-content>
<div class="md-dialog-content"> <div class="md-dialog-content">
<md-content class="md-padding" layout="column"> <md-content class="md-padding" layout="column">
<fieldset ng-disabled="loading"> <fieldset ng-disabled="$root.loading">
<md-radio-group ng-model="vm.addToDashboardType" class="md-primary"> <md-radio-group ng-model="vm.addToDashboardType" class="md-primary">
<md-radio-button flex ng-value=0 class="md-primary md-align-top-left md-radio-interactive"> <md-radio-button flex ng-value=0 class="md-primary md-align-top-left md-radio-interactive">
<section flex layout="column" style="width: 300px;"> <section flex layout="column" style="width: 300px;">
<span translate style="padding-bottom: 10px;">dashboard.select-existing</span> <span translate style="padding-bottom: 10px;">dashboard.select-existing</span>
<tb-dashboard-autocomplete the-form="theForm" <tb-dashboard-autocomplete the-form="theForm"
ng-disabled="loading || vm.addToDashboardType != 0" ng-disabled="$root.loading || vm.addToDashboardType != 0"
tb-required="vm.addToDashboardType === 0" tb-required="vm.addToDashboardType === 0"
ng-model="vm.dashboardId" ng-model="vm.dashboardId"
select-first-dashboard="false"> select-first-dashboard="false">
@ -69,11 +69,11 @@
style="margin-bottom: 0px; padding-right: 20px;"> style="margin-bottom: 0px; padding-right: 20px;">
{{ 'dashboard.open-dashboard' | translate }} {{ 'dashboard.open-dashboard' | translate }}
</md-checkbox> </md-checkbox>
<md-button ng-disabled="loading || theForm.$invalid || !theForm.$dirty" type="submit" <md-button ng-disabled="$root.loading || theForm.$invalid || !theForm.$dirty" type="submit"
class="md-raised md-primary"> class="md-raised md-primary">
{{ 'action.add' | translate }} {{ 'action.add' | translate }}
</md-button> </md-button>
<md-button ng-disabled="loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | <md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' |
translate }} translate }}
</md-button> </md-button>
</md-dialog-actions> </md-dialog-actions>

2
ui/src/app/entity/attribute/attribute-table.scss

@ -47,7 +47,7 @@ md-toolbar.md-table-toolbar.alternate {
.widgets-carousel { .widgets-carousel {
position: relative; position: relative;
margin: 0px; margin: 0px;
height: calc(100% - 100px);
min-height: 150px !important; min-height: 150px !important;
tb-dashboard { tb-dashboard {

2
ui/src/app/entity/attribute/attribute-table.tpl.html

@ -19,7 +19,7 @@
<section ng-show="!disableAttributeScopeSelection"> <section ng-show="!disableAttributeScopeSelection">
<md-input-container class="md-block" style="width: 200px;"> <md-input-container class="md-block" style="width: 200px;">
<label translate>attribute.attributes-scope</label> <label translate>attribute.attributes-scope</label>
<md-select ng-model="attributeScope" ng-disabled="loading() || attributeScopeSelectionReadonly"> <md-select ng-model="attributeScope" ng-disabled="$root.loading || attributeScopeSelectionReadonly">
<md-option ng-repeat="scope in attributeScopes" ng-value="scope"> <md-option ng-repeat="scope in attributeScopes" ng-value="scope">
{{scope.name | translate}} {{scope.name | translate}}
</md-option> </md-option>

2
ui/src/app/entity/entity-autocomplete.directive.js

@ -38,7 +38,7 @@ export default function EntityAutocomplete($compile, $templateCache, $q, $filter
if (scope.excludeEntityIds && scope.excludeEntityIds.length) { if (scope.excludeEntityIds && scope.excludeEntityIds.length) {
limit += scope.excludeEntityIds.length; limit += scope.excludeEntityIds.length;
} }
entityService.getEntitiesByNameFilter(scope.entityType, searchText, limit, null, scope.entitySubtype).then(function success(result) { entityService.getEntitiesByNameFilter(scope.entityType, searchText, limit, {ignoreLoading: true}, scope.entitySubtype).then(function success(result) {
if (result) { if (result) {
if (scope.excludeEntityIds && scope.excludeEntityIds.length) { if (scope.excludeEntityIds && scope.excludeEntityIds.length) {
var entities = []; var entities = [];

2
ui/src/app/entity/entity-list.directive.js

@ -38,7 +38,7 @@ export default function EntityListDirective($compile, $templateCache, $q, $mdUti
scope.fetchEntities = function(searchText, limit) { scope.fetchEntities = function(searchText, limit) {
var deferred = $q.defer(); var deferred = $q.defer();
entityService.getEntitiesByNameFilter(scope.entityType, searchText, limit).then( entityService.getEntitiesByNameFilter(scope.entityType, searchText, limit, {ignoreLoading: true}).then(
function success(result) { function success(result) {
if (result) { if (result) {
deferred.resolve(result); deferred.resolve(result);

4
ui/src/app/entity/entity-subtype-autocomplete.directive.js

@ -93,9 +93,9 @@ export default function EntitySubtypeAutocomplete($compile, $templateCache, $q,
if (!scope.entitySubtypes) { if (!scope.entitySubtypes) {
var entitySubtypesPromise; var entitySubtypesPromise;
if (scope.entityType == types.entityType.asset) { if (scope.entityType == types.entityType.asset) {
entitySubtypesPromise = assetService.getAssetTypes(); entitySubtypesPromise = assetService.getAssetTypes({ignoreLoading: true});
} else if (scope.entityType == types.entityType.device) { } else if (scope.entityType == types.entityType.device) {
entitySubtypesPromise = deviceService.getDeviceTypes(); entitySubtypesPromise = deviceService.getDeviceTypes({ignoreLoading: true});
} }
if (entitySubtypesPromise) { if (entitySubtypesPromise) {
entitySubtypesPromise.then( entitySubtypesPromise.then(

4
ui/src/app/entity/entity-subtype-list.directive.js

@ -95,9 +95,9 @@ export default function EntitySubtypeListDirective($compile, $templateCache, $q,
if (!scope.entitySubtypes) { if (!scope.entitySubtypes) {
var entitySubtypesPromise; var entitySubtypesPromise;
if (scope.entityType == types.entityType.asset) { if (scope.entityType == types.entityType.asset) {
entitySubtypesPromise = assetService.getAssetTypes(); entitySubtypesPromise = assetService.getAssetTypes({ignoreLoading: true});
} else if (scope.entityType == types.entityType.device) { } else if (scope.entityType == types.entityType.device) {
entitySubtypesPromise = deviceService.getDeviceTypes(); entitySubtypesPromise = deviceService.getDeviceTypes({ignoreLoading: true});
} }
if (entitySubtypesPromise) { if (entitySubtypesPromise) {
entitySubtypesPromise.then( entitySubtypesPromise.then(

4
ui/src/app/entity/entity-subtype-select.directive.js

@ -73,9 +73,9 @@ export default function EntitySubtypeSelect($compile, $templateCache, $translate
scope.entitySubtypes = []; scope.entitySubtypes = [];
var entitySubtypesPromise; var entitySubtypesPromise;
if (scope.entityType == types.entityType.asset) { if (scope.entityType == types.entityType.asset) {
entitySubtypesPromise = assetService.getAssetTypes(); entitySubtypesPromise = assetService.getAssetTypes({ignoreLoading: true});
} else if (scope.entityType == types.entityType.device) { } else if (scope.entityType == types.entityType.device) {
entitySubtypesPromise = deviceService.getDeviceTypes(); entitySubtypesPromise = deviceService.getDeviceTypes({ignoreLoading: true});
} }
if (entitySubtypesPromise) { if (entitySubtypesPromise) {
entitySubtypesPromise.then( entitySubtypesPromise.then(

12
ui/src/app/entity/relation/relation-dialog.tpl.html

@ -26,16 +26,16 @@
</md-button> </md-button>
</div> </div>
</md-toolbar> </md-toolbar>
<md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!loading" ng-show="loading"></md-progress-linear> <md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
<span style="min-height: 5px;" flex="" ng-show="!loading"></span> <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
<md-dialog-content> <md-dialog-content>
<div class="md-dialog-content"> <div class="md-dialog-content">
<md-content class="md-padding" layout="column"> <md-content class="md-padding" layout="column">
<fieldset ng-disabled="loading"> <fieldset ng-disabled="$root.loading">
<tb-relation-type-autocomplete ng-disabled="!vm.isAdd" <tb-relation-type-autocomplete ng-disabled="!vm.isAdd"
ng-model="vm.relation.type" ng-model="vm.relation.type"
tb-required="true" tb-required="true"
ng-disabled="loading"> ng-disabled="$root.loading">
</tb-relation-type-autocomplete> </tb-relation-type-autocomplete>
<small>{{(vm.direction == vm.types.entitySearchDirection.from ? <small>{{(vm.direction == vm.types.entitySearchDirection.from ?
'relation.to-entity' : 'relation.from-entity') | translate}}</small> 'relation.to-entity' : 'relation.from-entity') | translate}}</small>
@ -61,11 +61,11 @@
</md-dialog-content> </md-dialog-content>
<md-dialog-actions layout="row"> <md-dialog-actions layout="row">
<span flex></span> <span flex></span>
<md-button ng-disabled="loading || theForm.$invalid || !theForm.$dirty" type="submit" <md-button ng-disabled="$root.loading || theForm.$invalid || !theForm.$dirty" type="submit"
class="md-raised md-primary"> class="md-raised md-primary">
{{ (vm.isAdd ? 'action.add' : 'action.save') | translate }} {{ (vm.isAdd ? 'action.add' : 'action.save') | translate }}
</md-button> </md-button>
<md-button ng-disabled="loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | <md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' |
translate }} translate }}
</md-button> </md-button>
</md-dialog-actions> </md-dialog-actions>

4
ui/src/app/entity/relation/relation-filters.tpl.html

@ -37,7 +37,7 @@
allowed-entity-types="allowedEntityTypes" allowed-entity-types="allowedEntityTypes"
tb-required="false"> tb-required="false">
</tb-entity-type-list> </tb-entity-type-list>
<md-button ng-disabled="loading" class="md-icon-button md-primary" style="width: 40px; min-width: 40px;" <md-button ng-disabled="$root.loading" class="md-icon-button md-primary" style="width: 40px; min-width: 40px;"
ng-click="removeFilter($event, filter)" aria-label="{{ 'action.remove' | translate }}"> ng-click="removeFilter($event, filter)" aria-label="{{ 'action.remove' | translate }}">
<md-tooltip md-direction="top"> <md-tooltip md-direction="top">
{{ 'relation.remove-relation-filter' | translate }} {{ 'relation.remove-relation-filter' | translate }}
@ -54,7 +54,7 @@
class="tb-prompt" translate>relation.any-relation</span> class="tb-prompt" translate>relation.any-relation</span>
</div> </div>
<div> <div>
<md-button ng-disabled="loading" class="md-primary md-raised" ng-click="addFilter($event)" aria-label="{{ 'action.add' | translate }}"> <md-button ng-disabled="$root.loading" class="md-primary md-raised" ng-click="addFilter($event)" aria-label="{{ 'action.add' | translate }}">
<md-tooltip md-direction="top"> <md-tooltip md-direction="top">
{{ 'relation.add-relation-filter' | translate }} {{ 'relation.add-relation-filter' | translate }}
</md-tooltip> </md-tooltip>

2
ui/src/app/entity/relation/relation-table.tpl.html

@ -19,7 +19,7 @@
<section layout="row"> <section layout="row">
<md-input-container class="md-block" style="width: 200px;"> <md-input-container class="md-block" style="width: 200px;">
<label translate>relation.direction</label> <label translate>relation.direction</label>
<md-select ng-model="vm.direction" ng-disabled="loading"> <md-select ng-model="vm.direction" ng-disabled="$root.loading">
<md-option ng-repeat="direction in vm.types.entitySearchDirection" ng-value="direction"> <md-option ng-repeat="direction in vm.types.entitySearchDirection" ng-value="direction">
{{ ('relation.search-direction.' + direction) | translate}} {{ ('relation.search-direction.' + direction) | translate}}
</md-option> </md-option>

2
ui/src/app/event/event-content-dialog.tpl.html

@ -35,7 +35,7 @@
</md-dialog-content> </md-dialog-content>
<md-dialog-actions layout="row"> <md-dialog-actions layout="row">
<span flex></span> <span flex></span>
<md-button ng-disabled="loading" ng-click="vm.close()" style="margin-right:20px;">{{ 'action.close' | <md-button ng-disabled="$root.loading" ng-click="vm.close()" style="margin-right:20px;">{{ 'action.close' |
translate }} translate }}
</md-button> </md-button>
</md-dialog-actions> </md-dialog-actions>

6
ui/src/app/event/event-table.tpl.html

@ -19,7 +19,7 @@
<section layout="row"> <section layout="row">
<md-input-container class="md-block" style="width: 200px;"> <md-input-container class="md-block" style="width: 200px;">
<label translate>event.event-type</label> <label translate>event.event-type</label>
<md-select ng-model="eventType" ng-disabled="loading()"> <md-select ng-model="eventType" ng-disabled="$root.loading">
<md-option ng-repeat="type in eventTypes" ng-value="type.value"> <md-option ng-repeat="type in eventTypes" ng-value="type.value">
{{type.name | translate}} {{type.name | translate}}
</md-option> </md-option>
@ -30,8 +30,8 @@
<md-list flex layout="column" class="md-whiteframe-z1 tb-event-table"> <md-list flex layout="column" class="md-whiteframe-z1 tb-event-table">
<md-list class="tb-row tb-header" layout="row" tb-event-header event-type="{{eventType}}"> <md-list class="tb-row tb-header" layout="row" tb-event-header event-type="{{eventType}}">
</md-list> </md-list>
<md-progress-linear style="max-height: 0px;" md-mode="indeterminate" ng-disabled="!loading()" <md-progress-linear style="max-height: 0px;" md-mode="indeterminate" ng-disabled="!$root.loading"
ng-show="loading()"></md-progress-linear> ng-show="$root.loading"></md-progress-linear>
<md-divider></md-divider> <md-divider></md-divider>
<span translate layout-align="center center" <span translate layout-align="center center"
style="margin-top: 25px;" style="margin-top: 25px;"

8
ui/src/app/extension/extension-dialog.tpl.html

@ -27,14 +27,14 @@
</div> </div>
</md-toolbar> </md-toolbar>
<md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!loading" ng-show="loading"></md-progress-linear> <md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
<span style="min-height: 5px;" flex="" ng-show="!loading"></span> <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
<md-dialog-content> <md-dialog-content>
<div class="md-dialog-content"> <div class="md-dialog-content">
<md-content class="md-padding" layout="column"> <md-content class="md-padding" layout="column">
<fieldset ng-disabled="loading"> <fieldset ng-disabled="$root.loading">
<section flex layout="row"> <section flex layout="row">
<md-input-container flex="60" class="md-block" md-is-error="theForm.extensionId.$touched && theForm.extensionId.$invalid"> <md-input-container flex="60" class="md-block" md-is-error="theForm.extensionId.$touched && theForm.extensionId.$invalid">
<label translate>extension.extension-id</label> <label translate>extension.extension-id</label>
@ -74,7 +74,7 @@
{{ (vm.isAdd ? 'action.add' : 'action.save') | translate }} {{ (vm.isAdd ? 'action.add' : 'action.save') | translate }}
</md-button> </md-button>
<md-button ng-disabled="loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | translate }} <md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | translate }}
</md-button> </md-button>
</md-dialog-actions> </md-dialog-actions>
</form> </form>

10
ui/src/app/import-export/import-dialog.tpl.html

@ -26,11 +26,11 @@
</md-button> </md-button>
</div> </div>
</md-toolbar> </md-toolbar>
<md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!loading" ng-show="loading"></md-progress-linear> <md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
<span style="min-height: 5px;" flex="" ng-show="!loading"></span> <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
<md-dialog-content> <md-dialog-content>
<div class="md-dialog-content"> <div class="md-dialog-content">
<fieldset ng-disabled="loading"> <fieldset ng-disabled="$root.loading">
<div layout="column" layout-padding> <div layout="column" layout-padding>
<div class="tb-container"> <div class="tb-container">
<label class="tb-label" translate>{{ vm.importFileLabel }}</label> <label class="tb-label" translate>{{ vm.importFileLabel }}</label>
@ -63,10 +63,10 @@
</md-dialog-content> </md-dialog-content>
<md-dialog-actions layout="row"> <md-dialog-actions layout="row">
<span flex></span> <span flex></span>
<md-button ng-disabled="loading || !theForm.$dirty || !theForm.$valid || !vm.importData" type="submit" class="md-raised md-primary"> <md-button ng-disabled="$root.loading || !theForm.$dirty || !theForm.$valid || !vm.importData" type="submit" class="md-raised md-primary">
{{ 'action.import' | translate }} {{ 'action.import' | translate }}
</md-button> </md-button>
<md-button ng-disabled="loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | translate }}</md-button> <md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | translate }}</md-button>
</md-dialog-actions> </md-dialog-actions>
</form> </form>
</md-dialog> </md-dialog>

2
ui/src/app/layout/home.tpl.html

@ -76,7 +76,7 @@
</tb-user-menu> </tb-user-menu>
</div> </div>
</md-toolbar> </md-toolbar>
<md-progress-linear class="md-warn" style="z-index: 10; max-height: 0px; width: 100%;" md-mode="indeterminate" ng-disabled="!loading" ng-show="loading"></md-progress-linear> <md-progress-linear class="md-warn" style="z-index: 10; max-height: 0px; width: 100%;" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
<div flex layout="column" id="toast-parent" style="position: relative;"> <div flex layout="column" id="toast-parent" style="position: relative;">
<md-content ng-cloak flex layout="column" class="page-content" ui-view name="content"></md-content> <md-content ng-cloak flex layout="column" class="page-content" ui-view name="content"></md-content>

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

@ -1213,6 +1213,7 @@ export default angular.module('thingsboard.locale', [])
"remove-widget-title": "Are you sure you want to remove the widget '{{widgetTitle}}'?", "remove-widget-title": "Are you sure you want to remove the widget '{{widgetTitle}}'?",
"remove-widget-text": "After the confirmation the widget and all related data will become unrecoverable.", "remove-widget-text": "After the confirmation the widget and all related data will become unrecoverable.",
"timeseries": "Time series", "timeseries": "Time series",
"search-data": "Search data",
"latest-values": "Latest values", "latest-values": "Latest values",
"rpc": "Control widget", "rpc": "Control widget",
"alarm": "Alarm widget", "alarm": "Alarm widget",

2
ui/src/app/login/create-password.tpl.html

@ -23,7 +23,7 @@
</md-card-title-text> </md-card-title-text>
</md-card-title> </md-card-title>
<md-progress-linear class="md-warn" style="z-index: 1; max-height: 5px; width: inherit; position: absolute" <md-progress-linear class="md-warn" style="z-index: 1; max-height: 5px; width: inherit; position: absolute"
md-mode="indeterminate" ng-disabled="!loading" ng-show="loading"></md-progress-linear> md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
<md-card-content> <md-card-content>
<form class="create-password-form" ng-submit="vm.createPassword()"> <form class="create-password-form" ng-submit="vm.createPassword()">
<div layout="column" layout-padding="" id="toast-parent"> <div layout="column" layout-padding="" id="toast-parent">

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

@ -23,7 +23,7 @@
</md-card-title-text> </md-card-title-text>
</md-card-title> </md-card-title>
<md-progress-linear class="md-warn" style="z-index: 1; max-height: 5px; width: inherit; position: absolute" <md-progress-linear class="md-warn" style="z-index: 1; max-height: 5px; width: inherit; position: absolute"
md-mode="indeterminate" ng-disabled="!loading" ng-show="loading"></md-progress-linear> md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
<md-card-content> <md-card-content>
<form class="login-form" ng-submit="vm.login()"> <form class="login-form" ng-submit="vm.login()">
<div layout="column" layout-padding="" id="toast-parent"> <div layout="column" layout-padding="" id="toast-parent">

2
ui/src/app/login/reset-password-request.tpl.html

@ -23,7 +23,7 @@
</md-card-title-text> </md-card-title-text>
</md-card-title> </md-card-title>
<md-progress-linear class="md-warn" style="z-index: 1; max-height: 5px; width: inherit; position: absolute" <md-progress-linear class="md-warn" style="z-index: 1; max-height: 5px; width: inherit; position: absolute"
md-mode="indeterminate" ng-disabled="!loading" ng-show="loading"></md-progress-linear> md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
<md-card-content> <md-card-content>
<form class="request-password-reset-form" ng-submit="vm.sendResetPasswordLink()"> <form class="request-password-reset-form" ng-submit="vm.sendResetPasswordLink()">
<div layout="column" layout-padding="" id="toast-parent"> <div layout="column" layout-padding="" id="toast-parent">

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save