Commit 7446e012a6d8158fbf3e09d49c8f2f60d2eddec8

Authored by Igor Kulikov
1 parent c6f7862c

Introduce Current user alias

@@ -246,6 +246,28 @@ public class UserController extends BaseController { @@ -246,6 +246,28 @@ public class UserController extends BaseController {
246 } 246 }
247 } 247 }
248 248
  249 + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
  250 + @RequestMapping(value = "/users", params = {"pageSize", "page"}, method = RequestMethod.GET)
  251 + @ResponseBody
  252 + public PageData<User> getUsers(
  253 + @RequestParam int pageSize,
  254 + @RequestParam int page,
  255 + @RequestParam(required = false) String textSearch,
  256 + @RequestParam(required = false) String sortProperty,
  257 + @RequestParam(required = false) String sortOrder) throws ThingsboardException {
  258 + try {
  259 + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
  260 + SecurityUser currentUser = getCurrentUser();
  261 + if (Authority.TENANT_ADMIN.equals(currentUser.getAuthority())) {
  262 + return checkNotNull(userService.findUsersByTenantId(currentUser.getTenantId(), pageLink));
  263 + } else {
  264 + return checkNotNull(userService.findCustomerUsers(currentUser.getTenantId(), currentUser.getCustomerId(), pageLink));
  265 + }
  266 + } catch (Exception e) {
  267 + throw handleException(e);
  268 + }
  269 + }
  270 +
249 @PreAuthorize("hasAuthority('SYS_ADMIN')") 271 @PreAuthorize("hasAuthority('SYS_ADMIN')")
250 @RequestMapping(value = "/tenant/{tenantId}/users", params = {"pageSize", "page"}, method = RequestMethod.GET) 272 @RequestMapping(value = "/tenant/{tenantId}/users", params = {"pageSize", "page"}, method = RequestMethod.GET)
251 @ResponseBody 273 @ResponseBody
@@ -29,6 +29,7 @@ import org.thingsboard.server.common.data.Customer; @@ -29,6 +29,7 @@ import org.thingsboard.server.common.data.Customer;
29 import org.thingsboard.server.common.data.Device; 29 import org.thingsboard.server.common.data.Device;
30 import org.thingsboard.server.common.data.EntityView; 30 import org.thingsboard.server.common.data.EntityView;
31 import org.thingsboard.server.common.data.Tenant; 31 import org.thingsboard.server.common.data.Tenant;
  32 +import org.thingsboard.server.common.data.User;
32 import org.thingsboard.server.common.data.asset.Asset; 33 import org.thingsboard.server.common.data.asset.Asset;
33 import org.thingsboard.server.common.data.exception.ThingsboardException; 34 import org.thingsboard.server.common.data.exception.ThingsboardException;
34 import org.thingsboard.server.common.data.id.AssetId; 35 import org.thingsboard.server.common.data.id.AssetId;
@@ -40,6 +41,7 @@ import org.thingsboard.server.common.data.id.EntityViewId; @@ -40,6 +41,7 @@ import org.thingsboard.server.common.data.id.EntityViewId;
40 import org.thingsboard.server.common.data.id.RuleChainId; 41 import org.thingsboard.server.common.data.id.RuleChainId;
41 import org.thingsboard.server.common.data.id.RuleNodeId; 42 import org.thingsboard.server.common.data.id.RuleNodeId;
42 import org.thingsboard.server.common.data.id.TenantId; 43 import org.thingsboard.server.common.data.id.TenantId;
  44 +import org.thingsboard.server.common.data.id.UserId;
43 import org.thingsboard.server.common.data.rule.RuleChain; 45 import org.thingsboard.server.common.data.rule.RuleChain;
44 import org.thingsboard.server.common.data.rule.RuleNode; 46 import org.thingsboard.server.common.data.rule.RuleNode;
45 import org.thingsboard.server.controller.HttpValidationCallback; 47 import org.thingsboard.server.controller.HttpValidationCallback;
@@ -172,6 +174,9 @@ public class AccessValidator { @@ -172,6 +174,9 @@ public class AccessValidator {
172 case TENANT: 174 case TENANT:
173 validateTenant(currentUser, operation, entityId, callback); 175 validateTenant(currentUser, operation, entityId, callback);
174 return; 176 return;
  177 + case USER:
  178 + validateUser(currentUser, operation, entityId, callback);
  179 + return;
175 case ENTITY_VIEW: 180 case ENTITY_VIEW:
176 validateEntityView(currentUser, operation, entityId, callback); 181 validateEntityView(currentUser, operation, entityId, callback);
177 return; 182 return;
@@ -308,6 +313,22 @@ public class AccessValidator { @@ -308,6 +313,22 @@ public class AccessValidator {
308 } 313 }
309 } 314 }
310 315
  316 + private void validateUser(final SecurityUser currentUser, Operation operation, EntityId entityId, FutureCallback<ValidationResult> callback) {
  317 + ListenableFuture<User> userFuture = userService.findUserByIdAsync(currentUser.getTenantId(), new UserId(entityId.getId()));
  318 + Futures.addCallback(userFuture, getCallback(callback, user -> {
  319 + if (user == null) {
  320 + return ValidationResult.entityNotFound("User with requested id wasn't found!");
  321 + }
  322 + try {
  323 + accessControlService.checkPermission(currentUser, Resource.USER, operation, entityId, user);
  324 + } catch (ThingsboardException e) {
  325 + return ValidationResult.accessDenied(e.getMessage());
  326 + }
  327 + return ValidationResult.ok(user);
  328 +
  329 + }), executor);
  330 + }
  331 +
311 private void validateEntityView(final SecurityUser currentUser, Operation operation, EntityId entityId, FutureCallback<ValidationResult> callback) { 332 private void validateEntityView(final SecurityUser currentUser, Operation operation, EntityId entityId, FutureCallback<ValidationResult> callback) {
312 if (currentUser.isSystemAdmin()) { 333 if (currentUser.isSystemAdmin()) {
313 callback.onSuccess(ValidationResult.accessDenied(SYSTEM_ADMINISTRATOR_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION)); 334 callback.onSuccess(ValidationResult.accessDenied(SYSTEM_ADMINISTRATOR_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION));
@@ -52,8 +52,10 @@ public interface UserService { @@ -52,8 +52,10 @@ public interface UserService {
52 UserCredentials replaceUserCredentials(TenantId tenantId, UserCredentials userCredentials); 52 UserCredentials replaceUserCredentials(TenantId tenantId, UserCredentials userCredentials);
53 53
54 void deleteUser(TenantId tenantId, UserId userId); 54 void deleteUser(TenantId tenantId, UserId userId);
55 -  
56 - PageData<User> findTenantAdmins(TenantId tenantId, PageLink pageLink); 55 +
  56 + PageData<User> findUsersByTenantId(TenantId tenantId, PageLink pageLink);
  57 +
  58 + PageData<User> findTenantAdmins(TenantId tenantId, PageLink pageLink);
57 59
58 void deleteTenantAdmins(TenantId tenantId); 60 void deleteTenantAdmins(TenantId tenantId);
59 61
@@ -86,7 +86,7 @@ public class EntityKeyMapping { @@ -86,7 +86,7 @@ public class EntityKeyMapping {
86 allowedEntityFieldMap.get(EntityType.TENANT).add(REGION); 86 allowedEntityFieldMap.get(EntityType.TENANT).add(REGION);
87 allowedEntityFieldMap.put(EntityType.CUSTOMER, new HashSet<>(contactBasedEntityFields)); 87 allowedEntityFieldMap.put(EntityType.CUSTOMER, new HashSet<>(contactBasedEntityFields));
88 88
89 - allowedEntityFieldMap.put(EntityType.USER, new HashSet<>(Arrays.asList(FIRST_NAME, LAST_NAME, EMAIL))); 89 + allowedEntityFieldMap.put(EntityType.USER, new HashSet<>(Arrays.asList(CREATED_TIME, FIRST_NAME, LAST_NAME, EMAIL)));
90 90
91 allowedEntityFieldMap.put(EntityType.DASHBOARD, new HashSet<>(commonEntityFields)); 91 allowedEntityFieldMap.put(EntityType.DASHBOARD, new HashSet<>(commonEntityFields));
92 allowedEntityFieldMap.put(EntityType.RULE_CHAIN, new HashSet<>(commonEntityFields)); 92 allowedEntityFieldMap.put(EntityType.RULE_CHAIN, new HashSet<>(commonEntityFields));
@@ -377,27 +377,29 @@ public class EntityKeyMapping { @@ -377,27 +377,29 @@ public class EntityKeyMapping {
377 } 377 }
378 378
379 private String buildSimplePredicateQuery(EntityQueryContext ctx, String alias, EntityKey key, KeyFilterPredicate predicate) { 379 private String buildSimplePredicateQuery(EntityQueryContext ctx, String alias, EntityKey key, KeyFilterPredicate predicate) {
380 - if (predicate.getType().equals(FilterPredicateType.NUMERIC)) {  
381 - if (key.getType().equals(EntityKeyType.ENTITY_FIELD)) {  
382 - String column = entityFieldColumnMap.get(key.getKey());  
383 - return this.buildNumericPredicateQuery(ctx, alias + "." + column, (NumericFilterPredicate) predicate); 380 + if (key.getType().equals(EntityKeyType.ENTITY_FIELD)) {
  381 + String column = entityFieldColumnMap.get(key.getKey());
  382 + String field = alias + "." + column;
  383 + if (predicate.getType().equals(FilterPredicateType.NUMERIC)) {
  384 + return this.buildNumericPredicateQuery(ctx, field, (NumericFilterPredicate) predicate);
  385 + } else if (predicate.getType().equals(FilterPredicateType.STRING)) {
  386 + return this.buildStringPredicateQuery(ctx, field, (StringFilterPredicate) predicate);
384 } else { 387 } else {
  388 + return this.buildBooleanPredicateQuery(ctx, field, (BooleanFilterPredicate) predicate);
  389 + }
  390 + } else {
  391 + if (predicate.getType().equals(FilterPredicateType.NUMERIC)) {
385 String longQuery = this.buildNumericPredicateQuery(ctx, alias + ".long_v", (NumericFilterPredicate) predicate); 392 String longQuery = this.buildNumericPredicateQuery(ctx, alias + ".long_v", (NumericFilterPredicate) predicate);
386 String doubleQuery = this.buildNumericPredicateQuery(ctx, alias + ".dbl_v", (NumericFilterPredicate) predicate); 393 String doubleQuery = this.buildNumericPredicateQuery(ctx, alias + ".dbl_v", (NumericFilterPredicate) predicate);
387 return String.format("(%s or %s)", longQuery, doubleQuery); 394 return String.format("(%s or %s)", longQuery, doubleQuery);
388 - }  
389 - } else {  
390 - String column;  
391 - if (key.getType().equals(EntityKeyType.ENTITY_FIELD)) {  
392 - column = entityFieldColumnMap.get(key.getKey());  
393 } else { 395 } else {
394 - column = predicate.getType().equals(FilterPredicateType.STRING) ? "str_v" : "bool_v";  
395 - }  
396 - String field = alias + "." + column;  
397 - if (predicate.getType().equals(FilterPredicateType.STRING)) {  
398 - return this.buildStringPredicateQuery(ctx, field, (StringFilterPredicate) predicate);  
399 - } else {  
400 - return this.buildBooleanPredicateQuery(ctx, field, (BooleanFilterPredicate) predicate); 396 + String column = predicate.getType().equals(FilterPredicateType.STRING) ? "str_v" : "bool_v";
  397 + String field = alias + "." + column;
  398 + if (predicate.getType().equals(FilterPredicateType.STRING)) {
  399 + return this.buildStringPredicateQuery(ctx, field, (StringFilterPredicate) predicate);
  400 + } else {
  401 + return this.buildBooleanPredicateQuery(ctx, field, (BooleanFilterPredicate) predicate);
  402 + }
401 } 403 }
402 } 404 }
403 } 405 }
@@ -60,6 +60,16 @@ public class JpaUserDao extends JpaAbstractSearchTextDao<UserEntity, User> imple @@ -60,6 +60,16 @@ public class JpaUserDao extends JpaAbstractSearchTextDao<UserEntity, User> imple
60 } 60 }
61 61
62 @Override 62 @Override
  63 + public PageData<User> findByTenantId(UUID tenantId, PageLink pageLink) {
  64 + return DaoUtil.toPageData(
  65 + userRepository
  66 + .findByTenantId(
  67 + tenantId,
  68 + Objects.toString(pageLink.getTextSearch(), ""),
  69 + DaoUtil.toPageable(pageLink)));
  70 + }
  71 +
  72 + @Override
63 public PageData<User> findTenantAdmins(UUID tenantId, PageLink pageLink) { 73 public PageData<User> findTenantAdmins(UUID tenantId, PageLink pageLink) {
64 return DaoUtil.toPageData( 74 return DaoUtil.toPageData(
65 userRepository 75 userRepository
@@ -43,4 +43,10 @@ public interface UserRepository extends PagingAndSortingRepository<UserEntity, U @@ -43,4 +43,10 @@ public interface UserRepository extends PagingAndSortingRepository<UserEntity, U
43 @Param("authority") Authority authority, 43 @Param("authority") Authority authority,
44 Pageable pageable); 44 Pageable pageable);
45 45
  46 + @Query("SELECT u FROM UserEntity u WHERE u.tenantId = :tenantId " +
  47 + "AND LOWER(u.searchText) LIKE LOWER(CONCAT(:searchText, '%'))")
  48 + Page<UserEntity> findByTenantId(@Param("tenantId") UUID tenantId,
  49 + @Param("searchText") String searchText,
  50 + Pageable pageable);
  51 +
46 } 52 }
@@ -40,6 +40,15 @@ public interface UserDao extends Dao<User> { @@ -40,6 +40,15 @@ public interface UserDao extends Dao<User> {
40 * @return the user entity 40 * @return the user entity
41 */ 41 */
42 User findByEmail(TenantId tenantId, String email); 42 User findByEmail(TenantId tenantId, String email);
  43 +
  44 + /**
  45 + * Find users by tenantId and page link.
  46 + *
  47 + * @param tenantId the tenantId
  48 + * @param pageLink the page link
  49 + * @return the list of user entities
  50 + */
  51 + PageData<User> findByTenantId(UUID tenantId, PageLink pageLink);
43 52
44 /** 53 /**
45 * Find tenant admin users by tenantId and page link. 54 * Find tenant admin users by tenantId and page link.
@@ -220,6 +220,14 @@ public class UserServiceImpl extends AbstractEntityService implements UserServic @@ -220,6 +220,14 @@ public class UserServiceImpl extends AbstractEntityService implements UserServic
220 } 220 }
221 221
222 @Override 222 @Override
  223 + public PageData<User> findUsersByTenantId(TenantId tenantId, PageLink pageLink) {
  224 + log.trace("Executing findUsersByTenantId, tenantId [{}], pageLink [{}]", tenantId, pageLink);
  225 + validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
  226 + validatePageLink(pageLink);
  227 + return userDao.findByTenantId(tenantId.getId(), pageLink);
  228 + }
  229 +
  230 + @Override
223 public PageData<User> findTenantAdmins(TenantId tenantId, PageLink pageLink) { 231 public PageData<User> findTenantAdmins(TenantId tenantId, PageLink pageLink) {
224 log.trace("Executing findTenantAdmins, tenantId [{}], pageLink [{}]", tenantId, pageLink); 232 log.trace("Executing findTenantAdmins, tenantId [{}], pageLink [{}]", tenantId, pageLink);
225 validateId(tenantId, INCORRECT_TENANT_ID + tenantId); 233 validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
@@ -310,7 +310,8 @@ export class EntityService { @@ -310,7 +310,8 @@ export class EntityService {
310 } 310 }
311 break; 311 break;
312 case EntityType.USER: 312 case EntityType.USER:
313 - console.error('Get User Entities is not implemented!'); 313 + pageLink.sortOrder.property = 'email';
  314 + entitiesObservable = this.userService.getUsers(pageLink);
314 break; 315 break;
315 case EntityType.ALARM: 316 case EntityType.ALARM:
316 console.error('Get Alarm Entities is not implemented!'); 317 console.error('Get Alarm Entities is not implemented!');
@@ -548,6 +549,7 @@ export class EntityService { @@ -548,6 +549,7 @@ export class EntityService {
548 entityTypes.push(EntityType.ENTITY_VIEW); 549 entityTypes.push(EntityType.ENTITY_VIEW);
549 entityTypes.push(EntityType.TENANT); 550 entityTypes.push(EntityType.TENANT);
550 entityTypes.push(EntityType.CUSTOMER); 551 entityTypes.push(EntityType.CUSTOMER);
  552 + entityTypes.push(EntityType.USER);
551 entityTypes.push(EntityType.DASHBOARD); 553 entityTypes.push(EntityType.DASHBOARD);
552 if (useAliasEntityTypes) { 554 if (useAliasEntityTypes) {
553 entityTypes.push(AliasEntityType.CURRENT_CUSTOMER); 555 entityTypes.push(AliasEntityType.CURRENT_CUSTOMER);
@@ -559,12 +561,16 @@ export class EntityService { @@ -559,12 +561,16 @@ export class EntityService {
559 entityTypes.push(EntityType.ASSET); 561 entityTypes.push(EntityType.ASSET);
560 entityTypes.push(EntityType.ENTITY_VIEW); 562 entityTypes.push(EntityType.ENTITY_VIEW);
561 entityTypes.push(EntityType.CUSTOMER); 563 entityTypes.push(EntityType.CUSTOMER);
  564 + entityTypes.push(EntityType.USER);
562 entityTypes.push(EntityType.DASHBOARD); 565 entityTypes.push(EntityType.DASHBOARD);
563 if (useAliasEntityTypes) { 566 if (useAliasEntityTypes) {
564 entityTypes.push(AliasEntityType.CURRENT_CUSTOMER); 567 entityTypes.push(AliasEntityType.CURRENT_CUSTOMER);
565 } 568 }
566 break; 569 break;
567 } 570 }
  571 + if (useAliasEntityTypes) {
  572 + entityTypes.push(AliasEntityType.CURRENT_USER);
  573 + }
568 if (allowedEntityTypes && allowedEntityTypes.length) { 574 if (allowedEntityTypes && allowedEntityTypes.length) {
569 for (let index = entityTypes.length - 1; index >= 0; index--) { 575 for (let index = entityTypes.length - 1; index >= 0; index--) {
570 if (allowedEntityTypes.indexOf(entityTypes[index]) === -1) { 576 if (allowedEntityTypes.indexOf(entityTypes[index]) === -1) {
@@ -961,6 +967,10 @@ export class EntityService { @@ -961,6 +967,10 @@ export class EntityService {
961 const authUser = getCurrentAuthUser(this.store); 967 const authUser = getCurrentAuthUser(this.store);
962 entityId.entityType = EntityType.TENANT; 968 entityId.entityType = EntityType.TENANT;
963 entityId.id = authUser.tenantId; 969 entityId.id = authUser.tenantId;
  970 + } else if (entityType === AliasEntityType.CURRENT_USER){
  971 + const authUser = getCurrentAuthUser(this.store);
  972 + entityId.entityType = EntityType.USER;
  973 + entityId.id = authUser.userId;
964 } 974 }
965 return entityId; 975 return entityId;
966 } 976 }
@@ -32,6 +32,12 @@ export class UserService { @@ -32,6 +32,12 @@ export class UserService {
32 private http: HttpClient 32 private http: HttpClient
33 ) { } 33 ) { }
34 34
  35 + public getUsers(pageLink: PageLink,
  36 + config?: RequestConfig): Observable<PageData<User>> {
  37 + return this.http.get<PageData<User>>(`/api/users${pageLink.toQuery()}`,
  38 + defaultHttpOptionsFromConfig(config));
  39 + }
  40 +
35 public getTenantAdmins(tenantId: string, pageLink: PageLink, 41 public getTenantAdmins(tenantId: string, pageLink: PageLink,
36 config?: RequestConfig): Observable<PageData<User>> { 42 config?: RequestConfig): Observable<PageData<User>> {
37 return this.http.get<PageData<User>>(`/api/tenant/${tenantId}/users${pageLink.toQuery()}`, 43 return this.http.get<PageData<User>>(`/api/tenant/${tenantId}/users${pageLink.toQuery()}`,
@@ -28,6 +28,7 @@ import { @@ -28,6 +28,7 @@ import {
28 AliasesEntitySelectPanelData 28 AliasesEntitySelectPanelData
29 } from './aliases-entity-select-panel.component'; 29 } from './aliases-entity-select-panel.component';
30 import { deepClone } from '@core/utils'; 30 import { deepClone } from '@core/utils';
  31 +import { AliasFilterType } from '@shared/models/alias.models';
31 32
32 @Component({ 33 @Component({
33 selector: 'tb-aliases-entity-select', 34 selector: 'tb-aliases-entity-select',
@@ -178,7 +179,7 @@ export class AliasesEntitySelectComponent implements OnInit, OnDestroy { @@ -178,7 +179,7 @@ export class AliasesEntitySelectComponent implements OnInit, OnDestroy {
178 for (const aliasId of Object.keys(allEntityAliases)) { 179 for (const aliasId of Object.keys(allEntityAliases)) {
179 const aliasInfo = this.aliasController.getInstantAliasInfo(aliasId); 180 const aliasInfo = this.aliasController.getInstantAliasInfo(aliasId);
180 if (aliasInfo && !aliasInfo.resolveMultiple && aliasInfo.currentEntity 181 if (aliasInfo && !aliasInfo.resolveMultiple && aliasInfo.currentEntity
181 - && aliasInfo.entityFilter) { 182 + && aliasInfo.entityFilter && aliasInfo.entityFilter.type !== AliasFilterType.singleEntity) {
182 this.entityAliasesInfo[aliasId] = deepClone(aliasInfo); 183 this.entityAliasesInfo[aliasId] = deepClone(aliasInfo);
183 this.hasSelectableAliasEntities = true; 184 this.hasSelectableAliasEntities = true;
184 } 185 }
@@ -84,6 +84,9 @@ import { @@ -84,6 +84,9 @@ import {
84 KeyFilter 84 KeyFilter
85 } from '@shared/models/query/query.models'; 85 } from '@shared/models/query/query.models';
86 import { sortItems } from '@shared/models/page/page-link'; 86 import { sortItems } from '@shared/models/page/page-link';
  87 +import { entityFields } from '@shared/models/entity.models';
  88 +import { alarmFields } from '@shared/models/alarm.models';
  89 +import { DatePipe } from '@angular/common';
87 90
88 interface EntitiesTableWidgetSettings extends TableWidgetSettings { 91 interface EntitiesTableWidgetSettings extends TableWidgetSettings {
89 entitiesTitle: string; 92 entitiesTitle: string;
@@ -153,6 +156,7 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni @@ -153,6 +156,7 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni
153 private overlay: Overlay, 156 private overlay: Overlay,
154 private viewContainerRef: ViewContainerRef, 157 private viewContainerRef: ViewContainerRef,
155 private utils: UtilsService, 158 private utils: UtilsService,
  159 + private datePipe: DatePipe,
156 private translate: TranslateService, 160 private translate: TranslateService,
157 private domSanitizer: DomSanitizer) { 161 private domSanitizer: DomSanitizer) {
158 super(store); 162 super(store);
@@ -511,9 +515,7 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni @@ -511,9 +515,7 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni
511 content = '' + value; 515 content = '' + value;
512 } 516 }
513 } else { 517 } else {
514 - const decimals = (contentInfo.decimals || contentInfo.decimals === 0) ? contentInfo.decimals : this.ctx.widgetConfig.decimals;  
515 - const units = contentInfo.units || this.ctx.widgetConfig.units;  
516 - content = this.ctx.utils.formatValue(value, decimals, units, true); 518 + content = this.defaultContent(key, contentInfo, value);
517 } 519 }
518 return isDefined(content) ? this.domSanitizer.bypassSecurityTrustHtml(content) : ''; 520 return isDefined(content) ? this.domSanitizer.bypassSecurityTrustHtml(content) : '';
519 } else { 521 } else {
@@ -521,6 +523,22 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni @@ -521,6 +523,22 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni
521 } 523 }
522 } 524 }
523 525
  526 + private defaultContent(key: EntityColumn, contentInfo: CellContentInfo, value: any): any {
  527 + if (isDefined(value)) {
  528 + const entityField = entityFields[key.name];
  529 + if (entityField) {
  530 + if (entityField.time) {
  531 + return this.datePipe.transform(value, 'yyyy-MM-dd HH:mm:ss');
  532 + }
  533 + }
  534 + const decimals = (contentInfo.decimals || contentInfo.decimals === 0) ? contentInfo.decimals : this.ctx.widgetConfig.decimals;
  535 + const units = contentInfo.units || this.ctx.widgetConfig.units;
  536 + return this.ctx.utils.formatValue(value, decimals, units, true);
  537 + } else {
  538 + return '';
  539 + }
  540 + }
  541 +
524 public onRowClick($event: Event, entity: EntityData, isDouble?: boolean) { 542 public onRowClick($event: Event, entity: EntityData, isDouble?: boolean) {
525 if ($event) { 543 if ($event) {
526 $event.stopPropagation(); 544 $event.stopPropagation();
@@ -15,6 +15,23 @@ @@ -15,6 +15,23 @@
15 limitations under the License. 15 limitations under the License.
16 16
17 --> 17 -->
  18 +<mat-tab *ngIf="entity"
  19 + label="{{ 'attribute.attributes' | translate }}" #attributesTab="matTab">
  20 + <tb-attribute-table [defaultAttributeScope]="attributeScopes.SERVER_SCOPE"
  21 + [active]="attributesTab.isActive"
  22 + [entityId]="entity.id"
  23 + [entityName]="entity.name">
  24 + </tb-attribute-table>
  25 +</mat-tab>
  26 +<mat-tab *ngIf="entity"
  27 + label="{{ 'attribute.latest-telemetry' | translate }}" #telemetryTab="matTab">
  28 + <tb-attribute-table [defaultAttributeScope]="latestTelemetryTypes.LATEST_TELEMETRY"
  29 + disableAttributeScopeSelection
  30 + [active]="telemetryTab.isActive"
  31 + [entityId]="entity.id"
  32 + [entityName]="entity.name">
  33 + </tb-attribute-table>
  34 +</mat-tab>
18 <mat-tab *ngIf="entity && authUser.authority === authorities.TENANT_ADMIN" 35 <mat-tab *ngIf="entity && authUser.authority === authorities.TENANT_ADMIN"
19 label="{{ 'audit-log.audit-logs' | translate }}" #auditLogsTab="matTab"> 36 label="{{ 'audit-log.audit-logs' | translate }}" #auditLogsTab="matTab">
20 <tb-audit-log-table [active]="auditLogsTab.isActive" [auditLogMode]="auditLogModes.USER" [userId]="entity.id" detailsMode="true"></tb-audit-log-table> 37 <tb-audit-log-table [active]="auditLogsTab.isActive" [auditLogMode]="auditLogModes.USER" [userId]="entity.id" detailsMode="true"></tb-audit-log-table>
@@ -175,6 +175,7 @@ export class EntityAutocompleteComponent implements ControlValueAccessor, OnInit @@ -175,6 +175,7 @@ export class EntityAutocompleteComponent implements ControlValueAccessor, OnInit
175 this.entityRequiredText = 'customer.customer-required'; 175 this.entityRequiredText = 'customer.customer-required';
176 break; 176 break;
177 case EntityType.USER: 177 case EntityType.USER:
  178 + case AliasEntityType.CURRENT_USER:
178 this.entityText = 'user.user'; 179 this.entityText = 'user.user';
179 this.noEntitiesMatchingText = 'user.no-users-matching'; 180 this.noEntitiesMatchingText = 'user.no-users-matching';
180 this.entityRequiredText = 'user.user-required'; 181 this.entityRequiredText = 'user.user-required';
@@ -324,6 +325,8 @@ export class EntityAutocompleteComponent implements ControlValueAccessor, OnInit @@ -324,6 +325,8 @@ export class EntityAutocompleteComponent implements ControlValueAccessor, OnInit
324 return EntityType.CUSTOMER; 325 return EntityType.CUSTOMER;
325 } else if (entityType === AliasEntityType.CURRENT_TENANT) { 326 } else if (entityType === AliasEntityType.CURRENT_TENANT) {
326 return EntityType.TENANT; 327 return EntityType.TENANT;
  328 + } else if (entityType === AliasEntityType.CURRENT_USER) {
  329 + return EntityType.USER;
327 } 330 }
328 return entityType; 331 return entityType;
329 } 332 }
@@ -27,7 +27,8 @@ @@ -27,7 +27,8 @@
27 </tb-entity-type-select> 27 </tb-entity-type-select>
28 <tb-entity-autocomplete 28 <tb-entity-autocomplete
29 fxFlex 29 fxFlex
30 - *ngIf="modelValue.entityType && modelValue.entityType !== AliasEntityType.CURRENT_TENANT" 30 + *ngIf="modelValue.entityType && modelValue.entityType !== AliasEntityType.CURRENT_TENANT
  31 + && modelValue.entityType !== AliasEntityType.CURRENT_USER"
31 [required]="required" 32 [required]="required"
32 [entityType]="modelValue.entityType" 33 [entityType]="modelValue.entityType"
33 formControlName="entityId"> 34 formControlName="entityId">
@@ -97,7 +97,7 @@ export class EntitySelectComponent implements ControlValueAccessor, OnInit, Afte @@ -97,7 +97,7 @@ export class EntitySelectComponent implements ControlValueAccessor, OnInit, Afte
97 ngOnInit() { 97 ngOnInit() {
98 this.entitySelectFormGroup.get('entityType').valueChanges.subscribe( 98 this.entitySelectFormGroup.get('entityType').valueChanges.subscribe(
99 (value) => { 99 (value) => {
100 - if(value === AliasEntityType.CURRENT_TENANT){ 100 + if(value === AliasEntityType.CURRENT_TENANT || value === AliasEntityType.CURRENT_USER) {
101 this.modelValue.id = NULL_UUID; 101 this.modelValue.id = NULL_UUID;
102 } 102 }
103 this.updateView(value, this.modelValue.id); 103 this.updateView(value, this.modelValue.id);
@@ -145,7 +145,9 @@ export class EntitySelectComponent implements ControlValueAccessor, OnInit, Afte @@ -145,7 +145,9 @@ export class EntitySelectComponent implements ControlValueAccessor, OnInit, Afte
145 entityType, 145 entityType,
146 id: this.modelValue.entityType !== entityType ? null : entityId 146 id: this.modelValue.entityType !== entityType ? null : entityId
147 }; 147 };
148 - if (this.modelValue.entityType && (this.modelValue.id || this.modelValue.entityType === AliasEntityType.CURRENT_TENANT)) { 148 + if (this.modelValue.entityType && (this.modelValue.id ||
  149 + this.modelValue.entityType === AliasEntityType.CURRENT_TENANT ||
  150 + this.modelValue.entityType === AliasEntityType.CURRENT_USER)) {
149 this.propagateChange(this.modelValue); 151 this.propagateChange(this.modelValue);
150 } else { 152 } else {
151 this.propagateChange(null); 153 this.propagateChange(null);
@@ -50,7 +50,8 @@ export enum EntityType { @@ -50,7 +50,8 @@ export enum EntityType {
50 50
51 export enum AliasEntityType { 51 export enum AliasEntityType {
52 CURRENT_CUSTOMER = 'CURRENT_CUSTOMER', 52 CURRENT_CUSTOMER = 'CURRENT_CUSTOMER',
53 - CURRENT_TENANT = 'CURRENT_TENANT' 53 + CURRENT_TENANT = 'CURRENT_TENANT',
  54 + CURRENT_USER = 'CURRENT_USER'
54 } 55 }
55 56
56 export interface EntityTypeTranslation { 57 export interface EntityTypeTranslation {
@@ -229,6 +230,13 @@ export const entityTypeTranslations = new Map<EntityType | AliasEntityType, Enti @@ -229,6 +230,13 @@ export const entityTypeTranslations = new Map<EntityType | AliasEntityType, Enti
229 type: 'entity.type-current-tenant', 230 type: 'entity.type-current-tenant',
230 list: 'entity.type-current-tenant' 231 list: 'entity.type-current-tenant'
231 } 232 }
  233 + ],
  234 + [
  235 + AliasEntityType.CURRENT_USER,
  236 + {
  237 + type: 'entity.type-current-user',
  238 + list: 'entity.type-current-user'
  239 + }
232 ] 240 ]
233 ] 241 ]
234 ); 242 );
@@ -841,6 +841,7 @@ @@ -841,6 +841,7 @@
841 "rulenode-name-starts-with": "Rule nodes whose names start with '{{prefix}}'", 841 "rulenode-name-starts-with": "Rule nodes whose names start with '{{prefix}}'",
842 "type-current-customer": "Current Customer", 842 "type-current-customer": "Current Customer",
843 "type-current-tenant": "Current Tenant", 843 "type-current-tenant": "Current Tenant",
  844 + "type-current-user": "Current User",
844 "search": "Search entities", 845 "search": "Search entities",
845 "selected-entities": "{ count, plural, 1 {1 entity} other {# entities} } selected", 846 "selected-entities": "{ count, plural, 1 {1 entity} other {# entities} } selected",
846 "entity-name": "Entity name", 847 "entity-name": "Entity name",