Commit 69422fea0e98e1722ee25c1b6503b305c9b7341b

Authored by Volodymyr Babak
2 parents 3898e70c 5f478d9e

Merge branch 'master' of github.com:volodymyr-babak/thingsboard

Showing 100 changed files with 532 additions and 410 deletions

Too many changes to show.

To preserve performance only 100 of 129 files are displayed.

... ... @@ -126,10 +126,6 @@
126 126 <artifactId>jjwt</artifactId>
127 127 </dependency>
128 128 <dependency>
129   - <groupId>joda-time</groupId>
130   - <artifactId>joda-time</artifactId>
131   - </dependency>
132   - <dependency>
133 129 <groupId>org.apache.velocity</groupId>
134 130 <artifactId>velocity</artifactId>
135 131 </dependency>
... ...
... ... @@ -112,9 +112,9 @@
112 112 "templateHtml": "<tb-timeseries-table-widget \n table-id=\"tableId\"\n ctx=\"ctx\">\n</tb-timeseries-table-widget>",
113 113 "templateCss": "",
114 114 "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}",
115   - "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}",
  115 + "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}",
116 116 "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}",
117   - "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}"
  117 + "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\":{}}"
118 118 }
119 119 }
120 120 ]
... ...
... ... @@ -62,6 +62,7 @@ public final class PluginProcessingContext implements PluginContext {
62 62 private static final Executor executor = Executors.newSingleThreadExecutor();
63 63 public static final String CUSTOMER_USER_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION = "Customer user is not allowed to perform this operation!";
64 64 public static final String SYSTEM_ADMINISTRATOR_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION = "System administrator is not allowed to perform this operation!";
  65 + public static final String DEVICE_WITH_REQUESTED_ID_NOT_FOUND = "Device with requested id wasn't found!";
65 66
66 67 private final SharedPluginProcessingContext pluginCtx;
67 68 private final Optional<PluginApiCallSecurityContext> securityCtx;
... ... @@ -309,7 +310,7 @@ public final class PluginProcessingContext implements PluginContext {
309 310 ListenableFuture<Device> deviceFuture = pluginCtx.deviceService.findDeviceByIdAsync(new DeviceId(entityId.getId()));
310 311 Futures.addCallback(deviceFuture, getCallback(callback, device -> {
311 312 if (device == null) {
312   - return ValidationResult.entityNotFound("Device with requested id wasn't found!");
  313 + return ValidationResult.entityNotFound(DEVICE_WITH_REQUESTED_ID_NOT_FOUND);
313 314 } else {
314 315 if (!device.getTenantId().equals(ctx.getTenantId())) {
315 316 return ValidationResult.accessDenied("Device doesn't belong to the current Tenant!");
... ...
... ... @@ -47,8 +47,8 @@ public class WebSocketConfiguration implements WebSocketConfigurer {
47 47 @Bean
48 48 public ServletServerContainerFactoryBean createWebSocketContainer() {
49 49 ServletServerContainerFactoryBean container = new ServletServerContainerFactoryBean();
50   - container.setMaxTextMessageBufferSize(8192);
51   - container.setMaxBinaryMessageBufferSize(8192);
  50 + container.setMaxTextMessageBufferSize(32768);
  51 + container.setMaxBinaryMessageBufferSize(32768);
52 52 return container;
53 53 }
54 54
... ...
... ... @@ -82,7 +82,7 @@ public class CustomerController extends BaseController {
82 82
83 83 @PreAuthorize("hasAuthority('TENANT_ADMIN')")
84 84 @RequestMapping(value = "/customer", method = RequestMethod.POST)
85   - @ResponseBody
  85 + @ResponseBody
86 86 public Customer saveCustomer(@RequestBody Customer customer) throws ThingsboardException {
87 87 try {
88 88 customer.setTenantId(getCurrentUser().getTenantId());
... ... @@ -107,7 +107,7 @@ public class CustomerController extends BaseController {
107 107 }
108 108
109 109 @PreAuthorize("hasAuthority('TENANT_ADMIN')")
110   - @RequestMapping(value = "/customers", params = { "limit" }, method = RequestMethod.GET)
  110 + @RequestMapping(value = "/customers", params = {"limit"}, method = RequestMethod.GET)
111 111 @ResponseBody
112 112 public TextPageData<Customer> getCustomers(@RequestParam int limit,
113 113 @RequestParam(required = false) String textSearch,
... ... @@ -122,4 +122,16 @@ public class CustomerController extends BaseController {
122 122 }
123 123 }
124 124
  125 + @PreAuthorize("hasAuthority('TENANT_ADMIN')")
  126 + @RequestMapping(value = "/tenant/customers", params = {"customerTitle"}, method = RequestMethod.GET)
  127 + @ResponseBody
  128 + public Customer getTenantCustomer(
  129 + @RequestParam String customerTitle) throws ThingsboardException {
  130 + try {
  131 + TenantId tenantId = getCurrentUser().getTenantId();
  132 + return checkNotNull(customerService.findCustomerByTenantIdAndTitle(tenantId, customerTitle));
  133 + } catch (Exception e) {
  134 + throw handleException(e);
  135 + }
  136 + }
125 137 }
... ...
... ... @@ -77,7 +77,6 @@ public class PluginWebSocketHandler extends TextWebSocketHandler implements Plug
77 77 log.warn("[{}] Failed to find session", session.getId());
78 78 session.close(CloseStatus.SERVER_ERROR.withReason("Session not found!"));
79 79 }
80   - session.sendMessage(message);
81 80 } catch (IOException e) {
82 81 log.warn("IO error", e);
83 82 }
... ...
... ... @@ -96,7 +96,7 @@ public class DefaultMailService implements MailService {
96 96 javaMailProperties.put(MAIL_PROP + protocol + ".port", jsonConfig.get("smtpPort").asText());
97 97 javaMailProperties.put(MAIL_PROP + protocol + ".timeout", jsonConfig.get("timeout").asText());
98 98 javaMailProperties.put(MAIL_PROP + protocol + ".auth", String.valueOf(StringUtils.isNotEmpty(jsonConfig.get("username").asText())));
99   - javaMailProperties.put(MAIL_PROP + protocol + ".starttls.enable", jsonConfig.get("enableTls"));
  99 + javaMailProperties.put(MAIL_PROP + protocol + ".starttls.enable", jsonConfig.has("enableTls") ? jsonConfig.get("enableTls").asText() : "false");
100 100 return javaMailProperties;
101 101 }
102 102
... ...
... ... @@ -20,7 +20,6 @@ import io.jsonwebtoken.Jws;
20 20 import io.jsonwebtoken.Jwts;
21 21 import io.jsonwebtoken.SignatureAlgorithm;
22 22 import org.apache.commons.lang3.StringUtils;
23   -import org.joda.time.DateTime;
24 23 import org.springframework.beans.factory.annotation.Autowired;
25 24 import org.springframework.stereotype.Component;
26 25 import org.thingsboard.server.common.data.id.CustomerId;
... ... @@ -31,7 +30,9 @@ import org.thingsboard.server.config.JwtSettings;
31 30 import org.thingsboard.server.service.security.model.SecurityUser;
32 31 import org.thingsboard.server.service.security.model.UserPrincipal;
33 32
34   -import java.util.Arrays;
  33 +import java.time.ZonedDateTime;
  34 +import java.util.Collections;
  35 +import java.util.Date;
35 36 import java.util.List;
36 37 import java.util.UUID;
37 38 import java.util.stream.Collectors;
... ... @@ -81,13 +82,13 @@ public class JwtTokenFactory {
81 82 claims.put(CUSTOMER_ID, securityUser.getCustomerId().getId().toString());
82 83 }
83 84
84   - DateTime currentTime = new DateTime();
  85 + ZonedDateTime currentTime = ZonedDateTime.now();
85 86
86 87 String token = Jwts.builder()
87 88 .setClaims(claims)
88 89 .setIssuer(settings.getTokenIssuer())
89   - .setIssuedAt(currentTime.toDate())
90   - .setExpiration(currentTime.plusSeconds(settings.getTokenExpirationTime()).toDate())
  90 + .setIssuedAt(Date.from(currentTime.toInstant()))
  91 + .setExpiration(Date.from(currentTime.plusSeconds(settings.getTokenExpirationTime()).toInstant()))
91 92 .signWith(SignatureAlgorithm.HS512, settings.getTokenSigningKey())
92 93 .compact();
93 94
... ... @@ -129,11 +130,11 @@ public class JwtTokenFactory {
129 130 throw new IllegalArgumentException("Cannot create JWT Token without username/email");
130 131 }
131 132
132   - DateTime currentTime = new DateTime();
  133 + ZonedDateTime currentTime = ZonedDateTime.now();
133 134
134 135 UserPrincipal principal = securityUser.getUserPrincipal();
135 136 Claims claims = Jwts.claims().setSubject(principal.getValue());
136   - claims.put(SCOPES, Arrays.asList(Authority.REFRESH_TOKEN.name()));
  137 + claims.put(SCOPES, Collections.singletonList(Authority.REFRESH_TOKEN.name()));
137 138 claims.put(USER_ID, securityUser.getId().getId().toString());
138 139 claims.put(IS_PUBLIC, principal.getType() == UserPrincipal.Type.PUBLIC_ID);
139 140
... ... @@ -141,8 +142,8 @@ public class JwtTokenFactory {
141 142 .setClaims(claims)
142 143 .setIssuer(settings.getTokenIssuer())
143 144 .setId(UUID.randomUUID().toString())
144   - .setIssuedAt(currentTime.toDate())
145   - .setExpiration(currentTime.plusSeconds(settings.getRefreshTokenExpTime()).toDate())
  145 + .setIssuedAt(Date.from(currentTime.toInstant()))
  146 + .setExpiration(Date.from(currentTime.plusSeconds(settings.getRefreshTokenExpTime()).toInstant()))
146 147 .signWith(SignatureAlgorithm.HS512, settings.getTokenSigningKey())
147 148 .compact();
148 149
... ...
... ... @@ -107,6 +107,11 @@ public abstract class AbstractControllerTest {
107 107 protected static final String CUSTOMER_USER_EMAIL = "testcustomer@thingsboard.org";
108 108 private static final String CUSTOMER_USER_PASSWORD = "customer";
109 109
  110 + /** See {@link org.springframework.test.web.servlet.DefaultMvcResult#getAsyncResult(long)}
  111 + * and {@link org.springframework.mock.web.MockAsyncContext#getTimeout()}
  112 + */
  113 + private static final long DEFAULT_TIMEOUT = -1L;
  114 +
110 115 protected MediaType contentType = new MediaType(MediaType.APPLICATION_JSON.getType(),
111 116 MediaType.APPLICATION_JSON.getSubtype(),
112 117 Charset.forName("utf8"));
... ... @@ -366,7 +371,7 @@ public abstract class AbstractControllerTest {
366 371 }
367 372
368 373 protected <T> T doPost(String urlTemplate, T content, Class<T> responseClass, ResultMatcher resultMatcher, String... params) throws Exception {
369   - return readResponse(doPost(urlTemplate, params).andExpect(resultMatcher), responseClass);
  374 + return readResponse(doPost(urlTemplate, content, params).andExpect(resultMatcher), responseClass);
370 375 }
371 376
372 377 protected <T> T doPost(String urlTemplate, T content, Class<T> responseClass, String... params) throws Exception {
... ... @@ -374,7 +379,11 @@ public abstract class AbstractControllerTest {
374 379 }
375 380
376 381 protected <T> T doPostAsync(String urlTemplate, T content, Class<T> responseClass, ResultMatcher resultMatcher, String... params) throws Exception {
377   - return readResponse(doPostAsync(urlTemplate, content, params).andExpect(resultMatcher), responseClass);
  382 + return readResponse(doPostAsync(urlTemplate, content, DEFAULT_TIMEOUT, params).andExpect(resultMatcher), responseClass);
  383 + }
  384 +
  385 + protected <T> T doPostAsync(String urlTemplate, T content, Class<T> responseClass, ResultMatcher resultMatcher, Long timeout, String... params) throws Exception {
  386 + return readResponse(doPostAsync(urlTemplate, content, timeout, params).andExpect(resultMatcher), responseClass);
378 387 }
379 388
380 389 protected <T> T doDelete(String urlTemplate, Class<T> responseClass, String... params) throws Exception {
... ... @@ -396,12 +405,13 @@ public abstract class AbstractControllerTest {
396 405 return mockMvc.perform(postRequest);
397 406 }
398 407
399   - protected <T> ResultActions doPostAsync(String urlTemplate, T content, String... params) throws Exception {
  408 + protected <T> ResultActions doPostAsync(String urlTemplate, T content, Long timeout, String... params) throws Exception {
400 409 MockHttpServletRequestBuilder postRequest = post(urlTemplate);
401 410 setJwtToken(postRequest);
402 411 String json = json(content);
403 412 postRequest.contentType(contentType).content(json);
404 413 MvcResult result = mockMvc.perform(postRequest).andReturn();
  414 + result.getAsyncResult(timeout);
405 415 return mockMvc.perform(asyncDispatch(result));
406 416 }
407 417
... ... @@ -414,8 +424,8 @@ public abstract class AbstractControllerTest {
414 424
415 425 protected void populateParams(MockHttpServletRequestBuilder request, String... params) {
416 426 if (params != null && params.length > 0) {
417   - Assert.assertEquals(params.length % 2, 0);
418   - MultiValueMap<String, String> paramsMap = new LinkedMultiValueMap<String, String>();
  427 + Assert.assertEquals(0, params.length % 2);
  428 + MultiValueMap<String, String> paramsMap = new LinkedMultiValueMap<>();
419 429 for (int i = 0; i < params.length; i += 2) {
420 430 paramsMap.add(params[i], params[i + 1]);
421 431 }
... ...
... ... @@ -15,21 +15,23 @@
15 15 */
16 16 package org.thingsboard.server.mqtt.rpc;
17 17
  18 +import java.util.Arrays;
  19 +
  20 +import com.datastax.driver.core.utils.UUIDs;
  21 +import com.fasterxml.jackson.core.type.TypeReference;
18 22 import lombok.extern.slf4j.Slf4j;
19 23 import org.apache.commons.lang3.StringUtils;
20 24 import org.eclipse.paho.client.mqttv3.*;
21 25 import org.junit.*;
22   -import org.springframework.http.HttpStatus;
23   -import org.springframework.web.client.HttpClientErrorException;
  26 +import org.thingsboard.server.actors.plugin.PluginProcessingContext;
24 27 import org.thingsboard.server.common.data.Device;
25 28 import org.thingsboard.server.common.data.Tenant;
26 29 import org.thingsboard.server.common.data.User;
  30 +import org.thingsboard.server.common.data.page.TextPageData;
  31 +import org.thingsboard.server.common.data.plugin.PluginMetaData;
27 32 import org.thingsboard.server.common.data.security.Authority;
28 33 import org.thingsboard.server.common.data.security.DeviceCredentials;
29 34 import org.thingsboard.server.controller.AbstractControllerTest;
30   -import org.thingsboard.server.dao.service.DaoNoSqlTest;
31   -
32   -import java.util.UUID;
33 35
34 36 import static org.junit.Assert.assertEquals;
35 37 import static org.junit.Assert.assertNotNull;
... ... @@ -42,15 +44,19 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
42 44 public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractControllerTest {
43 45
44 46 private static final String MQTT_URL = "tcp://localhost:1883";
45   - private static final String FAIL_MSG_IF_HTTP_CLIENT_ERROR_NOT_ENCOUNTERED = "HttpClientErrorException expected, but not encountered";
  47 + private static final Long TIME_TO_HANDLE_REQUEST = 500L;
46 48
47 49 private Tenant savedTenant;
48 50 private User tenantAdmin;
  51 + private Long asyncContextTimeoutToUseRpcPlugin;
  52 +
49 53
50 54 @Before
51 55 public void beforeTest() throws Exception {
52 56 loginSysAdmin();
53 57
  58 + asyncContextTimeoutToUseRpcPlugin = getAsyncContextTimeoutToUseRpcPlugin();
  59 +
54 60 Tenant tenant = new Tenant();
55 61 tenant.setTitle("My tenant");
56 62 savedTenant = doPost("/api/tenant", tenant, Tenant.class);
... ... @@ -70,8 +76,7 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractC
70 76 public void afterTest() throws Exception {
71 77 loginSysAdmin();
72 78 if (savedTenant != null) {
73   - doDelete("/api/tenant/" + savedTenant.getId().getId().toString())
74   - .andExpect(status().isOk());
  79 + doDelete("/api/tenant/" + savedTenant.getId().getId().toString()).andExpect(status().isOk());
75 80 }
76 81 }
77 82
... ... @@ -102,7 +107,6 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractC
102 107 }
103 108
104 109 @Test
105   - @Ignore // TODO: figure out the right error code for this case. Ignored due to failure: expected 408 but was: 200
106 110 public void testServerMqttOneWayRpcDeviceOffline() throws Exception {
107 111 Device device = new Device();
108 112 device.setName("Test One-Way Server-Side RPC Device Offline");
... ... @@ -115,29 +119,19 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractC
115 119
116 120 String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"23\",\"value\": 1}}";
117 121 String deviceId = savedDevice.getId().getId().toString();
118   - try {
119   - doPost("/api/plugins/rpc/oneway/" + deviceId, setGpioRequest, String.class, status().is(408));
120   - Assert.fail(FAIL_MSG_IF_HTTP_CLIENT_ERROR_NOT_ENCOUNTERED);
121   - } catch (HttpClientErrorException e) {
122   - log.error(e.getMessage(), e);
123   - Assert.assertEquals(HttpStatus.REQUEST_TIMEOUT, e.getStatusCode());
124   - Assert.assertEquals("408 null", e.getMessage());
125   - }
  122 +
  123 + doPostAsync("/api/plugins/rpc/oneway/" + deviceId, setGpioRequest, String.class, status().isRequestTimeout(),
  124 + asyncContextTimeoutToUseRpcPlugin);
126 125 }
127 126
128 127 @Test
129   - @Ignore // TODO: figure out the right error code for this case. Ignored due to failure: expected 400 (404?) but was: 401
130 128 public void testServerMqttOneWayRpcDeviceDoesNotExist() throws Exception {
131 129 String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"23\",\"value\": 1}}";
132   - String nonExistentDeviceId = UUID.randomUUID().toString();
133   - try {
134   - doPostAsync("/api/plugins/rpc/oneway/" + nonExistentDeviceId, setGpioRequest, String.class, status().is(400));
135   - Assert.fail(FAIL_MSG_IF_HTTP_CLIENT_ERROR_NOT_ENCOUNTERED);
136   - } catch (HttpClientErrorException e) {
137   - log.error(e.getMessage(), e);
138   - Assert.assertEquals(HttpStatus.BAD_REQUEST, e.getStatusCode());
139   - Assert.assertEquals("400 null", e.getMessage());
140   - }
  130 + String nonExistentDeviceId = UUIDs.timeBased().toString();
  131 +
  132 + String result = doPostAsync("/api/plugins/rpc/oneway/" + nonExistentDeviceId, setGpioRequest, String.class,
  133 + status().isNotFound());
  134 + Assert.assertEquals(PluginProcessingContext.DEVICE_WITH_REQUESTED_ID_NOT_FOUND, result);
141 135 }
142 136
143 137 @Test
... ... @@ -168,7 +162,6 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractC
168 162 }
169 163
170 164 @Test
171   - @Ignore // TODO: figure out the right error code for this case. Ignored due to failure: expected 408 but was: 200
172 165 public void testServerMqttTwoWayRpcDeviceOffline() throws Exception {
173 166 Device device = new Device();
174 167 device.setName("Test Two-Way Server-Side RPC Device Offline");
... ... @@ -181,29 +174,19 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractC
181 174
182 175 String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"23\",\"value\": 1}}";
183 176 String deviceId = savedDevice.getId().getId().toString();
184   - try {
185   - doPost("/api/plugins/rpc/twoway/" + deviceId, setGpioRequest, String.class, status().is(408));
186   - Assert.fail(FAIL_MSG_IF_HTTP_CLIENT_ERROR_NOT_ENCOUNTERED);
187   - } catch (HttpClientErrorException e) {
188   - log.error(e.getMessage(), e);
189   - Assert.assertEquals(HttpStatus.REQUEST_TIMEOUT, e.getStatusCode());
190   - Assert.assertEquals("408 null", e.getMessage());
191   - }
  177 +
  178 + doPostAsync("/api/plugins/rpc/twoway/" + deviceId, setGpioRequest, String.class, status().isRequestTimeout(),
  179 + asyncContextTimeoutToUseRpcPlugin);
192 180 }
193 181
194 182 @Test
195   - @Ignore // TODO: figure out the right error code for this case. Ignored due to failure: expected 400 (404?) but was: 401
196 183 public void testServerMqttTwoWayRpcDeviceDoesNotExist() throws Exception {
197 184 String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"23\",\"value\": 1}}";
198   - String nonExistentDeviceId = UUID.randomUUID().toString();
199   - try {
200   - doPostAsync("/api/plugins/rpc/oneway/" + nonExistentDeviceId, setGpioRequest, String.class, status().is(400));
201   - Assert.fail(FAIL_MSG_IF_HTTP_CLIENT_ERROR_NOT_ENCOUNTERED);
202   - } catch (HttpClientErrorException e) {
203   - log.error(e.getMessage(), e);
204   - Assert.assertEquals(HttpStatus.BAD_REQUEST, e.getStatusCode());
205   - Assert.assertEquals("400 null", e.getMessage());
206   - }
  185 + String nonExistentDeviceId = UUIDs.timeBased().toString();
  186 +
  187 + String result = doPostAsync("/api/plugins/rpc/twoway/" + nonExistentDeviceId, setGpioRequest, String.class,
  188 + status().isNotFound());
  189 + Assert.assertEquals(PluginProcessingContext.DEVICE_WITH_REQUESTED_ID_NOT_FOUND, result);
207 190 }
208 191
209 192 private Device getSavedDevice(Device device) throws Exception {
... ... @@ -214,6 +197,13 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractC
214 197 return doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class);
215 198 }
216 199
  200 + private Long getAsyncContextTimeoutToUseRpcPlugin() throws Exception {
  201 + TextPageData<PluginMetaData> plugins = doGetTyped("/api/plugin/system?limit=1&textSearch=system rpc plugin",
  202 + new TypeReference<TextPageData<PluginMetaData>>(){});
  203 + Long systemRpcPluginTimeout = plugins.getData().iterator().next().getConfiguration().get("defaultTimeout").asLong();
  204 + return systemRpcPluginTimeout + TIME_TO_HANDLE_REQUEST;
  205 + }
  206 +
217 207 private static class TestMqttCallback implements MqttCallback {
218 208
219 209 private final MqttAsyncClient client;
... ... @@ -228,10 +218,10 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractC
228 218
229 219 @Override
230 220 public void messageArrived(String requestTopic, MqttMessage mqttMessage) throws Exception {
231   - log.info("Message Arrived: " + mqttMessage.getPayload().toString());
  221 + log.info("Message Arrived: " + Arrays.toString(mqttMessage.getPayload()));
232 222 MqttMessage message = new MqttMessage();
233 223 String responseTopic = requestTopic.replace("request", "response");
234   - message.setPayload("{\"value1\":\"A\", \"value2\":\"B\"}".getBytes());
  224 + message.setPayload("{\"value1\":\"A\", \"value2\":\"B\"}".getBytes("UTF-8"));
235 225 client.publish(responseTopic, message);
236 226 }
237 227
... ...
... ... @@ -22,20 +22,24 @@ import org.thingsboard.server.common.data.id.TenantId;
22 22 import org.thingsboard.server.common.data.page.TextPageData;
23 23 import org.thingsboard.server.common.data.page.TextPageLink;
24 24
  25 +import java.util.Optional;
  26 +
25 27 public interface CustomerService {
26 28
27 29 Customer findCustomerById(CustomerId customerId);
28 30
  31 + Optional<Customer> findCustomerByTenantIdAndTitle(TenantId tenantId, String title);
  32 +
29 33 ListenableFuture<Customer> findCustomerByIdAsync(CustomerId customerId);
30 34
31 35 Customer saveCustomer(Customer customer);
32   -
  36 +
33 37 void deleteCustomer(CustomerId customerId);
34 38
35 39 Customer findOrCreatePublicCustomer(TenantId tenantId);
36 40
37 41 TextPageData<Customer> findCustomersByTenantId(TenantId tenantId, TextPageLink pageLink);
38   -
  42 +
39 43 void deleteCustomersByTenantId(TenantId tenantId);
40 44
41 45 }
... ...
... ... @@ -52,6 +52,7 @@ public class CustomerServiceImpl extends AbstractEntityService implements Custom
52 52
53 53 private static final String PUBLIC_CUSTOMER_TITLE = "Public";
54 54 public static final String INCORRECT_CUSTOMER_ID = "Incorrect customerId ";
  55 + public static final String INCORRECT_TENANT_ID = "Incorrect tenantId ";
55 56
56 57 @Autowired
57 58 private CustomerDao customerDao;
... ... @@ -79,6 +80,13 @@ public class CustomerServiceImpl extends AbstractEntityService implements Custom
79 80 }
80 81
81 82 @Override
  83 + public Optional<Customer> findCustomerByTenantIdAndTitle(TenantId tenantId, String title) {
  84 + log.trace("Executing findCustomerByTenantIdAndTitle [{}] [{}]", tenantId, title);
  85 + validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
  86 + return customerDao.findCustomersByTenantIdAndTitle(tenantId.getId(), title);
  87 + }
  88 +
  89 + @Override
82 90 public ListenableFuture<Customer> findCustomerByIdAsync(CustomerId customerId) {
83 91 log.trace("Executing findCustomerByIdAsync [{}]", customerId);
84 92 validateId(customerId, INCORRECT_CUSTOMER_ID + customerId);
... ...
... ... @@ -33,8 +33,7 @@
33 33 <spring.version>4.3.4.RELEASE</spring.version>
34 34 <spring-security.version>4.2.0.RELEASE</spring-security.version>
35 35 <jjwt.version>0.7.0</jjwt.version>
36   - <joda-time.version>2.4</joda-time.version>
37   - <json-path.version>2.2.0</json-path.version>
  36 + <json-path.version>2.2.0</json-path.version>
38 37 <junit.version>4.12</junit.version>
39 38 <slf4j.version>1.7.7</slf4j.version>
40 39 <logback.version>1.2.3</logback.version>
... ... @@ -484,11 +483,6 @@
484 483 <version>${jjwt.version}</version>
485 484 </dependency>
486 485 <dependency>
487   - <groupId>joda-time</groupId>
488   - <artifactId>joda-time</artifactId>
489   - <version>${joda-time.version}</version>
490   - </dependency>
491   - <dependency>
492 486 <groupId>org.apache.velocity</groupId>
493 487 <artifactId>velocity</artifactId>
494 488 <version>${velocity.version}</version>
... ...
... ... @@ -29,13 +29,12 @@ import org.springframework.web.client.RestTemplate;
29 29 import org.thingsboard.server.common.data.Customer;
30 30 import org.thingsboard.server.common.data.Device;
31 31 import org.thingsboard.server.common.data.alarm.Alarm;
32   -import org.thingsboard.server.common.data.alarm.AlarmSeverity;
33   -import org.thingsboard.server.common.data.alarm.AlarmStatus;
34 32 import org.thingsboard.server.common.data.asset.Asset;
35 33 import org.thingsboard.server.common.data.id.AssetId;
36 34 import org.thingsboard.server.common.data.id.CustomerId;
37 35 import org.thingsboard.server.common.data.id.DeviceId;
38 36 import org.thingsboard.server.common.data.id.EntityId;
  37 +import org.thingsboard.server.common.data.relation.EntityRelation;
39 38 import org.thingsboard.server.common.data.security.DeviceCredentials;
40 39
41 40 import java.io.IOException;
... ... @@ -78,6 +77,36 @@ public class RestClient implements ClientHttpRequestInterceptor {
78 77 }
79 78 }
80 79
  80 + public Optional<Customer> findCustomer(String title) {
  81 + Map<String, String> params = new HashMap<String, String>();
  82 + params.put("customerTitle", title);
  83 + try {
  84 + ResponseEntity<Customer> customerEntity = restTemplate.getForEntity(baseURL + "/api/tenant/customers?customerTitle={customerTitle}", Customer.class, params);
  85 + return Optional.of(customerEntity.getBody());
  86 + } catch (HttpClientErrorException exception) {
  87 + if (exception.getStatusCode() == HttpStatus.NOT_FOUND) {
  88 + return Optional.empty();
  89 + } else {
  90 + throw exception;
  91 + }
  92 + }
  93 + }
  94 +
  95 + public Optional<Asset> findAsset(String name) {
  96 + Map<String, String> params = new HashMap<String, String>();
  97 + params.put("assetName", name);
  98 + try {
  99 + ResponseEntity<Asset> assetEntity = restTemplate.getForEntity(baseURL + "/api/tenant/assets?assetName={assetName}", Asset.class, params);
  100 + return Optional.of(assetEntity.getBody());
  101 + } catch (HttpClientErrorException exception) {
  102 + if (exception.getStatusCode() == HttpStatus.NOT_FOUND) {
  103 + return Optional.empty();
  104 + } else {
  105 + throw exception;
  106 + }
  107 + }
  108 + }
  109 +
81 110 public Customer createCustomer(String title) {
82 111 Customer customer = new Customer();
83 112 customer.setTitle(title);
... ... @@ -112,6 +141,14 @@ public class RestClient implements ClientHttpRequestInterceptor {
112 141 customerId.toString(), assetId.toString()).getBody();
113 142 }
114 143
  144 + public EntityRelation makeRelation(String relationType, EntityId idFrom, EntityId idTo) {
  145 + EntityRelation relation = new EntityRelation();
  146 + relation.setFrom(idFrom);
  147 + relation.setTo(idTo);
  148 + relation.setType(relationType);
  149 + return restTemplate.postForEntity(baseURL + "/api/relation", relation, EntityRelation.class).getBody();
  150 + }
  151 +
115 152 public DeviceCredentials getCredentials(DeviceId id) {
116 153 return restTemplate.getForEntity(baseURL + "/api/device/" + id.getId().toString() + "/credentials", DeviceCredentials.class).getBody();
117 154 }
... ...
... ... @@ -74,13 +74,13 @@ echo "Generating SSL Key Pair..."
74 74
75 75 keytool -genkeypair -v \
76 76 -alias $CLIENT_KEY_ALIAS \
77   - -dname "CN=$DOMAIN_SUFFIX, OU=$ORGANIZATIONAL_UNIT, O=$ORGANIZATION, L=$CITY, ST=$STATE_OR_PROVINCE, C=$TWO_LETTER_COUNTRY_CODE" \
78 77 -keystore $CLIENT_FILE_PREFIX.jks \
79 78 -keypass $CLIENT_KEY_PASSWORD \
80 79 -storepass $CLIENT_KEYSTORE_PASSWORD \
81 80 -keyalg RSA \
82 81 -keysize 2048 \
83   - -validity 9999
  82 + -validity 9999 \
  83 + -dname "CN=$DOMAIN_SUFFIX, OU=$ORGANIZATIONAL_UNIT, O=$ORGANIZATION, L=$CITY, ST=$STATE_OR_PROVINCE, C=$TWO_LETTER_COUNTRY_CODE"
84 84
85 85 echo "Converting keystore to pkcs12"
86 86 keytool -importkeystore \
... ...
... ... @@ -17,7 +17,7 @@
17 17 DOMAIN_SUFFIX="$(hostname)"
18 18 ORGANIZATIONAL_UNIT=Thingsboard
19 19 ORGANIZATION=Thingsboard
20   -CITY=San Francisco
  20 +CITY=SF
21 21 STATE_OR_PROVINCE=CA
22 22 TWO_LETTER_COUNTRY_CODE=US
23 23
... ...
... ... @@ -22,11 +22,11 @@
22 22 <span translate class="md-headline">admin.general-settings</span>
23 23 </md-card-title-text>
24 24 </md-card-title>
25   - <md-progress-linear md-mode="indeterminate" ng-disabled="!loading" ng-show="loading"></md-progress-linear>
26   - <span style="min-height: 5px;" flex="" ng-show="!loading"></span>
  25 + <md-progress-linear md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
  26 + <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
27 27 <md-card-content>
28 28 <form name="vm.settingsForm" ng-submit="vm.save()" tb-confirm-on-exit confirm-form="vm.settingsForm">
29   - <fieldset ng-disabled="loading">
  29 + <fieldset ng-disabled="$root.loading">
30 30 <md-input-container class="md-block">
31 31 <label translate>admin.base-url</label>
32 32 <input required name="baseUrl" ng-model="vm.settings.jsonValue.baseUrl">
... ... @@ -35,7 +35,7 @@
35 35 </div>
36 36 </md-input-container>
37 37 <div layout="row" layout-align="end center" width="100%" layout-wrap>
38   - <md-button ng-disabled="loading || vm.settingsForm.$invalid || !vm.settingsForm.$dirty" type="submit" class="md-raised md-primary">{{'action.save' | translate}}</md-button>
  38 + <md-button ng-disabled="$root.loading || vm.settingsForm.$invalid || !vm.settingsForm.$dirty" type="submit" class="md-raised md-primary">{{'action.save' | translate}}</md-button>
39 39 </div>
40 40 </fieldset>
41 41 </form>
... ...
... ... @@ -24,11 +24,11 @@
24 24 <div id="help-container"></div>
25 25 </md-card-title-text>
26 26 </md-card-title>
27   - <md-progress-linear md-mode="indeterminate" ng-disabled="!loading" ng-show="loading"></md-progress-linear>
28   - <span style="min-height: 5px;" flex="" ng-show="!loading"></span>
  27 + <md-progress-linear md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
  28 + <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
29 29 <md-card-content>
30 30 <form name="vm.settingsForm" ng-submit="vm.save()" tb-confirm-on-exit confirm-form="vm.settingsForm">
31   - <fieldset ng-disabled="loading">
  31 + <fieldset ng-disabled="$root.loading">
32 32 <md-input-container class="md-block">
33 33 <label translate>admin.mail-from</label>
34 34 <input required name="mailFrom" ng-model="vm.settings.jsonValue.mailFrom">
... ... @@ -38,7 +38,7 @@
38 38 </md-input-container>
39 39 <md-input-container class="md-block">
40 40 <label translate>admin.smtp-protocol</label>
41   - <md-select ng-disabled="loading" ng-model="vm.settings.jsonValue.smtpProtocol">
  41 + <md-select ng-disabled="$root.loading" ng-model="vm.settings.jsonValue.smtpProtocol">
42 42 <md-option ng-repeat="smtpProtocol in vm.smtpProtocols" value="{{smtpProtocol}}">
43 43 {{smtpProtocol.toUpperCase()}}
44 44 </md-option>
... ... @@ -78,7 +78,7 @@
78 78 <div translate ng-message="md-maxlength">admin.timeout-invalid</div>
79 79 </div>
80 80 </md-input-container>
81   - <md-checkbox ng-disabled="loading" ng-true-value="'true'" ng-false-value="'false'"
  81 + <md-checkbox ng-disabled="$root.loading" ng-true-value="'true'" ng-false-value="'false'"
82 82 aria-label="{{ 'admin.enable-tls' | translate }}" ng-model="vm.settings.jsonValue.enableTls">{{ 'admin.enable-tls' | translate }}</md-checkbox>
83 83 <md-input-container class="md-block">
84 84 <label translate>common.username</label>
... ... @@ -89,8 +89,8 @@
89 89 <input name="password" placeholder="{{ 'common.enter-password' | translate }}" type="password" ng-model="vm.settings.jsonValue.password">
90 90 </md-input-container>
91 91 <div layout="row" layout-align="end center" width="100%" layout-wrap>
92   - <md-button ng-disabled="loading || vm.settingsForm.$invalid" ng-click="vm.sendTestMail()" class="md-raised">{{'admin.send-test-mail' | translate}}</md-button>
93   - <md-button ng-disabled="loading || vm.settingsForm.$invalid || !vm.settingsForm.$dirty" type="submit" class="md-raised md-primary">{{'action.save' | translate}}</md-button>
  92 + <md-button ng-disabled="$root.loading || vm.settingsForm.$invalid" ng-click="vm.sendTestMail()" class="md-raised">{{'admin.send-test-mail' | translate}}</md-button>
  93 + <md-button ng-disabled="$root.loading || vm.settingsForm.$invalid || !vm.settingsForm.$dirty" type="submit" class="md-raised md-primary">{{'action.save' | translate}}</md-button>
94 94 </div>
95 95 </fieldset>
96 96 </form>
... ...
... ... @@ -87,7 +87,7 @@
87 87 <md-button ng-if="vm.allowAcknowledgment && (vm.alarm.status==vm.types.alarmStatus.activeUnack ||
88 88 vm.alarm.status==vm.types.alarmStatus.clearedUnack)"
89 89 class="md-raised md-primary"
90   - ng-disabled="loading"
  90 + ng-disabled="$root.loading"
91 91 ng-click="vm.acknowledge()"
92 92 style="margin-right:20px;">{{ 'alarm.acknowledge' |
93 93 translate }}
... ... @@ -95,12 +95,12 @@
95 95 <md-button ng-if="vm.allowClear && (vm.alarm.status==vm.types.alarmStatus.activeAck ||
96 96 vm.alarm.status==vm.types.alarmStatus.activeUnack)"
97 97 class="md-raised md-primary"
98   - ng-disabled="loading"
  98 + ng-disabled="$root.loading"
99 99 ng-click="vm.clear()">{{ 'alarm.clear' |
100 100 translate }}
101 101 </md-button>
102 102 <span flex></span>
103   - <md-button ng-disabled="loading" ng-click="vm.close()" style="margin-right:20px;">{{ 'action.close' |
  103 + <md-button ng-disabled="$root.loading" ng-click="vm.close()" style="margin-right:20px;">{{ 'action.close' |
104 104 translate }}
105 105 </md-button>
106 106 </md-dialog-actions>
... ...
... ... @@ -19,7 +19,7 @@
19 19 <section layout="row">
20 20 <md-input-container class="md-block" style="width: 200px;">
21 21 <label translate>alarm.alarm-status</label>
22   - <md-select ng-model="alarmSearchStatus" ng-disabled="loading()">
  22 + <md-select ng-model="alarmSearchStatus" ng-disabled="$root.loading">
23 23 <md-option ng-repeat="searchStatus in types.alarmSearchStatus" ng-value="searchStatus">
24 24 {{ ('alarm.search-status.' + searchStatus) | translate }}
25 25 </md-option>
... ... @@ -31,8 +31,8 @@
31 31 <md-list flex layout="column" class="tb-alarm-table">
32 32 <md-list class="tb-row tb-header" layout="row" tb-alarm-header>
33 33 </md-list>
34   - <md-progress-linear style="max-height: 0px;" md-mode="indeterminate" ng-disabled="!loading()"
35   - ng-show="loading()"></md-progress-linear>
  34 + <md-progress-linear style="max-height: 0px;" md-mode="indeterminate" ng-disabled="!$root.loading"
  35 + ng-show="$root.loading"></md-progress-linear>
36 36 <md-divider></md-divider>
37 37 <span translate layout-align="center center"
38 38 style="margin-top: 25px;"
... ...
... ... @@ -265,10 +265,10 @@ function AssetService($http, $q, customerService, userService) {
265 265 return deferred.promise;
266 266 }
267 267
268   - function getAssetTypes() {
  268 + function getAssetTypes(config) {
269 269 var deferred = $q.defer();
270 270 var url = '/api/asset/types';
271   - $http.get(url).then(function success(response) {
  271 + $http.get(url, config).then(function success(response) {
272 272 deferred.resolve(response.data);
273 273 }, function fail() {
274 274 deferred.reject();
... ...
... ... @@ -35,7 +35,7 @@ function AttributeService($http, $q, $filter, types, telemetryWebsocketService)
35 35
36 36 return service;
37 37
38   - function getEntityKeys(entityType, entityId, query, type) {
  38 + function getEntityKeys(entityType, entityId, query, type, config) {
39 39 var deferred = $q.defer();
40 40 var url = '/api/plugins/telemetry/' + entityType + '/' + entityId + '/keys/';
41 41 if (type === types.dataKeyType.timeseries) {
... ... @@ -43,7 +43,7 @@ function AttributeService($http, $q, $filter, types, telemetryWebsocketService)
43 43 } else if (type === types.dataKeyType.attribute) {
44 44 url += 'attributes';
45 45 }
46   - $http.get(url, null).then(function success(response) {
  46 + $http.get(url, config).then(function success(response) {
47 47 var result = [];
48 48 if (response.data) {
49 49 if (query) {
... ...
... ... @@ -32,7 +32,7 @@ function CustomerService($http, $q, types) {
32 32
33 33 return service;
34 34
35   - function getCustomers(pageLink) {
  35 + function getCustomers(pageLink, config) {
36 36 var deferred = $q.defer();
37 37 var url = '/api/customers?limit=' + pageLink.limit;
38 38 if (angular.isDefined(pageLink.textSearch)) {
... ... @@ -44,7 +44,7 @@ function CustomerService($http, $q, types) {
44 44 if (angular.isDefined(pageLink.textOffset)) {
45 45 url += '&textOffset=' + pageLink.textOffset;
46 46 }
47   - $http.get(url, null).then(function success(response) {
  47 + $http.get(url, config).then(function success(response) {
48 48 deferred.resolve(response.data);
49 49 }, function fail() {
50 50 deferred.reject();
... ... @@ -52,10 +52,10 @@ function CustomerService($http, $q, types) {
52 52 return deferred.promise;
53 53 }
54 54
55   - function getCustomer(customerId) {
  55 + function getCustomer(customerId, config) {
56 56 var deferred = $q.defer();
57 57 var url = '/api/customer/' + customerId;
58   - $http.get(url, null).then(function success(response) {
  58 + $http.get(url, config).then(function success(response) {
59 59 deferred.resolve(response.data);
60 60 }, function fail(response) {
61 61 deferred.reject(response.data);
... ...
... ... @@ -43,7 +43,7 @@ function DashboardService($rootScope, $http, $q, $location, customerService) {
43 43
44 44 return service;
45 45
46   - function getTenantDashboardsByTenantId(tenantId, pageLink) {
  46 + function getTenantDashboardsByTenantId(tenantId, pageLink, config) {
47 47 var deferred = $q.defer();
48 48 var url = '/api/tenant/' + tenantId + '/dashboards?limit=' + pageLink.limit;
49 49 if (angular.isDefined(pageLink.textSearch)) {
... ... @@ -55,7 +55,7 @@ function DashboardService($rootScope, $http, $q, $location, customerService) {
55 55 if (angular.isDefined(pageLink.textOffset)) {
56 56 url += '&textOffset=' + pageLink.textOffset;
57 57 }
58   - $http.get(url, null).then(function success(response) {
  58 + $http.get(url, config).then(function success(response) {
59 59 deferred.resolve(response.data);
60 60 }, function fail() {
61 61 deferred.reject();
... ... @@ -63,7 +63,7 @@ function DashboardService($rootScope, $http, $q, $location, customerService) {
63 63 return deferred.promise;
64 64 }
65 65
66   - function getTenantDashboards(pageLink, applyCustomersInfo) {
  66 + function getTenantDashboards(pageLink, applyCustomersInfo, config) {
67 67 var deferred = $q.defer();
68 68 var url = '/api/tenant/dashboards?limit=' + pageLink.limit;
69 69 if (angular.isDefined(pageLink.textSearch)) {
... ... @@ -75,7 +75,7 @@ function DashboardService($rootScope, $http, $q, $location, customerService) {
75 75 if (angular.isDefined(pageLink.textOffset)) {
76 76 url += '&textOffset=' + pageLink.textOffset;
77 77 }
78   - $http.get(url, null).then(function success(response) {
  78 + $http.get(url, config).then(function success(response) {
79 79 if (applyCustomersInfo) {
80 80 customerService.applyAssignedCustomersInfo(response.data.data).then(
81 81 function success(data) {
... ... @@ -95,7 +95,7 @@ function DashboardService($rootScope, $http, $q, $location, customerService) {
95 95 return deferred.promise;
96 96 }
97 97
98   - function getCustomerDashboards(customerId, pageLink, applyCustomersInfo) {
  98 + function getCustomerDashboards(customerId, pageLink, applyCustomersInfo, config) {
99 99 var deferred = $q.defer();
100 100 var url = '/api/customer/' + customerId + '/dashboards?limit=' + pageLink.limit;
101 101 if (angular.isDefined(pageLink.textSearch)) {
... ... @@ -107,7 +107,7 @@ function DashboardService($rootScope, $http, $q, $location, customerService) {
107 107 if (angular.isDefined(pageLink.textOffset)) {
108 108 url += '&textOffset=' + pageLink.textOffset;
109 109 }
110   - $http.get(url, null).then(function success(response) {
  110 + $http.get(url, config).then(function success(response) {
111 111 if (applyCustomersInfo) {
112 112 customerService.applyAssignedCustomerInfo(response.data.data, customerId).then(
113 113 function success(data) {
... ... @@ -158,10 +158,10 @@ function DashboardService($rootScope, $http, $q, $location, customerService) {
158 158 return deferred.promise;
159 159 }
160 160
161   - function getDashboardInfo(dashboardId) {
  161 + function getDashboardInfo(dashboardId, config) {
162 162 var deferred = $q.defer();
163 163 var url = '/api/dashboard/info/' + dashboardId;
164   - $http.get(url, null).then(function success(response) {
  164 + $http.get(url, config).then(function success(response) {
165 165 deferred.resolve(response.data);
166 166 }, function fail() {
167 167 deferred.reject();
... ...
... ... @@ -293,10 +293,10 @@ function DeviceService($http, $q, attributeService, customerService, types) {
293 293 return deferred.promise;
294 294 }
295 295
296   - function getDeviceTypes() {
  296 + function getDeviceTypes(config) {
297 297 var deferred = $q.defer();
298 298 var url = '/api/device/types';
299   - $http.get(url).then(function success(response) {
  299 + $http.get(url, config).then(function success(response) {
300 300 deferred.resolve(response.data);
301 301 }, function fail() {
302 302 deferred.reject();
... ...
... ... @@ -175,10 +175,10 @@ function EntityRelationService($http, $q) {
175 175 return deferred.promise;
176 176 }
177 177
178   - function findInfoByQuery(query) {
  178 + function findInfoByQuery(query, config) {
179 179 var deferred = $q.defer();
180 180 var url = '/api/relations/info';
181   - $http.post(url, query).then(function success(response) {
  181 + $http.post(url, query, config).then(function success(response) {
182 182 deferred.resolve(response.data);
183 183 }, function fail() {
184 184 deferred.reject();
... ...
... ... @@ -56,22 +56,22 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
56 56 promise = assetService.getAsset(entityId, true, config);
57 57 break;
58 58 case types.entityType.tenant:
59   - promise = tenantService.getTenant(entityId);
  59 + promise = tenantService.getTenant(entityId, config);
60 60 break;
61 61 case types.entityType.customer:
62   - promise = customerService.getCustomer(entityId);
  62 + promise = customerService.getCustomer(entityId, config);
63 63 break;
64 64 case types.entityType.rule:
65   - promise = ruleService.getRule(entityId);
  65 + promise = ruleService.getRule(entityId, config);
66 66 break;
67 67 case types.entityType.plugin:
68   - promise = pluginService.getPlugin(entityId);
  68 + promise = pluginService.getPlugin(entityId, config);
69 69 break;
70 70 case types.entityType.dashboard:
71   - promise = dashboardService.getDashboardInfo(entityId);
  71 + promise = dashboardService.getDashboardInfo(entityId, config);
72 72 break;
73 73 case types.entityType.user:
74   - promise = userService.getUser(entityId);
  74 + promise = userService.getUser(entityId, true, config);
75 75 break;
76 76 case types.entityType.alarm:
77 77 $log.error('Get Alarm Entity is not implemented!');
... ... @@ -136,22 +136,28 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
136 136 promise = assetService.getAssets(entityIds, config);
137 137 break;
138 138 case types.entityType.tenant:
139   - promise = getEntitiesByIdsPromise(tenantService.getTenant, entityIds);
  139 + promise = getEntitiesByIdsPromise(
  140 + (id) => tenantService.getTenant(id, config), entityIds);
140 141 break;
141 142 case types.entityType.customer:
142   - promise = getEntitiesByIdsPromise(customerService.getCustomer, entityIds);
  143 + promise = getEntitiesByIdsPromise(
  144 + (id) => customerService.getCustomer(id, config), entityIds);
143 145 break;
144 146 case types.entityType.rule:
145   - promise = getEntitiesByIdsPromise(ruleService.getRule, entityIds);
  147 + promise = getEntitiesByIdsPromise(
  148 + (id) => ruleService.getRule(id, config), entityIds);
146 149 break;
147 150 case types.entityType.plugin:
148   - promise = getEntitiesByIdsPromise(pluginService.getPlugin, entityIds);
  151 + promise = getEntitiesByIdsPromise(
  152 + (id) => pluginService.getPlugin(id, config), entityIds);
149 153 break;
150 154 case types.entityType.dashboard:
151   - promise = getEntitiesByIdsPromise(dashboardService.getDashboardInfo, entityIds);
  155 + promise = getEntitiesByIdsPromise(
  156 + (id) => dashboardService.getDashboardInfo(id, config), entityIds);
152 157 break;
153 158 case types.entityType.user:
154   - promise = getEntitiesByIdsPromise(userService.getUser, entityIds);
  159 + promise = getEntitiesByIdsPromise(
  160 + (id) => userService.getUser(id, true, config), entityIds);
155 161 break;
156 162 case types.entityType.alarm:
157 163 $log.error('Get Alarm Entity is not implemented!');
... ... @@ -178,11 +184,11 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
178 184 return deferred.promise;
179 185 }
180 186
181   - function getSingleTenantByPageLinkPromise(pageLink) {
  187 + function getSingleTenantByPageLinkPromise(pageLink, config) {
182 188 var user = userService.getCurrentUser();
183 189 var tenantId = user.tenantId;
184 190 var deferred = $q.defer();
185   - tenantService.getTenant(tenantId).then(
  191 + tenantService.getTenant(tenantId, config).then(
186 192 function success(tenant) {
187 193 var tenantName = tenant.name;
188 194 var result = {
... ... @@ -202,11 +208,11 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
202 208 return deferred.promise;
203 209 }
204 210
205   - function getSingleCustomerByPageLinkPromise(pageLink) {
  211 + function getSingleCustomerByPageLinkPromise(pageLink, config) {
206 212 var user = userService.getCurrentUser();
207 213 var customerId = user.customerId;
208 214 var deferred = $q.defer();
209   - customerService.getCustomer(customerId).then(
  215 + customerService.getCustomer(customerId, config).then(
210 216 function success(customer) {
211 217 var customerName = customer.name;
212 218 var result = {
... ... @@ -247,29 +253,29 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
247 253 break;
248 254 case types.entityType.tenant:
249 255 if (user.authority === 'TENANT_ADMIN') {
250   - promise = getSingleTenantByPageLinkPromise(pageLink);
  256 + promise = getSingleTenantByPageLinkPromise(pageLink, config);
251 257 } else {
252   - promise = tenantService.getTenants(pageLink);
  258 + promise = tenantService.getTenants(pageLink, config);
253 259 }
254 260 break;
255 261 case types.entityType.customer:
256 262 if (user.authority === 'CUSTOMER_USER') {
257   - promise = getSingleCustomerByPageLinkPromise(pageLink);
  263 + promise = getSingleCustomerByPageLinkPromise(pageLink, config);
258 264 } else {
259   - promise = customerService.getCustomers(pageLink);
  265 + promise = customerService.getCustomers(pageLink, config);
260 266 }
261 267 break;
262 268 case types.entityType.rule:
263   - promise = ruleService.getAllRules(pageLink);
  269 + promise = ruleService.getAllRules(pageLink, config);
264 270 break;
265 271 case types.entityType.plugin:
266   - promise = pluginService.getAllPlugins(pageLink);
  272 + promise = pluginService.getAllPlugins(pageLink, config);
267 273 break;
268 274 case types.entityType.dashboard:
269 275 if (user.authority === 'CUSTOMER_USER') {
270   - promise = dashboardService.getCustomerDashboards(customerId, pageLink, false);
  276 + promise = dashboardService.getCustomerDashboards(customerId, pageLink, false, config);
271 277 } else {
272   - promise = dashboardService.getTenantDashboards(pageLink, false);
  278 + promise = dashboardService.getTenantDashboards(pageLink, false, config);
273 279 }
274 280 break;
275 281 case types.entityType.user:
... ... @@ -426,7 +432,7 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
426 432 var stateEntityId = getStateEntityId(filter, stateParams);
427 433 switch (filter.type) {
428 434 case types.aliasFilterType.singleEntity.value:
429   - getEntity(filter.singleEntity.entityType, filter.singleEntity.id).then(
  435 + getEntity(filter.singleEntity.entityType, filter.singleEntity.id, {ignoreLoading: true}).then(
430 436 function success(entity) {
431 437 result.entities = entitiesToEntitiesInfo([entity]);
432 438 deferred.resolve(result);
... ... @@ -437,7 +443,7 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
437 443 );
438 444 break;
439 445 case types.aliasFilterType.entityList.value:
440   - getEntities(filter.entityType, filter.entityList).then(
  446 + getEntities(filter.entityType, filter.entityList, {ignoreLoading: true}).then(
441 447 function success(entities) {
442 448 if (entities && entities.length || !failOnEmpty) {
443 449 result.entities = entitiesToEntitiesInfo(entities);
... ... @@ -452,7 +458,7 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
452 458 );
453 459 break;
454 460 case types.aliasFilterType.entityName.value:
455   - getEntitiesByNameFilter(filter.entityType, filter.entityNameFilter, maxItems).then(
  461 + getEntitiesByNameFilter(filter.entityType, filter.entityNameFilter, maxItems, {ignoreLoading: true}).then(
456 462 function success(entities) {
457 463 if (entities && entities.length || !failOnEmpty) {
458 464 result.entities = entitiesToEntitiesInfo(entities);
... ... @@ -469,7 +475,7 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
469 475 case types.aliasFilterType.stateEntity.value:
470 476 result.stateEntity = true;
471 477 if (stateEntityId) {
472   - getEntity(stateEntityId.entityType, stateEntityId.id).then(
  478 + getEntity(stateEntityId.entityType, stateEntityId.id, {ignoreLoading: true}).then(
473 479 function success(entity) {
474 480 result.entities = entitiesToEntitiesInfo([entity]);
475 481 deferred.resolve(result);
... ... @@ -483,7 +489,7 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
483 489 }
484 490 break;
485 491 case types.aliasFilterType.assetType.value:
486   - getEntitiesByNameFilter(types.entityType.asset, filter.assetNameFilter, maxItems, null, filter.assetType).then(
  492 + getEntitiesByNameFilter(types.entityType.asset, filter.assetNameFilter, maxItems, {ignoreLoading: true}, filter.assetType).then(
487 493 function success(entities) {
488 494 if (entities && entities.length || !failOnEmpty) {
489 495 result.entities = entitiesToEntitiesInfo(entities);
... ... @@ -498,7 +504,7 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
498 504 );
499 505 break;
500 506 case types.aliasFilterType.deviceType.value:
501   - getEntitiesByNameFilter(types.entityType.device, filter.deviceNameFilter, maxItems, null, filter.deviceType).then(
  507 + getEntitiesByNameFilter(types.entityType.device, filter.deviceNameFilter, maxItems, {ignoreLoading: true}, filter.deviceType).then(
502 508 function success(entities) {
503 509 if (entities && entities.length || !failOnEmpty) {
504 510 result.entities = entitiesToEntitiesInfo(entities);
... ... @@ -533,7 +539,7 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
533 539 filters: filter.filters
534 540 };
535 541 searchQuery.parameters.maxLevel = filter.maxLevel && filter.maxLevel > 0 ? filter.maxLevel : -1;
536   - entityRelationService.findInfoByQuery(searchQuery).then(
  542 + entityRelationService.findInfoByQuery(searchQuery, {ignoreLoading: true}).then(
537 543 function success(allRelations) {
538 544 if (allRelations && allRelations.length || !failOnEmpty) {
539 545 if (angular.isDefined(maxItems) && maxItems > 0 && allRelations) {
... ... @@ -577,10 +583,10 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
577 583 var findByQueryPromise;
578 584 if (filter.type == types.aliasFilterType.assetSearchQuery.value) {
579 585 searchQuery.assetTypes = filter.assetTypes;
580   - findByQueryPromise = assetService.findByQuery(searchQuery, false);
  586 + findByQueryPromise = assetService.findByQuery(searchQuery, false, {ignoreLoading: true});
581 587 } else if (filter.type == types.aliasFilterType.deviceSearchQuery.value) {
582 588 searchQuery.deviceTypes = filter.deviceTypes;
583   - findByQueryPromise = deviceService.findByQuery(searchQuery, false);
  589 + findByQueryPromise = deviceService.findByQuery(searchQuery, false, {ignoreLoading: true});
584 590 }
585 591 findByQueryPromise.then(
586 592 function success(entities) {
... ... @@ -762,7 +768,7 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
762 768 return deferred.promise;
763 769 }
764 770
765   - function getEntityKeys(entityType, entityId, query, type) {
  771 + function getEntityKeys(entityType, entityId, query, type, config) {
766 772 var deferred = $q.defer();
767 773 var url = '/api/plugins/telemetry/' + entityType + '/' + entityId + '/keys/';
768 774 if (type === types.dataKeyType.timeseries) {
... ... @@ -770,7 +776,7 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
770 776 } else if (type === types.dataKeyType.attribute) {
771 777 url += 'attributes';
772 778 }
773   - $http.get(url, null).then(function success(response) {
  779 + $http.get(url, config).then(function success(response) {
774 780 var result = [];
775 781 if (response.data) {
776 782 if (query) {
... ...
... ... @@ -50,11 +50,11 @@ function PluginService($http, $q, $rootScope, $filter, componentDescriptorServic
50 50 tenantPlugins = undefined;
51 51 }
52 52
53   - function loadPluginsCache() {
  53 + function loadPluginsCache(config) {
54 54 var deferred = $q.defer();
55 55 if (!allPlugins) {
56 56 var url = '/api/plugins';
57   - $http.get(url, null).then(function success(response) {
  57 + $http.get(url, config).then(function success(response) {
58 58 componentDescriptorService.getComponentDescriptorsByType(types.componentType.plugin).then(
59 59 function success(pluginComponents) {
60 60 allPlugins = response.data;
... ... @@ -93,9 +93,9 @@ function PluginService($http, $q, $rootScope, $filter, componentDescriptorServic
93 93 return deferred.promise;
94 94 }
95 95
96   - function getSystemPlugins(pageLink) {
  96 + function getSystemPlugins(pageLink, config) {
97 97 var deferred = $q.defer();
98   - loadPluginsCache().then(
  98 + loadPluginsCache(config).then(
99 99 function success() {
100 100 utils.filterSearchTextEntities(systemPlugins, 'name', pageLink, deferred);
101 101 },
... ... @@ -106,9 +106,9 @@ function PluginService($http, $q, $rootScope, $filter, componentDescriptorServic
106 106 return deferred.promise;
107 107 }
108 108
109   - function getTenantPlugins(pageLink) {
  109 + function getTenantPlugins(pageLink, config) {
110 110 var deferred = $q.defer();
111   - loadPluginsCache().then(
  111 + loadPluginsCache(config).then(
112 112 function success() {
113 113 utils.filterSearchTextEntities(tenantPlugins, 'name', pageLink, deferred);
114 114 },
... ... @@ -119,9 +119,9 @@ function PluginService($http, $q, $rootScope, $filter, componentDescriptorServic
119 119 return deferred.promise;
120 120 }
121 121
122   - function getAllActionPlugins(pageLink) {
  122 + function getAllActionPlugins(pageLink, config) {
123 123 var deferred = $q.defer();
124   - loadPluginsCache().then(
  124 + loadPluginsCache(config).then(
125 125 function success() {
126 126 utils.filterSearchTextEntities(allActionPlugins, 'name', pageLink, deferred);
127 127 },
... ... @@ -132,9 +132,9 @@ function PluginService($http, $q, $rootScope, $filter, componentDescriptorServic
132 132 return deferred.promise;
133 133 }
134 134
135   - function getAllPlugins(pageLink) {
  135 + function getAllPlugins(pageLink, config) {
136 136 var deferred = $q.defer();
137   - loadPluginsCache().then(
  137 + loadPluginsCache(config).then(
138 138 function success() {
139 139 utils.filterSearchTextEntities(allPlugins, 'name', pageLink, deferred);
140 140 },
... ... @@ -156,10 +156,10 @@ function PluginService($http, $q, $rootScope, $filter, componentDescriptorServic
156 156 return deferred.promise;
157 157 }
158 158
159   - function getPlugin(pluginId) {
  159 + function getPlugin(pluginId, config) {
160 160 var deferred = $q.defer();
161 161 var url = '/api/plugin/' + pluginId;
162   - $http.get(url, null).then(function success(response) {
  162 + $http.get(url, config).then(function success(response) {
163 163 deferred.resolve(response.data);
164 164 }, function fail(response) {
165 165 deferred.reject(response.data);
... ...
... ... @@ -47,11 +47,11 @@ function RuleService($http, $q, $rootScope, $filter, types, utils) {
47 47 tenantRules = undefined;
48 48 }
49 49
50   - function loadRulesCache() {
  50 + function loadRulesCache(config) {
51 51 var deferred = $q.defer();
52 52 if (!allRules) {
53 53 var url = '/api/rules';
54   - $http.get(url, null).then(function success(response) {
  54 + $http.get(url, config).then(function success(response) {
55 55 allRules = response.data;
56 56 systemRules = [];
57 57 tenantRules = [];
... ... @@ -100,9 +100,9 @@ function RuleService($http, $q, $rootScope, $filter, types, utils) {
100 100 return deferred.promise;
101 101 }
102 102
103   - function getAllRules(pageLink) {
  103 + function getAllRules(pageLink, config) {
104 104 var deferred = $q.defer();
105   - loadRulesCache().then(
  105 + loadRulesCache(config).then(
106 106 function success() {
107 107 utils.filterSearchTextEntities(allRules, 'name', pageLink, deferred);
108 108 },
... ... @@ -124,10 +124,10 @@ function RuleService($http, $q, $rootScope, $filter, types, utils) {
124 124 return deferred.promise;
125 125 }
126 126
127   - function getRule(ruleId) {
  127 + function getRule(ruleId, config) {
128 128 var deferred = $q.defer();
129 129 var url = '/api/rule/' + ruleId;
130   - $http.get(url, null).then(function success(response) {
  130 + $http.get(url, config).then(function success(response) {
131 131 deferred.resolve(response.data);
132 132 }, function fail(response) {
133 133 deferred.reject(response.data);
... ...
... ... @@ -23,6 +23,8 @@ export default angular.module('thingsboard.api.telemetryWebsocket', [thingsboard
23 23 const RECONNECT_INTERVAL = 2000;
24 24 const WS_IDLE_TIMEOUT = 90000;
25 25
  26 +const MAX_PUBLISH_COMMANDS = 10;
  27 +
26 28 /*@ngInject*/
27 29 function TelemetryWebsocketService($rootScope, $websocket, $timeout, $window, types, userService) {
28 30
... ... @@ -75,19 +77,40 @@ function TelemetryWebsocketService($rootScope, $websocket, $timeout, $window, ty
75 77 return service;
76 78
77 79 function publishCommands () {
78   - if (isOpened && (cmdsWrapper.tsSubCmds.length > 0 ||
79   - cmdsWrapper.historyCmds.length > 0 ||
80   - cmdsWrapper.attrSubCmds.length > 0)) {
81   - dataStream.send(angular.copy(cmdsWrapper)).then(function () {
  80 + while(isOpened && hasCommands()) {
  81 + dataStream.send(preparePublishCommands()).then(function () {
82 82 checkToClose();
83 83 });
84   - cmdsWrapper.tsSubCmds = [];
85   - cmdsWrapper.historyCmds = [];
86   - cmdsWrapper.attrSubCmds = [];
87 84 }
88 85 tryOpenSocket();
89 86 }
90 87
  88 + function hasCommands() {
  89 + return cmdsWrapper.tsSubCmds.length > 0 ||
  90 + cmdsWrapper.historyCmds.length > 0 ||
  91 + cmdsWrapper.attrSubCmds.length > 0;
  92 + }
  93 +
  94 + function preparePublishCommands() {
  95 + var preparedWrapper = {};
  96 + var leftCount = MAX_PUBLISH_COMMANDS;
  97 + preparedWrapper.tsSubCmds = popCmds(cmdsWrapper.tsSubCmds, leftCount);
  98 + leftCount -= preparedWrapper.tsSubCmds.length;
  99 + preparedWrapper.historyCmds = popCmds(cmdsWrapper.historyCmds, leftCount);
  100 + leftCount -= preparedWrapper.historyCmds.length;
  101 + preparedWrapper.attrSubCmds = popCmds(cmdsWrapper.attrSubCmds, leftCount);
  102 + return preparedWrapper;
  103 + }
  104 +
  105 + function popCmds(cmds, leftCount) {
  106 + var toPublish = Math.min(cmds.length, leftCount);
  107 + if (toPublish > 0) {
  108 + return cmds.splice(0, toPublish);
  109 + } else {
  110 + return [];
  111 + }
  112 + }
  113 +
91 114 function onError (/*message*/) {
92 115 isOpening = false;
93 116 }
... ...
... ... @@ -29,7 +29,7 @@ function TenantService($http, $q) {
29 29
30 30 return service;
31 31
32   - function getTenants (pageLink) {
  32 + function getTenants (pageLink, config) {
33 33 var deferred = $q.defer();
34 34 var url = '/api/tenants?limit=' + pageLink.limit;
35 35 if (angular.isDefined(pageLink.textSearch)) {
... ... @@ -41,7 +41,7 @@ function TenantService($http, $q) {
41 41 if (angular.isDefined(pageLink.textOffset)) {
42 42 url += '&textOffset=' + pageLink.textOffset;
43 43 }
44   - $http.get(url, null).then(function success(response) {
  44 + $http.get(url, config).then(function success(response) {
45 45 deferred.resolve(response.data);
46 46 }, function fail() {
47 47 deferred.reject();
... ... @@ -49,10 +49,10 @@ function TenantService($http, $q) {
49 49 return deferred.promise;
50 50 }
51 51
52   - function getTenant (tenantId) {
  52 + function getTenant (tenantId, config) {
53 53 var deferred = $q.defer();
54 54 var url = '/api/tenant/' + tenantId;
55   - $http.get(url, null).then(function success(response) {
  55 + $http.get(url, config).then(function success(response) {
56 56 deferred.resolve(response.data);
57 57 }, function fail(response) {
58 58 deferred.reject(response.data);
... ...
... ... @@ -421,10 +421,14 @@ function UserService($http, $q, $rootScope, adminService, dashboardService, logi
421 421 return deferred.promise;
422 422 }
423 423
424   - function getUser(userId, ignoreErrors) {
  424 + function getUser(userId, ignoreErrors, config) {
425 425 var deferred = $q.defer();
426 426 var url = '/api/user/' + userId;
427   - $http.get(url, { ignoreErrors: ignoreErrors }).then(function success(response) {
  427 + if (!config) {
  428 + config = {};
  429 + }
  430 + config = Object.assign(config, { ignoreErrors: ignoreErrors });
  431 + $http.get(url, config).then(function success(response) {
428 432 deferred.resolve(response.data);
429 433 }, function fail() {
430 434 deferred.reject();
... ...
... ... @@ -298,11 +298,11 @@ function WidgetService($rootScope, $http, $q, $filter, $ocLazyLoad, $window, $tr
298 298 tenantWidgetsBundles = undefined;
299 299 }
300 300
301   - function loadWidgetsBundleCache() {
  301 + function loadWidgetsBundleCache(config) {
302 302 var deferred = $q.defer();
303 303 if (!allWidgetsBundles) {
304 304 var url = '/api/widgetsBundles';
305   - $http.get(url, null).then(function success(response) {
  305 + $http.get(url, config).then(function success(response) {
306 306 allWidgetsBundles = response.data;
307 307 systemWidgetsBundles = [];
308 308 tenantWidgetsBundles = [];
... ... @@ -326,9 +326,9 @@ function WidgetService($rootScope, $http, $q, $filter, $ocLazyLoad, $window, $tr
326 326 }
327 327
328 328
329   - function getSystemWidgetsBundles() {
  329 + function getSystemWidgetsBundles(config) {
330 330 var deferred = $q.defer();
331   - loadWidgetsBundleCache().then(
  331 + loadWidgetsBundleCache(config).then(
332 332 function success() {
333 333 deferred.resolve(systemWidgetsBundles);
334 334 },
... ... @@ -339,9 +339,9 @@ function WidgetService($rootScope, $http, $q, $filter, $ocLazyLoad, $window, $tr
339 339 return deferred.promise;
340 340 }
341 341
342   - function getTenantWidgetsBundles() {
  342 + function getTenantWidgetsBundles(config) {
343 343 var deferred = $q.defer();
344   - loadWidgetsBundleCache().then(
  344 + loadWidgetsBundleCache(config).then(
345 345 function success() {
346 346 deferred.resolve(tenantWidgetsBundles);
347 347 },
... ... @@ -352,9 +352,9 @@ function WidgetService($rootScope, $http, $q, $filter, $ocLazyLoad, $window, $tr
352 352 return deferred.promise;
353 353 }
354 354
355   - function getAllWidgetsBundles() {
  355 + function getAllWidgetsBundles(config) {
356 356 var deferred = $q.defer();
357   - loadWidgetsBundleCache().then(
  357 + loadWidgetsBundleCache(config).then(
358 358 function success() {
359 359 deferred.resolve(allWidgetsBundles);
360 360 },
... ...
... ... @@ -27,8 +27,8 @@
27 27 </md-button>
28 28 </div>
29 29 </md-toolbar>
30   - <md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!loading" ng-show="loading"></md-progress-linear>
31   - <span style="min-height: 5px;" flex="" ng-show="!loading"></span>
  30 + <md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
  31 + <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
32 32 <md-dialog-content>
33 33 <div class="md-dialog-content">
34 34 <tb-asset asset="vm.item" is-edit="true" the-form="theForm"></tb-asset>
... ... @@ -36,10 +36,10 @@
36 36 </md-dialog-content>
37 37 <md-dialog-actions layout="row">
38 38 <span flex></span>
39   - <md-button ng-disabled="loading || theForm.$invalid || !theForm.$dirty" type="submit" class="md-raised md-primary">
  39 + <md-button ng-disabled="$root.loading || theForm.$invalid || !theForm.$dirty" type="submit" class="md-raised md-primary">
40 40 {{ 'action.add' | translate }}
41 41 </md-button>
42   - <md-button ng-disabled="loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | translate }}</md-button>
  42 + <md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | translate }}</md-button>
43 43 </md-dialog-actions>
44 44 </form>
45 45 </md-dialog>
... ...
... ... @@ -26,8 +26,8 @@
26 26 </md-button>
27 27 </div>
28 28 </md-toolbar>
29   - <md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!loading" ng-show="loading"></md-progress-linear>
30   - <span style="min-height: 5px;" flex="" ng-show="!loading"></span>
  29 + <md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
  30 + <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
31 31 <md-dialog-content>
32 32 <div class="md-dialog-content">
33 33 <fieldset>
... ... @@ -65,11 +65,11 @@
65 65 </md-dialog-content>
66 66 <md-dialog-actions layout="row">
67 67 <span flex></span>
68   - <md-button ng-disabled="loading || vm.assets.selectedCount == 0" type="submit"
  68 + <md-button ng-disabled="$root.loading || vm.assets.selectedCount == 0" type="submit"
69 69 class="md-raised md-primary">
70 70 {{ 'action.assign' | translate }}
71 71 </md-button>
72   - <md-button ng-disabled="loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' |
  72 + <md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' |
73 73 translate }}
74 74 </md-button>
75 75 </md-dialog-actions>
... ...
... ... @@ -48,7 +48,7 @@
48 48 ng-show="!isEdit && isPublic && (assetScope === 'customer' || assetScope === 'tenant')">
49 49 {{ 'asset.asset-public' | translate }}
50 50 </div>
51   - <fieldset ng-disabled="loading || !isEdit">
  51 + <fieldset ng-disabled="$root.loading || !isEdit">
52 52 <md-input-container class="md-block">
53 53 <label translate>asset.name</label>
54 54 <input required name="name" ng-model="asset.name">
... ... @@ -57,7 +57,7 @@
57 57 </div>
58 58 </md-input-container>
59 59 <tb-entity-subtype-autocomplete
60   - ng-disabled="loading || !isEdit"
  60 + ng-disabled="$root.loading || !isEdit"
61 61 tb-required="true"
62 62 the-form="theForm"
63 63 ng-model="asset.type"
... ...
... ... @@ -26,8 +26,8 @@
26 26 </md-button>
27 27 </div>
28 28 </md-toolbar>
29   - <md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!loading" ng-show="loading"></md-progress-linear>
30   - <span style="min-height: 5px;" flex="" ng-show="!loading"></span>
  29 + <md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
  30 + <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
31 31 <md-dialog-content>
32 32 <div class="md-dialog-content">
33 33 <fieldset>
... ... @@ -65,10 +65,10 @@
65 65 </md-dialog-content>
66 66 <md-dialog-actions layout="row">
67 67 <span flex></span>
68   - <md-button ng-disabled="loading || vm.customers.selection==null" type="submit" class="md-raised md-primary">
  68 + <md-button ng-disabled="$root.loading || vm.customers.selection==null" type="submit" class="md-raised md-primary">
69 69 {{ 'action.assign' | translate }}
70 70 </md-button>
71   - <md-button ng-disabled="loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' |
  71 + <md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' |
72 72 translate }}
73 73 </md-button>
74 74 </md-dialog-actions>
... ...
... ... @@ -134,6 +134,8 @@ function Utils($mdColorPalette, $rootScope, $window, $translate, $q, $timeout, t
134 134 defaultAlarmDataKeys.push(dataKey);
135 135 }
136 136
  137 + var imageAspectMap = {};
  138 +
137 139 var service = {
138 140 getDefaultDatasource: getDefaultDatasource,
139 141 generateObjectFromJsonSchema: generateObjectFromJsonSchema,
... ... @@ -159,7 +161,8 @@ function Utils($mdColorPalette, $rootScope, $window, $translate, $q, $timeout, t
159 161 insertVariable: insertVariable,
160 162 customTranslation: customTranslation,
161 163 objToBase64: objToBase64,
162   - base64toObj: base64toObj
  164 + base64toObj: base64toObj,
  165 + loadImageAspect: loadImageAspect
163 166 }
164 167
165 168 return service;
... ... @@ -543,4 +546,34 @@ function Utils($mdColorPalette, $rootScope, $window, $translate, $q, $timeout, t
543 546 return obj;
544 547 }
545 548
  549 + function loadImageAspect(imageUrl) {
  550 + var deferred = $q.defer();
  551 + if (imageUrl && imageUrl.length) {
  552 + var urlHashCode = hashCode(imageUrl);
  553 + var aspect = imageAspectMap[urlHashCode];
  554 + if (angular.isUndefined(aspect)) {
  555 + var testImage = document.createElement('img'); // eslint-disable-line
  556 + testImage.style.visibility = 'hidden';
  557 + testImage.onload = function() {
  558 + aspect = testImage.width / testImage.height;
  559 + document.body.removeChild(testImage); //eslint-disable-line
  560 + imageAspectMap[urlHashCode] = aspect;
  561 + deferred.resolve(aspect);
  562 + };
  563 + testImage.onerror = function() {
  564 + aspect = 0;
  565 + imageAspectMap[urlHashCode] = aspect;
  566 + deferred.resolve(aspect);
  567 + };
  568 + document.body.appendChild(testImage); //eslint-disable-line
  569 + testImage.src = imageUrl;
  570 + } else {
  571 + deferred.resolve(aspect);
  572 + }
  573 + } else {
  574 + deferred.resolve(0);
  575 + }
  576 + return deferred.promise;
  577 + }
  578 +
546 579 }
... ...
... ... @@ -27,11 +27,11 @@
27 27 </md-button>
28 28 </div>
29 29 </md-toolbar>
30   - <md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!loading" ng-show="loading"></md-progress-linear>
31   - <span style="min-height: 5px;" flex="" ng-show="!loading"></span>
  30 + <md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
  31 + <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
32 32 <md-dialog-content>
33 33 <div class="md-dialog-content tb-filter">
34   - <fieldset ng-disabled="loading || vm.isReadOnly">
  34 + <fieldset ng-disabled="$root.loading || vm.isReadOnly">
35 35 <section flex layout="row">
36 36 <md-input-container flex class="md-block">
37 37 <label translate>rule.component-name</label>
... ... @@ -42,7 +42,7 @@
42 42 </md-input-container>
43 43 <md-input-container flex class="md-block">
44 44 <label translate>rule.component-type</label>
45   - <md-select required name="componentType" ng-model="vm.componentInfo.component.clazz" ng-disabled="loading || vm.isReadOnly">
  45 + <md-select required name="componentType" ng-model="vm.componentInfo.component.clazz" ng-disabled="$root.loading || vm.isReadOnly">
46 46 <md-option ng-repeat="componentDescriptor in vm.componentDescriptors" ng-value="componentDescriptor.clazz">
47 47 {{componentDescriptor.name}}
48 48 </md-option>
... ... @@ -57,7 +57,7 @@
57 57 <tb-json-form schema="vm.componentDescriptor.configurationDescriptor.schema"
58 58 form="vm.componentDescriptor.configurationDescriptor.form"
59 59 model="vm.componentInfo.component.configuration"
60   - readonly="loading || vm.isReadOnly"
  60 + readonly="$root.loading || vm.isReadOnly"
61 61 form-control="theForm">
62 62 </tb-json-form>
63 63 </md-card-content>
... ... @@ -67,11 +67,11 @@
67 67 </md-dialog-content>
68 68 <md-dialog-actions layout="row">
69 69 <span flex></span>
70   - <md-button ng-if="!vm.isReadOnly" ng-disabled="loading || theForm.$invalid || !theForm.$dirty" type="submit"
  70 + <md-button ng-if="!vm.isReadOnly" ng-disabled="$root.loading || theForm.$invalid || !theForm.$dirty" type="submit"
71 71 class="md-raised md-primary">
72 72 {{ (vm.isAdd ? 'action.add' : 'action.save') | translate }}
73 73 </md-button>
74   - <md-button ng-disabled="loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' |
  74 + <md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' |
75 75 translate }}
76 76 </md-button>
77 77 </md-dialog-actions>
... ...
... ... @@ -24,7 +24,7 @@
24 24 {{ componentTypeName }}
25 25 </span>
26 26 <span ng-if="readOnly" style="min-width: 40px; min-height: 40px; margin: 0 6px;"></br></span>
27   - <md-button ng-disabled="loading" class="md-icon-button md-primary"
  27 + <md-button ng-disabled="$root.loading" class="md-icon-button md-primary"
28 28 style="min-width: 40px;"
29 29 ng-click="openComponent($event)"
30 30 aria-label="{{ (readOnly ? 'action.view' : 'action.edit') | translate }}">
... ... @@ -43,7 +43,7 @@
43 43 edit
44 44 </md-icon>
45 45 </md-button>
46   - <md-button ng-if="!readOnly" ng-disabled="loading" class="md-icon-button md-primary"
  46 + <md-button ng-if="!readOnly" ng-disabled="$root.loading" class="md-icon-button md-primary"
47 47 style="min-width: 40px;"
48 48 ng-click="onRemoveComponent({event: $event})"
49 49 aria-label="{{ 'action.remove' | translate }}">
... ...
... ... @@ -48,19 +48,19 @@ function DashboardAutocomplete($compile, $templateCache, $q, dashboardService, u
48 48 var promise;
49 49 if (scope.dashboardsScope === 'customer' || userService.getAuthority() === 'CUSTOMER_USER') {
50 50 if (scope.customerId) {
51   - promise = dashboardService.getCustomerDashboards(scope.customerId, pageLink, false);
  51 + promise = dashboardService.getCustomerDashboards(scope.customerId, pageLink, false, {ignoreLoading: true});
52 52 } else {
53 53 promise = $q.when({data: []});
54 54 }
55 55 } else {
56 56 if (userService.getAuthority() === 'SYS_ADMIN') {
57 57 if (scope.tenantId) {
58   - promise = dashboardService.getTenantDashboardsByTenantId(scope.tenantId, pageLink);
  58 + promise = dashboardService.getTenantDashboardsByTenantId(scope.tenantId, pageLink, {ignoreLoading: true});
59 59 } else {
60 60 promise = $q.when({data: []});
61 61 }
62 62 } else {
63   - promise = dashboardService.getTenantDashboards(pageLink, false);
  63 + promise = dashboardService.getTenantDashboards(pageLink, false, {ignoreLoading: true});
64 64 }
65 65 }
66 66
... ...
... ... @@ -48,12 +48,12 @@ function DashboardSelect($compile, $templateCache, $q, $mdMedia, $mdPanel, $docu
48 48 var promise;
49 49 if (scope.dashboardsScope === 'customer' || userService.getAuthority() === 'CUSTOMER_USER') {
50 50 if (scope.customerId && scope.customerId != types.id.nullUid) {
51   - promise = dashboardService.getCustomerDashboards(scope.customerId, pageLink, false);
  51 + promise = dashboardService.getCustomerDashboards(scope.customerId, pageLink, false, {ignoreLoading: true});
52 52 } else {
53 53 promise = $q.when({data: []});
54 54 }
55 55 } else {
56   - promise = dashboardService.getTenantDashboards(pageLink, false);
  56 + promise = dashboardService.getTenantDashboards(pageLink, false, {ignoreLoading: true});
57 57 }
58 58
59 59 promise.then(function success(result) {
... ...
... ... @@ -43,7 +43,7 @@ function DatakeyConfigDialogController($scope, $mdDialog, $q, entityService, dat
43 43 function success(aliasInfo) {
44 44 var entity = aliasInfo.currentEntity;
45 45 if (entity) {
46   - entityService.getEntityKeys(entity.entityType, entity.id, query, type).then(
  46 + entityService.getEntityKeys(entity.entityType, entity.id, query, type, {ignoreLoading: true}).then(
47 47 function success(keys) {
48 48 deferred.resolve(keys);
49 49 },
... ...
... ... @@ -26,8 +26,8 @@
26 26 </md-button>
27 27 </div>
28 28 </md-toolbar>
29   - <md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!loading" ng-show="loading"></md-progress-linear>
30   - <span style="min-height: 5px;" flex="" ng-show="!loading"></span>
  29 + <md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
  30 + <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
31 31 <md-dialog-content>
32 32 <tb-datakey-config ng-model="vm.dataKey"
33 33 fetch-entity-keys="vm.fetchEntityKeys(entityAliasId, query, type)"
... ... @@ -37,10 +37,10 @@
37 37 </md-dialog-content>
38 38 <md-dialog-actions layout="row">
39 39 <span flex></span>
40   - <md-button ng-disabled="loading || theForm.$invalid || !theForm.$dirty" type="submit" class="md-raised md-primary">
  40 + <md-button ng-disabled="$root.loading || theForm.$invalid || !theForm.$dirty" type="submit" class="md-raised md-primary">
41 41 {{ 'action.save' | translate }}
42 42 </md-button>
43   - <md-button ng-disabled="loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | translate }}</md-button>
  43 + <md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | translate }}</md-button>
44 44 </md-dialog-actions>
45 45 </form>
46 46 </md-dialog>
... ...
... ... @@ -35,7 +35,7 @@
35 35 </div>
36 36 <section ng-if="!isReadOnly" layout="row" layout-wrap
37 37 class="tb-header-buttons md-fab">
38   - <md-button ng-show="isEdit" ng-disabled="loading || theForm.$invalid || !theForm.$dirty"
  38 + <md-button ng-show="isEdit" ng-disabled="$root.loading || theForm.$invalid || !theForm.$dirty"
39 39 class="tb-btn-header md-accent md-hue-2 md-fab md-fab-bottom-right"
40 40 aria-label="{{ 'action.apply' | translate }}"
41 41 ng-click="detailsApply()">
... ... @@ -44,7 +44,7 @@
44 44 </md-tooltip>
45 45 <ng-md-icon icon="done"></ng-md-icon>
46 46 </md-button>
47   - <md-button ng-disabled="loading || (isAlwaysEdit && !theForm.$dirty)" class="tb-btn-header md-accent md-hue-2 md-fab md-fab-bottom-right"
  47 + <md-button ng-disabled="$root.loading || (isAlwaysEdit && !theForm.$dirty)" class="tb-btn-header md-accent md-hue-2 md-fab md-fab-bottom-right"
48 48 aria-label="{{ 'details.edit-mode' | translate }}"
49 49 ng-click="toggleDetailsEditMode()">
50 50 <md-tooltip md-direction="top">
... ...
... ... @@ -45,7 +45,7 @@
45 45 <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>
46 46 </md-card-content>
47 47 <md-card-actions layout="row" layout-align="end end">
48   - <md-button ng-if="action.isEnabled(rowItem[n])" ng-disabled="loading" class="md-icon-button md-primary" ng-repeat="action in vm.actionsList"
  48 + <md-button ng-if="action.isEnabled(rowItem[n])" ng-disabled="$root.loading" class="md-icon-button md-primary" ng-repeat="action in vm.actionsList"
49 49 ng-click="action.onAction($event, rowItem[n])" aria-label="{{ action.name() }}">
50 50 <md-tooltip md-direction="top">
51 51 {{ action.details( rowItem[n] ) }}
... ... @@ -81,28 +81,28 @@
81 81 </section>
82 82
83 83 <section layout="row" layout-wrap class="tb-footer-buttons md-fab " layout-align="start end">
84   - <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"
  84 + <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"
85 85 ng-click="groupAction.onAction($event, vm.items)" aria-label="{{ groupAction.name() }}">
86 86 <md-tooltip md-direction="top">
87 87 {{ groupAction.details(vm.items.selectedCount) }}
88 88 </md-tooltip>
89 89 <ng-md-icon icon="{{groupAction.icon}}"></ng-md-icon>
90 90 </md-button>
91   - <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}}" >
  91 + <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}}" >
92 92 <md-tooltip md-direction="top">
93 93 {{'grid.scroll-to-top' | translate}}
94 94 </md-tooltip>
95 95 <ng-md-icon icon="arrow_drop_up"></ng-md-icon>
96 96 </md-button>
97   - <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() }}" >
  97 + <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() }}" >
98 98 <md-tooltip md-direction="top">
99 99 {{ vm.addItemAction.details() }}
100 100 </md-tooltip>
101 101 <ng-md-icon icon="{{ vm.addItemAction.icon }}"></ng-md-icon>
102 102 </md-button>
103   - <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()">
  103 + <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()">
104 104 <md-fab-trigger>
105   - <md-button ng-disabled="loading" class="tb-btn-footer md-accent md-hue-2 md-fab" aria-label="{{ vm.addItemAction.name() }}" >
  105 + <md-button ng-disabled="$root.loading" class="tb-btn-footer md-accent md-hue-2 md-fab" aria-label="{{ vm.addItemAction.name() }}" >
106 106 <md-tooltip md-direction="top">
107 107 {{ vm.addItemAction.details() }}
108 108 </md-tooltip>
... ... @@ -110,7 +110,7 @@
110 110 </md-button>
111 111 </md-fab-trigger>
112 112 <md-fab-actions>
113   - <md-button ng-disabled="loading" class="md-accent md-hue-2 md-fab" ng-repeat="addItemAction in vm.addItemActions"
  113 + <md-button ng-disabled="$root.loading" class="md-accent md-hue-2 md-fab" ng-repeat="addItemAction in vm.addItemActions"
114 114 ng-click="addItemAction.onAction($event)" aria-label="{{ addItemAction.name() }}" >
115 115 <md-tooltip md-direction="top">
116 116 {{ addItemAction.details() }}
... ...
... ... @@ -16,7 +16,7 @@
16 16
17 17 -->
18 18 <form name="theForm" ng-submit="vm.update()">
19   - <fieldset ng-disabled="loading">
  19 + <fieldset ng-disabled="$root.loading">
20 20 <md-content style="height: 100%" flex layout="column">
21 21 <section layout="column">
22 22 <md-content class="md-padding" layout="column">
... ...
... ... @@ -32,15 +32,15 @@
32 32 </md-button>
33 33 </div>
34 34 </md-toolbar>
35   - <md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!loading" ng-show="loading"></md-progress-linear>
36   - <span style="min-height: 5px;" flex="" ng-show="!loading"></span>
  35 + <md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
  36 + <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
37 37 <div class="tb-absolute-fill tb-icons-load" ng-show="vm.loadingIcons" layout="column" layout-align="center center">
38 38 <md-progress-circular md-mode="indeterminate" ng-disabled="!vm.loadingIcons" class="md-accent" md-diameter="40"></md-progress-circular>
39 39 </div>
40 40 <md-dialog-content>
41 41 <div class="md-dialog-content">
42 42 <md-content class="md-padding" layout="column">
43   - <fieldset ng-disabled="loading">
  43 + <fieldset ng-disabled="$root.loading">
44 44 <md-button ng-class="{'md-primary md-raised': icon == vm.selectedIcon}" class="tb-select-icon-button md-icon-button"
45 45 ng-repeat="icon in vm.icons" ng-click="vm.selectIcon($event, icon)" tb-on-finish-render="iconsLoadFinished">
46 46 <md-icon class="material-icons">{{icon}}</md-icon>
... ... @@ -54,7 +54,7 @@
54 54 </md-dialog-content>
55 55 <md-dialog-actions layout="row">
56 56 <span flex></span>
57   - <md-button ng-disabled="loading" ng-click="vm.cancel()">
  57 + <md-button ng-disabled="$root.loading" ng-click="vm.cancel()">
58 58 {{ 'action.cancel' | translate }}
59 59 </md-button>
60 60 </md-dialog-actions>
... ...
... ... @@ -56,7 +56,7 @@ function PluginSelect($compile, $templateCache, $q, pluginService, types) {
56 56
57 57 var deferred = $q.defer();
58 58
59   - scope.pluginFetchFunction(pageLink).then(function success(result) {
  59 + scope.pluginFetchFunction(pageLink, {ignoreLoading: true}).then(function success(result) {
60 60 deferred.resolve(result.data);
61 61 }, function fail() {
62 62 deferred.reject();
... ... @@ -89,7 +89,7 @@ function PluginSelect($compile, $templateCache, $q, pluginService, types) {
89 89
90 90 if (scope.selectFirstPlugin) {
91 91 var pageLink = {limit: 1, textSearch: ''};
92   - scope.pluginFetchFunction(pageLink).then(function success(result) {
  92 + scope.pluginFetchFunction(pageLink, {ignoreLoading: true}).then(function success(result) {
93 93 var plugins = result.data;
94 94 if (plugins.length > 0) {
95 95 scope.plugin = plugins[0];
... ...
... ... @@ -16,7 +16,7 @@
16 16
17 17 -->
18 18 <form name="theForm" ng-submit="vm.update()">
19   - <fieldset ng-disabled="loading">
  19 + <fieldset ng-disabled="$root.loading">
20 20 <md-content style="height: 100%" flex layout="column">
21 21 <section layout="column">
22 22 <md-tabs ng-class="{'tb-headless': vm.historyOnly}" md-dynamic-height md-selected="vm.timewindow.selectedTab" md-border-bottom>
... ... @@ -81,10 +81,10 @@
81 81 <span flex></span>
82 82 <section layout="row" layout-alignment="start center">
83 83 <span flex></span>
84   - <md-button ng-disabled="loading || theForm.$invalid || !theForm.$dirty" type="submit" class="md-raised md-primary">
  84 + <md-button ng-disabled="$root.loading || theForm.$invalid || !theForm.$dirty" type="submit" class="md-raised md-primary">
85 85 {{ 'action.update' | translate }}
86 86 </md-button>
87   - <md-button ng-disabled="loading" ng-click="vm.cancel()" style="margin-right:20px;">
  87 + <md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">
88 88 {{ 'action.cancel' | translate }}
89 89 </md-button>
90 90 </section>
... ...
... ... @@ -26,12 +26,12 @@
26 26 </md-button>
27 27 </div>
28 28 </md-toolbar>
29   - <md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!loading" ng-show="loading"></md-progress-linear>
30   - <span style="min-height: 5px;" flex="" ng-show="!loading"></span>
  29 + <md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
  30 + <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
31 31 <md-dialog-content>
32 32 <div class="md-dialog-content">
33 33 <md-content class="md-padding" layout="column">
34   - <fieldset ng-disabled="loading" layout="column">
  34 + <fieldset ng-disabled="$root.loading" layout="column">
35 35 <md-input-container class="md-block">
36 36 <label translate>widget-config.action-source</label>
37 37 <md-select name="actionSource" required aria-label="{{ 'widget-config.action-source' | translate }}" ng-model="vm.action.actionSourceId">
... ... @@ -120,7 +120,7 @@
120 120 </div>
121 121 <tb-js-func ng-if="vm.action.type == vm.types.widgetActionTypes.custom.value"
122 122 ng-model="vm.action.customFunction"
123   - function-args="{{ ['$event', 'widgetContext', 'entityId'] }}"
  123 + function-args="{{ ['$event', 'widgetContext', 'entityId', 'entityName', 'additionalParams'] }}"
124 124 validation-args="{{ [] }}">
125 125 </tb-js-func>
126 126 </fieldset>
... ... @@ -129,11 +129,11 @@
129 129 </md-dialog-content>
130 130 <md-dialog-actions layout="row">
131 131 <span flex></span>
132   - <md-button ng-disabled="loading || theForm.$invalid || !theForm.$dirty" type="submit"
  132 + <md-button ng-disabled="$root.loading || theForm.$invalid || !theForm.$dirty" type="submit"
133 133 class="md-raised md-primary">
134 134 {{ (vm.isAdd ? 'action.add' : 'action.save') | translate }}
135 135 </md-button>
136   - <md-button ng-disabled="loading" ng-click="vm.cancel()" style="margin-right:20px;">
  136 + <md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">
137 137 {{ 'action.cancel' | translate }}
138 138 </md-button>
139 139 </md-dialog-actions>
... ...
... ... @@ -104,7 +104,7 @@
104 104 generate-data-key="generateDataKey(chip,type)"
105 105 fetch-entity-keys="fetchEntityKeys({entityAliasId: entityAliasId, query: query, type: type})"
106 106 on-create-entity-alias="onCreateEntityAlias({event: event, alias: alias})"></tb-datasource>
107   - <md-button ng-disabled="loading" class="md-icon-button md-primary"
  107 + <md-button ng-disabled="$root.loading" class="md-icon-button md-primary"
108 108 style="min-width: 40px;"
109 109 ng-click="removeDatasource($event, datasource)"
110 110 aria-label="{{ 'action.remove' | translate }}">
... ... @@ -121,7 +121,7 @@
121 121 </div>
122 122 </div>
123 123 <div flex layout="row" layout-align="start center">
124   - <md-button ng-show="typeParameters.maxDatasources == -1 || datasources.length < typeParameters.maxDatasources" ng-disabled="loading" class="md-primary md-raised"
  124 + <md-button ng-show="typeParameters.maxDatasources == -1 || datasources.length < typeParameters.maxDatasources" ng-disabled="$root.loading" class="md-primary md-raised"
125 125 ng-click="addDatasource($event)" aria-label="{{ 'action.add' | translate }}">
126 126 <md-tooltip md-direction="top">
127 127 {{ 'widget-config.add-datasource' | translate }}
... ...
... ... @@ -444,7 +444,7 @@ export default function WidgetController($scope, $state, $timeout, $window, $ele
444 444 }
445 445 }
446 446
447   - function handleWidgetAction($event, descriptor, entityId, entityName) {
  447 + function handleWidgetAction($event, descriptor, entityId, entityName, additionalParams) {
448 448 var type = descriptor.type;
449 449 var targetEntityParamName = descriptor.stateEntityParamName;
450 450 var targetEntityId;
... ... @@ -485,8 +485,11 @@ export default function WidgetController($scope, $state, $timeout, $window, $ele
485 485 var customFunction = descriptor.customFunction;
486 486 if (angular.isDefined(customFunction) && customFunction.length > 0) {
487 487 try {
488   - var customActionFunction = new Function('$event', 'widgetContext', 'entityId', 'entityName', customFunction);
489   - customActionFunction($event, widgetContext, entityId, entityName);
  488 + if (!additionalParams) {
  489 + additionalParams = {};
  490 + }
  491 + var customActionFunction = new Function('$event', 'widgetContext', 'entityId', 'entityName', 'additionalParams', customFunction);
  492 + customActionFunction($event, widgetContext, entityId, entityName, additionalParams);
490 493 } catch (e) {
491 494 //
492 495 }
... ...
... ... @@ -48,7 +48,7 @@ function WidgetsBundleSelect($compile, $templateCache, widgetService, types) {
48 48 }
49 49 }
50 50
51   - widgetsBundleFetchFunction().then(
  51 + widgetsBundleFetchFunction({ignoreLoading: true}).then(
52 52 function success(widgetsBundles) {
53 53 scope.widgetsBundles = widgetsBundles;
54 54 if (scope.selectFirstBundle) {
... ...
... ... @@ -27,8 +27,8 @@
27 27 </md-button>
28 28 </div>
29 29 </md-toolbar>
30   - <md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!loading" ng-show="loading"></md-progress-linear>
31   - <span style="min-height: 5px;" flex="" ng-show="!loading"></span>
  30 + <md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
  31 + <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
32 32 <md-dialog-content>
33 33 <div class="md-dialog-content">
34 34 <tb-customer customer="vm.item" is-edit="true" the-form="theForm"></tb-customer>
... ... @@ -36,10 +36,10 @@
36 36 </md-dialog-content>
37 37 <md-dialog-actions layout="row">
38 38 <span flex></span>
39   - <md-button ng-disabled="loading || theForm.$invalid || !theForm.$dirty" type="submit" class="md-raised md-primary">
  39 + <md-button ng-disabled="$root.loading || theForm.$invalid || !theForm.$dirty" type="submit" class="md-raised md-primary">
40 40 {{ 'action.add' | translate }}
41 41 </md-button>
42   - <md-button ng-disabled="loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | translate }}</md-button>
  42 + <md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | translate }}</md-button>
43 43 </md-dialog-actions>
44 44 </form>
45 45 </md-dialog>
... ...
... ... @@ -32,7 +32,7 @@
32 32 </div>
33 33
34 34 <md-content class="md-padding" layout="column">
35   - <fieldset ng-show="!isPublic" ng-disabled="loading || !isEdit">
  35 + <fieldset ng-show="!isPublic" ng-disabled="$root.loading || !isEdit">
36 36 <md-input-container class="md-block">
37 37 <label translate>customer.title</label>
38 38 <input required name="title" ng-model="customer.title">
... ...
... ... @@ -27,8 +27,8 @@
27 27 </md-button>
28 28 </div>
29 29 </md-toolbar>
30   - <md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!loading" ng-show="loading"></md-progress-linear>
31   - <span style="min-height: 5px;" flex="" ng-show="!loading"></span>
  30 + <md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
  31 + <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
32 32 <md-dialog-content>
33 33 <div class="md-dialog-content">
34 34 <tb-dashboard-details dashboard="vm.item" is-edit="true" the-form="theForm"></tb-dashboard-details>
... ... @@ -36,10 +36,10 @@
36 36 </md-dialog-content>
37 37 <md-dialog-actions layout="row">
38 38 <span flex></span>
39   - <md-button ng-disabled="loading || theForm.$invalid || !theForm.$dirty" type="submit" class="md-raised md-primary">
  39 + <md-button ng-disabled="$root.loading || theForm.$invalid || !theForm.$dirty" type="submit" class="md-raised md-primary">
40 40 {{ 'action.add' | translate }}
41 41 </md-button>
42   - <md-button ng-disabled="loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | translate }}</md-button>
  42 + <md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | translate }}</md-button>
43 43 </md-dialog-actions>
44 44 </form>
45 45 </md-dialog>
\ No newline at end of file
... ...
... ... @@ -26,8 +26,8 @@
26 26 </md-button>
27 27 </div>
28 28 </md-toolbar>
29   - <md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!loading" ng-show="loading"></md-progress-linear>
30   - <span style="min-height: 5px;" flex="" ng-show="!loading"></span>
  29 + <md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
  30 + <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
31 31 <md-dialog-content>
32 32 <div class="md-dialog-content">
33 33 <fieldset>
... ... @@ -65,11 +65,11 @@
65 65 </md-dialog-content>
66 66 <md-dialog-actions layout="row">
67 67 <span flex></span>
68   - <md-button ng-disabled="loading || vm.dashboards.selectedCount == 0" type="submit"
  68 + <md-button ng-disabled="$root.loading || vm.dashboards.selectedCount == 0" type="submit"
69 69 class="md-raised md-primary">
70 70 {{ 'action.assign' | translate }}
71 71 </md-button>
72   - <md-button ng-disabled="loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' |
  72 + <md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' |
73 73 translate }}
74 74 </md-button>
75 75 </md-dialog-actions>
... ...
... ... @@ -110,7 +110,7 @@ export default function AddWidgetController($scope, widgetService, entityService
110 110 function success(aliasInfo) {
111 111 var entity = aliasInfo.currentEntity;
112 112 if (entity) {
113   - entityService.getEntityKeys(entity.entityType, entity.id, query, type).then(
  113 + entityService.getEntityKeys(entity.entityType, entity.id, query, type, {ignoreLoading: true}).then(
114 114 function success(keys) {
115 115 deferred.resolve(keys);
116 116 },
... ...
... ... @@ -27,11 +27,11 @@
27 27 </md-button>
28 28 </div>
29 29 </md-toolbar>
30   - <md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!loading" ng-show="loading"></md-progress-linear>
31   - <span style="min-height: 5px;" flex="" ng-show="!loading"></span>
  30 + <md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
  31 + <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
32 32 <md-dialog-content>
33 33 <div class="md-dialog-content" style="padding-top: 0px;">
34   - <fieldset ng-disabled="loading" style="position: relative; height: 600px;">
  34 + <fieldset ng-disabled="$root.loading" style="position: relative; height: 600px;">
35 35 <tb-widget-config widget-type="vm.widget.type"
36 36 type-parameters="vm.widgetInfo.typeParameters"
37 37 action-sources="vm.widgetInfo.actionSources"
... ... @@ -50,11 +50,11 @@
50 50 </md-dialog-content>
51 51 <md-dialog-actions layout="row">
52 52 <span flex></span>
53   - <md-button ng-disabled="loading || theForm.$invalid" type="submit"
  53 + <md-button ng-disabled="$root.loading || theForm.$invalid" type="submit"
54 54 class="md-raised md-primary">
55 55 {{ 'action.add' | translate }}
56 56 </md-button>
57   - <md-button ng-disabled="loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' |
  57 + <md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' |
58 58 translate }}
59 59 </md-button>
60 60 </md-dialog-actions>
... ...
... ... @@ -26,8 +26,8 @@
26 26 </md-button>
27 27 </div>
28 28 </md-toolbar>
29   - <md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!loading" ng-show="loading"></md-progress-linear>
30   - <span style="min-height: 5px;" flex="" ng-show="!loading"></span>
  29 + <md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
  30 + <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
31 31 <md-dialog-content>
32 32 <div class="md-dialog-content">
33 33 <fieldset>
... ... @@ -65,10 +65,10 @@
65 65 </md-dialog-content>
66 66 <md-dialog-actions layout="row">
67 67 <span flex></span>
68   - <md-button ng-disabled="loading || vm.customers.selection==null" type="submit" class="md-raised md-primary">
  68 + <md-button ng-disabled="$root.loading || vm.customers.selection==null" type="submit" class="md-raised md-primary">
69 69 {{ 'action.assign' | translate }}
70 70 </md-button>
71   - <md-button ng-disabled="loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' |
  71 + <md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' |
72 72 translate }}
73 73 </md-button>
74 74 </md-dialog-actions>
... ...
... ... @@ -59,7 +59,7 @@
59 59 </md-button>
60 60 </div>
61 61 </div>
62   - <fieldset ng-disabled="loading || !isEdit">
  62 + <fieldset ng-disabled="$root.loading || !isEdit">
63 63 <md-input-container class="md-block">
64 64 <label translate>dashboard.title</label>
65 65 <input required name="title" ng-model="dashboard.title">
... ...
... ... @@ -26,11 +26,11 @@
26 26 </md-button>
27 27 </div>
28 28 </md-toolbar>
29   - <md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!loading" ng-show="loading"></md-progress-linear>
30   - <span style="min-height: 5px;" flex="" ng-show="!loading"></span>
  29 + <md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
  30 + <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
31 31 <md-dialog-content>
32 32 <div class="md-dialog-content">
33   - <fieldset ng-disabled="loading">
  33 + <fieldset ng-disabled="$root.loading">
34 34 <div ng-show="vm.settings">
35 35 <md-input-container class="md-block">
36 36 <label translate>dashboard.state-controller</label>
... ... @@ -194,10 +194,10 @@
194 194 </md-dialog-content>
195 195 <md-dialog-actions layout="row">
196 196 <span flex></span>
197   - <md-button ng-disabled="loading || !theForm.$dirty || !theForm.$valid" type="submit" class="md-raised md-primary">
  197 + <md-button ng-disabled="$root.loading || !theForm.$dirty || !theForm.$valid" type="submit" class="md-raised md-primary">
198 198 {{ 'action.save' | translate }}
199 199 </md-button>
200   - <md-button ng-disabled="loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | translate }}</md-button>
  200 + <md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | translate }}</md-button>
201 201 </md-dialog-actions>
202 202 </form>
203 203 </md-dialog>
... ...
... ... @@ -110,7 +110,7 @@
110 110 </section>
111 111 <section class="tb-dashboard-container tb-absolute-fill"
112 112 ng-class="{ 'is-fullscreen': forceFullscreen, 'tb-dashboard-toolbar-opened': vm.toolbarOpened, 'tb-dashboard-toolbar-closed': !vm.toolbarOpened }">
113   - <section ng-show="!loading && vm.dashboardConfigurationError()" layout-align="center center"
  113 + <section ng-show="!$root.loading && vm.dashboardConfigurationError()" layout-align="center center"
114 114 ng-style="{'color': vm.dashboard.configuration.settings.titleColor}"
115 115 ng-class="{'tb-padded' : !vm.widgetEditMode}"
116 116 style="text-transform: uppercase; display: flex; z-index: 1;"
... ... @@ -277,10 +277,10 @@
277 277 </div>
278 278 </tb-details-sidenav>
279 279 <section layout="row" layout-wrap class="tb-footer-buttons md-fab" layout-align="start end">
280   - <md-fab-speed-dial ng-disabled="loading" ng-show="!vm.isAddingWidget && vm.isEdit && !vm.widgetEditMode"
  280 + <md-fab-speed-dial ng-disabled="$root.loading" ng-show="!vm.isAddingWidget && vm.isEdit && !vm.widgetEditMode"
281 281 md-open="vm.addItemActionsOpen" class="md-scale" md-direction="up">
282 282 <md-fab-trigger>
283   - <md-button ng-disabled="loading"
  283 + <md-button ng-disabled="$root.loading"
284 284 class="tb-btn-footer md-accent md-hue-2 md-fab"
285 285 aria-label="{{ 'dashboard.add-widget' | translate }}">
286 286 <md-tooltip md-direction="top">
... ... @@ -290,7 +290,7 @@
290 290 </md-button>
291 291 </md-fab-trigger>
292 292 <md-fab-actions>
293   - <md-button ng-disabled="loading"
  293 + <md-button ng-disabled="$root.loading"
294 294 class="tmd-accent md-hue-2 md-fab" ng-click="vm.addWidget($event)"
295 295 aria-label="{{ 'action.create' | translate }}">
296 296 <md-tooltip md-direction="top">
... ... @@ -298,7 +298,7 @@
298 298 </md-tooltip>
299 299 <ng-md-icon icon="insert_drive_file"></ng-md-icon>
300 300 </md-button>
301   - <md-button ng-disabled="loading"
  301 + <md-button ng-disabled="$root.loading"
302 302 class="tmd-accent md-hue-2 md-fab" ng-click="vm.importWidget($event)"
303 303 aria-label="{{ 'action.import' | translate }}">
304 304 <md-tooltip md-direction="top">
... ... @@ -308,7 +308,7 @@
308 308 </md-button>
309 309 </md-fab-actions>
310 310 </md-fab-speed-dial>
311   - <md-button ng-if="(vm.isTenantAdmin() || vm.isSystemAdmin()) && !forceFullscreen" ng-show="vm.isEdit && !vm.isAddingWidget && !loading" ng-disabled="loading"
  311 + <md-button ng-if="(vm.isTenantAdmin() || vm.isSystemAdmin()) && !forceFullscreen" ng-show="vm.isEdit && !vm.isAddingWidget && !$root.loading" ng-disabled="$root.loading"
312 312 class="tb-btn-footer md-accent md-hue-2 md-fab"
313 313 aria-label="{{ 'action.apply' | translate }}"
314 314 ng-click="vm.saveDashboard()">
... ... @@ -317,8 +317,8 @@
317 317 </md-tooltip>
318 318 <ng-md-icon icon="done"></ng-md-icon>
319 319 </md-button>
320   - <md-button ng-show="!vm.isAddingWidget && !loading"
321   - ng-if="(vm.isTenantAdmin() || vm.isSystemAdmin()) && !forceFullscreen" ng-disabled="loading"
  320 + <md-button ng-show="!vm.isAddingWidget && !$root.loading"
  321 + ng-if="(vm.isTenantAdmin() || vm.isSystemAdmin()) && !forceFullscreen" ng-disabled="$root.loading"
322 322 class="tb-btn-footer md-accent md-hue-2 md-fab"
323 323 aria-label="{{ 'action.edit-mode' | translate }}"
324 324 ng-click="vm.toggleDashboardEditMode()">
... ...
... ... @@ -75,7 +75,7 @@ export default function EditWidgetDirective($compile, $templateCache, types, wid
75 75 function success(aliasInfo) {
76 76 var entity = aliasInfo.currentEntity;
77 77 if (entity) {
78   - entityService.getEntityKeys(entity.entityType, entity.id, query, type).then(
  78 + entityService.getEntityKeys(entity.entityType, entity.id, query, type, {ignoreLoading: true}).then(
79 79 function success(keys) {
80 80 deferred.resolve(keys);
81 81 },
... ...
... ... @@ -15,7 +15,7 @@
15 15 limitations under the License.
16 16
17 17 -->
18   -<fieldset ng-disabled="loading">
  18 +<fieldset ng-disabled="$root.loading">
19 19 <tb-widget-config widget-type="widget.type"
20 20 type-parameters="typeParameters"
21 21 action-sources="actionSources"
... ...
... ... @@ -22,7 +22,7 @@
22 22 'background-attachment': 'scroll',
23 23 'background-size': vm.layoutCtx.gridSettings.backgroundSizeMode || '100%',
24 24 'background-position': '0% 0%'}">
25   - <section ng-show="!loading && vm.noData()" layout-align="center center"
  25 + <section ng-show="!$root.loading && vm.noData()" layout-align="center center"
26 26 ng-style="{'color': vm.layoutCtx.gridSettings.color}"
27 27 style="text-transform: uppercase; display: flex; z-index: 1; pointer-events: none;"
28 28 class="md-headline tb-absolute-fill">
... ...
... ... @@ -26,11 +26,11 @@
26 26 </md-button>
27 27 </div>
28 28 </md-toolbar>
29   - <md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!loading" ng-show="loading"></md-progress-linear>
30   - <span style="min-height: 5px;" flex="" ng-show="!loading"></span>
  29 + <md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
  30 + <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
31 31 <md-dialog-content>
32 32 <div class="md-dialog-content">
33   - <fieldset ng-disabled="loading">
  33 + <fieldset ng-disabled="$root.loading">
34 34 <div layout="row" layout-align="start center">
35 35 <md-checkbox ng-disabled="true" flex aria-label="{{ 'layout.main' | translate }}"
36 36 ng-model="vm.displayLayouts.main">{{ 'layout.main' | translate }}
... ... @@ -56,10 +56,10 @@
56 56 </md-dialog-content>
57 57 <md-dialog-actions layout="row">
58 58 <span flex></span>
59   - <md-button ng-disabled="loading || !theForm.$dirty || !theForm.$valid" type="submit" class="md-raised md-primary">
  59 + <md-button ng-disabled="$root.loading || !theForm.$dirty || !theForm.$valid" type="submit" class="md-raised md-primary">
60 60 {{ 'action.save' | translate }}
61 61 </md-button>
62   - <md-button ng-disabled="loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | translate }}</md-button>
  62 + <md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | translate }}</md-button>
63 63 </md-dialog-actions>
64 64 </form>
65 65 </md-dialog>
... ...
... ... @@ -26,11 +26,11 @@
26 26 </md-button>
27 27 </div>
28 28 </md-toolbar>
29   - <md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!loading" ng-show="loading"></md-progress-linear>
30   - <span style="min-height: 5px;" flex="" ng-show="!loading"></span>
  29 + <md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
  30 + <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
31 31 <md-dialog-content>
32 32 <div class="md-dialog-content">
33   - <fieldset ng-disabled="loading">
  33 + <fieldset ng-disabled="$root.loading">
34 34 <div layout="row" layout-align="start center">
35 35 <md-button flex class="tb-layout-button md-raised md-primary" layout="column"
36 36 ng-click="vm.selectLayout($event, 'main')">
... ...
... ... @@ -26,12 +26,12 @@
26 26 </md-button>
27 27 </div>
28 28 </md-toolbar>
29   - <md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!loading" ng-show="loading"></md-progress-linear>
30   - <span style="min-height: 5px;" flex="" ng-show="!loading"></span>
  29 + <md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
  30 + <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
31 31 <md-dialog-content>
32 32 <div class="md-dialog-content">
33 33 <md-content class="md-padding" layout="column">
34   - <fieldset ng-disabled="loading">
  34 + <fieldset ng-disabled="$root.loading">
35 35 <md-input-container class="md-block">
36 36 <label translate>dashboard.state-name</label>
37 37 <input name="name" required ng-model="vm.state.name">
... ... @@ -57,11 +57,11 @@
57 57 </md-dialog-content>
58 58 <md-dialog-actions layout="row">
59 59 <span flex></span>
60   - <md-button ng-disabled="loading || theForm.$invalid || !theForm.$dirty" type="submit"
  60 + <md-button ng-disabled="$root.loading || theForm.$invalid || !theForm.$dirty" type="submit"
61 61 class="md-raised md-primary">
62 62 {{ (vm.isAdd ? 'action.add' : 'action.save') | translate }}
63 63 </md-button>
64   - <md-button ng-disabled="loading" ng-click="vm.cancel()" style="margin-right:20px;">
  64 + <md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">
65 65 {{ 'action.cancel' | translate }}
66 66 </md-button>
67 67 </md-dialog-actions>
... ...
... ... @@ -26,11 +26,11 @@
26 26 </md-button>
27 27 </div>
28 28 </md-toolbar>
29   - <md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!loading" ng-show="loading"></md-progress-linear>
30   - <span style="min-height: 5px;" flex="" ng-show="!loading"></span>
  29 + <md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
  30 + <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
31 31 <md-dialog-content>
32 32 <div class="md-dialog-content">
33   - <fieldset ng-disabled="loading">
  33 + <fieldset ng-disabled="$root.loading">
34 34 <div class="manage-dashboard-states" layout="column">
35 35 <md-toolbar class="md-table-toolbar md-default" ng-show="vm.query.search === null">
36 36 <div class="md-toolbar-tools">
... ... @@ -118,10 +118,10 @@
118 118 </md-dialog-content>
119 119 <md-dialog-actions layout="row">
120 120 <span flex></span>
121   - <md-button ng-disabled="loading || !theForm.$dirty || !theForm.$valid" type="submit" class="md-raised md-primary">
  121 + <md-button ng-disabled="$root.loading || !theForm.$dirty || !theForm.$valid" type="submit" class="md-raised md-primary">
122 122 {{ 'action.save' | translate }}
123 123 </md-button>
124   - <md-button ng-disabled="loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | translate }}</md-button>
  124 + <md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | translate }}</md-button>
125 125 </md-dialog-actions>
126 126 </form>
127 127 </md-dialog>
... ...
... ... @@ -26,11 +26,11 @@
26 26 </md-button>
27 27 </div>
28 28 </md-toolbar>
29   - <md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!loading" ng-show="loading"></md-progress-linear>
30   - <span style="min-height: 5px;" flex="" ng-show="!loading"></span>
  29 + <md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
  30 + <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
31 31 <md-dialog-content>
32 32 <div class="md-dialog-content">
33   - <fieldset ng-disabled="loading">
  33 + <fieldset ng-disabled="$root.loading">
34 34 <md-select required aria-label="{{ 'dashboard.state' | translate }}" ng-model="vm.stateId">
35 35 <md-option ng-repeat="(stateId, state) in vm.states" ng-value="stateId">
36 36 {{state.name}}
... ... @@ -41,10 +41,10 @@
41 41 </md-dialog-content>
42 42 <md-dialog-actions layout="row">
43 43 <span flex></span>
44   - <md-button ng-disabled="loading || !theForm.$dirty || !theForm.$valid" type="submit" class="md-raised md-primary">
  44 + <md-button ng-disabled="$root.loading || !theForm.$dirty || !theForm.$valid" type="submit" class="md-raised md-primary">
45 45 {{ 'action.save' | translate }}
46 46 </md-button>
47   - <md-button ng-disabled="loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | translate }}</md-button>
  47 + <md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | translate }}</md-button>
48 48 </md-dialog-actions>
49 49 </form>
50 50 </md-dialog>
... ...
... ... @@ -27,8 +27,8 @@
27 27 </md-button>
28 28 </div>
29 29 </md-toolbar>
30   - <md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!loading" ng-show="loading"></md-progress-linear>
31   - <span style="min-height: 5px;" flex="" ng-show="!loading"></span>
  30 + <md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
  31 + <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
32 32 <md-dialog-content>
33 33 <div class="md-dialog-content">
34 34 <tb-device device="vm.item" is-edit="true" the-form="theForm"></tb-device>
... ... @@ -36,10 +36,10 @@
36 36 </md-dialog-content>
37 37 <md-dialog-actions layout="row">
38 38 <span flex></span>
39   - <md-button ng-disabled="loading || theForm.$invalid || !theForm.$dirty" type="submit" class="md-raised md-primary">
  39 + <md-button ng-disabled="$root.loading || theForm.$invalid || !theForm.$dirty" type="submit" class="md-raised md-primary">
40 40 {{ 'action.add' | translate }}
41 41 </md-button>
42   - <md-button ng-disabled="loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | translate }}</md-button>
  42 + <md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | translate }}</md-button>
43 43 </md-dialog-actions>
44 44 </form>
45 45 </md-dialog>
\ No newline at end of file
... ...
... ... @@ -26,8 +26,8 @@
26 26 </md-button>
27 27 </div>
28 28 </md-toolbar>
29   - <md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!loading" ng-show="loading"></md-progress-linear>
30   - <span style="min-height: 5px;" flex="" ng-show="!loading"></span>
  29 + <md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
  30 + <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
31 31 <md-dialog-content>
32 32 <div class="md-dialog-content">
33 33 <fieldset>
... ... @@ -65,11 +65,11 @@
65 65 </md-dialog-content>
66 66 <md-dialog-actions layout="row">
67 67 <span flex></span>
68   - <md-button ng-disabled="loading || vm.devices.selectedCount == 0" type="submit"
  68 + <md-button ng-disabled="$root.loading || vm.devices.selectedCount == 0" type="submit"
69 69 class="md-raised md-primary">
70 70 {{ 'action.assign' | translate }}
71 71 </md-button>
72   - <md-button ng-disabled="loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' |
  72 + <md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' |
73 73 translate }}
74 74 </md-button>
75 75 </md-dialog-actions>
... ...
... ... @@ -26,8 +26,8 @@
26 26 </md-button>
27 27 </div>
28 28 </md-toolbar>
29   - <md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!loading" ng-show="loading"></md-progress-linear>
30   - <span style="min-height: 5px;" flex="" ng-show="!loading"></span>
  29 + <md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
  30 + <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
31 31 <md-dialog-content>
32 32 <div class="md-dialog-content">
33 33 <fieldset>
... ... @@ -65,10 +65,10 @@
65 65 </md-dialog-content>
66 66 <md-dialog-actions layout="row">
67 67 <span flex></span>
68   - <md-button ng-disabled="loading || vm.customers.selection==null" type="submit" class="md-raised md-primary">
  68 + <md-button ng-disabled="$root.loading || vm.customers.selection==null" type="submit" class="md-raised md-primary">
69 69 {{ 'action.assign' | translate }}
70 70 </md-button>
71   - <md-button ng-disabled="loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' |
  71 + <md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' |
72 72 translate }}
73 73 </md-button>
74 74 </md-dialog-actions>
... ...
... ... @@ -26,14 +26,14 @@
26 26 </md-button>
27 27 </div>
28 28 </md-toolbar>
29   - <md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!loading" ng-show="loading"></md-progress-linear>
30   - <span style="min-height: 5px;" flex="" ng-show="!loading"></span>
  29 + <md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
  30 + <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
31 31 <md-dialog-content>
32 32 <div class="md-dialog-content">
33   - <fieldset ng-disabled="loading || vm.isReadOnly">
  33 + <fieldset ng-disabled="$root.loading || vm.isReadOnly">
34 34 <md-input-container class="md-block">
35 35 <label translate>device.credentials-type</label>
36   - <md-select ng-disabled="loading || vm.isReadOnly" ng-model="vm.deviceCredentials.credentialsType"
  36 + <md-select ng-disabled="$root.loading || vm.isReadOnly" ng-model="vm.deviceCredentials.credentialsType"
37 37 ng-change="vm.clear()">
38 38 <md-option ng-repeat="credentialsType in vm.credentialsTypes" value="{{credentialsType.value}}">
39 39 {{credentialsType.name}}
... ... @@ -62,10 +62,10 @@
62 62 </md-dialog-content>
63 63 <md-dialog-actions layout="row">
64 64 <span flex></span>
65   - <md-button ng-if="!vm.isReadOnly" ng-disabled="loading || theForm.$invalid || !theForm.$dirty || !vm.valid()" type="submit" class="md-raised md-primary">
  65 + <md-button ng-if="!vm.isReadOnly" ng-disabled="$root.loading || theForm.$invalid || !theForm.$dirty || !vm.valid()" type="submit" class="md-raised md-primary">
66 66 {{ 'action.save' | translate }}
67 67 </md-button>
68   - <md-button ng-disabled="loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ (vm.isReadOnly ? 'action.close' : 'action.cancel') | translate }}</md-button>
  68 + <md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ (vm.isReadOnly ? 'action.close' : 'action.cancel') | translate }}</md-button>
69 69 </md-dialog-actions>
70 70 </form>
71 71 </md-dialog>
\ No newline at end of file
... ...
... ... @@ -58,7 +58,7 @@
58 58 ng-show="!isEdit && isPublic && (deviceScope === 'customer' || deviceScope === 'tenant')">
59 59 {{ 'device.device-public' | translate }}
60 60 </div>
61   - <fieldset ng-disabled="loading || !isEdit">
  61 + <fieldset ng-disabled="$root.loading || !isEdit">
62 62 <md-input-container class="md-block">
63 63 <label translate>device.name</label>
64 64 <input required name="name" ng-model="device.name">
... ... @@ -67,14 +67,14 @@
67 67 </div>
68 68 </md-input-container>
69 69 <tb-entity-subtype-autocomplete
70   - ng-disabled="loading || !isEdit"
  70 + ng-disabled="$root.loading || !isEdit"
71 71 tb-required="true"
72 72 the-form="theForm"
73 73 ng-model="device.type"
74 74 entity-type="types.entityType.device">
75 75 </tb-entity-subtype-autocomplete>
76 76 <md-input-container class="md-block">
77   - <md-checkbox ng-disabled="loading || !isEdit" flex aria-label="{{ 'device.is-gateway' | translate }}"
  77 + <md-checkbox ng-disabled="$root.loading || !isEdit" flex aria-label="{{ 'device.is-gateway' | translate }}"
78 78 ng-model="device.additionalInfo.gateway">{{ 'device.is-gateway' | translate }}
79 79 </md-checkbox>
80 80 </md-input-container>
... ...
... ... @@ -26,11 +26,11 @@
26 26 </md-button>
27 27 </div>
28 28 </md-toolbar>
29   - <md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!loading" ng-show="loading"></md-progress-linear>
30   - <span style="min-height: 5px;" flex="" ng-show="!loading"></span>
  29 + <md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
  30 + <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
31 31 <md-dialog-content>
32 32 <div class="md-dialog-content">
33   - <fieldset ng-disabled="loading">
  33 + <fieldset ng-disabled="$root.loading">
34 34 <div flex layout="column">
35 35 <div layout="row">
36 36 <md-input-container flex class="md-block">
... ... @@ -64,10 +64,10 @@
64 64 </md-dialog-content>
65 65 <md-dialog-actions layout="row">
66 66 <span flex></span>
67   - <md-button ng-disabled="loading || theForm.$invalid || !theForm.$dirty" type="submit" class="md-raised md-primary">
  67 + <md-button ng-disabled="$root.loading || theForm.$invalid || !theForm.$dirty" type="submit" class="md-raised md-primary">
68 68 {{ (vm.isAdd ? 'action.add' : 'action.save') | translate }}
69 69 </md-button>
70   - <md-button ng-disabled="loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | translate }}</md-button>
  70 + <md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | translate }}</md-button>
71 71 </md-dialog-actions>
72 72 </form>
73 73 </md-dialog>
... ...
... ... @@ -26,8 +26,8 @@
26 26 </md-button>
27 27 </div>
28 28 </md-toolbar>
29   - <md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!loading" ng-show="loading"></md-progress-linear>
30   - <span style="min-height: 5px;" flex="" ng-show="!loading"></span>
  29 + <md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
  30 + <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
31 31 <div class="tb-aliases-header" flex layout="row" layout-align="start center">
32 32 <span flex="5"></span>
33 33 <div flex layout="row" layout-align="start center">
... ... @@ -40,7 +40,7 @@
40 40 <md-divider></md-divider>
41 41 <md-dialog-content>
42 42 <div class="md-dialog-content">
43   - <fieldset ng-disabled="loading">
  43 + <fieldset ng-disabled="$root.loading">
44 44 <div ng-form name="aliasForm" flex layout="row" layout-align="start center" ng-repeat="entityAlias in vm.entityAliases track by $index">
45 45 <span flex="5">{{$index + 1}}.</span>
46 46 <di class="md-whiteframe-4dp tb-alias" flex layout="row" layout-align="start center">
... ... @@ -63,7 +63,7 @@
63 63 aria-label="resolve-multiple-switcher">
64 64 </md-switch>
65 65 </section>
66   - <md-button ng-disabled="loading" class="md-icon-button md-primary" style="min-width: 40px;"
  66 + <md-button ng-disabled="$root.loading" class="md-icon-button md-primary" style="min-width: 40px;"
67 67 ng-click="vm.editAlias($event, entityAlias)" aria-label="{{ 'action.edit' | translate }}">
68 68 <md-tooltip md-direction="top">
69 69 {{ 'alias.edit' | translate }}
... ... @@ -72,7 +72,7 @@
72 72 edit
73 73 </md-icon>
74 74 </md-button>
75   - <md-button ng-disabled="loading" class="md-icon-button md-primary" style="min-width: 40px;"
  75 + <md-button ng-disabled="$root.loading" class="md-icon-button md-primary" style="min-width: 40px;"
76 76 ng-click="vm.removeAlias($event, entityAlias)" aria-label="{{ 'action.remove' | translate }}">
77 77 <md-tooltip md-direction="top">
78 78 {{ 'entity.remove-alias' | translate }}
... ... @@ -87,7 +87,7 @@
87 87 </div>
88 88 </md-dialog-content>
89 89 <md-dialog-actions layout="row">
90   - <md-button ng-show="!vm.disableAdd" ng-disabled="loading" class="md-primary md-raised"
  90 + <md-button ng-show="!vm.disableAdd" ng-disabled="$root.loading" class="md-primary md-raised"
91 91 ng-click="vm.addAlias($event)"
92 92 aria-label="{{ 'alias.add' | translate }}">
93 93 <md-tooltip md-direction="top">
... ... @@ -96,10 +96,10 @@
96 96 <span translate>alias.add</span>
97 97 </md-button>
98 98 <span flex></span>
99   - <md-button ng-disabled="loading || theForm.$invalid || !theForm.$dirty" type="submit" class="md-raised md-primary">
  99 + <md-button ng-disabled="$root.loading || theForm.$invalid || !theForm.$dirty" type="submit" class="md-raised md-primary">
100 100 {{ 'action.save' | translate }}
101 101 </md-button>
102   - <md-button ng-disabled="loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | translate }}</md-button>
  102 + <md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | translate }}</md-button>
103 103 </md-dialog-actions>
104 104 </form>
105 105 </md-dialog>
\ No newline at end of file
... ...
... ... @@ -26,12 +26,12 @@
26 26 </md-button>
27 27 </div>
28 28 </md-toolbar>
29   - <md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!loading" ng-show="loading"></md-progress-linear>
30   - <span style="min-height: 5px;" flex="" ng-show="!loading"></span>
  29 + <md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
  30 + <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
31 31 <md-dialog-content>
32 32 <div class="md-dialog-content">
33 33 <md-content class="md-padding" layout="column">
34   - <fieldset ng-disabled="loading">
  34 + <fieldset ng-disabled="$root.loading">
35 35 <md-input-container class="md-block">
36 36 <label translate>attribute.key</label>
37 37 <input required name="key" ng-model="vm.attribute.key">
... ... @@ -42,7 +42,7 @@
42 42 <section layout="row">
43 43 <md-input-container flex="40" class="md-block" style="width: 200px;">
44 44 <label translate>value.type</label>
45   - <md-select ng-model="vm.valueType" ng-disabled="loading()">
  45 + <md-select ng-model="vm.valueType" ng-disabled="$root.loading">
46 46 <md-option ng-repeat="type in vm.valueTypes" ng-value="type">
47 47 <md-icon md-svg-icon="{{ type.icon }}"></md-icon>
48 48 <span>{{type.name | translate}}</span>
... ... @@ -83,11 +83,11 @@
83 83 </md-dialog-content>
84 84 <md-dialog-actions layout="row">
85 85 <span flex></span>
86   - <md-button ng-disabled="loading || theForm.$invalid || !theForm.$dirty" type="submit"
  86 + <md-button ng-disabled="$root.loading || theForm.$invalid || !theForm.$dirty" type="submit"
87 87 class="md-raised md-primary">
88 88 {{ 'action.add' | translate }}
89 89 </md-button>
90   - <md-button ng-disabled="loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' |
  90 + <md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' |
91 91 translate }}
92 92 </md-button>
93 93 </md-dialog-actions>
... ...
... ... @@ -26,18 +26,18 @@
26 26 </md-button>
27 27 </div>
28 28 </md-toolbar>
29   - <md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!loading" ng-show="loading"></md-progress-linear>
30   - <span style="min-height: 5px;" flex="" ng-show="!loading"></span>
  29 + <md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
  30 + <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
31 31 <md-dialog-content>
32 32 <div class="md-dialog-content">
33 33 <md-content class="md-padding" layout="column">
34   - <fieldset ng-disabled="loading">
  34 + <fieldset ng-disabled="$root.loading">
35 35 <md-radio-group ng-model="vm.addToDashboardType" class="md-primary">
36 36 <md-radio-button flex ng-value=0 class="md-primary md-align-top-left md-radio-interactive">
37 37 <section flex layout="column" style="width: 300px;">
38 38 <span translate style="padding-bottom: 10px;">dashboard.select-existing</span>
39 39 <tb-dashboard-autocomplete the-form="theForm"
40   - ng-disabled="loading || vm.addToDashboardType != 0"
  40 + ng-disabled="$root.loading || vm.addToDashboardType != 0"
41 41 tb-required="vm.addToDashboardType === 0"
42 42 ng-model="vm.dashboardId"
43 43 select-first-dashboard="false">
... ... @@ -69,11 +69,11 @@
69 69 style="margin-bottom: 0px; padding-right: 20px;">
70 70 {{ 'dashboard.open-dashboard' | translate }}
71 71 </md-checkbox>
72   - <md-button ng-disabled="loading || theForm.$invalid || !theForm.$dirty" type="submit"
  72 + <md-button ng-disabled="$root.loading || theForm.$invalid || !theForm.$dirty" type="submit"
73 73 class="md-raised md-primary">
74 74 {{ 'action.add' | translate }}
75 75 </md-button>
76   - <md-button ng-disabled="loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' |
  76 + <md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' |
77 77 translate }}
78 78 </md-button>
79 79 </md-dialog-actions>
... ...
... ... @@ -47,7 +47,7 @@ md-toolbar.md-table-toolbar.alternate {
47 47 .widgets-carousel {
48 48 position: relative;
49 49 margin: 0px;
50   -
  50 + height: calc(100% - 100px);
51 51 min-height: 150px !important;
52 52
53 53 tb-dashboard {
... ...
... ... @@ -19,7 +19,7 @@
19 19 <section ng-show="!disableAttributeScopeSelection">
20 20 <md-input-container class="md-block" style="width: 200px;">
21 21 <label translate>attribute.attributes-scope</label>
22   - <md-select ng-model="attributeScope" ng-disabled="loading() || attributeScopeSelectionReadonly">
  22 + <md-select ng-model="attributeScope" ng-disabled="$root.loading || attributeScopeSelectionReadonly">
23 23 <md-option ng-repeat="scope in attributeScopes" ng-value="scope">
24 24 {{scope.name | translate}}
25 25 </md-option>
... ...
... ... @@ -38,7 +38,7 @@ export default function EntityAutocomplete($compile, $templateCache, $q, $filter
38 38 if (scope.excludeEntityIds && scope.excludeEntityIds.length) {
39 39 limit += scope.excludeEntityIds.length;
40 40 }
41   - entityService.getEntitiesByNameFilter(scope.entityType, searchText, limit, null, scope.entitySubtype).then(function success(result) {
  41 + entityService.getEntitiesByNameFilter(scope.entityType, searchText, limit, {ignoreLoading: true}, scope.entitySubtype).then(function success(result) {
42 42 if (result) {
43 43 if (scope.excludeEntityIds && scope.excludeEntityIds.length) {
44 44 var entities = [];
... ...
... ... @@ -38,7 +38,7 @@ export default function EntityListDirective($compile, $templateCache, $q, $mdUti
38 38
39 39 scope.fetchEntities = function(searchText, limit) {
40 40 var deferred = $q.defer();
41   - entityService.getEntitiesByNameFilter(scope.entityType, searchText, limit).then(
  41 + entityService.getEntitiesByNameFilter(scope.entityType, searchText, limit, {ignoreLoading: true}).then(
42 42 function success(result) {
43 43 if (result) {
44 44 deferred.resolve(result);
... ...
... ... @@ -93,9 +93,9 @@ export default function EntitySubtypeAutocomplete($compile, $templateCache, $q,
93 93 if (!scope.entitySubtypes) {
94 94 var entitySubtypesPromise;
95 95 if (scope.entityType == types.entityType.asset) {
96   - entitySubtypesPromise = assetService.getAssetTypes();
  96 + entitySubtypesPromise = assetService.getAssetTypes({ignoreLoading: true});
97 97 } else if (scope.entityType == types.entityType.device) {
98   - entitySubtypesPromise = deviceService.getDeviceTypes();
  98 + entitySubtypesPromise = deviceService.getDeviceTypes({ignoreLoading: true});
99 99 }
100 100 if (entitySubtypesPromise) {
101 101 entitySubtypesPromise.then(
... ...
... ... @@ -95,9 +95,9 @@ export default function EntitySubtypeListDirective($compile, $templateCache, $q,
95 95 if (!scope.entitySubtypes) {
96 96 var entitySubtypesPromise;
97 97 if (scope.entityType == types.entityType.asset) {
98   - entitySubtypesPromise = assetService.getAssetTypes();
  98 + entitySubtypesPromise = assetService.getAssetTypes({ignoreLoading: true});
99 99 } else if (scope.entityType == types.entityType.device) {
100   - entitySubtypesPromise = deviceService.getDeviceTypes();
  100 + entitySubtypesPromise = deviceService.getDeviceTypes({ignoreLoading: true});
101 101 }
102 102 if (entitySubtypesPromise) {
103 103 entitySubtypesPromise.then(
... ...
... ... @@ -73,9 +73,9 @@ export default function EntitySubtypeSelect($compile, $templateCache, $translate
73 73 scope.entitySubtypes = [];
74 74 var entitySubtypesPromise;
75 75 if (scope.entityType == types.entityType.asset) {
76   - entitySubtypesPromise = assetService.getAssetTypes();
  76 + entitySubtypesPromise = assetService.getAssetTypes({ignoreLoading: true});
77 77 } else if (scope.entityType == types.entityType.device) {
78   - entitySubtypesPromise = deviceService.getDeviceTypes();
  78 + entitySubtypesPromise = deviceService.getDeviceTypes({ignoreLoading: true});
79 79 }
80 80 if (entitySubtypesPromise) {
81 81 entitySubtypesPromise.then(
... ...
... ... @@ -26,16 +26,16 @@
26 26 </md-button>
27 27 </div>
28 28 </md-toolbar>
29   - <md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!loading" ng-show="loading"></md-progress-linear>
30   - <span style="min-height: 5px;" flex="" ng-show="!loading"></span>
  29 + <md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
  30 + <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
31 31 <md-dialog-content>
32 32 <div class="md-dialog-content">
33 33 <md-content class="md-padding" layout="column">
34   - <fieldset ng-disabled="loading">
  34 + <fieldset ng-disabled="$root.loading">
35 35 <tb-relation-type-autocomplete ng-disabled="!vm.isAdd"
36 36 ng-model="vm.relation.type"
37 37 tb-required="true"
38   - ng-disabled="loading">
  38 + ng-disabled="$root.loading">
39 39 </tb-relation-type-autocomplete>
40 40 <small>{{(vm.direction == vm.types.entitySearchDirection.from ?
41 41 'relation.to-entity' : 'relation.from-entity') | translate}}</small>
... ... @@ -61,11 +61,11 @@
61 61 </md-dialog-content>
62 62 <md-dialog-actions layout="row">
63 63 <span flex></span>
64   - <md-button ng-disabled="loading || theForm.$invalid || !theForm.$dirty" type="submit"
  64 + <md-button ng-disabled="$root.loading || theForm.$invalid || !theForm.$dirty" type="submit"
65 65 class="md-raised md-primary">
66 66 {{ (vm.isAdd ? 'action.add' : 'action.save') | translate }}
67 67 </md-button>
68   - <md-button ng-disabled="loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' |
  68 + <md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' |
69 69 translate }}
70 70 </md-button>
71 71 </md-dialog-actions>
... ...
... ... @@ -37,7 +37,7 @@
37 37 allowed-entity-types="allowedEntityTypes"
38 38 tb-required="false">
39 39 </tb-entity-type-list>
40   - <md-button ng-disabled="loading" class="md-icon-button md-primary" style="width: 40px; min-width: 40px;"
  40 + <md-button ng-disabled="$root.loading" class="md-icon-button md-primary" style="width: 40px; min-width: 40px;"
41 41 ng-click="removeFilter($event, filter)" aria-label="{{ 'action.remove' | translate }}">
42 42 <md-tooltip md-direction="top">
43 43 {{ 'relation.remove-relation-filter' | translate }}
... ... @@ -54,7 +54,7 @@
54 54 class="tb-prompt" translate>relation.any-relation</span>
55 55 </div>
56 56 <div>
57   - <md-button ng-disabled="loading" class="md-primary md-raised" ng-click="addFilter($event)" aria-label="{{ 'action.add' | translate }}">
  57 + <md-button ng-disabled="$root.loading" class="md-primary md-raised" ng-click="addFilter($event)" aria-label="{{ 'action.add' | translate }}">
58 58 <md-tooltip md-direction="top">
59 59 {{ 'relation.add-relation-filter' | translate }}
60 60 </md-tooltip>
... ...
... ... @@ -19,7 +19,7 @@
19 19 <section layout="row">
20 20 <md-input-container class="md-block" style="width: 200px;">
21 21 <label translate>relation.direction</label>
22   - <md-select ng-model="vm.direction" ng-disabled="loading">
  22 + <md-select ng-model="vm.direction" ng-disabled="$root.loading">
23 23 <md-option ng-repeat="direction in vm.types.entitySearchDirection" ng-value="direction">
24 24 {{ ('relation.search-direction.' + direction) | translate}}
25 25 </md-option>
... ...
... ... @@ -35,7 +35,7 @@
35 35 </md-dialog-content>
36 36 <md-dialog-actions layout="row">
37 37 <span flex></span>
38   - <md-button ng-disabled="loading" ng-click="vm.close()" style="margin-right:20px;">{{ 'action.close' |
  38 + <md-button ng-disabled="$root.loading" ng-click="vm.close()" style="margin-right:20px;">{{ 'action.close' |
39 39 translate }}
40 40 </md-button>
41 41 </md-dialog-actions>
... ...
... ... @@ -19,7 +19,7 @@
19 19 <section layout="row">
20 20 <md-input-container class="md-block" style="width: 200px;">
21 21 <label translate>event.event-type</label>
22   - <md-select ng-model="eventType" ng-disabled="loading()">
  22 + <md-select ng-model="eventType" ng-disabled="$root.loading">
23 23 <md-option ng-repeat="type in eventTypes" ng-value="type.value">
24 24 {{type.name | translate}}
25 25 </md-option>
... ... @@ -30,8 +30,8 @@
30 30 <md-list flex layout="column" class="md-whiteframe-z1 tb-event-table">
31 31 <md-list class="tb-row tb-header" layout="row" tb-event-header event-type="{{eventType}}">
32 32 </md-list>
33   - <md-progress-linear style="max-height: 0px;" md-mode="indeterminate" ng-disabled="!loading()"
34   - ng-show="loading()"></md-progress-linear>
  33 + <md-progress-linear style="max-height: 0px;" md-mode="indeterminate" ng-disabled="!$root.loading"
  34 + ng-show="$root.loading"></md-progress-linear>
35 35 <md-divider></md-divider>
36 36 <span translate layout-align="center center"
37 37 style="margin-top: 25px;"
... ...
... ... @@ -27,14 +27,14 @@
27 27 </div>
28 28 </md-toolbar>
29 29
30   - <md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!loading" ng-show="loading"></md-progress-linear>
  30 + <md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
31 31
32   - <span style="min-height: 5px;" flex="" ng-show="!loading"></span>
  32 + <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
33 33
34 34 <md-dialog-content>
35 35 <div class="md-dialog-content">
36 36 <md-content class="md-padding" layout="column">
37   - <fieldset ng-disabled="loading">
  37 + <fieldset ng-disabled="$root.loading">
38 38 <section flex layout="row">
39 39 <md-input-container flex="60" class="md-block" md-is-error="theForm.extensionId.$touched && theForm.extensionId.$invalid">
40 40 <label translate>extension.extension-id</label>
... ... @@ -74,7 +74,7 @@
74 74 {{ (vm.isAdd ? 'action.add' : 'action.save') | translate }}
75 75 </md-button>
76 76
77   - <md-button ng-disabled="loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | translate }}
  77 + <md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | translate }}
78 78 </md-button>
79 79 </md-dialog-actions>
80 80 </form>
... ...
... ... @@ -26,11 +26,11 @@
26 26 </md-button>
27 27 </div>
28 28 </md-toolbar>
29   - <md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!loading" ng-show="loading"></md-progress-linear>
30   - <span style="min-height: 5px;" flex="" ng-show="!loading"></span>
  29 + <md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
  30 + <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
31 31 <md-dialog-content>
32 32 <div class="md-dialog-content">
33   - <fieldset ng-disabled="loading">
  33 + <fieldset ng-disabled="$root.loading">
34 34 <div layout="column" layout-padding>
35 35 <div class="tb-container">
36 36 <label class="tb-label" translate>{{ vm.importFileLabel }}</label>
... ... @@ -63,10 +63,10 @@
63 63 </md-dialog-content>
64 64 <md-dialog-actions layout="row">
65 65 <span flex></span>
66   - <md-button ng-disabled="loading || !theForm.$dirty || !theForm.$valid || !vm.importData" type="submit" class="md-raised md-primary">
  66 + <md-button ng-disabled="$root.loading || !theForm.$dirty || !theForm.$valid || !vm.importData" type="submit" class="md-raised md-primary">
67 67 {{ 'action.import' | translate }}
68 68 </md-button>
69   - <md-button ng-disabled="loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | translate }}</md-button>
  69 + <md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | translate }}</md-button>
70 70 </md-dialog-actions>
71 71 </form>
72 72 </md-dialog>
... ...
... ... @@ -76,7 +76,7 @@
76 76 </tb-user-menu>
77 77 </div>
78 78 </md-toolbar>
79   - <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>
  79 + <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>
80 80
81 81 <div flex layout="column" id="toast-parent" style="position: relative;">
82 82 <md-content ng-cloak flex layout="column" class="page-content" ui-view name="content"></md-content>
... ...
... ... @@ -1213,6 +1213,7 @@ export default angular.module('thingsboard.locale', [])
1213 1213 "remove-widget-title": "Are you sure you want to remove the widget '{{widgetTitle}}'?",
1214 1214 "remove-widget-text": "After the confirmation the widget and all related data will become unrecoverable.",
1215 1215 "timeseries": "Time series",
  1216 + "search-data": "Search data",
1216 1217 "latest-values": "Latest values",
1217 1218 "rpc": "Control widget",
1218 1219 "alarm": "Alarm widget",
... ...
... ... @@ -23,7 +23,7 @@
23 23 </md-card-title-text>
24 24 </md-card-title>
25 25 <md-progress-linear class="md-warn" style="z-index: 1; max-height: 5px; width: inherit; position: absolute"
26   - md-mode="indeterminate" ng-disabled="!loading" ng-show="loading"></md-progress-linear>
  26 + md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
27 27 <md-card-content>
28 28 <form class="create-password-form" ng-submit="vm.createPassword()">
29 29 <div layout="column" layout-padding="" id="toast-parent">
... ...
... ... @@ -23,7 +23,7 @@
23 23 </md-card-title-text>
24 24 </md-card-title>
25 25 <md-progress-linear class="md-warn" style="z-index: 1; max-height: 5px; width: inherit; position: absolute"
26   - md-mode="indeterminate" ng-disabled="!loading" ng-show="loading"></md-progress-linear>
  26 + md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
27 27 <md-card-content>
28 28 <form class="login-form" ng-submit="vm.login()">
29 29 <div layout="column" layout-padding="" id="toast-parent">
... ...
... ... @@ -23,7 +23,7 @@
23 23 </md-card-title-text>
24 24 </md-card-title>
25 25 <md-progress-linear class="md-warn" style="z-index: 1; max-height: 5px; width: inherit; position: absolute"
26   - md-mode="indeterminate" ng-disabled="!loading" ng-show="loading"></md-progress-linear>
  26 + md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
27 27 <md-card-content>
28 28 <form class="request-password-reset-form" ng-submit="vm.sendResetPasswordLink()">
29 29 <div layout="column" layout-padding="" id="toast-parent">
... ...