Browse Source

Implement multiple customer assigned dashboards UI. Add Current Customer entity type for dashboard aliases.

pull/619/head
Igor Kulikov 9 years ago
parent
commit
0021f007f1
  1. 3
      application/src/main/java/org/thingsboard/server/controller/BaseController.java
  2. 160
      application/src/main/java/org/thingsboard/server/controller/DashboardController.java
  3. 17
      application/src/main/java/org/thingsboard/server/service/install/DatabaseHelper.java
  4. 4
      application/src/test/java/org/thingsboard/server/controller/BaseDashboardControllerTest.java
  5. 5
      common/data/src/main/java/org/thingsboard/server/common/data/Customer.java
  6. 49
      common/data/src/main/java/org/thingsboard/server/common/data/DashboardInfo.java
  7. 54
      common/data/src/main/java/org/thingsboard/server/common/data/ShortCustomerInfo.java
  8. 4
      dao/src/main/java/org/thingsboard/server/dao/customer/CustomerServiceImpl.java
  9. 6
      dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardService.java
  10. 75
      dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardServiceImpl.java
  11. 28
      dao/src/main/java/org/thingsboard/server/dao/model/nosql/DashboardEntity.java
  12. 32
      dao/src/main/java/org/thingsboard/server/dao/model/nosql/DashboardInfoEntity.java
  13. 26
      dao/src/main/java/org/thingsboard/server/dao/model/sql/DashboardEntity.java
  14. 27
      dao/src/main/java/org/thingsboard/server/dao/model/sql/DashboardInfoEntity.java
  15. 2
      dao/src/test/java/org/thingsboard/server/dao/service/BaseDashboardServiceTest.java
  16. 150
      ui/src/app/api/dashboard.service.js
  17. 43
      ui/src/app/api/entity.service.js
  18. 4
      ui/src/app/api/user.service.js
  19. 7
      ui/src/app/common/types.constant.js
  20. 4
      ui/src/app/components/dashboard-autocomplete.directive.js
  21. 4
      ui/src/app/components/dashboard-select.directive.js
  22. 2
      ui/src/app/dashboard/add-dashboards-to-customer.controller.js
  23. 123
      ui/src/app/dashboard/assign-to-customer.controller.js
  24. 76
      ui/src/app/dashboard/assign-to-customer.tpl.html
  25. 27
      ui/src/app/dashboard/dashboard-card.scss
  26. 4
      ui/src/app/dashboard/dashboard-card.tpl.html
  27. 27
      ui/src/app/dashboard/dashboard-fieldset.tpl.html
  28. 29
      ui/src/app/dashboard/dashboard.directive.js
  29. 226
      ui/src/app/dashboard/dashboards.controller.js
  30. 6
      ui/src/app/dashboard/dashboards.tpl.html
  31. 4
      ui/src/app/dashboard/index.js
  32. 69
      ui/src/app/dashboard/manage-assigned-customers.controller.js
  33. 51
      ui/src/app/dashboard/manage-assigned-customers.tpl.html
  34. 37
      ui/src/app/entity/entity-autocomplete.directive.js
  35. 8
      ui/src/app/entity/entity-filter.tpl.html
  36. 3
      ui/src/app/entity/entity-select.directive.js
  37. 1
      ui/src/app/entity/entity-select.tpl.html
  38. 5
      ui/src/app/entity/entity-type-select.directive.js
  39. 11
      ui/src/app/import-export/import-export.service.js
  40. 16
      ui/src/app/locale/locale.constant.js

3
application/src/main/java/org/thingsboard/server/controller/BaseController.java

@ -434,7 +434,6 @@ public abstract class BaseController {
try { try {
validateId(dashboardId, "Incorrect dashboardId " + dashboardId); validateId(dashboardId, "Incorrect dashboardId " + dashboardId);
DashboardInfo dashboardInfo = dashboardService.findDashboardInfoById(dashboardId); DashboardInfo dashboardInfo = dashboardService.findDashboardInfoById(dashboardId);
SecurityUser authUser = getCurrentUser();
checkDashboard(dashboardInfo); checkDashboard(dashboardInfo);
return dashboardInfo; return dashboardInfo;
} catch (Exception e) { } catch (Exception e) {
@ -447,7 +446,7 @@ public abstract class BaseController {
checkTenantId(dashboard.getTenantId()); checkTenantId(dashboard.getTenantId());
SecurityUser authUser = getCurrentUser(); SecurityUser authUser = getCurrentUser();
if (authUser.getAuthority() == Authority.CUSTOMER_USER) { if (authUser.getAuthority() == Authority.CUSTOMER_USER) {
if (dashboard.getAssignedCustomers() == null || !dashboard.getAssignedCustomers().containsKey(authUser.getCustomerId().toString())) { if (!dashboard.isAssignedToCustomer(authUser.getCustomerId())) {
throw new ThingsboardException(YOU_DON_T_HAVE_PERMISSION_TO_PERFORM_THIS_OPERATION, throw new ThingsboardException(YOU_DON_T_HAVE_PERMISSION_TO_PERFORM_THIS_OPERATION,
ThingsboardErrorCode.PERMISSION_DENIED); ThingsboardErrorCode.PERMISSION_DENIED);
} }

160
application/src/main/java/org/thingsboard/server/controller/DashboardController.java

@ -18,10 +18,7 @@ package org.thingsboard.server.controller;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.*;
import org.thingsboard.server.common.data.Dashboard;
import org.thingsboard.server.common.data.DashboardInfo;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DashboardId; import org.thingsboard.server.common.data.id.DashboardId;
@ -34,6 +31,9 @@ import org.thingsboard.server.dao.exception.IncorrectParameterException;
import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.dao.model.ModelConstants;
import org.thingsboard.server.exception.ThingsboardException; import org.thingsboard.server.exception.ThingsboardException;
import java.util.HashSet;
import java.util.Set;
@RestController @RestController
@RequestMapping("/api") @RequestMapping("/api")
public class DashboardController extends BaseController { public class DashboardController extends BaseController {
@ -181,6 +181,158 @@ public class DashboardController extends BaseController {
} }
} }
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/dashboard/{dashboardId}/customers", method = RequestMethod.POST)
@ResponseBody
public Dashboard updateDashboardCustomers(@PathVariable(DASHBOARD_ID) String strDashboardId,
@RequestBody String[] strCustomerIds) throws ThingsboardException {
checkParameter(DASHBOARD_ID, strDashboardId);
try {
DashboardId dashboardId = new DashboardId(toUUID(strDashboardId));
Dashboard dashboard = checkDashboardId(dashboardId);
Set<CustomerId> customerIds = new HashSet<>();
if (strCustomerIds != null) {
for (String strCustomerId : strCustomerIds) {
customerIds.add(new CustomerId(toUUID(strCustomerId)));
}
}
Set<CustomerId> addedCustomerIds = new HashSet<>();
Set<CustomerId> removedCustomerIds = new HashSet<>();
for (CustomerId customerId : customerIds) {
if (!dashboard.isAssignedToCustomer(customerId)) {
addedCustomerIds.add(customerId);
}
}
Set<ShortCustomerInfo> assignedCustomers = dashboard.getAssignedCustomers();
if (assignedCustomers != null) {
for (ShortCustomerInfo customerInfo : assignedCustomers) {
if (!customerIds.contains(customerInfo.getCustomerId())) {
removedCustomerIds.add(customerInfo.getCustomerId());
}
}
}
if (addedCustomerIds.isEmpty() && removedCustomerIds.isEmpty()) {
return dashboard;
} else {
Dashboard savedDashboard = null;
for (CustomerId customerId : addedCustomerIds) {
savedDashboard = checkNotNull(dashboardService.assignDashboardToCustomer(dashboardId, customerId));
ShortCustomerInfo customerInfo = savedDashboard.getAssignedCustomerInfo(customerId);
logEntityAction(dashboardId, savedDashboard,
customerId,
ActionType.ASSIGNED_TO_CUSTOMER, null, strDashboardId, customerId.toString(), customerInfo.getTitle());
}
for (CustomerId customerId : removedCustomerIds) {
ShortCustomerInfo customerInfo = dashboard.getAssignedCustomerInfo(customerId);
savedDashboard = checkNotNull(dashboardService.unassignDashboardFromCustomer(dashboardId, customerId));
logEntityAction(dashboardId, dashboard,
customerId,
ActionType.UNASSIGNED_FROM_CUSTOMER, null, strDashboardId, customerId.toString(), customerInfo.getTitle());
}
return savedDashboard;
}
} catch (Exception e) {
logEntityAction(emptyId(EntityType.DASHBOARD), null,
null,
ActionType.ASSIGNED_TO_CUSTOMER, e, strDashboardId);
throw handleException(e);
}
}
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/dashboard/{dashboardId}/customers/add", method = RequestMethod.POST)
@ResponseBody
public Dashboard addDashboardCustomers(@PathVariable(DASHBOARD_ID) String strDashboardId,
@RequestBody String[] strCustomerIds) throws ThingsboardException {
checkParameter(DASHBOARD_ID, strDashboardId);
try {
DashboardId dashboardId = new DashboardId(toUUID(strDashboardId));
Dashboard dashboard = checkDashboardId(dashboardId);
Set<CustomerId> customerIds = new HashSet<>();
if (strCustomerIds != null) {
for (String strCustomerId : strCustomerIds) {
CustomerId customerId = new CustomerId(toUUID(strCustomerId));
if (!dashboard.isAssignedToCustomer(customerId)) {
customerIds.add(customerId);
}
}
}
if (customerIds.isEmpty()) {
return dashboard;
} else {
Dashboard savedDashboard = null;
for (CustomerId customerId : customerIds) {
savedDashboard = checkNotNull(dashboardService.assignDashboardToCustomer(dashboardId, customerId));
ShortCustomerInfo customerInfo = savedDashboard.getAssignedCustomerInfo(customerId);
logEntityAction(dashboardId, savedDashboard,
customerId,
ActionType.ASSIGNED_TO_CUSTOMER, null, strDashboardId, customerId.toString(), customerInfo.getTitle());
}
return savedDashboard;
}
} catch (Exception e) {
logEntityAction(emptyId(EntityType.DASHBOARD), null,
null,
ActionType.ASSIGNED_TO_CUSTOMER, e, strDashboardId);
throw handleException(e);
}
}
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/dashboard/{dashboardId}/customers/remove", method = RequestMethod.POST)
@ResponseBody
public Dashboard removeDashboardCustomers(@PathVariable(DASHBOARD_ID) String strDashboardId,
@RequestBody String[] strCustomerIds) throws ThingsboardException {
checkParameter(DASHBOARD_ID, strDashboardId);
try {
DashboardId dashboardId = new DashboardId(toUUID(strDashboardId));
Dashboard dashboard = checkDashboardId(dashboardId);
Set<CustomerId> customerIds = new HashSet<>();
if (strCustomerIds != null) {
for (String strCustomerId : strCustomerIds) {
CustomerId customerId = new CustomerId(toUUID(strCustomerId));
if (dashboard.isAssignedToCustomer(customerId)) {
customerIds.add(customerId);
}
}
}
if (customerIds.isEmpty()) {
return dashboard;
} else {
Dashboard savedDashboard = null;
for (CustomerId customerId : customerIds) {
ShortCustomerInfo customerInfo = dashboard.getAssignedCustomerInfo(customerId);
savedDashboard = checkNotNull(dashboardService.unassignDashboardFromCustomer(dashboardId, customerId));
logEntityAction(dashboardId, dashboard,
customerId,
ActionType.UNASSIGNED_FROM_CUSTOMER, null, strDashboardId, customerId.toString(), customerInfo.getTitle());
}
return savedDashboard;
}
} catch (Exception e) {
logEntityAction(emptyId(EntityType.DASHBOARD), null,
null,
ActionType.UNASSIGNED_FROM_CUSTOMER, e, strDashboardId);
throw handleException(e);
}
}
@PreAuthorize("hasAuthority('TENANT_ADMIN')") @PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/customer/public/dashboard/{dashboardId}", method = RequestMethod.POST) @RequestMapping(value = "/customer/public/dashboard/{dashboardId}", method = RequestMethod.POST)
@ResponseBody @ResponseBody

17
application/src/main/java/org/thingsboard/server/service/install/DatabaseHelper.java

@ -15,12 +15,13 @@
*/ */
package org.thingsboard.server.service.install; package org.thingsboard.server.service.install;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.csv.CSVFormat; import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.CSVParser; import org.apache.commons.csv.CSVParser;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import com.fasterxml.jackson.databind.JsonNode; import org.thingsboard.server.common.data.ShortCustomerInfo;
import org.thingsboard.server.common.data.UUIDConverter; import org.thingsboard.server.common.data.UUIDConverter;
import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DashboardId; import org.thingsboard.server.common.data.id.DashboardId;
@ -54,7 +55,8 @@ public class DatabaseHelper {
public static final ObjectMapper objectMapper = new ObjectMapper(); public static final ObjectMapper objectMapper = new ObjectMapper();
public static void upgradeTo40_assignDashboards(Path dashboardsDump, DashboardService dashboardService, boolean sql) throws Exception { public static void upgradeTo40_assignDashboards(Path dashboardsDump, DashboardService dashboardService, boolean sql) throws Exception {
String[] columns = new String[]{ID, TENANT_ID, CUSTOMER_ID, TITLE, SEARCH_TEXT, ASSIGNED_CUSTOMERS, CONFIGURATION}; JavaType assignedCustomersType =
objectMapper.getTypeFactory().constructCollectionType(HashSet.class, ShortCustomerInfo.class);
try (CSVParser csvParser = new CSVParser(Files.newBufferedReader(dashboardsDump), CSV_DUMP_FORMAT.withFirstRecordAsHeader())) { try (CSVParser csvParser = new CSVParser(Files.newBufferedReader(dashboardsDump), CSV_DUMP_FORMAT.withFirstRecordAsHeader())) {
csvParser.forEach(record -> { csvParser.forEach(record -> {
String customerIdString = record.get(CUSTOMER_ID); String customerIdString = record.get(CUSTOMER_ID);
@ -63,12 +65,11 @@ public class DatabaseHelper {
List<CustomerId> customerIds = new ArrayList<>(); List<CustomerId> customerIds = new ArrayList<>();
if (!StringUtils.isEmpty(assignedCustomersString)) { if (!StringUtils.isEmpty(assignedCustomersString)) {
try { try {
JsonNode assignedCustomersJson = objectMapper.readTree(assignedCustomersString); Set<ShortCustomerInfo> assignedCustomers = objectMapper.readValue(assignedCustomersString, assignedCustomersType);
Map<String,String> assignedCustomers = objectMapper.treeToValue(assignedCustomersJson, HashMap.class); assignedCustomers.forEach((customerInfo) -> {
assignedCustomers.forEach((strCustomerId, title) -> { CustomerId customerId = customerInfo.getCustomerId();
CustomerId customerId = new CustomerId(UUID.fromString(strCustomerId));
if (!customerId.isNullUid()) { if (!customerId.isNullUid()) {
customerIds.add(new CustomerId(UUID.fromString(strCustomerId))); customerIds.add(customerId);
} }
}); });
} catch (IOException e) { } catch (IOException e) {
@ -78,7 +79,7 @@ public class DatabaseHelper {
if (!StringUtils.isEmpty(customerIdString)) { if (!StringUtils.isEmpty(customerIdString)) {
CustomerId customerId = new CustomerId(toUUID(customerIdString, sql)); CustomerId customerId = new CustomerId(toUUID(customerIdString, sql));
if (!customerId.isNullUid()) { if (!customerId.isNullUid()) {
customerIds.add(new CustomerId(toUUID(customerIdString, sql))); customerIds.add(customerId);
} }
} }
for (CustomerId customerId : customerIds) { for (CustomerId customerId : customerIds) {

4
application/src/test/java/org/thingsboard/server/controller/BaseDashboardControllerTest.java

@ -138,10 +138,10 @@ public abstract class BaseDashboardControllerTest extends AbstractControllerTest
Dashboard assignedDashboard = doPost("/api/customer/" + savedCustomer.getId().getId().toString() Dashboard assignedDashboard = doPost("/api/customer/" + savedCustomer.getId().getId().toString()
+ "/dashboard/" + savedDashboard.getId().getId().toString(), Dashboard.class); + "/dashboard/" + savedDashboard.getId().getId().toString(), Dashboard.class);
Assert.assertTrue(assignedDashboard.getAssignedCustomers().containsKey(savedCustomer.getId().toString())); Assert.assertTrue(assignedDashboard.getAssignedCustomers().contains(savedCustomer.toShortCustomerInfo()));
Dashboard foundDashboard = doGet("/api/dashboard/" + savedDashboard.getId().getId().toString(), Dashboard.class); Dashboard foundDashboard = doGet("/api/dashboard/" + savedDashboard.getId().getId().toString(), Dashboard.class);
Assert.assertTrue(foundDashboard.getAssignedCustomers().containsKey(savedCustomer.getId().toString())); Assert.assertTrue(foundDashboard.getAssignedCustomers().contains(savedCustomer.toShortCustomerInfo()));
Dashboard unassignedDashboard = Dashboard unassignedDashboard =
doDelete("/api/customer/"+savedCustomer.getId().getId().toString()+"/dashboard/" + savedDashboard.getId().getId().toString(), Dashboard.class); doDelete("/api/customer/"+savedCustomer.getId().getId().toString()+"/dashboard/" + savedDashboard.getId().getId().toString(), Dashboard.class);

5
common/data/src/main/java/org/thingsboard/server/common/data/Customer.java

@ -69,6 +69,11 @@ public class Customer extends ContactBased<CustomerId> implements HasName {
return false; return false;
} }
@JsonIgnore
public ShortCustomerInfo toShortCustomerInfo() {
return new ShortCustomerInfo(id, title, isPublic());
}
@Override @Override
@JsonProperty(access = Access.READ_ONLY) @JsonProperty(access = Access.READ_ONLY)
public String getName() { public String getName() {

49
common/data/src/main/java/org/thingsboard/server/common/data/DashboardInfo.java

@ -20,16 +20,13 @@ import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DashboardId; import org.thingsboard.server.common.data.id.DashboardId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import java.util.HashMap; import java.util.*;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class DashboardInfo extends SearchTextBased<DashboardId> implements HasName { public class DashboardInfo extends SearchTextBased<DashboardId> implements HasName {
private TenantId tenantId; private TenantId tenantId;
private String title; private String title;
private Map<String, String> assignedCustomers; private Set<ShortCustomerInfo> assignedCustomers;
public DashboardInfo() { public DashboardInfo() {
super(); super();
@ -62,38 +59,56 @@ public class DashboardInfo extends SearchTextBased<DashboardId> implements HasNa
this.title = title; this.title = title;
} }
public Map<String, String> getAssignedCustomers() { public Set<ShortCustomerInfo> getAssignedCustomers() {
return assignedCustomers; return assignedCustomers;
} }
public void setAssignedCustomers(Map<String, String> assignedCustomers) { public void setAssignedCustomers(Set<ShortCustomerInfo> assignedCustomers) {
this.assignedCustomers = assignedCustomers; this.assignedCustomers = assignedCustomers;
} }
public boolean addAssignedCustomer(CustomerId customerId, String title) { public boolean isAssignedToCustomer(CustomerId customerId) {
if (this.assignedCustomers != null && this.assignedCustomers.containsKey(customerId.toString())) { return this.assignedCustomers != null && this.assignedCustomers.contains(new ShortCustomerInfo(customerId, null, false));
}
public ShortCustomerInfo getAssignedCustomerInfo(CustomerId customerId) {
if (this.assignedCustomers != null) {
for (ShortCustomerInfo customerInfo : this.assignedCustomers) {
if (customerInfo.getCustomerId().equals(customerId)) {
return customerInfo;
}
}
}
return null;
}
public boolean addAssignedCustomer(Customer customer) {
ShortCustomerInfo customerInfo = customer.toShortCustomerInfo();
if (this.assignedCustomers != null && this.assignedCustomers.contains(customerInfo)) {
return false; return false;
} else { } else {
if (this.assignedCustomers == null) { if (this.assignedCustomers == null) {
this.assignedCustomers = new HashMap<>(); this.assignedCustomers = new HashSet<>();
} }
this.assignedCustomers.put(customerId.toString(), title); this.assignedCustomers.add(customerInfo);
return true; return true;
} }
} }
public boolean updateAssignedCustomer(CustomerId customerId, String title) { public boolean updateAssignedCustomer(Customer customer) {
if (this.assignedCustomers != null && this.assignedCustomers.containsKey(customerId.toString())) { ShortCustomerInfo customerInfo = customer.toShortCustomerInfo();
this.assignedCustomers.put(customerId.toString(), title); if (this.assignedCustomers != null && this.assignedCustomers.contains(customerInfo)) {
this.assignedCustomers.add(customerInfo);
return true; return true;
} else { } else {
return false; return false;
} }
} }
public boolean removeAssignedCustomer(CustomerId customerId) { public boolean removeAssignedCustomer(Customer customer) {
if (this.assignedCustomers != null && this.assignedCustomers.containsKey(customerId.toString())) { ShortCustomerInfo customerInfo = customer.toShortCustomerInfo();
this.assignedCustomers.remove(customerId.toString()); if (this.assignedCustomers != null && this.assignedCustomers.contains(customerInfo)) {
this.assignedCustomers.remove(customerInfo);
return true; return true;
} else { } else {
return false; return false;

54
common/data/src/main/java/org/thingsboard/server/common/data/ShortCustomerInfo.java

@ -0,0 +1,54 @@
/**
* Copyright © 2016-2017 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.common.data;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;
import org.thingsboard.server.common.data.id.CustomerId;
/**
* Created by igor on 2/27/18.
*/
@AllArgsConstructor
public class ShortCustomerInfo {
@Getter @Setter
private CustomerId customerId;
@Getter @Setter
private String title;
@Getter @Setter
private boolean isPublic;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ShortCustomerInfo that = (ShortCustomerInfo) o;
return customerId.equals(that.customerId);
}
@Override
public int hashCode() {
return customerId.hashCode();
}
}

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

@ -98,7 +98,7 @@ public class CustomerServiceImpl extends AbstractEntityService implements Custom
log.trace("Executing saveCustomer [{}]", customer); log.trace("Executing saveCustomer [{}]", customer);
customerValidator.validate(customer); customerValidator.validate(customer);
Customer savedCustomer = customerDao.save(customer); Customer savedCustomer = customerDao.save(customer);
dashboardService.updateCustomerDashboards(savedCustomer.getTenantId(), savedCustomer.getId(), savedCustomer.getTitle()); dashboardService.updateCustomerDashboards(savedCustomer.getId());
return savedCustomer; return savedCustomer;
} }
@ -110,7 +110,7 @@ public class CustomerServiceImpl extends AbstractEntityService implements Custom
if (customer == null) { if (customer == null) {
throw new IncorrectParameterException("Unable to delete non-existent customer."); throw new IncorrectParameterException("Unable to delete non-existent customer.");
} }
dashboardService.unassignCustomerDashboards(customer.getTenantId(), customerId); dashboardService.unassignCustomerDashboards(customerId);
assetService.unassignCustomerAssets(customer.getTenantId(), customerId); assetService.unassignCustomerAssets(customer.getTenantId(), customerId);
deviceService.unassignCustomerDevices(customer.getTenantId(), customerId); deviceService.unassignCustomerDevices(customer.getTenantId(), customerId);
userService.deleteCustomerUsers(customer.getTenantId(), customerId); userService.deleteCustomerUsers(customer.getTenantId(), customerId);

6
dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardService.java

@ -26,7 +26,7 @@ import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.common.data.page.TimePageData; import org.thingsboard.server.common.data.page.TimePageData;
import org.thingsboard.server.common.data.page.TimePageLink; import org.thingsboard.server.common.data.page.TimePageLink;
import java.sql.Time; import java.util.Set;
public interface DashboardService { public interface DashboardService {
@ -52,8 +52,8 @@ public interface DashboardService {
ListenableFuture<TimePageData<DashboardInfo>> findDashboardsByTenantIdAndCustomerId(TenantId tenantId, CustomerId customerId, TimePageLink pageLink); ListenableFuture<TimePageData<DashboardInfo>> findDashboardsByTenantIdAndCustomerId(TenantId tenantId, CustomerId customerId, TimePageLink pageLink);
void unassignCustomerDashboards(TenantId tenantId, CustomerId customerId); void unassignCustomerDashboards(CustomerId customerId);
void updateCustomerDashboards(TenantId tenantId, CustomerId customerId, String customerTitle); void updateCustomerDashboards(CustomerId customerId);
} }

75
dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardServiceImpl.java

@ -117,7 +117,7 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb
if (!customer.getTenantId().getId().equals(dashboard.getTenantId().getId())) { if (!customer.getTenantId().getId().equals(dashboard.getTenantId().getId())) {
throw new DataValidationException("Can't assign dashboard to customer from different tenant!"); throw new DataValidationException("Can't assign dashboard to customer from different tenant!");
} }
if (dashboard.addAssignedCustomer(customerId, customer.getTitle())) { if (dashboard.addAssignedCustomer(customer)) {
try { try {
createRelation(new EntityRelation(customerId, dashboardId, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.DASHBOARD)); createRelation(new EntityRelation(customerId, dashboardId, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.DASHBOARD));
} catch (ExecutionException | InterruptedException e) { } catch (ExecutionException | InterruptedException e) {
@ -133,7 +133,11 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb
@Override @Override
public Dashboard unassignDashboardFromCustomer(DashboardId dashboardId, CustomerId customerId) { public Dashboard unassignDashboardFromCustomer(DashboardId dashboardId, CustomerId customerId) {
Dashboard dashboard = findDashboardById(dashboardId); Dashboard dashboard = findDashboardById(dashboardId);
if (dashboard.removeAssignedCustomer(customerId)) { Customer customer = customerDao.findById(customerId.getId());
if (customer == null) {
throw new DataValidationException("Can't unassign dashboard from non-existent customer!");
}
if (dashboard.removeAssignedCustomer(customer)) {
try { try {
deleteRelation(new EntityRelation(customerId, dashboardId, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.DASHBOARD)); deleteRelation(new EntityRelation(customerId, dashboardId, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.DASHBOARD));
} catch (ExecutionException | InterruptedException e) { } catch (ExecutionException | InterruptedException e) {
@ -146,9 +150,9 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb
} }
} }
private Dashboard updateAssignedCustomerTitle(DashboardId dashboardId, CustomerId customerId, String customerTitle) { private Dashboard updateAssignedCustomer(DashboardId dashboardId, Customer customer) {
Dashboard dashboard = findDashboardById(dashboardId); Dashboard dashboard = findDashboardById(dashboardId);
if (dashboard.updateAssignedCustomer(customerId, customerTitle)) { if (dashboard.updateAssignedCustomer(customer)) {
return saveDashboard(dashboard); return saveDashboard(dashboard);
} else { } else {
return dashboard; return dashboard;
@ -207,20 +211,25 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb
} }
@Override @Override
public void unassignCustomerDashboards(TenantId tenantId, CustomerId customerId) { public void unassignCustomerDashboards(CustomerId customerId) {
log.trace("Executing unassignCustomerDashboards, tenantId [{}], customerId [{}]", tenantId, customerId); log.trace("Executing unassignCustomerDashboards, customerId [{}]", customerId);
Validator.validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
Validator.validateId(customerId, "Incorrect customerId " + customerId); Validator.validateId(customerId, "Incorrect customerId " + customerId);
new CustomerDashboardsUnassigner(tenantId, customerId).removeEntities(customerId); Customer customer = customerDao.findById(customerId.getId());
if (customer == null) {
throw new DataValidationException("Can't unassign dashboards from non-existent customer!");
}
new CustomerDashboardsUnassigner(customer).removeEntities(customer);
} }
@Override @Override
public void updateCustomerDashboards(TenantId tenantId, CustomerId customerId, String customerTitle) { public void updateCustomerDashboards(CustomerId customerId) {
log.trace("Executing updateCustomerDashboards, tenantId [{}], customerId [{}], customerTitle [{}]", tenantId, customerId, customerTitle); log.trace("Executing updateCustomerDashboards, customerId [{}]", customerId);
Validator.validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
Validator.validateId(customerId, "Incorrect customerId " + customerId); Validator.validateId(customerId, "Incorrect customerId " + customerId);
Validator.validateString(customerTitle, "Incorrect customerTitle " + customerTitle); Customer customer = customerDao.findById(customerId.getId());
new CustomerDashboardsUpdater(tenantId, customerId, customerTitle).removeEntities(customerId); if (customer == null) {
throw new DataValidationException("Can't update dashboards for non-existent customer!");
}
new CustomerDashboardsUpdater(customer).removeEntities(customer);
} }
private DataValidator<Dashboard> dashboardValidator = private DataValidator<Dashboard> dashboardValidator =
@ -255,58 +264,52 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb
} }
}; };
private class CustomerDashboardsUnassigner extends TimePaginatedRemover<CustomerId, DashboardInfo> { private class CustomerDashboardsUnassigner extends TimePaginatedRemover<Customer, DashboardInfo> {
private TenantId tenantId;
private CustomerId customerId;
CustomerDashboardsUnassigner(TenantId tenantId, CustomerId customerId) { private Customer customer;
this.tenantId = tenantId;
this.customerId = customerId; CustomerDashboardsUnassigner(Customer customer) {
this.customer = customer;
} }
@Override @Override
protected List<DashboardInfo> findEntities(CustomerId id, TimePageLink pageLink) { protected List<DashboardInfo> findEntities(Customer customer, TimePageLink pageLink) {
try { try {
return dashboardInfoDao.findDashboardsByTenantIdAndCustomerId(tenantId.getId(), id.getId(), pageLink).get(); return dashboardInfoDao.findDashboardsByTenantIdAndCustomerId(customer.getTenantId().getId(), customer.getId().getId(), pageLink).get();
} catch (InterruptedException | ExecutionException e) { } catch (InterruptedException | ExecutionException e) {
log.warn("Failed to get dashboards by tenantId [{}] and customerId [{}].", tenantId, id); log.warn("Failed to get dashboards by tenantId [{}] and customerId [{}].", customer.getTenantId().getId(), customer.getId().getId());
throw new RuntimeException(e); throw new RuntimeException(e);
} }
} }
@Override @Override
protected void removeEntity(DashboardInfo entity) { protected void removeEntity(DashboardInfo entity) {
unassignDashboardFromCustomer(new DashboardId(entity.getUuidId()), this.customerId); unassignDashboardFromCustomer(new DashboardId(entity.getUuidId()), this.customer.getId());
} }
} }
private class CustomerDashboardsUpdater extends TimePaginatedRemover<CustomerId, DashboardInfo> { private class CustomerDashboardsUpdater extends TimePaginatedRemover<Customer, DashboardInfo> {
private TenantId tenantId; private Customer customer;
private CustomerId customerId;
private String customerTitle;
CustomerDashboardsUpdater(TenantId tenantId, CustomerId customerId, String customerTitle) { CustomerDashboardsUpdater(Customer customer) {
this.tenantId = tenantId; this.customer = customer;
this.customerId = customerId;
this.customerTitle = customerTitle;
} }
@Override @Override
protected List<DashboardInfo> findEntities(CustomerId id, TimePageLink pageLink) { protected List<DashboardInfo> findEntities(Customer customer, TimePageLink pageLink) {
try { try {
return dashboardInfoDao.findDashboardsByTenantIdAndCustomerId(tenantId.getId(), id.getId(), pageLink).get(); return dashboardInfoDao.findDashboardsByTenantIdAndCustomerId(customer.getTenantId().getId(), customer.getId().getId(), pageLink).get();
} catch (InterruptedException | ExecutionException e) { } catch (InterruptedException | ExecutionException e) {
log.warn("Failed to get dashboards by tenantId [{}] and customerId [{}].", tenantId, id); log.warn("Failed to get dashboards by tenantId [{}] and customerId [{}].", customer.getTenantId().getId(), customer.getId().getId());
throw new RuntimeException(e); throw new RuntimeException(e);
} }
} }
@Override @Override
protected void removeEntity(DashboardInfo entity) { protected void removeEntity(DashboardInfo entity) {
updateAssignedCustomerTitle(new DashboardId(entity.getUuidId()), this.customerId, this.customerTitle); updateAssignedCustomer(new DashboardId(entity.getUuidId()), this.customer);
} }
} }

28
dao/src/main/java/org/thingsboard/server/dao/model/nosql/DashboardEntity.java

@ -20,18 +20,22 @@ import com.datastax.driver.mapping.annotations.Column;
import com.datastax.driver.mapping.annotations.PartitionKey; import com.datastax.driver.mapping.annotations.PartitionKey;
import com.datastax.driver.mapping.annotations.Table; import com.datastax.driver.mapping.annotations.Table;
import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.ToString; import lombok.ToString;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;
import org.thingsboard.server.common.data.Dashboard; import org.thingsboard.server.common.data.Dashboard;
import org.thingsboard.server.common.data.ShortCustomerInfo;
import org.thingsboard.server.common.data.id.DashboardId; import org.thingsboard.server.common.data.id.DashboardId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.dao.model.SearchTextEntity; import org.thingsboard.server.dao.model.SearchTextEntity;
import org.thingsboard.server.dao.model.type.JsonCodec; import org.thingsboard.server.dao.model.type.JsonCodec;
import java.util.HashMap; import java.io.IOException;
import java.util.HashSet;
import java.util.UUID; import java.util.UUID;
import static org.thingsboard.server.dao.model.ModelConstants.*; import static org.thingsboard.server.dao.model.ModelConstants.*;
@ -43,6 +47,8 @@ import static org.thingsboard.server.dao.model.ModelConstants.*;
public final class DashboardEntity implements SearchTextEntity<Dashboard> { public final class DashboardEntity implements SearchTextEntity<Dashboard> {
private static final ObjectMapper objectMapper = new ObjectMapper(); private static final ObjectMapper objectMapper = new ObjectMapper();
private static final JavaType assignedCustomersType =
objectMapper.getTypeFactory().constructCollectionType(HashSet.class, ShortCustomerInfo.class);
@PartitionKey(value = 0) @PartitionKey(value = 0)
@Column(name = ID_PROPERTY) @Column(name = ID_PROPERTY)
@ -58,8 +64,8 @@ public final class DashboardEntity implements SearchTextEntity<Dashboard> {
@Column(name = SEARCH_TEXT_PROPERTY) @Column(name = SEARCH_TEXT_PROPERTY)
private String searchText; private String searchText;
@Column(name = DASHBOARD_ASSIGNED_CUSTOMERS_PROPERTY, codec = JsonCodec.class) @Column(name = DASHBOARD_ASSIGNED_CUSTOMERS_PROPERTY)
private JsonNode assignedCustomers; private String assignedCustomers;
@Column(name = DASHBOARD_CONFIGURATION_PROPERTY, codec = JsonCodec.class) @Column(name = DASHBOARD_CONFIGURATION_PROPERTY, codec = JsonCodec.class)
private JsonNode configuration; private JsonNode configuration;
@ -77,7 +83,11 @@ public final class DashboardEntity implements SearchTextEntity<Dashboard> {
} }
this.title = dashboard.getTitle(); this.title = dashboard.getTitle();
if (dashboard.getAssignedCustomers() != null) { if (dashboard.getAssignedCustomers() != null) {
this.assignedCustomers = objectMapper.valueToTree(dashboard.getAssignedCustomers()); try {
this.assignedCustomers = objectMapper.writeValueAsString(dashboard.getAssignedCustomers());
} catch (JsonProcessingException e) {
log.error("Unable to serialize assigned customers to string!", e);
}
} }
this.configuration = dashboard.getConfiguration(); this.configuration = dashboard.getConfiguration();
} }
@ -106,11 +116,11 @@ public final class DashboardEntity implements SearchTextEntity<Dashboard> {
this.title = title; this.title = title;
} }
public JsonNode getAssignedCustomers() { public String getAssignedCustomers() {
return assignedCustomers; return assignedCustomers;
} }
public void setAssignedCustomers(JsonNode assignedCustomers) { public void setAssignedCustomers(String assignedCustomers) {
this.assignedCustomers = assignedCustomers; this.assignedCustomers = assignedCustomers;
} }
@ -144,10 +154,10 @@ public final class DashboardEntity implements SearchTextEntity<Dashboard> {
dashboard.setTenantId(new TenantId(tenantId)); dashboard.setTenantId(new TenantId(tenantId));
} }
dashboard.setTitle(title); dashboard.setTitle(title);
if (assignedCustomers != null) { if (!StringUtils.isEmpty(assignedCustomers)) {
try { try {
dashboard.setAssignedCustomers(objectMapper.treeToValue(assignedCustomers, HashMap.class)); dashboard.setAssignedCustomers(objectMapper.readValue(assignedCustomers, assignedCustomersType));
} catch (JsonProcessingException e) { } catch (IOException e) {
log.warn("Unable to parse assigned customers!", e); log.warn("Unable to parse assigned customers!", e);
} }
} }

32
dao/src/main/java/org/thingsboard/server/dao/model/nosql/DashboardInfoEntity.java

@ -20,20 +20,20 @@ import com.datastax.driver.mapping.annotations.Column;
import com.datastax.driver.mapping.annotations.PartitionKey; import com.datastax.driver.mapping.annotations.PartitionKey;
import com.datastax.driver.mapping.annotations.Table; import com.datastax.driver.mapping.annotations.Table;
import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.ToString; import lombok.ToString;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;
import org.thingsboard.server.common.data.DashboardInfo; import org.thingsboard.server.common.data.DashboardInfo;
import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.ShortCustomerInfo;
import org.thingsboard.server.common.data.id.DashboardId; import org.thingsboard.server.common.data.id.DashboardId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.dao.model.SearchTextEntity; import org.thingsboard.server.dao.model.SearchTextEntity;
import org.thingsboard.server.dao.model.type.JsonCodec;
import java.util.HashMap; import java.io.IOException;
import java.util.Set; import java.util.HashSet;
import java.util.UUID; import java.util.UUID;
import static org.thingsboard.server.dao.model.ModelConstants.*; import static org.thingsboard.server.dao.model.ModelConstants.*;
@ -45,6 +45,8 @@ import static org.thingsboard.server.dao.model.ModelConstants.*;
public class DashboardInfoEntity implements SearchTextEntity<DashboardInfo> { public class DashboardInfoEntity implements SearchTextEntity<DashboardInfo> {
private static final ObjectMapper objectMapper = new ObjectMapper(); private static final ObjectMapper objectMapper = new ObjectMapper();
private static final JavaType assignedCustomersType =
objectMapper.getTypeFactory().constructCollectionType(HashSet.class, ShortCustomerInfo.class);
@PartitionKey(value = 0) @PartitionKey(value = 0)
@Column(name = ID_PROPERTY) @Column(name = ID_PROPERTY)
@ -60,8 +62,8 @@ public class DashboardInfoEntity implements SearchTextEntity<DashboardInfo> {
@Column(name = SEARCH_TEXT_PROPERTY) @Column(name = SEARCH_TEXT_PROPERTY)
private String searchText; private String searchText;
@Column(name = DASHBOARD_ASSIGNED_CUSTOMERS_PROPERTY, codec = JsonCodec.class) @Column(name = DASHBOARD_ASSIGNED_CUSTOMERS_PROPERTY)
private JsonNode assignedCustomers; private String assignedCustomers;
public DashboardInfoEntity() { public DashboardInfoEntity() {
super(); super();
@ -76,7 +78,11 @@ public class DashboardInfoEntity implements SearchTextEntity<DashboardInfo> {
} }
this.title = dashboardInfo.getTitle(); this.title = dashboardInfo.getTitle();
if (dashboardInfo.getAssignedCustomers() != null) { if (dashboardInfo.getAssignedCustomers() != null) {
this.assignedCustomers = objectMapper.valueToTree(dashboardInfo.getAssignedCustomers()); try {
this.assignedCustomers = objectMapper.writeValueAsString(dashboardInfo.getAssignedCustomers());
} catch (JsonProcessingException e) {
log.error("Unable to serialize assigned customers to string!", e);
}
} }
} }
@ -104,11 +110,11 @@ public class DashboardInfoEntity implements SearchTextEntity<DashboardInfo> {
this.title = title; this.title = title;
} }
public JsonNode getAssignedCustomers() { public String getAssignedCustomers() {
return assignedCustomers; return assignedCustomers;
} }
public void setAssignedCustomers(JsonNode assignedCustomers) { public void setAssignedCustomers(String assignedCustomers) {
this.assignedCustomers = assignedCustomers; this.assignedCustomers = assignedCustomers;
} }
@ -134,10 +140,10 @@ public class DashboardInfoEntity implements SearchTextEntity<DashboardInfo> {
dashboardInfo.setTenantId(new TenantId(tenantId)); dashboardInfo.setTenantId(new TenantId(tenantId));
} }
dashboardInfo.setTitle(title); dashboardInfo.setTitle(title);
if (assignedCustomers != null) { if (!StringUtils.isEmpty(assignedCustomers)) {
try { try {
dashboardInfo.setAssignedCustomers(objectMapper.treeToValue(assignedCustomers, HashMap.class)); dashboardInfo.setAssignedCustomers(objectMapper.readValue(assignedCustomers, assignedCustomersType));
} catch (JsonProcessingException e) { } catch (IOException e) {
log.warn("Unable to parse assigned customers!", e); log.warn("Unable to parse assigned customers!", e);
} }
} }

26
dao/src/main/java/org/thingsboard/server/dao/model/sql/DashboardEntity.java

@ -17,6 +17,7 @@ package org.thingsboard.server.dao.model.sql;
import com.datastax.driver.core.utils.UUIDs; import com.datastax.driver.core.utils.UUIDs;
import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Data; import lombok.Data;
@ -24,8 +25,9 @@ import lombok.EqualsAndHashCode;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.hibernate.annotations.Type; import org.hibernate.annotations.Type;
import org.hibernate.annotations.TypeDef; import org.hibernate.annotations.TypeDef;
import org.springframework.util.StringUtils;
import org.thingsboard.server.common.data.Dashboard; import org.thingsboard.server.common.data.Dashboard;
import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.ShortCustomerInfo;
import org.thingsboard.server.common.data.id.DashboardId; import org.thingsboard.server.common.data.id.DashboardId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.dao.model.BaseSqlEntity; import org.thingsboard.server.dao.model.BaseSqlEntity;
@ -36,9 +38,8 @@ import org.thingsboard.server.dao.util.mapping.JsonStringType;
import javax.persistence.Column; import javax.persistence.Column;
import javax.persistence.Entity; import javax.persistence.Entity;
import javax.persistence.Table; import javax.persistence.Table;
import java.util.HashMap; import java.io.IOException;
import java.util.List; import java.util.HashSet;
import java.util.Set;
@Data @Data
@Slf4j @Slf4j
@ -49,6 +50,8 @@ import java.util.Set;
public final class DashboardEntity extends BaseSqlEntity<Dashboard> implements SearchTextEntity<Dashboard> { public final class DashboardEntity extends BaseSqlEntity<Dashboard> implements SearchTextEntity<Dashboard> {
private static final ObjectMapper objectMapper = new ObjectMapper(); private static final ObjectMapper objectMapper = new ObjectMapper();
private static final JavaType assignedCustomersType =
objectMapper.getTypeFactory().constructCollectionType(HashSet.class, ShortCustomerInfo.class);
@Column(name = ModelConstants.DASHBOARD_TENANT_ID_PROPERTY) @Column(name = ModelConstants.DASHBOARD_TENANT_ID_PROPERTY)
private String tenantId; private String tenantId;
@ -59,9 +62,8 @@ public final class DashboardEntity extends BaseSqlEntity<Dashboard> implements S
@Column(name = ModelConstants.SEARCH_TEXT_PROPERTY) @Column(name = ModelConstants.SEARCH_TEXT_PROPERTY)
private String searchText; private String searchText;
@Type(type = "json")
@Column(name = ModelConstants.DASHBOARD_ASSIGNED_CUSTOMERS_PROPERTY) @Column(name = ModelConstants.DASHBOARD_ASSIGNED_CUSTOMERS_PROPERTY)
private JsonNode assignedCustomers; private String assignedCustomers;
@Type(type = "json") @Type(type = "json")
@Column(name = ModelConstants.DASHBOARD_CONFIGURATION_PROPERTY) @Column(name = ModelConstants.DASHBOARD_CONFIGURATION_PROPERTY)
@ -80,7 +82,11 @@ public final class DashboardEntity extends BaseSqlEntity<Dashboard> implements S
} }
this.title = dashboard.getTitle(); this.title = dashboard.getTitle();
if (dashboard.getAssignedCustomers() != null) { if (dashboard.getAssignedCustomers() != null) {
this.assignedCustomers = objectMapper.valueToTree(dashboard.getAssignedCustomers()); try {
this.assignedCustomers = objectMapper.writeValueAsString(dashboard.getAssignedCustomers());
} catch (JsonProcessingException e) {
log.error("Unable to serialize assigned customers to string!", e);
}
} }
this.configuration = dashboard.getConfiguration(); this.configuration = dashboard.getConfiguration();
} }
@ -103,10 +109,10 @@ public final class DashboardEntity extends BaseSqlEntity<Dashboard> implements S
dashboard.setTenantId(new TenantId(toUUID(tenantId))); dashboard.setTenantId(new TenantId(toUUID(tenantId)));
} }
dashboard.setTitle(title); dashboard.setTitle(title);
if (assignedCustomers != null) { if (!StringUtils.isEmpty(assignedCustomers)) {
try { try {
dashboard.setAssignedCustomers(objectMapper.treeToValue(assignedCustomers, HashMap.class)); dashboard.setAssignedCustomers(objectMapper.readValue(assignedCustomers, assignedCustomersType));
} catch (JsonProcessingException e) { } catch (IOException e) {
log.warn("Unable to parse assigned customers!", e); log.warn("Unable to parse assigned customers!", e);
} }
} }

27
dao/src/main/java/org/thingsboard/server/dao/model/sql/DashboardInfoEntity.java

@ -17,14 +17,14 @@ package org.thingsboard.server.dao.model.sql;
import com.datastax.driver.core.utils.UUIDs; import com.datastax.driver.core.utils.UUIDs;
import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.hibernate.annotations.Type; import org.springframework.util.StringUtils;
import org.thingsboard.server.common.data.DashboardInfo; import org.thingsboard.server.common.data.DashboardInfo;
import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.ShortCustomerInfo;
import org.thingsboard.server.common.data.id.DashboardId; import org.thingsboard.server.common.data.id.DashboardId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.dao.model.BaseSqlEntity; import org.thingsboard.server.dao.model.BaseSqlEntity;
@ -34,8 +34,8 @@ import org.thingsboard.server.dao.model.SearchTextEntity;
import javax.persistence.Column; import javax.persistence.Column;
import javax.persistence.Entity; import javax.persistence.Entity;
import javax.persistence.Table; import javax.persistence.Table;
import java.util.HashMap; import java.io.IOException;
import java.util.Set; import java.util.HashSet;
@Data @Data
@Slf4j @Slf4j
@ -45,6 +45,8 @@ import java.util.Set;
public class DashboardInfoEntity extends BaseSqlEntity<DashboardInfo> implements SearchTextEntity<DashboardInfo> { public class DashboardInfoEntity extends BaseSqlEntity<DashboardInfo> implements SearchTextEntity<DashboardInfo> {
private static final ObjectMapper objectMapper = new ObjectMapper(); private static final ObjectMapper objectMapper = new ObjectMapper();
private static final JavaType assignedCustomersType =
objectMapper.getTypeFactory().constructCollectionType(HashSet.class, ShortCustomerInfo.class);
@Column(name = ModelConstants.DASHBOARD_TENANT_ID_PROPERTY) @Column(name = ModelConstants.DASHBOARD_TENANT_ID_PROPERTY)
private String tenantId; private String tenantId;
@ -55,9 +57,8 @@ public class DashboardInfoEntity extends BaseSqlEntity<DashboardInfo> implements
@Column(name = ModelConstants.SEARCH_TEXT_PROPERTY) @Column(name = ModelConstants.SEARCH_TEXT_PROPERTY)
private String searchText; private String searchText;
@Type(type = "json")
@Column(name = ModelConstants.DASHBOARD_ASSIGNED_CUSTOMERS_PROPERTY) @Column(name = ModelConstants.DASHBOARD_ASSIGNED_CUSTOMERS_PROPERTY)
private JsonNode assignedCustomers; private String assignedCustomers;
public DashboardInfoEntity() { public DashboardInfoEntity() {
super(); super();
@ -72,7 +73,11 @@ public class DashboardInfoEntity extends BaseSqlEntity<DashboardInfo> implements
} }
this.title = dashboardInfo.getTitle(); this.title = dashboardInfo.getTitle();
if (dashboardInfo.getAssignedCustomers() != null) { if (dashboardInfo.getAssignedCustomers() != null) {
this.assignedCustomers = objectMapper.valueToTree(dashboardInfo.getAssignedCustomers()); try {
this.assignedCustomers = objectMapper.writeValueAsString(dashboardInfo.getAssignedCustomers());
} catch (JsonProcessingException e) {
log.error("Unable to serialize assigned customers to string!", e);
}
} }
} }
@ -98,10 +103,10 @@ public class DashboardInfoEntity extends BaseSqlEntity<DashboardInfo> implements
dashboardInfo.setTenantId(new TenantId(toUUID(tenantId))); dashboardInfo.setTenantId(new TenantId(toUUID(tenantId)));
} }
dashboardInfo.setTitle(title); dashboardInfo.setTitle(title);
if (assignedCustomers != null) { if (!StringUtils.isEmpty(assignedCustomers)) {
try { try {
dashboardInfo.setAssignedCustomers(objectMapper.treeToValue(assignedCustomers, HashMap.class)); dashboardInfo.setAssignedCustomers(objectMapper.readValue(assignedCustomers, assignedCustomersType));
} catch (JsonProcessingException e) { } catch (IOException e) {
log.warn("Unable to parse assigned customers!", e); log.warn("Unable to parse assigned customers!", e);
} }
} }

2
dao/src/test/java/org/thingsboard/server/dao/service/BaseDashboardServiceTest.java

@ -320,7 +320,7 @@ public abstract class BaseDashboardServiceTest extends AbstractServiceTest {
Assert.assertEquals(dashboards, loadedDashboards); Assert.assertEquals(dashboards, loadedDashboards);
dashboardService.unassignCustomerDashboards(tenantId, customerId); dashboardService.unassignCustomerDashboards(customerId);
pageLink = new TimePageLink(42); pageLink = new TimePageLink(42);
pageData = dashboardService.findDashboardsByTenantIdAndCustomerId(tenantId, customerId, pageLink).get(); pageData = dashboardService.findDashboardsByTenantIdAndCustomerId(tenantId, customerId, pageLink).get();

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

@ -17,7 +17,7 @@ export default angular.module('thingsboard.api.dashboard', [])
.factory('dashboardService', DashboardService).name; .factory('dashboardService', DashboardService).name;
/*@ngInject*/ /*@ngInject*/
function DashboardService($rootScope, $http, $q, $location, customerService) { function DashboardService($rootScope, $http, $q, $location, $filter) {
var stDiffPromise; var stDiffPromise;
@ -37,7 +37,11 @@ function DashboardService($rootScope, $http, $q, $location, customerService) {
deleteDashboard: deleteDashboard, deleteDashboard: deleteDashboard,
saveDashboard: saveDashboard, saveDashboard: saveDashboard,
unassignDashboardFromCustomer: unassignDashboardFromCustomer, unassignDashboardFromCustomer: unassignDashboardFromCustomer,
updateDashboardCustomers: updateDashboardCustomers,
addDashboardCustomers: addDashboardCustomers,
removeDashboardCustomers: removeDashboardCustomers,
makeDashboardPublic: makeDashboardPublic, makeDashboardPublic: makeDashboardPublic,
makeDashboardPrivate: makeDashboardPrivate,
getPublicDashboardLink: getPublicDashboardLink getPublicDashboardLink: getPublicDashboardLink
} }
@ -56,14 +60,14 @@ function DashboardService($rootScope, $http, $q, $location, customerService) {
url += '&textOffset=' + pageLink.textOffset; url += '&textOffset=' + pageLink.textOffset;
} }
$http.get(url, config).then(function success(response) { $http.get(url, config).then(function success(response) {
deferred.resolve(response.data); deferred.resolve(prepareDashboards(response.data));
}, function fail() { }, function fail() {
deferred.reject(); deferred.reject();
}); });
return deferred.promise; return deferred.promise;
} }
function getTenantDashboards(pageLink, applyCustomersInfo, config) { function getTenantDashboards(pageLink, config) {
var deferred = $q.defer(); var deferred = $q.defer();
var url = '/api/tenant/dashboards?limit=' + pageLink.limit; var url = '/api/tenant/dashboards?limit=' + pageLink.limit;
if (angular.isDefined(pageLink.textSearch)) { if (angular.isDefined(pageLink.textSearch)) {
@ -76,51 +80,25 @@ function DashboardService($rootScope, $http, $q, $location, customerService) {
url += '&textOffset=' + pageLink.textOffset; url += '&textOffset=' + pageLink.textOffset;
} }
$http.get(url, config).then(function success(response) { $http.get(url, config).then(function success(response) {
if (applyCustomersInfo) { deferred.resolve(prepareDashboards(response.data));
customerService.applyAssignedCustomersInfo(response.data.data).then(
function success(data) {
response.data.data = data;
deferred.resolve(response.data);
},
function fail() {
deferred.reject();
}
);
} else {
deferred.resolve(response.data);
}
}, function fail() { }, function fail() {
deferred.reject(); deferred.reject();
}); });
return deferred.promise; return deferred.promise;
} }
function getCustomerDashboards(customerId, pageLink, applyCustomersInfo, config) { function getCustomerDashboards(customerId, pageLink, config) {
var deferred = $q.defer(); var deferred = $q.defer();
var url = '/api/customer/' + customerId + '/dashboards?limit=' + pageLink.limit; var url = '/api/customer/' + customerId + '/dashboards?limit=' + pageLink.limit;
if (angular.isDefined(pageLink.textSearch)) {
url += '&textSearch=' + pageLink.textSearch;
}
if (angular.isDefined(pageLink.idOffset)) { if (angular.isDefined(pageLink.idOffset)) {
url += '&idOffset=' + pageLink.idOffset; url += '&offset=' + pageLink.idOffset;
}
if (angular.isDefined(pageLink.textOffset)) {
url += '&textOffset=' + pageLink.textOffset;
} }
$http.get(url, config).then(function success(response) { $http.get(url, config).then(function success(response) {
if (applyCustomersInfo) { response.data = prepareDashboards(response.data);
customerService.applyAssignedCustomerInfo(response.data.data, customerId).then( if (pageLink.textSearch) {
function success(data) { response.data.data = $filter('filter')(response.data.data, {title: pageLink.textSearch});
response.data.data = data;
deferred.resolve(response.data);
},
function fail() {
deferred.reject();
}
);
} else {
deferred.resolve(response.data);
} }
deferred.resolve(response.data);
}, function fail() { }, function fail() {
deferred.reject(); deferred.reject();
}); });
@ -151,7 +129,7 @@ function DashboardService($rootScope, $http, $q, $location, customerService) {
var deferred = $q.defer(); var deferred = $q.defer();
var url = '/api/dashboard/' + dashboardId; var url = '/api/dashboard/' + dashboardId;
$http.get(url, null).then(function success(response) { $http.get(url, null).then(function success(response) {
deferred.resolve(response.data); deferred.resolve(prepareDashboard(response.data));
}, function fail() { }, function fail() {
deferred.reject(); deferred.reject();
}); });
@ -162,7 +140,7 @@ function DashboardService($rootScope, $http, $q, $location, customerService) {
var deferred = $q.defer(); var deferred = $q.defer();
var url = '/api/dashboard/info/' + dashboardId; var url = '/api/dashboard/info/' + dashboardId;
$http.get(url, config).then(function success(response) { $http.get(url, config).then(function success(response) {
deferred.resolve(response.data); deferred.resolve(prepareDashboard(response.data));
}, function fail() { }, function fail() {
deferred.reject(); deferred.reject();
}); });
@ -172,8 +150,8 @@ function DashboardService($rootScope, $http, $q, $location, customerService) {
function saveDashboard(dashboard) { function saveDashboard(dashboard) {
var deferred = $q.defer(); var deferred = $q.defer();
var url = '/api/dashboard'; var url = '/api/dashboard';
$http.post(url, dashboard).then(function success(response) { $http.post(url, cleanDashboard(dashboard)).then(function success(response) {
deferred.resolve(response.data); deferred.resolve(prepareDashboard(response.data));
}, function fail() { }, function fail() {
deferred.reject(); deferred.reject();
}); });
@ -195,18 +173,51 @@ function DashboardService($rootScope, $http, $q, $location, customerService) {
var deferred = $q.defer(); var deferred = $q.defer();
var url = '/api/customer/' + customerId + '/dashboard/' + dashboardId; var url = '/api/customer/' + customerId + '/dashboard/' + dashboardId;
$http.post(url, null).then(function success(response) { $http.post(url, null).then(function success(response) {
deferred.resolve(response.data); deferred.resolve(prepareDashboard(response.data));
}, function fail() { }, function fail() {
deferred.reject(); deferred.reject();
}); });
return deferred.promise; return deferred.promise;
} }
function unassignDashboardFromCustomer(dashboardId) { function unassignDashboardFromCustomer(customerId, dashboardId) {
var deferred = $q.defer(); var deferred = $q.defer();
var url = '/api/customer/dashboard/' + dashboardId; var url = '/api/customer/' + customerId + '/dashboard/' + dashboardId;
$http.delete(url).then(function success(response) { $http.delete(url).then(function success(response) {
deferred.resolve(response.data); deferred.resolve(prepareDashboard(response.data));
}, function fail() {
deferred.reject();
});
return deferred.promise;
}
function updateDashboardCustomers(dashboardId, customerIds) {
var deferred = $q.defer();
var url = '/api/dashboard/' + dashboardId + '/customers';
$http.post(url, customerIds).then(function success(response) {
deferred.resolve(prepareDashboard(response.data));
}, function fail() {
deferred.reject();
});
return deferred.promise;
}
function addDashboardCustomers(dashboardId, customerIds) {
var deferred = $q.defer();
var url = '/api/dashboard/' + dashboardId + '/customers/add';
$http.post(url, customerIds).then(function success(response) {
deferred.resolve(prepareDashboard(response.data));
}, function fail() {
deferred.reject();
});
return deferred.promise;
}
function removeDashboardCustomers(dashboardId, customerIds) {
var deferred = $q.defer();
var url = '/api/dashboard/' + dashboardId + '/customers/remove';
$http.post(url, customerIds).then(function success(response) {
deferred.resolve(prepareDashboard(response.data));
}, function fail() { }, function fail() {
deferred.reject(); deferred.reject();
}); });
@ -217,7 +228,18 @@ function DashboardService($rootScope, $http, $q, $location, customerService) {
var deferred = $q.defer(); var deferred = $q.defer();
var url = '/api/customer/public/dashboard/' + dashboardId; var url = '/api/customer/public/dashboard/' + dashboardId;
$http.post(url, null).then(function success(response) { $http.post(url, null).then(function success(response) {
deferred.resolve(response.data); deferred.resolve(prepareDashboard(response.data));
}, function fail() {
deferred.reject();
});
return deferred.promise;
}
function makeDashboardPrivate(dashboardId) {
var deferred = $q.defer();
var url = '/api/customer/public/dashboard/' + dashboardId;
$http.delete(url).then(function success(response) {
deferred.resolve(prepareDashboard(response.data));
}, function fail() { }, function fail() {
deferred.reject(); deferred.reject();
}); });
@ -230,8 +252,44 @@ function DashboardService($rootScope, $http, $q, $location, customerService) {
if (port != 80 && port != 443) { if (port != 80 && port != 443) {
url += ":" + port; url += ":" + port;
} }
url += "/dashboards/" + dashboard.id.id + "?publicId=" + dashboard.customerId.id; url += "/dashboards/" + dashboard.id.id + "?publicId=" + dashboard.publicCustomerId;
return url; return url;
} }
function prepareDashboards(dashboardsData) {
if (dashboardsData.data) {
for (var i = 0; i < dashboardsData.data.length; i++) {
dashboardsData.data[i] = prepareDashboard(dashboardsData.data[i]);
}
}
return dashboardsData;
}
function prepareDashboard(dashboard) {
dashboard.publicCustomerId = null;
dashboard.assignedCustomersText = "";
dashboard.assignedCustomersIds = [];
if (dashboard.assignedCustomers && dashboard.assignedCustomers.length) {
var assignedCustomersTitles = [];
for (var i = 0; i < dashboard.assignedCustomers.length; i++) {
var assignedCustomer = dashboard.assignedCustomers[i];
dashboard.assignedCustomersIds.push(assignedCustomer.customerId.id);
if (assignedCustomer.public) {
dashboard.publicCustomerId = assignedCustomer.customerId.id;
} else {
assignedCustomersTitles.push(assignedCustomer.title);
}
}
dashboard.assignedCustomersText = assignedCustomersTitles.join(', ');
}
return dashboard;
}
function cleanDashboard(dashboard) {
delete dashboard.publicCustomerId;
delete dashboard.assignedCustomersText;
delete dashboard.assignedCustomersIds;
return dashboard;
}
} }

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

@ -273,9 +273,9 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
break; break;
case types.entityType.dashboard: case types.entityType.dashboard:
if (user.authority === 'CUSTOMER_USER') { if (user.authority === 'CUSTOMER_USER') {
promise = dashboardService.getCustomerDashboards(customerId, pageLink, false, config); promise = dashboardService.getCustomerDashboards(customerId, pageLink, config);
} else { } else {
promise = dashboardService.getTenantDashboards(pageLink, false, config); promise = dashboardService.getTenantDashboards(pageLink, config);
} }
break; break;
case types.entityType.user: case types.entityType.user:
@ -403,6 +403,21 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
return deferred.promise; return deferred.promise;
} }
function resolveAliasEntityId(entityType, id) {
var entityId = {
entityType: entityType,
id: id
};
if (entityType == types.aliasEntityType.current_customer) {
var user = userService.getCurrentUser();
entityId.entityType = types.entityType.customer;
if (user.authority === 'CUSTOMER_USER') {
entityId.id = user.customerId;
}
}
return entityId;
}
function getStateEntityId(filter, stateParams) { function getStateEntityId(filter, stateParams) {
var entityId = null; var entityId = null;
if (stateParams) { if (stateParams) {
@ -417,6 +432,9 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
if (!entityId) { if (!entityId) {
entityId = filter.defaultStateEntity; entityId = filter.defaultStateEntity;
} }
if (entityId) {
entityId = resolveAliasEntityId(entityId.entityType, entityId.id);
}
return entityId; return entityId;
} }
@ -432,7 +450,8 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
var stateEntityId = getStateEntityId(filter, stateParams); var stateEntityId = getStateEntityId(filter, stateParams);
switch (filter.type) { switch (filter.type) {
case types.aliasFilterType.singleEntity.value: case types.aliasFilterType.singleEntity.value:
getEntity(filter.singleEntity.entityType, filter.singleEntity.id, {ignoreLoading: true}).then( var aliasEntityId = resolveAliasEntityId(filter.singleEntity.entityType, filter.singleEntity.id);
getEntity(aliasEntityId.entityType, aliasEntityId.id, {ignoreLoading: true}).then(
function success(entity) { function success(entity) {
result.entities = entitiesToEntitiesInfo([entity]); result.entities = entitiesToEntitiesInfo([entity]);
deferred.resolve(result); deferred.resolve(result);
@ -530,10 +549,11 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
rootEntityId = filter.rootEntity.id; rootEntityId = filter.rootEntity.id;
} }
if (rootEntityType && rootEntityId) { if (rootEntityType && rootEntityId) {
var relationQueryRootEntityId = resolveAliasEntityId(rootEntityType, rootEntityId);
var searchQuery = { var searchQuery = {
parameters: { parameters: {
rootId: rootEntityId, rootId: relationQueryRootEntityId.id,
rootType: rootEntityType, rootType: relationQueryRootEntityId.entityType,
direction: filter.direction direction: filter.direction
}, },
filters: filter.filters filters: filter.filters
@ -571,10 +591,11 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
rootEntityId = filter.rootEntity.id; rootEntityId = filter.rootEntity.id;
} }
if (rootEntityType && rootEntityId) { if (rootEntityType && rootEntityId) {
var searchQueryRootEntityId = resolveAliasEntityId(rootEntityType, rootEntityId);
searchQuery = { searchQuery = {
parameters: { parameters: {
rootId: rootEntityId, rootId: searchQueryRootEntityId.id,
rootType: rootEntityType, rootType: searchQueryRootEntityId.entityType,
direction: filter.direction direction: filter.direction
}, },
relationType: filter.relationType relationType: filter.relationType
@ -709,7 +730,7 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
return result; return result;
} }
function prepareAllowedEntityTypesList(allowedEntityTypes) { function prepareAllowedEntityTypesList(allowedEntityTypes, useAliasEntityTypes) {
var authority = userService.getAuthority(); var authority = userService.getAuthority();
var entityTypes = {}; var entityTypes = {};
switch(authority) { switch(authority) {
@ -726,12 +747,18 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
entityTypes.rule = types.entityType.rule; entityTypes.rule = types.entityType.rule;
entityTypes.plugin = types.entityType.plugin; entityTypes.plugin = types.entityType.plugin;
entityTypes.dashboard = types.entityType.dashboard; entityTypes.dashboard = types.entityType.dashboard;
if (useAliasEntityTypes) {
entityTypes.current_customer = types.aliasEntityType.current_customer;
}
break; break;
case 'CUSTOMER_USER': case 'CUSTOMER_USER':
entityTypes.device = types.entityType.device; entityTypes.device = types.entityType.device;
entityTypes.asset = types.entityType.asset; entityTypes.asset = types.entityType.asset;
entityTypes.customer = types.entityType.customer; entityTypes.customer = types.entityType.customer;
entityTypes.dashboard = types.entityType.dashboard; entityTypes.dashboard = types.entityType.dashboard;
if (useAliasEntityTypes) {
entityTypes.current_customer = types.aliasEntityType.current_customer;
}
break; break;
} }

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

@ -266,9 +266,9 @@ function UserService($http, $q, $rootScope, adminService, dashboardService, logi
var pageLink = {limit: 100}; var pageLink = {limit: 100};
var fetchDashboardsPromise; var fetchDashboardsPromise;
if (currentUser.authority === 'TENANT_ADMIN') { if (currentUser.authority === 'TENANT_ADMIN') {
fetchDashboardsPromise = dashboardService.getTenantDashboards(pageLink, false); fetchDashboardsPromise = dashboardService.getTenantDashboards(pageLink);
} else { } else {
fetchDashboardsPromise = dashboardService.getCustomerDashboards(currentUser.customerId, pageLink, false); fetchDashboardsPromise = dashboardService.getCustomerDashboards(currentUser.customerId, pageLink);
} }
fetchDashboardsPromise.then( fetchDashboardsPromise.then(
function success(result) { function success(result) {

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

@ -296,6 +296,9 @@ export default angular.module('thingsboard.types', [])
dashboard: "DASHBOARD", dashboard: "DASHBOARD",
alarm: "ALARM" alarm: "ALARM"
}, },
aliasEntityType: {
current_customer: "CURRENT_CUSTOMER"
},
entityTypeTranslations: { entityTypeTranslations: {
"DEVICE": { "DEVICE": {
type: 'entity.type-device', type: 'entity.type-device',
@ -350,6 +353,10 @@ export default angular.module('thingsboard.types', [])
typePlural: 'entity.type-alarms', typePlural: 'entity.type-alarms',
list: 'entity.list-of-alarms', list: 'entity.list-of-alarms',
nameStartsWith: 'entity.alarm-name-starts-with' nameStartsWith: 'entity.alarm-name-starts-with'
},
"CURRENT_CUSTOMER": {
type: 'entity.type-current-customer',
list: 'entity.type-current-customer'
} }
}, },
entitySearchDirection: { entitySearchDirection: {

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

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

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

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

2
ui/src/app/dashboard/add-dashboards-to-customer.controller.js

@ -52,7 +52,7 @@ export default function AddDashboardsToCustomerController(dashboardService, $mdD
fetchMoreItems_: function () { fetchMoreItems_: function () {
if (vm.dashboards.hasNext && !vm.dashboards.pending) { if (vm.dashboards.hasNext && !vm.dashboards.pending) {
vm.dashboards.pending = true; vm.dashboards.pending = true;
dashboardService.getTenantDashboards(vm.dashboards.nextPageLink, false).then( dashboardService.getTenantDashboards(vm.dashboards.nextPageLink).then(
function success(dashboards) { function success(dashboards) {
vm.dashboards.data = vm.dashboards.data.concat(dashboards.data); vm.dashboards.data = vm.dashboards.data.concat(dashboards.data);
vm.dashboards.nextPageLink = dashboards.nextPageLink; vm.dashboards.nextPageLink = dashboards.nextPageLink;

123
ui/src/app/dashboard/assign-to-customer.controller.js

@ -1,123 +0,0 @@
/*
* Copyright © 2016-2017 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*@ngInject*/
export default function AssignDashboardToCustomerController(customerService, dashboardService, $mdDialog, $q, dashboardIds, customers) {
var vm = this;
vm.customers = customers;
vm.searchText = '';
vm.assign = assign;
vm.cancel = cancel;
vm.isCustomerSelected = isCustomerSelected;
vm.hasData = hasData;
vm.noData = noData;
vm.searchCustomerTextUpdated = searchCustomerTextUpdated;
vm.toggleCustomerSelection = toggleCustomerSelection;
vm.theCustomers = {
getItemAtIndex: function (index) {
if (index > vm.customers.data.length) {
vm.theCustomers.fetchMoreItems_(index);
return null;
}
var item = vm.customers.data[index];
if (item) {
item.indexNumber = index + 1;
}
return item;
},
getLength: function () {
if (vm.customers.hasNext) {
return vm.customers.data.length + vm.customers.nextPageLink.limit;
} else {
return vm.customers.data.length;
}
},
fetchMoreItems_: function () {
if (vm.customers.hasNext && !vm.customers.pending) {
vm.customers.pending = true;
customerService.getCustomers(vm.customers.nextPageLink).then(
function success(customers) {
vm.customers.data = vm.customers.data.concat(customers.data);
vm.customers.nextPageLink = customers.nextPageLink;
vm.customers.hasNext = customers.hasNext;
if (vm.customers.hasNext) {
vm.customers.nextPageLink.limit = vm.customers.pageSize;
}
vm.customers.pending = false;
},
function fail() {
vm.customers.hasNext = false;
vm.customers.pending = false;
});
}
}
};
function cancel () {
$mdDialog.cancel();
}
function assign () {
var tasks = [];
for (var dashboardId in dashboardIds) {
tasks.push(dashboardService.assignDashboardToCustomer(vm.customers.selection.id.id, dashboardIds[dashboardId]));
}
$q.all(tasks).then(function () {
$mdDialog.hide();
});
}
function noData () {
return vm.customers.data.length == 0 && !vm.customers.hasNext;
}
function hasData () {
return vm.customers.data.length > 0;
}
function toggleCustomerSelection ($event, customer) {
$event.stopPropagation();
if (vm.isCustomerSelected(customer)) {
vm.customers.selection = null;
} else {
vm.customers.selection = customer;
}
}
function isCustomerSelected (customer) {
return vm.customers.selection != null && customer &&
customer.id.id === vm.customers.selection.id.id;
}
function searchCustomerTextUpdated () {
vm.customers = {
pageSize: vm.customers.pageSize,
data: [],
nextPageLink: {
limit: vm.customers.pageSize,
textSearch: vm.searchText
},
selection: null,
hasNext: true,
pending: false
};
}
}

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

@ -1,76 +0,0 @@
<!--
Copyright © 2016-2017 The Thingsboard Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<md-dialog aria-label="{{ 'dashboard.assign-dashboard-to-customer' | translate }}">
<form name="theForm" ng-submit="vm.assign()">
<md-toolbar>
<div class="md-toolbar-tools">
<h2 translate>dashboard.assign-dashboard-to-customer</h2>
<span flex></span>
<md-button class="md-icon-button" ng-click="vm.cancel()">
<ng-md-icon icon="close" aria-label="{{ 'dialog.close' | translate }}"></ng-md-icon>
</md-button>
</div>
</md-toolbar>
<md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
<span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
<md-dialog-content>
<div class="md-dialog-content">
<fieldset>
<span translate>dashboard.assign-to-customer-text</span>
<md-input-container class="md-block" style='margin-bottom: 0px;'>
<label>&nbsp;</label>
<md-icon aria-label="{{ 'action.search' | translate }}" class="material-icons">
search
</md-icon>
<input id="customer-search" autofocus ng-model="vm.searchText"
ng-change="vm.searchCustomerTextUpdated()"
placeholder="{{ 'common.enter-search' | translate }}"/>
</md-input-container>
<div style='min-height: 150px;'>
<span translate layout-align="center center"
style="text-transform: uppercase; display: flex; height: 150px;"
class="md-subhead"
ng-show="vm.noData()">customer.no-customers-text</span>
<md-virtual-repeat-container ng-show="vm.hasData()"
tb-scope-element="repeatContainer" md-top-index="vm.topIndex" flex
style='min-height: 150px; width: 100%;'>
<md-list>
<md-list-item md-virtual-repeat="customer in vm.theCustomers" md-on-demand
class="repeated-item" flex>
<md-checkbox ng-click="vm.toggleCustomerSelection($event, customer)"
aria-label="{{ 'item.selected' | translate }}"
ng-checked="vm.isCustomerSelected(customer)"></md-checkbox>
<span> {{ customer.title }} </span>
</md-list-item>
</md-list>
</md-virtual-repeat-container>
</div>
</fieldset>
</div>
</md-dialog-content>
<md-dialog-actions layout="row">
<span flex></span>
<md-button ng-disabled="$root.loading || vm.customers.selection==null" type="submit" class="md-raised md-primary">
{{ 'action.assign' | translate }}
</md-button>
<md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' |
translate }}
</md-button>
</md-dialog-actions>
</form>
</md-dialog>

27
ui/src/app/dashboard/dashboard-card.scss

@ -0,0 +1,27 @@
/**
* Copyright © 2016-2017 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
.tb-dashboard-assigned-customers {
display: block;
display: -webkit-box;
height: 34px;
overflow: hidden;
text-overflow: ellipsis;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
margin-bottom: 4px;
}

4
ui/src/app/dashboard/dashboard-card.tpl.html

@ -15,6 +15,6 @@
limitations under the License. limitations under the License.
--> -->
<div class="tb-small" ng-show="vm.isAssignedToCustomer()">{{'dashboard.assignedToCustomer' | translate}} '{{vm.item.assignedCustomer.title}}'</div> <div class="tb-small tb-dashboard-assigned-customers" ng-show="vm.parentCtl.dashboardsScope === 'tenant' && vm.item.assignedCustomersText">{{'dashboard.assignedToCustomers' | translate}}: '{{vm.item.assignedCustomersText}}'</div>
<div class="tb-small" ng-show="vm.isPublic()">{{'dashboard.public' | translate}}</div> <div class="tb-small" ng-show="vm.parentCtl.dashboardsScope === 'tenant' && vm.item.publicCustomerId">{{'dashboard.public' | translate}}</div>

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

@ -19,24 +19,29 @@
ng-show="!isEdit && dashboardScope === 'tenant'" ng-show="!isEdit && dashboardScope === 'tenant'"
class="md-raised md-primary">{{ 'dashboard.export' | translate }}</md-button> class="md-raised md-primary">{{ 'dashboard.export' | translate }}</md-button>
<md-button ng-click="onMakePublic({event: $event})" <md-button ng-click="onMakePublic({event: $event})"
ng-show="!isEdit && dashboardScope === 'tenant' && !isAssignedToCustomer && !isPublic" ng-show="!isEdit && dashboardScope === 'tenant' && !dashboard.publicCustomerId"
class="md-raised md-primary">{{ 'dashboard.make-public' | translate }}</md-button> class="md-raised md-primary">{{ 'dashboard.make-public' | translate }}</md-button>
<md-button ng-click="onAssignToCustomer({event: $event})" <md-button ng-click="onMakePrivate({event: $event})"
ng-show="!isEdit && dashboardScope === 'tenant' && !isAssignedToCustomer" ng-show="!isEdit && ((dashboardScope === 'tenant' && dashboard.publicCustomerId ||
class="md-raised md-primary">{{ 'dashboard.assign-to-customer' | translate }}</md-button> dashboardScope === 'customer' && customerId == dashboard.publicCustomerId))"
<md-button ng-click="onUnassignFromCustomer({event: $event, isPublic: isPublic})" class="md-raised md-primary">{{ 'dashboard.make-private' | translate }}</md-button>
ng-show="!isEdit && (dashboardScope === 'customer' || dashboardScope === 'tenant') && isAssignedToCustomer" <md-button ng-click="onManageAssignedCustomers({event: $event})"
class="md-raised md-primary">{{ isPublic ? 'dashboard.make-private' : 'dashboard.unassign-from-customer' | translate }}</md-button> ng-show="!isEdit && dashboardScope === 'tenant'"
class="md-raised md-primary">{{ 'dashboard.manage-assigned-customers' | translate }}</md-button>
<md-button ng-click="onUnassignFromCustomer({event: $event})"
ng-show="!isEdit && dashboardScope === 'customer' && customerId != dashboard.publicCustomerId"
class="md-raised md-primary">{{ 'dashboard.unassign-from-customer' | translate }}</md-button>
<md-button ng-click="onDeleteDashboard({event: $event})" <md-button ng-click="onDeleteDashboard({event: $event})"
ng-show="!isEdit && dashboardScope === 'tenant'" ng-show="!isEdit && dashboardScope === 'tenant'"
class="md-raised md-primary">{{ 'dashboard.delete' | translate }}</md-button> class="md-raised md-primary">{{ 'dashboard.delete' | translate }}</md-button>
<md-content class="md-padding" layout="column"> <md-content class="md-padding" layout="column">
<md-input-container class="md-block" <md-input-container class="md-block"
ng-show="!isEdit && isAssignedToCustomer && !isPublic && dashboardScope === 'tenant'"> ng-show="!isEdit && dashboard.assignedCustomersText && dashboardScope === 'tenant'">
<label translate>dashboard.assignedToCustomer</label> <label translate>dashboard.assignedToCustomers</label>
<input ng-model="assignedCustomer.title" disabled> <input ng-model="dashboard.assignedCustomersText" disabled>
</md-input-container> </md-input-container>
<div layout="column" ng-show="!isEdit && isPublic && (dashboardScope === 'customer' || dashboardScope === 'tenant')"> <div layout="column" ng-show="!isEdit && ((dashboardScope === 'tenant' && dashboard.publicCustomerId ||
dashboardScope === 'customer' && customerId == dashboard.publicCustomerId))">
<tb-social-share-panel style="padding-bottom: 10px;" <tb-social-share-panel style="padding-bottom: 10px;"
share-title="{{ 'dashboard.socialshare-title' | translate:{dashboardTitle: dashboard.title} }}" share-title="{{ 'dashboard.socialshare-title' | translate:{dashboardTitle: dashboard.title} }}"
share-text="{{ 'dashboard.socialshare-text' | translate:{dashboardTitle: dashboard.title} }}" share-text="{{ 'dashboard.socialshare-text' | translate:{dashboardTitle: dashboard.title} }}"

29
ui/src/app/dashboard/dashboard.directive.js

@ -20,36 +20,17 @@ import dashboardFieldsetTemplate from './dashboard-fieldset.tpl.html';
/* eslint-enable import/no-unresolved, import/default */ /* eslint-enable import/no-unresolved, import/default */
/*@ngInject*/ /*@ngInject*/
export default function DashboardDirective($compile, $templateCache, $translate, types, toast, customerService, dashboardService) { export default function DashboardDirective($compile, $templateCache, $translate, types, toast, dashboardService) {
var linker = function (scope, element) { var linker = function (scope, element) {
var template = $templateCache.get(dashboardFieldsetTemplate); var template = $templateCache.get(dashboardFieldsetTemplate);
element.html(template); element.html(template);
scope.isAssignedToCustomer = false;
scope.isPublic = false;
scope.assignedCustomer = null;
scope.publicLink = null; scope.publicLink = null;
scope.$watch('dashboard', function(newVal) { scope.$watch('dashboard', function(newVal) {
if (newVal) { if (newVal) {
if (scope.dashboard.customerId && scope.dashboard.customerId.id !== types.id.nullUid) { if (scope.dashboard.publicCustomerId) {
scope.isAssignedToCustomer = true; scope.publicLink = dashboardService.getPublicDashboardLink(scope.dashboard);
customerService.getShortCustomerInfo(scope.dashboard.customerId.id).then(
function success(customer) {
scope.assignedCustomer = customer;
scope.isPublic = customer.isPublic;
if (scope.isPublic) {
scope.publicLink = dashboardService.getPublicDashboardLink(scope.dashboard);
} else {
scope.publicLink = null;
}
}
);
} else { } else {
scope.isAssignedToCustomer = false;
scope.isPublic = false;
scope.publicLink = null; scope.publicLink = null;
scope.assignedCustomer = null;
} }
} }
}); });
@ -66,10 +47,12 @@ export default function DashboardDirective($compile, $templateCache, $translate,
scope: { scope: {
dashboard: '=', dashboard: '=',
isEdit: '=', isEdit: '=',
customerId: '=',
dashboardScope: '=', dashboardScope: '=',
theForm: '=', theForm: '=',
onAssignToCustomer: '&',
onMakePublic: '&', onMakePublic: '&',
onMakePrivate: '&',
onManageAssignedCustomers: '&',
onUnassignFromCustomer: '&', onUnassignFromCustomer: '&',
onExportDashboard: '&', onExportDashboard: '&',
onDeleteDashboard: '&' onDeleteDashboard: '&'

226
ui/src/app/dashboard/dashboards.controller.js

@ -17,12 +17,14 @@
import addDashboardTemplate from './add-dashboard.tpl.html'; import addDashboardTemplate from './add-dashboard.tpl.html';
import dashboardCard from './dashboard-card.tpl.html'; import dashboardCard from './dashboard-card.tpl.html';
import assignToCustomerTemplate from './assign-to-customer.tpl.html';
import addDashboardsToCustomerTemplate from './add-dashboards-to-customer.tpl.html'; import addDashboardsToCustomerTemplate from './add-dashboards-to-customer.tpl.html';
import makeDashboardPublicDialogTemplate from './make-dashboard-public-dialog.tpl.html'; import makeDashboardPublicDialogTemplate from './make-dashboard-public-dialog.tpl.html';
import manageAssignedCustomersTemplate from './manage-assigned-customers.tpl.html';
/* eslint-enable import/no-unresolved, import/default */ /* eslint-enable import/no-unresolved, import/default */
import './dashboard-card.scss';
/*@ngInject*/ /*@ngInject*/
export function MakeDashboardPublicDialogController($mdDialog, $translate, toast, dashboardService, dashboard) { export function MakeDashboardPublicDialogController($mdDialog, $translate, toast, dashboardService, dashboard) {
@ -48,23 +50,8 @@ export function MakeDashboardPublicDialogController($mdDialog, $translate, toast
export function DashboardCardController(types) { export function DashboardCardController(types) {
var vm = this; var vm = this;
vm.types = types; vm.types = types;
vm.isAssignedToCustomer = function() {
if (vm.item && vm.item.customerId && vm.parentCtl.dashboardsScope === 'tenant' &&
vm.item.customerId.id != vm.types.id.nullUid && !vm.item.assignedCustomer.isPublic) {
return true;
}
return false;
}
vm.isPublic = function() {
if (vm.item && vm.item.assignedCustomer && vm.parentCtl.dashboardsScope === 'tenant' && vm.item.assignedCustomer.isPublic) {
return true;
}
return false;
}
} }
/*@ngInject*/ /*@ngInject*/
@ -135,8 +122,9 @@ export function DashboardsController(userService, dashboardService, customerServ
vm.dashboardsScope = $state.$current.data.dashboardsType; vm.dashboardsScope = $state.$current.data.dashboardsType;
vm.assignToCustomer = assignToCustomer;
vm.makePublic = makePublic; vm.makePublic = makePublic;
vm.makePrivate = makePrivate;
vm.manageAssignedCustomers = manageAssignedCustomers;
vm.unassignFromCustomer = unassignFromCustomer; vm.unassignFromCustomer = unassignFromCustomer;
vm.exportDashboard = exportDashboard; vm.exportDashboard = exportDashboard;
@ -155,6 +143,7 @@ export function DashboardsController(userService, dashboardService, customerServ
} }
if (customerId) { if (customerId) {
vm.customerId = customerId;
vm.customerDashboardsTitle = $translate.instant('customer.dashboards'); vm.customerDashboardsTitle = $translate.instant('customer.dashboards');
customerService.getShortCustomerInfo(customerId).then( customerService.getShortCustomerInfo(customerId).then(
function success(info) { function success(info) {
@ -167,7 +156,7 @@ export function DashboardsController(userService, dashboardService, customerServ
if (vm.dashboardsScope === 'tenant') { if (vm.dashboardsScope === 'tenant') {
fetchDashboardsFunction = function (pageLink) { fetchDashboardsFunction = function (pageLink) {
return dashboardService.getTenantDashboards(pageLink, true); return dashboardService.getTenantDashboards(pageLink);
}; };
deleteDashboardFunction = function (dashboardId) { deleteDashboardFunction = function (dashboardId) {
return dashboardService.deleteDashboard(dashboardId); return dashboardService.deleteDashboard(dashboardId);
@ -194,11 +183,33 @@ export function DashboardsController(userService, dashboardService, customerServ
details: function() { return $translate.instant('dashboard.make-public') }, details: function() { return $translate.instant('dashboard.make-public') },
icon: "share", icon: "share",
isEnabled: function(dashboard) { isEnabled: function(dashboard) {
return dashboard && (!dashboard.customerId || dashboard.customerId.id === types.id.nullUid); return dashboard && !dashboard.publicCustomerId;
} }
}); });
dashboardActionsList.push({ dashboardActionsList.push({
onAction: function ($event, item) {
makePrivate($event, item);
},
name: function() { return $translate.instant('action.make-private') },
details: function() { return $translate.instant('dashboard.make-private') },
icon: "reply",
isEnabled: function(dashboard) {
return dashboard && dashboard.publicCustomerId;
}
});
dashboardActionsList.push({
onAction: function ($event, item) {
manageAssignedCustomers($event, item);
},
name: function() { return $translate.instant('action.assign') },
details: function() { return $translate.instant('dashboard.manage-assigned-customers') },
icon: "assignment_ind",
isEnabled: function(dashboard) {
return dashboard;
}
});
/*dashboardActionsList.push({
onAction: function ($event, item) { onAction: function ($event, item) {
assignToCustomer($event, [ item.id.id ]); assignToCustomer($event, [ item.id.id ]);
}, },
@ -208,8 +219,8 @@ export function DashboardsController(userService, dashboardService, customerServ
isEnabled: function(dashboard) { isEnabled: function(dashboard) {
return dashboard && (!dashboard.customerId || dashboard.customerId.id === types.id.nullUid); return dashboard && (!dashboard.customerId || dashboard.customerId.id === types.id.nullUid);
} }
}); });*/
dashboardActionsList.push({ /*dashboardActionsList.push({
onAction: function ($event, item) { onAction: function ($event, item) {
unassignFromCustomer($event, item, false); unassignFromCustomer($event, item, false);
}, },
@ -219,18 +230,7 @@ export function DashboardsController(userService, dashboardService, customerServ
isEnabled: function(dashboard) { isEnabled: function(dashboard) {
return dashboard && dashboard.customerId && dashboard.customerId.id !== types.id.nullUid && !dashboard.assignedCustomer.isPublic; return dashboard && dashboard.customerId && dashboard.customerId.id !== types.id.nullUid && !dashboard.assignedCustomer.isPublic;
} }
}); });*/
dashboardActionsList.push({
onAction: function ($event, item) {
unassignFromCustomer($event, item, true);
},
name: function() { return $translate.instant('action.make-private') },
details: function() { return $translate.instant('dashboard.make-private') },
icon: "reply",
isEnabled: function(dashboard) {
return dashboard && dashboard.customerId && dashboard.customerId.id !== types.id.nullUid && dashboard.assignedCustomer.isPublic;
}
});
dashboardActionsList.push( dashboardActionsList.push(
{ {
@ -246,7 +246,7 @@ export function DashboardsController(userService, dashboardService, customerServ
dashboardGroupActionsList.push( dashboardGroupActionsList.push(
{ {
onAction: function ($event, items) { onAction: function ($event, items) {
assignDashboardsToCustomer($event, items); assignDashboardsToCustomers($event, items);
}, },
name: function() { return $translate.instant('dashboard.assign-dashboards') }, name: function() { return $translate.instant('dashboard.assign-dashboards') },
details: function(selectedCount) { details: function(selectedCount) {
@ -255,6 +255,17 @@ export function DashboardsController(userService, dashboardService, customerServ
icon: "assignment_ind" icon: "assignment_ind"
} }
); );
dashboardGroupActionsList.push(
{
onAction: function ($event, items) {
unassignDashboardsFromCustomers($event, items);
},
name: function() { return $translate.instant('dashboard.unassign-dashboards') },
details: function(selectedCount) {
return $translate.instant('dashboard.unassign-dashboards-action-text', {count: selectedCount}, "messageformat");
},
icon: "assignment_return" }
);
dashboardGroupActionsList.push( dashboardGroupActionsList.push(
{ {
@ -290,10 +301,10 @@ export function DashboardsController(userService, dashboardService, customerServ
}); });
} else if (vm.dashboardsScope === 'customer' || vm.dashboardsScope === 'customer_user') { } else if (vm.dashboardsScope === 'customer' || vm.dashboardsScope === 'customer_user') {
fetchDashboardsFunction = function (pageLink) { fetchDashboardsFunction = function (pageLink) {
return dashboardService.getCustomerDashboards(customerId, pageLink, true); return dashboardService.getCustomerDashboards(customerId, pageLink);
}; };
deleteDashboardFunction = function (dashboardId) { deleteDashboardFunction = function (dashboardId) {
return dashboardService.unassignDashboardFromCustomer(dashboardId); return dashboardService.unassignDashboardFromCustomer(customerId, dashboardId);
}; };
refreshDashboardsParamsFunction = function () { refreshDashboardsParamsFunction = function () {
return {"customerId": customerId, "topIndex": vm.topIndex}; return {"customerId": customerId, "topIndex": vm.topIndex};
@ -314,26 +325,27 @@ export function DashboardsController(userService, dashboardService, customerServ
dashboardActionsList.push( dashboardActionsList.push(
{ {
onAction: function ($event, item) { onAction: function ($event, item) {
unassignFromCustomer($event, item, false); makePrivate($event, item);
}, },
name: function() { return $translate.instant('action.unassign') }, name: function() { return $translate.instant('action.make-private') },
details: function() { return $translate.instant('dashboard.unassign-from-customer') }, details: function() { return $translate.instant('dashboard.make-private') },
icon: "assignment_return", icon: "reply",
isEnabled: function(dashboard) { isEnabled: function(dashboard) {
return dashboard && !dashboard.assignedCustomer.isPublic; return dashboard && customerId == dashboard.publicCustomerId;
} }
} }
); );
dashboardActionsList.push( dashboardActionsList.push(
{ {
onAction: function ($event, item) { onAction: function ($event, item) {
unassignFromCustomer($event, item, true); unassignFromCustomer($event, item, customerId);
}, },
name: function() { return $translate.instant('action.make-private') }, name: function() { return $translate.instant('action.unassign') },
details: function() { return $translate.instant('dashboard.make-private') }, details: function() { return $translate.instant('dashboard.unassign-from-customer') },
icon: "reply", icon: "assignment_return",
isEnabled: function(dashboard) { isEnabled: function(dashboard) {
return dashboard && dashboard.assignedCustomer.isPublic; return dashboard && customerId != dashboard.publicCustomerId;
} }
} }
); );
@ -341,7 +353,7 @@ export function DashboardsController(userService, dashboardService, customerServ
dashboardGroupActionsList.push( dashboardGroupActionsList.push(
{ {
onAction: function ($event, items) { onAction: function ($event, items) {
unassignDashboardsFromCustomer($event, items); unassignDashboardsFromCustomer($event, items, customerId);
}, },
name: function() { return $translate.instant('dashboard.unassign-dashboards') }, name: function() { return $translate.instant('dashboard.unassign-dashboards') },
details: function(selectedCount) { details: function(selectedCount) {
@ -351,7 +363,6 @@ export function DashboardsController(userService, dashboardService, customerServ
} }
); );
vm.dashboardGridConfig.addItemAction = { vm.dashboardGridConfig.addItemAction = {
onAction: function ($event) { onAction: function ($event) {
addDashboardsToCustomer($event); addDashboardsToCustomer($event);
@ -428,39 +439,42 @@ export function DashboardsController(userService, dashboardService, customerServ
return deferred.promise; return deferred.promise;
} }
function assignToCustomer($event, dashboardIds) { function manageAssignedCustomers($event, dashboard) {
showManageAssignedCustomersDialog($event, [dashboard.id.id], 'manage', dashboard.assignedCustomersIds);
}
function assignDashboardsToCustomers($event, items) {
var dashboardIds = [];
for (var id in items.selections) {
dashboardIds.push(id);
}
showManageAssignedCustomersDialog($event, dashboardIds, 'assign');
}
function unassignDashboardsFromCustomers($event, items) {
var dashboardIds = [];
for (var id in items.selections) {
dashboardIds.push(id);
}
showManageAssignedCustomersDialog($event, dashboardIds, 'unassign');
}
function showManageAssignedCustomersDialog($event, dashboardIds, actionType, assignedCustomers) {
if ($event) { if ($event) {
$event.stopPropagation(); $event.stopPropagation();
} }
var pageSize = 10; $mdDialog.show({
customerService.getCustomers({limit: pageSize, textSearch: ''}).then( controller: 'ManageAssignedCustomersController',
function success(_customers) { controllerAs: 'vm',
var customers = { templateUrl: manageAssignedCustomersTemplate,
pageSize: pageSize, locals: {actionType: actionType, dashboardIds: dashboardIds, assignedCustomers: assignedCustomers},
data: _customers.data, parent: angular.element($document[0].body),
nextPageLink: _customers.nextPageLink, fullscreen: true,
selection: null, targetEvent: $event
hasNext: _customers.hasNext, }).then(function () {
pending: false vm.grid.refreshList();
}; }, function () {
if (customers.hasNext) { });
customers.nextPageLink.limit = pageSize;
}
$mdDialog.show({
controller: 'AssignDashboardToCustomerController',
controllerAs: 'vm',
templateUrl: assignToCustomerTemplate,
locals: {dashboardIds: dashboardIds, customers: customers},
parent: angular.element($document[0].body),
fullscreen: true,
targetEvent: $event
}).then(function () {
vm.grid.refreshList();
}, function () {
});
},
function fail() {
});
} }
function addDashboardsToCustomer($event) { function addDashboardsToCustomer($event) {
@ -468,7 +482,7 @@ export function DashboardsController(userService, dashboardService, customerServ
$event.stopPropagation(); $event.stopPropagation();
} }
var pageSize = 10; var pageSize = 10;
dashboardService.getTenantDashboards({limit: pageSize, textSearch: ''}, false).then( dashboardService.getTenantDashboards({limit: pageSize, textSearch: ''}).then(
function success(_dashboards) { function success(_dashboards) {
var dashboards = { var dashboards = {
pageSize: pageSize, pageSize: pageSize,
@ -499,30 +513,13 @@ export function DashboardsController(userService, dashboardService, customerServ
}); });
} }
function assignDashboardsToCustomer($event, items) { function unassignFromCustomer($event, dashboard, customerId) {
var dashboardIds = [];
for (var id in items.selections) {
dashboardIds.push(id);
}
assignToCustomer($event, dashboardIds);
}
function unassignFromCustomer($event, dashboard, isPublic) {
if ($event) { if ($event) {
$event.stopPropagation(); $event.stopPropagation();
} }
var title; var title = $translate.instant('dashboard.unassign-dashboard-title', {dashboardTitle: dashboard.title});
var content; var content = $translate.instant('dashboard.unassign-dashboard-text');
var label; var label = $translate.instant('dashboard.unassign-dashboard');
if (isPublic) {
title = $translate.instant('dashboard.make-private-dashboard-title', {dashboardTitle: dashboard.title});
content = $translate.instant('dashboard.make-private-dashboard-text');
label = $translate.instant('dashboard.make-private-dashboard');
} else {
title = $translate.instant('dashboard.unassign-dashboard-title', {dashboardTitle: dashboard.title});
content = $translate.instant('dashboard.unassign-dashboard-text');
label = $translate.instant('dashboard.unassign-dashboard');
}
var confirm = $mdDialog.confirm() var confirm = $mdDialog.confirm()
.targetEvent($event) .targetEvent($event)
.title(title) .title(title)
@ -531,7 +528,7 @@ export function DashboardsController(userService, dashboardService, customerServ
.cancel($translate.instant('action.no')) .cancel($translate.instant('action.no'))
.ok($translate.instant('action.yes')); .ok($translate.instant('action.yes'));
$mdDialog.show(confirm).then(function () { $mdDialog.show(confirm).then(function () {
dashboardService.unassignDashboardFromCustomer(dashboard.id.id).then(function success() { dashboardService.unassignDashboardFromCustomer(customerId, dashboard.id.id).then(function success() {
vm.grid.refreshList(); vm.grid.refreshList();
}); });
}); });
@ -556,12 +553,33 @@ export function DashboardsController(userService, dashboardService, customerServ
}); });
} }
function makePrivate($event, dashboard) {
if ($event) {
$event.stopPropagation();
}
var title = $translate.instant('dashboard.make-private-dashboard-title', {dashboardTitle: dashboard.title});
var content = $translate.instant('dashboard.make-private-dashboard-text');
var label = $translate.instant('dashboard.make-private-dashboard');
var confirm = $mdDialog.confirm()
.targetEvent($event)
.title(title)
.htmlContent(content)
.ariaLabel(label)
.cancel($translate.instant('action.no'))
.ok($translate.instant('action.yes'));
$mdDialog.show(confirm).then(function () {
dashboardService.makeDashboardPrivate(dashboard.id.id).then(function success() {
vm.grid.refreshList();
});
});
}
function exportDashboard($event, dashboard) { function exportDashboard($event, dashboard) {
$event.stopPropagation(); $event.stopPropagation();
importExport.exportDashboard(dashboard.id.id); importExport.exportDashboard(dashboard.id.id);
} }
function unassignDashboardsFromCustomer($event, items) { function unassignDashboardsFromCustomer($event, items, customerId) {
var confirm = $mdDialog.confirm() var confirm = $mdDialog.confirm()
.targetEvent($event) .targetEvent($event)
.title($translate.instant('dashboard.unassign-dashboards-title', {count: items.selectedCount}, 'messageformat')) .title($translate.instant('dashboard.unassign-dashboards-title', {count: items.selectedCount}, 'messageformat'))
@ -572,7 +590,7 @@ export function DashboardsController(userService, dashboardService, customerServ
$mdDialog.show(confirm).then(function () { $mdDialog.show(confirm).then(function () {
var tasks = []; var tasks = [];
for (var id in items.selections) { for (var id in items.selections) {
tasks.push(dashboardService.unassignDashboardFromCustomer(id)); tasks.push(dashboardService.unassignDashboardFromCustomer(customerId, id));
} }
$q.all(tasks).then(function () { $q.all(tasks).then(function () {
vm.grid.refreshList(); vm.grid.refreshList();

6
ui/src/app/dashboard/dashboards.tpl.html

@ -25,10 +25,12 @@
<tb-dashboard-details dashboard="vm.grid.operatingItem()" <tb-dashboard-details dashboard="vm.grid.operatingItem()"
is-edit="vm.grid.detailsConfig.isDetailsEditMode" is-edit="vm.grid.detailsConfig.isDetailsEditMode"
dashboard-scope="vm.dashboardsScope" dashboard-scope="vm.dashboardsScope"
customer-id="vm.customerId"
the-form="vm.grid.detailsForm" the-form="vm.grid.detailsForm"
on-assign-to-customer="vm.assignToCustomer(event, [ vm.grid.detailsConfig.currentItem.id.id ])"
on-make-public="vm.makePublic(event, vm.grid.detailsConfig.currentItem)" on-make-public="vm.makePublic(event, vm.grid.detailsConfig.currentItem)"
on-unassign-from-customer="vm.unassignFromCustomer(event, vm.grid.detailsConfig.currentItem, isPublic)" on-make-private="vm.makePrivate(event, vm.grid.detailsConfig.currentItem)"
on-manage-assigned-customers="vm.manageAssignedCustomers(event, vm.grid.detailsConfig.currentItem)"
on-unassign-from-customer="vm.unassignFromCustomer(event, vm.grid.detailsConfig.currentItem, vm.customerId)"
on-export-dashboard="vm.exportDashboard(event, vm.grid.detailsConfig.currentItem)" on-export-dashboard="vm.exportDashboard(event, vm.grid.detailsConfig.currentItem)"
on-delete-dashboard="vm.grid.deleteItem(event, vm.grid.detailsConfig.currentItem)"></tb-dashboard-details> on-delete-dashboard="vm.grid.deleteItem(event, vm.grid.detailsConfig.currentItem)"></tb-dashboard-details>
</md-tab> </md-tab>

4
ui/src/app/dashboard/index.js

@ -40,8 +40,8 @@ import DashboardRoutes from './dashboard.routes';
import {DashboardsController, DashboardCardController, MakeDashboardPublicDialogController} from './dashboards.controller'; import {DashboardsController, DashboardCardController, MakeDashboardPublicDialogController} from './dashboards.controller';
import DashboardController from './dashboard.controller'; import DashboardController from './dashboard.controller';
import DashboardSettingsController from './dashboard-settings.controller'; import DashboardSettingsController from './dashboard-settings.controller';
import AssignDashboardToCustomerController from './assign-to-customer.controller';
import AddDashboardsToCustomerController from './add-dashboards-to-customer.controller'; import AddDashboardsToCustomerController from './add-dashboards-to-customer.controller';
import ManageAssignedCustomersController from './manage-assigned-customers.controller';
import AddWidgetController from './add-widget.controller'; import AddWidgetController from './add-widget.controller';
import DashboardDirective from './dashboard.directive'; import DashboardDirective from './dashboard.directive';
import EditWidgetDirective from './edit-widget.directive'; import EditWidgetDirective from './edit-widget.directive';
@ -74,8 +74,8 @@ export default angular.module('thingsboard.dashboard', [
.controller('MakeDashboardPublicDialogController', MakeDashboardPublicDialogController) .controller('MakeDashboardPublicDialogController', MakeDashboardPublicDialogController)
.controller('DashboardController', DashboardController) .controller('DashboardController', DashboardController)
.controller('DashboardSettingsController', DashboardSettingsController) .controller('DashboardSettingsController', DashboardSettingsController)
.controller('AssignDashboardToCustomerController', AssignDashboardToCustomerController)
.controller('AddDashboardsToCustomerController', AddDashboardsToCustomerController) .controller('AddDashboardsToCustomerController', AddDashboardsToCustomerController)
.controller('ManageAssignedCustomersController', ManageAssignedCustomersController)
.controller('AddWidgetController', AddWidgetController) .controller('AddWidgetController', AddWidgetController)
.directive('tbDashboardDetails', DashboardDirective) .directive('tbDashboardDetails', DashboardDirective)
.directive('tbEditWidget', EditWidgetDirective) .directive('tbEditWidget', EditWidgetDirective)

69
ui/src/app/dashboard/manage-assigned-customers.controller.js

@ -0,0 +1,69 @@
/*
* Copyright © 2016-2017 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*@ngInject*/
export default function ManageAssignedCustomersController($mdDialog, $q, types, dashboardService, actionType, dashboardIds, assignedCustomers) {
var vm = this;
vm.types = types;
vm.actionType = actionType;
vm.dashboardIds = dashboardIds;
vm.assignedCustomers = assignedCustomers;
if (actionType != 'manage') {
vm.assignedCustomers = [];
}
if (actionType == 'manage') {
vm.titleText = 'dashboard.manage-assigned-customers';
vm.labelText = 'dashboard.assigned-customers';
vm.actionName = 'action.update';
} else if (actionType == 'assign') {
vm.titleText = 'dashboard.assign-to-customers';
vm.labelText = 'dashboard.assign-to-customers-text';
vm.actionName = 'action.assign';
} else if (actionType == 'unassign') {
vm.titleText = 'dashboard.unassign-from-customers';
vm.labelText = 'dashboard.unassign-from-customers-text';
vm.actionName = 'action.unassign';
}
vm.submit = submit;
vm.cancel = cancel;
function cancel () {
$mdDialog.cancel();
}
function submit () {
var tasks = [];
for (var i=0;i<vm.dashboardIds.length;i++) {
var dashboardId = vm.dashboardIds[i];
var promise;
if (vm.actionType == 'manage') {
promise = dashboardService.updateDashboardCustomers(dashboardId, vm.assignedCustomers);
} else if (vm.actionType == 'assign') {
promise = dashboardService.addDashboardCustomers(dashboardId, vm.assignedCustomers);
} else if (vm.actionType == 'unassign') {
promise = dashboardService.removeDashboardCustomers(dashboardId, vm.assignedCustomers);
}
tasks.push(promise);
}
$q.all(tasks).then(function () {
$mdDialog.hide();
});
}
}

51
ui/src/app/dashboard/manage-assigned-customers.tpl.html

@ -0,0 +1,51 @@
<!--
Copyright © 2016-2017 The Thingsboard Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<md-dialog aria-label="{{ vm.titleText | translate }}" style="width: 600px;">
<form name="theForm" ng-submit="vm.submit()">
<md-toolbar>
<div class="md-toolbar-tools">
<h2 translate>{{vm.titleText}}</h2>
<span flex></span>
<md-button class="md-icon-button" ng-click="vm.cancel()">
<ng-md-icon icon="close" aria-label="{{ 'dialog.close' | translate }}"></ng-md-icon>
</md-button>
</div>
</md-toolbar>
<md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear>
<span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span>
<md-dialog-content>
<div class="md-dialog-content">
<fieldset>
<span translate>{{vm.labelText}}</span>
<tb-entity-list ng-disabled="$root.loading"
ng-model="vm.assignedCustomers"
entity-type="vm.types.entityType.customer"></tb-entity-list>
</fieldset>
</div>
</md-dialog-content>
<md-dialog-actions layout="row">
<span flex></span>
<md-button ng-disabled="$root.loading || !theForm.$dirty || !theForm.$valid" type="submit" class="md-raised md-primary">
{{ vm.actionName | translate }}
</md-button>
<md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' |
translate }}
</md-button>
</md-dialog-actions>
</form>
</md-dialog>

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

@ -38,7 +38,12 @@ export default function EntityAutocomplete($compile, $templateCache, $q, $filter
if (scope.excludeEntityIds && scope.excludeEntityIds.length) { if (scope.excludeEntityIds && scope.excludeEntityIds.length) {
limit += scope.excludeEntityIds.length; limit += scope.excludeEntityIds.length;
} }
entityService.getEntitiesByNameFilter(scope.entityType, searchText, limit, {ignoreLoading: true}, scope.entitySubtype).then(function success(result) { var targetType = scope.entityType;
if (targetType == types.aliasEntityType.current_customer) {
targetType = types.entityType.customer;
}
entityService.getEntitiesByNameFilter(targetType, searchText, limit, {ignoreLoading: true}, scope.entitySubtype).then(function success(result) {
if (result) { if (result) {
if (scope.excludeEntityIds && scope.excludeEntityIds.length) { if (scope.excludeEntityIds && scope.excludeEntityIds.length) {
var entities = []; var entities = [];
@ -71,7 +76,11 @@ export default function EntityAutocomplete($compile, $templateCache, $q, $filter
ngModelCtrl.$render = function () { ngModelCtrl.$render = function () {
if (ngModelCtrl.$viewValue) { if (ngModelCtrl.$viewValue) {
entityService.getEntity(scope.entityType, ngModelCtrl.$viewValue).then( var targetType = scope.entityType;
if (targetType == types.aliasEntityType.current_customer) {
targetType = types.entityType.customer;
}
entityService.getEntity(targetType, ngModelCtrl.$viewValue).then(
function success(entity) { function success(entity) {
scope.entity = entity; scope.entity = entity;
}, },
@ -114,55 +123,61 @@ export default function EntityAutocomplete($compile, $templateCache, $q, $filter
scope.selectEntityText = 'asset.select-asset'; scope.selectEntityText = 'asset.select-asset';
scope.entityText = 'asset.asset'; scope.entityText = 'asset.asset';
scope.noEntitiesMatchingText = 'asset.no-assets-matching'; scope.noEntitiesMatchingText = 'asset.no-assets-matching';
scope.entityRequiredText = 'asset.asset-required' scope.entityRequiredText = 'asset.asset-required';
break; break;
case types.entityType.device: case types.entityType.device:
scope.selectEntityText = 'device.select-device'; scope.selectEntityText = 'device.select-device';
scope.entityText = 'device.device'; scope.entityText = 'device.device';
scope.noEntitiesMatchingText = 'device.no-devices-matching'; scope.noEntitiesMatchingText = 'device.no-devices-matching';
scope.entityRequiredText = 'device.device-required' scope.entityRequiredText = 'device.device-required';
break; break;
case types.entityType.rule: case types.entityType.rule:
scope.selectEntityText = 'rule.select-rule'; scope.selectEntityText = 'rule.select-rule';
scope.entityText = 'rule.rule'; scope.entityText = 'rule.rule';
scope.noEntitiesMatchingText = 'rule.no-rules-matching'; scope.noEntitiesMatchingText = 'rule.no-rules-matching';
scope.entityRequiredText = 'rule.rule-required' scope.entityRequiredText = 'rule.rule-required';
break; break;
case types.entityType.plugin: case types.entityType.plugin:
scope.selectEntityText = 'plugin.select-plugin'; scope.selectEntityText = 'plugin.select-plugin';
scope.entityText = 'plugin.plugin'; scope.entityText = 'plugin.plugin';
scope.noEntitiesMatchingText = 'plugin.no-plugins-matching'; scope.noEntitiesMatchingText = 'plugin.no-plugins-matching';
scope.entityRequiredText = 'plugin.plugin-required' scope.entityRequiredText = 'plugin.plugin-required';
break; break;
case types.entityType.tenant: case types.entityType.tenant:
scope.selectEntityText = 'tenant.select-tenant'; scope.selectEntityText = 'tenant.select-tenant';
scope.entityText = 'tenant.tenant'; scope.entityText = 'tenant.tenant';
scope.noEntitiesMatchingText = 'tenant.no-tenants-matching'; scope.noEntitiesMatchingText = 'tenant.no-tenants-matching';
scope.entityRequiredText = 'tenant.tenant-required' scope.entityRequiredText = 'tenant.tenant-required';
break; break;
case types.entityType.customer: case types.entityType.customer:
scope.selectEntityText = 'customer.select-customer'; scope.selectEntityText = 'customer.select-customer';
scope.entityText = 'customer.customer'; scope.entityText = 'customer.customer';
scope.noEntitiesMatchingText = 'customer.no-customers-matching'; scope.noEntitiesMatchingText = 'customer.no-customers-matching';
scope.entityRequiredText = 'customer.customer-required' scope.entityRequiredText = 'customer.customer-required';
break; break;
case types.entityType.user: case types.entityType.user:
scope.selectEntityText = 'user.select-user'; scope.selectEntityText = 'user.select-user';
scope.entityText = 'user.user'; scope.entityText = 'user.user';
scope.noEntitiesMatchingText = 'user.no-users-matching'; scope.noEntitiesMatchingText = 'user.no-users-matching';
scope.entityRequiredText = 'user.user-required' scope.entityRequiredText = 'user.user-required';
break; break;
case types.entityType.dashboard: case types.entityType.dashboard:
scope.selectEntityText = 'dashboard.select-dashboard'; scope.selectEntityText = 'dashboard.select-dashboard';
scope.entityText = 'dashboard.dashboard'; scope.entityText = 'dashboard.dashboard';
scope.noEntitiesMatchingText = 'dashboard.no-dashboards-matching'; scope.noEntitiesMatchingText = 'dashboard.no-dashboards-matching';
scope.entityRequiredText = 'dashboard.dashboard-required' scope.entityRequiredText = 'dashboard.dashboard-required';
break; break;
case types.entityType.alarm: case types.entityType.alarm:
scope.selectEntityText = 'alarm.select-alarm'; scope.selectEntityText = 'alarm.select-alarm';
scope.entityText = 'alarm.alarm'; scope.entityText = 'alarm.alarm';
scope.noEntitiesMatchingText = 'alarm.no-alarms-matching'; scope.noEntitiesMatchingText = 'alarm.no-alarms-matching';
scope.entityRequiredText = 'alarm.alarm-required' scope.entityRequiredText = 'alarm.alarm-required';
break;
case types.aliasEntityType.current_customer:
scope.selectEntityText = 'customer.select-default-customer';
scope.entityText = 'customer.default-customer';
scope.noEntitiesMatchingText = 'customer.no-customers-matching';
scope.entityRequiredText = 'customer.default-customer-required';
break; break;
} }
if (scope.entity && scope.entity.id.entityType != scope.entityType) { if (scope.entity && scope.entity.id.entityType != scope.entityType) {

8
ui/src/app/entity/entity-filter.tpl.html

@ -32,6 +32,7 @@
<tb-entity-select flex <tb-entity-select flex
the-form="theForm" the-form="theForm"
tb-required="true" tb-required="true"
use-alias-entity-types="true"
ng-model="filter.singleEntity"> ng-model="filter.singleEntity">
</tb-entity-select> </tb-entity-select>
</section> </section>
@ -78,6 +79,7 @@
<tb-entity-select flex <tb-entity-select flex
the-form="theForm" the-form="theForm"
tb-required="false" tb-required="false"
use-alias-entity-types="true"
ng-model="filter.defaultStateEntity"> ng-model="filter.defaultStateEntity">
</tb-entity-select> </tb-entity-select>
</div> </div>
@ -123,6 +125,7 @@
the-form="theForm" the-form="theForm"
tb-required="!filter.rootStateEntity" tb-required="!filter.rootStateEntity"
ng-disabled="filter.rootStateEntity" ng-disabled="filter.rootStateEntity"
use-alias-entity-types="true"
ng-model="filter.rootEntity"> ng-model="filter.rootEntity">
</tb-entity-select> </tb-entity-select>
</div> </div>
@ -139,6 +142,7 @@
<tb-entity-select flex <tb-entity-select flex
the-form="theForm" the-form="theForm"
tb-required="false" tb-required="false"
use-alias-entity-types="true"
ng-model="filter.defaultStateEntity"> ng-model="filter.defaultStateEntity">
</tb-entity-select> </tb-entity-select>
</div> </div>
@ -182,6 +186,7 @@
the-form="theForm" the-form="theForm"
tb-required="!filter.rootStateEntity" tb-required="!filter.rootStateEntity"
ng-disabled="filter.rootStateEntity" ng-disabled="filter.rootStateEntity"
use-alias-entity-types="true"
ng-model="filter.rootEntity"> ng-model="filter.rootEntity">
</tb-entity-select> </tb-entity-select>
</div> </div>
@ -198,6 +203,7 @@
<tb-entity-select flex <tb-entity-select flex
the-form="theForm" the-form="theForm"
tb-required="false" tb-required="false"
use-alias-entity-types="true"
ng-model="filter.defaultStateEntity"> ng-model="filter.defaultStateEntity">
</tb-entity-select> </tb-entity-select>
</div> </div>
@ -249,6 +255,7 @@
the-form="theForm" the-form="theForm"
tb-required="!filter.rootStateEntity" tb-required="!filter.rootStateEntity"
ng-disabled="filter.rootStateEntity" ng-disabled="filter.rootStateEntity"
use-alias-entity-types="true"
ng-model="filter.rootEntity"> ng-model="filter.rootEntity">
</tb-entity-select> </tb-entity-select>
</div> </div>
@ -265,6 +272,7 @@
<tb-entity-select flex <tb-entity-select flex
the-form="theForm" the-form="theForm"
tb-required="false" tb-required="false"
use-alias-entity-types="true"
ng-model="filter.defaultStateEntity"> ng-model="filter.defaultStateEntity">
</tb-entity-select> </tb-entity-select>
</div> </div>

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

@ -105,7 +105,8 @@ export default function EntitySelect($compile, $templateCache) {
scope: { scope: {
theForm: '=?', theForm: '=?',
tbRequired: '=?', tbRequired: '=?',
disabled:'=ngDisabled' disabled:'=ngDisabled',
useAliasEntityTypes: "=?"
} }
}; };
} }

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

@ -20,6 +20,7 @@
the-form="theForm" the-form="theForm"
ng-disabled="disabled" ng-disabled="disabled"
tb-required="tbRequired" tb-required="tbRequired"
use-alias-entity-types="useAliasEntityTypes"
ng-model="model.entityType"> ng-model="model.entityType">
</tb-entity-type-select> </tb-entity-type-select>
<tb-entity-autocomplete flex ng-if="model.entityType" <tb-entity-autocomplete flex ng-if="model.entityType"

5
ui/src/app/entity/entity-type-select.directive.js

@ -39,7 +39,7 @@ export default function EntityTypeSelect($compile, $templateCache, utils, entity
scope.ngModelCtrl = ngModelCtrl; scope.ngModelCtrl = ngModelCtrl;
scope.entityTypes = entityService.prepareAllowedEntityTypesList(scope.allowedEntityTypes); scope.entityTypes = entityService.prepareAllowedEntityTypesList(scope.allowedEntityTypes, scope.useAliasEntityTypes);
scope.typeName = function(type) { scope.typeName = function(type) {
return type ? types.entityTypeTranslations[type].type : ''; return type ? types.entityTypeTranslations[type].type : '';
@ -79,7 +79,8 @@ export default function EntityTypeSelect($compile, $templateCache, utils, entity
theForm: '=?', theForm: '=?',
tbRequired: '=?', tbRequired: '=?',
disabled:'=ngDisabled', disabled:'=ngDisabled',
allowedEntityTypes: "=?" allowedEntityTypes: "=?",
useAliasEntityTypes: "=?"
} }
}; };
} }

11
ui/src/app/import-export/import-export.service.js

@ -540,7 +540,7 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document,
function success(dashboard) { function success(dashboard) {
var name = dashboard.title; var name = dashboard.title;
name = name.toLowerCase().replace(/\W/g,"_"); name = name.toLowerCase().replace(/\W/g,"_");
exportToPc(prepareExport(dashboard), name + '.json'); exportToPc(prepareDashboardExport(dashboard), name + '.json');
}, },
function fail(rejection) { function fail(rejection) {
var message = rejection; var message = rejection;
@ -552,6 +552,15 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document,
); );
} }
function prepareDashboardExport(dashboard) {
dashboard = prepareExport(dashboard);
delete dashboard.assignedCustomers;
delete dashboard.publicCustomerId;
delete dashboard.assignedCustomersText;
delete dashboard.assignedCustomersIds;
return dashboard;
}
function importDashboard($event) { function importDashboard($event) {
var deferred = $q.defer(); var deferred = $q.defer();
openImportDialog($event, 'dashboard.import', 'dashboard.dashboard-file').then( openImportDialog($event, 'dashboard.import', 'dashboard.dashboard-file').then(

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

@ -383,7 +383,10 @@ export default angular.module('thingsboard.locale', [])
"idCopiedMessage": "Customer Id has been copied to clipboard", "idCopiedMessage": "Customer Id has been copied to clipboard",
"select-customer": "Select customer", "select-customer": "Select customer",
"no-customers-matching": "No customers matching '{{entity}}' were found.", "no-customers-matching": "No customers matching '{{entity}}' were found.",
"customer-required": "Customer is required" "customer-required": "Customer is required",
"select-default-customer": "Select default customer",
"default-customer": "Default customer",
"default-customer-required": "Default customer is required in order to debug dashboard on Tenant level"
}, },
"datetime": { "datetime": {
"date-from": "Date from", "date-from": "Date from",
@ -404,6 +407,12 @@ export default angular.module('thingsboard.locale', [])
"unassign-from-customer": "Unassign from customer", "unassign-from-customer": "Unassign from customer",
"make-public": "Make dashboard public", "make-public": "Make dashboard public",
"make-private": "Make dashboard private", "make-private": "Make dashboard private",
"manage-assigned-customers": "Manage assigned customers",
"assigned-customers": "Assigned customers",
"assign-to-customers": "Assign Dashboard(s) To Customers",
"assign-to-customers-text": "Please select the customers to assign the dashboard(s)",
"unassign-from-customers": "Unassign Dashboard(s) From Customers",
"unassign-from-customers-text": "Please select the customers to unassign from the dashboard(s)",
"no-dashboards-text": "No dashboards found", "no-dashboards-text": "No dashboards found",
"no-widgets": "No widgets configured", "no-widgets": "No widgets configured",
"add-widget": "Add new widget", "add-widget": "Add new widget",
@ -418,7 +427,8 @@ export default angular.module('thingsboard.locale', [])
"add-dashboard-text": "Add new dashboard", "add-dashboard-text": "Add new dashboard",
"assign-dashboards": "Assign dashboards", "assign-dashboards": "Assign dashboards",
"assign-new-dashboard": "Assign new dashboard", "assign-new-dashboard": "Assign new dashboard",
"assign-dashboards-text": "Assign { count, select, 1 {1 dashboard} other {# dashboards} } to customer", "assign-dashboards-text": "Assign { count, select, 1 {1 dashboard} other {# dashboards} } to customers",
"unassign-dashboards-action-text": "Unassign { count, select, 1 {1 dashboard} other {# dashboards} } from customers",
"delete-dashboards": "Delete dashboards", "delete-dashboards": "Delete dashboards",
"unassign-dashboards": "Unassign dashboards", "unassign-dashboards": "Unassign dashboards",
"unassign-dashboards-action-title": "Unassign { count, select, 1 {1 dashboard} other {# dashboards} } from customer", "unassign-dashboards-action-title": "Unassign { count, select, 1 {1 dashboard} other {# dashboards} } from customer",
@ -500,6 +510,7 @@ export default angular.module('thingsboard.locale', [])
"Please contact your administrator in order to resolve this issue.", "Please contact your administrator in order to resolve this issue.",
"select-devices": "Select devices", "select-devices": "Select devices",
"assignedToCustomer": "Assigned to customer", "assignedToCustomer": "Assigned to customer",
"assignedToCustomers": "Assigned to customers",
"public": "Public", "public": "Public",
"public-link": "Public link", "public-link": "Public link",
"copy-public-link": "Copy public link", "copy-public-link": "Copy public link",
@ -735,6 +746,7 @@ export default angular.module('thingsboard.locale', [])
"type-alarms": "Alarms", "type-alarms": "Alarms",
"list-of-alarms": "{ count, select, 1 {One alarms} other {List of # alarms} }", "list-of-alarms": "{ count, select, 1 {One alarms} other {List of # alarms} }",
"alarm-name-starts-with": "Alarms whose names start with '{{prefix}}'", "alarm-name-starts-with": "Alarms whose names start with '{{prefix}}'",
"type-current-customer": "Current Customer",
"search": "Search entities", "search": "Search entities",
"selected-entities": "{ count, select, 1 {1 entity} other {# entities} } selected", "selected-entities": "{ count, select, 1 {1 entity} other {# entities} } selected",
"entity-name": "Entity name", "entity-name": "Entity name",

Loading…
Cancel
Save