thingsboard-developers

Details

diff --git a/application/src/main/java/org/thingsboard/server/actors/plugin/PluginProcessingContext.java b/application/src/main/java/org/thingsboard/server/actors/plugin/PluginProcessingContext.java
index 5dbc5f4..57a838f 100644
--- a/application/src/main/java/org/thingsboard/server/actors/plugin/PluginProcessingContext.java
+++ b/application/src/main/java/org/thingsboard/server/actors/plugin/PluginProcessingContext.java
@@ -326,7 +326,19 @@ public final class PluginProcessingContext implements PluginContext {
                             callback.onSuccess(this, Boolean.FALSE);
                         } else {
                             ListenableFuture<RuleMetaData> ruleFuture = pluginCtx.ruleService.findRuleByIdAsync(new RuleId(entityId.getId()));
-                            Futures.addCallback(ruleFuture, getCallback(callback, rule -> rule != null && rule.getTenantId().equals(ctx.getTenantId())));
+                            Futures.addCallback(ruleFuture, getCallback(callback, rule -> {
+                                if (rule == null) {
+                                    return Boolean.FALSE;
+                                } else {
+                                    if (ctx.isTenantAdmin() && !rule.getTenantId().equals(ctx.getTenantId())) {
+                                        return Boolean.FALSE;
+                                    } else if (ctx.isSystemAdmin() && !rule.getTenantId().isNullUid()) {
+                                        return Boolean.FALSE;
+                                    } else {
+                                        return Boolean.TRUE;
+                                    }
+                                }
+                            }));
                         }
                         return;
                     case PLUGIN:
@@ -334,7 +346,19 @@ public final class PluginProcessingContext implements PluginContext {
                             callback.onSuccess(this, Boolean.FALSE);
                         } else {
                             ListenableFuture<PluginMetaData> pluginFuture = pluginCtx.pluginService.findPluginByIdAsync(new PluginId(entityId.getId()));
-                            Futures.addCallback(pluginFuture, getCallback(callback, plugin -> plugin != null && plugin.getTenantId().equals(ctx.getTenantId())));
+                            Futures.addCallback(pluginFuture, getCallback(callback, plugin -> {
+                                if (plugin == null) {
+                                    return Boolean.FALSE;
+                                } else {
+                                    if (ctx.isTenantAdmin() && !plugin.getTenantId().equals(ctx.getTenantId())) {
+                                        return Boolean.FALSE;
+                                    } else if (ctx.isSystemAdmin() && !plugin.getTenantId().isNullUid()) {
+                                        return Boolean.FALSE;
+                                    } else {
+                                        return Boolean.TRUE;
+                                    }
+                                }
+                            }));
                         }
                         return;
                     case CUSTOMER:
diff --git a/ui/src/app/entity/attribute/attribute-table.directive.js b/ui/src/app/entity/attribute/attribute-table.directive.js
index e05fcb6..da7697d 100644
--- a/ui/src/app/entity/attribute/attribute-table.directive.js
+++ b/ui/src/app/entity/attribute/attribute-table.directive.js
@@ -51,7 +51,6 @@ export default function AttributeTableDirective($compile, $templateCache, $rootS
         scope.types = types;
 
         scope.entityType = attrs.entityType;
-        scope.attributeScope = getAttributeScopeByValue(attrs.defaultAttributeScope);
 
         if (scope.entityType === types.entityType.device) {
             scope.attributeScopes = types.attributesScope;
@@ -60,8 +59,13 @@ export default function AttributeTableDirective($compile, $templateCache, $rootS
             scope.attributeScopes = {};
             scope.attributeScopes.server = types.attributesScope.server;
             scope.attributeScopeSelectionReadonly = true;
+        }
+
+        scope.attributeScope = getAttributeScopeByValue(attrs.defaultAttributeScope);
+
+        if (scope.entityType != types.entityType.device) {
             if (scope.attributeScope != types.latestTelemetry) {
-                scope.attributeScope = scope.attributeScopes.server.value;
+                scope.attributeScope = scope.attributeScopes.server;
             }
         }
 
@@ -81,8 +85,8 @@ export default function AttributeTableDirective($compile, $templateCache, $rootS
             search: null
         };
 
-        scope.$watch("entityId", function(newVal, prevVal) {
-            if (newVal && !angular.equals(newVal, prevVal)) {
+        scope.$watch("entityId", function(newVal) {
+            if (newVal) {
                 scope.resetFilter();
                 scope.getEntityAttributes(false, true);
             }
diff --git a/ui/src/app/locale/locale.constant.js b/ui/src/app/locale/locale.constant.js
index fcde13c..6bc6e7e 100644
--- a/ui/src/app/locale/locale.constant.js
+++ b/ui/src/app/locale/locale.constant.js
@@ -171,7 +171,7 @@ export default angular.module('thingsboard.locale', [])
                 "attribute": {
                     "attributes": "Attributes",
                     "latest-telemetry": "Latest telemetry",
-                    "attributes-scope": "Device attributes scope",
+                    "attributes-scope": "Entity attributes scope",
                     "scope-latest-telemetry": "Latest telemetry",
                     "scope-client": "Client attributes",
                     "scope-server": "Server attributes",
diff --git a/ui/src/app/plugin/plugin.controller.js b/ui/src/app/plugin/plugin.controller.js
index b250f61..c83dc38 100644
--- a/ui/src/app/plugin/plugin.controller.js
+++ b/ui/src/app/plugin/plugin.controller.js
@@ -138,6 +138,8 @@ export default function PluginController(pluginService, userService, importExpor
         vm.pluginGridConfig.topIndex = $stateParams.topIndex;
     }
 
+    vm.isPluginEditable = isPluginEditable;
+
     vm.activatePlugin = activatePlugin;
     vm.suspendPlugin = suspendPlugin;
     vm.exportPlugin = exportPlugin;
diff --git a/ui/src/app/plugin/plugins.tpl.html b/ui/src/app/plugin/plugins.tpl.html
index d04ebcb..5b03506 100644
--- a/ui/src/app/plugin/plugins.tpl.html
+++ b/ui/src/app/plugin/plugins.tpl.html
@@ -19,7 +19,7 @@
     <details-buttons tb-help="vm.helpLinkIdForPlugin()" help-container-id="help-container">
         <div id="help-container"></div>
     </details-buttons>
-    <md-tabs ng-class="{'tb-headless': vm.grid.detailsConfig.isDetailsEditMode}"
+    <md-tabs ng-class="{'tb-headless': (vm.grid.detailsConfig.isDetailsEditMode || !vm.isPluginEditable(vm.grid.operatingItem()))}"
              id="tabs" md-border-bottom flex class="tb-absolute-fill">
         <md-tab label="{{ 'plugin.details' | translate }}">
             <tb-plugin plugin="vm.grid.operatingItem()"
@@ -31,7 +31,7 @@
                  on-export-plugin="vm.exportPlugin(event, vm.grid.detailsConfig.currentItem)"
                  on-delete-plugin="vm.grid.deleteItem(event, vm.grid.detailsConfig.currentItem)"></tb-plugin>
         </md-tab>
-        <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'attribute.attributes' | translate }}">
+        <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode && vm.isPluginEditable(vm.grid.operatingItem())" label="{{ 'attribute.attributes' | translate }}">
             <tb-attribute-table flex
                                 entity-id="vm.grid.operatingItem().id.id"
                                 entity-type="{{vm.types.entityType.plugin}}"
@@ -39,7 +39,7 @@
                                 default-attribute-scope="{{vm.types.attributesScope.server.value}}">
             </tb-attribute-table>
         </md-tab>
-        <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'attribute.latest-telemetry' | translate }}">
+        <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode && vm.isPluginEditable(vm.grid.operatingItem())" label="{{ 'attribute.latest-telemetry' | translate }}">
             <tb-attribute-table flex
                                 entity-id="vm.grid.operatingItem().id.id"
                                 entity-type="{{vm.types.entityType.plugin}}"
@@ -48,7 +48,7 @@
                                 disable-attribute-scope-selection="true">
             </tb-attribute-table>
         </md-tab>
-        <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'plugin.events' | translate }}">
+        <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode && vm.isPluginEditable(vm.grid.operatingItem())" label="{{ 'plugin.events' | translate }}">
             <tb-event-table flex entity-type="vm.types.entityType.plugin"
                             entity-id="vm.grid.operatingItem().id.id"
                             tenant-id="vm.grid.operatingItem().tenantId.id"
@@ -56,7 +56,7 @@
                             disabled-event-types="{{vm.types.eventType.alarm.value}}">
             </tb-event-table>
         </md-tab>
-        <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'relation.relations' | translate }}">
+        <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode && vm.isPluginEditable(vm.grid.operatingItem())" label="{{ 'relation.relations' | translate }}">
             <tb-relation-table flex
                                entity-id="vm.grid.operatingItem().id.id"
                                entity-type="{{vm.types.entityType.plugin}}">
diff --git a/ui/src/app/rule/rule.controller.js b/ui/src/app/rule/rule.controller.js
index 3cff981..9cc90cf 100644
--- a/ui/src/app/rule/rule.controller.js
+++ b/ui/src/app/rule/rule.controller.js
@@ -134,6 +134,8 @@ export default function RuleController(ruleService, userService, importExport, $
         vm.ruleGridConfig.topIndex = $stateParams.topIndex;
     }
 
+    vm.isRuleEditable = isRuleEditable;
+
     vm.activateRule = activateRule;
     vm.suspendRule = suspendRule;
     vm.exportRule = exportRule;
diff --git a/ui/src/app/rule/rules.tpl.html b/ui/src/app/rule/rules.tpl.html
index 16097cc..098bbee 100644
--- a/ui/src/app/rule/rules.tpl.html
+++ b/ui/src/app/rule/rules.tpl.html
@@ -19,7 +19,7 @@
     <details-buttons tb-help="'rules'" help-container-id="help-container">
         <div id="help-container"></div>
     </details-buttons>
-    <md-tabs ng-class="{'tb-headless': vm.grid.detailsConfig.isDetailsEditMode}"
+    <md-tabs ng-class="{'tb-headless': (vm.grid.detailsConfig.isDetailsEditMode || !vm.isRuleEditable(vm.grid.operatingItem()))}"
              id="tabs" md-border-bottom flex class="tb-absolute-fill">
         <md-tab label="{{ 'rule.details' | translate }}">
             <tb-rule rule="vm.grid.operatingItem()"
@@ -31,7 +31,7 @@
                                on-export-rule="vm.exportRule(event, vm.grid.detailsConfig.currentItem)"
                                on-delete-rule="vm.grid.deleteItem(event, vm.grid.detailsConfig.currentItem)"></tb-rule>
         </md-tab>
-        <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'attribute.attributes' | translate }}">
+        <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode && vm.isRuleEditable(vm.grid.operatingItem())" label="{{ 'attribute.attributes' | translate }}">
             <tb-attribute-table flex
                                 entity-id="vm.grid.operatingItem().id.id"
                                 entity-type="{{vm.types.entityType.rule}}"
@@ -39,7 +39,7 @@
                                 default-attribute-scope="{{vm.types.attributesScope.server.value}}">
             </tb-attribute-table>
         </md-tab>
-        <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'attribute.latest-telemetry' | translate }}">
+        <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode && vm.isRuleEditable(vm.grid.operatingItem())" label="{{ 'attribute.latest-telemetry' | translate }}">
             <tb-attribute-table flex
                                 entity-id="vm.grid.operatingItem().id.id"
                                 entity-type="{{vm.types.entityType.rule}}"
@@ -48,7 +48,7 @@
                                 disable-attribute-scope-selection="true">
             </tb-attribute-table>
         </md-tab>
-        <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'rule.events' | translate }}">
+        <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode && vm.isRuleEditable(vm.grid.operatingItem())" label="{{ 'rule.events' | translate }}">
             <tb-event-table flex entity-type="vm.types.entityType.rule"
                             entity-id="vm.grid.operatingItem().id.id"
                             tenant-id="vm.grid.operatingItem().tenantId.id"
@@ -56,7 +56,7 @@
                             disabled-event-types="{{vm.types.eventType.alarm.value}}">
             </tb-event-table>
         </md-tab>
-        <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'relation.relations' | translate }}">
+        <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode && vm.isRuleEditable(vm.grid.operatingItem())" label="{{ 'relation.relations' | translate }}">
             <tb-relation-table flex
                                entity-id="vm.grid.operatingItem().id.id"
                                entity-type="{{vm.types.entityType.rule}}">