Showing
23 changed files
with
865 additions
and
6 deletions
... | ... | @@ -46,6 +46,7 @@ import org.thingsboard.server.dao.device.DeviceService; |
46 | 46 | import org.thingsboard.server.dao.event.EventService; |
47 | 47 | import org.thingsboard.server.dao.plugin.PluginService; |
48 | 48 | import org.thingsboard.server.dao.relation.RelationService; |
49 | +import org.thingsboard.server.dao.rule.RuleChainService; | |
49 | 50 | import org.thingsboard.server.dao.rule.RuleService; |
50 | 51 | import org.thingsboard.server.dao.tenant.TenantService; |
51 | 52 | import org.thingsboard.server.dao.timeseries.TimeseriesService; |
... | ... | @@ -97,6 +98,9 @@ public class ActorSystemContext { |
97 | 98 | @Getter private RuleService ruleService; |
98 | 99 | |
99 | 100 | @Autowired |
101 | + @Getter private RuleChainService ruleChainService; | |
102 | + | |
103 | + @Autowired | |
100 | 104 | @Getter private PluginService pluginService; |
101 | 105 | |
102 | 106 | @Autowired | ... | ... |
... | ... | @@ -36,6 +36,7 @@ import org.thingsboard.server.common.data.page.TextPageLink; |
36 | 36 | import org.thingsboard.server.common.data.plugin.PluginMetaData; |
37 | 37 | import org.thingsboard.server.common.data.relation.EntityRelation; |
38 | 38 | import org.thingsboard.server.common.data.relation.RelationTypeGroup; |
39 | +import org.thingsboard.server.common.data.rule.RuleChain; | |
39 | 40 | import org.thingsboard.server.common.data.rule.RuleMetaData; |
40 | 41 | import org.thingsboard.server.common.msg.cluster.ServerAddress; |
41 | 42 | import org.thingsboard.server.extensions.api.device.DeviceAttributesEventNotificationMsg; |
... | ... | @@ -330,6 +331,9 @@ public final class PluginProcessingContext implements PluginContext { |
330 | 331 | case RULE: |
331 | 332 | validateRule(ctx, entityId, callback); |
332 | 333 | return; |
334 | + case RULE_CHAIN: | |
335 | + validateRuleChain(ctx, entityId, callback); | |
336 | + return; | |
333 | 337 | case PLUGIN: |
334 | 338 | validatePlugin(ctx, entityId, callback); |
335 | 339 | return; |
... | ... | @@ -411,6 +415,28 @@ public final class PluginProcessingContext implements PluginContext { |
411 | 415 | } |
412 | 416 | } |
413 | 417 | |
418 | + private void validateRuleChain(final PluginApiCallSecurityContext ctx, EntityId entityId, ValidationCallback callback) { | |
419 | + if (ctx.isCustomerUser()) { | |
420 | + callback.onSuccess(this, ValidationResult.accessDenied(CUSTOMER_USER_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION)); | |
421 | + } else { | |
422 | + ListenableFuture<RuleChain> ruleChainFuture = pluginCtx.ruleChainService.findRuleChainByIdAsync(new RuleChainId(entityId.getId())); | |
423 | + Futures.addCallback(ruleChainFuture, getCallback(callback, ruleChain -> { | |
424 | + if (ruleChain == null) { | |
425 | + return ValidationResult.entityNotFound("Rule chain with requested id wasn't found!"); | |
426 | + } else { | |
427 | + if (ctx.isTenantAdmin() && !ruleChain.getTenantId().equals(ctx.getTenantId())) { | |
428 | + return ValidationResult.accessDenied("Rule chain doesn't belong to the current Tenant!"); | |
429 | + } else if (ctx.isSystemAdmin() && !ruleChain.getTenantId().isNullUid()) { | |
430 | + return ValidationResult.accessDenied("Rule chain is not in system scope!"); | |
431 | + } else { | |
432 | + return ValidationResult.ok(); | |
433 | + } | |
434 | + } | |
435 | + })); | |
436 | + } | |
437 | + } | |
438 | + | |
439 | + | |
414 | 440 | private void validatePlugin(final PluginApiCallSecurityContext ctx, EntityId entityId, ValidationCallback callback) { |
415 | 441 | if (ctx.isCustomerUser()) { |
416 | 442 | callback.onSuccess(this, ValidationResult.accessDenied(CUSTOMER_USER_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION)); | ... | ... |
... | ... | @@ -32,6 +32,7 @@ import org.thingsboard.server.dao.customer.CustomerService; |
32 | 32 | import org.thingsboard.server.dao.device.DeviceService; |
33 | 33 | import org.thingsboard.server.dao.plugin.PluginService; |
34 | 34 | import org.thingsboard.server.dao.relation.RelationService; |
35 | +import org.thingsboard.server.dao.rule.RuleChainService; | |
35 | 36 | import org.thingsboard.server.dao.rule.RuleService; |
36 | 37 | import org.thingsboard.server.dao.tenant.TenantService; |
37 | 38 | import org.thingsboard.server.dao.timeseries.TimeseriesService; |
... | ... | @@ -56,6 +57,7 @@ public final class SharedPluginProcessingContext { |
56 | 57 | final AssetService assetService; |
57 | 58 | final DeviceService deviceService; |
58 | 59 | final RuleService ruleService; |
60 | + final RuleChainService ruleChainService; | |
59 | 61 | final PluginService pluginService; |
60 | 62 | final CustomerService customerService; |
61 | 63 | final TenantService tenantService; |
... | ... | @@ -84,6 +86,7 @@ public final class SharedPluginProcessingContext { |
84 | 86 | this.rpcService = sysContext.getRpcService(); |
85 | 87 | this.routingService = sysContext.getRoutingService(); |
86 | 88 | this.ruleService = sysContext.getRuleService(); |
89 | + this.ruleChainService = sysContext.getRuleChainService(); | |
87 | 90 | this.pluginService = sysContext.getPluginService(); |
88 | 91 | this.customerService = sysContext.getCustomerService(); |
89 | 92 | this.tenantService = sysContext.getTenantService(); | ... | ... |
... | ... | @@ -550,6 +550,8 @@ public abstract class BaseController { |
550 | 550 | throw new ThingsboardException(YOU_DON_T_HAVE_PERMISSION_TO_PERFORM_THIS_OPERATION, |
551 | 551 | ThingsboardErrorCode.PERMISSION_DENIED); |
552 | 552 | |
553 | + } else if (tenantId.getId().equals(ModelConstants.NULL_UUID)) { | |
554 | + ruleChain.setConfiguration(null); | |
553 | 555 | } |
554 | 556 | } |
555 | 557 | return ruleChain; | ... | ... |
... | ... | @@ -28,6 +28,7 @@ import org.thingsboard.server.common.data.page.TextPageLink; |
28 | 28 | import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; |
29 | 29 | import org.thingsboard.server.common.data.plugin.PluginMetaData; |
30 | 30 | import org.thingsboard.server.common.data.rule.RuleChain; |
31 | +import org.thingsboard.server.common.data.rule.RuleChainMetaData; | |
31 | 32 | import org.thingsboard.server.common.data.security.Authority; |
32 | 33 | import org.thingsboard.server.dao.model.ModelConstants; |
33 | 34 | import org.thingsboard.server.exception.ThingsboardException; |
... | ... | @@ -54,6 +55,21 @@ public class RuleChainController extends BaseController { |
54 | 55 | } |
55 | 56 | |
56 | 57 | @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") |
58 | + @RequestMapping(value = "/ruleChain/{ruleChainId}/metadata", method = RequestMethod.GET) | |
59 | + @ResponseBody | |
60 | + public RuleChainMetaData getRuleChainMetaData(@PathVariable(RULE_CHAIN_ID) String strRuleChainId) throws ThingsboardException { | |
61 | + checkParameter(RULE_CHAIN_ID, strRuleChainId); | |
62 | + try { | |
63 | + RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId)); | |
64 | + checkRuleChain(ruleChainId); | |
65 | + return ruleChainService.loadRuleChainMetaData(ruleChainId); | |
66 | + } catch (Exception e) { | |
67 | + throw handleException(e); | |
68 | + } | |
69 | + } | |
70 | + | |
71 | + | |
72 | + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") | |
57 | 73 | @RequestMapping(value = "/ruleChain", method = RequestMethod.POST) |
58 | 74 | @ResponseBody |
59 | 75 | public RuleChain saveRuleChain(@RequestBody RuleChain ruleChain) throws ThingsboardException { |
... | ... | @@ -77,6 +93,28 @@ public class RuleChainController extends BaseController { |
77 | 93 | } |
78 | 94 | |
79 | 95 | @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") |
96 | + @RequestMapping(value = "/ruleChain/metadata", method = RequestMethod.POST) | |
97 | + @ResponseBody | |
98 | + public RuleChainMetaData saveRuleChainMetaData(@RequestBody RuleChainMetaData ruleChainMetaData) throws ThingsboardException { | |
99 | + try { | |
100 | + RuleChain ruleChain = checkRuleChain(ruleChainMetaData.getRuleChainId()); | |
101 | + RuleChainMetaData savedRuleChainMetaData = checkNotNull(ruleChainService.saveRuleChainMetaData(ruleChainMetaData)); | |
102 | + | |
103 | + logEntityAction(ruleChain.getId(), ruleChain, | |
104 | + null, | |
105 | + ActionType.UPDATED, null, ruleChainMetaData); | |
106 | + | |
107 | + return savedRuleChainMetaData; | |
108 | + } catch (Exception e) { | |
109 | + | |
110 | + logEntityAction(emptyId(EntityType.RULE_CHAIN), null, | |
111 | + null, ActionType.UPDATED, e, ruleChainMetaData); | |
112 | + | |
113 | + throw handleException(e); | |
114 | + } | |
115 | + } | |
116 | + | |
117 | + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") | |
80 | 118 | @RequestMapping(value = "/ruleChains", params = {"limit"}, method = RequestMethod.GET) |
81 | 119 | @ResponseBody |
82 | 120 | public TextPageData<RuleChain> getRuleChains( | ... | ... |
... | ... | @@ -38,6 +38,7 @@ import org.thingsboard.server.common.data.id.*; |
38 | 38 | import org.thingsboard.server.common.data.kv.AttributeKvEntry; |
39 | 39 | import org.thingsboard.server.common.data.page.TimePageData; |
40 | 40 | import org.thingsboard.server.common.data.page.TimePageLink; |
41 | +import org.thingsboard.server.common.data.rule.RuleChainMetaData; | |
41 | 42 | import org.thingsboard.server.common.data.security.DeviceCredentials; |
42 | 43 | import org.thingsboard.server.dao.audit.sink.AuditLogSink; |
43 | 44 | import org.thingsboard.server.dao.entity.EntityService; |
... | ... | @@ -158,11 +159,20 @@ public class AuditLogServiceImpl implements AuditLogService { |
158 | 159 | switch(actionType) { |
159 | 160 | case ADDED: |
160 | 161 | case UPDATED: |
161 | - ObjectNode entityNode = objectMapper.valueToTree(entity); | |
162 | - if (entityId.getEntityType() == EntityType.DASHBOARD) { | |
163 | - entityNode.put("configuration", ""); | |
162 | + if (entity != null) { | |
163 | + ObjectNode entityNode = objectMapper.valueToTree(entity); | |
164 | + if (entityId.getEntityType() == EntityType.DASHBOARD) { | |
165 | + entityNode.put("configuration", ""); | |
166 | + } | |
167 | + actionData.set("entity", entityNode); | |
168 | + } | |
169 | + if (entityId.getEntityType() == EntityType.RULE_CHAIN) { | |
170 | + RuleChainMetaData ruleChainMetaData = extractParameter(RuleChainMetaData.class, additionalInfo); | |
171 | + if (ruleChainMetaData != null) { | |
172 | + ObjectNode ruleChainMetaDataNode = objectMapper.valueToTree(ruleChainMetaData); | |
173 | + actionData.set("metadata", ruleChainMetaDataNode); | |
174 | + } | |
164 | 175 | } |
165 | - actionData.set("entity", entityNode); | |
166 | 176 | break; |
167 | 177 | case DELETED: |
168 | 178 | case ACTIVATED: | ... | ... |
... | ... | @@ -16,6 +16,7 @@ |
16 | 16 | |
17 | 17 | package org.thingsboard.server.dao.rule; |
18 | 18 | |
19 | +import com.google.common.util.concurrent.ListenableFuture; | |
19 | 20 | import lombok.extern.slf4j.Slf4j; |
20 | 21 | import org.apache.commons.lang3.StringUtils; |
21 | 22 | import org.springframework.beans.factory.annotation.Autowired; |
... | ... | @@ -219,6 +220,12 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC |
219 | 220 | } |
220 | 221 | |
221 | 222 | @Override |
223 | + public ListenableFuture<RuleChain> findRuleChainByIdAsync(RuleChainId ruleChainId) { | |
224 | + Validator.validateId(ruleChainId, "Incorrect rule chain id for search request."); | |
225 | + return ruleChainDao.findByIdAsync(ruleChainId.getId()); | |
226 | + } | |
227 | + | |
228 | + @Override | |
222 | 229 | public RuleChain getRootTenantRuleChain(TenantId tenantId) { |
223 | 230 | Validator.validateId(tenantId, "Incorrect tenant id for search request."); |
224 | 231 | List<EntityRelation> relations = relationService.findByFrom(tenantId, RelationTypeGroup.RULE_CHAIN); | ... | ... |
... | ... | @@ -16,6 +16,7 @@ |
16 | 16 | |
17 | 17 | package org.thingsboard.server.dao.rule; |
18 | 18 | |
19 | +import com.google.common.util.concurrent.ListenableFuture; | |
19 | 20 | import org.thingsboard.server.common.data.id.RuleChainId; |
20 | 21 | import org.thingsboard.server.common.data.id.RuleNodeId; |
21 | 22 | import org.thingsboard.server.common.data.id.TenantId; |
... | ... | @@ -41,6 +42,8 @@ public interface RuleChainService { |
41 | 42 | |
42 | 43 | RuleChain findRuleChainById(RuleChainId ruleChainId); |
43 | 44 | |
45 | + ListenableFuture<RuleChain> findRuleChainByIdAsync(RuleChainId ruleChainId); | |
46 | + | |
44 | 47 | RuleChain getRootTenantRuleChain(TenantId tenantId); |
45 | 48 | |
46 | 49 | List<RuleNode> getRuleChainNodes(RuleChainId ruleChainId); | ... | ... |
ui/src/app/api/rule-chain.service.js
0 → 100644
1 | +/* | |
2 | + * Copyright © 2016-2018 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 | +export default angular.module('thingsboard.api.ruleChain', []) | |
17 | + .factory('ruleChainService', RuleChainService).name; | |
18 | + | |
19 | +/*@ngInject*/ | |
20 | +function RuleChainService($http, $q) { | |
21 | + | |
22 | + var service = { | |
23 | + getSystemRuleChains: getSystemRuleChains, | |
24 | + getTenantRuleChains: getTenantRuleChains, | |
25 | + getRuleChains: getRuleChains, | |
26 | + getRuleChain: getRuleChain, | |
27 | + saveRuleChain: saveRuleChain, | |
28 | + deleteRuleChain: deleteRuleChain, | |
29 | + getRuleChainMetaData: getRuleChainMetaData, | |
30 | + saveRuleChainMetaData: saveRuleChainMetaData | |
31 | + }; | |
32 | + | |
33 | + return service; | |
34 | + | |
35 | + function getSystemRuleChains (pageLink, config) { | |
36 | + var deferred = $q.defer(); | |
37 | + var url = '/api/system/ruleChains?limit=' + pageLink.limit; | |
38 | + if (angular.isDefined(pageLink.textSearch)) { | |
39 | + url += '&textSearch=' + pageLink.textSearch; | |
40 | + } | |
41 | + if (angular.isDefined(pageLink.idOffset)) { | |
42 | + url += '&idOffset=' + pageLink.idOffset; | |
43 | + } | |
44 | + if (angular.isDefined(pageLink.textOffset)) { | |
45 | + url += '&textOffset=' + pageLink.textOffset; | |
46 | + } | |
47 | + $http.get(url, config).then(function success(response) { | |
48 | + deferred.resolve(response.data); | |
49 | + }, function fail() { | |
50 | + deferred.reject(); | |
51 | + }); | |
52 | + return deferred.promise; | |
53 | + } | |
54 | + | |
55 | + function getTenantRuleChains (pageLink, config) { | |
56 | + var deferred = $q.defer(); | |
57 | + var url = '/api/tenant/ruleChains?limit=' + pageLink.limit; | |
58 | + if (angular.isDefined(pageLink.textSearch)) { | |
59 | + url += '&textSearch=' + pageLink.textSearch; | |
60 | + } | |
61 | + if (angular.isDefined(pageLink.idOffset)) { | |
62 | + url += '&idOffset=' + pageLink.idOffset; | |
63 | + } | |
64 | + if (angular.isDefined(pageLink.textOffset)) { | |
65 | + url += '&textOffset=' + pageLink.textOffset; | |
66 | + } | |
67 | + $http.get(url, config).then(function success(response) { | |
68 | + deferred.resolve(response.data); | |
69 | + }, function fail() { | |
70 | + deferred.reject(); | |
71 | + }); | |
72 | + return deferred.promise; | |
73 | + } | |
74 | + | |
75 | + function getRuleChains (pageLink, config) { | |
76 | + var deferred = $q.defer(); | |
77 | + var url = '/api/ruleChains?limit=' + pageLink.limit; | |
78 | + if (angular.isDefined(pageLink.textSearch)) { | |
79 | + url += '&textSearch=' + pageLink.textSearch; | |
80 | + } | |
81 | + if (angular.isDefined(pageLink.idOffset)) { | |
82 | + url += '&idOffset=' + pageLink.idOffset; | |
83 | + } | |
84 | + if (angular.isDefined(pageLink.textOffset)) { | |
85 | + url += '&textOffset=' + pageLink.textOffset; | |
86 | + } | |
87 | + $http.get(url, config).then(function success(response) { | |
88 | + deferred.resolve(response.data); | |
89 | + }, function fail() { | |
90 | + deferred.reject(); | |
91 | + }); | |
92 | + return deferred.promise; | |
93 | + } | |
94 | + | |
95 | + function getRuleChain(ruleChainId, config) { | |
96 | + var deferred = $q.defer(); | |
97 | + var url = '/api/ruleChain/' + ruleChainId; | |
98 | + $http.get(url, config).then(function success(response) { | |
99 | + deferred.resolve(response.data); | |
100 | + }, function fail() { | |
101 | + deferred.reject(); | |
102 | + }); | |
103 | + return deferred.promise; | |
104 | + } | |
105 | + | |
106 | + function saveRuleChain(ruleChain) { | |
107 | + var deferred = $q.defer(); | |
108 | + var url = '/api/ruleChain'; | |
109 | + $http.post(url, ruleChain).then(function success(response) { | |
110 | + deferred.resolve(response.data); | |
111 | + }, function fail() { | |
112 | + deferred.reject(); | |
113 | + }); | |
114 | + return deferred.promise; | |
115 | + } | |
116 | + | |
117 | + function deleteRuleChain(ruleChainId) { | |
118 | + var deferred = $q.defer(); | |
119 | + var url = '/api/ruleChain/' + ruleChainId; | |
120 | + $http.delete(url).then(function success() { | |
121 | + deferred.resolve(); | |
122 | + }, function fail() { | |
123 | + deferred.reject(); | |
124 | + }); | |
125 | + return deferred.promise; | |
126 | + } | |
127 | + | |
128 | + function getRuleChainMetaData(ruleChainId, config) { | |
129 | + var deferred = $q.defer(); | |
130 | + var url = '/api/ruleChain/' + ruleChainId + '/metadata'; | |
131 | + $http.get(url, config).then(function success(response) { | |
132 | + deferred.resolve(response.data); | |
133 | + }, function fail() { | |
134 | + deferred.reject(); | |
135 | + }); | |
136 | + return deferred.promise; | |
137 | + } | |
138 | + | |
139 | + function saveRuleChainMetaData(ruleChainMetaData) { | |
140 | + var deferred = $q.defer(); | |
141 | + var url = '/api/ruleChain/metadata'; | |
142 | + $http.post(url, ruleChainMetaData).then(function success(response) { | |
143 | + deferred.resolve(response.data); | |
144 | + }, function fail() { | |
145 | + deferred.reject(); | |
146 | + }); | |
147 | + return deferred.promise; | |
148 | + } | |
149 | + | |
150 | +} | ... | ... |
... | ... | @@ -73,6 +73,7 @@ import thingsboardApiAttribute from './api/attribute.service'; |
73 | 73 | import thingsboardApiEntity from './api/entity.service'; |
74 | 74 | import thingsboardApiAlarm from './api/alarm.service'; |
75 | 75 | import thingsboardApiAuditLog from './api/audit-log.service'; |
76 | +import thingsboardApiRuleChain from './api/rule-chain.service'; | |
76 | 77 | |
77 | 78 | import 'typeface-roboto'; |
78 | 79 | import 'font-awesome/css/font-awesome.min.css'; |
... | ... | @@ -135,6 +136,7 @@ angular.module('thingsboard', [ |
135 | 136 | thingsboardApiEntity, |
136 | 137 | thingsboardApiAlarm, |
137 | 138 | thingsboardApiAuditLog, |
139 | + thingsboardApiRuleChain, | |
138 | 140 | uiRouter]) |
139 | 141 | .config(AppConfig) |
140 | 142 | .factory('globalInterceptor', GlobalInterceptor) | ... | ... |
... | ... | @@ -294,7 +294,8 @@ export default angular.module('thingsboard.types', []) |
294 | 294 | customer: "CUSTOMER", |
295 | 295 | user: "USER", |
296 | 296 | dashboard: "DASHBOARD", |
297 | - alarm: "ALARM" | |
297 | + alarm: "ALARM", | |
298 | + rulechain: "RULE_CHAIN" | |
298 | 299 | }, |
299 | 300 | aliasEntityType: { |
300 | 301 | current_customer: "CURRENT_CUSTOMER" |
... | ... | @@ -354,6 +355,12 @@ export default angular.module('thingsboard.types', []) |
354 | 355 | list: 'entity.list-of-alarms', |
355 | 356 | nameStartsWith: 'entity.alarm-name-starts-with' |
356 | 357 | }, |
358 | + "RULE_CHAIN": { | |
359 | + type: 'entity.type-rulechain', | |
360 | + typePlural: 'entity.type-rulechains', | |
361 | + list: 'entity.list-of-rulechains', | |
362 | + nameStartsWith: 'entity.rulechain-name-starts-with' | |
363 | + }, | |
357 | 364 | "CURRENT_CUSTOMER": { |
358 | 365 | type: 'entity.type-current-customer', |
359 | 366 | list: 'entity.type-current-customer' | ... | ... |
... | ... | @@ -26,7 +26,7 @@ import entityAliasesTemplate from '../entity/alias/entity-aliases.tpl.html'; |
26 | 26 | /*@ngInject*/ |
27 | 27 | export default function ImportExport($log, $translate, $q, $mdDialog, $document, $http, itembuffer, utils, types, |
28 | 28 | dashboardUtils, entityService, dashboardService, pluginService, ruleService, |
29 | - widgetService, toast, attributeService) { | |
29 | + ruleChainService, widgetService, toast, attributeService) { | |
30 | 30 | |
31 | 31 | |
32 | 32 | var service = { |
... | ... | @@ -38,6 +38,8 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document, |
38 | 38 | importPlugin: importPlugin, |
39 | 39 | exportRule: exportRule, |
40 | 40 | importRule: importRule, |
41 | + exportRuleChain: exportRuleChain, | |
42 | + importRuleChain: importRuleChain, | |
41 | 43 | exportWidgetType: exportWidgetType, |
42 | 44 | importWidgetType: importWidgetType, |
43 | 45 | exportWidgetsBundle: exportWidgetsBundle, |
... | ... | @@ -275,6 +277,61 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document, |
275 | 277 | return true; |
276 | 278 | } |
277 | 279 | |
280 | + // Rule chain functions | |
281 | + | |
282 | + function exportRuleChain(ruleChainId) { | |
283 | + ruleChainService.getRuleChain(ruleChainId).then( | |
284 | + function success(ruleChain) { | |
285 | + var name = ruleChain.name; | |
286 | + name = name.toLowerCase().replace(/\W/g,"_"); | |
287 | + exportToPc(prepareExport(ruleChain), name + '.json'); | |
288 | + //TODO: metadata | |
289 | + }, | |
290 | + function fail(rejection) { | |
291 | + var message = rejection; | |
292 | + if (!message) { | |
293 | + message = $translate.instant('error.unknown-error'); | |
294 | + } | |
295 | + toast.showError($translate.instant('rulechain.export-failed-error', {error: message})); | |
296 | + } | |
297 | + ); | |
298 | + } | |
299 | + | |
300 | + function importRuleChain($event) { | |
301 | + var deferred = $q.defer(); | |
302 | + openImportDialog($event, 'rulechain.import', 'rulechain.rulechain-file').then( | |
303 | + function success(ruleChain) { | |
304 | + if (!validateImportedRuleChain(ruleChain)) { | |
305 | + toast.showError($translate.instant('rulechain.invalid-rulechain-file-error')); | |
306 | + deferred.reject(); | |
307 | + } else { | |
308 | + //TODO: rulechain metadata | |
309 | + ruleChainService.saveRuleChain(ruleChain).then( | |
310 | + function success() { | |
311 | + deferred.resolve(); | |
312 | + }, | |
313 | + function fail() { | |
314 | + deferred.reject(); | |
315 | + } | |
316 | + ); | |
317 | + } | |
318 | + }, | |
319 | + function fail() { | |
320 | + deferred.reject(); | |
321 | + } | |
322 | + ); | |
323 | + return deferred.promise; | |
324 | + } | |
325 | + | |
326 | + function validateImportedRuleChain(ruleChain) { | |
327 | + //TODO: rulechain metadata | |
328 | + if (angular.isUndefined(ruleChain.name)) | |
329 | + { | |
330 | + return false; | |
331 | + } | |
332 | + return true; | |
333 | + } | |
334 | + | |
278 | 335 | // Plugin functions |
279 | 336 | |
280 | 337 | function exportPlugin(pluginId) { | ... | ... |
... | ... | @@ -49,6 +49,7 @@ import thingsboardWidgetLibrary from '../widget'; |
49 | 49 | import thingsboardDashboard from '../dashboard'; |
50 | 50 | import thingsboardPlugin from '../plugin'; |
51 | 51 | import thingsboardRule from '../rule'; |
52 | +import thingsboardRuleChain from '../rulechain'; | |
52 | 53 | |
53 | 54 | import thingsboardJsonForm from '../jsonform'; |
54 | 55 | |
... | ... | @@ -81,6 +82,7 @@ export default angular.module('thingsboard.home', [ |
81 | 82 | thingsboardDashboard, |
82 | 83 | thingsboardPlugin, |
83 | 84 | thingsboardRule, |
85 | + thingsboardRuleChain, | |
84 | 86 | thingsboardJsonForm, |
85 | 87 | thingsboardApiDevice, |
86 | 88 | thingsboardApiLogin, | ... | ... |
... | ... | @@ -745,6 +745,10 @@ export default angular.module('thingsboard.locale', []) |
745 | 745 | "type-alarms": "Alarms", |
746 | 746 | "list-of-alarms": "{ count, select, 1 {One alarms} other {List of # alarms} }", |
747 | 747 | "alarm-name-starts-with": "Alarms whose names start with '{{prefix}}'", |
748 | + "type-rulechain": "Rule chain", | |
749 | + "type-rulechains": "Rule chains", | |
750 | + "list-of-rulechains": "{ count, select, 1 {One rule chain} other {List of # rule chains} }", | |
751 | + "rulechain-name-starts-with": "Rule chains whose names start with '{{prefix}}'", | |
748 | 752 | "type-current-customer": "Current Customer", |
749 | 753 | "search": "Search entities", |
750 | 754 | "selected-entities": "{ count, select, 1 {1 entity} other {# entities} } selected", |
... | ... | @@ -1133,6 +1137,38 @@ export default angular.module('thingsboard.locale', []) |
1133 | 1137 | "no-rules-matching": "No rules matching '{{entity}}' were found.", |
1134 | 1138 | "rule-required": "Rule is required" |
1135 | 1139 | }, |
1140 | + "rulechain": { | |
1141 | + "rulechain": "Rule chain", | |
1142 | + "rulechains": "Rule chains", | |
1143 | + "delete": "Delete rule chain", | |
1144 | + "name": "Name", | |
1145 | + "name-required": "Name is required.", | |
1146 | + "description": "Description", | |
1147 | + "add": "Add Rule Chain", | |
1148 | + "delete-rulechain-title": "Are you sure you want to delete the rule chain '{{ruleChainName}}'?", | |
1149 | + "delete-rulechain-text": "Be careful, after the confirmation the rule chain and all related data will become unrecoverable.", | |
1150 | + "delete-rulechains-title": "Are you sure you want to delete { count, select, 1 {1 rule chain} other {# rule chains} }?", | |
1151 | + "delete-rulechains-action-title": "Delete { count, select, 1 {1 rule chain} other {# rule chains} }", | |
1152 | + "delete-rulechains-text": "Be careful, after the confirmation all selected rule chains will be removed and all related data will become unrecoverable.", | |
1153 | + "add-rulechain-text": "Add new rule chain", | |
1154 | + "no-rulechains-text": "No rule chains found", | |
1155 | + "rulechain-details": "Rule chain details", | |
1156 | + "details": "Details", | |
1157 | + "events": "Events", | |
1158 | + "system": "System", | |
1159 | + "import": "Import rule chain", | |
1160 | + "export": "Export rule chain", | |
1161 | + "export-failed-error": "Unable to export rule chain: {{error}}", | |
1162 | + "create-new-rulechain": "Create new rule chain", | |
1163 | + "rule-file": "Rule chain file", | |
1164 | + "invalid-rulechain-file-error": "Unable to import rule chain: Invalid rule chain data structure.", | |
1165 | + "copyId": "Copy rule chain Id", | |
1166 | + "idCopiedMessage": "Rule chain Id has been copied to clipboard", | |
1167 | + "select-rulechain": "Select rule chain", | |
1168 | + "no-rulechains-matching": "No rule chains matching '{{entity}}' were found.", | |
1169 | + "rulechain-required": "Rule chain is required", | |
1170 | + "management": "Rules management" | |
1171 | + }, | |
1136 | 1172 | "rule-plugin": { |
1137 | 1173 | "management": "Rules and plugins management" |
1138 | 1174 | }, | ... | ... |
ui/src/app/rulechain/add-rulechain.tpl.html
0 → 100644
1 | +<!-- | |
2 | + | |
3 | + Copyright © 2016-2018 The Thingsboard Authors | |
4 | + | |
5 | + Licensed under the Apache License, Version 2.0 (the "License"); | |
6 | + you may not use this file except in compliance with the License. | |
7 | + You may obtain a copy of the License at | |
8 | + | |
9 | + http://www.apache.org/licenses/LICENSE-2.0 | |
10 | + | |
11 | + Unless required by applicable law or agreed to in writing, software | |
12 | + distributed under the License is distributed on an "AS IS" BASIS, | |
13 | + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
14 | + See the License for the specific language governing permissions and | |
15 | + limitations under the License. | |
16 | + | |
17 | +--> | |
18 | +<md-dialog aria-label="{{ 'rulechain.add' | translate }}" tb-help="'rulechains'" help-container-id="help-container"> | |
19 | + <form name="theForm" ng-submit="vm.add()"> | |
20 | + <md-toolbar> | |
21 | + <div class="md-toolbar-tools"> | |
22 | + <h2 translate>rulechain.add</h2> | |
23 | + <span flex></span> | |
24 | + <div id="help-container"></div> | |
25 | + <md-button class="md-icon-button" ng-click="vm.cancel()"> | |
26 | + <ng-md-icon icon="close" aria-label="{{ 'dialog.close' | translate }}"></ng-md-icon> | |
27 | + </md-button> | |
28 | + </div> | |
29 | + </md-toolbar> | |
30 | + <md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear> | |
31 | + <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span> | |
32 | + <md-dialog-content> | |
33 | + <div class="md-dialog-content"> | |
34 | + <tb-rule-chain rule-chain="vm.item" is-edit="true" the-form="theForm"></tb-rule-chain> | |
35 | + </div> | |
36 | + </md-dialog-content> | |
37 | + <md-dialog-actions layout="row"> | |
38 | + <span flex></span> | |
39 | + <md-button ng-disabled="$root.loading || theForm.$invalid || !theForm.$dirty" type="submit" | |
40 | + class="md-raised md-primary"> | |
41 | + {{ 'action.add' | translate }} | |
42 | + </md-button> | |
43 | + <md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | | |
44 | + translate }} | |
45 | + </md-button> | |
46 | + </md-dialog-actions> | |
47 | + </form> | |
48 | +</md-dialog> | ... | ... |
ui/src/app/rulechain/index.js
0 → 100644
1 | +/* | |
2 | + * Copyright © 2016-2018 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 | +import RuleChainRoutes from './rulechain.routes'; | |
18 | +import RuleChainController from './rulechain.controller'; | |
19 | +import RuleChainDirective from './rulechain.directive'; | |
20 | + | |
21 | +export default angular.module('thingsboard.ruleChain', []) | |
22 | + .config(RuleChainRoutes) | |
23 | + .controller('RuleChainController', RuleChainController) | |
24 | + .directive('tbRuleChain', RuleChainDirective) | |
25 | + .name; | ... | ... |
ui/src/app/rulechain/rulechain-card.tpl.html
0 → 100644
1 | +<!-- | |
2 | + | |
3 | + Copyright © 2016-2018 The Thingsboard Authors | |
4 | + | |
5 | + Licensed under the Apache License, Version 2.0 (the "License"); | |
6 | + you may not use this file except in compliance with the License. | |
7 | + You may obtain a copy of the License at | |
8 | + | |
9 | + http://www.apache.org/licenses/LICENSE-2.0 | |
10 | + | |
11 | + Unless required by applicable law or agreed to in writing, software | |
12 | + distributed under the License is distributed on an "AS IS" BASIS, | |
13 | + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
14 | + See the License for the specific language governing permissions and | |
15 | + limitations under the License. | |
16 | + | |
17 | +--> | |
18 | +<div class="tb-uppercase" ng-if="item && parentCtl.types.id.nullUid === item.tenantId.id" translate>rulechain.system</div> | ... | ... |
1 | +<!-- | |
2 | + | |
3 | + Copyright © 2016-2018 The Thingsboard Authors | |
4 | + | |
5 | + Licensed under the Apache License, Version 2.0 (the "License"); | |
6 | + you may not use this file except in compliance with the License. | |
7 | + You may obtain a copy of the License at | |
8 | + | |
9 | + http://www.apache.org/licenses/LICENSE-2.0 | |
10 | + | |
11 | + Unless required by applicable law or agreed to in writing, software | |
12 | + distributed under the License is distributed on an "AS IS" BASIS, | |
13 | + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
14 | + See the License for the specific language governing permissions and | |
15 | + limitations under the License. | |
16 | + | |
17 | +--> | |
18 | +<md-button ng-click="onExportRuleChain({event: $event})" | |
19 | + ng-show="!isEdit" | |
20 | + class="md-raised md-primary">{{ 'rulechain.export' | translate }}</md-button> | |
21 | +<md-button ng-click="onDeleteRuleChain({event: $event})" | |
22 | + ng-show="!isEdit && !isReadOnly" | |
23 | + class="md-raised md-primary">{{ 'rulechain.delete' | translate }}</md-button> | |
24 | + | |
25 | +<div layout="row"> | |
26 | + <md-button ngclipboard data-clipboard-action="copy" | |
27 | + ngclipboard-success="onRuleChainIdCopied(e)" | |
28 | + data-clipboard-text="{{ruleChain.id.id}}" ng-show="!isEdit" | |
29 | + class="md-raised"> | |
30 | + <md-icon md-svg-icon="mdi:clipboard-arrow-left"></md-icon> | |
31 | + <span translate>rulechain.copyId</span> | |
32 | + </md-button> | |
33 | +</div> | |
34 | + | |
35 | +<md-content class="md-padding tb-rulechain" layout="column"> | |
36 | + <fieldset ng-disabled="$root.loading || !isEdit || isReadOnly"> | |
37 | + <md-input-container class="md-block"> | |
38 | + <label translate>rulechain.name</label> | |
39 | + <input required name="name" ng-model="ruleChain.name"> | |
40 | + <div ng-messages="theForm.name.$error"> | |
41 | + <div translate ng-message="required">rulechain.name-required</div> | |
42 | + </div> | |
43 | + </md-input-container> | |
44 | + <md-input-container class="md-block"> | |
45 | + <label translate>rulechain.description</label> | |
46 | + <textarea ng-model="ruleChain.additionalInfo.description" rows="2"></textarea> | |
47 | + </md-input-container> | |
48 | + </fieldset> | |
49 | +</md-content> | ... | ... |
ui/src/app/rulechain/rulechain.controller.js
0 → 100644
1 | +/* | |
2 | + * Copyright © 2016-2018 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 | +/* eslint-disable import/no-unresolved, import/default */ | |
17 | + | |
18 | +import addRuleChainTemplate from './add-rulechain.tpl.html'; | |
19 | +import ruleChainCard from './rulechain-card.tpl.html'; | |
20 | + | |
21 | +/* eslint-enable import/no-unresolved, import/default */ | |
22 | + | |
23 | +/*@ngInject*/ | |
24 | +export default function RuleChainController(ruleChainService, userService, importExport, $state, $stateParams, $filter, $translate, types) { | |
25 | + | |
26 | + var ruleChainActionsList = [ | |
27 | + { | |
28 | + onAction: function ($event, item) { | |
29 | + exportRuleChain($event, item); | |
30 | + }, | |
31 | + name: function() { $translate.instant('action.export') }, | |
32 | + details: function() { return $translate.instant('rulechain.export') }, | |
33 | + icon: "file_download" | |
34 | + }, | |
35 | + { | |
36 | + onAction: function ($event, item) { | |
37 | + vm.grid.deleteItem($event, item); | |
38 | + }, | |
39 | + name: function() { return $translate.instant('action.delete') }, | |
40 | + details: function() { return $translate.instant('rulechain.delete') }, | |
41 | + icon: "delete", | |
42 | + isEnabled: isRuleChainEditable | |
43 | + } | |
44 | + ]; | |
45 | + | |
46 | + var ruleChainAddItemActionsList = [ | |
47 | + { | |
48 | + onAction: function ($event) { | |
49 | + vm.grid.addItem($event); | |
50 | + }, | |
51 | + name: function() { return $translate.instant('action.create') }, | |
52 | + details: function() { return $translate.instant('rulechain.create-new-rulechain') }, | |
53 | + icon: "insert_drive_file" | |
54 | + }, | |
55 | + { | |
56 | + onAction: function ($event) { | |
57 | + importExport.importRuleChain($event).then( | |
58 | + function() { | |
59 | + vm.grid.refreshList(); | |
60 | + } | |
61 | + ); | |
62 | + }, | |
63 | + name: function() { return $translate.instant('action.import') }, | |
64 | + details: function() { return $translate.instant('rulechain.import') }, | |
65 | + icon: "file_upload" | |
66 | + } | |
67 | + ]; | |
68 | + | |
69 | + var vm = this; | |
70 | + | |
71 | + vm.types = types; | |
72 | + | |
73 | + vm.ruleChainGridConfig = { | |
74 | + | |
75 | + refreshParamsFunc: null, | |
76 | + | |
77 | + deleteItemTitleFunc: deleteRuleChainTitle, | |
78 | + deleteItemContentFunc: deleteRuleChainText, | |
79 | + deleteItemsTitleFunc: deleteRuleChainsTitle, | |
80 | + deleteItemsActionTitleFunc: deleteRuleChainsActionTitle, | |
81 | + deleteItemsContentFunc: deleteRuleChainsText, | |
82 | + | |
83 | + fetchItemsFunc: fetchRuleChains, | |
84 | + saveItemFunc: saveRuleChain, | |
85 | + deleteItemFunc: deleteRuleChain, | |
86 | + | |
87 | + getItemTitleFunc: getRuleChainTitle, | |
88 | + itemCardTemplateUrl: ruleChainCard, | |
89 | + parentCtl: vm, | |
90 | + | |
91 | + actionsList: ruleChainActionsList, | |
92 | + addItemActions: ruleChainAddItemActionsList, | |
93 | + | |
94 | + onGridInited: gridInited, | |
95 | + | |
96 | + addItemTemplateUrl: addRuleChainTemplate, | |
97 | + | |
98 | + addItemText: function() { return $translate.instant('rulechain.add-rulechain-text') }, | |
99 | + noItemsText: function() { return $translate.instant('rulechain.no-rulechains-text') }, | |
100 | + itemDetailsText: function() { return $translate.instant('rulechain.rulechain-details') }, | |
101 | + isSelectionEnabled: isRuleChainEditable, | |
102 | + isDetailsReadOnly: function(ruleChain) { | |
103 | + return !isRuleChainEditable(ruleChain); | |
104 | + } | |
105 | + }; | |
106 | + | |
107 | + if (angular.isDefined($stateParams.items) && $stateParams.items !== null) { | |
108 | + vm.ruleChainGridConfig.items = $stateParams.items; | |
109 | + } | |
110 | + | |
111 | + if (angular.isDefined($stateParams.topIndex) && $stateParams.topIndex > 0) { | |
112 | + vm.ruleChainGridConfig.topIndex = $stateParams.topIndex; | |
113 | + } | |
114 | + | |
115 | + vm.isRuleChainEditable = isRuleChainEditable; | |
116 | + | |
117 | + vm.exportRuleChain = exportRuleChain; | |
118 | + | |
119 | + function deleteRuleChainTitle(ruleChain) { | |
120 | + return $translate.instant('rulechain.delete-rulechain-title', {ruleChainName: ruleChain.name}); | |
121 | + } | |
122 | + | |
123 | + function deleteRuleChainText() { | |
124 | + return $translate.instant('rulechain.delete-rulechain-text'); | |
125 | + } | |
126 | + | |
127 | + function deleteRuleChainsTitle(selectedCount) { | |
128 | + return $translate.instant('rulechain.delete-rulechains-title', {count: selectedCount}, 'messageformat'); | |
129 | + } | |
130 | + | |
131 | + function deleteRuleChainsActionTitle(selectedCount) { | |
132 | + return $translate.instant('rulechain.delete-rulechains-action-title', {count: selectedCount}, 'messageformat'); | |
133 | + } | |
134 | + | |
135 | + function deleteRuleChainsText() { | |
136 | + return $translate.instant('rulechain.delete-rulechains-text'); | |
137 | + } | |
138 | + | |
139 | + function gridInited(grid) { | |
140 | + vm.grid = grid; | |
141 | + } | |
142 | + | |
143 | + function fetchRuleChains(pageLink) { | |
144 | + return ruleChainService.getRuleChains(pageLink); | |
145 | + } | |
146 | + | |
147 | + function saveRuleChain(ruleChain) { | |
148 | + return ruleChainService.saveRuleChain(ruleChain); | |
149 | + } | |
150 | + | |
151 | + function deleteRuleChain(ruleChainId) { | |
152 | + return ruleChainService.deleteRuleChain(ruleChainId); | |
153 | + } | |
154 | + | |
155 | + function getRuleChainTitle(ruleChain) { | |
156 | + return ruleChain ? ruleChain.name : ''; | |
157 | + } | |
158 | + | |
159 | + function isRuleChainEditable(ruleChain) { | |
160 | + if (userService.getAuthority() === 'TENANT_ADMIN') { | |
161 | + return ruleChain && ruleChain.tenantId.id != types.id.nullUid; | |
162 | + } else { | |
163 | + return userService.getAuthority() === 'SYS_ADMIN'; | |
164 | + } | |
165 | + } | |
166 | + | |
167 | + function exportRuleChain($event, ruleChain) { | |
168 | + $event.stopPropagation(); | |
169 | + importExport.exportRuleChain(ruleChain.id.id); | |
170 | + } | |
171 | + | |
172 | +} | ... | ... |
ui/src/app/rulechain/rulechain.directive.js
0 → 100644
1 | +/* | |
2 | + * Copyright © 2016-2018 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 | +/* eslint-disable import/no-unresolved, import/default */ | |
18 | + | |
19 | +import ruleChainFieldsetTemplate from './rulechain-fieldset.tpl.html'; | |
20 | + | |
21 | +/* eslint-enable import/no-unresolved, import/default */ | |
22 | + | |
23 | +/*@ngInject*/ | |
24 | +export default function RuleChainDirective($compile, $templateCache, $mdDialog, $document, $q, $translate, types, toast) { | |
25 | + var linker = function (scope, element) { | |
26 | + var template = $templateCache.get(ruleChainFieldsetTemplate); | |
27 | + element.html(template); | |
28 | + | |
29 | + scope.onRuleChainIdCopied = function() { | |
30 | + toast.showSuccess($translate.instant('rulechain.idCopiedMessage'), 750, angular.element(element).parent().parent(), 'bottom left'); | |
31 | + }; | |
32 | + | |
33 | + $compile(element.contents())(scope); | |
34 | + } | |
35 | + return { | |
36 | + restrict: "E", | |
37 | + link: linker, | |
38 | + scope: { | |
39 | + ruleChain: '=', | |
40 | + isEdit: '=', | |
41 | + isReadOnly: '=', | |
42 | + theForm: '=', | |
43 | + onExportRuleChain: '&', | |
44 | + onDeleteRuleChain: '&' | |
45 | + } | |
46 | + }; | |
47 | +} | ... | ... |
ui/src/app/rulechain/rulechain.routes.js
0 → 100644
1 | +/* | |
2 | + * Copyright © 2016-2018 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 | +/* eslint-disable import/no-unresolved, import/default */ | |
17 | + | |
18 | +import ruleChainsTemplate from './rulechains.tpl.html'; | |
19 | + | |
20 | +/* eslint-enable import/no-unresolved, import/default */ | |
21 | + | |
22 | +/*@ngInject*/ | |
23 | +export default function RuleChainRoutes($stateProvider) { | |
24 | + | |
25 | + $stateProvider | |
26 | + .state('home.ruleChains', { | |
27 | + url: '/ruleChains', | |
28 | + params: {'topIndex': 0}, | |
29 | + module: 'private', | |
30 | + auth: ['SYS_ADMIN', 'TENANT_ADMIN'], | |
31 | + views: { | |
32 | + "content@home": { | |
33 | + templateUrl: ruleChainsTemplate, | |
34 | + controllerAs: 'vm', | |
35 | + controller: 'RuleChainController' | |
36 | + } | |
37 | + }, | |
38 | + data: { | |
39 | + searchEnabled: true, | |
40 | + pageTitle: 'rulechain.rulechains' | |
41 | + }, | |
42 | + ncyBreadcrumb: { | |
43 | + label: '{"icon": "settings_ethernet", "label": "rulechain.rulechains"}' | |
44 | + } | |
45 | + }); | |
46 | +} | ... | ... |
ui/src/app/rulechain/rulechains.tpl.html
0 → 100644
1 | +<!-- | |
2 | + | |
3 | + Copyright © 2016-2018 The Thingsboard Authors | |
4 | + | |
5 | + Licensed under the Apache License, Version 2.0 (the "License"); | |
6 | + you may not use this file except in compliance with the License. | |
7 | + You may obtain a copy of the License at | |
8 | + | |
9 | + http://www.apache.org/licenses/LICENSE-2.0 | |
10 | + | |
11 | + Unless required by applicable law or agreed to in writing, software | |
12 | + distributed under the License is distributed on an "AS IS" BASIS, | |
13 | + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
14 | + See the License for the specific language governing permissions and | |
15 | + limitations under the License. | |
16 | + | |
17 | +--> | |
18 | +<tb-grid grid-configuration="vm.ruleChainGridConfig"> | |
19 | + <details-buttons tb-help="'rulechains'" help-container-id="help-container"> | |
20 | + <div id="help-container"></div> | |
21 | + </details-buttons> | |
22 | + <md-tabs ng-class="{'tb-headless': (vm.grid.detailsConfig.isDetailsEditMode || !vm.isRuleChainEditable(vm.grid.operatingItem()))}" | |
23 | + id="tabs" md-border-bottom flex class="tb-absolute-fill"> | |
24 | + <md-tab label="{{ 'rulechain.details' | translate }}"> | |
25 | + <tb-rule-chain rule-chain="vm.grid.operatingItem()" | |
26 | + is-edit="vm.grid.detailsConfig.isDetailsEditMode" | |
27 | + is-read-only="vm.grid.isDetailsReadOnly(vm.grid.operatingItem())" | |
28 | + the-form="vm.grid.detailsForm" | |
29 | + on-export-rule-chain="vm.exportRuleChain(event, vm.grid.detailsConfig.currentItem)" | |
30 | + on-delete-rule-chain="vm.grid.deleteItem(event, vm.grid.detailsConfig.currentItem)"></tb-rule-chain> | |
31 | + </md-tab> | |
32 | + <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode && vm.isRuleChainEditable(vm.grid.operatingItem())" md-on-select="vm.grid.triggerResize()" label="{{ 'attribute.attributes' | translate }}"> | |
33 | + <tb-attribute-table flex | |
34 | + entity-id="vm.grid.operatingItem().id.id" | |
35 | + entity-type="{{vm.types.entityType.rulechain}}" | |
36 | + entity-name="vm.grid.operatingItem().name" | |
37 | + default-attribute-scope="{{vm.types.attributesScope.server.value}}"> | |
38 | + </tb-attribute-table> | |
39 | + </md-tab> | |
40 | + <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode && vm.isRuleChainEditable(vm.grid.operatingItem())" md-on-select="vm.grid.triggerResize()" label="{{ 'attribute.latest-telemetry' | translate }}"> | |
41 | + <tb-attribute-table flex | |
42 | + entity-id="vm.grid.operatingItem().id.id" | |
43 | + entity-type="{{vm.types.entityType.rulechain}}" | |
44 | + entity-name="vm.grid.operatingItem().name" | |
45 | + default-attribute-scope="{{vm.types.latestTelemetry.value}}" | |
46 | + disable-attribute-scope-selection="true"> | |
47 | + </tb-attribute-table> | |
48 | + </md-tab> | |
49 | + <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode && vm.isRuleChainEditable(vm.grid.operatingItem())" md-on-select="vm.grid.triggerResize()" label="{{ 'alarm.alarms' | translate }}"> | |
50 | + <tb-alarm-table flex entity-type="vm.types.entityType.rulechain" | |
51 | + entity-id="vm.grid.operatingItem().id.id"> | |
52 | + </tb-alarm-table> | |
53 | + </md-tab> | |
54 | + <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode && vm.isRuleChainEditable(vm.grid.operatingItem())" md-on-select="vm.grid.triggerResize()" label="{{ 'rulechain.events' | translate }}"> | |
55 | + <tb-event-table flex entity-type="vm.types.entityType.rulechain" | |
56 | + entity-id="vm.grid.operatingItem().id.id" | |
57 | + tenant-id="vm.grid.operatingItem().tenantId.id" | |
58 | + default-event-type="{{vm.types.eventType.lcEvent.value}}"> | |
59 | + </tb-event-table> | |
60 | + </md-tab> | |
61 | + <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode && vm.isRuleChainEditable(vm.grid.operatingItem())" md-on-select="vm.grid.triggerResize()" label="{{ 'relation.relations' | translate }}"> | |
62 | + <tb-relation-table flex | |
63 | + entity-id="vm.grid.operatingItem().id.id" | |
64 | + entity-type="{{vm.types.entityType.rulechain}}"> | |
65 | + </tb-relation-table> | |
66 | + </md-tab> | |
67 | + <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode && vm.isRuleChainEditable(vm.grid.operatingItem()) && vm.grid.isTenantAdmin()" | |
68 | + md-on-select="vm.grid.triggerResize()" label="{{ 'audit-log.audit-logs' | translate }}"> | |
69 | + <tb-audit-log-table flex entity-type="vm.types.entityType.rulechain" | |
70 | + entity-id="vm.grid.operatingItem().id.id" | |
71 | + audit-log-mode="{{vm.types.auditLogMode.entity}}"> | |
72 | + </tb-audit-log-table> | |
73 | + </md-tab> | |
74 | + </md-tabs> | |
75 | +</tb-grid> | ... | ... |
... | ... | @@ -79,6 +79,12 @@ function Menu(userService, $state, $rootScope) { |
79 | 79 | icon: 'settings_ethernet' |
80 | 80 | }, |
81 | 81 | { |
82 | + name: 'rulechain.rulechains', | |
83 | + type: 'link', | |
84 | + state: 'home.ruleChains', | |
85 | + icon: 'settings_ethernet' | |
86 | + }, | |
87 | + { | |
82 | 88 | name: 'tenant.tenants', |
83 | 89 | type: 'link', |
84 | 90 | state: 'home.tenants', |
... | ... | @@ -128,6 +134,16 @@ function Menu(userService, $state, $rootScope) { |
128 | 134 | ] |
129 | 135 | }, |
130 | 136 | { |
137 | + name: 'rulechain.management', | |
138 | + places: [ | |
139 | + { | |
140 | + name: 'rulechain.rulechains', | |
141 | + icon: 'settings_ethernet', | |
142 | + state: 'home.ruleChains' | |
143 | + } | |
144 | + ] | |
145 | + }, | |
146 | + { | |
131 | 147 | name: 'tenant.management', |
132 | 148 | places: [ |
133 | 149 | { |
... | ... | @@ -183,6 +199,12 @@ function Menu(userService, $state, $rootScope) { |
183 | 199 | icon: 'settings_ethernet' |
184 | 200 | }, |
185 | 201 | { |
202 | + name: 'rulechain.rulechains', | |
203 | + type: 'link', | |
204 | + state: 'home.ruleChains', | |
205 | + icon: 'settings_ethernet' | |
206 | + }, | |
207 | + { | |
186 | 208 | name: 'customer.customers', |
187 | 209 | type: 'link', |
188 | 210 | state: 'home.customers', |
... | ... | @@ -236,6 +258,16 @@ function Menu(userService, $state, $rootScope) { |
236 | 258 | ] |
237 | 259 | }, |
238 | 260 | { |
261 | + name: 'rulechain.management', | |
262 | + places: [ | |
263 | + { | |
264 | + name: 'rulechain.rulechains', | |
265 | + icon: 'settings_ethernet', | |
266 | + state: 'home.ruleChains' | |
267 | + } | |
268 | + ] | |
269 | + }, | |
270 | + { | |
239 | 271 | name: 'customer.management', |
240 | 272 | places: [ |
241 | 273 | { | ... | ... |