Commit 0021f007f1de05bba8e202ebc7c3c3ed0ee4aaa7
1 parent
7645e6f2
Implement multiple customer assigned dashboards UI. Add Current Customer entity …
…type for dashboard aliases.
Showing
39 changed files
with
848 additions
and
497 deletions
... | ... | @@ -434,7 +434,6 @@ public abstract class BaseController { |
434 | 434 | try { |
435 | 435 | validateId(dashboardId, "Incorrect dashboardId " + dashboardId); |
436 | 436 | DashboardInfo dashboardInfo = dashboardService.findDashboardInfoById(dashboardId); |
437 | - SecurityUser authUser = getCurrentUser(); | |
438 | 437 | checkDashboard(dashboardInfo); |
439 | 438 | return dashboardInfo; |
440 | 439 | } catch (Exception e) { |
... | ... | @@ -447,7 +446,7 @@ public abstract class BaseController { |
447 | 446 | checkTenantId(dashboard.getTenantId()); |
448 | 447 | SecurityUser authUser = getCurrentUser(); |
449 | 448 | if (authUser.getAuthority() == Authority.CUSTOMER_USER) { |
450 | - if (dashboard.getAssignedCustomers() == null || !dashboard.getAssignedCustomers().containsKey(authUser.getCustomerId().toString())) { | |
449 | + if (!dashboard.isAssignedToCustomer(authUser.getCustomerId())) { | |
451 | 450 | throw new ThingsboardException(YOU_DON_T_HAVE_PERMISSION_TO_PERFORM_THIS_OPERATION, |
452 | 451 | ThingsboardErrorCode.PERMISSION_DENIED); |
453 | 452 | } | ... | ... |
... | ... | @@ -18,10 +18,7 @@ package org.thingsboard.server.controller; |
18 | 18 | import org.springframework.http.HttpStatus; |
19 | 19 | import org.springframework.security.access.prepost.PreAuthorize; |
20 | 20 | import org.springframework.web.bind.annotation.*; |
21 | -import org.thingsboard.server.common.data.Customer; | |
22 | -import org.thingsboard.server.common.data.Dashboard; | |
23 | -import org.thingsboard.server.common.data.DashboardInfo; | |
24 | -import org.thingsboard.server.common.data.EntityType; | |
21 | +import org.thingsboard.server.common.data.*; | |
25 | 22 | import org.thingsboard.server.common.data.audit.ActionType; |
26 | 23 | import org.thingsboard.server.common.data.id.CustomerId; |
27 | 24 | import org.thingsboard.server.common.data.id.DashboardId; |
... | ... | @@ -34,6 +31,9 @@ import org.thingsboard.server.dao.exception.IncorrectParameterException; |
34 | 31 | import org.thingsboard.server.dao.model.ModelConstants; |
35 | 32 | import org.thingsboard.server.exception.ThingsboardException; |
36 | 33 | |
34 | +import java.util.HashSet; | |
35 | +import java.util.Set; | |
36 | + | |
37 | 37 | @RestController |
38 | 38 | @RequestMapping("/api") |
39 | 39 | public class DashboardController extends BaseController { |
... | ... | @@ -182,6 +182,158 @@ public class DashboardController extends BaseController { |
182 | 182 | } |
183 | 183 | |
184 | 184 | @PreAuthorize("hasAuthority('TENANT_ADMIN')") |
185 | + @RequestMapping(value = "/dashboard/{dashboardId}/customers", method = RequestMethod.POST) | |
186 | + @ResponseBody | |
187 | + public Dashboard updateDashboardCustomers(@PathVariable(DASHBOARD_ID) String strDashboardId, | |
188 | + @RequestBody String[] strCustomerIds) throws ThingsboardException { | |
189 | + checkParameter(DASHBOARD_ID, strDashboardId); | |
190 | + try { | |
191 | + DashboardId dashboardId = new DashboardId(toUUID(strDashboardId)); | |
192 | + Dashboard dashboard = checkDashboardId(dashboardId); | |
193 | + | |
194 | + Set<CustomerId> customerIds = new HashSet<>(); | |
195 | + if (strCustomerIds != null) { | |
196 | + for (String strCustomerId : strCustomerIds) { | |
197 | + customerIds.add(new CustomerId(toUUID(strCustomerId))); | |
198 | + } | |
199 | + } | |
200 | + | |
201 | + Set<CustomerId> addedCustomerIds = new HashSet<>(); | |
202 | + Set<CustomerId> removedCustomerIds = new HashSet<>(); | |
203 | + for (CustomerId customerId : customerIds) { | |
204 | + if (!dashboard.isAssignedToCustomer(customerId)) { | |
205 | + addedCustomerIds.add(customerId); | |
206 | + } | |
207 | + } | |
208 | + | |
209 | + Set<ShortCustomerInfo> assignedCustomers = dashboard.getAssignedCustomers(); | |
210 | + if (assignedCustomers != null) { | |
211 | + for (ShortCustomerInfo customerInfo : assignedCustomers) { | |
212 | + if (!customerIds.contains(customerInfo.getCustomerId())) { | |
213 | + removedCustomerIds.add(customerInfo.getCustomerId()); | |
214 | + } | |
215 | + } | |
216 | + } | |
217 | + | |
218 | + if (addedCustomerIds.isEmpty() && removedCustomerIds.isEmpty()) { | |
219 | + return dashboard; | |
220 | + } else { | |
221 | + Dashboard savedDashboard = null; | |
222 | + for (CustomerId customerId : addedCustomerIds) { | |
223 | + savedDashboard = checkNotNull(dashboardService.assignDashboardToCustomer(dashboardId, customerId)); | |
224 | + ShortCustomerInfo customerInfo = savedDashboard.getAssignedCustomerInfo(customerId); | |
225 | + logEntityAction(dashboardId, savedDashboard, | |
226 | + customerId, | |
227 | + ActionType.ASSIGNED_TO_CUSTOMER, null, strDashboardId, customerId.toString(), customerInfo.getTitle()); | |
228 | + } | |
229 | + for (CustomerId customerId : removedCustomerIds) { | |
230 | + ShortCustomerInfo customerInfo = dashboard.getAssignedCustomerInfo(customerId); | |
231 | + savedDashboard = checkNotNull(dashboardService.unassignDashboardFromCustomer(dashboardId, customerId)); | |
232 | + logEntityAction(dashboardId, dashboard, | |
233 | + customerId, | |
234 | + ActionType.UNASSIGNED_FROM_CUSTOMER, null, strDashboardId, customerId.toString(), customerInfo.getTitle()); | |
235 | + | |
236 | + } | |
237 | + return savedDashboard; | |
238 | + } | |
239 | + } catch (Exception e) { | |
240 | + | |
241 | + logEntityAction(emptyId(EntityType.DASHBOARD), null, | |
242 | + null, | |
243 | + ActionType.ASSIGNED_TO_CUSTOMER, e, strDashboardId); | |
244 | + | |
245 | + throw handleException(e); | |
246 | + } | |
247 | + } | |
248 | + | |
249 | + @PreAuthorize("hasAuthority('TENANT_ADMIN')") | |
250 | + @RequestMapping(value = "/dashboard/{dashboardId}/customers/add", method = RequestMethod.POST) | |
251 | + @ResponseBody | |
252 | + public Dashboard addDashboardCustomers(@PathVariable(DASHBOARD_ID) String strDashboardId, | |
253 | + @RequestBody String[] strCustomerIds) throws ThingsboardException { | |
254 | + checkParameter(DASHBOARD_ID, strDashboardId); | |
255 | + try { | |
256 | + DashboardId dashboardId = new DashboardId(toUUID(strDashboardId)); | |
257 | + Dashboard dashboard = checkDashboardId(dashboardId); | |
258 | + | |
259 | + Set<CustomerId> customerIds = new HashSet<>(); | |
260 | + if (strCustomerIds != null) { | |
261 | + for (String strCustomerId : strCustomerIds) { | |
262 | + CustomerId customerId = new CustomerId(toUUID(strCustomerId)); | |
263 | + if (!dashboard.isAssignedToCustomer(customerId)) { | |
264 | + customerIds.add(customerId); | |
265 | + } | |
266 | + } | |
267 | + } | |
268 | + | |
269 | + if (customerIds.isEmpty()) { | |
270 | + return dashboard; | |
271 | + } else { | |
272 | + Dashboard savedDashboard = null; | |
273 | + for (CustomerId customerId : customerIds) { | |
274 | + savedDashboard = checkNotNull(dashboardService.assignDashboardToCustomer(dashboardId, customerId)); | |
275 | + ShortCustomerInfo customerInfo = savedDashboard.getAssignedCustomerInfo(customerId); | |
276 | + logEntityAction(dashboardId, savedDashboard, | |
277 | + customerId, | |
278 | + ActionType.ASSIGNED_TO_CUSTOMER, null, strDashboardId, customerId.toString(), customerInfo.getTitle()); | |
279 | + } | |
280 | + return savedDashboard; | |
281 | + } | |
282 | + } catch (Exception e) { | |
283 | + | |
284 | + logEntityAction(emptyId(EntityType.DASHBOARD), null, | |
285 | + null, | |
286 | + ActionType.ASSIGNED_TO_CUSTOMER, e, strDashboardId); | |
287 | + | |
288 | + throw handleException(e); | |
289 | + } | |
290 | + } | |
291 | + | |
292 | + @PreAuthorize("hasAuthority('TENANT_ADMIN')") | |
293 | + @RequestMapping(value = "/dashboard/{dashboardId}/customers/remove", method = RequestMethod.POST) | |
294 | + @ResponseBody | |
295 | + public Dashboard removeDashboardCustomers(@PathVariable(DASHBOARD_ID) String strDashboardId, | |
296 | + @RequestBody String[] strCustomerIds) throws ThingsboardException { | |
297 | + checkParameter(DASHBOARD_ID, strDashboardId); | |
298 | + try { | |
299 | + DashboardId dashboardId = new DashboardId(toUUID(strDashboardId)); | |
300 | + Dashboard dashboard = checkDashboardId(dashboardId); | |
301 | + | |
302 | + Set<CustomerId> customerIds = new HashSet<>(); | |
303 | + if (strCustomerIds != null) { | |
304 | + for (String strCustomerId : strCustomerIds) { | |
305 | + CustomerId customerId = new CustomerId(toUUID(strCustomerId)); | |
306 | + if (dashboard.isAssignedToCustomer(customerId)) { | |
307 | + customerIds.add(customerId); | |
308 | + } | |
309 | + } | |
310 | + } | |
311 | + | |
312 | + if (customerIds.isEmpty()) { | |
313 | + return dashboard; | |
314 | + } else { | |
315 | + Dashboard savedDashboard = null; | |
316 | + for (CustomerId customerId : customerIds) { | |
317 | + ShortCustomerInfo customerInfo = dashboard.getAssignedCustomerInfo(customerId); | |
318 | + savedDashboard = checkNotNull(dashboardService.unassignDashboardFromCustomer(dashboardId, customerId)); | |
319 | + logEntityAction(dashboardId, dashboard, | |
320 | + customerId, | |
321 | + ActionType.UNASSIGNED_FROM_CUSTOMER, null, strDashboardId, customerId.toString(), customerInfo.getTitle()); | |
322 | + | |
323 | + } | |
324 | + return savedDashboard; | |
325 | + } | |
326 | + } catch (Exception e) { | |
327 | + | |
328 | + logEntityAction(emptyId(EntityType.DASHBOARD), null, | |
329 | + null, | |
330 | + ActionType.UNASSIGNED_FROM_CUSTOMER, e, strDashboardId); | |
331 | + | |
332 | + throw handleException(e); | |
333 | + } | |
334 | + } | |
335 | + | |
336 | + @PreAuthorize("hasAuthority('TENANT_ADMIN')") | |
185 | 337 | @RequestMapping(value = "/customer/public/dashboard/{dashboardId}", method = RequestMethod.POST) |
186 | 338 | @ResponseBody |
187 | 339 | public Dashboard assignDashboardToPublicCustomer(@PathVariable(DASHBOARD_ID) String strDashboardId) throws ThingsboardException { | ... | ... |
... | ... | @@ -15,12 +15,13 @@ |
15 | 15 | */ |
16 | 16 | package org.thingsboard.server.service.install; |
17 | 17 | |
18 | +import com.fasterxml.jackson.databind.JavaType; | |
18 | 19 | import com.fasterxml.jackson.databind.ObjectMapper; |
19 | 20 | import lombok.extern.slf4j.Slf4j; |
20 | 21 | import org.apache.commons.csv.CSVFormat; |
21 | 22 | import org.apache.commons.csv.CSVParser; |
22 | 23 | import org.apache.commons.lang3.StringUtils; |
23 | -import com.fasterxml.jackson.databind.JsonNode; | |
24 | +import org.thingsboard.server.common.data.ShortCustomerInfo; | |
24 | 25 | import org.thingsboard.server.common.data.UUIDConverter; |
25 | 26 | import org.thingsboard.server.common.data.id.CustomerId; |
26 | 27 | import org.thingsboard.server.common.data.id.DashboardId; |
... | ... | @@ -54,7 +55,8 @@ public class DatabaseHelper { |
54 | 55 | public static final ObjectMapper objectMapper = new ObjectMapper(); |
55 | 56 | |
56 | 57 | public static void upgradeTo40_assignDashboards(Path dashboardsDump, DashboardService dashboardService, boolean sql) throws Exception { |
57 | - String[] columns = new String[]{ID, TENANT_ID, CUSTOMER_ID, TITLE, SEARCH_TEXT, ASSIGNED_CUSTOMERS, CONFIGURATION}; | |
58 | + JavaType assignedCustomersType = | |
59 | + objectMapper.getTypeFactory().constructCollectionType(HashSet.class, ShortCustomerInfo.class); | |
58 | 60 | try (CSVParser csvParser = new CSVParser(Files.newBufferedReader(dashboardsDump), CSV_DUMP_FORMAT.withFirstRecordAsHeader())) { |
59 | 61 | csvParser.forEach(record -> { |
60 | 62 | String customerIdString = record.get(CUSTOMER_ID); |
... | ... | @@ -63,12 +65,11 @@ public class DatabaseHelper { |
63 | 65 | List<CustomerId> customerIds = new ArrayList<>(); |
64 | 66 | if (!StringUtils.isEmpty(assignedCustomersString)) { |
65 | 67 | try { |
66 | - JsonNode assignedCustomersJson = objectMapper.readTree(assignedCustomersString); | |
67 | - Map<String,String> assignedCustomers = objectMapper.treeToValue(assignedCustomersJson, HashMap.class); | |
68 | - assignedCustomers.forEach((strCustomerId, title) -> { | |
69 | - CustomerId customerId = new CustomerId(UUID.fromString(strCustomerId)); | |
68 | + Set<ShortCustomerInfo> assignedCustomers = objectMapper.readValue(assignedCustomersString, assignedCustomersType); | |
69 | + assignedCustomers.forEach((customerInfo) -> { | |
70 | + CustomerId customerId = customerInfo.getCustomerId(); | |
70 | 71 | if (!customerId.isNullUid()) { |
71 | - customerIds.add(new CustomerId(UUID.fromString(strCustomerId))); | |
72 | + customerIds.add(customerId); | |
72 | 73 | } |
73 | 74 | }); |
74 | 75 | } catch (IOException e) { |
... | ... | @@ -78,7 +79,7 @@ public class DatabaseHelper { |
78 | 79 | if (!StringUtils.isEmpty(customerIdString)) { |
79 | 80 | CustomerId customerId = new CustomerId(toUUID(customerIdString, sql)); |
80 | 81 | if (!customerId.isNullUid()) { |
81 | - customerIds.add(new CustomerId(toUUID(customerIdString, sql))); | |
82 | + customerIds.add(customerId); | |
82 | 83 | } |
83 | 84 | } |
84 | 85 | for (CustomerId customerId : customerIds) { | ... | ... |
... | ... | @@ -138,10 +138,10 @@ public abstract class BaseDashboardControllerTest extends AbstractControllerTest |
138 | 138 | Dashboard assignedDashboard = doPost("/api/customer/" + savedCustomer.getId().getId().toString() |
139 | 139 | + "/dashboard/" + savedDashboard.getId().getId().toString(), Dashboard.class); |
140 | 140 | |
141 | - Assert.assertTrue(assignedDashboard.getAssignedCustomers().containsKey(savedCustomer.getId().toString())); | |
141 | + Assert.assertTrue(assignedDashboard.getAssignedCustomers().contains(savedCustomer.toShortCustomerInfo())); | |
142 | 142 | |
143 | 143 | Dashboard foundDashboard = doGet("/api/dashboard/" + savedDashboard.getId().getId().toString(), Dashboard.class); |
144 | - Assert.assertTrue(foundDashboard.getAssignedCustomers().containsKey(savedCustomer.getId().toString())); | |
144 | + Assert.assertTrue(foundDashboard.getAssignedCustomers().contains(savedCustomer.toShortCustomerInfo())); | |
145 | 145 | |
146 | 146 | Dashboard unassignedDashboard = |
147 | 147 | doDelete("/api/customer/"+savedCustomer.getId().getId().toString()+"/dashboard/" + savedDashboard.getId().getId().toString(), Dashboard.class); | ... | ... |
... | ... | @@ -69,6 +69,11 @@ public class Customer extends ContactBased<CustomerId> implements HasName { |
69 | 69 | return false; |
70 | 70 | } |
71 | 71 | |
72 | + @JsonIgnore | |
73 | + public ShortCustomerInfo toShortCustomerInfo() { | |
74 | + return new ShortCustomerInfo(id, title, isPublic()); | |
75 | + } | |
76 | + | |
72 | 77 | @Override |
73 | 78 | @JsonProperty(access = Access.READ_ONLY) |
74 | 79 | public String getName() { | ... | ... |
... | ... | @@ -20,16 +20,13 @@ import org.thingsboard.server.common.data.id.CustomerId; |
20 | 20 | import org.thingsboard.server.common.data.id.DashboardId; |
21 | 21 | import org.thingsboard.server.common.data.id.TenantId; |
22 | 22 | |
23 | -import java.util.HashMap; | |
24 | -import java.util.List; | |
25 | -import java.util.Map; | |
26 | -import java.util.Set; | |
23 | +import java.util.*; | |
27 | 24 | |
28 | 25 | public class DashboardInfo extends SearchTextBased<DashboardId> implements HasName { |
29 | 26 | |
30 | 27 | private TenantId tenantId; |
31 | 28 | private String title; |
32 | - private Map<String, String> assignedCustomers; | |
29 | + private Set<ShortCustomerInfo> assignedCustomers; | |
33 | 30 | |
34 | 31 | public DashboardInfo() { |
35 | 32 | super(); |
... | ... | @@ -62,38 +59,56 @@ public class DashboardInfo extends SearchTextBased<DashboardId> implements HasNa |
62 | 59 | this.title = title; |
63 | 60 | } |
64 | 61 | |
65 | - public Map<String, String> getAssignedCustomers() { | |
62 | + public Set<ShortCustomerInfo> getAssignedCustomers() { | |
66 | 63 | return assignedCustomers; |
67 | 64 | } |
68 | 65 | |
69 | - public void setAssignedCustomers(Map<String, String> assignedCustomers) { | |
66 | + public void setAssignedCustomers(Set<ShortCustomerInfo> assignedCustomers) { | |
70 | 67 | this.assignedCustomers = assignedCustomers; |
71 | 68 | } |
72 | 69 | |
73 | - public boolean addAssignedCustomer(CustomerId customerId, String title) { | |
74 | - if (this.assignedCustomers != null && this.assignedCustomers.containsKey(customerId.toString())) { | |
70 | + public boolean isAssignedToCustomer(CustomerId customerId) { | |
71 | + return this.assignedCustomers != null && this.assignedCustomers.contains(new ShortCustomerInfo(customerId, null, false)); | |
72 | + } | |
73 | + | |
74 | + public ShortCustomerInfo getAssignedCustomerInfo(CustomerId customerId) { | |
75 | + if (this.assignedCustomers != null) { | |
76 | + for (ShortCustomerInfo customerInfo : this.assignedCustomers) { | |
77 | + if (customerInfo.getCustomerId().equals(customerId)) { | |
78 | + return customerInfo; | |
79 | + } | |
80 | + } | |
81 | + } | |
82 | + return null; | |
83 | + } | |
84 | + | |
85 | + public boolean addAssignedCustomer(Customer customer) { | |
86 | + ShortCustomerInfo customerInfo = customer.toShortCustomerInfo(); | |
87 | + if (this.assignedCustomers != null && this.assignedCustomers.contains(customerInfo)) { | |
75 | 88 | return false; |
76 | 89 | } else { |
77 | 90 | if (this.assignedCustomers == null) { |
78 | - this.assignedCustomers = new HashMap<>(); | |
91 | + this.assignedCustomers = new HashSet<>(); | |
79 | 92 | } |
80 | - this.assignedCustomers.put(customerId.toString(), title); | |
93 | + this.assignedCustomers.add(customerInfo); | |
81 | 94 | return true; |
82 | 95 | } |
83 | 96 | } |
84 | 97 | |
85 | - public boolean updateAssignedCustomer(CustomerId customerId, String title) { | |
86 | - if (this.assignedCustomers != null && this.assignedCustomers.containsKey(customerId.toString())) { | |
87 | - this.assignedCustomers.put(customerId.toString(), title); | |
98 | + public boolean updateAssignedCustomer(Customer customer) { | |
99 | + ShortCustomerInfo customerInfo = customer.toShortCustomerInfo(); | |
100 | + if (this.assignedCustomers != null && this.assignedCustomers.contains(customerInfo)) { | |
101 | + this.assignedCustomers.add(customerInfo); | |
88 | 102 | return true; |
89 | 103 | } else { |
90 | 104 | return false; |
91 | 105 | } |
92 | 106 | } |
93 | 107 | |
94 | - public boolean removeAssignedCustomer(CustomerId customerId) { | |
95 | - if (this.assignedCustomers != null && this.assignedCustomers.containsKey(customerId.toString())) { | |
96 | - this.assignedCustomers.remove(customerId.toString()); | |
108 | + public boolean removeAssignedCustomer(Customer customer) { | |
109 | + ShortCustomerInfo customerInfo = customer.toShortCustomerInfo(); | |
110 | + if (this.assignedCustomers != null && this.assignedCustomers.contains(customerInfo)) { | |
111 | + this.assignedCustomers.remove(customerInfo); | |
97 | 112 | return true; |
98 | 113 | } else { |
99 | 114 | return false; | ... | ... |
1 | +/** | |
2 | + * Copyright © 2016-2017 The Thingsboard Authors | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | +package org.thingsboard.server.common.data; | |
17 | + | |
18 | +import lombok.AllArgsConstructor; | |
19 | +import lombok.Getter; | |
20 | +import lombok.Setter; | |
21 | +import org.thingsboard.server.common.data.id.CustomerId; | |
22 | + | |
23 | +/** | |
24 | + * Created by igor on 2/27/18. | |
25 | + */ | |
26 | + | |
27 | +@AllArgsConstructor | |
28 | +public class ShortCustomerInfo { | |
29 | + | |
30 | + @Getter @Setter | |
31 | + private CustomerId customerId; | |
32 | + | |
33 | + @Getter @Setter | |
34 | + private String title; | |
35 | + | |
36 | + @Getter @Setter | |
37 | + private boolean isPublic; | |
38 | + | |
39 | + @Override | |
40 | + public boolean equals(Object o) { | |
41 | + if (this == o) return true; | |
42 | + if (o == null || getClass() != o.getClass()) return false; | |
43 | + | |
44 | + ShortCustomerInfo that = (ShortCustomerInfo) o; | |
45 | + | |
46 | + return customerId.equals(that.customerId); | |
47 | + | |
48 | + } | |
49 | + | |
50 | + @Override | |
51 | + public int hashCode() { | |
52 | + return customerId.hashCode(); | |
53 | + } | |
54 | +} | ... | ... |
... | ... | @@ -98,7 +98,7 @@ public class CustomerServiceImpl extends AbstractEntityService implements Custom |
98 | 98 | log.trace("Executing saveCustomer [{}]", customer); |
99 | 99 | customerValidator.validate(customer); |
100 | 100 | Customer savedCustomer = customerDao.save(customer); |
101 | - dashboardService.updateCustomerDashboards(savedCustomer.getTenantId(), savedCustomer.getId(), savedCustomer.getTitle()); | |
101 | + dashboardService.updateCustomerDashboards(savedCustomer.getId()); | |
102 | 102 | return savedCustomer; |
103 | 103 | } |
104 | 104 | |
... | ... | @@ -110,7 +110,7 @@ public class CustomerServiceImpl extends AbstractEntityService implements Custom |
110 | 110 | if (customer == null) { |
111 | 111 | throw new IncorrectParameterException("Unable to delete non-existent customer."); |
112 | 112 | } |
113 | - dashboardService.unassignCustomerDashboards(customer.getTenantId(), customerId); | |
113 | + dashboardService.unassignCustomerDashboards(customerId); | |
114 | 114 | assetService.unassignCustomerAssets(customer.getTenantId(), customerId); |
115 | 115 | deviceService.unassignCustomerDevices(customer.getTenantId(), customerId); |
116 | 116 | userService.deleteCustomerUsers(customer.getTenantId(), customerId); | ... | ... |
... | ... | @@ -26,7 +26,7 @@ import org.thingsboard.server.common.data.page.TextPageLink; |
26 | 26 | import org.thingsboard.server.common.data.page.TimePageData; |
27 | 27 | import org.thingsboard.server.common.data.page.TimePageLink; |
28 | 28 | |
29 | -import java.sql.Time; | |
29 | +import java.util.Set; | |
30 | 30 | |
31 | 31 | public interface DashboardService { |
32 | 32 | |
... | ... | @@ -52,8 +52,8 @@ public interface DashboardService { |
52 | 52 | |
53 | 53 | ListenableFuture<TimePageData<DashboardInfo>> findDashboardsByTenantIdAndCustomerId(TenantId tenantId, CustomerId customerId, TimePageLink pageLink); |
54 | 54 | |
55 | - void unassignCustomerDashboards(TenantId tenantId, CustomerId customerId); | |
55 | + void unassignCustomerDashboards(CustomerId customerId); | |
56 | 56 | |
57 | - void updateCustomerDashboards(TenantId tenantId, CustomerId customerId, String customerTitle); | |
57 | + void updateCustomerDashboards(CustomerId customerId); | |
58 | 58 | |
59 | 59 | } | ... | ... |
... | ... | @@ -117,7 +117,7 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb |
117 | 117 | if (!customer.getTenantId().getId().equals(dashboard.getTenantId().getId())) { |
118 | 118 | throw new DataValidationException("Can't assign dashboard to customer from different tenant!"); |
119 | 119 | } |
120 | - if (dashboard.addAssignedCustomer(customerId, customer.getTitle())) { | |
120 | + if (dashboard.addAssignedCustomer(customer)) { | |
121 | 121 | try { |
122 | 122 | createRelation(new EntityRelation(customerId, dashboardId, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.DASHBOARD)); |
123 | 123 | } catch (ExecutionException | InterruptedException e) { |
... | ... | @@ -133,7 +133,11 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb |
133 | 133 | @Override |
134 | 134 | public Dashboard unassignDashboardFromCustomer(DashboardId dashboardId, CustomerId customerId) { |
135 | 135 | Dashboard dashboard = findDashboardById(dashboardId); |
136 | - if (dashboard.removeAssignedCustomer(customerId)) { | |
136 | + Customer customer = customerDao.findById(customerId.getId()); | |
137 | + if (customer == null) { | |
138 | + throw new DataValidationException("Can't unassign dashboard from non-existent customer!"); | |
139 | + } | |
140 | + if (dashboard.removeAssignedCustomer(customer)) { | |
137 | 141 | try { |
138 | 142 | deleteRelation(new EntityRelation(customerId, dashboardId, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.DASHBOARD)); |
139 | 143 | } catch (ExecutionException | InterruptedException e) { |
... | ... | @@ -146,9 +150,9 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb |
146 | 150 | } |
147 | 151 | } |
148 | 152 | |
149 | - private Dashboard updateAssignedCustomerTitle(DashboardId dashboardId, CustomerId customerId, String customerTitle) { | |
153 | + private Dashboard updateAssignedCustomer(DashboardId dashboardId, Customer customer) { | |
150 | 154 | Dashboard dashboard = findDashboardById(dashboardId); |
151 | - if (dashboard.updateAssignedCustomer(customerId, customerTitle)) { | |
155 | + if (dashboard.updateAssignedCustomer(customer)) { | |
152 | 156 | return saveDashboard(dashboard); |
153 | 157 | } else { |
154 | 158 | return dashboard; |
... | ... | @@ -207,20 +211,25 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb |
207 | 211 | } |
208 | 212 | |
209 | 213 | @Override |
210 | - public void unassignCustomerDashboards(TenantId tenantId, CustomerId customerId) { | |
211 | - log.trace("Executing unassignCustomerDashboards, tenantId [{}], customerId [{}]", tenantId, customerId); | |
212 | - Validator.validateId(tenantId, INCORRECT_TENANT_ID + tenantId); | |
214 | + public void unassignCustomerDashboards(CustomerId customerId) { | |
215 | + log.trace("Executing unassignCustomerDashboards, customerId [{}]", customerId); | |
213 | 216 | Validator.validateId(customerId, "Incorrect customerId " + customerId); |
214 | - new CustomerDashboardsUnassigner(tenantId, customerId).removeEntities(customerId); | |
217 | + Customer customer = customerDao.findById(customerId.getId()); | |
218 | + if (customer == null) { | |
219 | + throw new DataValidationException("Can't unassign dashboards from non-existent customer!"); | |
220 | + } | |
221 | + new CustomerDashboardsUnassigner(customer).removeEntities(customer); | |
215 | 222 | } |
216 | 223 | |
217 | 224 | @Override |
218 | - public void updateCustomerDashboards(TenantId tenantId, CustomerId customerId, String customerTitle) { | |
219 | - log.trace("Executing updateCustomerDashboards, tenantId [{}], customerId [{}], customerTitle [{}]", tenantId, customerId, customerTitle); | |
220 | - Validator.validateId(tenantId, INCORRECT_TENANT_ID + tenantId); | |
225 | + public void updateCustomerDashboards(CustomerId customerId) { | |
226 | + log.trace("Executing updateCustomerDashboards, customerId [{}]", customerId); | |
221 | 227 | Validator.validateId(customerId, "Incorrect customerId " + customerId); |
222 | - Validator.validateString(customerTitle, "Incorrect customerTitle " + customerTitle); | |
223 | - new CustomerDashboardsUpdater(tenantId, customerId, customerTitle).removeEntities(customerId); | |
228 | + Customer customer = customerDao.findById(customerId.getId()); | |
229 | + if (customer == null) { | |
230 | + throw new DataValidationException("Can't update dashboards for non-existent customer!"); | |
231 | + } | |
232 | + new CustomerDashboardsUpdater(customer).removeEntities(customer); | |
224 | 233 | } |
225 | 234 | |
226 | 235 | private DataValidator<Dashboard> dashboardValidator = |
... | ... | @@ -255,58 +264,52 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb |
255 | 264 | } |
256 | 265 | }; |
257 | 266 | |
258 | - private class CustomerDashboardsUnassigner extends TimePaginatedRemover<CustomerId, DashboardInfo> { | |
259 | - | |
260 | - private TenantId tenantId; | |
261 | - private CustomerId customerId; | |
267 | + private class CustomerDashboardsUnassigner extends TimePaginatedRemover<Customer, DashboardInfo> { | |
262 | 268 | |
263 | - CustomerDashboardsUnassigner(TenantId tenantId, CustomerId customerId) { | |
264 | - this.tenantId = tenantId; | |
265 | - this.customerId = customerId; | |
269 | + private Customer customer; | |
270 | + | |
271 | + CustomerDashboardsUnassigner(Customer customer) { | |
272 | + this.customer = customer; | |
266 | 273 | } |
267 | 274 | |
268 | 275 | @Override |
269 | - protected List<DashboardInfo> findEntities(CustomerId id, TimePageLink pageLink) { | |
276 | + protected List<DashboardInfo> findEntities(Customer customer, TimePageLink pageLink) { | |
270 | 277 | try { |
271 | - return dashboardInfoDao.findDashboardsByTenantIdAndCustomerId(tenantId.getId(), id.getId(), pageLink).get(); | |
278 | + return dashboardInfoDao.findDashboardsByTenantIdAndCustomerId(customer.getTenantId().getId(), customer.getId().getId(), pageLink).get(); | |
272 | 279 | } catch (InterruptedException | ExecutionException e) { |
273 | - log.warn("Failed to get dashboards by tenantId [{}] and customerId [{}].", tenantId, id); | |
280 | + log.warn("Failed to get dashboards by tenantId [{}] and customerId [{}].", customer.getTenantId().getId(), customer.getId().getId()); | |
274 | 281 | throw new RuntimeException(e); |
275 | 282 | } |
276 | 283 | } |
277 | 284 | |
278 | 285 | @Override |
279 | 286 | protected void removeEntity(DashboardInfo entity) { |
280 | - unassignDashboardFromCustomer(new DashboardId(entity.getUuidId()), this.customerId); | |
287 | + unassignDashboardFromCustomer(new DashboardId(entity.getUuidId()), this.customer.getId()); | |
281 | 288 | } |
282 | 289 | |
283 | 290 | } |
284 | 291 | |
285 | - private class CustomerDashboardsUpdater extends TimePaginatedRemover<CustomerId, DashboardInfo> { | |
292 | + private class CustomerDashboardsUpdater extends TimePaginatedRemover<Customer, DashboardInfo> { | |
286 | 293 | |
287 | - private TenantId tenantId; | |
288 | - private CustomerId customerId; | |
289 | - private String customerTitle; | |
294 | + private Customer customer; | |
290 | 295 | |
291 | - CustomerDashboardsUpdater(TenantId tenantId, CustomerId customerId, String customerTitle) { | |
292 | - this.tenantId = tenantId; | |
293 | - this.customerId = customerId; | |
294 | - this.customerTitle = customerTitle; | |
296 | + CustomerDashboardsUpdater(Customer customer) { | |
297 | + this.customer = customer; | |
295 | 298 | } |
296 | 299 | |
297 | 300 | @Override |
298 | - protected List<DashboardInfo> findEntities(CustomerId id, TimePageLink pageLink) { | |
301 | + protected List<DashboardInfo> findEntities(Customer customer, TimePageLink pageLink) { | |
299 | 302 | try { |
300 | - return dashboardInfoDao.findDashboardsByTenantIdAndCustomerId(tenantId.getId(), id.getId(), pageLink).get(); | |
303 | + return dashboardInfoDao.findDashboardsByTenantIdAndCustomerId(customer.getTenantId().getId(), customer.getId().getId(), pageLink).get(); | |
301 | 304 | } catch (InterruptedException | ExecutionException e) { |
302 | - log.warn("Failed to get dashboards by tenantId [{}] and customerId [{}].", tenantId, id); | |
305 | + log.warn("Failed to get dashboards by tenantId [{}] and customerId [{}].", customer.getTenantId().getId(), customer.getId().getId()); | |
303 | 306 | throw new RuntimeException(e); |
304 | 307 | } |
305 | 308 | } |
306 | 309 | |
307 | 310 | @Override |
308 | 311 | protected void removeEntity(DashboardInfo entity) { |
309 | - updateAssignedCustomerTitle(new DashboardId(entity.getUuidId()), this.customerId, this.customerTitle); | |
312 | + updateAssignedCustomer(new DashboardId(entity.getUuidId()), this.customer); | |
310 | 313 | } |
311 | 314 | |
312 | 315 | } | ... | ... |
... | ... | @@ -20,18 +20,22 @@ import com.datastax.driver.mapping.annotations.Column; |
20 | 20 | import com.datastax.driver.mapping.annotations.PartitionKey; |
21 | 21 | import com.datastax.driver.mapping.annotations.Table; |
22 | 22 | import com.fasterxml.jackson.core.JsonProcessingException; |
23 | +import com.fasterxml.jackson.databind.JavaType; | |
23 | 24 | import com.fasterxml.jackson.databind.JsonNode; |
24 | 25 | import com.fasterxml.jackson.databind.ObjectMapper; |
25 | 26 | import lombok.EqualsAndHashCode; |
26 | 27 | import lombok.ToString; |
27 | 28 | import lombok.extern.slf4j.Slf4j; |
29 | +import org.springframework.util.StringUtils; | |
28 | 30 | import org.thingsboard.server.common.data.Dashboard; |
31 | +import org.thingsboard.server.common.data.ShortCustomerInfo; | |
29 | 32 | import org.thingsboard.server.common.data.id.DashboardId; |
30 | 33 | import org.thingsboard.server.common.data.id.TenantId; |
31 | 34 | import org.thingsboard.server.dao.model.SearchTextEntity; |
32 | 35 | import org.thingsboard.server.dao.model.type.JsonCodec; |
33 | 36 | |
34 | -import java.util.HashMap; | |
37 | +import java.io.IOException; | |
38 | +import java.util.HashSet; | |
35 | 39 | import java.util.UUID; |
36 | 40 | |
37 | 41 | import static org.thingsboard.server.dao.model.ModelConstants.*; |
... | ... | @@ -43,6 +47,8 @@ import static org.thingsboard.server.dao.model.ModelConstants.*; |
43 | 47 | public final class DashboardEntity implements SearchTextEntity<Dashboard> { |
44 | 48 | |
45 | 49 | private static final ObjectMapper objectMapper = new ObjectMapper(); |
50 | + private static final JavaType assignedCustomersType = | |
51 | + objectMapper.getTypeFactory().constructCollectionType(HashSet.class, ShortCustomerInfo.class); | |
46 | 52 | |
47 | 53 | @PartitionKey(value = 0) |
48 | 54 | @Column(name = ID_PROPERTY) |
... | ... | @@ -58,8 +64,8 @@ public final class DashboardEntity implements SearchTextEntity<Dashboard> { |
58 | 64 | @Column(name = SEARCH_TEXT_PROPERTY) |
59 | 65 | private String searchText; |
60 | 66 | |
61 | - @Column(name = DASHBOARD_ASSIGNED_CUSTOMERS_PROPERTY, codec = JsonCodec.class) | |
62 | - private JsonNode assignedCustomers; | |
67 | + @Column(name = DASHBOARD_ASSIGNED_CUSTOMERS_PROPERTY) | |
68 | + private String assignedCustomers; | |
63 | 69 | |
64 | 70 | @Column(name = DASHBOARD_CONFIGURATION_PROPERTY, codec = JsonCodec.class) |
65 | 71 | private JsonNode configuration; |
... | ... | @@ -77,7 +83,11 @@ public final class DashboardEntity implements SearchTextEntity<Dashboard> { |
77 | 83 | } |
78 | 84 | this.title = dashboard.getTitle(); |
79 | 85 | if (dashboard.getAssignedCustomers() != null) { |
80 | - this.assignedCustomers = objectMapper.valueToTree(dashboard.getAssignedCustomers()); | |
86 | + try { | |
87 | + this.assignedCustomers = objectMapper.writeValueAsString(dashboard.getAssignedCustomers()); | |
88 | + } catch (JsonProcessingException e) { | |
89 | + log.error("Unable to serialize assigned customers to string!", e); | |
90 | + } | |
81 | 91 | } |
82 | 92 | this.configuration = dashboard.getConfiguration(); |
83 | 93 | } |
... | ... | @@ -106,11 +116,11 @@ public final class DashboardEntity implements SearchTextEntity<Dashboard> { |
106 | 116 | this.title = title; |
107 | 117 | } |
108 | 118 | |
109 | - public JsonNode getAssignedCustomers() { | |
119 | + public String getAssignedCustomers() { | |
110 | 120 | return assignedCustomers; |
111 | 121 | } |
112 | 122 | |
113 | - public void setAssignedCustomers(JsonNode assignedCustomers) { | |
123 | + public void setAssignedCustomers(String assignedCustomers) { | |
114 | 124 | this.assignedCustomers = assignedCustomers; |
115 | 125 | } |
116 | 126 | |
... | ... | @@ -144,10 +154,10 @@ public final class DashboardEntity implements SearchTextEntity<Dashboard> { |
144 | 154 | dashboard.setTenantId(new TenantId(tenantId)); |
145 | 155 | } |
146 | 156 | dashboard.setTitle(title); |
147 | - if (assignedCustomers != null) { | |
157 | + if (!StringUtils.isEmpty(assignedCustomers)) { | |
148 | 158 | try { |
149 | - dashboard.setAssignedCustomers(objectMapper.treeToValue(assignedCustomers, HashMap.class)); | |
150 | - } catch (JsonProcessingException e) { | |
159 | + dashboard.setAssignedCustomers(objectMapper.readValue(assignedCustomers, assignedCustomersType)); | |
160 | + } catch (IOException e) { | |
151 | 161 | log.warn("Unable to parse assigned customers!", e); |
152 | 162 | } |
153 | 163 | } | ... | ... |
... | ... | @@ -20,20 +20,20 @@ import com.datastax.driver.mapping.annotations.Column; |
20 | 20 | import com.datastax.driver.mapping.annotations.PartitionKey; |
21 | 21 | import com.datastax.driver.mapping.annotations.Table; |
22 | 22 | import com.fasterxml.jackson.core.JsonProcessingException; |
23 | -import com.fasterxml.jackson.databind.JsonNode; | |
23 | +import com.fasterxml.jackson.databind.JavaType; | |
24 | 24 | import com.fasterxml.jackson.databind.ObjectMapper; |
25 | 25 | import lombok.EqualsAndHashCode; |
26 | 26 | import lombok.ToString; |
27 | 27 | import lombok.extern.slf4j.Slf4j; |
28 | +import org.springframework.util.StringUtils; | |
28 | 29 | import org.thingsboard.server.common.data.DashboardInfo; |
29 | -import org.thingsboard.server.common.data.id.CustomerId; | |
30 | +import org.thingsboard.server.common.data.ShortCustomerInfo; | |
30 | 31 | import org.thingsboard.server.common.data.id.DashboardId; |
31 | 32 | import org.thingsboard.server.common.data.id.TenantId; |
32 | 33 | import org.thingsboard.server.dao.model.SearchTextEntity; |
33 | -import org.thingsboard.server.dao.model.type.JsonCodec; | |
34 | 34 | |
35 | -import java.util.HashMap; | |
36 | -import java.util.Set; | |
35 | +import java.io.IOException; | |
36 | +import java.util.HashSet; | |
37 | 37 | import java.util.UUID; |
38 | 38 | |
39 | 39 | import static org.thingsboard.server.dao.model.ModelConstants.*; |
... | ... | @@ -45,6 +45,8 @@ import static org.thingsboard.server.dao.model.ModelConstants.*; |
45 | 45 | public class DashboardInfoEntity implements SearchTextEntity<DashboardInfo> { |
46 | 46 | |
47 | 47 | private static final ObjectMapper objectMapper = new ObjectMapper(); |
48 | + private static final JavaType assignedCustomersType = | |
49 | + objectMapper.getTypeFactory().constructCollectionType(HashSet.class, ShortCustomerInfo.class); | |
48 | 50 | |
49 | 51 | @PartitionKey(value = 0) |
50 | 52 | @Column(name = ID_PROPERTY) |
... | ... | @@ -60,8 +62,8 @@ public class DashboardInfoEntity implements SearchTextEntity<DashboardInfo> { |
60 | 62 | @Column(name = SEARCH_TEXT_PROPERTY) |
61 | 63 | private String searchText; |
62 | 64 | |
63 | - @Column(name = DASHBOARD_ASSIGNED_CUSTOMERS_PROPERTY, codec = JsonCodec.class) | |
64 | - private JsonNode assignedCustomers; | |
65 | + @Column(name = DASHBOARD_ASSIGNED_CUSTOMERS_PROPERTY) | |
66 | + private String assignedCustomers; | |
65 | 67 | |
66 | 68 | public DashboardInfoEntity() { |
67 | 69 | super(); |
... | ... | @@ -76,7 +78,11 @@ public class DashboardInfoEntity implements SearchTextEntity<DashboardInfo> { |
76 | 78 | } |
77 | 79 | this.title = dashboardInfo.getTitle(); |
78 | 80 | if (dashboardInfo.getAssignedCustomers() != null) { |
79 | - this.assignedCustomers = objectMapper.valueToTree(dashboardInfo.getAssignedCustomers()); | |
81 | + try { | |
82 | + this.assignedCustomers = objectMapper.writeValueAsString(dashboardInfo.getAssignedCustomers()); | |
83 | + } catch (JsonProcessingException e) { | |
84 | + log.error("Unable to serialize assigned customers to string!", e); | |
85 | + } | |
80 | 86 | } |
81 | 87 | } |
82 | 88 | |
... | ... | @@ -104,11 +110,11 @@ public class DashboardInfoEntity implements SearchTextEntity<DashboardInfo> { |
104 | 110 | this.title = title; |
105 | 111 | } |
106 | 112 | |
107 | - public JsonNode getAssignedCustomers() { | |
113 | + public String getAssignedCustomers() { | |
108 | 114 | return assignedCustomers; |
109 | 115 | } |
110 | 116 | |
111 | - public void setAssignedCustomers(JsonNode assignedCustomers) { | |
117 | + public void setAssignedCustomers(String assignedCustomers) { | |
112 | 118 | this.assignedCustomers = assignedCustomers; |
113 | 119 | } |
114 | 120 | |
... | ... | @@ -134,10 +140,10 @@ public class DashboardInfoEntity implements SearchTextEntity<DashboardInfo> { |
134 | 140 | dashboardInfo.setTenantId(new TenantId(tenantId)); |
135 | 141 | } |
136 | 142 | dashboardInfo.setTitle(title); |
137 | - if (assignedCustomers != null) { | |
143 | + if (!StringUtils.isEmpty(assignedCustomers)) { | |
138 | 144 | try { |
139 | - dashboardInfo.setAssignedCustomers(objectMapper.treeToValue(assignedCustomers, HashMap.class)); | |
140 | - } catch (JsonProcessingException e) { | |
145 | + dashboardInfo.setAssignedCustomers(objectMapper.readValue(assignedCustomers, assignedCustomersType)); | |
146 | + } catch (IOException e) { | |
141 | 147 | log.warn("Unable to parse assigned customers!", e); |
142 | 148 | } |
143 | 149 | } | ... | ... |
... | ... | @@ -17,6 +17,7 @@ package org.thingsboard.server.dao.model.sql; |
17 | 17 | |
18 | 18 | import com.datastax.driver.core.utils.UUIDs; |
19 | 19 | import com.fasterxml.jackson.core.JsonProcessingException; |
20 | +import com.fasterxml.jackson.databind.JavaType; | |
20 | 21 | import com.fasterxml.jackson.databind.JsonNode; |
21 | 22 | import com.fasterxml.jackson.databind.ObjectMapper; |
22 | 23 | import lombok.Data; |
... | ... | @@ -24,8 +25,9 @@ import lombok.EqualsAndHashCode; |
24 | 25 | import lombok.extern.slf4j.Slf4j; |
25 | 26 | import org.hibernate.annotations.Type; |
26 | 27 | import org.hibernate.annotations.TypeDef; |
28 | +import org.springframework.util.StringUtils; | |
27 | 29 | import org.thingsboard.server.common.data.Dashboard; |
28 | -import org.thingsboard.server.common.data.id.CustomerId; | |
30 | +import org.thingsboard.server.common.data.ShortCustomerInfo; | |
29 | 31 | import org.thingsboard.server.common.data.id.DashboardId; |
30 | 32 | import org.thingsboard.server.common.data.id.TenantId; |
31 | 33 | import org.thingsboard.server.dao.model.BaseSqlEntity; |
... | ... | @@ -36,9 +38,8 @@ import org.thingsboard.server.dao.util.mapping.JsonStringType; |
36 | 38 | import javax.persistence.Column; |
37 | 39 | import javax.persistence.Entity; |
38 | 40 | import javax.persistence.Table; |
39 | -import java.util.HashMap; | |
40 | -import java.util.List; | |
41 | -import java.util.Set; | |
41 | +import java.io.IOException; | |
42 | +import java.util.HashSet; | |
42 | 43 | |
43 | 44 | @Data |
44 | 45 | @Slf4j |
... | ... | @@ -49,6 +50,8 @@ import java.util.Set; |
49 | 50 | public final class DashboardEntity extends BaseSqlEntity<Dashboard> implements SearchTextEntity<Dashboard> { |
50 | 51 | |
51 | 52 | private static final ObjectMapper objectMapper = new ObjectMapper(); |
53 | + private static final JavaType assignedCustomersType = | |
54 | + objectMapper.getTypeFactory().constructCollectionType(HashSet.class, ShortCustomerInfo.class); | |
52 | 55 | |
53 | 56 | @Column(name = ModelConstants.DASHBOARD_TENANT_ID_PROPERTY) |
54 | 57 | private String tenantId; |
... | ... | @@ -59,9 +62,8 @@ public final class DashboardEntity extends BaseSqlEntity<Dashboard> implements S |
59 | 62 | @Column(name = ModelConstants.SEARCH_TEXT_PROPERTY) |
60 | 63 | private String searchText; |
61 | 64 | |
62 | - @Type(type = "json") | |
63 | 65 | @Column(name = ModelConstants.DASHBOARD_ASSIGNED_CUSTOMERS_PROPERTY) |
64 | - private JsonNode assignedCustomers; | |
66 | + private String assignedCustomers; | |
65 | 67 | |
66 | 68 | @Type(type = "json") |
67 | 69 | @Column(name = ModelConstants.DASHBOARD_CONFIGURATION_PROPERTY) |
... | ... | @@ -80,7 +82,11 @@ public final class DashboardEntity extends BaseSqlEntity<Dashboard> implements S |
80 | 82 | } |
81 | 83 | this.title = dashboard.getTitle(); |
82 | 84 | if (dashboard.getAssignedCustomers() != null) { |
83 | - this.assignedCustomers = objectMapper.valueToTree(dashboard.getAssignedCustomers()); | |
85 | + try { | |
86 | + this.assignedCustomers = objectMapper.writeValueAsString(dashboard.getAssignedCustomers()); | |
87 | + } catch (JsonProcessingException e) { | |
88 | + log.error("Unable to serialize assigned customers to string!", e); | |
89 | + } | |
84 | 90 | } |
85 | 91 | this.configuration = dashboard.getConfiguration(); |
86 | 92 | } |
... | ... | @@ -103,10 +109,10 @@ public final class DashboardEntity extends BaseSqlEntity<Dashboard> implements S |
103 | 109 | dashboard.setTenantId(new TenantId(toUUID(tenantId))); |
104 | 110 | } |
105 | 111 | dashboard.setTitle(title); |
106 | - if (assignedCustomers != null) { | |
112 | + if (!StringUtils.isEmpty(assignedCustomers)) { | |
107 | 113 | try { |
108 | - dashboard.setAssignedCustomers(objectMapper.treeToValue(assignedCustomers, HashMap.class)); | |
109 | - } catch (JsonProcessingException e) { | |
114 | + dashboard.setAssignedCustomers(objectMapper.readValue(assignedCustomers, assignedCustomersType)); | |
115 | + } catch (IOException e) { | |
110 | 116 | log.warn("Unable to parse assigned customers!", e); |
111 | 117 | } |
112 | 118 | } | ... | ... |
... | ... | @@ -17,14 +17,14 @@ package org.thingsboard.server.dao.model.sql; |
17 | 17 | |
18 | 18 | import com.datastax.driver.core.utils.UUIDs; |
19 | 19 | import com.fasterxml.jackson.core.JsonProcessingException; |
20 | -import com.fasterxml.jackson.databind.JsonNode; | |
20 | +import com.fasterxml.jackson.databind.JavaType; | |
21 | 21 | import com.fasterxml.jackson.databind.ObjectMapper; |
22 | 22 | import lombok.Data; |
23 | 23 | import lombok.EqualsAndHashCode; |
24 | 24 | import lombok.extern.slf4j.Slf4j; |
25 | -import org.hibernate.annotations.Type; | |
25 | +import org.springframework.util.StringUtils; | |
26 | 26 | import org.thingsboard.server.common.data.DashboardInfo; |
27 | -import org.thingsboard.server.common.data.id.CustomerId; | |
27 | +import org.thingsboard.server.common.data.ShortCustomerInfo; | |
28 | 28 | import org.thingsboard.server.common.data.id.DashboardId; |
29 | 29 | import org.thingsboard.server.common.data.id.TenantId; |
30 | 30 | import org.thingsboard.server.dao.model.BaseSqlEntity; |
... | ... | @@ -34,8 +34,8 @@ import org.thingsboard.server.dao.model.SearchTextEntity; |
34 | 34 | import javax.persistence.Column; |
35 | 35 | import javax.persistence.Entity; |
36 | 36 | import javax.persistence.Table; |
37 | -import java.util.HashMap; | |
38 | -import java.util.Set; | |
37 | +import java.io.IOException; | |
38 | +import java.util.HashSet; | |
39 | 39 | |
40 | 40 | @Data |
41 | 41 | @Slf4j |
... | ... | @@ -45,6 +45,8 @@ import java.util.Set; |
45 | 45 | public class DashboardInfoEntity extends BaseSqlEntity<DashboardInfo> implements SearchTextEntity<DashboardInfo> { |
46 | 46 | |
47 | 47 | private static final ObjectMapper objectMapper = new ObjectMapper(); |
48 | + private static final JavaType assignedCustomersType = | |
49 | + objectMapper.getTypeFactory().constructCollectionType(HashSet.class, ShortCustomerInfo.class); | |
48 | 50 | |
49 | 51 | @Column(name = ModelConstants.DASHBOARD_TENANT_ID_PROPERTY) |
50 | 52 | private String tenantId; |
... | ... | @@ -55,9 +57,8 @@ public class DashboardInfoEntity extends BaseSqlEntity<DashboardInfo> implements |
55 | 57 | @Column(name = ModelConstants.SEARCH_TEXT_PROPERTY) |
56 | 58 | private String searchText; |
57 | 59 | |
58 | - @Type(type = "json") | |
59 | 60 | @Column(name = ModelConstants.DASHBOARD_ASSIGNED_CUSTOMERS_PROPERTY) |
60 | - private JsonNode assignedCustomers; | |
61 | + private String assignedCustomers; | |
61 | 62 | |
62 | 63 | public DashboardInfoEntity() { |
63 | 64 | super(); |
... | ... | @@ -72,7 +73,11 @@ public class DashboardInfoEntity extends BaseSqlEntity<DashboardInfo> implements |
72 | 73 | } |
73 | 74 | this.title = dashboardInfo.getTitle(); |
74 | 75 | if (dashboardInfo.getAssignedCustomers() != null) { |
75 | - this.assignedCustomers = objectMapper.valueToTree(dashboardInfo.getAssignedCustomers()); | |
76 | + try { | |
77 | + this.assignedCustomers = objectMapper.writeValueAsString(dashboardInfo.getAssignedCustomers()); | |
78 | + } catch (JsonProcessingException e) { | |
79 | + log.error("Unable to serialize assigned customers to string!", e); | |
80 | + } | |
76 | 81 | } |
77 | 82 | } |
78 | 83 | |
... | ... | @@ -98,10 +103,10 @@ public class DashboardInfoEntity extends BaseSqlEntity<DashboardInfo> implements |
98 | 103 | dashboardInfo.setTenantId(new TenantId(toUUID(tenantId))); |
99 | 104 | } |
100 | 105 | dashboardInfo.setTitle(title); |
101 | - if (assignedCustomers != null) { | |
106 | + if (!StringUtils.isEmpty(assignedCustomers)) { | |
102 | 107 | try { |
103 | - dashboardInfo.setAssignedCustomers(objectMapper.treeToValue(assignedCustomers, HashMap.class)); | |
104 | - } catch (JsonProcessingException e) { | |
108 | + dashboardInfo.setAssignedCustomers(objectMapper.readValue(assignedCustomers, assignedCustomersType)); | |
109 | + } catch (IOException e) { | |
105 | 110 | log.warn("Unable to parse assigned customers!", e); |
106 | 111 | } |
107 | 112 | } | ... | ... |
... | ... | @@ -320,7 +320,7 @@ public abstract class BaseDashboardServiceTest extends AbstractServiceTest { |
320 | 320 | |
321 | 321 | Assert.assertEquals(dashboards, loadedDashboards); |
322 | 322 | |
323 | - dashboardService.unassignCustomerDashboards(tenantId, customerId); | |
323 | + dashboardService.unassignCustomerDashboards(customerId); | |
324 | 324 | |
325 | 325 | pageLink = new TimePageLink(42); |
326 | 326 | pageData = dashboardService.findDashboardsByTenantIdAndCustomerId(tenantId, customerId, pageLink).get(); | ... | ... |
... | ... | @@ -17,7 +17,7 @@ export default angular.module('thingsboard.api.dashboard', []) |
17 | 17 | .factory('dashboardService', DashboardService).name; |
18 | 18 | |
19 | 19 | /*@ngInject*/ |
20 | -function DashboardService($rootScope, $http, $q, $location, customerService) { | |
20 | +function DashboardService($rootScope, $http, $q, $location, $filter) { | |
21 | 21 | |
22 | 22 | var stDiffPromise; |
23 | 23 | |
... | ... | @@ -37,7 +37,11 @@ function DashboardService($rootScope, $http, $q, $location, customerService) { |
37 | 37 | deleteDashboard: deleteDashboard, |
38 | 38 | saveDashboard: saveDashboard, |
39 | 39 | unassignDashboardFromCustomer: unassignDashboardFromCustomer, |
40 | + updateDashboardCustomers: updateDashboardCustomers, | |
41 | + addDashboardCustomers: addDashboardCustomers, | |
42 | + removeDashboardCustomers: removeDashboardCustomers, | |
40 | 43 | makeDashboardPublic: makeDashboardPublic, |
44 | + makeDashboardPrivate: makeDashboardPrivate, | |
41 | 45 | getPublicDashboardLink: getPublicDashboardLink |
42 | 46 | } |
43 | 47 | |
... | ... | @@ -56,14 +60,14 @@ function DashboardService($rootScope, $http, $q, $location, customerService) { |
56 | 60 | url += '&textOffset=' + pageLink.textOffset; |
57 | 61 | } |
58 | 62 | $http.get(url, config).then(function success(response) { |
59 | - deferred.resolve(response.data); | |
63 | + deferred.resolve(prepareDashboards(response.data)); | |
60 | 64 | }, function fail() { |
61 | 65 | deferred.reject(); |
62 | 66 | }); |
63 | 67 | return deferred.promise; |
64 | 68 | } |
65 | 69 | |
66 | - function getTenantDashboards(pageLink, applyCustomersInfo, config) { | |
70 | + function getTenantDashboards(pageLink, config) { | |
67 | 71 | var deferred = $q.defer(); |
68 | 72 | var url = '/api/tenant/dashboards?limit=' + pageLink.limit; |
69 | 73 | if (angular.isDefined(pageLink.textSearch)) { |
... | ... | @@ -76,51 +80,25 @@ function DashboardService($rootScope, $http, $q, $location, customerService) { |
76 | 80 | url += '&textOffset=' + pageLink.textOffset; |
77 | 81 | } |
78 | 82 | $http.get(url, config).then(function success(response) { |
79 | - if (applyCustomersInfo) { | |
80 | - customerService.applyAssignedCustomersInfo(response.data.data).then( | |
81 | - function success(data) { | |
82 | - response.data.data = data; | |
83 | - deferred.resolve(response.data); | |
84 | - }, | |
85 | - function fail() { | |
86 | - deferred.reject(); | |
87 | - } | |
88 | - ); | |
89 | - } else { | |
90 | - deferred.resolve(response.data); | |
91 | - } | |
83 | + deferred.resolve(prepareDashboards(response.data)); | |
92 | 84 | }, function fail() { |
93 | 85 | deferred.reject(); |
94 | 86 | }); |
95 | 87 | return deferred.promise; |
96 | 88 | } |
97 | 89 | |
98 | - function getCustomerDashboards(customerId, pageLink, applyCustomersInfo, config) { | |
90 | + function getCustomerDashboards(customerId, pageLink, config) { | |
99 | 91 | var deferred = $q.defer(); |
100 | 92 | var url = '/api/customer/' + customerId + '/dashboards?limit=' + pageLink.limit; |
101 | - if (angular.isDefined(pageLink.textSearch)) { | |
102 | - url += '&textSearch=' + pageLink.textSearch; | |
103 | - } | |
104 | 93 | if (angular.isDefined(pageLink.idOffset)) { |
105 | - url += '&idOffset=' + pageLink.idOffset; | |
106 | - } | |
107 | - if (angular.isDefined(pageLink.textOffset)) { | |
108 | - url += '&textOffset=' + pageLink.textOffset; | |
94 | + url += '&offset=' + pageLink.idOffset; | |
109 | 95 | } |
110 | 96 | $http.get(url, config).then(function success(response) { |
111 | - if (applyCustomersInfo) { | |
112 | - customerService.applyAssignedCustomerInfo(response.data.data, customerId).then( | |
113 | - function success(data) { | |
114 | - response.data.data = data; | |
115 | - deferred.resolve(response.data); | |
116 | - }, | |
117 | - function fail() { | |
118 | - deferred.reject(); | |
119 | - } | |
120 | - ); | |
121 | - } else { | |
122 | - deferred.resolve(response.data); | |
97 | + response.data = prepareDashboards(response.data); | |
98 | + if (pageLink.textSearch) { | |
99 | + response.data.data = $filter('filter')(response.data.data, {title: pageLink.textSearch}); | |
123 | 100 | } |
101 | + deferred.resolve(response.data); | |
124 | 102 | }, function fail() { |
125 | 103 | deferred.reject(); |
126 | 104 | }); |
... | ... | @@ -151,7 +129,7 @@ function DashboardService($rootScope, $http, $q, $location, customerService) { |
151 | 129 | var deferred = $q.defer(); |
152 | 130 | var url = '/api/dashboard/' + dashboardId; |
153 | 131 | $http.get(url, null).then(function success(response) { |
154 | - deferred.resolve(response.data); | |
132 | + deferred.resolve(prepareDashboard(response.data)); | |
155 | 133 | }, function fail() { |
156 | 134 | deferred.reject(); |
157 | 135 | }); |
... | ... | @@ -162,7 +140,7 @@ function DashboardService($rootScope, $http, $q, $location, customerService) { |
162 | 140 | var deferred = $q.defer(); |
163 | 141 | var url = '/api/dashboard/info/' + dashboardId; |
164 | 142 | $http.get(url, config).then(function success(response) { |
165 | - deferred.resolve(response.data); | |
143 | + deferred.resolve(prepareDashboard(response.data)); | |
166 | 144 | }, function fail() { |
167 | 145 | deferred.reject(); |
168 | 146 | }); |
... | ... | @@ -172,8 +150,8 @@ function DashboardService($rootScope, $http, $q, $location, customerService) { |
172 | 150 | function saveDashboard(dashboard) { |
173 | 151 | var deferred = $q.defer(); |
174 | 152 | var url = '/api/dashboard'; |
175 | - $http.post(url, dashboard).then(function success(response) { | |
176 | - deferred.resolve(response.data); | |
153 | + $http.post(url, cleanDashboard(dashboard)).then(function success(response) { | |
154 | + deferred.resolve(prepareDashboard(response.data)); | |
177 | 155 | }, function fail() { |
178 | 156 | deferred.reject(); |
179 | 157 | }); |
... | ... | @@ -195,18 +173,51 @@ function DashboardService($rootScope, $http, $q, $location, customerService) { |
195 | 173 | var deferred = $q.defer(); |
196 | 174 | var url = '/api/customer/' + customerId + '/dashboard/' + dashboardId; |
197 | 175 | $http.post(url, null).then(function success(response) { |
198 | - deferred.resolve(response.data); | |
176 | + deferred.resolve(prepareDashboard(response.data)); | |
199 | 177 | }, function fail() { |
200 | 178 | deferred.reject(); |
201 | 179 | }); |
202 | 180 | return deferred.promise; |
203 | 181 | } |
204 | 182 | |
205 | - function unassignDashboardFromCustomer(dashboardId) { | |
183 | + function unassignDashboardFromCustomer(customerId, dashboardId) { | |
206 | 184 | var deferred = $q.defer(); |
207 | - var url = '/api/customer/dashboard/' + dashboardId; | |
185 | + var url = '/api/customer/' + customerId + '/dashboard/' + dashboardId; | |
208 | 186 | $http.delete(url).then(function success(response) { |
209 | - deferred.resolve(response.data); | |
187 | + deferred.resolve(prepareDashboard(response.data)); | |
188 | + }, function fail() { | |
189 | + deferred.reject(); | |
190 | + }); | |
191 | + return deferred.promise; | |
192 | + } | |
193 | + | |
194 | + function updateDashboardCustomers(dashboardId, customerIds) { | |
195 | + var deferred = $q.defer(); | |
196 | + var url = '/api/dashboard/' + dashboardId + '/customers'; | |
197 | + $http.post(url, customerIds).then(function success(response) { | |
198 | + deferred.resolve(prepareDashboard(response.data)); | |
199 | + }, function fail() { | |
200 | + deferred.reject(); | |
201 | + }); | |
202 | + return deferred.promise; | |
203 | + } | |
204 | + | |
205 | + function addDashboardCustomers(dashboardId, customerIds) { | |
206 | + var deferred = $q.defer(); | |
207 | + var url = '/api/dashboard/' + dashboardId + '/customers/add'; | |
208 | + $http.post(url, customerIds).then(function success(response) { | |
209 | + deferred.resolve(prepareDashboard(response.data)); | |
210 | + }, function fail() { | |
211 | + deferred.reject(); | |
212 | + }); | |
213 | + return deferred.promise; | |
214 | + } | |
215 | + | |
216 | + function removeDashboardCustomers(dashboardId, customerIds) { | |
217 | + var deferred = $q.defer(); | |
218 | + var url = '/api/dashboard/' + dashboardId + '/customers/remove'; | |
219 | + $http.post(url, customerIds).then(function success(response) { | |
220 | + deferred.resolve(prepareDashboard(response.data)); | |
210 | 221 | }, function fail() { |
211 | 222 | deferred.reject(); |
212 | 223 | }); |
... | ... | @@ -217,7 +228,18 @@ function DashboardService($rootScope, $http, $q, $location, customerService) { |
217 | 228 | var deferred = $q.defer(); |
218 | 229 | var url = '/api/customer/public/dashboard/' + dashboardId; |
219 | 230 | $http.post(url, null).then(function success(response) { |
220 | - deferred.resolve(response.data); | |
231 | + deferred.resolve(prepareDashboard(response.data)); | |
232 | + }, function fail() { | |
233 | + deferred.reject(); | |
234 | + }); | |
235 | + return deferred.promise; | |
236 | + } | |
237 | + | |
238 | + function makeDashboardPrivate(dashboardId) { | |
239 | + var deferred = $q.defer(); | |
240 | + var url = '/api/customer/public/dashboard/' + dashboardId; | |
241 | + $http.delete(url).then(function success(response) { | |
242 | + deferred.resolve(prepareDashboard(response.data)); | |
221 | 243 | }, function fail() { |
222 | 244 | deferred.reject(); |
223 | 245 | }); |
... | ... | @@ -230,8 +252,44 @@ function DashboardService($rootScope, $http, $q, $location, customerService) { |
230 | 252 | if (port != 80 && port != 443) { |
231 | 253 | url += ":" + port; |
232 | 254 | } |
233 | - url += "/dashboards/" + dashboard.id.id + "?publicId=" + dashboard.customerId.id; | |
255 | + url += "/dashboards/" + dashboard.id.id + "?publicId=" + dashboard.publicCustomerId; | |
234 | 256 | return url; |
235 | 257 | } |
236 | 258 | |
259 | + function prepareDashboards(dashboardsData) { | |
260 | + if (dashboardsData.data) { | |
261 | + for (var i = 0; i < dashboardsData.data.length; i++) { | |
262 | + dashboardsData.data[i] = prepareDashboard(dashboardsData.data[i]); | |
263 | + } | |
264 | + } | |
265 | + return dashboardsData; | |
266 | + } | |
267 | + | |
268 | + function prepareDashboard(dashboard) { | |
269 | + dashboard.publicCustomerId = null; | |
270 | + dashboard.assignedCustomersText = ""; | |
271 | + dashboard.assignedCustomersIds = []; | |
272 | + if (dashboard.assignedCustomers && dashboard.assignedCustomers.length) { | |
273 | + var assignedCustomersTitles = []; | |
274 | + for (var i = 0; i < dashboard.assignedCustomers.length; i++) { | |
275 | + var assignedCustomer = dashboard.assignedCustomers[i]; | |
276 | + dashboard.assignedCustomersIds.push(assignedCustomer.customerId.id); | |
277 | + if (assignedCustomer.public) { | |
278 | + dashboard.publicCustomerId = assignedCustomer.customerId.id; | |
279 | + } else { | |
280 | + assignedCustomersTitles.push(assignedCustomer.title); | |
281 | + } | |
282 | + } | |
283 | + dashboard.assignedCustomersText = assignedCustomersTitles.join(', '); | |
284 | + } | |
285 | + return dashboard; | |
286 | + } | |
287 | + | |
288 | + function cleanDashboard(dashboard) { | |
289 | + delete dashboard.publicCustomerId; | |
290 | + delete dashboard.assignedCustomersText; | |
291 | + delete dashboard.assignedCustomersIds; | |
292 | + return dashboard; | |
293 | + } | |
294 | + | |
237 | 295 | } | ... | ... |
... | ... | @@ -273,9 +273,9 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device |
273 | 273 | break; |
274 | 274 | case types.entityType.dashboard: |
275 | 275 | if (user.authority === 'CUSTOMER_USER') { |
276 | - promise = dashboardService.getCustomerDashboards(customerId, pageLink, false, config); | |
276 | + promise = dashboardService.getCustomerDashboards(customerId, pageLink, config); | |
277 | 277 | } else { |
278 | - promise = dashboardService.getTenantDashboards(pageLink, false, config); | |
278 | + promise = dashboardService.getTenantDashboards(pageLink, config); | |
279 | 279 | } |
280 | 280 | break; |
281 | 281 | case types.entityType.user: |
... | ... | @@ -403,6 +403,21 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device |
403 | 403 | return deferred.promise; |
404 | 404 | } |
405 | 405 | |
406 | + function resolveAliasEntityId(entityType, id) { | |
407 | + var entityId = { | |
408 | + entityType: entityType, | |
409 | + id: id | |
410 | + }; | |
411 | + if (entityType == types.aliasEntityType.current_customer) { | |
412 | + var user = userService.getCurrentUser(); | |
413 | + entityId.entityType = types.entityType.customer; | |
414 | + if (user.authority === 'CUSTOMER_USER') { | |
415 | + entityId.id = user.customerId; | |
416 | + } | |
417 | + } | |
418 | + return entityId; | |
419 | + } | |
420 | + | |
406 | 421 | function getStateEntityId(filter, stateParams) { |
407 | 422 | var entityId = null; |
408 | 423 | if (stateParams) { |
... | ... | @@ -417,6 +432,9 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device |
417 | 432 | if (!entityId) { |
418 | 433 | entityId = filter.defaultStateEntity; |
419 | 434 | } |
435 | + if (entityId) { | |
436 | + entityId = resolveAliasEntityId(entityId.entityType, entityId.id); | |
437 | + } | |
420 | 438 | return entityId; |
421 | 439 | } |
422 | 440 | |
... | ... | @@ -432,7 +450,8 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device |
432 | 450 | var stateEntityId = getStateEntityId(filter, stateParams); |
433 | 451 | switch (filter.type) { |
434 | 452 | case types.aliasFilterType.singleEntity.value: |
435 | - getEntity(filter.singleEntity.entityType, filter.singleEntity.id, {ignoreLoading: true}).then( | |
453 | + var aliasEntityId = resolveAliasEntityId(filter.singleEntity.entityType, filter.singleEntity.id); | |
454 | + getEntity(aliasEntityId.entityType, aliasEntityId.id, {ignoreLoading: true}).then( | |
436 | 455 | function success(entity) { |
437 | 456 | result.entities = entitiesToEntitiesInfo([entity]); |
438 | 457 | deferred.resolve(result); |
... | ... | @@ -530,10 +549,11 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device |
530 | 549 | rootEntityId = filter.rootEntity.id; |
531 | 550 | } |
532 | 551 | if (rootEntityType && rootEntityId) { |
552 | + var relationQueryRootEntityId = resolveAliasEntityId(rootEntityType, rootEntityId); | |
533 | 553 | var searchQuery = { |
534 | 554 | parameters: { |
535 | - rootId: rootEntityId, | |
536 | - rootType: rootEntityType, | |
555 | + rootId: relationQueryRootEntityId.id, | |
556 | + rootType: relationQueryRootEntityId.entityType, | |
537 | 557 | direction: filter.direction |
538 | 558 | }, |
539 | 559 | filters: filter.filters |
... | ... | @@ -571,10 +591,11 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device |
571 | 591 | rootEntityId = filter.rootEntity.id; |
572 | 592 | } |
573 | 593 | if (rootEntityType && rootEntityId) { |
594 | + var searchQueryRootEntityId = resolveAliasEntityId(rootEntityType, rootEntityId); | |
574 | 595 | searchQuery = { |
575 | 596 | parameters: { |
576 | - rootId: rootEntityId, | |
577 | - rootType: rootEntityType, | |
597 | + rootId: searchQueryRootEntityId.id, | |
598 | + rootType: searchQueryRootEntityId.entityType, | |
578 | 599 | direction: filter.direction |
579 | 600 | }, |
580 | 601 | relationType: filter.relationType |
... | ... | @@ -709,7 +730,7 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device |
709 | 730 | return result; |
710 | 731 | } |
711 | 732 | |
712 | - function prepareAllowedEntityTypesList(allowedEntityTypes) { | |
733 | + function prepareAllowedEntityTypesList(allowedEntityTypes, useAliasEntityTypes) { | |
713 | 734 | var authority = userService.getAuthority(); |
714 | 735 | var entityTypes = {}; |
715 | 736 | switch(authority) { |
... | ... | @@ -726,12 +747,18 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device |
726 | 747 | entityTypes.rule = types.entityType.rule; |
727 | 748 | entityTypes.plugin = types.entityType.plugin; |
728 | 749 | entityTypes.dashboard = types.entityType.dashboard; |
750 | + if (useAliasEntityTypes) { | |
751 | + entityTypes.current_customer = types.aliasEntityType.current_customer; | |
752 | + } | |
729 | 753 | break; |
730 | 754 | case 'CUSTOMER_USER': |
731 | 755 | entityTypes.device = types.entityType.device; |
732 | 756 | entityTypes.asset = types.entityType.asset; |
733 | 757 | entityTypes.customer = types.entityType.customer; |
734 | 758 | entityTypes.dashboard = types.entityType.dashboard; |
759 | + if (useAliasEntityTypes) { | |
760 | + entityTypes.current_customer = types.aliasEntityType.current_customer; | |
761 | + } | |
735 | 762 | break; |
736 | 763 | } |
737 | 764 | ... | ... |
... | ... | @@ -266,9 +266,9 @@ function UserService($http, $q, $rootScope, adminService, dashboardService, logi |
266 | 266 | var pageLink = {limit: 100}; |
267 | 267 | var fetchDashboardsPromise; |
268 | 268 | if (currentUser.authority === 'TENANT_ADMIN') { |
269 | - fetchDashboardsPromise = dashboardService.getTenantDashboards(pageLink, false); | |
269 | + fetchDashboardsPromise = dashboardService.getTenantDashboards(pageLink); | |
270 | 270 | } else { |
271 | - fetchDashboardsPromise = dashboardService.getCustomerDashboards(currentUser.customerId, pageLink, false); | |
271 | + fetchDashboardsPromise = dashboardService.getCustomerDashboards(currentUser.customerId, pageLink); | |
272 | 272 | } |
273 | 273 | fetchDashboardsPromise.then( |
274 | 274 | function success(result) { | ... | ... |
... | ... | @@ -296,6 +296,9 @@ export default angular.module('thingsboard.types', []) |
296 | 296 | dashboard: "DASHBOARD", |
297 | 297 | alarm: "ALARM" |
298 | 298 | }, |
299 | + aliasEntityType: { | |
300 | + current_customer: "CURRENT_CUSTOMER" | |
301 | + }, | |
299 | 302 | entityTypeTranslations: { |
300 | 303 | "DEVICE": { |
301 | 304 | type: 'entity.type-device', |
... | ... | @@ -350,6 +353,10 @@ export default angular.module('thingsboard.types', []) |
350 | 353 | typePlural: 'entity.type-alarms', |
351 | 354 | list: 'entity.list-of-alarms', |
352 | 355 | nameStartsWith: 'entity.alarm-name-starts-with' |
356 | + }, | |
357 | + "CURRENT_CUSTOMER": { | |
358 | + type: 'entity.type-current-customer', | |
359 | + list: 'entity.type-current-customer' | |
353 | 360 | } |
354 | 361 | }, |
355 | 362 | entitySearchDirection: { | ... | ... |
... | ... | @@ -48,7 +48,7 @@ function DashboardAutocomplete($compile, $templateCache, $q, dashboardService, u |
48 | 48 | var promise; |
49 | 49 | if (scope.dashboardsScope === 'customer' || userService.getAuthority() === 'CUSTOMER_USER') { |
50 | 50 | if (scope.customerId) { |
51 | - promise = dashboardService.getCustomerDashboards(scope.customerId, pageLink, false, {ignoreLoading: true}); | |
51 | + promise = dashboardService.getCustomerDashboards(scope.customerId, pageLink, {ignoreLoading: true}); | |
52 | 52 | } else { |
53 | 53 | promise = $q.when({data: []}); |
54 | 54 | } |
... | ... | @@ -60,7 +60,7 @@ function DashboardAutocomplete($compile, $templateCache, $q, dashboardService, u |
60 | 60 | promise = $q.when({data: []}); |
61 | 61 | } |
62 | 62 | } else { |
63 | - promise = dashboardService.getTenantDashboards(pageLink, false, {ignoreLoading: true}); | |
63 | + promise = dashboardService.getTenantDashboards(pageLink, {ignoreLoading: true}); | |
64 | 64 | } |
65 | 65 | } |
66 | 66 | ... | ... |
... | ... | @@ -48,12 +48,12 @@ function DashboardSelect($compile, $templateCache, $q, $mdMedia, $mdPanel, $docu |
48 | 48 | var promise; |
49 | 49 | if (scope.dashboardsScope === 'customer' || userService.getAuthority() === 'CUSTOMER_USER') { |
50 | 50 | if (scope.customerId && scope.customerId != types.id.nullUid) { |
51 | - promise = dashboardService.getCustomerDashboards(scope.customerId, pageLink, false, {ignoreLoading: true}); | |
51 | + promise = dashboardService.getCustomerDashboards(scope.customerId, pageLink, {ignoreLoading: true}); | |
52 | 52 | } else { |
53 | 53 | promise = $q.when({data: []}); |
54 | 54 | } |
55 | 55 | } else { |
56 | - promise = dashboardService.getTenantDashboards(pageLink, false, {ignoreLoading: true}); | |
56 | + promise = dashboardService.getTenantDashboards(pageLink, {ignoreLoading: true}); | |
57 | 57 | } |
58 | 58 | |
59 | 59 | promise.then(function success(result) { | ... | ... |
... | ... | @@ -52,7 +52,7 @@ export default function AddDashboardsToCustomerController(dashboardService, $mdD |
52 | 52 | fetchMoreItems_: function () { |
53 | 53 | if (vm.dashboards.hasNext && !vm.dashboards.pending) { |
54 | 54 | vm.dashboards.pending = true; |
55 | - dashboardService.getTenantDashboards(vm.dashboards.nextPageLink, false).then( | |
55 | + dashboardService.getTenantDashboards(vm.dashboards.nextPageLink).then( | |
56 | 56 | function success(dashboards) { |
57 | 57 | vm.dashboards.data = vm.dashboards.data.concat(dashboards.data); |
58 | 58 | vm.dashboards.nextPageLink = dashboards.nextPageLink; | ... | ... |
ui/src/app/dashboard/assign-to-customer.controller.js
deleted
100644 → 0
1 | -/* | |
2 | - * Copyright © 2016-2017 The Thingsboard Authors | |
3 | - * | |
4 | - * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | - * you may not use this file except in compliance with the License. | |
6 | - * You may obtain a copy of the License at | |
7 | - * | |
8 | - * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | - * | |
10 | - * Unless required by applicable law or agreed to in writing, software | |
11 | - * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | - * See the License for the specific language governing permissions and | |
14 | - * limitations under the License. | |
15 | - */ | |
16 | -/*@ngInject*/ | |
17 | -export default function AssignDashboardToCustomerController(customerService, dashboardService, $mdDialog, $q, dashboardIds, customers) { | |
18 | - | |
19 | - var vm = this; | |
20 | - | |
21 | - vm.customers = customers; | |
22 | - vm.searchText = ''; | |
23 | - | |
24 | - vm.assign = assign; | |
25 | - vm.cancel = cancel; | |
26 | - vm.isCustomerSelected = isCustomerSelected; | |
27 | - vm.hasData = hasData; | |
28 | - vm.noData = noData; | |
29 | - vm.searchCustomerTextUpdated = searchCustomerTextUpdated; | |
30 | - vm.toggleCustomerSelection = toggleCustomerSelection; | |
31 | - | |
32 | - vm.theCustomers = { | |
33 | - getItemAtIndex: function (index) { | |
34 | - if (index > vm.customers.data.length) { | |
35 | - vm.theCustomers.fetchMoreItems_(index); | |
36 | - return null; | |
37 | - } | |
38 | - var item = vm.customers.data[index]; | |
39 | - if (item) { | |
40 | - item.indexNumber = index + 1; | |
41 | - } | |
42 | - return item; | |
43 | - }, | |
44 | - | |
45 | - getLength: function () { | |
46 | - if (vm.customers.hasNext) { | |
47 | - return vm.customers.data.length + vm.customers.nextPageLink.limit; | |
48 | - } else { | |
49 | - return vm.customers.data.length; | |
50 | - } | |
51 | - }, | |
52 | - | |
53 | - fetchMoreItems_: function () { | |
54 | - if (vm.customers.hasNext && !vm.customers.pending) { | |
55 | - vm.customers.pending = true; | |
56 | - customerService.getCustomers(vm.customers.nextPageLink).then( | |
57 | - function success(customers) { | |
58 | - vm.customers.data = vm.customers.data.concat(customers.data); | |
59 | - vm.customers.nextPageLink = customers.nextPageLink; | |
60 | - vm.customers.hasNext = customers.hasNext; | |
61 | - if (vm.customers.hasNext) { | |
62 | - vm.customers.nextPageLink.limit = vm.customers.pageSize; | |
63 | - } | |
64 | - vm.customers.pending = false; | |
65 | - }, | |
66 | - function fail() { | |
67 | - vm.customers.hasNext = false; | |
68 | - vm.customers.pending = false; | |
69 | - }); | |
70 | - } | |
71 | - } | |
72 | - }; | |
73 | - | |
74 | - function cancel () { | |
75 | - $mdDialog.cancel(); | |
76 | - } | |
77 | - | |
78 | - function assign () { | |
79 | - var tasks = []; | |
80 | - for (var dashboardId in dashboardIds) { | |
81 | - tasks.push(dashboardService.assignDashboardToCustomer(vm.customers.selection.id.id, dashboardIds[dashboardId])); | |
82 | - } | |
83 | - $q.all(tasks).then(function () { | |
84 | - $mdDialog.hide(); | |
85 | - }); | |
86 | - } | |
87 | - | |
88 | - function noData () { | |
89 | - return vm.customers.data.length == 0 && !vm.customers.hasNext; | |
90 | - } | |
91 | - | |
92 | - function hasData () { | |
93 | - return vm.customers.data.length > 0; | |
94 | - } | |
95 | - | |
96 | - function toggleCustomerSelection ($event, customer) { | |
97 | - $event.stopPropagation(); | |
98 | - if (vm.isCustomerSelected(customer)) { | |
99 | - vm.customers.selection = null; | |
100 | - } else { | |
101 | - vm.customers.selection = customer; | |
102 | - } | |
103 | - } | |
104 | - | |
105 | - function isCustomerSelected (customer) { | |
106 | - return vm.customers.selection != null && customer && | |
107 | - customer.id.id === vm.customers.selection.id.id; | |
108 | - } | |
109 | - | |
110 | - function searchCustomerTextUpdated () { | |
111 | - vm.customers = { | |
112 | - pageSize: vm.customers.pageSize, | |
113 | - data: [], | |
114 | - nextPageLink: { | |
115 | - limit: vm.customers.pageSize, | |
116 | - textSearch: vm.searchText | |
117 | - }, | |
118 | - selection: null, | |
119 | - hasNext: true, | |
120 | - pending: false | |
121 | - }; | |
122 | - } | |
123 | -} |
ui/src/app/dashboard/dashboard-card.scss
0 → 100644
1 | +/** | |
2 | + * Copyright © 2016-2017 The Thingsboard Authors | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | + | |
17 | +.tb-dashboard-assigned-customers { | |
18 | + display: block; | |
19 | + display: -webkit-box; | |
20 | + height: 34px; | |
21 | + overflow: hidden; | |
22 | + text-overflow: ellipsis; | |
23 | + -webkit-line-clamp: 2; | |
24 | + -webkit-box-orient: vertical; | |
25 | + margin-bottom: 4px; | |
26 | +} | |
27 | + | ... | ... |
... | ... | @@ -15,6 +15,6 @@ |
15 | 15 | limitations under the License. |
16 | 16 | |
17 | 17 | --> |
18 | -<div class="tb-small" ng-show="vm.isAssignedToCustomer()">{{'dashboard.assignedToCustomer' | translate}} '{{vm.item.assignedCustomer.title}}'</div> | |
19 | -<div class="tb-small" ng-show="vm.isPublic()">{{'dashboard.public' | translate}}</div> | |
18 | +<div class="tb-small tb-dashboard-assigned-customers" ng-show="vm.parentCtl.dashboardsScope === 'tenant' && vm.item.assignedCustomersText">{{'dashboard.assignedToCustomers' | translate}}: '{{vm.item.assignedCustomersText}}'</div> | |
19 | +<div class="tb-small" ng-show="vm.parentCtl.dashboardsScope === 'tenant' && vm.item.publicCustomerId">{{'dashboard.public' | translate}}</div> | |
20 | 20 | ... | ... |
... | ... | @@ -19,24 +19,29 @@ |
19 | 19 | ng-show="!isEdit && dashboardScope === 'tenant'" |
20 | 20 | class="md-raised md-primary">{{ 'dashboard.export' | translate }}</md-button> |
21 | 21 | <md-button ng-click="onMakePublic({event: $event})" |
22 | - ng-show="!isEdit && dashboardScope === 'tenant' && !isAssignedToCustomer && !isPublic" | |
22 | + ng-show="!isEdit && dashboardScope === 'tenant' && !dashboard.publicCustomerId" | |
23 | 23 | class="md-raised md-primary">{{ 'dashboard.make-public' | translate }}</md-button> |
24 | -<md-button ng-click="onAssignToCustomer({event: $event})" | |
25 | - ng-show="!isEdit && dashboardScope === 'tenant' && !isAssignedToCustomer" | |
26 | - class="md-raised md-primary">{{ 'dashboard.assign-to-customer' | translate }}</md-button> | |
27 | -<md-button ng-click="onUnassignFromCustomer({event: $event, isPublic: isPublic})" | |
28 | - ng-show="!isEdit && (dashboardScope === 'customer' || dashboardScope === 'tenant') && isAssignedToCustomer" | |
29 | - class="md-raised md-primary">{{ isPublic ? 'dashboard.make-private' : 'dashboard.unassign-from-customer' | translate }}</md-button> | |
24 | +<md-button ng-click="onMakePrivate({event: $event})" | |
25 | + ng-show="!isEdit && ((dashboardScope === 'tenant' && dashboard.publicCustomerId || | |
26 | + dashboardScope === 'customer' && customerId == dashboard.publicCustomerId))" | |
27 | + class="md-raised md-primary">{{ 'dashboard.make-private' | translate }}</md-button> | |
28 | +<md-button ng-click="onManageAssignedCustomers({event: $event})" | |
29 | + ng-show="!isEdit && dashboardScope === 'tenant'" | |
30 | + class="md-raised md-primary">{{ 'dashboard.manage-assigned-customers' | translate }}</md-button> | |
31 | +<md-button ng-click="onUnassignFromCustomer({event: $event})" | |
32 | + ng-show="!isEdit && dashboardScope === 'customer' && customerId != dashboard.publicCustomerId" | |
33 | + class="md-raised md-primary">{{ 'dashboard.unassign-from-customer' | translate }}</md-button> | |
30 | 34 | <md-button ng-click="onDeleteDashboard({event: $event})" |
31 | 35 | ng-show="!isEdit && dashboardScope === 'tenant'" |
32 | 36 | class="md-raised md-primary">{{ 'dashboard.delete' | translate }}</md-button> |
33 | 37 | <md-content class="md-padding" layout="column"> |
34 | 38 | <md-input-container class="md-block" |
35 | - ng-show="!isEdit && isAssignedToCustomer && !isPublic && dashboardScope === 'tenant'"> | |
36 | - <label translate>dashboard.assignedToCustomer</label> | |
37 | - <input ng-model="assignedCustomer.title" disabled> | |
39 | + ng-show="!isEdit && dashboard.assignedCustomersText && dashboardScope === 'tenant'"> | |
40 | + <label translate>dashboard.assignedToCustomers</label> | |
41 | + <input ng-model="dashboard.assignedCustomersText" disabled> | |
38 | 42 | </md-input-container> |
39 | - <div layout="column" ng-show="!isEdit && isPublic && (dashboardScope === 'customer' || dashboardScope === 'tenant')"> | |
43 | + <div layout="column" ng-show="!isEdit && ((dashboardScope === 'tenant' && dashboard.publicCustomerId || | |
44 | + dashboardScope === 'customer' && customerId == dashboard.publicCustomerId))"> | |
40 | 45 | <tb-social-share-panel style="padding-bottom: 10px;" |
41 | 46 | share-title="{{ 'dashboard.socialshare-title' | translate:{dashboardTitle: dashboard.title} }}" |
42 | 47 | share-text="{{ 'dashboard.socialshare-text' | translate:{dashboardTitle: dashboard.title} }}" | ... | ... |
... | ... | @@ -20,36 +20,17 @@ import dashboardFieldsetTemplate from './dashboard-fieldset.tpl.html'; |
20 | 20 | /* eslint-enable import/no-unresolved, import/default */ |
21 | 21 | |
22 | 22 | /*@ngInject*/ |
23 | -export default function DashboardDirective($compile, $templateCache, $translate, types, toast, customerService, dashboardService) { | |
23 | +export default function DashboardDirective($compile, $templateCache, $translate, types, toast, dashboardService) { | |
24 | 24 | var linker = function (scope, element) { |
25 | 25 | var template = $templateCache.get(dashboardFieldsetTemplate); |
26 | 26 | element.html(template); |
27 | - | |
28 | - scope.isAssignedToCustomer = false; | |
29 | - scope.isPublic = false; | |
30 | - scope.assignedCustomer = null; | |
31 | 27 | scope.publicLink = null; |
32 | - | |
33 | 28 | scope.$watch('dashboard', function(newVal) { |
34 | 29 | if (newVal) { |
35 | - if (scope.dashboard.customerId && scope.dashboard.customerId.id !== types.id.nullUid) { | |
36 | - scope.isAssignedToCustomer = true; | |
37 | - customerService.getShortCustomerInfo(scope.dashboard.customerId.id).then( | |
38 | - function success(customer) { | |
39 | - scope.assignedCustomer = customer; | |
40 | - scope.isPublic = customer.isPublic; | |
41 | - if (scope.isPublic) { | |
42 | - scope.publicLink = dashboardService.getPublicDashboardLink(scope.dashboard); | |
43 | - } else { | |
44 | - scope.publicLink = null; | |
45 | - } | |
46 | - } | |
47 | - ); | |
30 | + if (scope.dashboard.publicCustomerId) { | |
31 | + scope.publicLink = dashboardService.getPublicDashboardLink(scope.dashboard); | |
48 | 32 | } else { |
49 | - scope.isAssignedToCustomer = false; | |
50 | - scope.isPublic = false; | |
51 | 33 | scope.publicLink = null; |
52 | - scope.assignedCustomer = null; | |
53 | 34 | } |
54 | 35 | } |
55 | 36 | }); |
... | ... | @@ -66,10 +47,12 @@ export default function DashboardDirective($compile, $templateCache, $translate, |
66 | 47 | scope: { |
67 | 48 | dashboard: '=', |
68 | 49 | isEdit: '=', |
50 | + customerId: '=', | |
69 | 51 | dashboardScope: '=', |
70 | 52 | theForm: '=', |
71 | - onAssignToCustomer: '&', | |
72 | 53 | onMakePublic: '&', |
54 | + onMakePrivate: '&', | |
55 | + onManageAssignedCustomers: '&', | |
73 | 56 | onUnassignFromCustomer: '&', |
74 | 57 | onExportDashboard: '&', |
75 | 58 | onDeleteDashboard: '&' | ... | ... |
... | ... | @@ -17,12 +17,14 @@ |
17 | 17 | |
18 | 18 | import addDashboardTemplate from './add-dashboard.tpl.html'; |
19 | 19 | import dashboardCard from './dashboard-card.tpl.html'; |
20 | -import assignToCustomerTemplate from './assign-to-customer.tpl.html'; | |
21 | 20 | import addDashboardsToCustomerTemplate from './add-dashboards-to-customer.tpl.html'; |
22 | 21 | import makeDashboardPublicDialogTemplate from './make-dashboard-public-dialog.tpl.html'; |
22 | +import manageAssignedCustomersTemplate from './manage-assigned-customers.tpl.html'; | |
23 | 23 | |
24 | 24 | /* eslint-enable import/no-unresolved, import/default */ |
25 | 25 | |
26 | +import './dashboard-card.scss'; | |
27 | + | |
26 | 28 | /*@ngInject*/ |
27 | 29 | export function MakeDashboardPublicDialogController($mdDialog, $translate, toast, dashboardService, dashboard) { |
28 | 30 | |
... | ... | @@ -48,23 +50,8 @@ export function MakeDashboardPublicDialogController($mdDialog, $translate, toast |
48 | 50 | export function DashboardCardController(types) { |
49 | 51 | |
50 | 52 | var vm = this; |
51 | - | |
52 | 53 | vm.types = types; |
53 | 54 | |
54 | - vm.isAssignedToCustomer = function() { | |
55 | - if (vm.item && vm.item.customerId && vm.parentCtl.dashboardsScope === 'tenant' && | |
56 | - vm.item.customerId.id != vm.types.id.nullUid && !vm.item.assignedCustomer.isPublic) { | |
57 | - return true; | |
58 | - } | |
59 | - return false; | |
60 | - } | |
61 | - | |
62 | - vm.isPublic = function() { | |
63 | - if (vm.item && vm.item.assignedCustomer && vm.parentCtl.dashboardsScope === 'tenant' && vm.item.assignedCustomer.isPublic) { | |
64 | - return true; | |
65 | - } | |
66 | - return false; | |
67 | - } | |
68 | 55 | } |
69 | 56 | |
70 | 57 | /*@ngInject*/ |
... | ... | @@ -135,8 +122,9 @@ export function DashboardsController(userService, dashboardService, customerServ |
135 | 122 | |
136 | 123 | vm.dashboardsScope = $state.$current.data.dashboardsType; |
137 | 124 | |
138 | - vm.assignToCustomer = assignToCustomer; | |
139 | 125 | vm.makePublic = makePublic; |
126 | + vm.makePrivate = makePrivate; | |
127 | + vm.manageAssignedCustomers = manageAssignedCustomers; | |
140 | 128 | vm.unassignFromCustomer = unassignFromCustomer; |
141 | 129 | vm.exportDashboard = exportDashboard; |
142 | 130 | |
... | ... | @@ -155,6 +143,7 @@ export function DashboardsController(userService, dashboardService, customerServ |
155 | 143 | } |
156 | 144 | |
157 | 145 | if (customerId) { |
146 | + vm.customerId = customerId; | |
158 | 147 | vm.customerDashboardsTitle = $translate.instant('customer.dashboards'); |
159 | 148 | customerService.getShortCustomerInfo(customerId).then( |
160 | 149 | function success(info) { |
... | ... | @@ -167,7 +156,7 @@ export function DashboardsController(userService, dashboardService, customerServ |
167 | 156 | |
168 | 157 | if (vm.dashboardsScope === 'tenant') { |
169 | 158 | fetchDashboardsFunction = function (pageLink) { |
170 | - return dashboardService.getTenantDashboards(pageLink, true); | |
159 | + return dashboardService.getTenantDashboards(pageLink); | |
171 | 160 | }; |
172 | 161 | deleteDashboardFunction = function (dashboardId) { |
173 | 162 | return dashboardService.deleteDashboard(dashboardId); |
... | ... | @@ -194,11 +183,33 @@ export function DashboardsController(userService, dashboardService, customerServ |
194 | 183 | details: function() { return $translate.instant('dashboard.make-public') }, |
195 | 184 | icon: "share", |
196 | 185 | isEnabled: function(dashboard) { |
197 | - return dashboard && (!dashboard.customerId || dashboard.customerId.id === types.id.nullUid); | |
186 | + return dashboard && !dashboard.publicCustomerId; | |
198 | 187 | } |
199 | 188 | }); |
200 | - | |
201 | 189 | dashboardActionsList.push({ |
190 | + onAction: function ($event, item) { | |
191 | + makePrivate($event, item); | |
192 | + }, | |
193 | + name: function() { return $translate.instant('action.make-private') }, | |
194 | + details: function() { return $translate.instant('dashboard.make-private') }, | |
195 | + icon: "reply", | |
196 | + isEnabled: function(dashboard) { | |
197 | + return dashboard && dashboard.publicCustomerId; | |
198 | + } | |
199 | + }); | |
200 | + dashboardActionsList.push({ | |
201 | + onAction: function ($event, item) { | |
202 | + manageAssignedCustomers($event, item); | |
203 | + }, | |
204 | + name: function() { return $translate.instant('action.assign') }, | |
205 | + details: function() { return $translate.instant('dashboard.manage-assigned-customers') }, | |
206 | + icon: "assignment_ind", | |
207 | + isEnabled: function(dashboard) { | |
208 | + return dashboard; | |
209 | + } | |
210 | + }); | |
211 | + | |
212 | + /*dashboardActionsList.push({ | |
202 | 213 | onAction: function ($event, item) { |
203 | 214 | assignToCustomer($event, [ item.id.id ]); |
204 | 215 | }, |
... | ... | @@ -208,8 +219,8 @@ export function DashboardsController(userService, dashboardService, customerServ |
208 | 219 | isEnabled: function(dashboard) { |
209 | 220 | return dashboard && (!dashboard.customerId || dashboard.customerId.id === types.id.nullUid); |
210 | 221 | } |
211 | - }); | |
212 | - dashboardActionsList.push({ | |
222 | + });*/ | |
223 | + /*dashboardActionsList.push({ | |
213 | 224 | onAction: function ($event, item) { |
214 | 225 | unassignFromCustomer($event, item, false); |
215 | 226 | }, |
... | ... | @@ -219,18 +230,7 @@ export function DashboardsController(userService, dashboardService, customerServ |
219 | 230 | isEnabled: function(dashboard) { |
220 | 231 | return dashboard && dashboard.customerId && dashboard.customerId.id !== types.id.nullUid && !dashboard.assignedCustomer.isPublic; |
221 | 232 | } |
222 | - }); | |
223 | - dashboardActionsList.push({ | |
224 | - onAction: function ($event, item) { | |
225 | - unassignFromCustomer($event, item, true); | |
226 | - }, | |
227 | - name: function() { return $translate.instant('action.make-private') }, | |
228 | - details: function() { return $translate.instant('dashboard.make-private') }, | |
229 | - icon: "reply", | |
230 | - isEnabled: function(dashboard) { | |
231 | - return dashboard && dashboard.customerId && dashboard.customerId.id !== types.id.nullUid && dashboard.assignedCustomer.isPublic; | |
232 | - } | |
233 | - }); | |
233 | + });*/ | |
234 | 234 | |
235 | 235 | dashboardActionsList.push( |
236 | 236 | { |
... | ... | @@ -246,7 +246,7 @@ export function DashboardsController(userService, dashboardService, customerServ |
246 | 246 | dashboardGroupActionsList.push( |
247 | 247 | { |
248 | 248 | onAction: function ($event, items) { |
249 | - assignDashboardsToCustomer($event, items); | |
249 | + assignDashboardsToCustomers($event, items); | |
250 | 250 | }, |
251 | 251 | name: function() { return $translate.instant('dashboard.assign-dashboards') }, |
252 | 252 | details: function(selectedCount) { |
... | ... | @@ -255,6 +255,17 @@ export function DashboardsController(userService, dashboardService, customerServ |
255 | 255 | icon: "assignment_ind" |
256 | 256 | } |
257 | 257 | ); |
258 | + dashboardGroupActionsList.push( | |
259 | + { | |
260 | + onAction: function ($event, items) { | |
261 | + unassignDashboardsFromCustomers($event, items); | |
262 | + }, | |
263 | + name: function() { return $translate.instant('dashboard.unassign-dashboards') }, | |
264 | + details: function(selectedCount) { | |
265 | + return $translate.instant('dashboard.unassign-dashboards-action-text', {count: selectedCount}, "messageformat"); | |
266 | + }, | |
267 | + icon: "assignment_return" } | |
268 | + ); | |
258 | 269 | |
259 | 270 | dashboardGroupActionsList.push( |
260 | 271 | { |
... | ... | @@ -290,10 +301,10 @@ export function DashboardsController(userService, dashboardService, customerServ |
290 | 301 | }); |
291 | 302 | } else if (vm.dashboardsScope === 'customer' || vm.dashboardsScope === 'customer_user') { |
292 | 303 | fetchDashboardsFunction = function (pageLink) { |
293 | - return dashboardService.getCustomerDashboards(customerId, pageLink, true); | |
304 | + return dashboardService.getCustomerDashboards(customerId, pageLink); | |
294 | 305 | }; |
295 | 306 | deleteDashboardFunction = function (dashboardId) { |
296 | - return dashboardService.unassignDashboardFromCustomer(dashboardId); | |
307 | + return dashboardService.unassignDashboardFromCustomer(customerId, dashboardId); | |
297 | 308 | }; |
298 | 309 | refreshDashboardsParamsFunction = function () { |
299 | 310 | return {"customerId": customerId, "topIndex": vm.topIndex}; |
... | ... | @@ -314,26 +325,27 @@ export function DashboardsController(userService, dashboardService, customerServ |
314 | 325 | dashboardActionsList.push( |
315 | 326 | { |
316 | 327 | onAction: function ($event, item) { |
317 | - unassignFromCustomer($event, item, false); | |
328 | + makePrivate($event, item); | |
318 | 329 | }, |
319 | - name: function() { return $translate.instant('action.unassign') }, | |
320 | - details: function() { return $translate.instant('dashboard.unassign-from-customer') }, | |
321 | - icon: "assignment_return", | |
330 | + name: function() { return $translate.instant('action.make-private') }, | |
331 | + details: function() { return $translate.instant('dashboard.make-private') }, | |
332 | + icon: "reply", | |
322 | 333 | isEnabled: function(dashboard) { |
323 | - return dashboard && !dashboard.assignedCustomer.isPublic; | |
334 | + return dashboard && customerId == dashboard.publicCustomerId; | |
324 | 335 | } |
325 | 336 | } |
326 | 337 | ); |
338 | + | |
327 | 339 | dashboardActionsList.push( |
328 | 340 | { |
329 | 341 | onAction: function ($event, item) { |
330 | - unassignFromCustomer($event, item, true); | |
342 | + unassignFromCustomer($event, item, customerId); | |
331 | 343 | }, |
332 | - name: function() { return $translate.instant('action.make-private') }, | |
333 | - details: function() { return $translate.instant('dashboard.make-private') }, | |
334 | - icon: "reply", | |
344 | + name: function() { return $translate.instant('action.unassign') }, | |
345 | + details: function() { return $translate.instant('dashboard.unassign-from-customer') }, | |
346 | + icon: "assignment_return", | |
335 | 347 | isEnabled: function(dashboard) { |
336 | - return dashboard && dashboard.assignedCustomer.isPublic; | |
348 | + return dashboard && customerId != dashboard.publicCustomerId; | |
337 | 349 | } |
338 | 350 | } |
339 | 351 | ); |
... | ... | @@ -341,7 +353,7 @@ export function DashboardsController(userService, dashboardService, customerServ |
341 | 353 | dashboardGroupActionsList.push( |
342 | 354 | { |
343 | 355 | onAction: function ($event, items) { |
344 | - unassignDashboardsFromCustomer($event, items); | |
356 | + unassignDashboardsFromCustomer($event, items, customerId); | |
345 | 357 | }, |
346 | 358 | name: function() { return $translate.instant('dashboard.unassign-dashboards') }, |
347 | 359 | details: function(selectedCount) { |
... | ... | @@ -351,7 +363,6 @@ export function DashboardsController(userService, dashboardService, customerServ |
351 | 363 | } |
352 | 364 | ); |
353 | 365 | |
354 | - | |
355 | 366 | vm.dashboardGridConfig.addItemAction = { |
356 | 367 | onAction: function ($event) { |
357 | 368 | addDashboardsToCustomer($event); |
... | ... | @@ -428,39 +439,42 @@ export function DashboardsController(userService, dashboardService, customerServ |
428 | 439 | return deferred.promise; |
429 | 440 | } |
430 | 441 | |
431 | - function assignToCustomer($event, dashboardIds) { | |
442 | + function manageAssignedCustomers($event, dashboard) { | |
443 | + showManageAssignedCustomersDialog($event, [dashboard.id.id], 'manage', dashboard.assignedCustomersIds); | |
444 | + } | |
445 | + | |
446 | + function assignDashboardsToCustomers($event, items) { | |
447 | + var dashboardIds = []; | |
448 | + for (var id in items.selections) { | |
449 | + dashboardIds.push(id); | |
450 | + } | |
451 | + showManageAssignedCustomersDialog($event, dashboardIds, 'assign'); | |
452 | + } | |
453 | + | |
454 | + function unassignDashboardsFromCustomers($event, items) { | |
455 | + var dashboardIds = []; | |
456 | + for (var id in items.selections) { | |
457 | + dashboardIds.push(id); | |
458 | + } | |
459 | + showManageAssignedCustomersDialog($event, dashboardIds, 'unassign'); | |
460 | + } | |
461 | + | |
462 | + function showManageAssignedCustomersDialog($event, dashboardIds, actionType, assignedCustomers) { | |
432 | 463 | if ($event) { |
433 | 464 | $event.stopPropagation(); |
434 | 465 | } |
435 | - var pageSize = 10; | |
436 | - customerService.getCustomers({limit: pageSize, textSearch: ''}).then( | |
437 | - function success(_customers) { | |
438 | - var customers = { | |
439 | - pageSize: pageSize, | |
440 | - data: _customers.data, | |
441 | - nextPageLink: _customers.nextPageLink, | |
442 | - selection: null, | |
443 | - hasNext: _customers.hasNext, | |
444 | - pending: false | |
445 | - }; | |
446 | - if (customers.hasNext) { | |
447 | - customers.nextPageLink.limit = pageSize; | |
448 | - } | |
449 | - $mdDialog.show({ | |
450 | - controller: 'AssignDashboardToCustomerController', | |
451 | - controllerAs: 'vm', | |
452 | - templateUrl: assignToCustomerTemplate, | |
453 | - locals: {dashboardIds: dashboardIds, customers: customers}, | |
454 | - parent: angular.element($document[0].body), | |
455 | - fullscreen: true, | |
456 | - targetEvent: $event | |
457 | - }).then(function () { | |
458 | - vm.grid.refreshList(); | |
459 | - }, function () { | |
460 | - }); | |
461 | - }, | |
462 | - function fail() { | |
463 | - }); | |
466 | + $mdDialog.show({ | |
467 | + controller: 'ManageAssignedCustomersController', | |
468 | + controllerAs: 'vm', | |
469 | + templateUrl: manageAssignedCustomersTemplate, | |
470 | + locals: {actionType: actionType, dashboardIds: dashboardIds, assignedCustomers: assignedCustomers}, | |
471 | + parent: angular.element($document[0].body), | |
472 | + fullscreen: true, | |
473 | + targetEvent: $event | |
474 | + }).then(function () { | |
475 | + vm.grid.refreshList(); | |
476 | + }, function () { | |
477 | + }); | |
464 | 478 | } |
465 | 479 | |
466 | 480 | function addDashboardsToCustomer($event) { |
... | ... | @@ -468,7 +482,7 @@ export function DashboardsController(userService, dashboardService, customerServ |
468 | 482 | $event.stopPropagation(); |
469 | 483 | } |
470 | 484 | var pageSize = 10; |
471 | - dashboardService.getTenantDashboards({limit: pageSize, textSearch: ''}, false).then( | |
485 | + dashboardService.getTenantDashboards({limit: pageSize, textSearch: ''}).then( | |
472 | 486 | function success(_dashboards) { |
473 | 487 | var dashboards = { |
474 | 488 | pageSize: pageSize, |
... | ... | @@ -499,30 +513,13 @@ export function DashboardsController(userService, dashboardService, customerServ |
499 | 513 | }); |
500 | 514 | } |
501 | 515 | |
502 | - function assignDashboardsToCustomer($event, items) { | |
503 | - var dashboardIds = []; | |
504 | - for (var id in items.selections) { | |
505 | - dashboardIds.push(id); | |
506 | - } | |
507 | - assignToCustomer($event, dashboardIds); | |
508 | - } | |
509 | - | |
510 | - function unassignFromCustomer($event, dashboard, isPublic) { | |
516 | + function unassignFromCustomer($event, dashboard, customerId) { | |
511 | 517 | if ($event) { |
512 | 518 | $event.stopPropagation(); |
513 | 519 | } |
514 | - var title; | |
515 | - var content; | |
516 | - var label; | |
517 | - if (isPublic) { | |
518 | - title = $translate.instant('dashboard.make-private-dashboard-title', {dashboardTitle: dashboard.title}); | |
519 | - content = $translate.instant('dashboard.make-private-dashboard-text'); | |
520 | - label = $translate.instant('dashboard.make-private-dashboard'); | |
521 | - } else { | |
522 | - title = $translate.instant('dashboard.unassign-dashboard-title', {dashboardTitle: dashboard.title}); | |
523 | - content = $translate.instant('dashboard.unassign-dashboard-text'); | |
524 | - label = $translate.instant('dashboard.unassign-dashboard'); | |
525 | - } | |
520 | + var title = $translate.instant('dashboard.unassign-dashboard-title', {dashboardTitle: dashboard.title}); | |
521 | + var content = $translate.instant('dashboard.unassign-dashboard-text'); | |
522 | + var label = $translate.instant('dashboard.unassign-dashboard'); | |
526 | 523 | var confirm = $mdDialog.confirm() |
527 | 524 | .targetEvent($event) |
528 | 525 | .title(title) |
... | ... | @@ -531,7 +528,7 @@ export function DashboardsController(userService, dashboardService, customerServ |
531 | 528 | .cancel($translate.instant('action.no')) |
532 | 529 | .ok($translate.instant('action.yes')); |
533 | 530 | $mdDialog.show(confirm).then(function () { |
534 | - dashboardService.unassignDashboardFromCustomer(dashboard.id.id).then(function success() { | |
531 | + dashboardService.unassignDashboardFromCustomer(customerId, dashboard.id.id).then(function success() { | |
535 | 532 | vm.grid.refreshList(); |
536 | 533 | }); |
537 | 534 | }); |
... | ... | @@ -556,12 +553,33 @@ export function DashboardsController(userService, dashboardService, customerServ |
556 | 553 | }); |
557 | 554 | } |
558 | 555 | |
556 | + function makePrivate($event, dashboard) { | |
557 | + if ($event) { | |
558 | + $event.stopPropagation(); | |
559 | + } | |
560 | + var title = $translate.instant('dashboard.make-private-dashboard-title', {dashboardTitle: dashboard.title}); | |
561 | + var content = $translate.instant('dashboard.make-private-dashboard-text'); | |
562 | + var label = $translate.instant('dashboard.make-private-dashboard'); | |
563 | + var confirm = $mdDialog.confirm() | |
564 | + .targetEvent($event) | |
565 | + .title(title) | |
566 | + .htmlContent(content) | |
567 | + .ariaLabel(label) | |
568 | + .cancel($translate.instant('action.no')) | |
569 | + .ok($translate.instant('action.yes')); | |
570 | + $mdDialog.show(confirm).then(function () { | |
571 | + dashboardService.makeDashboardPrivate(dashboard.id.id).then(function success() { | |
572 | + vm.grid.refreshList(); | |
573 | + }); | |
574 | + }); | |
575 | + } | |
576 | + | |
559 | 577 | function exportDashboard($event, dashboard) { |
560 | 578 | $event.stopPropagation(); |
561 | 579 | importExport.exportDashboard(dashboard.id.id); |
562 | 580 | } |
563 | 581 | |
564 | - function unassignDashboardsFromCustomer($event, items) { | |
582 | + function unassignDashboardsFromCustomer($event, items, customerId) { | |
565 | 583 | var confirm = $mdDialog.confirm() |
566 | 584 | .targetEvent($event) |
567 | 585 | .title($translate.instant('dashboard.unassign-dashboards-title', {count: items.selectedCount}, 'messageformat')) |
... | ... | @@ -572,7 +590,7 @@ export function DashboardsController(userService, dashboardService, customerServ |
572 | 590 | $mdDialog.show(confirm).then(function () { |
573 | 591 | var tasks = []; |
574 | 592 | for (var id in items.selections) { |
575 | - tasks.push(dashboardService.unassignDashboardFromCustomer(id)); | |
593 | + tasks.push(dashboardService.unassignDashboardFromCustomer(customerId, id)); | |
576 | 594 | } |
577 | 595 | $q.all(tasks).then(function () { |
578 | 596 | vm.grid.refreshList(); | ... | ... |
... | ... | @@ -25,10 +25,12 @@ |
25 | 25 | <tb-dashboard-details dashboard="vm.grid.operatingItem()" |
26 | 26 | is-edit="vm.grid.detailsConfig.isDetailsEditMode" |
27 | 27 | dashboard-scope="vm.dashboardsScope" |
28 | + customer-id="vm.customerId" | |
28 | 29 | the-form="vm.grid.detailsForm" |
29 | - on-assign-to-customer="vm.assignToCustomer(event, [ vm.grid.detailsConfig.currentItem.id.id ])" | |
30 | 30 | on-make-public="vm.makePublic(event, vm.grid.detailsConfig.currentItem)" |
31 | - on-unassign-from-customer="vm.unassignFromCustomer(event, vm.grid.detailsConfig.currentItem, isPublic)" | |
31 | + on-make-private="vm.makePrivate(event, vm.grid.detailsConfig.currentItem)" | |
32 | + on-manage-assigned-customers="vm.manageAssignedCustomers(event, vm.grid.detailsConfig.currentItem)" | |
33 | + on-unassign-from-customer="vm.unassignFromCustomer(event, vm.grid.detailsConfig.currentItem, vm.customerId)" | |
32 | 34 | on-export-dashboard="vm.exportDashboard(event, vm.grid.detailsConfig.currentItem)" |
33 | 35 | on-delete-dashboard="vm.grid.deleteItem(event, vm.grid.detailsConfig.currentItem)"></tb-dashboard-details> |
34 | 36 | </md-tab> | ... | ... |
... | ... | @@ -40,8 +40,8 @@ import DashboardRoutes from './dashboard.routes'; |
40 | 40 | import {DashboardsController, DashboardCardController, MakeDashboardPublicDialogController} from './dashboards.controller'; |
41 | 41 | import DashboardController from './dashboard.controller'; |
42 | 42 | import DashboardSettingsController from './dashboard-settings.controller'; |
43 | -import AssignDashboardToCustomerController from './assign-to-customer.controller'; | |
44 | 43 | import AddDashboardsToCustomerController from './add-dashboards-to-customer.controller'; |
44 | +import ManageAssignedCustomersController from './manage-assigned-customers.controller'; | |
45 | 45 | import AddWidgetController from './add-widget.controller'; |
46 | 46 | import DashboardDirective from './dashboard.directive'; |
47 | 47 | import EditWidgetDirective from './edit-widget.directive'; |
... | ... | @@ -74,8 +74,8 @@ export default angular.module('thingsboard.dashboard', [ |
74 | 74 | .controller('MakeDashboardPublicDialogController', MakeDashboardPublicDialogController) |
75 | 75 | .controller('DashboardController', DashboardController) |
76 | 76 | .controller('DashboardSettingsController', DashboardSettingsController) |
77 | - .controller('AssignDashboardToCustomerController', AssignDashboardToCustomerController) | |
78 | 77 | .controller('AddDashboardsToCustomerController', AddDashboardsToCustomerController) |
78 | + .controller('ManageAssignedCustomersController', ManageAssignedCustomersController) | |
79 | 79 | .controller('AddWidgetController', AddWidgetController) |
80 | 80 | .directive('tbDashboardDetails', DashboardDirective) |
81 | 81 | .directive('tbEditWidget', EditWidgetDirective) | ... | ... |
1 | +/* | |
2 | + * Copyright © 2016-2017 The Thingsboard Authors | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | +/*@ngInject*/ | |
17 | +export default function ManageAssignedCustomersController($mdDialog, $q, types, dashboardService, actionType, dashboardIds, assignedCustomers) { | |
18 | + | |
19 | + var vm = this; | |
20 | + | |
21 | + vm.types = types; | |
22 | + vm.actionType = actionType; | |
23 | + vm.dashboardIds = dashboardIds; | |
24 | + vm.assignedCustomers = assignedCustomers; | |
25 | + if (actionType != 'manage') { | |
26 | + vm.assignedCustomers = []; | |
27 | + } | |
28 | + | |
29 | + if (actionType == 'manage') { | |
30 | + vm.titleText = 'dashboard.manage-assigned-customers'; | |
31 | + vm.labelText = 'dashboard.assigned-customers'; | |
32 | + vm.actionName = 'action.update'; | |
33 | + } else if (actionType == 'assign') { | |
34 | + vm.titleText = 'dashboard.assign-to-customers'; | |
35 | + vm.labelText = 'dashboard.assign-to-customers-text'; | |
36 | + vm.actionName = 'action.assign'; | |
37 | + } else if (actionType == 'unassign') { | |
38 | + vm.titleText = 'dashboard.unassign-from-customers'; | |
39 | + vm.labelText = 'dashboard.unassign-from-customers-text'; | |
40 | + vm.actionName = 'action.unassign'; | |
41 | + } | |
42 | + | |
43 | + vm.submit = submit; | |
44 | + vm.cancel = cancel; | |
45 | + | |
46 | + function cancel () { | |
47 | + $mdDialog.cancel(); | |
48 | + } | |
49 | + | |
50 | + function submit () { | |
51 | + var tasks = []; | |
52 | + for (var i=0;i<vm.dashboardIds.length;i++) { | |
53 | + var dashboardId = vm.dashboardIds[i]; | |
54 | + var promise; | |
55 | + if (vm.actionType == 'manage') { | |
56 | + promise = dashboardService.updateDashboardCustomers(dashboardId, vm.assignedCustomers); | |
57 | + } else if (vm.actionType == 'assign') { | |
58 | + promise = dashboardService.addDashboardCustomers(dashboardId, vm.assignedCustomers); | |
59 | + } else if (vm.actionType == 'unassign') { | |
60 | + promise = dashboardService.removeDashboardCustomers(dashboardId, vm.assignedCustomers); | |
61 | + } | |
62 | + tasks.push(promise); | |
63 | + } | |
64 | + $q.all(tasks).then(function () { | |
65 | + $mdDialog.hide(); | |
66 | + }); | |
67 | + } | |
68 | + | |
69 | +} | ... | ... |
ui/src/app/dashboard/manage-assigned-customers.tpl.html
renamed from
ui/src/app/dashboard/assign-to-customer.tpl.html
... | ... | @@ -15,11 +15,11 @@ |
15 | 15 | limitations under the License. |
16 | 16 | |
17 | 17 | --> |
18 | -<md-dialog aria-label="{{ 'dashboard.assign-dashboard-to-customer' | translate }}"> | |
19 | - <form name="theForm" ng-submit="vm.assign()"> | |
18 | +<md-dialog aria-label="{{ vm.titleText | translate }}" style="width: 600px;"> | |
19 | + <form name="theForm" ng-submit="vm.submit()"> | |
20 | 20 | <md-toolbar> |
21 | 21 | <div class="md-toolbar-tools"> |
22 | - <h2 translate>dashboard.assign-dashboard-to-customer</h2> | |
22 | + <h2 translate>{{vm.titleText}}</h2> | |
23 | 23 | <span flex></span> |
24 | 24 | <md-button class="md-icon-button" ng-click="vm.cancel()"> |
25 | 25 | <ng-md-icon icon="close" aria-label="{{ 'dialog.close' | translate }}"></ng-md-icon> |
... | ... | @@ -31,42 +31,17 @@ |
31 | 31 | <md-dialog-content> |
32 | 32 | <div class="md-dialog-content"> |
33 | 33 | <fieldset> |
34 | - <span translate>dashboard.assign-to-customer-text</span> | |
35 | - <md-input-container class="md-block" style='margin-bottom: 0px;'> | |
36 | - <label> </label> | |
37 | - <md-icon aria-label="{{ 'action.search' | translate }}" class="material-icons"> | |
38 | - search | |
39 | - </md-icon> | |
40 | - <input id="customer-search" autofocus ng-model="vm.searchText" | |
41 | - ng-change="vm.searchCustomerTextUpdated()" | |
42 | - placeholder="{{ 'common.enter-search' | translate }}"/> | |
43 | - </md-input-container> | |
44 | - <div style='min-height: 150px;'> | |
45 | - <span translate layout-align="center center" | |
46 | - style="text-transform: uppercase; display: flex; height: 150px;" | |
47 | - class="md-subhead" | |
48 | - ng-show="vm.noData()">customer.no-customers-text</span> | |
49 | - <md-virtual-repeat-container ng-show="vm.hasData()" | |
50 | - tb-scope-element="repeatContainer" md-top-index="vm.topIndex" flex | |
51 | - style='min-height: 150px; width: 100%;'> | |
52 | - <md-list> | |
53 | - <md-list-item md-virtual-repeat="customer in vm.theCustomers" md-on-demand | |
54 | - class="repeated-item" flex> | |
55 | - <md-checkbox ng-click="vm.toggleCustomerSelection($event, customer)" | |
56 | - aria-label="{{ 'item.selected' | translate }}" | |
57 | - ng-checked="vm.isCustomerSelected(customer)"></md-checkbox> | |
58 | - <span> {{ customer.title }} </span> | |
59 | - </md-list-item> | |
60 | - </md-list> | |
61 | - </md-virtual-repeat-container> | |
62 | - </div> | |
34 | + <span translate>{{vm.labelText}}</span> | |
35 | + <tb-entity-list ng-disabled="$root.loading" | |
36 | + ng-model="vm.assignedCustomers" | |
37 | + entity-type="vm.types.entityType.customer"></tb-entity-list> | |
63 | 38 | </fieldset> |
64 | 39 | </div> |
65 | 40 | </md-dialog-content> |
66 | 41 | <md-dialog-actions layout="row"> |
67 | 42 | <span flex></span> |
68 | - <md-button ng-disabled="$root.loading || vm.customers.selection==null" type="submit" class="md-raised md-primary"> | |
69 | - {{ 'action.assign' | translate }} | |
43 | + <md-button ng-disabled="$root.loading || !theForm.$dirty || !theForm.$valid" type="submit" class="md-raised md-primary"> | |
44 | + {{ vm.actionName | translate }} | |
70 | 45 | </md-button> |
71 | 46 | <md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | |
72 | 47 | translate }} | ... | ... |
... | ... | @@ -38,7 +38,12 @@ export default function EntityAutocomplete($compile, $templateCache, $q, $filter |
38 | 38 | if (scope.excludeEntityIds && scope.excludeEntityIds.length) { |
39 | 39 | limit += scope.excludeEntityIds.length; |
40 | 40 | } |
41 | - entityService.getEntitiesByNameFilter(scope.entityType, searchText, limit, {ignoreLoading: true}, scope.entitySubtype).then(function success(result) { | |
41 | + var targetType = scope.entityType; | |
42 | + if (targetType == types.aliasEntityType.current_customer) { | |
43 | + targetType = types.entityType.customer; | |
44 | + } | |
45 | + | |
46 | + entityService.getEntitiesByNameFilter(targetType, searchText, limit, {ignoreLoading: true}, scope.entitySubtype).then(function success(result) { | |
42 | 47 | if (result) { |
43 | 48 | if (scope.excludeEntityIds && scope.excludeEntityIds.length) { |
44 | 49 | var entities = []; |
... | ... | @@ -71,7 +76,11 @@ export default function EntityAutocomplete($compile, $templateCache, $q, $filter |
71 | 76 | |
72 | 77 | ngModelCtrl.$render = function () { |
73 | 78 | if (ngModelCtrl.$viewValue) { |
74 | - entityService.getEntity(scope.entityType, ngModelCtrl.$viewValue).then( | |
79 | + var targetType = scope.entityType; | |
80 | + if (targetType == types.aliasEntityType.current_customer) { | |
81 | + targetType = types.entityType.customer; | |
82 | + } | |
83 | + entityService.getEntity(targetType, ngModelCtrl.$viewValue).then( | |
75 | 84 | function success(entity) { |
76 | 85 | scope.entity = entity; |
77 | 86 | }, |
... | ... | @@ -114,55 +123,61 @@ export default function EntityAutocomplete($compile, $templateCache, $q, $filter |
114 | 123 | scope.selectEntityText = 'asset.select-asset'; |
115 | 124 | scope.entityText = 'asset.asset'; |
116 | 125 | scope.noEntitiesMatchingText = 'asset.no-assets-matching'; |
117 | - scope.entityRequiredText = 'asset.asset-required' | |
126 | + scope.entityRequiredText = 'asset.asset-required'; | |
118 | 127 | break; |
119 | 128 | case types.entityType.device: |
120 | 129 | scope.selectEntityText = 'device.select-device'; |
121 | 130 | scope.entityText = 'device.device'; |
122 | 131 | scope.noEntitiesMatchingText = 'device.no-devices-matching'; |
123 | - scope.entityRequiredText = 'device.device-required' | |
132 | + scope.entityRequiredText = 'device.device-required'; | |
124 | 133 | break; |
125 | 134 | case types.entityType.rule: |
126 | 135 | scope.selectEntityText = 'rule.select-rule'; |
127 | 136 | scope.entityText = 'rule.rule'; |
128 | 137 | scope.noEntitiesMatchingText = 'rule.no-rules-matching'; |
129 | - scope.entityRequiredText = 'rule.rule-required' | |
138 | + scope.entityRequiredText = 'rule.rule-required'; | |
130 | 139 | break; |
131 | 140 | case types.entityType.plugin: |
132 | 141 | scope.selectEntityText = 'plugin.select-plugin'; |
133 | 142 | scope.entityText = 'plugin.plugin'; |
134 | 143 | scope.noEntitiesMatchingText = 'plugin.no-plugins-matching'; |
135 | - scope.entityRequiredText = 'plugin.plugin-required' | |
144 | + scope.entityRequiredText = 'plugin.plugin-required'; | |
136 | 145 | break; |
137 | 146 | case types.entityType.tenant: |
138 | 147 | scope.selectEntityText = 'tenant.select-tenant'; |
139 | 148 | scope.entityText = 'tenant.tenant'; |
140 | 149 | scope.noEntitiesMatchingText = 'tenant.no-tenants-matching'; |
141 | - scope.entityRequiredText = 'tenant.tenant-required' | |
150 | + scope.entityRequiredText = 'tenant.tenant-required'; | |
142 | 151 | break; |
143 | 152 | case types.entityType.customer: |
144 | 153 | scope.selectEntityText = 'customer.select-customer'; |
145 | 154 | scope.entityText = 'customer.customer'; |
146 | 155 | scope.noEntitiesMatchingText = 'customer.no-customers-matching'; |
147 | - scope.entityRequiredText = 'customer.customer-required' | |
156 | + scope.entityRequiredText = 'customer.customer-required'; | |
148 | 157 | break; |
149 | 158 | case types.entityType.user: |
150 | 159 | scope.selectEntityText = 'user.select-user'; |
151 | 160 | scope.entityText = 'user.user'; |
152 | 161 | scope.noEntitiesMatchingText = 'user.no-users-matching'; |
153 | - scope.entityRequiredText = 'user.user-required' | |
162 | + scope.entityRequiredText = 'user.user-required'; | |
154 | 163 | break; |
155 | 164 | case types.entityType.dashboard: |
156 | 165 | scope.selectEntityText = 'dashboard.select-dashboard'; |
157 | 166 | scope.entityText = 'dashboard.dashboard'; |
158 | 167 | scope.noEntitiesMatchingText = 'dashboard.no-dashboards-matching'; |
159 | - scope.entityRequiredText = 'dashboard.dashboard-required' | |
168 | + scope.entityRequiredText = 'dashboard.dashboard-required'; | |
160 | 169 | break; |
161 | 170 | case types.entityType.alarm: |
162 | 171 | scope.selectEntityText = 'alarm.select-alarm'; |
163 | 172 | scope.entityText = 'alarm.alarm'; |
164 | 173 | scope.noEntitiesMatchingText = 'alarm.no-alarms-matching'; |
165 | - scope.entityRequiredText = 'alarm.alarm-required' | |
174 | + scope.entityRequiredText = 'alarm.alarm-required'; | |
175 | + break; | |
176 | + case types.aliasEntityType.current_customer: | |
177 | + scope.selectEntityText = 'customer.select-default-customer'; | |
178 | + scope.entityText = 'customer.default-customer'; | |
179 | + scope.noEntitiesMatchingText = 'customer.no-customers-matching'; | |
180 | + scope.entityRequiredText = 'customer.default-customer-required'; | |
166 | 181 | break; |
167 | 182 | } |
168 | 183 | if (scope.entity && scope.entity.id.entityType != scope.entityType) { | ... | ... |
... | ... | @@ -32,6 +32,7 @@ |
32 | 32 | <tb-entity-select flex |
33 | 33 | the-form="theForm" |
34 | 34 | tb-required="true" |
35 | + use-alias-entity-types="true" | |
35 | 36 | ng-model="filter.singleEntity"> |
36 | 37 | </tb-entity-select> |
37 | 38 | </section> |
... | ... | @@ -78,6 +79,7 @@ |
78 | 79 | <tb-entity-select flex |
79 | 80 | the-form="theForm" |
80 | 81 | tb-required="false" |
82 | + use-alias-entity-types="true" | |
81 | 83 | ng-model="filter.defaultStateEntity"> |
82 | 84 | </tb-entity-select> |
83 | 85 | </div> |
... | ... | @@ -123,6 +125,7 @@ |
123 | 125 | the-form="theForm" |
124 | 126 | tb-required="!filter.rootStateEntity" |
125 | 127 | ng-disabled="filter.rootStateEntity" |
128 | + use-alias-entity-types="true" | |
126 | 129 | ng-model="filter.rootEntity"> |
127 | 130 | </tb-entity-select> |
128 | 131 | </div> |
... | ... | @@ -139,6 +142,7 @@ |
139 | 142 | <tb-entity-select flex |
140 | 143 | the-form="theForm" |
141 | 144 | tb-required="false" |
145 | + use-alias-entity-types="true" | |
142 | 146 | ng-model="filter.defaultStateEntity"> |
143 | 147 | </tb-entity-select> |
144 | 148 | </div> |
... | ... | @@ -182,6 +186,7 @@ |
182 | 186 | the-form="theForm" |
183 | 187 | tb-required="!filter.rootStateEntity" |
184 | 188 | ng-disabled="filter.rootStateEntity" |
189 | + use-alias-entity-types="true" | |
185 | 190 | ng-model="filter.rootEntity"> |
186 | 191 | </tb-entity-select> |
187 | 192 | </div> |
... | ... | @@ -198,6 +203,7 @@ |
198 | 203 | <tb-entity-select flex |
199 | 204 | the-form="theForm" |
200 | 205 | tb-required="false" |
206 | + use-alias-entity-types="true" | |
201 | 207 | ng-model="filter.defaultStateEntity"> |
202 | 208 | </tb-entity-select> |
203 | 209 | </div> |
... | ... | @@ -249,6 +255,7 @@ |
249 | 255 | the-form="theForm" |
250 | 256 | tb-required="!filter.rootStateEntity" |
251 | 257 | ng-disabled="filter.rootStateEntity" |
258 | + use-alias-entity-types="true" | |
252 | 259 | ng-model="filter.rootEntity"> |
253 | 260 | </tb-entity-select> |
254 | 261 | </div> |
... | ... | @@ -265,6 +272,7 @@ |
265 | 272 | <tb-entity-select flex |
266 | 273 | the-form="theForm" |
267 | 274 | tb-required="false" |
275 | + use-alias-entity-types="true" | |
268 | 276 | ng-model="filter.defaultStateEntity"> |
269 | 277 | </tb-entity-select> |
270 | 278 | </div> | ... | ... |
... | ... | @@ -39,7 +39,7 @@ export default function EntityTypeSelect($compile, $templateCache, utils, entity |
39 | 39 | |
40 | 40 | scope.ngModelCtrl = ngModelCtrl; |
41 | 41 | |
42 | - scope.entityTypes = entityService.prepareAllowedEntityTypesList(scope.allowedEntityTypes); | |
42 | + scope.entityTypes = entityService.prepareAllowedEntityTypesList(scope.allowedEntityTypes, scope.useAliasEntityTypes); | |
43 | 43 | |
44 | 44 | scope.typeName = function(type) { |
45 | 45 | return type ? types.entityTypeTranslations[type].type : ''; |
... | ... | @@ -79,7 +79,8 @@ export default function EntityTypeSelect($compile, $templateCache, utils, entity |
79 | 79 | theForm: '=?', |
80 | 80 | tbRequired: '=?', |
81 | 81 | disabled:'=ngDisabled', |
82 | - allowedEntityTypes: "=?" | |
82 | + allowedEntityTypes: "=?", | |
83 | + useAliasEntityTypes: "=?" | |
83 | 84 | } |
84 | 85 | }; |
85 | 86 | } | ... | ... |
... | ... | @@ -540,7 +540,7 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document, |
540 | 540 | function success(dashboard) { |
541 | 541 | var name = dashboard.title; |
542 | 542 | name = name.toLowerCase().replace(/\W/g,"_"); |
543 | - exportToPc(prepareExport(dashboard), name + '.json'); | |
543 | + exportToPc(prepareDashboardExport(dashboard), name + '.json'); | |
544 | 544 | }, |
545 | 545 | function fail(rejection) { |
546 | 546 | var message = rejection; |
... | ... | @@ -552,6 +552,15 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document, |
552 | 552 | ); |
553 | 553 | } |
554 | 554 | |
555 | + function prepareDashboardExport(dashboard) { | |
556 | + dashboard = prepareExport(dashboard); | |
557 | + delete dashboard.assignedCustomers; | |
558 | + delete dashboard.publicCustomerId; | |
559 | + delete dashboard.assignedCustomersText; | |
560 | + delete dashboard.assignedCustomersIds; | |
561 | + return dashboard; | |
562 | + } | |
563 | + | |
555 | 564 | function importDashboard($event) { |
556 | 565 | var deferred = $q.defer(); |
557 | 566 | openImportDialog($event, 'dashboard.import', 'dashboard.dashboard-file').then( | ... | ... |
... | ... | @@ -383,7 +383,10 @@ export default angular.module('thingsboard.locale', []) |
383 | 383 | "idCopiedMessage": "Customer Id has been copied to clipboard", |
384 | 384 | "select-customer": "Select customer", |
385 | 385 | "no-customers-matching": "No customers matching '{{entity}}' were found.", |
386 | - "customer-required": "Customer is required" | |
386 | + "customer-required": "Customer is required", | |
387 | + "select-default-customer": "Select default customer", | |
388 | + "default-customer": "Default customer", | |
389 | + "default-customer-required": "Default customer is required in order to debug dashboard on Tenant level" | |
387 | 390 | }, |
388 | 391 | "datetime": { |
389 | 392 | "date-from": "Date from", |
... | ... | @@ -404,6 +407,12 @@ export default angular.module('thingsboard.locale', []) |
404 | 407 | "unassign-from-customer": "Unassign from customer", |
405 | 408 | "make-public": "Make dashboard public", |
406 | 409 | "make-private": "Make dashboard private", |
410 | + "manage-assigned-customers": "Manage assigned customers", | |
411 | + "assigned-customers": "Assigned customers", | |
412 | + "assign-to-customers": "Assign Dashboard(s) To Customers", | |
413 | + "assign-to-customers-text": "Please select the customers to assign the dashboard(s)", | |
414 | + "unassign-from-customers": "Unassign Dashboard(s) From Customers", | |
415 | + "unassign-from-customers-text": "Please select the customers to unassign from the dashboard(s)", | |
407 | 416 | "no-dashboards-text": "No dashboards found", |
408 | 417 | "no-widgets": "No widgets configured", |
409 | 418 | "add-widget": "Add new widget", |
... | ... | @@ -418,7 +427,8 @@ export default angular.module('thingsboard.locale', []) |
418 | 427 | "add-dashboard-text": "Add new dashboard", |
419 | 428 | "assign-dashboards": "Assign dashboards", |
420 | 429 | "assign-new-dashboard": "Assign new dashboard", |
421 | - "assign-dashboards-text": "Assign { count, select, 1 {1 dashboard} other {# dashboards} } to customer", | |
430 | + "assign-dashboards-text": "Assign { count, select, 1 {1 dashboard} other {# dashboards} } to customers", | |
431 | + "unassign-dashboards-action-text": "Unassign { count, select, 1 {1 dashboard} other {# dashboards} } from customers", | |
422 | 432 | "delete-dashboards": "Delete dashboards", |
423 | 433 | "unassign-dashboards": "Unassign dashboards", |
424 | 434 | "unassign-dashboards-action-title": "Unassign { count, select, 1 {1 dashboard} other {# dashboards} } from customer", |
... | ... | @@ -500,6 +510,7 @@ export default angular.module('thingsboard.locale', []) |
500 | 510 | "Please contact your administrator in order to resolve this issue.", |
501 | 511 | "select-devices": "Select devices", |
502 | 512 | "assignedToCustomer": "Assigned to customer", |
513 | + "assignedToCustomers": "Assigned to customers", | |
503 | 514 | "public": "Public", |
504 | 515 | "public-link": "Public link", |
505 | 516 | "copy-public-link": "Copy public link", |
... | ... | @@ -735,6 +746,7 @@ export default angular.module('thingsboard.locale', []) |
735 | 746 | "type-alarms": "Alarms", |
736 | 747 | "list-of-alarms": "{ count, select, 1 {One alarms} other {List of # alarms} }", |
737 | 748 | "alarm-name-starts-with": "Alarms whose names start with '{{prefix}}'", |
749 | + "type-current-customer": "Current Customer", | |
738 | 750 | "search": "Search entities", |
739 | 751 | "selected-entities": "{ count, select, 1 {1 entity} other {# entities} } selected", |
740 | 752 | "entity-name": "Entity name", | ... | ... |