Commit 1bb77e2eba46819a051f9b757fdcf4819386b0d7

Authored by VoBa
Committed by GitHub
2 parents af3a3683 ee111c64

Merge pull request #71 from deaflynx/develop/2.6-edge

Develop/2.6 edge Edge management, enableEdge
... ... @@ -36,7 +36,8 @@ function EdgeService($http, $q, customerService) {
36 36 makeEdgePublic: makeEdgePublic,
37 37 setRootRuleChain: setRootRuleChain,
38 38 getEdgeEvents: getEdgeEvents,
39   - syncEdge: syncEdge
  39 + syncEdge: syncEdge,
  40 + findMissingToRelatedRuleChains: findMissingToRelatedRuleChains
40 41 };
41 42
42 43 return service;
... ... @@ -305,4 +306,15 @@ function EdgeService($http, $q, customerService) {
305 306 });
306 307 return deferred.promise;
307 308 }
  309 +
  310 + function findMissingToRelatedRuleChains(edgeId) {
  311 + var deferred = $q.defer();
  312 + var url = '/api/edge/missingToRelatedRuleChains/' + edgeId;
  313 + $http.get(url, null).then(function success(response) {
  314 + deferred.resolve(response.data);
  315 + }, function fail(response) {
  316 + deferred.reject(response.data);
  317 + });
  318 + return deferred.promise;
  319 + }
308 320 }
... ...
... ... @@ -910,6 +910,9 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
910 910 }
911 911 }
912 912 }
  913 + if (!userService.isEdgesSupportEnabled()) {
  914 + delete entityTypes.edge;
  915 + }
913 916 return entityTypes;
914 917 }
915 918
... ...
... ... @@ -29,7 +29,8 @@ function UserService($http, $q, $rootScope, adminService, dashboardService, time
29 29 allowedDashboardIds = [],
30 30 redirectParams = null,
31 31 userTokenAccessEnabled = false,
32   - userLoaded = false;
  32 + userLoaded = false,
  33 + edgesSupportEnabled = false;
33 34
34 35 var refreshTokenQueue = [];
35 36
... ... @@ -65,7 +66,8 @@ function UserService($http, $q, $rootScope, adminService, dashboardService, time
65 66 reloadUser: reloadUser,
66 67 isUserTokenAccessEnabled: isUserTokenAccessEnabled,
67 68 loginAsUser: loginAsUser,
68   - setUserCredentialsEnabled: setUserCredentialsEnabled
  69 + setUserCredentialsEnabled: setUserCredentialsEnabled,
  70 + isEdgesSupportEnabled: isEdgesSupportEnabled
69 71 }
70 72
71 73 reloadUser();
... ... @@ -451,6 +453,7 @@ function UserService($http, $q, $rootScope, adminService, dashboardService, time
451 453 function loadSystemParams() {
452 454 var promises = [];
453 455 promises.push(loadIsUserTokenAccessEnabled());
  456 + promises.push(loadIsEdgesSupportEnabled());
454 457 promises.push(timeService.loadMaxDatapointsLimit());
455 458 return $q.all(promises);
456 459 }
... ... @@ -685,4 +688,20 @@ function UserService($http, $q, $rootScope, adminService, dashboardService, time
685 688 });
686 689 }
687 690
  691 + function isEdgesSupportEnabled() {
  692 + return edgesSupportEnabled;
  693 + }
  694 +
  695 + function loadIsEdgesSupportEnabled() {
  696 + var deferred = $q.defer();
  697 + var url = '/api/edges/enabled';
  698 + $http.get(url).then(function success(response) {
  699 + edgesSupportEnabled = response.data;
  700 + deferred.resolve(response);
  701 + }, function fail() {
  702 + deferred.reject();
  703 + });
  704 + return deferred.promise;
  705 + }
  706 +
688 707 }
... ...
... ... @@ -1076,6 +1076,127 @@ export default angular.module('thingsboard.types', [])
1076 1076 },
1077 1077 translate: {
1078 1078 customTranslationsPrefix: "custom."
1079   - }
  1079 + },
  1080 + edgeEventActionType: {
  1081 + "ADDED": {
  1082 + name: "audit-log.type-added"
  1083 + },
  1084 + "DELETED": {
  1085 + name: "audit-log.type-deleted"
  1086 + },
  1087 + "UPDATED": {
  1088 + name: "audit-log.type-updated"
  1089 + },
  1090 + "ATTRIBUTES_UPDATED": {
  1091 + name: "audit-log.type-attributes-updated"
  1092 + },
  1093 + "ATTRIBUTES_DELETED": {
  1094 + name: "audit-log.type-attributes-deleted"
  1095 + },
  1096 + "RPC_CALL": {
  1097 + name: "audit-log.type-rpc-call"
  1098 + },
  1099 + "CREDENTIALS_UPDATED": {
  1100 + name: "audit-log.type-credentials-updated"
  1101 + },
  1102 + "RELATION_ADD_OR_UPDATE": {
  1103 + name: "audit-log.type-relation-add-or-update"
  1104 + },
  1105 + "RELATION_DELETED": {
  1106 + name: "audit-log.type-relation-delete"
  1107 + },
  1108 + "ALARM_ACK": {
  1109 + name: "audit-log.type-alarm-ack"
  1110 + },
  1111 + "ALARM_CLEAR": {
  1112 + name: "audit-log.type-alarm-clear"
  1113 + },
  1114 + "CREDENTIALS_REQUEST": {
  1115 + name: "edge.credentials-request"
  1116 + },
  1117 + "ATTRIBUTES_REQUEST": {
  1118 + name: "edge.attributes-request"
  1119 + },
  1120 + "TIMESERIES_UPDATED": {
  1121 + name: "edge.timeseries-updated"
  1122 + },
  1123 + "RELATION_REQUEST": {
  1124 + name: "edge.relation-request"
  1125 + },
  1126 + "RULE_CHAIN_METADATA_REQUEST": {
  1127 + name: "edge.rule-chain-metadata-request"
  1128 + },
  1129 + "GROUP_ENTITIES_REQUEST": {
  1130 + name: "edge.group_entities_request"
  1131 + },
  1132 + "GROUP_PERMISSIONS_REQUEST": {
  1133 + name: "edge.group-permissions-request"
  1134 + },
  1135 + "ASSIGNED_TO_EDGE": {
  1136 + name: "audit-log.type-assigned-to-edge"
  1137 + },
  1138 + "UNASSIGNED_FROM_EDGE": {
  1139 + name: "audit-log.type-unassigned-from-edge"
  1140 + }
  1141 + },
  1142 + edgeEventTypeTranslations:{
  1143 + "DASHBOARD": {
  1144 + name: "entity.type-dashboard"
  1145 + },
  1146 + "ASSET": {
  1147 + name: "entity.type-asset"
  1148 + },
  1149 + "DEVICE": {
  1150 + name: "entity.type-device"
  1151 + },
  1152 + "ENTITY_VIEW": {
  1153 + name: "entity.type-entity_view"
  1154 + },
  1155 + "ALARM": {
  1156 + name: "entity.type-alarm"
  1157 + },
  1158 + "RULE_CHAIN": {
  1159 + name: "entity.type-rulechain"
  1160 + },
  1161 + "RULE_CHAIN_META_DATA": {
  1162 + name: "entity.type-rule-chain-metadata"
  1163 + },
  1164 + "EDGE": {
  1165 + name: "entity.type-edge"
  1166 + },
  1167 + "USER": {
  1168 + name: "entity.type-user"
  1169 + },
  1170 + "CUSTOMER": {
  1171 + name: "entity.type-customer"
  1172 + },
  1173 + "RELATION": {
  1174 + name: "entity.type-relation"
  1175 + },
  1176 + "ENTITY_GROUP": {
  1177 + name: "entity.type-entity-group"
  1178 + },
  1179 + "SCHEDULER_EVENT": {
  1180 + name: "entity.type-scheduler-event"
  1181 + },
  1182 + "WHITE_LABELING": {
  1183 + name: "white-labeling.white-labeling"
  1184 + },
  1185 + "LOGIN_WHITE_LABELING": {
  1186 + name: "white-labeling.login-white-labeling"
  1187 + },
  1188 + "CUSTOM_TRANSLATION": {
  1189 + name: "custom-translation.custom-translation",
  1190 + },
  1191 + "WIDGETS_BUNDLE": {
  1192 + name: "widget.widget-bundle"
  1193 + },
  1194 + "WIDGET_TYPE": {
  1195 + name: "widget.type"
  1196 + },
  1197 + "ADMIN_SETTINGS": {
  1198 + name: "permission.resource.display-type.ADMIN_SETTINGS"
  1199 + }
  1200 + },
1080 1201 }
1081 1202 ).name;
... ...
... ... @@ -81,7 +81,7 @@ export default function CustomerController(customerService, $state, $stateParams
81 81 onAction: function ($event, item) {
82 82 openCustomerEdges($event, item);
83 83 },
84   - name: function() { return $translate.instant('edge.edges') },
  84 + name: function() { return $translate.instant('edge.edge-instances') },
85 85 details: function(customer) {
86 86 if (customer && customer.additionalInfo && customer.additionalInfo.isPublic) {
87 87 return $translate.instant('customer.manage-public-edges')
... ...
... ... @@ -15,34 +15,38 @@
15 15 limitations under the License.
16 16
17 17 -->
18   -<md-button ng-click="onAssignToCustomer({event: $event})"
19   - ng-show="!isEdit && edgeScope === 'tenant' && !isAssignedToCustomer"
20   - class="md-raised md-primary">{{ 'edge.assign-to-customer' | translate }}</md-button>
21   -<md-button ng-click="onUnassignFromCustomer({event: $event, isPublic: isPublic})"
22   - ng-show="!isEdit && (edgeScope === 'customer' || edgeScope === 'tenant') && isAssignedToCustomer"
23   - class="md-raised md-primary">{{ isPublic ? 'edge.make-private' : 'edge.unassign-from-customer' | translate }}</md-button>
24   -<md-button ng-click="onMakePublic({event: $event})"
25   - ng-show="!isEdit && edgeScope === 'tenant' && !isAssignedToCustomer && !isPublic"
26   - class="md-raised md-primary">{{ 'edge.make-public' | translate }}</md-button>
27   -<md-button ng-click="onManageEdgeAssets({event: $event})"
28   - ng-show="!isEdit && edgeScope === 'tenant'"
29   - class="md-raised md-primary">{{ 'edge.manage-edge-assets' | translate }}</md-button>
30   -<md-button ng-click="onManageEdgeDevices({event: $event})"
31   - ng-show="!isEdit && edgeScope === 'tenant'"
32   - class="md-raised md-primary">{{ 'edge.manage-edge-devices' | translate }}</md-button>
33   -<md-button ng-click="onManageEdgeEntityViews({event: $event})"
34   - ng-show="!isEdit && edgeScope === 'tenant'"
35   - class="md-raised md-primary">{{ 'edge.manage-edge-entity-views' | translate }}</md-button>
36   -<md-button ng-click="onManageEdgeDashboards({event: $event})"
37   - ng-show="!isEdit && edgeScope === 'tenant'"
38   - class="md-raised md-primary">{{ 'edge.manage-edge-dashboards' | translate }}</md-button>
39   -<md-button ng-click="onManageEdgeRuleChains({event: $event})"
40   - ng-show="!isEdit && edgeScope === 'tenant'"
41   - class="md-raised md-primary">{{ 'edge.manage-edge-rulechains' | translate }}</md-button>
42   -<md-button ng-click="onDeleteEdge({event: $event})"
43   - ng-show="!isEdit && edgeScope === 'tenant'"
44   - class="md-raised md-primary">{{ 'edge.delete' | translate }}</md-button>
  18 +<div layout="row">
  19 + <md-button ng-click="onAssignToCustomer({event: $event})"
  20 + ng-show="!isEdit && edgeScope === 'tenant' && !isAssignedToCustomer"
  21 + class="md-raised md-primary">{{ 'edge.assign-to-customer' | translate }}</md-button>
  22 + <md-button ng-click="onUnassignFromCustomer({event: $event, isPublic: isPublic})"
  23 + ng-show="!isEdit && (edgeScope === 'customer' || edgeScope === 'tenant') && isAssignedToCustomer"
  24 + class="md-raised md-primary">{{ isPublic ? 'edge.make-private' : 'edge.unassign-from-customer' | translate }}</md-button>
  25 + <md-button ng-click="onMakePublic({event: $event})"
  26 + ng-show="!isEdit && edgeScope === 'tenant' && !isAssignedToCustomer && !isPublic"
  27 + class="md-raised md-primary">{{ 'edge.make-public' | translate }}</md-button>
  28 + <md-button ng-click="onDeleteEdge({event: $event})"
  29 + ng-show="!isEdit && edgeScope === 'tenant'"
  30 + class="md-raised md-primary">{{ 'edge.delete' | translate }}</md-button>
  31 +</div>
  32 +<div layout="row">
  33 + <md-button ng-click="onManageEdgeAssets({event: $event})"
  34 + ng-show="!isEdit && edgeScope === 'tenant'"
  35 + class="md-raised md-primary">{{ 'edge.assets' | translate }}</md-button>
  36 + <md-button ng-click="onManageEdgeDevices({event: $event})"
  37 + ng-show="!isEdit && edgeScope === 'tenant'"
  38 + class="md-raised md-primary">{{ 'edge.devices' | translate }}</md-button>
  39 + <md-button ng-click="onManageEdgeEntityViews({event: $event})"
  40 + ng-show="!isEdit && edgeScope === 'tenant'"
  41 + class="md-raised md-primary">{{ 'edge.entity-views' | translate }}</md-button>
  42 + <md-button ng-click="onManageEdgeDashboards({event: $event})"
  43 + ng-show="!isEdit && edgeScope === 'tenant'"
  44 + class="md-raised md-primary">{{ 'edge.dashboards' | translate }}</md-button>
  45 + <md-button ng-click="onManageEdgeRuleChains({event: $event})"
  46 + ng-show="!isEdit && edgeScope === 'tenant'"
  47 + class="md-raised md-primary">{{ 'edge.rulechain-templates' | translate }}</md-button>
45 48
  49 +</div>
46 50 <div layout="row">
47 51 <md-button ngclipboard data-clipboard-action="copy"
48 52 ngclipboard-success="onEdgeIdCopied()"
... ... @@ -98,14 +102,7 @@
98 102 ng-model="edge.type"
99 103 entity-type="types.entityType.edge">
100 104 </tb-entity-subtype-autocomplete>
101   - <md-input-container class="md-block">
102   - <label translate>edge.label</label>
103   - <input name="label" ng-model="edge.label">
104   - </md-input-container>
105   - <md-input-container class="md-block">
106   - <label translate>edge.description</label>
107   - <textarea ng-model="edge.additionalInfo.description" rows="2"></textarea>
108   - </md-input-container>
  105 + <div translate class="tb-hint">edge.license-key-hint</div>
109 106 <md-input-container class="md-block">
110 107 <label translate>edge.edge-license-key</label>
111 108 <input required name="edgeLicenseKey" ng-model="edge.edgeLicenseKey">
... ... @@ -113,6 +110,7 @@
113 110 <div translate ng-message="required">edge.edge-license-key-required</div>
114 111 </div>
115 112 </md-input-container>
  113 + <div translate class="tb-hint">edge.cloud-endpoint-hint</div>
116 114 <md-input-container class="md-block">
117 115 <label translate>edge.cloud-endpoint</label>
118 116 <input required name="cloudEndpoint" ng-model="edge.cloudEndpoint">
... ... @@ -122,7 +120,6 @@
122 120 </md-input-container>
123 121 </fieldset>
124 122 <div layout="row">
125   -
126 123 <md-input-container class="md-block" flex>
127 124 <label translate>edge.edge-key</label>
128 125 <input ng-model="edge.routingKey" disabled>
... ... @@ -152,4 +149,14 @@
152 149 </md-tooltip>
153 150 </md-button>
154 151 </div>
  152 + <fieldset ng-disabled="$root.loading || !isEdit">
  153 + <md-input-container class="md-block">
  154 + <label translate>edge.label</label>
  155 + <input name="label" ng-model="edge.label">
  156 + </md-input-container>
  157 + <md-input-container class="md-block">
  158 + <label translate>edge.description</label>
  159 + <textarea ng-model="edge.additionalInfo.description" rows="2"></textarea>
  160 + </md-input-container>
  161 + </fieldset>
155 162 </md-content>
... ...
... ... @@ -283,7 +283,7 @@ export function EdgeController($rootScope, userService, edgeService, customerSer
283 283 details: function() {
284 284 return $translate.instant('edge.manage-edge-rulechains');
285 285 },
286   - icon: "code"
  286 + icon: "settings_ethernet"
287 287 }
288 288 );
289 289
... ...
... ... @@ -36,6 +36,7 @@ export default function EdgeDirective($compile, $templateCache, $translate, $mdD
36 36 scope.edge.routingKey = utils.guid('');
37 37 scope.edge.secret = generateSecret(20);
38 38 scope.edge.cloudEndpoint = utils.baseUrl();
  39 + scope.edge.type = 'default';
39 40 }
40 41 if (scope.edge.customerId && scope.edge.customerId.id !== types.id.nullUid) {
41 42 scope.isAssignedToCustomer = true;
... ...
... ... @@ -46,10 +46,10 @@ export default function EdgeRoutes($stateProvider, types) {
46 46 searchEnabled: true,
47 47 searchByEntitySubtype: true,
48 48 searchEntityType: types.entityType.edge,
49   - pageTitle: 'edge.edges'
  49 + pageTitle: 'edge.edge-instances'
50 50 },
51 51 ncyBreadcrumb: {
52   - label: '{"icon": "router", "label": "edge.edges"}'
  52 + label: '{"icon": "router", "label": "edge.edge-instances"}'
53 53 }
54 54 }).state('home.edges.entityViews', {
55 55 url: '/:edgeId/entityViews',
... ... @@ -194,11 +194,11 @@ export default function EdgeRoutes($stateProvider, types) {
194 194 },
195 195 data: {
196 196 searchEnabled: true,
197   - pageTitle: 'edge.rulechains',
  197 + pageTitle: 'edge.rulechain-templates',
198 198 ruleChainsType: 'edge'
199 199 },
200 200 ncyBreadcrumb: {
201   - label: '{"icon": "code", "label": "rulechain.edge-rulechains"}'
  201 + label: '{"icon": "settings_ethernet", "label": "edge.rulechain-templates"}'
202 202 }
203 203 }).state('home.edges.ruleChains.ruleChain', {
204 204 url: '/:ruleChainId',
... ... @@ -232,10 +232,10 @@ export default function EdgeRoutes($stateProvider, types) {
232 232 data: {
233 233 import: false,
234 234 searchEnabled: false,
235   - pageTitle: 'edge.rulechain'
  235 + pageTitle: 'edge.rulechain-templates'
236 236 },
237 237 ncyBreadcrumb: {
238   - label: '{"icon": "code", "label": "{{ vm.ruleChain.name }}", "translate": "false"}'
  238 + label: '{"icon": "settings_ethernet", "label": "{{ vm.ruleChain.name }}", "translate": "false"}'
239 239 }
240 240 });
241 241 }
... ...
... ... @@ -22,7 +22,7 @@ import entityFilterTemplate from './entity-filter.tpl.html';
22 22 import './entity-filter.scss';
23 23
24 24 /*@ngInject*/
25   -export default function EntityFilterDirective($compile, $templateCache, $q, $document, $mdDialog, types, entityService) {
  25 +export default function EntityFilterDirective($compile, $templateCache, $q, $document, $mdDialog, types, entityService, userService) {
26 26
27 27 var linker = function (scope, element, attrs, ngModelCtrl) {
28 28
... ... @@ -31,6 +31,9 @@ export default function EntityFilterDirective($compile, $templateCache, $q, $doc
31 31
32 32 scope.ngModelCtrl = ngModelCtrl;
33 33 scope.types = types;
  34 + if (!userService.isEdgesSupportEnabled()) {
  35 + scope.allowedEntityTypes = Object.values(types.entityType).filter(entityType => entityType !== types.entityType.edge);
  36 + }
34 37 scope.aliasFilterTypes = entityService.getAliasFilterTypesByEntityTypes(scope.allowedEntityTypes);
35 38
36 39 scope.$watch('filter.type', function (newType, prevType) {
... ...
... ... @@ -16,8 +16,8 @@
16 16
17 17 -->
18 18 <div translate class="tb-cell" flex="20">event.event-time</div>
19   -<div translate class="tb-cell" flex="20">event.event-type</div>
20   -<div translate class="tb-cell" flex="40">edge.event-action</div>
21   -<div translate class="tb-cell" flex="20">edge.entity-id</div>
  19 +<div translate class="tb-cell" flex="10">event.event-type</div>
  20 +<div translate class="tb-cell" flex="15">edge.event-action</div>
  21 +<div translate class="tb-cell" flex="30">edge.entity-id</div>
22 22 <div translate class="tb-cell" flex="15">edge.status</div>
23 23 <div translate class="tb-cell" flex="10">edge.entity-info</div>
... ...
... ... @@ -16,9 +16,9 @@
16 16
17 17 -->
18 18 <div class="tb-cell" flex="20">{{ event.createdTime | date : 'yyyy-MM-dd HH:mm:ss' }}</div>
19   -<div class="tb-cell" flex="20">{{ event.type }}</div>
20   -<div class="tb-cell" flex="40">{{ event.action }}</div>
21   -<div class="tb-cell" flex="20">{{ event.entityId }}</div>
  19 +<div class="tb-cell" flex="10">{{ event.edgeEventTypeText }}</div>
  20 +<div class="tb-cell" flex="15">{{ event.edgeEventActionText }}</div>
  21 +<div class="tb-cell" flex="30">{{ event.entityId }}</div>
22 22 <div class="tb-cell" flex="15" ng-style="{'color': statusColor}">{{ updateStatus(event.createdTime) }}</div>
23 23 <div class="tb-cell" flex="10">
24 24 <md-button ng-if="checkEdgeEventType(event.type)" class="md-icon-button md-primary"
... ...
... ... @@ -22,7 +22,7 @@ import eventTableTemplate from './event-table.tpl.html';
22 22 /* eslint-enable import/no-unresolved, import/default */
23 23
24 24 /*@ngInject*/
25   -export default function EventTableDirective($compile, $templateCache, $rootScope, types,
  25 +export default function EventTableDirective($compile, $templateCache, $rootScope, $translate, types,
26 26 eventService, edgeService, attributeService) {
27 27
28 28 var linker = function (scope, element, attrs) {
... ... @@ -118,7 +118,11 @@ export default function EventTableDirective($compile, $templateCache, $rootScope
118 118 scope.events.pending = true;
119 119 promise.then(
120 120 function success(events) {
121   - scope.events.data = scope.events.data.concat(events.data);
  121 + if (scope.eventType === types.eventType.edgeEvent.value) {
  122 + scope.events.data = scope.events.data.concat(prepareEdgeEventData(events.data));
  123 + } else {
  124 + scope.events.data = scope.events.data.concat(events.data);
  125 + }
122 126 scope.events.nextPageLink = events.nextPageLink;
123 127 scope.events.hasNext = events.hasNext;
124 128 if (scope.events.hasNext) {
... ... @@ -266,6 +270,16 @@ export default function EventTableDirective($compile, $templateCache, $rootScope
266 270
267 271 $compile(element.contents())(scope);
268 272 }
  273 +
  274 + function prepareEdgeEventData(data) {
  275 + data.forEach(
  276 + edgeEvent => {
  277 + edgeEvent.edgeEventActionText = $translate.instant(types.edgeEventActionType[edgeEvent.action].name);
  278 + edgeEvent.edgeEventTypeText = $translate.instant(types.edgeEventTypeTranslations[edgeEvent.edgeId.entityType].name);
  279 + }
  280 + );
  281 + return data;
  282 + }
269 283
270 284 return {
271 285 restrict: "E",
... ...
... ... @@ -431,7 +431,7 @@
431 431 "customer-required": "Kunde ist erforderlich",
432 432 "select-default-customer": "Wählen Sie den Standardkunden aus.",
433 433 "default-customer": "Standardkunde",
434   - "edges": "Kunden Rand",
  434 + "edge-instances": "Kunden Rand",
435 435 "default-customer-required": "Ein Standardkunde ist erforderlich, um das Dashboard auf Mandantenebene zu testen."
436 436 },
437 437 "datetime": {
... ... @@ -730,7 +730,7 @@
730 730 },
731 731 "edge": {
732 732 "edge": "Rand",
733   - "edges": "Rand",
  733 + "edge-instances": "Rand",
734 734 "management": "Rand verwalten",
735 735 "no-edges-matching": "Keine passenden Rand '{{entity}}' gefunden.",
736 736 "add": "Rand hinzufügen",
... ... @@ -1355,8 +1355,6 @@
1355 1355 "rulechain": {
1356 1356 "rulechain": "Regelkette",
1357 1357 "rulechains": "Regelketten",
1358   - "core-rulechains": "Kernregelketten",
1359   - "edge-rulechains": "Randregelketten",
1360 1358 "root": "Wurzel",
1361 1359 "delete": "Regelkette löschen",
1362 1360 "name": "Name",
... ... @@ -1393,7 +1391,6 @@
1393 1391 "assign-rulechains": "Regelketten zuweisen",
1394 1392 "assign-new-rulechain": "Neues Regelkette zuweisen",
1395 1393 "delete-rulechains": "Regelketten löschen",
1396   - "default": "Standard",
1397 1394 "unassign-rulechain": "Nicht zugeordnete Regelkette",
1398 1395 "unassign-rulechains": "Nicht zugeordnete Regelketten",
1399 1396 "unassign-rulechain-title": "Möchten Sie die Zuordnung die Regelkette '{{ruleChainTitle}}' wirklich aufheben?",
... ... @@ -1403,16 +1400,17 @@
1403 1400 "unassign-rulechains-from-edge-text": "Nach der Bestätigung wird die Zuordnung aller ausgewählten Regelketten aufgehoben und sie sind für den Rand nicht mehr zugänglich.",
1404 1401 "assign-rulechain-to-edge": "Regelkette(n) dem Rand zuordnen",
1405 1402 "assign-rulechain-to-edge-text": "Bitte wählen Sie die Regelketten aus, die Sie dem Rand zuordnen möchten",
1406   - "set-default-root-edge": "Machen Sie Randregelkette zur Wurzel Standard",
1407   - "set-default-root-edge-rulechain-title": "Sind Sie sicher, dass Sie die Randregelkette '{{ruleChainName}}' zur Wurzel machen Standard?",
1408   - "set-default-root-edge-rulechain-text": "Nach der Bestätigung wird die Randregelkette zur Wurzel Standard und behandelt alle eingehenden Transportnachrichten.",
  1403 + "set-edge-template-root-rulechain": "Regelkette als Rand der Kantenvorlage erstellen",
  1404 + "set-edge-template-root-rulechain-title": "Möchten Sie die Kantenvorlage der Regelkette '{{ruleChainName}}' wirklich als Root festlegen?",
  1405 + "set-edge-template-root-rulechain-text": "Nach der Bestätigung wird die Regelkette zum Stamm der Kantenvorlage und zur Stammregelkette für neu erstellte Kanten.",
1409 1406 "invalid-rulechain-type-error": "Regelkette konnte nicht importiert werden: Ungültige Regelkettentyp. Erwarteter Typ ist {{expectedRuleChainType}}.",
1410   - "set-default-edge": "Machen Sie Regelkette Standard",
1411   - "set-default-edge-title": "Sind Sie sicher, dass Sie die Randregelkette '{{ruleChainName}}' machen Standard?",
1412   - "set-default-edge-text": "Nach der Bestätigung wird die Randregelkette für neu erstellte Rand vergeben.",
1413   - "remove-default-edge": "Randregelkette Standard entfernen",
1414   - "remove-default-edge-title": "Sind Sie sicher, dass Sie die Randregelkette '{{ruleChainName}}' aus der Standardliste entfernen?",
1415   - "remove-default-edge-text": "Nach der Bestätigung wird die Randregelkette nicht für neu erstellte Rand vergeben."
  1407 + "set-auto-assign-to-edge": "Weisen Sie den Kanten bei der Erstellung eine Regelkette zu",
  1408 + "set-auto-assign-to-edge-title": "Sind Sie sicher, dass Sie die Kantenregelkette '{{ruleChainName}}' bei der Erstellung den Kanten zuweisen möchten? ",
  1409 + "set-auto-assign-to-edge-text": "Nach der Bestätigung wird die Kantenregelkette bei der Erstellung automatisch den Kanten zugewiesen.",
  1410 + "unset-auto-assign-to-edge": "Weisen Sie Kanten bei der Erstellung keine Regelkette zu",
  1411 + "unset-auto-assign-to-edge-title": "Sind Sie sicher, dass Sie die Kantenregelkette '{{ruleChainName}}' bei der Erstellung nicht den Kanten zuweisen möchten?",
  1412 + "unset-auto-assign-to-edge-text": "Nach der Bestätigung wird die Kantenregelkette bei der Erstellung nicht mehr automatisch den Kanten zugewiesen.",
  1413 + "edge-template-root": "Vorlagenstamm"
1416 1414 },
1417 1415 "rulenode": {
1418 1416 "details": "Details",
... ...
... ... @@ -457,7 +457,7 @@
457 457 "select-default-customer": "Select default customer",
458 458 "default-customer": "Default customer",
459 459 "default-customer-required": "Default customer is required in order to debug dashboard on Tenant level",
460   - "edges": "Customer Edges"
  460 + "edges": "Customer edge instances"
461 461 },
462 462 "datetime": {
463 463 "date-from": "Date from",
... ... @@ -764,7 +764,7 @@
764 764 },
765 765 "edge": {
766 766 "edge": "Edge",
767   - "edges": "Edges",
  767 + "edge-instances": "Edge instances",
768 768 "edge-file": "Edge file",
769 769 "management": "Edge management",
770 770 "no-edges-matching": "No edges matching '{{entity}}' were found.",
... ... @@ -828,8 +828,8 @@
828 828 "unassign-from-edge": "Unassign from edge",
829 829 "dashboards": "Edge Dashboards",
830 830 "manage-edge-rulechains": "Manage edge rule chains",
831   - "rulechains": "Edge Rule Chains",
832   - "rulechain": "Edge Rule Chain",
  831 + "rulechain-templates": "Rule chain templates",
  832 + "rulechain-template": "Rule chain template",
833 833 "edge-key": "Edge key",
834 834 "copy-edge-key": "Copy edge key",
835 835 "edge-key-copied-message": "Edge key has been copied to clipboard",
... ... @@ -852,7 +852,11 @@
852 852 "enter-edge-type": "Enter edge type",
853 853 "no-edge-types-matching": "No edge types matching '{{entitySubtype}}' were found.",
854 854 "edge-type-list-empty": "No edge types selected.",
855   - "edge-types": "Edge types"
  855 + "edge-types": "Edge types",
  856 + "license-key-hint": "To obtain your license please navigate to the <a href='https://thingsboard.io/pricing/?active=thingsboard-edge' target='_blank'>pricing page</a> and select the best license option for your case.",
  857 + "cloud-endpoint-hint": "Edge requires HTTP(s) access to Cloud (ThingsBoard CE/PE) to verify the license key. Please specify Cloud URL that Edge is able to connect to.",
  858 + "missing-related-rule-chains-title": "Edge has missing related rule chain(s)",
  859 + "missing-related-rule-chains-text": "Assigned to edge rule chain(s) use rule nodes that forward message(s) to rule chain(s) that are not assigned to this edge. <br><br> List of missing rule chain(s): <br> {{missingRuleChains}}"
856 860 },
857 861 "error": {
858 862 "unable-to-connect": "Unable to connect to the server! Please check your internet connection.",
... ... @@ -1549,9 +1553,6 @@
1549 1553 "rulechain": {
1550 1554 "rulechain": "Rule chain",
1551 1555 "rulechains": "Rule chains",
1552   - "core-rulechains": "Core Rule chains",
1553   - "edge-rulechains": "Edge Rule chains",
1554   - "default-root": "Default root",
1555 1556 "root": "Root",
1556 1557 "delete": "Delete rule chain",
1557 1558 "name": "Name",
... ... @@ -1588,7 +1589,7 @@
1588 1589 "assign-rulechains": "Assign rulechains",
1589 1590 "assign-new-rulechain": "Assign new rulechain",
1590 1591 "delete-rulechains": "Delete rulechains",
1591   - "default": "Default",
  1592 + "set-auto-assign-to-edge-card": "Assign to edge(s) on creation",
1592 1593 "unassign-rulechain": "Unassign rulechain",
1593 1594 "unassign-rulechains": "Unassign rulechains",
1594 1595 "unassign-rulechain-title": "Are you sure you want to unassign the rulechain '{{ruleChainTitle}}'?",
... ... @@ -1601,13 +1602,17 @@
1601 1602 "set-default-root-edge": "Make rule chain default root",
1602 1603 "set-default-root-edge-rulechain-title": "Are you sure you want to make the rule chain '{{ruleChainName}}' default edge root?",
1603 1604 "set-default-root-edge-rulechain-text": "After the confirmation the rule chain will become default edge root and will handle all incoming transport messages.",
  1605 + "set-edge-template-root-rulechain": "Make rule chain as edge template root",
  1606 + "set-edge-template-root-rulechain-title": "Are you sure you want to make the rule chain '{{ruleChainName}}' edge template root?",
  1607 + "set-edge-template-root-rulechain-text": "After the confirmation the rule chain will become edge template root and will be root rule chain for a newly created edges.",
1604 1608 "invalid-rulechain-type-error": "Unable to import rule chain: Invalid rule chain type. Expected type is {{expectedRuleChainType}}.",
1605   - "set-default-edge": "Make rule chain default",
1606   - "set-default-edge-title": "Are you sure you want to make the edge rule chain '{{ruleChainName}}' default?",
1607   - "set-default-edge-text": "After the confirmation the edge rule chain will be added to default list and assigned to newly created edge(s).",
1608   - "remove-default-edge": "Remove rule chain from defaults",
1609   - "remove-default-edge-title": "Are you sure you want to remove the edge rule chain '{{ruleChainName}}' from default list?",
1610   - "remove-default-edge-text": "After the confirmation the edge rule chain will not be assigned for a newly created edges."
  1609 + "set-auto-assign-to-edge": "Assign rule chain to edge(s) on creation",
  1610 + "set-auto-assign-to-edge-title": "Are you sure you want to assign the edge rule chain '{{ruleChainName}}' to edge(s) on creation?",
  1611 + "set-auto-assign-to-edge-text": "After the confirmation the edge rule chain will be automatically assigned to edge(s) on creation.",
  1612 + "unset-auto-assign-to-edge": "Do not assign rule chain to edge(s) on creation",
  1613 + "unset-auto-assign-to-edge-title": "Are you sure you do not want to assign the edge rule chain '{{ruleChainName}}' to edge(s) on creation?",
  1614 + "unset-auto-assign-to-edge-text": "After the confirmation the edge rule chain will no longer be automatically assigned to edge(s) on creation.",
  1615 + "edge-template-root": "Template Root"
1611 1616 },
1612 1617 "rulenode": {
1613 1618 "details": "Details",
... ...
... ... @@ -743,7 +743,7 @@
743 743 },
744 744 "edge": {
745 745 "edge": "Borde",
746   - "edges": "Bordes",
  746 + "edge-instances": "Bordes",
747 747 "management": "Gestión de bordes",
748 748 "no-edges-matching": "No se encontraron bordes que coincidan con '{{entity}}'",
749 749 "add": "Agregar borde",
... ... @@ -1423,8 +1423,6 @@
1423 1423 "rulechain": {
1424 1424 "rulechain": "Cadena de reglas",
1425 1425 "rulechains": "Cadenas de reglas",
1426   - "core-rulechains": "Cadenas de reglas centrales",
1427   - "edge-rulechains": "Cadenas de reglas de borde",
1428 1426 "root": "Raíz",
1429 1427 "delete": "Eliminar cadena de reglas",
1430 1428 "name": "Nombre",
... ... @@ -1461,7 +1459,6 @@
1461 1459 "assign-rulechains": "Asignar cadenas de reglas",
1462 1460 "assign-new-rulechain": "Asignar nueva cadena de reglas",
1463 1461 "delete-rulechains": "Eliminar cadenas de reglas",
1464   - "default": "Predeterminado",
1465 1462 "unassign-rulechain": "Anular asignación de cadena de reglas",
1466 1463 "unassign-rulechains": "Anular asignación de cadenas de reglas",
1467 1464 "unassign-rulechain-title": "¿Está seguro de que desea desasignar la cadena de reglas '{{ruleChainTitle}}'?",
... ... @@ -1471,16 +1468,17 @@
1471 1468 "unassign-rulechains-from-edge-text": "Después de la confirmación, todas las cadenas de reglas seleccionadas quedarán sin asignar y el borde no podrá acceder a ellas",
1472 1469 "assign-rulechain-to-edge": "Asignar cadena (s) de reglas a borde",
1473 1470 "assign-rulechain-to-edge-text": "Seleccione las cadenas de reglas para asignar al borde",
1474   - "set-default-root-edge": "Hacer que la cadena de reglas sea la raíz predeterminada",
1475   - "set-default-root-edge-rulechain-title": "¿Está seguro de que desea hacer que la cadena de reglas '{{ruleChainName}}' sea la raíz de borde predeterminada?",
1476   - "set-default-root-edge-rulechain-text": "Después de la confirmación, la cadena de reglas se convertirá en raíz raíz predeterminada y manejará todos los mensajes de transporte entrantes",
  1471 + "set-edge-template-root-rulechain": "Hacer una cadena de reglas como raíz de la plantilla de borde",
  1472 + "set-edge-template-root-rulechain-title": "¿Está seguro de que desea que la cadena de reglas '{{ruleChainName}}' sea la raíz de la plantilla de borde?",
  1473 + "set-edge-template-root-rulechain-text": "Después de la confirmación, la cadena de reglas se convertirá en la raíz de la plantilla de borde y será la cadena de reglas raíz para los bordes recién creados.",
1477 1474 "invalid-rulechain-type-error": "No se puede importar la cadena de reglas: Tipo de cadena de reglas no válido. El tipo esperado es {{expectedRuleChainType}}",
1478   - "set-default-edge": "Hacer que la cadena de reglas de borde sea predeterminada",
1479   - "set-default-edge-title": "¿Está seguro de que desea que la cadena de reglas de borde '{{ruleChainName}}' sea predeterminada?",
1480   - "set-default-edge-text": "Después de la confirmación, la cadena de reglas de borde se agregará a la lista predeterminada y se asignará a los bordes recién creados",
1481   - "remove-default-edge": "Eliminar la cadena de regla de borde de los valores predeterminados",
1482   - "remove-default-edge-title": "¿Está seguro de que desea eliminar la cadena de reglas de borde '{{ruleChainName}}' de la lista predeterminada?",
1483   - "remove-default-edge-text": "Después de la confirmación, la cadena de reglas de borde no se asignará a los bordes recién creados"
  1475 + "set-auto-assign-to-edge": "Asignar cadena de reglas a los bordes en la creación",
  1476 + "set-auto-assign-to-edge-title": "¿Está seguro de que desea asignar la cadena de reglas de borde '{{ruleChainName}}' a los bordes en la creación?",
  1477 + "set-auto-assign-to-edge-text": "Después de la confirmación, la cadena de reglas de borde se asignará automáticamente a los bordes en la creación.",
  1478 + "unset-auto-assign-to-edge": "No asigne una cadena de reglas a los bordes en la creación",
  1479 + "unset-auto-assign-to-edge-title": "¿Está seguro de que no desea asignar la cadena de reglas de borde '{{ruleChainName}}' a los bordes en la creación?",
  1480 + "unset-auto-assign-to-edge-text": "Después de la confirmación, la cadena de reglas de borde ya no se asignará automáticamente a los bordes en la creación.",
  1481 + "edge-template-root": "Raíz de plantilla"
1484 1482 },
1485 1483 "rulenode": {
1486 1484 "details": "Detalles",
... ...
... ... @@ -748,7 +748,7 @@
748 748 },
749 749 "edge": {
750 750 "edge": "Bordure",
751   - "edges": "Bordures",
  751 + "edge-instances": "Bordures",
752 752 "management": "Gestion des bordures",
753 753 "no-edges-matching": "Aucun bordure correspondant à {{entity}} n'a été trouvé.",
754 754 "add": "Ajouter un bordure",
... ... @@ -1429,8 +1429,6 @@
1429 1429 "rulechain-required": "Chaîne de règles requise",
1430 1430 "rulechains": "Chaînes de règles",
1431 1431 "select-rulechain": "Sélectionner la chaîne de règles",
1432   - "core-rulechains": "Chaînes de règles fondamentales",
1433   - "edge-rulechains": "Chaînes de règles de la bordure",
1434 1432 "set-root": "Rend la chaîne de règles racine (root) ",
1435 1433 "set-root-rulechain-text": "Après la confirmation, la chaîne de règles deviendra racine (root) et gérera tous les messages de transport entrants.",
1436 1434 "set-root-rulechain-title": "Voulez-vous vraiment que la chaîne de règles '{{ruleChainName}} soit racine (root) ?",
... ... @@ -1438,7 +1436,6 @@
1438 1436 "assign-rulechains": "Attribuer aux chaînes de règles",
1439 1437 "assign-new-rulechain": "Attribuer une nouvele chaînes de règles",
1440 1438 "delete-rulechains": "Supprimer une chaînes de règles",
1441   - "default": "Défaut",
1442 1439 "unassign-rulechain": "Retirer chaîne de règles",
1443 1440 "unassign-rulechains": "Retirer chaînes de règles",
1444 1441 "unassign-rulechain-title": "AÊtes-vous sûr de vouloir retirer l'attribution de chaînes de règles '{{ruleChainTitle}}'?",
... ... @@ -1448,16 +1445,17 @@
1448 1445 "unassign-rulechains-from-edge-text": "Après la confirmation, tous les chaînes de règles sélectionnés ne seront pas attribués et ne seront pas accessibles a la bordure.",
1449 1446 "assign-rulechain-to-edge": "Attribuer les chaînes de règles a la bordure",
1450 1447 "assign-rulechain-to-edge-text": "Veuillez sélectionner la bordure pour attribuer le ou les chaînes de règles",
1451   - "set-default-root-edge": "Définir la racine par défaut de la chaîne de règles",
1452   - "set-default-root-edge-rulechain-title": "AVoulez-vous vraiment créer de chaînes de règles par défaut '{{ruleChainName}}'?",
1453   - "set-default-root-edge-rulechain-text": "Après la confirmation, la chaîne de règles deviendra la racine de la bordure par défaut et gérera tous les messages de transport entrants.",
  1448 + "set-edge-template-root-rulechain": "Faire de la chaîne de règles la racine du modèle d'arête",
  1449 + "set-edge-template-root-rulechain-title": "Voulez-vous vraiment définir la racine du modèle d'arête de la chaîne de règles '{{ruleChainName}}'?",
  1450 + "set-edge-template-root-rulechain-text": "Après la confirmation, la chaîne de règles deviendra la racine du modèle d'arête et sera la chaîne de règles racine pour les arêtes nouvellement créées.",
1454 1451 "invalid-rulechain-type-error": "Impossible d'importer la chaîne de règles: type de chaîne de règles non valide. Le type attendu est {{attenduRuleChainType}}.",
1455   - "set-default-edge": "Définir la chaîne de règles de la bordure par défaut",
1456   - "set-default-edge-title": "Voulez-vous vraiment définir la chaîne de règles de la bordure '{{ruleChainName}}' par défaut?",
1457   - "set-default-edge-text": "Après la confirmation, la chaîne de règles d'arête sera ajoutée à la liste par défaut et affectée aux arêtes nouvellement créées.",
1458   - "remove-default-edge": "Supprimer la chaîne de règles de la bordure des valeurs par défaut",
1459   - "remove-default-edge-title": "Voulez-vous vraiment supprimer la chaîne de règles de la bordure '{{ruleChainName}}' de la liste par défaut",
1460   - "remove-default-edge-text": "Après la confirmation, la chaîne de règles d'arête ne sera pas affectée aux arêtes nouvellement créées."
  1452 + "set-auto-assign-to-edge": "Attribuer une chaîne de règles aux arêtes lors de la création",
  1453 + "set-auto-assign-to-edge-title": "Voulez-vous vraiment attribuer la chaîne de règles d'arête '{{ruleChainName}}' à l'arête (s) lors de la création?",
  1454 + "set-auto-assign-to-edge-text": "Après la confirmation, la chaîne de règles d'arêtes sera automatiquement affectée aux arêtes lors de la création.",
  1455 + "unset-auto-assign-to-edge": "N'attribuez pas de chaîne de règles aux arêtes lors de la création",
  1456 + "unset-auto-assign-to-edge-title": "Êtes-vous sûr de ne pas vouloir attribuer la chaîne de règles d'arête '{{ruleChainName}}' à l'arête (s) lors de la création?",
  1457 + "unset-auto-assign-to-edge-text": "Après la confirmation, la chaîne de règles d'arêtes ne sera plus automatiquement affectée aux arêtes lors de la création.",
  1458 + "edge-template-root": "Racine du modèle"
1461 1459 },
1462 1460 "rulenode": {
1463 1461 "add": "Ajouter un noeud de règle",
... ...
... ... @@ -18,6 +18,6 @@
18 18 <div ng-if="(vm.parentCtl.ruleChainsScope === 'tenant' && item && item.root) ||
19 19 (vm.parentCtl.ruleChainsScope === 'edge' && vm.parentCtl.isRootRuleChain(item))" translate>rulechain.root</div>
20 20
21   -<div ng-if="vm.parentCtl.ruleChainsScope === 'edges' && vm.parentCtl.isRootRuleChain(item)" translate>rulechain.default-root</div>
  21 +<div ng-if="vm.parentCtl.ruleChainsScope === 'edges' && vm.parentCtl.isRootRuleChain(item)" translate>rulechain.edge-template-root</div>
22 22
23   -<div ng-if="(vm.parentCtl.ruleChainsScope === 'edges' && vm.parentCtl.isDefaultEdgeRuleChain(item))" translate>rulechain.default</div>
  23 +<div ng-if="(vm.parentCtl.ruleChainsScope === 'edges' && vm.parentCtl.isDefaultEdgeRuleChain(item))" translate>rulechain.set-auto-assign-to-edge-card</div>
... ...
... ... @@ -15,32 +15,31 @@
15 15 limitations under the License.
16 16
17 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   -
22   -<md-button ng-click="onSetRootRuleChain({event: $event})"
23   - ng-show="!isEdit && !ruleChain.root && ruleChainsScope == 'tenant'"
24   - class="md-raised md-primary">{{ 'rulechain.set-root' | translate }}</md-button>
25   -
26   -<md-button ng-click="onSetRootRuleChain({event: $event})"
27   - ng-show="!isEdit && !ruleChain.root && ruleChainsScope == 'edges'"
28   - class="md-raised md-primary">{{ 'rulechain.set-default-root-edge' | translate }}</md-button>
29   -<md-button ng-click="onSetDefaultEdgeRuleChain({event: $event})"
30   - ng-show="!isEdit && !ruleChain.root && !ruleChain.isDefault && ruleChainsScope == 'edges'"
31   - class="md-raised md-primary">{{ 'rulechain.set-default-edge' | translate }}</md-button>
32   -<md-button ng-click="onRemoveDefaultEdgeRuleChain({event: $event})"
33   - ng-show="!isEdit && !ruleChain.root && ruleChain.isDefault && ruleChainsScope == 'edges'"
34   - class="md-raised md-primary">{{ 'rulechain.remove-default-edge' | translate }}</md-button>
35   -
36   -<md-button ng-click="onSetRootRuleChain({event: $event})"
37   - ng-show="!isEdit && ruleChainsScope == 'edge' && edge.rootRuleChainId.id !== ruleChain.id.id"
38   - class="md-raised md-primary">{{ 'rulechain.set-root' | translate }}</md-button>
39   -
40   -<md-button ng-click="onDeleteRuleChain({event: $event})"
41   - ng-show="!isEdit && !ruleChain.root && ruleChainsScope != 'edge'"
42   - class="md-raised md-primary">{{ 'rulechain.delete' | translate }}</md-button>
  18 +<div layout="row">
  19 + <md-button ng-click="onExportRuleChain({event: $event})"
  20 + ng-show="!isEdit"
  21 + class="md-raised md-primary">{{ 'rulechain.export' | translate }}</md-button>
  22 + <md-button ng-click="onDeleteRuleChain({event: $event})"
  23 + ng-show="!isEdit && !ruleChain.root && ruleChainsScope != 'edge'"
  24 + class="md-raised md-primary">{{ 'rulechain.delete' | translate }}</md-button>
  25 +</div>
  26 +<div layout="row"><md-button ng-click="onSetRootRuleChain({event: $event})"
  27 + ng-show="!isEdit && !ruleChain.root && ruleChainsScope == 'tenant'"
  28 + class="md-raised md-primary">{{ 'rulechain.set-root' | translate }}</md-button>
  29 + <md-button ng-click="onSetRootRuleChain({event: $event})"
  30 + ng-show="!isEdit && !ruleChain.root && ruleChainsScope == 'edges'"
  31 + class="md-raised md-primary">{{ 'rulechain.set-edge-template-root-rulechain' | translate }}</md-button>
  32 + <md-button ng-click="onSetAutoAssignToEdgeRuleChain({event: $event})"
  33 + ng-show="!isEdit && !ruleChain.root && !ruleChain.isDefault && ruleChainsScope == 'edges'"
  34 + class="md-raised md-primary">{{ 'rulechain.set-auto-assign-to-edge' | translate }}</md-button>
  35 + <md-button ng-click="onUnsetAutoAssignToEdgeRuleChain({event: $event})"
  36 + ng-show="!isEdit && !ruleChain.root && ruleChain.isDefault && ruleChainsScope == 'edges'"
  37 + class="md-raised md-primary">{{ 'rulechain.unset-auto-assign-to-edge' | translate }}</md-button>
43 38
  39 + <md-button ng-click="onSetRootRuleChain({event: $event})"
  40 + ng-show="!isEdit && ruleChainsScope == 'edge' && edge.rootRuleChainId.id !== ruleChain.id.id"
  41 + class="md-raised md-primary">{{ 'rulechain.set-root' | translate }}</md-button>
  42 +</div>
44 43 <div layout="row">
45 44 <md-button ngclipboard data-clipboard-action="copy"
46 45 ngclipboard-success="onRuleChainIdCopied(e)"
... ...
... ... @@ -1270,9 +1270,9 @@ export function RuleChainController($state, $scope, $compile, $q, $mdUtil, $time
1270 1270 vm.isImport = false;
1271 1271 $mdUtil.nextTick(() => {
1272 1272 if (vm.ruleChain.type === vm.types.ruleChainType.core) {
1273   - $state.go('home.ruleChains.core.ruleChain', {ruleChainId: vm.ruleChain.id.id});
  1273 + $state.go('home.ruleChains.ruleChain', {ruleChainId: vm.ruleChain.id.id});
1274 1274 } else {
1275   - $state.go('home.ruleChains.edge.ruleChain', {ruleChainId: vm.ruleChain.id.id});
  1275 + $state.go('home.edges.edgeRuleChains.ruleChain', {ruleChainId: vm.ruleChain.id.id});
1276 1276 }
1277 1277 });
1278 1278 } else {
... ...
... ... @@ -42,8 +42,8 @@ export default function RuleChainDirective($compile, $templateCache, $mdDialog,
42 42 theForm: '=',
43 43 ruleChainsScope: '=',
44 44 edge: '=',
45   - onSetDefaultEdgeRuleChain: '&',
46   - onRemoveDefaultEdgeRuleChain: '&',
  45 + onSetAutoAssignToEdgeRuleChain: '&',
  46 + onUnsetAutoAssignToEdgeRuleChain: '&',
47 47 onSetRootRuleChain: '&',
48 48 onExportRuleChain: '&',
49 49 onDeleteRuleChain: '&'
... ...
... ... @@ -29,15 +29,6 @@ export default function RuleChainRoutes($stateProvider, NodeTemplatePathProvider
29 29 $stateProvider
30 30 .state('home.ruleChains', {
31 31 url: '/ruleChains',
32   - module: 'private',
33   - auth: ['SYS_ADMIN', 'TENANT_ADMIN'],
34   - redirectTo: 'home.ruleChains.core',
35   - ncyBreadcrumb: {
36   - label: '{"icon": "settings_ethernet", "label": "rulechain.rulechains"}'
37   - }
38   - })
39   - .state('home.ruleChains.core', {
40   - url: '/ruleChains/core',
41 32 params: {'topIndex': 0},
42 33 module: 'private',
43 34 auth: ['SYS_ADMIN', 'TENANT_ADMIN'],
... ... @@ -50,13 +41,13 @@ export default function RuleChainRoutes($stateProvider, NodeTemplatePathProvider
50 41 },
51 42 data: {
52 43 searchEnabled: true,
53   - pageTitle: 'rulechain.core-rulechains',
  44 + pageTitle: 'rulechain.rulechains',
54 45 ruleChainsType: 'tenant'
55 46 },
56 47 ncyBreadcrumb: {
57   - label: '{"icon": "settings_ethernet", "label": "rulechain.core-rulechains"}'
  48 + label: '{"icon": "settings_ethernet", "label": "rulechain.rulechains"}'
58 49 }
59   - }).state('home.ruleChains.core.ruleChain', {
  50 + }).state('home.ruleChains.ruleChain', {
60 51 url: '/:ruleChainId',
61 52 reloadOnSearch: false,
62 53 module: 'private',
... ... @@ -134,8 +125,8 @@ export default function RuleChainRoutes($stateProvider, NodeTemplatePathProvider
134 125 ncyBreadcrumb: {
135 126 label: '{"icon": "settings_ethernet", "label": "{{ (\'rulechain.import\' | translate) + \': \'+ vm.ruleChain.name }}", "translate": "false"}'
136 127 }
137   - }).state('home.ruleChains.edge', {
138   - url: '/ruleChains/edge',
  128 + }).state('home.edges.edgeRuleChains', {
  129 + url: '/ruleChains',
139 130 params: {'topIndex': 0},
140 131 module: 'private',
141 132 auth: ['TENANT_ADMIN'],
... ... @@ -148,13 +139,13 @@ export default function RuleChainRoutes($stateProvider, NodeTemplatePathProvider
148 139 },
149 140 data: {
150 141 searchEnabled: true,
151   - pageTitle: 'rulechain.edge-rulechains',
  142 + pageTitle: 'edge.rulechain-templates',
152 143 ruleChainsType: 'edges'
153 144 },
154 145 ncyBreadcrumb: {
155   - label: '{"icon": "code", "label": "rulechain.edge-rulechains"}'
  146 + label: '{"icon": "settings_ethernet", "label": "edge.rulechain-templates"}'
156 147 }
157   - }).state('home.ruleChains.edge.ruleChain', {
  148 + }).state('home.edges.edgeRuleChains.ruleChain', {
158 149 url: '/:ruleChainId',
159 150 reloadOnSearch: false,
160 151 module: 'private',
... ... @@ -186,10 +177,51 @@ export default function RuleChainRoutes($stateProvider, NodeTemplatePathProvider
186 177 data: {
187 178 import: false,
188 179 searchEnabled: false,
189   - pageTitle: 'edge.rulechain'
  180 + pageTitle: 'edge.rulechain-template'
  181 + },
  182 + ncyBreadcrumb: {
  183 + label: '{"icon": "settings_ethernet", "label": "{{ vm.ruleChain.name }}", "translate": "false"}'
  184 + }
  185 + }).state('home.edges.edgeRuleChains.importRuleChain', {
  186 + url: '/edges/ruleChains/import',
  187 + reloadOnSearch: false,
  188 + module: 'private',
  189 + auth: ['SYS_ADMIN', 'TENANT_ADMIN'],
  190 + views: {
  191 + "content@home": {
  192 + templateUrl: ruleChainTemplate,
  193 + controller: 'RuleChainController',
  194 + controllerAs: 'vm'
  195 + }
  196 + },
  197 + params: {
  198 + ruleChainImport: {},
  199 + ruleChainType: {}
  200 + },
  201 + resolve: {
  202 + ruleChain:
  203 + /*@ngInject*/
  204 + function($stateParams) {
  205 + return $stateParams.ruleChainImport.ruleChain;
  206 + },
  207 + ruleChainMetaData:
  208 + /*@ngInject*/
  209 + function($stateParams) {
  210 + return $stateParams.ruleChainImport.metadata;
  211 + },
  212 + ruleNodeComponents:
  213 + /*@ngInject*/
  214 + function($stateParams, ruleChainService) {
  215 + return ruleChainService.getRuleNodeComponents($stateParams.ruleChainType);
  216 + }
  217 + },
  218 + data: {
  219 + import: true,
  220 + searchEnabled: true,
  221 + pageTitle: 'edge.rulechain-template'
190 222 },
191 223 ncyBreadcrumb: {
192   - label: '{"icon": "code", "label": "{{ vm.ruleChain.name }}", "translate": "false"}'
  224 + label: '{"icon": "settings_ethernet", "label": "{{ (\'rulechain.import\' | translate) + \': \'+ vm.ruleChain.name }}", "translate": "false"}'
193 225 }
194 226 });
195 227 }
... ...
... ... @@ -97,7 +97,7 @@ export default function RuleChainsController(ruleChainService, userService, impo
97 97
98 98 vm.exportRuleChain = exportRuleChain;
99 99 vm.setRootRuleChain = setRootRuleChain;
100   - vm.setDefaultEdgeRuleChain = setDefaultEdgeRuleChain;
  100 + vm.setAutoAssignToEdgeRuleChain = setAutoAssignToEdgeRuleChain;
101 101 vm.unsetAutoAssignToEdgeRuleChain = unsetAutoAssignToEdgeRuleChain;
102 102
103 103 initController();
... ... @@ -185,10 +185,10 @@ export default function RuleChainsController(ruleChainService, userService, impo
185 185
186 186 ruleChainActionsList.push({
187 187 onAction: function ($event, item) {
188   - setDefaultEdgeRuleChain($event, item);
  188 + setAutoAssignToEdgeRuleChain($event, item);
189 189 },
190   - name: function() { return $translate.instant('rulechain.set-default-edge') },
191   - details: function() { return $translate.instant('rulechain.set-default-edge') },
  190 + name: function() { return $translate.instant('rulechain.set-auto-assign-to-edge') },
  191 + details: function() { return $translate.instant('rulechain.set-auto-assign-to-edge') },
192 192 icon: "bookmark_outline",
193 193 isEnabled: isNonDefaultEdgeRuleChain
194 194 });
... ... @@ -197,8 +197,8 @@ export default function RuleChainsController(ruleChainService, userService, impo
197 197 onAction: function ($event, item) {
198 198 unsetAutoAssignToEdgeRuleChain($event, item);
199 199 },
200   - name: function() { return $translate.instant('rulechain.remove-default-edge') },
201   - details: function() { return $translate.instant('rulechain.remove-default-edge') },
  200 + name: function() { return $translate.instant('rulechain.unset-auto-assign-to-edge') },
  201 + details: function() { return $translate.instant('rulechain.unset-auto-assign-to-edge') },
202 202 icon: "bookmark",
203 203 isEnabled: isDefaultEdgeRuleChain
204 204 });
... ... @@ -207,8 +207,8 @@ export default function RuleChainsController(ruleChainService, userService, impo
207 207 onAction: function ($event, item) {
208 208 setEdgeTemplateRootRuleChain($event, item);
209 209 },
210   - name: function() { return $translate.instant('rulechain.set-default-root-edge') },
211   - details: function() { return $translate.instant('rulechain.set-default-root-edge') },
  210 + name: function() { return $translate.instant('rulechain.set-edge-template-root-rulechain') },
  211 + details: function() { return $translate.instant('rulechain.set-edge-template-root-rulechain') },
212 212 icon: "flag",
213 213 isEnabled: isNonRootRuleChain
214 214 });
... ... @@ -247,7 +247,7 @@ export default function RuleChainsController(ruleChainService, userService, impo
247 247 onAction: function ($event) {
248 248 importExport.importRuleChain($event, types.ruleChainType.edge).then(
249 249 function(ruleChainImport) {
250   - $state.go('home.ruleChains.importRuleChain', {ruleChainImport:ruleChainImport, ruleChainType: types.ruleChainType.edge});
  250 + $state.go('home.edges.edgeRuleChains.importRuleChain', {ruleChainImport:ruleChainImport, ruleChainType: types.ruleChainType.edge});
251 251 }
252 252 );
253 253 },
... ... @@ -401,9 +401,9 @@ export default function RuleChainsController(ruleChainService, userService, impo
401 401 if (vm.ruleChainsScope === 'edge') {
402 402 $state.go('home.edges.ruleChains.ruleChain', Object.assign(ruleChainParams, edgeId = vm.edge.id.id));
403 403 } else if (vm.ruleChainsScope === 'edges') {
404   - $state.go('home.ruleChains.edge.ruleChain', ruleChainParams);
  404 + $state.go('home.edges.edgeRuleChains.ruleChain', ruleChainParams);
405 405 } else {
406   - $state.go('home.ruleChains.core.ruleChain', ruleChainParams);
  406 + $state.go('home.ruleChains.ruleChain', ruleChainParams);
407 407 }
408 408 }
409 409
... ... @@ -471,13 +471,13 @@ export default function RuleChainsController(ruleChainService, userService, impo
471 471 });
472 472 }
473 473
474   - function setDefaultEdgeRuleChain($event, ruleChain) {
  474 + function setAutoAssignToEdgeRuleChain($event, ruleChain) {
475 475 $event.stopPropagation();
476 476 var confirm = $mdDialog.confirm()
477 477 .targetEvent($event)
478   - .title($translate.instant('rulechain.set-default-edge-title', {ruleChainName: ruleChain.name}))
479   - .htmlContent($translate.instant('rulechain.set-default-edge-text'))
480   - .ariaLabel($translate.instant('rulechain.set-default-edge'))
  478 + .title($translate.instant('rulechain.set-auto-assign-to-edge-title', {ruleChainName: ruleChain.name}))
  479 + .htmlContent($translate.instant('rulechain.set-auto-assign-to-edge-text'))
  480 + .ariaLabel($translate.instant('rulechain.set-auto-assign-to-edge'))
481 481 .cancel($translate.instant('action.no'))
482 482 .ok($translate.instant('action.yes'));
483 483 $mdDialog.show(confirm).then(function () {
... ... @@ -493,9 +493,9 @@ export default function RuleChainsController(ruleChainService, userService, impo
493 493 $event.stopPropagation();
494 494 var confirm = $mdDialog.confirm()
495 495 .targetEvent($event)
496   - .title($translate.instant('rulechain.remove-default-edge-title', {ruleChainName: ruleChain.name}))
497   - .htmlContent($translate.instant('rulechain.remove-default-edge-text'))
498   - .ariaLabel($translate.instant('rulechain.remove-default-edge'))
  496 + .title($translate.instant('rulechain.unset-auto-assign-to-edge-title', {ruleChainName: ruleChain.name}))
  497 + .htmlContent($translate.instant('rulechain.unset-auto-assign-to-edge-text'))
  498 + .ariaLabel($translate.instant('rulechain.unset-auto-assign-to-edge'))
499 499 .cancel($translate.instant('action.no'))
500 500 .ok($translate.instant('action.yes'));
501 501 $mdDialog.show(confirm).then(function () {
... ... @@ -511,8 +511,8 @@ export default function RuleChainsController(ruleChainService, userService, impo
511 511 $event.stopPropagation();
512 512 var confirm = $mdDialog.confirm()
513 513 .targetEvent($event)
514   - .title($translate.instant('rulechain.set-default-root-edge-rulechain-title', {ruleChainName: ruleChain.name}))
515   - .htmlContent($translate.instant('rulechain.set-default-root-edge-rulechain-text'))
  514 + .title($translate.instant('rulechain.set-edge-template-root-rulechain-title', {ruleChainName: ruleChain.name}))
  515 + .htmlContent($translate.instant('rulechain.set-edge-template-root-rulechain-text'))
516 516 .ariaLabel($translate.instant('rulechain.set-root-rulechain-text'))
517 517 .cancel($translate.instant('action.no'))
518 518 .ok($translate.instant('action.yes'));
... ... @@ -572,8 +572,32 @@ export default function RuleChainsController(ruleChainService, userService, impo
572 572 fullscreen: true,
573 573 targetEvent: $event
574 574 }).then(function () {
575   - vm.grid.refreshList();
576   - }, function () {
  575 + edgeService.findMissingToRelatedRuleChains(edgeId).then(
  576 + function success(missingRuleChains) {
  577 + if (missingRuleChains && Object.keys(missingRuleChains).length > 0) {
  578 + let formattedMissingRuleChains = [];
  579 + for (const missingRuleChain of Object.keys(missingRuleChains)) {
  580 + const arrayOfMissingRuleChains = missingRuleChains[missingRuleChain];
  581 + const tmp = "- '" + missingRuleChain + "': '" + arrayOfMissingRuleChains.join("', ") + "'";
  582 + formattedMissingRuleChains.push(tmp);
  583 + }
  584 + var alert = $mdDialog.alert()
  585 + .parent(angular.element($document[0].body))
  586 + .clickOutsideToClose(true)
  587 + .title($translate.instant('edge.missing-related-rule-chains-title'))
  588 + .htmlContent($translate.instant('edge.missing-related-rule-chains-text', {missingRuleChains: formattedMissingRuleChains.join("<br>")}))
  589 + .ok($translate.instant('action.close'));
  590 + alert._options.fullscreen = true;
  591 + $mdDialog.show(alert).then(
  592 + function () {
  593 + vm.grid.refreshList();
  594 + }
  595 + );
  596 + } else {
  597 + vm.grid.refreshList();
  598 + }
  599 + }
  600 + );
577 601 });
578 602 },
579 603 function fail() {
... ...
... ... @@ -28,8 +28,8 @@
28 28 the-form="vm.grid.detailsForm"
29 29 rule-chains-scope="vm.ruleChainsScope"
30 30 edge="vm.edge"
31   - on-set-default-edge-rule-chain="vm.setDefaultEdgeRuleChain(event, vm.grid.detailsConfig.currentItem)"
32   - on-remove-default-edge-rule-chain="vm.unsetAutoAssignToEdgeRuleChain(event, vm.grid.detailsConfig.currentItem)"
  31 + on-set-auto-assign-to-edge-rule-chain="vm.setAutoAssignToEdgeRuleChain(event, vm.grid.detailsConfig.currentItem)"
  32 + on-unset-auto-assign-to-edge-rule-chain="vm.unsetAutoAssignToEdgeRuleChain(event, vm.grid.detailsConfig.currentItem)"
33 33 on-set-root-rule-chain="vm.setRootRuleChain(event, vm.grid.detailsConfig.currentItem)"
34 34 on-export-rule-chain="vm.exportRuleChain(event, vm.grid.detailsConfig.currentItem)"
35 35 on-delete-rule-chain="vm.grid.deleteItem(event, vm.grid.detailsConfig.currentItem)">
... ...
... ... @@ -156,24 +156,9 @@ function Menu(userService, $state, $rootScope) {
156 156 },
157 157 {
158 158 name: 'rulechain.rulechains',
159   - type: 'toggle',
  159 + type: 'link',
160 160 state: 'home.ruleChains',
161   - height: '80px',
162   - icon: 'settings_ethernet',
163   - pages: [
164   - {
165   - name: 'rulechain.core-rulechains',
166   - type: 'link',
167   - state: 'home.ruleChains.core',
168   - icon: 'settings_ethernet'
169   - },
170   - {
171   - name: 'rulechain.edge-rulechains',
172   - type: 'link',
173   - state: 'home.ruleChains.edge',
174   - icon: 'code'
175   - }
176   - ]
  161 + icon: 'settings_ethernet'
177 162 },
178 163 {
179 164 name: 'customer.customers',
... ... @@ -198,13 +183,33 @@ function Menu(userService, $state, $rootScope) {
198 183 type: 'link',
199 184 state: 'home.entityViews',
200 185 icon: 'view_quilt'
201   - },
202   - {
203   - name: 'edge.edges',
204   - type: 'link',
205   - state: 'home.edges',
206   - icon: 'router'
207   - },
  186 + }];
  187 + if (userService.isEdgesSupportEnabled()) {
  188 + sections.push(
  189 + {
  190 + name: 'edge.management',
  191 + type: 'toggle',
  192 + state: 'home.edges',
  193 + height: '80px',
  194 + icon: 'router',
  195 + pages: [
  196 + {
  197 + name: 'edge.edge-instances',
  198 + type: 'link',
  199 + state: 'home.edges',
  200 + icon: 'router'
  201 + },
  202 + {
  203 + name: 'edge.rulechain-templates',
  204 + type: 'link',
  205 + state: 'home.edges.edgeRuleChains',
  206 + icon: 'settings_ethernet'
  207 + }
  208 + ]
  209 + }
  210 + );
  211 + }
  212 + sections.push(
208 213 {
209 214 name: 'widget.widget-library',
210 215 type: 'link',
... ... @@ -222,21 +227,17 @@ function Menu(userService, $state, $rootScope) {
222 227 type: 'link',
223 228 state: 'home.auditLogs',
224 229 icon: 'track_changes'
225   - }];
  230 + }
  231 + );
226 232
227 233 homeSections =
228 234 [{
229   - name: 'rulechain.management',
  235 + name: 'rulechain.rulechains',
230 236 places: [
231 237 {
232   - name: 'rulechain.core-rulechains',
  238 + name: 'rulechain.rulechains',
233 239 icon: 'settings_ethernet',
234   - state: 'home.ruleChains.core'
235   - },
236   - {
237   - name: 'rulechain.edge-rulechains',
238   - icon: 'code',
239   - state: 'home.ruleChains.edge'
  240 + state: 'home.ruleChains'
240 241 }
241 242 ]
242 243 },
... ... @@ -279,43 +280,51 @@ function Menu(userService, $state, $rootScope) {
279 280 state: 'home.entityViews'
280 281 }
281 282 ]
282   - },
283   - {
284   - name: 'edge.management',
285   - places: [
286   - {
287   - name: 'edge.edges',
288   - icon: 'router',
289   - state: 'home.edges'
290   - }
291   - ]
292   - },
293   - {
294   - name: 'dashboard.management',
295   - places: [
296   - {
297   - name: 'widget.widget-library',
298   - icon: 'now_widgets',
299   - state: 'home.widgets-bundles'
300   - },
301   - {
302   - name: 'dashboard.dashboards',
303   - icon: 'dashboard',
304   - state: 'home.dashboards'
305   - }
306   - ]
307   - },
308   - {
309   - name: 'audit-log.audit',
310   - places: [
311   - {
312   - name: 'audit-log.audit-logs',
313   - icon: 'track_changes',
314   - state: 'home.auditLogs'
315   - }
316   - ]
317 283 }];
  284 + if (userService.isEdgesSupportEnabled()) {
  285 + homeSections.push({
  286 + name: 'edge.management',
  287 + places: [
  288 + {
  289 + name: 'edge.edge-instances',
  290 + icon: 'router',
  291 + state: 'home.edges'
  292 + },
  293 + {
  294 + name: 'edge.rulechain-templates',
  295 + icon: 'settings_ethernet',
  296 + state: 'home.edges.edgeRuleChains'
  297 + }
  298 + ]
318 299
  300 + });
  301 + }
  302 + homeSections.push(
  303 + {
  304 + name: 'dashboard.management',
  305 + places: [
  306 + {
  307 + name: 'widget.widget-library',
  308 + icon: 'now_widgets',
  309 + state: 'home.widgets-bundles'
  310 + },
  311 + {
  312 + name: 'dashboard.dashboards',
  313 + icon: 'dashboard',
  314 + state: 'home.dashboards'
  315 + }
  316 + ]
  317 + },
  318 + {
  319 + name: 'audit-log.audit',
  320 + places: [
  321 + {
  322 + name: 'audit-log.audit-logs',
  323 + icon: 'track_changes',
  324 + state: 'home.auditLogs'
  325 + }
  326 + ]
  327 + });
319 328 } else if (authority === 'CUSTOMER_USER') {
320 329 sections = [
321 330 {
... ... @@ -341,19 +350,25 @@ function Menu(userService, $state, $rootScope) {
341 350 type: 'link',
342 351 state: 'home.entityViews',
343 352 icon: 'view_quilt'
344   - },
345   - {
346   - name: 'edge.edges',
347   - type: 'link',
348   - state: 'home.edges',
349   - icon: 'router'
350   - },
  353 + }];
  354 + if (userService.isEdgesSupportEnabled()) {
  355 + sections.push(
  356 + {
  357 + name: 'edge.edge-instances',
  358 + type: 'link',
  359 + state: 'home.edges',
  360 + icon: 'router'
  361 + }
  362 + );
  363 + }
  364 + sections.push(
351 365 {
352 366 name: 'dashboard.dashboards',
353 367 type: 'link',
354 368 state: 'home.dashboards',
355 369 icon: 'dashboard'
356   - }];
  370 + }
  371 + );
357 372
358 373 homeSections =
359 374 [{
... ... @@ -385,17 +400,22 @@ function Menu(userService, $state, $rootScope) {
385 400 state: 'home.entityViews'
386 401 }
387 402 ]
388   - },
389   - {
390   - name: 'edge.management',
391   - places: [
392   - {
393   - name: 'edge.edges',
394   - icon: 'router',
395   - state: 'home.edges'
396   - }
397   - ]
398   - },
  403 + }];
  404 + if (userService.isEdgesSupportEnabled()) {
  405 + homeSections.push(
  406 + {
  407 + name: 'edge.edge-instances',
  408 + places: [
  409 + {
  410 + name: 'edge.edge-instances',
  411 + icon: 'router',
  412 + state: 'home.edges'
  413 + }
  414 + ]
  415 + }
  416 + );
  417 + }
  418 + homeSections.push(
399 419 {
400 420 name: 'dashboard.view-dashboards',
401 421 places: [
... ... @@ -405,7 +425,8 @@ function Menu(userService, $state, $rootScope) {
405 425 state: 'home.dashboards'
406 426 }
407 427 ]
408   - }];
  428 + }
  429 + );
409 430 }
410 431 }
411 432 }
... ... @@ -423,4 +444,4 @@ function Menu(userService, $state, $rootScope) {
423 444 return $state.includes(section.state);
424 445 }
425 446
426   -}
\ No newline at end of file
  447 +}
... ...