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 {
validateId(dashboardId, "Incorrect dashboardId " + dashboardId);
DashboardInfo dashboardInfo = dashboardService.findDashboardInfoById(dashboardId);
SecurityUser authUser = getCurrentUser();
checkDashboard(dashboardInfo);
return dashboardInfo;
} catch (Exception e) {
@ -447,7 +446,7 @@ public abstract class BaseController {
checkTenantId(dashboard.getTenantId());
SecurityUser authUser = getCurrentUser();
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,
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.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import org.thingsboard.server.common.data.Customer;
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.*;
import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.id.CustomerId;
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.exception.ThingsboardException;
import java.util.HashSet;
import java.util.Set;
@RestController
@RequestMapping("/api")
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')")
@RequestMapping(value = "/customer/public/dashboard/{dashboardId}", method = RequestMethod.POST)
@ResponseBody

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

@ -15,12 +15,13 @@
*/
package org.thingsboard.server.service.install;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.CSVParser;
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.id.CustomerId;
import org.thingsboard.server.common.data.id.DashboardId;
@ -54,7 +55,8 @@ public class DatabaseHelper {
public static final ObjectMapper objectMapper = new ObjectMapper();
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())) {
csvParser.forEach(record -> {
String customerIdString = record.get(CUSTOMER_ID);
@ -63,12 +65,11 @@ public class DatabaseHelper {
List<CustomerId> customerIds = new ArrayList<>();
if (!StringUtils.isEmpty(assignedCustomersString)) {
try {
JsonNode assignedCustomersJson = objectMapper.readTree(assignedCustomersString);
Map<String,String> assignedCustomers = objectMapper.treeToValue(assignedCustomersJson, HashMap.class);
assignedCustomers.forEach((strCustomerId, title) -> {
CustomerId customerId = new CustomerId(UUID.fromString(strCustomerId));
Set<ShortCustomerInfo> assignedCustomers = objectMapper.readValue(assignedCustomersString, assignedCustomersType);
assignedCustomers.forEach((customerInfo) -> {
CustomerId customerId = customerInfo.getCustomerId();
if (!customerId.isNullUid()) {
customerIds.add(new CustomerId(UUID.fromString(strCustomerId)));
customerIds.add(customerId);
}
});
} catch (IOException e) {
@ -78,7 +79,7 @@ public class DatabaseHelper {
if (!StringUtils.isEmpty(customerIdString)) {
CustomerId customerId = new CustomerId(toUUID(customerIdString, sql));
if (!customerId.isNullUid()) {
customerIds.add(new CustomerId(toUUID(customerIdString, sql)));
customerIds.add(customerId);
}
}
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/" + 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);
Assert.assertTrue(foundDashboard.getAssignedCustomers().containsKey(savedCustomer.getId().toString()));
Assert.assertTrue(foundDashboard.getAssignedCustomers().contains(savedCustomer.toShortCustomerInfo()));
Dashboard unassignedDashboard =
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;
}
@JsonIgnore
public ShortCustomerInfo toShortCustomerInfo() {
return new ShortCustomerInfo(id, title, isPublic());
}
@Override
@JsonProperty(access = Access.READ_ONLY)
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.TenantId;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.*;
public class DashboardInfo extends SearchTextBased<DashboardId> implements HasName {
private TenantId tenantId;
private String title;
private Map<String, String> assignedCustomers;
private Set<ShortCustomerInfo> assignedCustomers;
public DashboardInfo() {
super();
@ -62,38 +59,56 @@ public class DashboardInfo extends SearchTextBased<DashboardId> implements HasNa
this.title = title;
}
public Map<String, String> getAssignedCustomers() {
public Set<ShortCustomerInfo> getAssignedCustomers() {
return assignedCustomers;
}
public void setAssignedCustomers(Map<String, String> assignedCustomers) {
public void setAssignedCustomers(Set<ShortCustomerInfo> assignedCustomers) {
this.assignedCustomers = assignedCustomers;
}
public boolean addAssignedCustomer(CustomerId customerId, String title) {
if (this.assignedCustomers != null && this.assignedCustomers.containsKey(customerId.toString())) {
public boolean isAssignedToCustomer(CustomerId customerId) {
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;
} else {
if (this.assignedCustomers == null) {
this.assignedCustomers = new HashMap<>();
this.assignedCustomers = new HashSet<>();
}
this.assignedCustomers.put(customerId.toString(), title);
this.assignedCustomers.add(customerInfo);
return true;
}
}
public boolean updateAssignedCustomer(CustomerId customerId, String title) {
if (this.assignedCustomers != null && this.assignedCustomers.containsKey(customerId.toString())) {
this.assignedCustomers.put(customerId.toString(), title);
public boolean updateAssignedCustomer(Customer customer) {
ShortCustomerInfo customerInfo = customer.toShortCustomerInfo();
if (this.assignedCustomers != null && this.assignedCustomers.contains(customerInfo)) {
this.assignedCustomers.add(customerInfo);
return true;
} else {
return false;
}
}
public boolean removeAssignedCustomer(CustomerId customerId) {
if (this.assignedCustomers != null && this.assignedCustomers.containsKey(customerId.toString())) {
this.assignedCustomers.remove(customerId.toString());
public boolean removeAssignedCustomer(Customer customer) {
ShortCustomerInfo customerInfo = customer.toShortCustomerInfo();
if (this.assignedCustomers != null && this.assignedCustomers.contains(customerInfo)) {
this.assignedCustomers.remove(customerInfo);
return true;
} else {
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);
customerValidator.validate(customer);
Customer savedCustomer = customerDao.save(customer);
dashboardService.updateCustomerDashboards(savedCustomer.getTenantId(), savedCustomer.getId(), savedCustomer.getTitle());
dashboardService.updateCustomerDashboards(savedCustomer.getId());
return savedCustomer;
}
@ -110,7 +110,7 @@ public class CustomerServiceImpl extends AbstractEntityService implements Custom
if (customer == null) {
throw new IncorrectParameterException("Unable to delete non-existent customer.");
}
dashboardService.unassignCustomerDashboards(customer.getTenantId(), customerId);
dashboardService.unassignCustomerDashboards(customerId);
assetService.unassignCustomerAssets(customer.getTenantId(), customerId);
deviceService.unassignCustomerDevices(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.TimePageLink;
import java.sql.Time;
import java.util.Set;
public interface DashboardService {
@ -52,8 +52,8 @@ public interface DashboardService {
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())) {
throw new DataValidationException("Can't assign dashboard to customer from different tenant!");
}
if (dashboard.addAssignedCustomer(customerId, customer.getTitle())) {
if (dashboard.addAssignedCustomer(customer)) {
try {
createRelation(new EntityRelation(customerId, dashboardId, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.DASHBOARD));
} catch (ExecutionException | InterruptedException e) {
@ -133,7 +133,11 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb
@Override
public Dashboard unassignDashboardFromCustomer(DashboardId dashboardId, CustomerId customerId) {
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 {
deleteRelation(new EntityRelation(customerId, dashboardId, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.DASHBOARD));
} 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);
if (dashboard.updateAssignedCustomer(customerId, customerTitle)) {
if (dashboard.updateAssignedCustomer(customer)) {
return saveDashboard(dashboard);
} else {
return dashboard;
@ -207,20 +211,25 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb
}
@Override
public void unassignCustomerDashboards(TenantId tenantId, CustomerId customerId) {
log.trace("Executing unassignCustomerDashboards, tenantId [{}], customerId [{}]", tenantId, customerId);
Validator.validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
public void unassignCustomerDashboards(CustomerId customerId) {
log.trace("Executing unassignCustomerDashboards, 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
public void updateCustomerDashboards(TenantId tenantId, CustomerId customerId, String customerTitle) {
log.trace("Executing updateCustomerDashboards, tenantId [{}], customerId [{}], customerTitle [{}]", tenantId, customerId, customerTitle);
Validator.validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
public void updateCustomerDashboards(CustomerId customerId) {
log.trace("Executing updateCustomerDashboards, customerId [{}]", customerId);
Validator.validateId(customerId, "Incorrect customerId " + customerId);
Validator.validateString(customerTitle, "Incorrect customerTitle " + customerTitle);
new CustomerDashboardsUpdater(tenantId, customerId, customerTitle).removeEntities(customerId);
Customer customer = customerDao.findById(customerId.getId());
if (customer == null) {
throw new DataValidationException("Can't update dashboards for non-existent customer!");
}
new CustomerDashboardsUpdater(customer).removeEntities(customer);
}
private DataValidator<Dashboard> dashboardValidator =
@ -255,58 +264,52 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb
}
};
private class CustomerDashboardsUnassigner extends TimePaginatedRemover<CustomerId, DashboardInfo> {
private TenantId tenantId;
private CustomerId customerId;
private class CustomerDashboardsUnassigner extends TimePaginatedRemover<Customer, DashboardInfo> {
CustomerDashboardsUnassigner(TenantId tenantId, CustomerId customerId) {
this.tenantId = tenantId;
this.customerId = customerId;
private Customer customer;
CustomerDashboardsUnassigner(Customer customer) {
this.customer = customer;
}
@Override
protected List<DashboardInfo> findEntities(CustomerId id, TimePageLink pageLink) {
protected List<DashboardInfo> findEntities(Customer customer, TimePageLink pageLink) {
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) {
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);
}
}
@Override
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 CustomerId customerId;
private String customerTitle;
private Customer customer;
CustomerDashboardsUpdater(TenantId tenantId, CustomerId customerId, String customerTitle) {
this.tenantId = tenantId;
this.customerId = customerId;
this.customerTitle = customerTitle;
CustomerDashboardsUpdater(Customer customer) {
this.customer = customer;
}
@Override
protected List<DashboardInfo> findEntities(CustomerId id, TimePageLink pageLink) {
protected List<DashboardInfo> findEntities(Customer customer, TimePageLink pageLink) {
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) {
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);
}
}
@Override
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.Table;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;
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.TenantId;
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.HashSet;
import java.util.UUID;
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> {
private static final ObjectMapper objectMapper = new ObjectMapper();
private static final JavaType assignedCustomersType =
objectMapper.getTypeFactory().constructCollectionType(HashSet.class, ShortCustomerInfo.class);
@PartitionKey(value = 0)
@Column(name = ID_PROPERTY)
@ -58,8 +64,8 @@ public final class DashboardEntity implements SearchTextEntity<Dashboard> {
@Column(name = SEARCH_TEXT_PROPERTY)
private String searchText;
@Column(name = DASHBOARD_ASSIGNED_CUSTOMERS_PROPERTY, codec = JsonCodec.class)
private JsonNode assignedCustomers;
@Column(name = DASHBOARD_ASSIGNED_CUSTOMERS_PROPERTY)
private String assignedCustomers;
@Column(name = DASHBOARD_CONFIGURATION_PROPERTY, codec = JsonCodec.class)
private JsonNode configuration;
@ -77,7 +83,11 @@ public final class DashboardEntity implements SearchTextEntity<Dashboard> {
}
this.title = dashboard.getTitle();
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();
}
@ -106,11 +116,11 @@ public final class DashboardEntity implements SearchTextEntity<Dashboard> {
this.title = title;
}
public JsonNode getAssignedCustomers() {
public String getAssignedCustomers() {
return assignedCustomers;
}
public void setAssignedCustomers(JsonNode assignedCustomers) {
public void setAssignedCustomers(String assignedCustomers) {
this.assignedCustomers = assignedCustomers;
}
@ -144,10 +154,10 @@ public final class DashboardEntity implements SearchTextEntity<Dashboard> {
dashboard.setTenantId(new TenantId(tenantId));
}
dashboard.setTitle(title);
if (assignedCustomers != null) {
if (!StringUtils.isEmpty(assignedCustomers)) {
try {
dashboard.setAssignedCustomers(objectMapper.treeToValue(assignedCustomers, HashMap.class));
} catch (JsonProcessingException e) {
dashboard.setAssignedCustomers(objectMapper.readValue(assignedCustomers, assignedCustomersType));
} catch (IOException 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.Table;
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 lombok.EqualsAndHashCode;
import lombok.ToString;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;
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.TenantId;
import org.thingsboard.server.dao.model.SearchTextEntity;
import org.thingsboard.server.dao.model.type.JsonCodec;
import java.util.HashMap;
import java.util.Set;
import java.io.IOException;
import java.util.HashSet;
import java.util.UUID;
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> {
private static final ObjectMapper objectMapper = new ObjectMapper();
private static final JavaType assignedCustomersType =
objectMapper.getTypeFactory().constructCollectionType(HashSet.class, ShortCustomerInfo.class);
@PartitionKey(value = 0)
@Column(name = ID_PROPERTY)
@ -60,8 +62,8 @@ public class DashboardInfoEntity implements SearchTextEntity<DashboardInfo> {
@Column(name = SEARCH_TEXT_PROPERTY)
private String searchText;
@Column(name = DASHBOARD_ASSIGNED_CUSTOMERS_PROPERTY, codec = JsonCodec.class)
private JsonNode assignedCustomers;
@Column(name = DASHBOARD_ASSIGNED_CUSTOMERS_PROPERTY)
private String assignedCustomers;
public DashboardInfoEntity() {
super();
@ -76,7 +78,11 @@ public class DashboardInfoEntity implements SearchTextEntity<DashboardInfo> {
}
this.title = dashboardInfo.getTitle();
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;
}
public JsonNode getAssignedCustomers() {
public String getAssignedCustomers() {
return assignedCustomers;
}
public void setAssignedCustomers(JsonNode assignedCustomers) {
public void setAssignedCustomers(String assignedCustomers) {
this.assignedCustomers = assignedCustomers;
}
@ -134,10 +140,10 @@ public class DashboardInfoEntity implements SearchTextEntity<DashboardInfo> {
dashboardInfo.setTenantId(new TenantId(tenantId));
}
dashboardInfo.setTitle(title);
if (assignedCustomers != null) {
if (!StringUtils.isEmpty(assignedCustomers)) {
try {
dashboardInfo.setAssignedCustomers(objectMapper.treeToValue(assignedCustomers, HashMap.class));
} catch (JsonProcessingException e) {
dashboardInfo.setAssignedCustomers(objectMapper.readValue(assignedCustomers, assignedCustomersType));
} catch (IOException 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.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Data;
@ -24,8 +25,9 @@ import lombok.EqualsAndHashCode;
import lombok.extern.slf4j.Slf4j;
import org.hibernate.annotations.Type;
import org.hibernate.annotations.TypeDef;
import org.springframework.util.StringUtils;
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.TenantId;
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.Entity;
import javax.persistence.Table;
import java.util.HashMap;
import java.util.List;
import java.util.Set;
import java.io.IOException;
import java.util.HashSet;
@Data
@Slf4j
@ -49,6 +50,8 @@ import java.util.Set;
public final class DashboardEntity extends BaseSqlEntity<Dashboard> implements SearchTextEntity<Dashboard> {
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)
private String tenantId;
@ -59,9 +62,8 @@ public final class DashboardEntity extends BaseSqlEntity<Dashboard> implements S
@Column(name = ModelConstants.SEARCH_TEXT_PROPERTY)
private String searchText;
@Type(type = "json")
@Column(name = ModelConstants.DASHBOARD_ASSIGNED_CUSTOMERS_PROPERTY)
private JsonNode assignedCustomers;
private String assignedCustomers;
@Type(type = "json")
@Column(name = ModelConstants.DASHBOARD_CONFIGURATION_PROPERTY)
@ -80,7 +82,11 @@ public final class DashboardEntity extends BaseSqlEntity<Dashboard> implements S
}
this.title = dashboard.getTitle();
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();
}
@ -103,10 +109,10 @@ public final class DashboardEntity extends BaseSqlEntity<Dashboard> implements S
dashboard.setTenantId(new TenantId(toUUID(tenantId)));
}
dashboard.setTitle(title);
if (assignedCustomers != null) {
if (!StringUtils.isEmpty(assignedCustomers)) {
try {
dashboard.setAssignedCustomers(objectMapper.treeToValue(assignedCustomers, HashMap.class));
} catch (JsonProcessingException e) {
dashboard.setAssignedCustomers(objectMapper.readValue(assignedCustomers, assignedCustomersType));
} catch (IOException 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.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Data;
import lombok.EqualsAndHashCode;
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.id.CustomerId;
import org.thingsboard.server.common.data.ShortCustomerInfo;
import org.thingsboard.server.common.data.id.DashboardId;
import org.thingsboard.server.common.data.id.TenantId;
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.Entity;
import javax.persistence.Table;
import java.util.HashMap;
import java.util.Set;
import java.io.IOException;
import java.util.HashSet;
@Data
@Slf4j
@ -45,6 +45,8 @@ import java.util.Set;
public class DashboardInfoEntity extends BaseSqlEntity<DashboardInfo> implements SearchTextEntity<DashboardInfo> {
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)
private String tenantId;
@ -55,9 +57,8 @@ public class DashboardInfoEntity extends BaseSqlEntity<DashboardInfo> implements
@Column(name = ModelConstants.SEARCH_TEXT_PROPERTY)
private String searchText;
@Type(type = "json")
@Column(name = ModelConstants.DASHBOARD_ASSIGNED_CUSTOMERS_PROPERTY)
private JsonNode assignedCustomers;
private String assignedCustomers;
public DashboardInfoEntity() {
super();
@ -72,7 +73,11 @@ public class DashboardInfoEntity extends BaseSqlEntity<DashboardInfo> implements
}
this.title = dashboardInfo.getTitle();
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.setTitle(title);
if (assignedCustomers != null) {
if (!StringUtils.isEmpty(assignedCustomers)) {
try {
dashboardInfo.setAssignedCustomers(objectMapper.treeToValue(assignedCustomers, HashMap.class));
} catch (JsonProcessingException e) {
dashboardInfo.setAssignedCustomers(objectMapper.readValue(assignedCustomers, assignedCustomersType));
} catch (IOException 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);
dashboardService.unassignCustomerDashboards(tenantId, customerId);
dashboardService.unassignCustomerDashboards(customerId);
pageLink = new TimePageLink(42);
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;
/*@ngInject*/
function DashboardService($rootScope, $http, $q, $location, customerService) {
function DashboardService($rootScope, $http, $q, $location, $filter) {
var stDiffPromise;
@ -37,7 +37,11 @@ function DashboardService($rootScope, $http, $q, $location, customerService) {
deleteDashboard: deleteDashboard,
saveDashboard: saveDashboard,
unassignDashboardFromCustomer: unassignDashboardFromCustomer,
updateDashboardCustomers: updateDashboardCustomers,
addDashboardCustomers: addDashboardCustomers,
removeDashboardCustomers: removeDashboardCustomers,
makeDashboardPublic: makeDashboardPublic,
makeDashboardPrivate: makeDashboardPrivate,
getPublicDashboardLink: getPublicDashboardLink
}
@ -56,14 +60,14 @@ function DashboardService($rootScope, $http, $q, $location, customerService) {
url += '&textOffset=' + pageLink.textOffset;
}
$http.get(url, config).then(function success(response) {
deferred.resolve(response.data);
deferred.resolve(prepareDashboards(response.data));
}, function fail() {
deferred.reject();
});
return deferred.promise;
}
function getTenantDashboards(pageLink, applyCustomersInfo, config) {
function getTenantDashboards(pageLink, config) {
var deferred = $q.defer();
var url = '/api/tenant/dashboards?limit=' + pageLink.limit;
if (angular.isDefined(pageLink.textSearch)) {
@ -76,51 +80,25 @@ function DashboardService($rootScope, $http, $q, $location, customerService) {
url += '&textOffset=' + pageLink.textOffset;
}
$http.get(url, config).then(function success(response) {
if (applyCustomersInfo) {
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);
}
deferred.resolve(prepareDashboards(response.data));
}, function fail() {
deferred.reject();
});
return deferred.promise;
}
function getCustomerDashboards(customerId, pageLink, applyCustomersInfo, config) {
function getCustomerDashboards(customerId, pageLink, config) {
var deferred = $q.defer();
var url = '/api/customer/' + customerId + '/dashboards?limit=' + pageLink.limit;
if (angular.isDefined(pageLink.textSearch)) {
url += '&textSearch=' + pageLink.textSearch;
}
if (angular.isDefined(pageLink.idOffset)) {
url += '&idOffset=' + pageLink.idOffset;
}
if (angular.isDefined(pageLink.textOffset)) {
url += '&textOffset=' + pageLink.textOffset;
url += '&offset=' + pageLink.idOffset;
}
$http.get(url, config).then(function success(response) {
if (applyCustomersInfo) {
customerService.applyAssignedCustomerInfo(response.data.data, customerId).then(
function success(data) {
response.data.data = data;
deferred.resolve(response.data);
},
function fail() {
deferred.reject();
}
);
} else {
deferred.resolve(response.data);
response.data = prepareDashboards(response.data);
if (pageLink.textSearch) {
response.data.data = $filter('filter')(response.data.data, {title: pageLink.textSearch});
}
deferred.resolve(response.data);
}, function fail() {
deferred.reject();
});
@ -151,7 +129,7 @@ function DashboardService($rootScope, $http, $q, $location, customerService) {
var deferred = $q.defer();
var url = '/api/dashboard/' + dashboardId;
$http.get(url, null).then(function success(response) {
deferred.resolve(response.data);
deferred.resolve(prepareDashboard(response.data));
}, function fail() {
deferred.reject();
});
@ -162,7 +140,7 @@ function DashboardService($rootScope, $http, $q, $location, customerService) {
var deferred = $q.defer();
var url = '/api/dashboard/info/' + dashboardId;
$http.get(url, config).then(function success(response) {
deferred.resolve(response.data);
deferred.resolve(prepareDashboard(response.data));
}, function fail() {
deferred.reject();
});
@ -172,8 +150,8 @@ function DashboardService($rootScope, $http, $q, $location, customerService) {
function saveDashboard(dashboard) {
var deferred = $q.defer();
var url = '/api/dashboard';
$http.post(url, dashboard).then(function success(response) {
deferred.resolve(response.data);
$http.post(url, cleanDashboard(dashboard)).then(function success(response) {
deferred.resolve(prepareDashboard(response.data));
}, function fail() {
deferred.reject();
});
@ -195,18 +173,51 @@ function DashboardService($rootScope, $http, $q, $location, customerService) {
var deferred = $q.defer();
var url = '/api/customer/' + customerId + '/dashboard/' + dashboardId;
$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 unassignDashboardFromCustomer(dashboardId) {
function unassignDashboardFromCustomer(customerId, dashboardId) {
var deferred = $q.defer();
var url = '/api/customer/dashboard/' + dashboardId;
var url = '/api/customer/' + customerId + '/dashboard/' + dashboardId;
$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() {
deferred.reject();
});
@ -217,7 +228,18 @@ function DashboardService($rootScope, $http, $q, $location, customerService) {
var deferred = $q.defer();
var url = '/api/customer/public/dashboard/' + dashboardId;
$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() {
deferred.reject();
});
@ -230,8 +252,44 @@ function DashboardService($rootScope, $http, $q, $location, customerService) {
if (port != 80 && port != 443) {
url += ":" + port;
}
url += "/dashboards/" + dashboard.id.id + "?publicId=" + dashboard.customerId.id;
url += "/dashboards/" + dashboard.id.id + "?publicId=" + dashboard.publicCustomerId;
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;
case types.entityType.dashboard:
if (user.authority === 'CUSTOMER_USER') {
promise = dashboardService.getCustomerDashboards(customerId, pageLink, false, config);
promise = dashboardService.getCustomerDashboards(customerId, pageLink, config);
} else {
promise = dashboardService.getTenantDashboards(pageLink, false, config);
promise = dashboardService.getTenantDashboards(pageLink, config);
}
break;
case types.entityType.user:
@ -403,6 +403,21 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
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) {
var entityId = null;
if (stateParams) {
@ -417,6 +432,9 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
if (!entityId) {
entityId = filter.defaultStateEntity;
}
if (entityId) {
entityId = resolveAliasEntityId(entityId.entityType, entityId.id);
}
return entityId;
}
@ -432,7 +450,8 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
var stateEntityId = getStateEntityId(filter, stateParams);
switch (filter.type) {
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) {
result.entities = entitiesToEntitiesInfo([entity]);
deferred.resolve(result);
@ -530,10 +549,11 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
rootEntityId = filter.rootEntity.id;
}
if (rootEntityType && rootEntityId) {
var relationQueryRootEntityId = resolveAliasEntityId(rootEntityType, rootEntityId);
var searchQuery = {
parameters: {
rootId: rootEntityId,
rootType: rootEntityType,
rootId: relationQueryRootEntityId.id,
rootType: relationQueryRootEntityId.entityType,
direction: filter.direction
},
filters: filter.filters
@ -571,10 +591,11 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
rootEntityId = filter.rootEntity.id;
}
if (rootEntityType && rootEntityId) {
var searchQueryRootEntityId = resolveAliasEntityId(rootEntityType, rootEntityId);
searchQuery = {
parameters: {
rootId: rootEntityId,
rootType: rootEntityType,
rootId: searchQueryRootEntityId.id,
rootType: searchQueryRootEntityId.entityType,
direction: filter.direction
},
relationType: filter.relationType
@ -709,7 +730,7 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
return result;
}
function prepareAllowedEntityTypesList(allowedEntityTypes) {
function prepareAllowedEntityTypesList(allowedEntityTypes, useAliasEntityTypes) {
var authority = userService.getAuthority();
var entityTypes = {};
switch(authority) {
@ -726,12 +747,18 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
entityTypes.rule = types.entityType.rule;
entityTypes.plugin = types.entityType.plugin;
entityTypes.dashboard = types.entityType.dashboard;
if (useAliasEntityTypes) {
entityTypes.current_customer = types.aliasEntityType.current_customer;
}
break;
case 'CUSTOMER_USER':
entityTypes.device = types.entityType.device;
entityTypes.asset = types.entityType.asset;
entityTypes.customer = types.entityType.customer;
entityTypes.dashboard = types.entityType.dashboard;
if (useAliasEntityTypes) {
entityTypes.current_customer = types.aliasEntityType.current_customer;
}
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 fetchDashboardsPromise;
if (currentUser.authority === 'TENANT_ADMIN') {
fetchDashboardsPromise = dashboardService.getTenantDashboards(pageLink, false);
fetchDashboardsPromise = dashboardService.getTenantDashboards(pageLink);
} else {
fetchDashboardsPromise = dashboardService.getCustomerDashboards(currentUser.customerId, pageLink, false);
fetchDashboardsPromise = dashboardService.getCustomerDashboards(currentUser.customerId, pageLink);
}
fetchDashboardsPromise.then(
function success(result) {

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

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

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

@ -48,7 +48,7 @@ function DashboardAutocomplete($compile, $templateCache, $q, dashboardService, u
var promise;
if (scope.dashboardsScope === 'customer' || userService.getAuthority() === 'CUSTOMER_USER') {
if (scope.customerId) {
promise = dashboardService.getCustomerDashboards(scope.customerId, pageLink, false, {ignoreLoading: true});
promise = dashboardService.getCustomerDashboards(scope.customerId, pageLink, {ignoreLoading: true});
} else {
promise = $q.when({data: []});
}
@ -60,7 +60,7 @@ function DashboardAutocomplete($compile, $templateCache, $q, dashboardService, u
promise = $q.when({data: []});
}
} 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;
if (scope.dashboardsScope === 'customer' || userService.getAuthority() === 'CUSTOMER_USER') {
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 {
promise = $q.when({data: []});
}
} else {
promise = dashboardService.getTenantDashboards(pageLink, false, {ignoreLoading: true});
promise = dashboardService.getTenantDashboards(pageLink, {ignoreLoading: true});
}
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 () {
if (vm.dashboards.hasNext && !vm.dashboards.pending) {
vm.dashboards.pending = true;
dashboardService.getTenantDashboards(vm.dashboards.nextPageLink, false).then(
dashboardService.getTenantDashboards(vm.dashboards.nextPageLink).then(
function success(dashboards) {
vm.dashboards.data = vm.dashboards.data.concat(dashboards.data);
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.
-->
<div class="tb-small" ng-show="vm.isAssignedToCustomer()">{{'dashboard.assignedToCustomer' | translate}} '{{vm.item.assignedCustomer.title}}'</div>
<div class="tb-small" ng-show="vm.isPublic()">{{'dashboard.public' | translate}}</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.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'"
class="md-raised md-primary">{{ 'dashboard.export' | translate }}</md-button>
<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>
<md-button ng-click="onAssignToCustomer({event: $event})"
ng-show="!isEdit && dashboardScope === 'tenant' && !isAssignedToCustomer"
class="md-raised md-primary">{{ 'dashboard.assign-to-customer' | translate }}</md-button>
<md-button ng-click="onUnassignFromCustomer({event: $event, isPublic: isPublic})"
ng-show="!isEdit && (dashboardScope === 'customer' || dashboardScope === 'tenant') && isAssignedToCustomer"
class="md-raised md-primary">{{ isPublic ? 'dashboard.make-private' : 'dashboard.unassign-from-customer' | translate }}</md-button>
<md-button ng-click="onMakePrivate({event: $event})"
ng-show="!isEdit && ((dashboardScope === 'tenant' && dashboard.publicCustomerId ||
dashboardScope === 'customer' && customerId == dashboard.publicCustomerId))"
class="md-raised md-primary">{{ 'dashboard.make-private' | translate }}</md-button>
<md-button ng-click="onManageAssignedCustomers({event: $event})"
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})"
ng-show="!isEdit && dashboardScope === 'tenant'"
class="md-raised md-primary">{{ 'dashboard.delete' | translate }}</md-button>
<md-content class="md-padding" layout="column">
<md-input-container class="md-block"
ng-show="!isEdit && isAssignedToCustomer && !isPublic && dashboardScope === 'tenant'">
<label translate>dashboard.assignedToCustomer</label>
<input ng-model="assignedCustomer.title" disabled>
ng-show="!isEdit && dashboard.assignedCustomersText && dashboardScope === 'tenant'">
<label translate>dashboard.assignedToCustomers</label>
<input ng-model="dashboard.assignedCustomersText" disabled>
</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;"
share-title="{{ 'dashboard.socialshare-title' | 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 */
/*@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 template = $templateCache.get(dashboardFieldsetTemplate);
element.html(template);
scope.isAssignedToCustomer = false;
scope.isPublic = false;
scope.assignedCustomer = null;
scope.publicLink = null;
scope.$watch('dashboard', function(newVal) {
if (newVal) {
if (scope.dashboard.customerId && scope.dashboard.customerId.id !== types.id.nullUid) {
scope.isAssignedToCustomer = true;
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;
}
}
);
if (scope.dashboard.publicCustomerId) {
scope.publicLink = dashboardService.getPublicDashboardLink(scope.dashboard);
} else {
scope.isAssignedToCustomer = false;
scope.isPublic = false;
scope.publicLink = null;
scope.assignedCustomer = null;
}
}
});
@ -66,10 +47,12 @@ export default function DashboardDirective($compile, $templateCache, $translate,
scope: {
dashboard: '=',
isEdit: '=',
customerId: '=',
dashboardScope: '=',
theForm: '=',
onAssignToCustomer: '&',
onMakePublic: '&',
onMakePrivate: '&',
onManageAssignedCustomers: '&',
onUnassignFromCustomer: '&',
onExportDashboard: '&',
onDeleteDashboard: '&'

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

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

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

@ -25,10 +25,12 @@
<tb-dashboard-details dashboard="vm.grid.operatingItem()"
is-edit="vm.grid.detailsConfig.isDetailsEditMode"
dashboard-scope="vm.dashboardsScope"
customer-id="vm.customerId"
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-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-delete-dashboard="vm.grid.deleteItem(event, vm.grid.detailsConfig.currentItem)"></tb-dashboard-details>
</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 DashboardController from './dashboard.controller';
import DashboardSettingsController from './dashboard-settings.controller';
import AssignDashboardToCustomerController from './assign-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 DashboardDirective from './dashboard.directive';
import EditWidgetDirective from './edit-widget.directive';
@ -74,8 +74,8 @@ export default angular.module('thingsboard.dashboard', [
.controller('MakeDashboardPublicDialogController', MakeDashboardPublicDialogController)
.controller('DashboardController', DashboardController)
.controller('DashboardSettingsController', DashboardSettingsController)
.controller('AssignDashboardToCustomerController', AssignDashboardToCustomerController)
.controller('AddDashboardsToCustomerController', AddDashboardsToCustomerController)
.controller('ManageAssignedCustomersController', ManageAssignedCustomersController)
.controller('AddWidgetController', AddWidgetController)
.directive('tbDashboardDetails', DashboardDirective)
.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) {
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 (scope.excludeEntityIds && scope.excludeEntityIds.length) {
var entities = [];
@ -71,7 +76,11 @@ export default function EntityAutocomplete($compile, $templateCache, $q, $filter
ngModelCtrl.$render = function () {
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) {
scope.entity = entity;
},
@ -114,55 +123,61 @@ export default function EntityAutocomplete($compile, $templateCache, $q, $filter
scope.selectEntityText = 'asset.select-asset';
scope.entityText = 'asset.asset';
scope.noEntitiesMatchingText = 'asset.no-assets-matching';
scope.entityRequiredText = 'asset.asset-required'
scope.entityRequiredText = 'asset.asset-required';
break;
case types.entityType.device:
scope.selectEntityText = 'device.select-device';
scope.entityText = 'device.device';
scope.noEntitiesMatchingText = 'device.no-devices-matching';
scope.entityRequiredText = 'device.device-required'
scope.entityRequiredText = 'device.device-required';
break;
case types.entityType.rule:
scope.selectEntityText = 'rule.select-rule';
scope.entityText = 'rule.rule';
scope.noEntitiesMatchingText = 'rule.no-rules-matching';
scope.entityRequiredText = 'rule.rule-required'
scope.entityRequiredText = 'rule.rule-required';
break;
case types.entityType.plugin:
scope.selectEntityText = 'plugin.select-plugin';
scope.entityText = 'plugin.plugin';
scope.noEntitiesMatchingText = 'plugin.no-plugins-matching';
scope.entityRequiredText = 'plugin.plugin-required'
scope.entityRequiredText = 'plugin.plugin-required';
break;
case types.entityType.tenant:
scope.selectEntityText = 'tenant.select-tenant';
scope.entityText = 'tenant.tenant';
scope.noEntitiesMatchingText = 'tenant.no-tenants-matching';
scope.entityRequiredText = 'tenant.tenant-required'
scope.entityRequiredText = 'tenant.tenant-required';
break;
case types.entityType.customer:
scope.selectEntityText = 'customer.select-customer';
scope.entityText = 'customer.customer';
scope.noEntitiesMatchingText = 'customer.no-customers-matching';
scope.entityRequiredText = 'customer.customer-required'
scope.entityRequiredText = 'customer.customer-required';
break;
case types.entityType.user:
scope.selectEntityText = 'user.select-user';
scope.entityText = 'user.user';
scope.noEntitiesMatchingText = 'user.no-users-matching';
scope.entityRequiredText = 'user.user-required'
scope.entityRequiredText = 'user.user-required';
break;
case types.entityType.dashboard:
scope.selectEntityText = 'dashboard.select-dashboard';
scope.entityText = 'dashboard.dashboard';
scope.noEntitiesMatchingText = 'dashboard.no-dashboards-matching';
scope.entityRequiredText = 'dashboard.dashboard-required'
scope.entityRequiredText = 'dashboard.dashboard-required';
break;
case types.entityType.alarm:
scope.selectEntityText = 'alarm.select-alarm';
scope.entityText = 'alarm.alarm';
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;
}
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
the-form="theForm"
tb-required="true"
use-alias-entity-types="true"
ng-model="filter.singleEntity">
</tb-entity-select>
</section>
@ -78,6 +79,7 @@
<tb-entity-select flex
the-form="theForm"
tb-required="false"
use-alias-entity-types="true"
ng-model="filter.defaultStateEntity">
</tb-entity-select>
</div>
@ -123,6 +125,7 @@
the-form="theForm"
tb-required="!filter.rootStateEntity"
ng-disabled="filter.rootStateEntity"
use-alias-entity-types="true"
ng-model="filter.rootEntity">
</tb-entity-select>
</div>
@ -139,6 +142,7 @@
<tb-entity-select flex
the-form="theForm"
tb-required="false"
use-alias-entity-types="true"
ng-model="filter.defaultStateEntity">
</tb-entity-select>
</div>
@ -182,6 +186,7 @@
the-form="theForm"
tb-required="!filter.rootStateEntity"
ng-disabled="filter.rootStateEntity"
use-alias-entity-types="true"
ng-model="filter.rootEntity">
</tb-entity-select>
</div>
@ -198,6 +203,7 @@
<tb-entity-select flex
the-form="theForm"
tb-required="false"
use-alias-entity-types="true"
ng-model="filter.defaultStateEntity">
</tb-entity-select>
</div>
@ -249,6 +255,7 @@
the-form="theForm"
tb-required="!filter.rootStateEntity"
ng-disabled="filter.rootStateEntity"
use-alias-entity-types="true"
ng-model="filter.rootEntity">
</tb-entity-select>
</div>
@ -265,6 +272,7 @@
<tb-entity-select flex
the-form="theForm"
tb-required="false"
use-alias-entity-types="true"
ng-model="filter.defaultStateEntity">
</tb-entity-select>
</div>

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

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

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

@ -20,6 +20,7 @@
the-form="theForm"
ng-disabled="disabled"
tb-required="tbRequired"
use-alias-entity-types="useAliasEntityTypes"
ng-model="model.entityType">
</tb-entity-type-select>
<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.entityTypes = entityService.prepareAllowedEntityTypesList(scope.allowedEntityTypes);
scope.entityTypes = entityService.prepareAllowedEntityTypesList(scope.allowedEntityTypes, scope.useAliasEntityTypes);
scope.typeName = function(type) {
return type ? types.entityTypeTranslations[type].type : '';
@ -79,7 +79,8 @@ export default function EntityTypeSelect($compile, $templateCache, utils, entity
theForm: '=?',
tbRequired: '=?',
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) {
var name = dashboard.title;
name = name.toLowerCase().replace(/\W/g,"_");
exportToPc(prepareExport(dashboard), name + '.json');
exportToPc(prepareDashboardExport(dashboard), name + '.json');
},
function fail(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) {
var deferred = $q.defer();
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",
"select-customer": "Select customer",
"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": {
"date-from": "Date from",
@ -404,6 +407,12 @@ export default angular.module('thingsboard.locale', [])
"unassign-from-customer": "Unassign from customer",
"make-public": "Make dashboard public",
"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-widgets": "No widgets configured",
"add-widget": "Add new widget",
@ -418,7 +427,8 @@ export default angular.module('thingsboard.locale', [])
"add-dashboard-text": "Add new dashboard",
"assign-dashboards": "Assign dashboards",
"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",
"unassign-dashboards": "Unassign dashboards",
"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.",
"select-devices": "Select devices",
"assignedToCustomer": "Assigned to customer",
"assignedToCustomers": "Assigned to customers",
"public": "Public",
"public-link": "Public link",
"copy-public-link": "Copy public link",
@ -735,6 +746,7 @@ export default angular.module('thingsboard.locale', [])
"type-alarms": "Alarms",
"list-of-alarms": "{ count, select, 1 {One alarms} other {List of # alarms} }",
"alarm-name-starts-with": "Alarms whose names start with '{{prefix}}'",
"type-current-customer": "Current Customer",
"search": "Search entities",
"selected-entities": "{ count, select, 1 {1 entity} other {# entities} } selected",
"entity-name": "Entity name",

Loading…
Cancel
Save