Showing
18 changed files
with
175 additions
and
28 deletions
@@ -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", |