Showing
5 changed files
with
148 additions
and
4 deletions
... | ... | @@ -19,16 +19,22 @@ import com.google.common.util.concurrent.ListenableFuture; |
19 | 19 | import org.springframework.http.HttpStatus; |
20 | 20 | import org.springframework.security.access.prepost.PreAuthorize; |
21 | 21 | import org.springframework.web.bind.annotation.*; |
22 | +import org.thingsboard.server.common.data.Customer; | |
23 | +import org.thingsboard.server.common.data.Device; | |
22 | 24 | import org.thingsboard.server.common.data.EntitySubtype; |
23 | 25 | import org.thingsboard.server.common.data.EntityType; |
24 | 26 | import org.thingsboard.server.common.data.EntityView; |
25 | 27 | import org.thingsboard.server.common.data.audit.ActionType; |
26 | 28 | import org.thingsboard.server.common.data.entityview.EntityViewSearchQuery; |
27 | 29 | import org.thingsboard.server.common.data.exception.ThingsboardException; |
30 | +import org.thingsboard.server.common.data.id.CustomerId; | |
31 | +import org.thingsboard.server.common.data.id.DeviceId; | |
28 | 32 | import org.thingsboard.server.common.data.id.EntityViewId; |
29 | 33 | import org.thingsboard.server.common.data.id.TenantId; |
30 | 34 | import org.thingsboard.server.common.data.page.TextPageData; |
31 | 35 | import org.thingsboard.server.common.data.page.TextPageLink; |
36 | +import org.thingsboard.server.dao.exception.IncorrectParameterException; | |
37 | +import org.thingsboard.server.dao.model.ModelConstants; | |
32 | 38 | import org.thingsboard.server.service.security.model.SecurityUser; |
33 | 39 | |
34 | 40 | import java.util.ArrayList; |
... | ... | @@ -101,6 +107,84 @@ public class EntityViewController extends BaseController { |
101 | 107 | } |
102 | 108 | |
103 | 109 | @PreAuthorize("hasAuthority('TENANT_ADMIN')") |
110 | + @RequestMapping(value = "/customer/{customerId}/entityView/{entityViewId}", method = RequestMethod.POST) | |
111 | + @ResponseBody | |
112 | + public EntityView assignEntityViewToCustomer(@PathVariable("customerId") String strCustomerId, | |
113 | + @PathVariable(ENTITY_VIEW_ID) String strEntityViewId) throws ThingsboardException { | |
114 | + checkParameter("customerId", strCustomerId); | |
115 | + checkParameter(ENTITY_VIEW_ID, strEntityViewId); | |
116 | + try { | |
117 | + CustomerId customerId = new CustomerId(toUUID(strCustomerId)); | |
118 | + Customer customer = checkCustomerId(customerId); | |
119 | + | |
120 | + EntityViewId entityViewId = new EntityViewId(toUUID(strEntityViewId)); | |
121 | + checkEntityViewId(entityViewId); | |
122 | + | |
123 | + EntityView savedEntityView = checkNotNull(entityViewService.assignEntityViewToCustomer(entityViewId, customerId)); | |
124 | + | |
125 | + logEntityAction(entityViewId, savedEntityView, | |
126 | + savedEntityView.getCustomerId(), | |
127 | + ActionType.ASSIGNED_TO_CUSTOMER, null, strEntityViewId, strCustomerId, customer.getName()); | |
128 | + | |
129 | + return savedEntityView; | |
130 | + } catch (Exception e) { | |
131 | + logEntityAction(emptyId(EntityType.ENTITY_VIEW), null, | |
132 | + null, | |
133 | + ActionType.ASSIGNED_TO_CUSTOMER, e, strEntityViewId, strCustomerId); | |
134 | + throw handleException(e); | |
135 | + } | |
136 | + } | |
137 | + | |
138 | + @PreAuthorize("hasAuthority('TENANT_ADMIN')") | |
139 | + @RequestMapping(value = "/customer/entityView/{entityViewId}", method = RequestMethod.DELETE) | |
140 | + @ResponseBody | |
141 | + public EntityView unassignEntityViewFromCustomer(@PathVariable(ENTITY_VIEW_ID) String strEntityViewId) throws ThingsboardException { | |
142 | + checkParameter(ENTITY_VIEW_ID, strEntityViewId); | |
143 | + try { | |
144 | + EntityViewId entityViewId = new EntityViewId(toUUID(strEntityViewId)); | |
145 | + EntityView entityView = checkEntityViewId(entityViewId); | |
146 | + if (entityView.getCustomerId() == null || entityView.getCustomerId().getId().equals(ModelConstants.NULL_UUID)) { | |
147 | + throw new IncorrectParameterException("Entity View isn't assigned to any customer!"); | |
148 | + } | |
149 | + Customer customer = checkCustomerId(entityView.getCustomerId()); | |
150 | + | |
151 | + EntityView savedEntityView = checkNotNull(entityViewService.unassignEntityViewFromCustomer(entityViewId)); | |
152 | + | |
153 | + logEntityAction(entityViewId, entityView, | |
154 | + entityView.getCustomerId(), | |
155 | + ActionType.UNASSIGNED_FROM_CUSTOMER, null, strEntityViewId, customer.getId().toString(), customer.getName()); | |
156 | + | |
157 | + return savedEntityView; | |
158 | + } catch (Exception e) { | |
159 | + logEntityAction(emptyId(EntityType.ENTITY_VIEW), null, | |
160 | + null, | |
161 | + ActionType.UNASSIGNED_FROM_CUSTOMER, e, strEntityViewId); | |
162 | + throw handleException(e); | |
163 | + } | |
164 | + } | |
165 | + | |
166 | + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") | |
167 | + @RequestMapping(value = "/customer/{customerId}/entityViews", params = {"limit"}, method = RequestMethod.GET) | |
168 | + @ResponseBody | |
169 | + public TextPageData<EntityView> getCustomerEntityViews( | |
170 | + @PathVariable("customerId") String strCustomerId, | |
171 | + @RequestParam int limit, | |
172 | + @RequestParam(required = false) String textSearch, | |
173 | + @RequestParam(required = false) String idOffset, | |
174 | + @RequestParam(required = false) String textOffset) throws ThingsboardException { | |
175 | + checkParameter("customerId", strCustomerId); | |
176 | + try { | |
177 | + TenantId tenantId = getCurrentUser().getTenantId(); | |
178 | + CustomerId customerId = new CustomerId(toUUID(strCustomerId)); | |
179 | + checkCustomerId(customerId); | |
180 | + TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset); | |
181 | + return checkNotNull(entityViewService.findEntityViewsByTenantIdAndCustomerId(tenantId, customerId, pageLink)); | |
182 | + } catch (Exception e) { | |
183 | + throw handleException(e); | |
184 | + } | |
185 | + } | |
186 | + | |
187 | + @PreAuthorize("hasAuthority('TENANT_ADMIN')") | |
104 | 188 | @RequestMapping(value = "/tenant/entityViews", params = {"limit"}, method = RequestMethod.GET) |
105 | 189 | @ResponseBody |
106 | 190 | public TextPageData<EntityView> getTenantEntityViews( | ... | ... |
... | ... | @@ -52,9 +52,33 @@ |
52 | 52 | <div translate ng-message="required">entity-view.name-required</div> |
53 | 53 | </div> |
54 | 54 | </md-input-container> |
55 | + <tb-entity-select flex ng-disabled="!isEdit" | |
56 | + the-form="theForm" | |
57 | + tb-required="true" | |
58 | + allowed-entity-types="allowedEntityTypes" | |
59 | + ng-model="entityView.entityId"> | |
60 | + </tb-entity-select> | |
55 | 61 | <md-input-container class="md-block"> |
56 | 62 | <label translate>entity-view.description</label> |
57 | 63 | <textarea ng-model="entityView.additionalInfo.description" rows="2"></textarea> |
58 | 64 | </md-input-container> |
65 | + <section layout="row" layout-align="start start"> | |
66 | + <mdp-date-picker ng-model="startTs" | |
67 | + mdp-max-date="maxStartTs" | |
68 | + mdp-placeholder="{{ 'entity-view.start-ts' | translate }}"></mdp-date-picker> | |
69 | + <mdp-time-picker ng-model="startTs" | |
70 | + mdp-max-date="maxStartTs" | |
71 | + mdp-placeholder="{{ 'entity-view.start-ts' | translate }}" | |
72 | + mdp-auto-switch="true"></mdp-time-picker> | |
73 | + </section> | |
74 | + <section layout="row" layout-align="start start"> | |
75 | + <mdp-date-picker ng-model="endTs" | |
76 | + mdp-min-date="minEndTs" | |
77 | + mdp-placeholder="{{ 'entity-view.end-ts' | translate }}"></mdp-date-picker> | |
78 | + <mdp-time-picker ng-model="endTs" | |
79 | + mdp-min-date="minEndTs" | |
80 | + mdp-placeholder="{{ 'entity-view.end-ts' | translate }}" | |
81 | + mdp-auto-switch="true"></mdp-time-picker> | |
82 | + </section> | |
59 | 83 | </fieldset> |
60 | 84 | </md-content> | ... | ... |
... | ... | @@ -20,7 +20,7 @@ import entityViewFieldsetTemplate from './entity-view-fieldset.tpl.html'; |
20 | 20 | /* eslint-enable import/no-unresolved, import/default */ |
21 | 21 | |
22 | 22 | /*@ngInject*/ |
23 | -export default function EntityViewDirective($compile, $templateCache, toast, $translate, types, clipboardService, entityViewService, customerService) { | |
23 | +export default function EntityViewDirective($compile, $templateCache, $filter, toast, $translate, types, clipboardService, entityViewService, customerService) { | |
24 | 24 | var linker = function (scope, element) { |
25 | 25 | var template = $templateCache.get(entityViewFieldsetTemplate); |
26 | 26 | element.html(template); |
... | ... | @@ -30,6 +30,8 @@ export default function EntityViewDirective($compile, $templateCache, toast, $tr |
30 | 30 | scope.isPublic = false; |
31 | 31 | scope.assignedCustomer = null; |
32 | 32 | |
33 | + scope.allowedEntityTypes = [types.entityType.device, types.entityType.asset]; | |
34 | + | |
33 | 35 | scope.$watch('entityView', function(newVal) { |
34 | 36 | if (newVal) { |
35 | 37 | if (scope.entityView.customerId && scope.entityView.customerId.id !== types.id.nullUid) { |
... | ... | @@ -45,9 +47,41 @@ export default function EntityViewDirective($compile, $templateCache, toast, $tr |
45 | 47 | scope.isPublic = false; |
46 | 48 | scope.assignedCustomer = null; |
47 | 49 | } |
50 | + scope.startTs = $filter('date')(scope.entityView.endTs, 'yyyy-MM-dd HH:mm:ss'); | |
51 | + scope.endTs = $filter('date')(scope.entityView.startTs, 'yyyy-MM-dd HH:mm:ss'); | |
52 | + } | |
53 | + }); | |
54 | + | |
55 | + | |
56 | + scope.$watch('startTs', function (newDate) { | |
57 | + if (newDate) { | |
58 | + if (newDate.getTime() > scope.maxStartTs) { | |
59 | + scope.startTs = angular.copy(scope.maxStartTs); | |
60 | + } | |
61 | + updateMinMaxDates(); | |
62 | + } | |
63 | + }); | |
64 | + | |
65 | + scope.$watch('endTs', function (newDate) { | |
66 | + if (newDate) { | |
67 | + if (newDate.getTime() < scope.minEndTs) { | |
68 | + scope.endTs = angular.copy(scope.minEndTs); | |
69 | + } | |
70 | + updateMinMaxDates(); | |
48 | 71 | } |
49 | 72 | }); |
50 | 73 | |
74 | + function updateMinMaxDates() { | |
75 | + if (scope.endTs) { | |
76 | + scope.maxStartTs = angular.copy(new Date(scope.endTs.getTime() - 1000)); | |
77 | + scope.entityView.endTs = $filter('date')(scope.endTs, 'yyyy-MM-dd HH:mm:ss'); | |
78 | + } | |
79 | + if (scope.startTs) { | |
80 | + scope.minEndTs = angular.copy(new Date(scope.startTs.getTime() + 1000)); | |
81 | + scope.entityView.startTs = $filter('date')(scope.startTs, 'yyyy-MM-dd HH:mm:ss'); | |
82 | + } | |
83 | + } | |
84 | + | |
51 | 85 | scope.onEntityViewIdCopied = function() { |
52 | 86 | toast.showSuccess($translate.instant('entity-view.idCopiedMessage'), 750, angular.element(element).parent().parent(), 'bottom left'); |
53 | 87 | }; | ... | ... |
... | ... | @@ -38,7 +38,7 @@ export default function EntityViewRoutes($stateProvider, types) { |
38 | 38 | entityViewsType: 'tenant', |
39 | 39 | searchEnabled: true, |
40 | 40 | searchByEntitySubtype: true, |
41 | - searchEntityType: types.entityType.entityview, | |
41 | + searchEntityType: types.entityType.entityView, | |
42 | 42 | pageTitle: 'entity-view.entity-views' |
43 | 43 | }, |
44 | 44 | ncyBreadcrumb: { |
... | ... | @@ -61,7 +61,7 @@ export default function EntityViewRoutes($stateProvider, types) { |
61 | 61 | entityViewsType: 'customer', |
62 | 62 | searchEnabled: true, |
63 | 63 | searchByEntitySubtype: true, |
64 | - searchEntityType: types.entityType.entityview, | |
64 | + searchEntityType: types.entityType.entityView, | |
65 | 65 | pageTitle: 'customer.entity-views' |
66 | 66 | }, |
67 | 67 | ncyBreadcrumb: { | ... | ... |
... | ... | @@ -822,7 +822,9 @@ |
822 | 822 | "unable-entity-view-device-alias-title": "Unable to delete entity view alias", |
823 | 823 | "unable-entity-view-device-alias-text": "Device alias '{{entityViewAlias}}' can't be deleted as it used by the following widget(s):<br/>{{widgetsList}}", |
824 | 824 | "select-entity-view": "Select entity view", |
825 | - "make-public": "Make entity view public" | |
825 | + "make-public": "Make entity view public", | |
826 | + "start-ts": "Start ts", | |
827 | + "end-ts": "End ts" | |
826 | 828 | }, |
827 | 829 | "event": { |
828 | 830 | "event-type": "Event type", | ... | ... |