Browse Source

Merge branch 'master' of github.com:thingsboard/thingsboard

pull/3592/head
Andrii Shvaika 6 years ago
parent
commit
7a34e2a3f5
  1. 1
      application/src/main/data/json/system/oauth2_config_templates/github_config.json
  2. 18
      application/src/main/java/org/thingsboard/server/controller/OAuth2Controller.java
  3. 6
      common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientsDomainParams.java
  4. 6
      common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientsParams.java
  5. 12
      dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Utils.java
  6. 93
      dao/src/test/java/org/thingsboard/server/dao/service/BaseOAuth2ServiceTest.java
  7. 4
      ui-ngx/src/app/core/http/oauth2.service.ts
  8. 5
      ui-ngx/src/app/core/services/menu.service.ts
  9. 17
      ui-ngx/src/app/modules/home/components/alarm/alarm-details-dialog.component.html
  10. 10
      ui-ngx/src/app/modules/home/components/alias/entity-alias-dialog.component.html
  11. 10
      ui-ngx/src/app/modules/home/components/alias/entity-aliases-dialog.component.html
  12. 10
      ui-ngx/src/app/modules/home/components/attribute/add-attribute-dialog.component.html
  13. 15
      ui-ngx/src/app/modules/home/components/attribute/add-widget-to-dashboard-dialog.component.html
  14. 1
      ui-ngx/src/app/modules/home/components/attribute/edit-attribute-value-panel.component.html
  15. 10
      ui-ngx/src/app/modules/home/components/dashboard/select-target-state-dialog.component.html
  16. 1
      ui-ngx/src/app/modules/home/components/event/event-content-dialog.component.html
  17. 12
      ui-ngx/src/app/modules/home/components/filter/complex-filter-predicate-dialog.component.html
  18. 10
      ui-ngx/src/app/modules/home/components/filter/filter-dialog.component.html
  19. 12
      ui-ngx/src/app/modules/home/components/filter/filter-user-info-dialog.component.html
  20. 10
      ui-ngx/src/app/modules/home/components/filter/filters-dialog.component.html
  21. 12
      ui-ngx/src/app/modules/home/components/filter/key-filter-dialog.component.html
  22. 10
      ui-ngx/src/app/modules/home/components/filter/user-filter-dialog.component.html
  23. 10
      ui-ngx/src/app/modules/home/components/import-export/import-dialog.component.html
  24. 12
      ui-ngx/src/app/modules/home/components/profile/alarm/alarm-rule-condition-dialog.component.html
  25. 12
      ui-ngx/src/app/modules/home/components/profile/alarm/alarm-schedule-dialog.component.html
  26. 4
      ui-ngx/src/app/modules/home/components/profile/alarm/create-alarm-rules.component.html
  27. 21
      ui-ngx/src/app/modules/home/components/profile/alarm/create-alarm-rules.component.ts
  28. 4
      ui-ngx/src/app/modules/home/components/profile/alarm/device-profile-alarm.component.ts
  29. 9
      ui-ngx/src/app/modules/home/components/profile/alarm/device-profile-alarms.component.ts
  30. 12
      ui-ngx/src/app/modules/home/components/profile/device-profile-dialog.component.html
  31. 2
      ui-ngx/src/app/modules/home/components/profile/device-profile.component.html
  32. 12
      ui-ngx/src/app/modules/home/components/profile/tenant-profile-dialog.component.html
  33. 11
      ui-ngx/src/app/modules/home/components/relation/relation-dialog.component.html
  34. 10
      ui-ngx/src/app/modules/home/components/widget/action/widget-action-dialog.component.html
  35. 10
      ui-ngx/src/app/modules/home/components/widget/data-key-config-dialog.component.html
  36. 11
      ui-ngx/src/app/modules/home/components/widget/lib/alarm-filter-panel.component.html
  37. 1
      ui-ngx/src/app/modules/home/components/widget/lib/date-range-navigator/date-range-navigator-panel.component.html
  38. 5
      ui-ngx/src/app/modules/home/components/widget/lib/entities-table-widget.component.ts
  39. 12
      ui-ngx/src/app/modules/home/dialogs/add-entities-to-customer-dialog.component.html
  40. 12
      ui-ngx/src/app/modules/home/dialogs/assign-to-customer-dialog.component.html
  41. 31
      ui-ngx/src/app/modules/home/pages/admin/admin-routing.module.ts
  42. 31
      ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.ts
  43. 10
      ui-ngx/src/app/modules/home/pages/dashboard/add-widget-dialog.component.html
  44. 12
      ui-ngx/src/app/modules/home/pages/dashboard/dashboard-settings-dialog.component.html
  45. 10
      ui-ngx/src/app/modules/home/pages/dashboard/layout/manage-dashboard-layouts-dialog.component.html
  46. 1
      ui-ngx/src/app/modules/home/pages/dashboard/make-dashboard-public-dialog.component.html
  47. 13
      ui-ngx/src/app/modules/home/pages/dashboard/manage-dashboard-customers-dialog.component.html
  48. 10
      ui-ngx/src/app/modules/home/pages/dashboard/states/dashboard-state-dialog.component.html
  49. 10
      ui-ngx/src/app/modules/home/pages/dashboard/states/manage-dashboard-states-dialog.component.html
  50. 2
      ui-ngx/src/app/modules/home/pages/device-profile/device-profile-tabs.component.html
  51. 12
      ui-ngx/src/app/modules/home/pages/device/device-credentials-dialog.component.html
  52. 10
      ui-ngx/src/app/modules/home/pages/profile/change-password-dialog.component.html
  53. 10
      ui-ngx/src/app/modules/home/pages/rulechain/add-rule-node-dialog.component.html
  54. 10
      ui-ngx/src/app/modules/home/pages/rulechain/add-rule-node-link-dialog.component.html
  55. 10
      ui-ngx/src/app/modules/home/pages/user/add-user-dialog.component.html
  56. 12
      ui-ngx/src/app/modules/home/pages/widget/save-widget-type-as-dialog.component.html
  57. 1
      ui-ngx/src/app/shared/components/dialog/color-picker-dialog.component.html
  58. 11
      ui-ngx/src/app/shared/components/dialog/json-object-edit-dialog.component.html
  59. 11
      ui-ngx/src/app/shared/components/dialog/node-script-test-dialog.component.html
  60. 13
      ui-ngx/src/app/shared/components/time/timewindow-panel.component.html
  61. 44
      ui-ngx/src/app/shared/models/device.models.ts
  62. 1
      ui-ngx/src/assets/locale/locale.constant-en_US.json
  63. 3
      ui-ngx/src/styles.scss

1
application/src/main/data/json/system/oauth2_config_templates/github_config.json

@ -10,6 +10,7 @@
"mapperConfig": { "mapperConfig": {
"type": "GITHUB", "type": "GITHUB",
"basic": { "basic": {
"firstNameAttributeKey": "name",
"tenantNameStrategy": "DOMAIN" "tenantNameStrategy": "DOMAIN"
} }
}, },

18
application/src/main/java/org/thingsboard/server/controller/OAuth2Controller.java

@ -16,6 +16,7 @@
package org.thingsboard.server.controller; package org.thingsboard.server.controller;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
@ -23,6 +24,7 @@ import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.oauth2.OAuth2ClientInfo; import org.thingsboard.server.common.data.oauth2.OAuth2ClientInfo;
import org.thingsboard.server.common.data.oauth2.OAuth2ClientsParams; import org.thingsboard.server.common.data.oauth2.OAuth2ClientsParams;
import org.thingsboard.server.common.data.oauth2.SchemeType; import org.thingsboard.server.common.data.oauth2.SchemeType;
import org.thingsboard.server.dao.oauth2.OAuth2Configuration;
import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.security.permission.Operation; import org.thingsboard.server.service.security.permission.Operation;
import org.thingsboard.server.service.security.permission.Resource; import org.thingsboard.server.service.security.permission.Resource;
@ -36,6 +38,10 @@ import java.util.List;
@RequestMapping("/api") @RequestMapping("/api")
@Slf4j @Slf4j
public class OAuth2Controller extends BaseController { public class OAuth2Controller extends BaseController {
@Autowired
private OAuth2Configuration oAuth2Configuration;
@RequestMapping(value = "/noauth/oauth2Clients", method = RequestMethod.POST) @RequestMapping(value = "/noauth/oauth2Clients", method = RequestMethod.POST)
@ResponseBody @ResponseBody
public List<OAuth2ClientInfo> getOAuth2Clients(HttpServletRequest request) throws ThingsboardException { public List<OAuth2ClientInfo> getOAuth2Clients(HttpServletRequest request) throws ThingsboardException {
@ -70,4 +76,16 @@ public class OAuth2Controller extends BaseController {
throw handleException(e); throw handleException(e);
} }
} }
@PreAuthorize("hasAnyAuthority('SYS_ADMIN')")
@RequestMapping(value = "/oauth2/loginProcessingUrl", method = RequestMethod.GET)
@ResponseBody
public String getLoginProcessingUrl() throws ThingsboardException {
try {
accessControlService.checkPermission(getCurrentUser(), Resource.OAUTH2_CONFIGURATION_INFO, Operation.READ);
return "\"" + oAuth2Configuration.getLoginProcessingUrl() + "\"";
} catch (Exception e) {
throw handleException(e);
}
}
} }

6
common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientsDomainParams.java

@ -27,6 +27,6 @@ import java.util.Set;
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor
public class OAuth2ClientsDomainParams { public class OAuth2ClientsDomainParams {
private Set<DomainInfo> domainInfos; private List<DomainInfo> domainInfos;
private Set<ClientRegistrationDto> clientRegistrations; private List<ClientRegistrationDto> clientRegistrations;
} }

6
common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientsParams.java

@ -16,6 +16,8 @@
package org.thingsboard.server.common.data.oauth2; package org.thingsboard.server.common.data.oauth2;
import lombok.*; import lombok.*;
import java.util.List;
import java.util.Set; import java.util.Set;
@EqualsAndHashCode @EqualsAndHashCode
@ -26,5 +28,5 @@ import java.util.Set;
@AllArgsConstructor @AllArgsConstructor
public class OAuth2ClientsParams { public class OAuth2ClientsParams {
private boolean enabled; private boolean enabled;
private Set<OAuth2ClientsDomainParams> domainsParams; private List<OAuth2ClientsDomainParams> domainsParams;
} }

12
dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Utils.java

@ -32,19 +32,19 @@ public class OAuth2Utils {
} }
public static OAuth2ClientsParams toOAuth2Params(List<ExtendedOAuth2ClientRegistrationInfo> extendedOAuth2ClientRegistrationInfos) { public static OAuth2ClientsParams toOAuth2Params(List<ExtendedOAuth2ClientRegistrationInfo> extendedOAuth2ClientRegistrationInfos) {
Map<OAuth2ClientRegistrationInfoId, Set<DomainInfo>> domainsByInfoId = new HashMap<>(); Map<OAuth2ClientRegistrationInfoId, List<DomainInfo>> domainsByInfoId = new LinkedHashMap<>();
Map<OAuth2ClientRegistrationInfoId, OAuth2ClientRegistrationInfo> infoById = new HashMap<>(); Map<OAuth2ClientRegistrationInfoId, OAuth2ClientRegistrationInfo> infoById = new LinkedHashMap<>();
for (ExtendedOAuth2ClientRegistrationInfo extendedClientRegistrationInfo : extendedOAuth2ClientRegistrationInfos) { for (ExtendedOAuth2ClientRegistrationInfo extendedClientRegistrationInfo : extendedOAuth2ClientRegistrationInfos) {
String domainName = extendedClientRegistrationInfo.getDomainName(); String domainName = extendedClientRegistrationInfo.getDomainName();
SchemeType domainScheme = extendedClientRegistrationInfo.getDomainScheme(); SchemeType domainScheme = extendedClientRegistrationInfo.getDomainScheme();
domainsByInfoId.computeIfAbsent(extendedClientRegistrationInfo.getId(), key -> new HashSet<>()) domainsByInfoId.computeIfAbsent(extendedClientRegistrationInfo.getId(), key -> new ArrayList<>())
.add(new DomainInfo(domainScheme, domainName)); .add(new DomainInfo(domainScheme, domainName));
infoById.put(extendedClientRegistrationInfo.getId(), extendedClientRegistrationInfo); infoById.put(extendedClientRegistrationInfo.getId(), extendedClientRegistrationInfo);
} }
Map<Set<DomainInfo>, OAuth2ClientsDomainParams> domainParamsMap = new HashMap<>(); Map<List<DomainInfo>, OAuth2ClientsDomainParams> domainParamsMap = new LinkedHashMap<>();
domainsByInfoId.forEach((clientRegistrationInfoId, domainInfos) -> { domainsByInfoId.forEach((clientRegistrationInfoId, domainInfos) -> {
domainParamsMap.computeIfAbsent(domainInfos, domainParamsMap.computeIfAbsent(domainInfos,
key -> new OAuth2ClientsDomainParams(key, new HashSet<>()) key -> new OAuth2ClientsDomainParams(key, new ArrayList<>())
) )
.getClientRegistrations() .getClientRegistrations()
.add(toClientRegistrationDto(infoById.get(clientRegistrationInfoId))); .add(toClientRegistrationDto(infoById.get(clientRegistrationInfoId)));
@ -52,7 +52,7 @@ public class OAuth2Utils {
boolean enabled = extendedOAuth2ClientRegistrationInfos.stream() boolean enabled = extendedOAuth2ClientRegistrationInfos.stream()
.map(OAuth2ClientRegistrationInfo::isEnabled) .map(OAuth2ClientRegistrationInfo::isEnabled)
.findFirst().orElse(false); .findFirst().orElse(false);
return new OAuth2ClientsParams(enabled, new HashSet<>(domainParamsMap.values())); return new OAuth2ClientsParams(enabled, new ArrayList<>(domainParamsMap.values()));
} }
public static ClientRegistrationDto toClientRegistrationDto(OAuth2ClientRegistrationInfo oAuth2ClientRegistrationInfo) { public static ClientRegistrationDto toClientRegistrationDto(OAuth2ClientRegistrationInfo oAuth2ClientRegistrationInfo) {

93
dao/src/test/java/org/thingsboard/server/dao/service/BaseOAuth2ServiceTest.java

@ -15,6 +15,7 @@
*/ */
package org.thingsboard.server.dao.service; package org.thingsboard.server.dao.service;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets; import com.google.common.collect.Sets;
import org.junit.After; import org.junit.After;
import org.junit.Assert; import org.junit.Assert;
@ -29,7 +30,7 @@ import java.util.*;
import java.util.stream.Collectors; import java.util.stream.Collectors;
public class BaseOAuth2ServiceTest extends AbstractServiceTest { public class BaseOAuth2ServiceTest extends AbstractServiceTest {
private static final OAuth2ClientsParams EMPTY_PARAMS = new OAuth2ClientsParams(false, new HashSet<>()); private static final OAuth2ClientsParams EMPTY_PARAMS = new OAuth2ClientsParams(false, new ArrayList<>());
@Autowired @Autowired
protected OAuth2Service oAuth2Service; protected OAuth2Service oAuth2Service;
@ -48,14 +49,14 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest {
@Test(expected = DataValidationException.class) @Test(expected = DataValidationException.class)
public void testSaveHttpAndMixedDomainsTogether() { public void testSaveHttpAndMixedDomainsTogether() {
OAuth2ClientsParams clientsParams = new OAuth2ClientsParams(true, Sets.newHashSet( OAuth2ClientsParams clientsParams = new OAuth2ClientsParams(true, Lists.newArrayList(
OAuth2ClientsDomainParams.builder() OAuth2ClientsDomainParams.builder()
.domainInfos(Sets.newHashSet( .domainInfos(Lists.newArrayList(
DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTP).build(), DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTP).build(),
DomainInfo.builder().name("first-domain").scheme(SchemeType.MIXED).build(), DomainInfo.builder().name("first-domain").scheme(SchemeType.MIXED).build(),
DomainInfo.builder().name("third-domain").scheme(SchemeType.HTTPS).build() DomainInfo.builder().name("third-domain").scheme(SchemeType.HTTPS).build()
)) ))
.clientRegistrations(Sets.newHashSet( .clientRegistrations(Lists.newArrayList(
validClientRegistrationDto(), validClientRegistrationDto(),
validClientRegistrationDto(), validClientRegistrationDto(),
validClientRegistrationDto() validClientRegistrationDto()
@ -67,14 +68,14 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest {
@Test(expected = DataValidationException.class) @Test(expected = DataValidationException.class)
public void testSaveHttpsAndMixedDomainsTogether() { public void testSaveHttpsAndMixedDomainsTogether() {
OAuth2ClientsParams clientsParams = new OAuth2ClientsParams(true, Sets.newHashSet( OAuth2ClientsParams clientsParams = new OAuth2ClientsParams(true, Lists.newArrayList(
OAuth2ClientsDomainParams.builder() OAuth2ClientsDomainParams.builder()
.domainInfos(Sets.newHashSet( .domainInfos(Lists.newArrayList(
DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTPS).build(), DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTPS).build(),
DomainInfo.builder().name("first-domain").scheme(SchemeType.MIXED).build(), DomainInfo.builder().name("first-domain").scheme(SchemeType.MIXED).build(),
DomainInfo.builder().name("third-domain").scheme(SchemeType.HTTPS).build() DomainInfo.builder().name("third-domain").scheme(SchemeType.HTTPS).build()
)) ))
.clientRegistrations(Sets.newHashSet( .clientRegistrations(Lists.newArrayList(
validClientRegistrationDto(), validClientRegistrationDto(),
validClientRegistrationDto(), validClientRegistrationDto(),
validClientRegistrationDto() validClientRegistrationDto()
@ -131,20 +132,20 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest {
Assert.assertNotNull(foundClientsParams); Assert.assertNotNull(foundClientsParams);
Assert.assertEquals(clientsParams, foundClientsParams); Assert.assertEquals(clientsParams, foundClientsParams);
OAuth2ClientsParams newClientsParams = new OAuth2ClientsParams(true, Sets.newHashSet( OAuth2ClientsParams newClientsParams = new OAuth2ClientsParams(true, Lists.newArrayList(
OAuth2ClientsDomainParams.builder() OAuth2ClientsDomainParams.builder()
.domainInfos(Sets.newHashSet( .domainInfos(Lists.newArrayList(
DomainInfo.builder().name("another-domain").scheme(SchemeType.HTTPS).build() DomainInfo.builder().name("another-domain").scheme(SchemeType.HTTPS).build()
)) ))
.clientRegistrations(Sets.newHashSet( .clientRegistrations(Lists.newArrayList(
validClientRegistrationDto() validClientRegistrationDto()
)) ))
.build(), .build(),
OAuth2ClientsDomainParams.builder() OAuth2ClientsDomainParams.builder()
.domainInfos(Sets.newHashSet( .domainInfos(Lists.newArrayList(
DomainInfo.builder().name("test-domain").scheme(SchemeType.MIXED).build() DomainInfo.builder().name("test-domain").scheme(SchemeType.MIXED).build()
)) ))
.clientRegistrations(Sets.newHashSet( .clientRegistrations(Lists.newArrayList(
validClientRegistrationDto() validClientRegistrationDto()
)) ))
.build() .build()
@ -157,22 +158,22 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest {
@Test @Test
public void testGetOAuth2Clients() { public void testGetOAuth2Clients() {
Set<ClientRegistrationDto> firstGroup = Sets.newHashSet( List<ClientRegistrationDto> firstGroup = Lists.newArrayList(
validClientRegistrationDto(), validClientRegistrationDto(),
validClientRegistrationDto(), validClientRegistrationDto(),
validClientRegistrationDto(), validClientRegistrationDto(),
validClientRegistrationDto() validClientRegistrationDto()
); );
Set<ClientRegistrationDto> secondGroup = Sets.newHashSet( List<ClientRegistrationDto> secondGroup = Lists.newArrayList(
validClientRegistrationDto(), validClientRegistrationDto(),
validClientRegistrationDto() validClientRegistrationDto()
); );
Set<ClientRegistrationDto> thirdGroup = Sets.newHashSet( List<ClientRegistrationDto> thirdGroup = Lists.newArrayList(
validClientRegistrationDto() validClientRegistrationDto()
); );
OAuth2ClientsParams clientsParams = new OAuth2ClientsParams(true, Sets.newHashSet( OAuth2ClientsParams clientsParams = new OAuth2ClientsParams(true, Lists.newArrayList(
OAuth2ClientsDomainParams.builder() OAuth2ClientsDomainParams.builder()
.domainInfos(Sets.newHashSet( .domainInfos(Lists.newArrayList(
DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTP).build(), DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTP).build(),
DomainInfo.builder().name("second-domain").scheme(SchemeType.MIXED).build(), DomainInfo.builder().name("second-domain").scheme(SchemeType.MIXED).build(),
DomainInfo.builder().name("third-domain").scheme(SchemeType.HTTPS).build() DomainInfo.builder().name("third-domain").scheme(SchemeType.HTTPS).build()
@ -180,14 +181,14 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest {
.clientRegistrations(firstGroup) .clientRegistrations(firstGroup)
.build(), .build(),
OAuth2ClientsDomainParams.builder() OAuth2ClientsDomainParams.builder()
.domainInfos(Sets.newHashSet( .domainInfos(Lists.newArrayList(
DomainInfo.builder().name("second-domain").scheme(SchemeType.HTTP).build(), DomainInfo.builder().name("second-domain").scheme(SchemeType.HTTP).build(),
DomainInfo.builder().name("fourth-domain").scheme(SchemeType.MIXED).build() DomainInfo.builder().name("fourth-domain").scheme(SchemeType.MIXED).build()
)) ))
.clientRegistrations(secondGroup) .clientRegistrations(secondGroup)
.build(), .build(),
OAuth2ClientsDomainParams.builder() OAuth2ClientsDomainParams.builder()
.domainInfos(Sets.newHashSet( .domainInfos(Lists.newArrayList(
DomainInfo.builder().name("second-domain").scheme(SchemeType.HTTPS).build(), DomainInfo.builder().name("second-domain").scheme(SchemeType.HTTPS).build(),
DomainInfo.builder().name("fifth-domain").scheme(SchemeType.HTTP).build() DomainInfo.builder().name("fifth-domain").scheme(SchemeType.HTTP).build()
)) ))
@ -285,15 +286,15 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest {
@Test @Test
public void testGetOAuth2ClientsForHttpAndHttps() { public void testGetOAuth2ClientsForHttpAndHttps() {
Set<ClientRegistrationDto> firstGroup = Sets.newHashSet( List<ClientRegistrationDto> firstGroup = Lists.newArrayList(
validClientRegistrationDto(), validClientRegistrationDto(),
validClientRegistrationDto(), validClientRegistrationDto(),
validClientRegistrationDto(), validClientRegistrationDto(),
validClientRegistrationDto() validClientRegistrationDto()
); );
OAuth2ClientsParams clientsParams = new OAuth2ClientsParams(true, Sets.newHashSet( OAuth2ClientsParams clientsParams = new OAuth2ClientsParams(true, Lists.newArrayList(
OAuth2ClientsDomainParams.builder() OAuth2ClientsDomainParams.builder()
.domainInfos(Sets.newHashSet( .domainInfos(Lists.newArrayList(
DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTP).build(), DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTP).build(),
DomainInfo.builder().name("second-domain").scheme(SchemeType.MIXED).build(), DomainInfo.builder().name("second-domain").scheme(SchemeType.MIXED).build(),
DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTPS).build() DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTPS).build()
@ -335,25 +336,25 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest {
@Test @Test
public void testGetDisabledOAuth2Clients() { public void testGetDisabledOAuth2Clients() {
OAuth2ClientsParams clientsParams = new OAuth2ClientsParams(true, Sets.newHashSet( OAuth2ClientsParams clientsParams = new OAuth2ClientsParams(true, Lists.newArrayList(
OAuth2ClientsDomainParams.builder() OAuth2ClientsDomainParams.builder()
.domainInfos(Sets.newHashSet( .domainInfos(Lists.newArrayList(
DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTP).build(), DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTP).build(),
DomainInfo.builder().name("second-domain").scheme(SchemeType.MIXED).build(), DomainInfo.builder().name("second-domain").scheme(SchemeType.MIXED).build(),
DomainInfo.builder().name("third-domain").scheme(SchemeType.HTTPS).build() DomainInfo.builder().name("third-domain").scheme(SchemeType.HTTPS).build()
)) ))
.clientRegistrations(Sets.newHashSet( .clientRegistrations(Lists.newArrayList(
validClientRegistrationDto(), validClientRegistrationDto(),
validClientRegistrationDto(), validClientRegistrationDto(),
validClientRegistrationDto() validClientRegistrationDto()
)) ))
.build(), .build(),
OAuth2ClientsDomainParams.builder() OAuth2ClientsDomainParams.builder()
.domainInfos(Sets.newHashSet( .domainInfos(Lists.newArrayList(
DomainInfo.builder().name("second-domain").scheme(SchemeType.HTTP).build(), DomainInfo.builder().name("second-domain").scheme(SchemeType.HTTP).build(),
DomainInfo.builder().name("fourth-domain").scheme(SchemeType.MIXED).build() DomainInfo.builder().name("fourth-domain").scheme(SchemeType.MIXED).build()
)) ))
.clientRegistrations(Sets.newHashSet( .clientRegistrations(Lists.newArrayList(
validClientRegistrationDto(), validClientRegistrationDto(),
validClientRegistrationDto() validClientRegistrationDto()
)) ))
@ -374,35 +375,35 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest {
@Test @Test
public void testFindAllClientRegistrationInfos() { public void testFindAllClientRegistrationInfos() {
OAuth2ClientsParams clientsParams = new OAuth2ClientsParams(true, Sets.newHashSet( OAuth2ClientsParams clientsParams = new OAuth2ClientsParams(true, Lists.newArrayList(
OAuth2ClientsDomainParams.builder() OAuth2ClientsDomainParams.builder()
.domainInfos(Sets.newHashSet( .domainInfos(Lists.newArrayList(
DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTP).build(), DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTP).build(),
DomainInfo.builder().name("second-domain").scheme(SchemeType.MIXED).build(), DomainInfo.builder().name("second-domain").scheme(SchemeType.MIXED).build(),
DomainInfo.builder().name("third-domain").scheme(SchemeType.HTTPS).build() DomainInfo.builder().name("third-domain").scheme(SchemeType.HTTPS).build()
)) ))
.clientRegistrations(Sets.newHashSet( .clientRegistrations(Lists.newArrayList(
validClientRegistrationDto(), validClientRegistrationDto(),
validClientRegistrationDto(), validClientRegistrationDto(),
validClientRegistrationDto() validClientRegistrationDto()
)) ))
.build(), .build(),
OAuth2ClientsDomainParams.builder() OAuth2ClientsDomainParams.builder()
.domainInfos(Sets.newHashSet( .domainInfos(Lists.newArrayList(
DomainInfo.builder().name("second-domain").scheme(SchemeType.HTTP).build(), DomainInfo.builder().name("second-domain").scheme(SchemeType.HTTP).build(),
DomainInfo.builder().name("fourth-domain").scheme(SchemeType.MIXED).build() DomainInfo.builder().name("fourth-domain").scheme(SchemeType.MIXED).build()
)) ))
.clientRegistrations(Sets.newHashSet( .clientRegistrations(Lists.newArrayList(
validClientRegistrationDto(), validClientRegistrationDto(),
validClientRegistrationDto() validClientRegistrationDto()
)) ))
.build(), .build(),
OAuth2ClientsDomainParams.builder() OAuth2ClientsDomainParams.builder()
.domainInfos(Sets.newHashSet( .domainInfos(Lists.newArrayList(
DomainInfo.builder().name("second-domain").scheme(SchemeType.HTTPS).build(), DomainInfo.builder().name("second-domain").scheme(SchemeType.HTTPS).build(),
DomainInfo.builder().name("fifth-domain").scheme(SchemeType.HTTP).build() DomainInfo.builder().name("fifth-domain").scheme(SchemeType.HTTP).build()
)) ))
.clientRegistrations(Sets.newHashSet( .clientRegistrations(Lists.newArrayList(
validClientRegistrationDto() validClientRegistrationDto()
)) ))
.build() .build()
@ -423,35 +424,35 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest {
@Test @Test
public void testFindClientRegistrationById() { public void testFindClientRegistrationById() {
OAuth2ClientsParams clientsParams = new OAuth2ClientsParams(true, Sets.newHashSet( OAuth2ClientsParams clientsParams = new OAuth2ClientsParams(true, Lists.newArrayList(
OAuth2ClientsDomainParams.builder() OAuth2ClientsDomainParams.builder()
.domainInfos(Sets.newHashSet( .domainInfos(Lists.newArrayList(
DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTP).build(), DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTP).build(),
DomainInfo.builder().name("second-domain").scheme(SchemeType.MIXED).build(), DomainInfo.builder().name("second-domain").scheme(SchemeType.MIXED).build(),
DomainInfo.builder().name("third-domain").scheme(SchemeType.HTTPS).build() DomainInfo.builder().name("third-domain").scheme(SchemeType.HTTPS).build()
)) ))
.clientRegistrations(Sets.newHashSet( .clientRegistrations(Lists.newArrayList(
validClientRegistrationDto(), validClientRegistrationDto(),
validClientRegistrationDto(), validClientRegistrationDto(),
validClientRegistrationDto() validClientRegistrationDto()
)) ))
.build(), .build(),
OAuth2ClientsDomainParams.builder() OAuth2ClientsDomainParams.builder()
.domainInfos(Sets.newHashSet( .domainInfos(Lists.newArrayList(
DomainInfo.builder().name("second-domain").scheme(SchemeType.HTTP).build(), DomainInfo.builder().name("second-domain").scheme(SchemeType.HTTP).build(),
DomainInfo.builder().name("fourth-domain").scheme(SchemeType.MIXED).build() DomainInfo.builder().name("fourth-domain").scheme(SchemeType.MIXED).build()
)) ))
.clientRegistrations(Sets.newHashSet( .clientRegistrations(Lists.newArrayList(
validClientRegistrationDto(), validClientRegistrationDto(),
validClientRegistrationDto() validClientRegistrationDto()
)) ))
.build(), .build(),
OAuth2ClientsDomainParams.builder() OAuth2ClientsDomainParams.builder()
.domainInfos(Sets.newHashSet( .domainInfos(Lists.newArrayList(
DomainInfo.builder().name("second-domain").scheme(SchemeType.HTTPS).build(), DomainInfo.builder().name("second-domain").scheme(SchemeType.HTTPS).build(),
DomainInfo.builder().name("fifth-domain").scheme(SchemeType.HTTP).build() DomainInfo.builder().name("fifth-domain").scheme(SchemeType.HTTP).build()
)) ))
.clientRegistrations(Sets.newHashSet( .clientRegistrations(Lists.newArrayList(
validClientRegistrationDto() validClientRegistrationDto()
)) ))
.build() .build()
@ -466,14 +467,14 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest {
} }
private OAuth2ClientsParams createDefaultClientsParams() { private OAuth2ClientsParams createDefaultClientsParams() {
return new OAuth2ClientsParams(true, Sets.newHashSet( return new OAuth2ClientsParams(true, Lists.newArrayList(
OAuth2ClientsDomainParams.builder() OAuth2ClientsDomainParams.builder()
.domainInfos(Sets.newHashSet( .domainInfos(Lists.newArrayList(
DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTP).build(), DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTP).build(),
DomainInfo.builder().name("second-domain").scheme(SchemeType.MIXED).build(), DomainInfo.builder().name("second-domain").scheme(SchemeType.MIXED).build(),
DomainInfo.builder().name("third-domain").scheme(SchemeType.HTTPS).build() DomainInfo.builder().name("third-domain").scheme(SchemeType.HTTPS).build()
)) ))
.clientRegistrations(Sets.newHashSet( .clientRegistrations(Lists.newArrayList(
validClientRegistrationDto(), validClientRegistrationDto(),
validClientRegistrationDto(), validClientRegistrationDto(),
validClientRegistrationDto(), validClientRegistrationDto(),
@ -481,11 +482,11 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest {
)) ))
.build(), .build(),
OAuth2ClientsDomainParams.builder() OAuth2ClientsDomainParams.builder()
.domainInfos(Sets.newHashSet( .domainInfos(Lists.newArrayList(
DomainInfo.builder().name("second-domain").scheme(SchemeType.MIXED).build(), DomainInfo.builder().name("second-domain").scheme(SchemeType.MIXED).build(),
DomainInfo.builder().name("fourth-domain").scheme(SchemeType.MIXED).build() DomainInfo.builder().name("fourth-domain").scheme(SchemeType.MIXED).build()
)) ))
.clientRegistrations(Sets.newHashSet( .clientRegistrations(Lists.newArrayList(
validClientRegistrationDto(), validClientRegistrationDto(),
validClientRegistrationDto() validClientRegistrationDto()
)) ))

4
ui-ngx/src/app/core/http/oauth2.service.ts

@ -41,4 +41,8 @@ export class OAuth2Service {
return this.http.post<OAuth2ClientsParams>('/api/oauth2/config', OAuth2Setting, return this.http.post<OAuth2ClientsParams>('/api/oauth2/config', OAuth2Setting,
defaultHttpOptionsFromConfig(config)); defaultHttpOptionsFromConfig(config));
} }
public getLoginProcessingUrl(config?: RequestConfig): Observable<string> {
return this.http.get<string>(`/api/oauth2/loginProcessingUrl`, defaultHttpOptionsFromConfig(config));
}
} }

5
ui-ngx/src/app/core/services/menu.service.ts

@ -191,6 +191,11 @@ export class MenuService {
name: 'admin.security-settings', name: 'admin.security-settings',
icon: 'security', icon: 'security',
path: '/settings/security-settings' path: '/settings/security-settings'
},
{
name: 'admin.oauth2.oauth2',
icon: 'security',
path: '/settings/oauth2'
} }
] ]
} }

17
ui-ngx/src/app/modules/home/components/alarm/alarm-details-dialog.component.html

@ -88,14 +88,20 @@
</fieldset> </fieldset>
</div> </div>
<div mat-dialog-actions fxLayout="row"> <div mat-dialog-actions fxLayout="row">
<div fxLayout="row" *ngIf="alarm$ | async; let alarm;"> <button mat-button color="primary"
type="button"
[disabled]="(isLoading$ | async)"
(click)="close()" cdkFocusInitial>
{{ 'action.close' | translate }}
</button>
<span fxFlex></span>
<div fxLayout="row" *ngIf="alarm$ | async; let alarm;" fxLayoutGap="8px">
<button *ngIf="allowAcknowledgment && (alarm.status === alarmStatuses.ACTIVE_UNACK || <button *ngIf="allowAcknowledgment && (alarm.status === alarmStatuses.ACTIVE_UNACK ||
alarm.status === alarmStatuses.CLEARED_UNACK)" alarm.status === alarmStatuses.CLEARED_UNACK)"
mat-raised-button mat-raised-button
color="primary" color="primary"
type="button" type="button"
(click)="acknowledge()" (click)="acknowledge()"
style="margin-right: 20px;"
[disabled]="(isLoading$ | async)"> [disabled]="(isLoading$ | async)">
{{ 'alarm.acknowledge' | translate }} {{ 'alarm.acknowledge' | translate }}
</button> </button>
@ -109,12 +115,5 @@
{{ 'alarm.clear' | translate }} {{ 'alarm.clear' | translate }}
</button> </button>
</div> </div>
<span fxFlex></span>
<button mat-button color="primary"
type="button"
[disabled]="(isLoading$ | async)"
(click)="close()" cdkFocusInitial>
{{ 'action.close' | translate }}
</button>
</div> </div>
</form> </form>

10
ui-ngx/src/app/modules/home/components/alias/entity-alias-dialog.component.html

@ -59,16 +59,16 @@
</fieldset> </fieldset>
</div> </div>
<div mat-dialog-actions fxLayoutAlign="end center"> <div mat-dialog-actions fxLayoutAlign="end center">
<button mat-raised-button color="primary"
type="submit"
[disabled]="(isLoading$ | async) || entityAliasFormGroup.invalid || !entityAliasFormGroup.dirty">
{{ (isAdd ? 'action.add' : 'action.save') | translate }}
</button>
<button mat-button color="primary" <button mat-button color="primary"
type="button" type="button"
[disabled]="(isLoading$ | async)" [disabled]="(isLoading$ | async)"
(click)="cancel()" cdkFocusInitial> (click)="cancel()" cdkFocusInitial>
{{ 'action.cancel' | translate }} {{ 'action.cancel' | translate }}
</button> </button>
<button mat-raised-button color="primary"
type="submit"
[disabled]="(isLoading$ | async) || entityAliasFormGroup.invalid || !entityAliasFormGroup.dirty">
{{ (isAdd ? 'action.add' : 'action.save') | translate }}
</button>
</div> </div>
</form> </form>

10
ui-ngx/src/app/modules/home/components/alias/entity-aliases-dialog.component.html

@ -95,11 +95,6 @@
{{ 'alias.add' | translate }} {{ 'alias.add' | translate }}
</button> </button>
<span fxFlex></span> <span fxFlex></span>
<button mat-raised-button color="primary"
type="submit"
[disabled]="(isLoading$ | async) || entityAliasesFormGroup.invalid || !entityAliasesFormGroup.dirty">
{{ 'action.save' | translate }}
</button>
<button mat-button color="primary" <button mat-button color="primary"
type="button" type="button"
[disabled]="(isLoading$ | async)" [disabled]="(isLoading$ | async)"
@ -107,5 +102,10 @@
cdkFocusInitial> cdkFocusInitial>
{{ 'action.cancel' | translate }} {{ 'action.cancel' | translate }}
</button> </button>
<button mat-raised-button color="primary"
type="submit"
[disabled]="(isLoading$ | async) || entityAliasesFormGroup.invalid || !entityAliasesFormGroup.dirty">
{{ 'action.save' | translate }}
</button>
</div> </div>
</form> </form>

10
ui-ngx/src/app/modules/home/components/attribute/add-attribute-dialog.component.html

@ -44,16 +44,16 @@
</fieldset> </fieldset>
</div> </div>
<div mat-dialog-actions fxLayoutAlign="end center"> <div mat-dialog-actions fxLayoutAlign="end center">
<button mat-raised-button color="primary"
type="submit"
[disabled]="(isLoading$ | async) || attributeFormGroup.invalid || !attributeFormGroup.dirty">
{{ 'action.add' | translate }}
</button>
<button mat-button color="primary" <button mat-button color="primary"
type="button" type="button"
[disabled]="(isLoading$ | async)" [disabled]="(isLoading$ | async)"
(click)="cancel()" cdkFocusInitial> (click)="cancel()" cdkFocusInitial>
{{ 'action.cancel' | translate }} {{ 'action.cancel' | translate }}
</button> </button>
<button mat-raised-button color="primary"
type="submit"
[disabled]="(isLoading$ | async) || attributeFormGroup.invalid || !attributeFormGroup.dirty">
{{ 'action.add' | translate }}
</button>
</div> </div>
</form> </form>

15
ui-ngx/src/app/modules/home/components/attribute/add-widget-to-dashboard-dialog.component.html

@ -55,21 +55,22 @@
</mat-radio-group> </mat-radio-group>
</fieldset> </fieldset>
</div> </div>
<div mat-dialog-actions fxLayoutAlign="end center"> <div mat-dialog-actions fxLayout="row">
<mat-checkbox formControlName="openDashboard" <mat-checkbox formControlName="openDashboard"
style="margin-bottom: 0; padding-right: 20px;"> style="margin-bottom: 0;">
{{ 'dashboard.open-dashboard' | translate }} {{ 'dashboard.open-dashboard' | translate }}
</mat-checkbox> </mat-checkbox>
<button mat-raised-button color="primary" <span fxFlex></span>
type="submit"
[disabled]="(isLoading$ | async) || addWidgetFormGroup.invalid || !addWidgetFormGroup.dirty">
{{ 'action.add' | translate }}
</button>
<button mat-button color="primary" <button mat-button color="primary"
type="button" type="button"
[disabled]="(isLoading$ | async)" [disabled]="(isLoading$ | async)"
(click)="cancel()" cdkFocusInitial> (click)="cancel()" cdkFocusInitial>
{{ 'action.cancel' | translate }} {{ 'action.cancel' | translate }}
</button> </button>
<button mat-raised-button color="primary"
type="submit"
[disabled]="(isLoading$ | async) || addWidgetFormGroup.invalid || !addWidgetFormGroup.dirty">
{{ 'action.add' | translate }}
</button>
</div> </div>
</form> </form>

1
ui-ngx/src/app/modules/home/components/attribute/edit-attribute-value-panel.component.html

@ -26,7 +26,6 @@
<div fxLayout="row" class="tb-panel-actions"> <div fxLayout="row" class="tb-panel-actions">
<span fxFlex></span> <span fxFlex></span>
<button mat-button color="primary" <button mat-button color="primary"
style="margin-right: 20px;"
type="button" type="button"
[disabled]="(isLoading$ | async)" [disabled]="(isLoading$ | async)"
(click)="cancel()" cdkFocusInitial> (click)="cancel()" cdkFocusInitial>

10
ui-ngx/src/app/modules/home/components/dashboard/select-target-state-dialog.component.html

@ -40,11 +40,6 @@
</fieldset> </fieldset>
</div> </div>
<div mat-dialog-actions fxLayoutAlign="end center"> <div mat-dialog-actions fxLayoutAlign="end center">
<button mat-raised-button color="primary"
type="submit"
[disabled]="(isLoading$ | async) || stateFormGroup.invalid">
{{ 'action.select' | translate }}
</button>
<button mat-button color="primary" <button mat-button color="primary"
type="button" type="button"
[disabled]="(isLoading$ | async)" [disabled]="(isLoading$ | async)"
@ -52,5 +47,10 @@
cdkFocusInitial> cdkFocusInitial>
{{ 'action.cancel' | translate }} {{ 'action.cancel' | translate }}
</button> </button>
<button mat-raised-button color="primary"
type="submit"
[disabled]="(isLoading$ | async) || stateFormGroup.invalid">
{{ 'action.select' | translate }}
</button>
</div> </div>
</form> </form>

1
ui-ngx/src/app/modules/home/components/event/event-content-dialog.component.html

@ -31,7 +31,6 @@
<div mat-dialog-actions fxLayout="row"> <div mat-dialog-actions fxLayout="row">
<span fxFlex></span> <span fxFlex></span>
<button mat-button color="primary" <button mat-button color="primary"
style="margin-right: 20px;"
type="button" type="button"
[disabled]="(isLoading$ | async)" [disabled]="(isLoading$ | async)"
[mat-dialog-close]="false" cdkFocusInitial> [mat-dialog-close]="false" cdkFocusInitial>

12
ui-ngx/src/app/modules/home/components/filter/complex-filter-predicate-dialog.component.html

@ -46,12 +46,6 @@
</fieldset> </fieldset>
</div> </div>
<div mat-dialog-actions fxLayoutAlign="end center"> <div mat-dialog-actions fxLayoutAlign="end center">
<button mat-raised-button color="primary"
*ngIf="!data.readonly"
type="submit"
[disabled]="(isLoading$ | async) || complexFilterFormGroup.invalid || !complexFilterFormGroup.dirty">
{{ (isAdd ? 'action.add' : 'action.update') | translate }}
</button>
<button mat-button color="primary" <button mat-button color="primary"
type="button" type="button"
[disabled]="(isLoading$ | async)" [disabled]="(isLoading$ | async)"
@ -59,5 +53,11 @@
cdkFocusInitial> cdkFocusInitial>
{{ (data.readonly ? 'action.close' : 'action.cancel') | translate }} {{ (data.readonly ? 'action.close' : 'action.cancel') | translate }}
</button> </button>
<button mat-raised-button color="primary"
*ngIf="!data.readonly"
type="submit"
[disabled]="(isLoading$ | async) || complexFilterFormGroup.invalid || !complexFilterFormGroup.dirty">
{{ (isAdd ? 'action.add' : 'action.update') | translate }}
</button>
</div> </div>
</form> </form>

10
ui-ngx/src/app/modules/home/components/filter/filter-dialog.component.html

@ -55,16 +55,16 @@
</fieldset> </fieldset>
</div> </div>
<div mat-dialog-actions fxLayoutAlign="end center"> <div mat-dialog-actions fxLayoutAlign="end center">
<button mat-raised-button color="primary"
type="submit"
[disabled]="(isLoading$ | async) || filterFormGroup.invalid || !filterFormGroup.dirty">
{{ (isAdd ? 'action.add' : 'action.update') | translate }}
</button>
<button mat-button color="primary" <button mat-button color="primary"
type="button" type="button"
[disabled]="(isLoading$ | async)" [disabled]="(isLoading$ | async)"
(click)="cancel()" cdkFocusInitial> (click)="cancel()" cdkFocusInitial>
{{ 'action.cancel' | translate }} {{ 'action.cancel' | translate }}
</button> </button>
<button mat-raised-button color="primary"
type="submit"
[disabled]="(isLoading$ | async) || filterFormGroup.invalid || !filterFormGroup.dirty">
{{ (isAdd ? 'action.add' : 'action.update') | translate }}
</button>
</div> </div>
</form> </form>

12
ui-ngx/src/app/modules/home/components/filter/filter-user-info-dialog.component.html

@ -46,12 +46,6 @@
</fieldset> </fieldset>
</div> </div>
<div mat-dialog-actions fxLayoutAlign="end center"> <div mat-dialog-actions fxLayoutAlign="end center">
<button mat-raised-button color="primary"
*ngIf="!data.readonly"
type="submit"
[disabled]="(isLoading$ | async) || filterUserInfoFormGroup.invalid || !filterUserInfoFormGroup.dirty">
{{ 'action.update' | translate }}
</button>
<button mat-button color="primary" <button mat-button color="primary"
type="button" type="button"
[disabled]="(isLoading$ | async)" [disabled]="(isLoading$ | async)"
@ -59,5 +53,11 @@
cdkFocusInitial> cdkFocusInitial>
{{ (data.readonly ? 'action.close' : 'action.cancel') | translate }} {{ (data.readonly ? 'action.close' : 'action.cancel') | translate }}
</button> </button>
<button mat-raised-button color="primary"
*ngIf="!data.readonly"
type="submit"
[disabled]="(isLoading$ | async) || filterUserInfoFormGroup.invalid || !filterUserInfoFormGroup.dirty">
{{ 'action.update' | translate }}
</button>
</div> </div>
</form> </form>

10
ui-ngx/src/app/modules/home/components/filter/filters-dialog.component.html

@ -88,11 +88,6 @@
{{ 'filter.add' | translate }} {{ 'filter.add' | translate }}
</button> </button>
<span fxFlex></span> <span fxFlex></span>
<button mat-raised-button color="primary"
type="submit"
[disabled]="(isLoading$ | async) || filtersFormGroup.invalid || !filtersFormGroup.dirty">
{{ 'action.save' | translate }}
</button>
<button mat-button color="primary" <button mat-button color="primary"
type="button" type="button"
[disabled]="(isLoading$ | async)" [disabled]="(isLoading$ | async)"
@ -100,5 +95,10 @@
cdkFocusInitial> cdkFocusInitial>
{{ 'action.cancel' | translate }} {{ 'action.cancel' | translate }}
</button> </button>
<button mat-raised-button color="primary"
type="submit"
[disabled]="(isLoading$ | async) || filtersFormGroup.invalid || !filtersFormGroup.dirty">
{{ 'action.save' | translate }}
</button>
</div> </div>
</form> </form>

12
ui-ngx/src/app/modules/home/components/filter/key-filter-dialog.component.html

@ -79,12 +79,6 @@
</fieldset> </fieldset>
</div> </div>
<div mat-dialog-actions fxLayoutAlign="end center"> <div mat-dialog-actions fxLayoutAlign="end center">
<button mat-raised-button color="primary"
type="submit"
*ngIf="!data.readonly"
[disabled]="(isLoading$ | async) || keyFilterFormGroup.invalid || !keyFilterFormGroup.dirty">
{{ (data.isAdd ? 'action.add' : 'action.update') | translate }}
</button>
<button mat-button color="primary" <button mat-button color="primary"
type="button" type="button"
[disabled]="(isLoading$ | async)" [disabled]="(isLoading$ | async)"
@ -92,5 +86,11 @@
cdkFocusInitial> cdkFocusInitial>
{{ (data.readonly ? 'action.close' : 'action.cancel') | translate }} {{ (data.readonly ? 'action.close' : 'action.cancel') | translate }}
</button> </button>
<button mat-raised-button color="primary"
type="submit"
*ngIf="!data.readonly"
[disabled]="(isLoading$ | async) || keyFilterFormGroup.invalid || !keyFilterFormGroup.dirty">
{{ (data.isAdd ? 'action.add' : 'action.update') | translate }}
</button>
</div> </div>
</form> </form>

10
ui-ngx/src/app/modules/home/components/filter/user-filter-dialog.component.html

@ -65,16 +65,16 @@
</fieldset> </fieldset>
</div> </div>
<div mat-dialog-actions fxLayoutAlign="end center"> <div mat-dialog-actions fxLayoutAlign="end center">
<button mat-raised-button color="primary"
type="submit"
[disabled]="(isLoading$ | async) || userFilterFormGroup.invalid || !userFilterFormGroup.dirty">
{{ 'action.update' | translate }}
</button>
<button mat-button color="primary" <button mat-button color="primary"
type="button" type="button"
[disabled]="(isLoading$ | async)" [disabled]="(isLoading$ | async)"
(click)="cancel()" cdkFocusInitial> (click)="cancel()" cdkFocusInitial>
{{ 'action.cancel' | translate }} {{ 'action.cancel' | translate }}
</button> </button>
<button mat-raised-button color="primary"
type="submit"
[disabled]="(isLoading$ | async) || userFilterFormGroup.invalid || !userFilterFormGroup.dirty">
{{ 'action.update' | translate }}
</button>
</div> </div>
</form> </form>

10
ui-ngx/src/app/modules/home/components/import-export/import-dialog.component.html

@ -43,16 +43,16 @@
</fieldset> </fieldset>
</div> </div>
<div mat-dialog-actions fxLayoutAlign="end center"> <div mat-dialog-actions fxLayoutAlign="end center">
<button mat-raised-button color="primary"
type="submit"
[disabled]="(isLoading$ | async) || importFormGroup.invalid || !importFormGroup.dirty">
{{ 'action.import' | translate }}
</button>
<button mat-button color="primary" <button mat-button color="primary"
type="button" type="button"
[disabled]="(isLoading$ | async)" [disabled]="(isLoading$ | async)"
(click)="cancel()" cdkFocusInitial> (click)="cancel()" cdkFocusInitial>
{{ 'action.cancel' | translate }} {{ 'action.cancel' | translate }}
</button> </button>
<button mat-raised-button color="primary"
type="submit"
[disabled]="(isLoading$ | async) || importFormGroup.invalid || !importFormGroup.dirty">
{{ 'action.import' | translate }}
</button>
</div> </div>
</form> </form>

12
ui-ngx/src/app/modules/home/components/profile/alarm/alarm-rule-condition-dialog.component.html

@ -108,17 +108,17 @@
</fieldset> </fieldset>
</div> </div>
<div mat-dialog-actions fxLayoutAlign="end center"> <div mat-dialog-actions fxLayoutAlign="end center">
<button mat-raised-button color="primary"
*ngIf="!readonly"
type="submit"
[disabled]="(isLoading$ | async) || conditionFormGroup.invalid || !conditionFormGroup.dirty">
{{ 'action.save' | translate }}
</button>
<button mat-button color="primary" <button mat-button color="primary"
type="button" type="button"
[disabled]="(isLoading$ | async)" [disabled]="(isLoading$ | async)"
(click)="cancel()" cdkFocusInitial> (click)="cancel()" cdkFocusInitial>
{{ (readonly ? 'action.close' : 'action.cancel') | translate }} {{ (readonly ? 'action.close' : 'action.cancel') | translate }}
</button> </button>
<button mat-raised-button color="primary"
*ngIf="!readonly"
type="submit"
[disabled]="(isLoading$ | async) || conditionFormGroup.invalid || !conditionFormGroup.dirty">
{{ 'action.save' | translate }}
</button>
</div> </div>
</form> </form>

12
ui-ngx/src/app/modules/home/components/profile/alarm/alarm-schedule-dialog.component.html

@ -37,17 +37,17 @@
</fieldset> </fieldset>
</div> </div>
<div mat-dialog-actions fxLayoutAlign="end center"> <div mat-dialog-actions fxLayoutAlign="end center">
<button mat-raised-button color="primary"
*ngIf="!readonly"
type="submit"
[disabled]="(isLoading$ | async) || alarmScheduleFormGroup.invalid || !alarmScheduleFormGroup.dirty">
{{ 'action.save' | translate }}
</button>
<button mat-button color="primary" <button mat-button color="primary"
type="button" type="button"
[disabled]="(isLoading$ | async)" [disabled]="(isLoading$ | async)"
(click)="cancel()" cdkFocusInitial> (click)="cancel()" cdkFocusInitial>
{{ (readonly ? 'action.close' : 'action.cancel') | translate }} {{ (readonly ? 'action.close' : 'action.cancel') | translate }}
</button> </button>
<button mat-raised-button color="primary"
*ngIf="!readonly"
type="submit"
[disabled]="(isLoading$ | async) || alarmScheduleFormGroup.invalid || !alarmScheduleFormGroup.dirty">
{{ 'action.save' | translate }}
</button>
</div> </div>
</form> </form>

4
ui-ngx/src/app/modules/home/components/profile/alarm/create-alarm-rules.component.html

@ -47,9 +47,9 @@
<mat-icon>remove_circle_outline</mat-icon> <mat-icon>remove_circle_outline</mat-icon>
</button> </button>
</div> </div>
<div *ngIf="!createAlarmRulesFormArray().controls.length"> <div *ngIf="!createAlarmRulesFormArray().controls.length && !disabled">
<span translate fxLayoutAlign="center center" style="margin: 16px 0" <span translate fxLayoutAlign="center center" style="margin: 16px 0"
class="tb-prompt">device-profile.no-create-alarm-rules</span> class="tb-prompt required">device-profile.add-create-alarm-rule-prompt</span>
</div> </div>
<div fxLayout="row" *ngIf="!disabled"> <div fxLayout="row" *ngIf="!disabled">
<button mat-stroked-button color="primary" <button mat-stroked-button color="primary"

21
ui-ngx/src/app/modules/home/components/profile/alarm/create-alarm-rules.component.ts

@ -23,11 +23,11 @@ import {
FormControl, FormControl,
FormGroup, FormGroup,
NG_VALIDATORS, NG_VALIDATORS,
NG_VALUE_ACCESSOR, NG_VALUE_ACCESSOR, ValidationErrors,
Validator, Validator,
Validators Validators
} from '@angular/forms'; } from '@angular/forms';
import { AlarmRule } from '@shared/models/device.models'; import { AlarmRule, alarmRuleValidator } from '@shared/models/device.models';
import { MatDialog } from '@angular/material/dialog'; import { MatDialog } from '@angular/material/dialog';
import { Subscription } from 'rxjs'; import { Subscription } from 'rxjs';
import { AlarmSeverity, alarmSeverityTranslations } from '../../../../../shared/models/alarm.models'; import { AlarmSeverity, alarmSeverityTranslations } from '../../../../../shared/models/alarm.models';
@ -141,10 +141,23 @@ export class CreateAlarmRulesComponent implements ControlValueAccessor, OnInit,
}; };
const createAlarmRulesArray = this.createAlarmRulesFormGroup.get('createAlarmRules') as FormArray; const createAlarmRulesArray = this.createAlarmRulesFormGroup.get('createAlarmRules') as FormArray;
createAlarmRulesArray.push(this.fb.group({ createAlarmRulesArray.push(this.fb.group({
severity: [null, Validators.required], severity: [this.getFirstUnusedSeverity(), Validators.required],
alarmRule: [createAlarmRule, Validators.required] alarmRule: [createAlarmRule, alarmRuleValidator]
})); }));
this.createAlarmRulesFormGroup.updateValueAndValidity(); this.createAlarmRulesFormGroup.updateValueAndValidity();
if (!this.createAlarmRulesFormGroup.valid) {
this.updateModel();
}
}
private getFirstUnusedSeverity(): AlarmSeverity {
for (const severityKey of Object.keys(AlarmSeverity)) {
const severity = AlarmSeverity[severityKey];
if (this.usedSeverities.indexOf(severity) === -1) {
return severity;
}
}
return null;
} }
public validate(c: FormControl) { public validate(c: FormControl) {

4
ui-ngx/src/app/modules/home/components/profile/alarm/device-profile-alarm.component.ts

@ -25,7 +25,7 @@ import {
Validator, Validator,
Validators Validators
} from '@angular/forms'; } from '@angular/forms';
import { AlarmRule, DeviceProfileAlarm } from '@shared/models/device.models'; import { AlarmRule, DeviceProfileAlarm, deviceProfileAlarmValidator } from '@shared/models/device.models';
import { MatDialog } from '@angular/material/dialog'; import { MatDialog } from '@angular/material/dialog';
import { COMMA, ENTER, SEMICOLON } from '@angular/cdk/keycodes'; import { COMMA, ENTER, SEMICOLON } from '@angular/cdk/keycodes';
import { MatChipInputEvent } from '@angular/material/chips'; import { MatChipInputEvent } from '@angular/material/chips';
@ -92,7 +92,7 @@ export class DeviceProfileAlarmComponent implements ControlValueAccessor, OnInit
clearRule: [null], clearRule: [null],
propagate: [null], propagate: [null],
propagateRelationTypes: [null] propagateRelationTypes: [null]
}); }, { validators: deviceProfileAlarmValidator });
this.alarmFormGroup.valueChanges.subscribe(() => { this.alarmFormGroup.valueChanges.subscribe(() => {
this.updateModel(); this.updateModel();
}); });

9
ui-ngx/src/app/modules/home/components/profile/alarm/device-profile-alarms.component.ts

@ -30,7 +30,7 @@ import {
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { AppState } from '@app/core/core.state'; import { AppState } from '@app/core/core.state';
import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { coerceBooleanProperty } from '@angular/cdk/coercion';
import { DeviceProfileAlarm } from '@shared/models/device.models'; import { DeviceProfileAlarm, deviceProfileAlarmValidator } from '@shared/models/device.models';
import { guid } from '@core/utils'; import { guid } from '@core/utils';
import { Subscription } from 'rxjs'; import { Subscription } from 'rxjs';
import { MatDialog } from '@angular/material/dialog'; import { MatDialog } from '@angular/material/dialog';
@ -141,7 +141,7 @@ export class DeviceProfileAlarmsComponent implements ControlValueAccessor, OnIni
id: guid(), id: guid(),
alarmType: '', alarmType: '',
createRules: { createRules: {
empty: { CRITICAL: {
condition: { condition: {
condition: [] condition: []
} }
@ -149,8 +149,11 @@ export class DeviceProfileAlarmsComponent implements ControlValueAccessor, OnIni
} }
}; };
const alarmsArray = this.deviceProfileAlarmsFormGroup.get('alarms') as FormArray; const alarmsArray = this.deviceProfileAlarmsFormGroup.get('alarms') as FormArray;
alarmsArray.push(this.fb.control(alarm, [Validators.required])); alarmsArray.push(this.fb.control(alarm, [deviceProfileAlarmValidator]));
this.deviceProfileAlarmsFormGroup.updateValueAndValidity(); this.deviceProfileAlarmsFormGroup.updateValueAndValidity();
if (!this.deviceProfileAlarmsFormGroup.valid) {
this.updateModel();
}
} }
public validate(c: FormControl) { public validate(c: FormControl) {

12
ui-ngx/src/app/modules/home/components/profile/device-profile-dialog.component.html

@ -15,7 +15,7 @@
limitations under the License. limitations under the License.
--> -->
<form (ngSubmit)="save()" style="min-width: 1000px;"> <form (ngSubmit)="save()" style="width: 1000px;">
<mat-toolbar color="primary"> <mat-toolbar color="primary">
<h2>{{ (isAdd ? 'device-profile.add' : 'device-profile.edit' ) | translate }}</h2> <h2>{{ (isAdd ? 'device-profile.add' : 'device-profile.edit' ) | translate }}</h2>
<span fxFlex></span> <span fxFlex></span>
@ -37,11 +37,6 @@
</tb-device-profile> </tb-device-profile>
</div> </div>
<div mat-dialog-actions fxLayoutAlign="end center"> <div mat-dialog-actions fxLayoutAlign="end center">
<button mat-raised-button color="primary"
type="submit"
[disabled]="(isLoading$ | async) || deviceProfileComponent.entityForm?.invalid || !deviceProfileComponent.entityForm?.dirty">
{{ (isAdd ? 'action.add' : 'action.save') | translate }}
</button>
<button mat-button color="primary" <button mat-button color="primary"
type="button" type="button"
cdkFocusInitial cdkFocusInitial
@ -49,5 +44,10 @@
(click)="cancel()"> (click)="cancel()">
{{ 'action.cancel' | translate }} {{ 'action.cancel' | translate }}
</button> </button>
<button mat-raised-button color="primary"
type="submit"
[disabled]="(isLoading$ | async) || deviceProfileComponent.entityForm?.invalid || !deviceProfileComponent.entityForm?.dirty">
{{ (isAdd ? 'action.add' : 'action.save') | translate }}
</button>
</div> </div>
</form> </form>

2
ui-ngx/src/app/modules/home/components/profile/device-profile.component.html

@ -41,7 +41,7 @@
</div> </div>
<div class="mat-padding" fxLayout="column"> <div class="mat-padding" fxLayout="column">
<form [formGroup]="entityForm"> <form [formGroup]="entityForm">
<fieldset [disabled]="(isLoading$ | async) || !isEdit"> <fieldset [disabled]="(isLoading$ | async) || !isEdit" style="min-width: 0;">
<mat-form-field class="mat-block"> <mat-form-field class="mat-block">
<mat-label translate>device-profile.name</mat-label> <mat-label translate>device-profile.name</mat-label>
<input matInput formControlName="name" required/> <input matInput formControlName="name" required/>

12
ui-ngx/src/app/modules/home/components/profile/tenant-profile-dialog.component.html

@ -15,7 +15,7 @@
limitations under the License. limitations under the License.
--> -->
<form (ngSubmit)="save()" style="min-width: 600px;"> <form (ngSubmit)="save()" style="width: 600px;">
<mat-toolbar color="primary"> <mat-toolbar color="primary">
<h2>{{ (isAdd ? 'tenant-profile.add' : 'tenant-profile.edit' ) | translate }}</h2> <h2>{{ (isAdd ? 'tenant-profile.add' : 'tenant-profile.edit' ) | translate }}</h2>
<span fxFlex></span> <span fxFlex></span>
@ -37,11 +37,6 @@
</tb-tenant-profile> </tb-tenant-profile>
</div> </div>
<div mat-dialog-actions fxLayoutAlign="end center"> <div mat-dialog-actions fxLayoutAlign="end center">
<button mat-raised-button color="primary"
type="submit"
[disabled]="(isLoading$ | async) || tenantProfileComponent.entityForm?.invalid || !tenantProfileComponent.entityForm?.dirty">
{{ (isAdd ? 'action.add' : 'action.save') | translate }}
</button>
<button mat-button color="primary" <button mat-button color="primary"
type="button" type="button"
cdkFocusInitial cdkFocusInitial
@ -49,5 +44,10 @@
(click)="cancel()"> (click)="cancel()">
{{ 'action.cancel' | translate }} {{ 'action.cancel' | translate }}
</button> </button>
<button mat-raised-button color="primary"
type="submit"
[disabled]="(isLoading$ | async) || tenantProfileComponent.entityForm?.invalid || !tenantProfileComponent.entityForm?.dirty">
{{ (isAdd ? 'action.add' : 'action.save') | translate }}
</button>
</div> </div>
</form> </form>

11
ui-ngx/src/app/modules/home/components/relation/relation-dialog.component.html

@ -53,17 +53,16 @@
</div> </div>
<div mat-dialog-actions fxLayout="row"> <div mat-dialog-actions fxLayout="row">
<span fxFlex></span> <span fxFlex></span>
<button mat-button mat-raised-button color="primary"
type="submit"
[disabled]="(isLoading$ | async) || relationFormGroup.invalid || !(relationFormGroup.dirty || additionalInfo.dirty)">
{{ (isAdd ? 'action.add' : 'action.save') | translate }}
</button>
<button mat-button color="primary" <button mat-button color="primary"
style="margin-right: 20px;"
type="button" type="button"
[disabled]="(isLoading$ | async)" [disabled]="(isLoading$ | async)"
(click)="cancel()" cdkFocusInitial> (click)="cancel()" cdkFocusInitial>
{{ 'action.cancel' | translate }} {{ 'action.cancel' | translate }}
</button> </button>
<button mat-button mat-raised-button color="primary"
type="submit"
[disabled]="(isLoading$ | async) || relationFormGroup.invalid || !(relationFormGroup.dirty || additionalInfo.dirty)">
{{ (isAdd ? 'action.add' : 'action.save') | translate }}
</button>
</div> </div>
</form> </form>

10
ui-ngx/src/app/modules/home/components/widget/action/widget-action-dialog.component.html

@ -147,16 +147,16 @@
</fieldset> </fieldset>
</div> </div>
<div mat-dialog-actions fxLayoutAlign="end center"> <div mat-dialog-actions fxLayoutAlign="end center">
<button mat-raised-button color="primary"
type="submit"
[disabled]="(isLoading$ | async) || widgetActionFormGroup.invalid || actionTypeFormGroup.invalid || (!widgetActionFormGroup.dirty && !actionTypeFormGroup.dirty)">
{{ (isAdd ? 'action.add' : 'action.save') | translate }}
</button>
<button mat-button color="primary" <button mat-button color="primary"
type="button" type="button"
[disabled]="(isLoading$ | async)" [disabled]="(isLoading$ | async)"
(click)="cancel()" cdkFocusInitial> (click)="cancel()" cdkFocusInitial>
{{ 'action.cancel' | translate }} {{ 'action.cancel' | translate }}
</button> </button>
<button mat-raised-button color="primary"
type="submit"
[disabled]="(isLoading$ | async) || widgetActionFormGroup.invalid || actionTypeFormGroup.invalid || (!widgetActionFormGroup.dirty && !actionTypeFormGroup.dirty)">
{{ (isAdd ? 'action.add' : 'action.save') | translate }}
</button>
</div> </div>
</form> </form>

10
ui-ngx/src/app/modules/home/components/widget/data-key-config-dialog.component.html

@ -38,16 +38,16 @@
</tb-data-key-config> </tb-data-key-config>
</div> </div>
<div mat-dialog-actions fxLayoutAlign="end center"> <div mat-dialog-actions fxLayoutAlign="end center">
<button mat-raised-button color="primary"
type="submit"
[disabled]="(isLoading$ | async) || dataKeyFormGroup.invalid || !dataKeyFormGroup.dirty">
{{ 'action.save' | translate }}
</button>
<button mat-button color="primary" <button mat-button color="primary"
type="button" type="button"
[disabled]="(isLoading$ | async)" [disabled]="(isLoading$ | async)"
(click)="cancel()" cdkFocusInitial> (click)="cancel()" cdkFocusInitial>
{{ 'action.cancel' | translate }} {{ 'action.cancel' | translate }}
</button> </button>
<button mat-raised-button color="primary"
type="submit"
[disabled]="(isLoading$ | async) || dataKeyFormGroup.invalid || !dataKeyFormGroup.dirty">
{{ 'action.save' | translate }}
</button>
</div> </div>
</form> </form>

11
ui-ngx/src/app/modules/home/components/widget/lib/alarm-filter-panel.component.html

@ -50,17 +50,16 @@
</mat-chip-list> </mat-chip-list>
</mat-form-field> </mat-form-field>
<div fxLayout="row" class="tb-panel-actions" fxLayoutAlign="end center"> <div fxLayout="row" class="tb-panel-actions" fxLayoutAlign="end center">
<button type="button"
mat-button
(click)="cancel()">
{{ 'action.cancel' | translate }}
</button>
<button type="submit" <button type="submit"
mat-raised-button mat-raised-button
color="primary" color="primary"
[disabled]="alarmFilterFormGroup.invalid || !alarmFilterFormGroup.dirty"> [disabled]="alarmFilterFormGroup.invalid || !alarmFilterFormGroup.dirty">
{{ 'action.update' | translate }} {{ 'action.update' | translate }}
</button> </button>
<button type="button"
mat-button
(click)="cancel()"
style="margin-right: 20px;">
{{ 'action.cancel' | translate }}
</button>
</div> </div>
</form> </form>

1
ui-ngx/src/app/modules/home/components/widget/lib/date-range-navigator/date-range-navigator-panel.component.html

@ -28,7 +28,6 @@
<div class="tb-panel-actions" fxLayout="row" *ngIf="!settings.autoConfirm"> <div class="tb-panel-actions" fxLayout="row" *ngIf="!settings.autoConfirm">
<span fxFlex></span> <span fxFlex></span>
<button mat-button mat-raised-button color="primary" <button mat-button mat-raised-button color="primary"
style="margin-right: 20px;"
type="button" (click)="apply()"> type="button" (click)="apply()">
{{ 'action.ok' | translate }} {{ 'action.ok' | translate }}
</button> </button>

5
ui-ngx/src/app/modules/home/components/widget/lib/entities-table-widget.component.ts

@ -352,7 +352,8 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni
} }
dataKeys.push(dataKey); dataKeys.push(dataKey);
dataKey.title = this.utils.customTranslation(dataKey.label, dataKey.label); dataKey.label = this.utils.customTranslation(dataKey.label, dataKey.label);
dataKey.title = dataKey.label;
dataKey.def = 'def' + this.columns.length; dataKey.def = 'def' + this.columns.length;
const keySettings: TableWidgetDataKeySettings = dataKey.settings; const keySettings: TableWidgetDataKeySettings = dataKey.settings;
if (dataKey.type === DataKeyType.entityField && if (dataKey.type === DataKeyType.entityField &&
@ -374,7 +375,7 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni
} }
if (this.settings.defaultSortOrder && this.settings.defaultSortOrder.length) { if (this.settings.defaultSortOrder && this.settings.defaultSortOrder.length) {
this.defaultSortOrder = this.settings.defaultSortOrder; this.defaultSortOrder = this.utils.customTranslation(this.settings.defaultSortOrder, this.settings.defaultSortOrder);
} }
this.pageLink.sortOrder = entityDataSortOrderFromString(this.defaultSortOrder, this.columns); this.pageLink.sortOrder = entityDataSortOrderFromString(this.defaultSortOrder, this.columns);

12
ui-ngx/src/app/modules/home/dialogs/add-entities-to-customer-dialog.component.html

@ -40,17 +40,17 @@
</fieldset> </fieldset>
</div> </div>
<div mat-dialog-actions fxLayoutAlign="end center"> <div mat-dialog-actions fxLayoutAlign="end center">
<button mat-raised-button color="primary"
type="submit"
[disabled]="(isLoading$ | async) || addEntitiesToCustomerFormGroup.invalid
|| !addEntitiesToCustomerFormGroup.dirty">
{{ 'action.assign' | translate }}
</button>
<button mat-button color="primary" <button mat-button color="primary"
type="button" type="button"
[disabled]="(isLoading$ | async)" [disabled]="(isLoading$ | async)"
(click)="cancel()" cdkFocusInitial> (click)="cancel()" cdkFocusInitial>
{{ 'action.cancel' | translate }} {{ 'action.cancel' | translate }}
</button> </button>
<button mat-raised-button color="primary"
type="submit"
[disabled]="(isLoading$ | async) || addEntitiesToCustomerFormGroup.invalid
|| !addEntitiesToCustomerFormGroup.dirty">
{{ 'action.assign' | translate }}
</button>
</div> </div>
</form> </form>

12
ui-ngx/src/app/modules/home/dialogs/assign-to-customer-dialog.component.html

@ -39,17 +39,17 @@
</fieldset> </fieldset>
</div> </div>
<div mat-dialog-actions fxLayout="row" fxLayoutAlign="end center"> <div mat-dialog-actions fxLayout="row" fxLayoutAlign="end center">
<button mat-raised-button color="primary"
type="submit"
[disabled]="(isLoading$ | async) || assignToCustomerFormGroup.invalid
|| !assignToCustomerFormGroup.dirty">
{{ 'action.assign' | translate }}
</button>
<button mat-button color="primary" <button mat-button color="primary"
type="button" type="button"
[disabled]="(isLoading$ | async)" [disabled]="(isLoading$ | async)"
(click)="cancel()" cdkFocusInitial> (click)="cancel()" cdkFocusInitial>
{{ 'action.cancel' | translate }} {{ 'action.cancel' | translate }}
</button> </button>
<button mat-raised-button color="primary"
type="submit"
[disabled]="(isLoading$ | async) || assignToCustomerFormGroup.invalid
|| !assignToCustomerFormGroup.dirty">
{{ 'action.assign' | translate }}
</button>
</div> </div>
</form> </form>

31
ui-ngx/src/app/modules/home/pages/admin/admin-routing.module.ts

@ -14,8 +14,8 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { NgModule } from '@angular/core'; import { Injectable, NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router'; import { Resolve, RouterModule, Routes } from '@angular/router';
import { MailServerComponent } from '@modules/home/pages/admin/mail-server.component'; import { MailServerComponent } from '@modules/home/pages/admin/mail-server.component';
import { ConfirmOnExitGuard } from '@core/guards/confirm-on-exit.guard'; import { ConfirmOnExitGuard } from '@core/guards/confirm-on-exit.guard';
@ -23,6 +23,25 @@ import { Authority } from '@shared/models/authority.enum';
import { GeneralSettingsComponent } from '@modules/home/pages/admin/general-settings.component'; import { GeneralSettingsComponent } from '@modules/home/pages/admin/general-settings.component';
import { SecuritySettingsComponent } from '@modules/home/pages/admin/security-settings.component'; import { SecuritySettingsComponent } from '@modules/home/pages/admin/security-settings.component';
import { OAuth2SettingsComponent } from '@home/pages/admin/oauth2-settings.component'; import { OAuth2SettingsComponent } from '@home/pages/admin/oauth2-settings.component';
import { User } from '@shared/models/user.model';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import { UserService } from '@core/http/user.service';
import { Observable } from 'rxjs';
import { getCurrentAuthUser } from '@core/auth/auth.selectors';
import { OAuth2Service } from '@core/http/oauth2.service';
import { UserProfileResolver } from '@home/pages/profile/profile-routing.module';
@Injectable()
export class OAuth2LoginProcessingUrlResolver implements Resolve<string> {
constructor(private oauth2Service: OAuth2Service) {
}
resolve(): Observable<string> {
return this.oauth2Service.getLoginProcessingUrl();
}
}
const routes: Routes = [ const routes: Routes = [
{ {
@ -90,6 +109,9 @@ const routes: Routes = [
label: 'admin.oauth2.oauth2', label: 'admin.oauth2.oauth2',
icon: 'security' icon: 'security'
} }
},
resolve: {
loginProcessingUrl: OAuth2LoginProcessingUrlResolver
} }
} }
] ]
@ -98,6 +120,9 @@ const routes: Routes = [
@NgModule({ @NgModule({
imports: [RouterModule.forChild(routes)], imports: [RouterModule.forChild(routes)],
exports: [RouterModule] exports: [RouterModule],
providers: [
OAuth2LoginProcessingUrlResolver
]
}) })
export class AdminRoutingModule { } export class AdminRoutingModule { }

31
ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.ts

@ -43,6 +43,7 @@ import { DialogService } from '@core/services/dialog.service';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { isDefined, isDefinedAndNotNull } from '@core/utils'; import { isDefined, isDefinedAndNotNull } from '@core/utils';
import { OAuth2Service } from '@core/http/oauth2.service'; import { OAuth2Service } from '@core/http/oauth2.service';
import { ActivatedRoute } from '@angular/router';
@Component({ @Component({
selector: 'tb-oauth2-settings', selector: 'tb-oauth2-settings',
@ -87,7 +88,10 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha
templateProvider = ['Custom']; templateProvider = ['Custom'];
private loginProcessingUrl: string = this.route.snapshot.data.loginProcessingUrl;
constructor(protected store: Store<AppState>, constructor(protected store: Store<AppState>,
private route: ActivatedRoute,
private oauth2Service: OAuth2Service, private oauth2Service: OAuth2Service,
private fb: FormBuilder, private fb: FormBuilder,
private dialogService: DialogService, private dialogService: DialogService,
@ -130,7 +134,7 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha
return this.oauth2SettingsForm.get('domainsParams') as FormArray; return this.oauth2SettingsForm.get('domainsParams') as FormArray;
} }
private formBasicGroup(mapperConfigBasic?: MapperConfigBasic): FormGroup { private formBasicGroup(type: MapperConfigType, mapperConfigBasic?: MapperConfigBasic): FormGroup {
let tenantNamePattern; let tenantNamePattern;
if (mapperConfigBasic?.tenantNamePattern) { if (mapperConfigBasic?.tenantNamePattern) {
tenantNamePattern = mapperConfigBasic.tenantNamePattern; tenantNamePattern = mapperConfigBasic.tenantNamePattern;
@ -138,16 +142,20 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha
tenantNamePattern = {value: null, disabled: true}; tenantNamePattern = {value: null, disabled: true};
} }
const basicGroup = this.fb.group({ const basicGroup = this.fb.group({
emailAttributeKey: [mapperConfigBasic?.emailAttributeKey ? mapperConfigBasic.emailAttributeKey : 'email', Validators.required],
firstNameAttributeKey: [mapperConfigBasic?.firstNameAttributeKey ? mapperConfigBasic.firstNameAttributeKey : ''], firstNameAttributeKey: [mapperConfigBasic?.firstNameAttributeKey ? mapperConfigBasic.firstNameAttributeKey : ''],
lastNameAttributeKey: [mapperConfigBasic?.lastNameAttributeKey ? mapperConfigBasic.lastNameAttributeKey : ''], lastNameAttributeKey: [mapperConfigBasic?.lastNameAttributeKey ? mapperConfigBasic.lastNameAttributeKey : ''],
tenantNameStrategy: [mapperConfigBasic?.tenantNameStrategy ? mapperConfigBasic.tenantNameStrategy : TenantNameStrategy.DOMAIN], tenantNameStrategy: [mapperConfigBasic?.tenantNameStrategy ? mapperConfigBasic.tenantNameStrategy : TenantNameStrategy.DOMAIN],
tenantNamePattern: [tenantNamePattern, Validators.required], tenantNamePattern: [tenantNamePattern, Validators.required],
customerNamePattern: [mapperConfigBasic?.customerNamePattern ? mapperConfigBasic.customerNamePattern : null], customerNamePattern: [mapperConfigBasic?.customerNamePattern ? mapperConfigBasic.customerNamePattern : null],
defaultDashboardName: [mapperConfigBasic?.defaultDashboardName ? mapperConfigBasic.defaultDashboardName : null], defaultDashboardName: [mapperConfigBasic?.defaultDashboardName ? mapperConfigBasic.defaultDashboardName : null],
alwaysFullScreen: [mapperConfigBasic?.alwaysFullScreen ? mapperConfigBasic.alwaysFullScreen : false] alwaysFullScreen: [isDefinedAndNotNull(mapperConfigBasic?.alwaysFullScreen) ? mapperConfigBasic.alwaysFullScreen : false]
}); });
if (MapperConfigType.GITHUB !== type) {
basicGroup.addControl('emailAttributeKey',
this.fb.control( mapperConfigBasic?.emailAttributeKey ? mapperConfigBasic.emailAttributeKey : 'email', Validators.required));
}
this.subscriptions.push(basicGroup.get('tenantNameStrategy').valueChanges.subscribe((domain) => { this.subscriptions.push(basicGroup.get('tenantNameStrategy').valueChanges.subscribe((domain) => {
if (domain === 'CUSTOM') { if (domain === 'CUSTOM') {
basicGroup.get('tenantNamePattern').enable(); basicGroup.get('tenantNamePattern').enable();
@ -279,9 +287,12 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha
clientRegistration?.userNameAttributeName ? clientRegistration.userNameAttributeName : 'email', Validators.required], clientRegistration?.userNameAttributeName ? clientRegistration.userNameAttributeName : 'email', Validators.required],
mapperConfig: this.fb.group({ mapperConfig: this.fb.group({
allowUserCreation: [ allowUserCreation: [
clientRegistration?.mapperConfig?.allowUserCreation ? clientRegistration.mapperConfig.allowUserCreation : true isDefinedAndNotNull(clientRegistration?.mapperConfig?.allowUserCreation) ?
clientRegistration.mapperConfig.allowUserCreation : true
],
activateUser: [
isDefinedAndNotNull(clientRegistration?.mapperConfig?.activateUser) ? clientRegistration.mapperConfig.activateUser : false
], ],
activateUser: [clientRegistration?.mapperConfig?.activateUser ? clientRegistration.mapperConfig.activateUser : false],
type: [ type: [
clientRegistration?.mapperConfig?.type ? clientRegistration.mapperConfig.type : MapperConfigType.BASIC, Validators.required clientRegistration?.mapperConfig?.type ? clientRegistration.mapperConfig.type : MapperConfigType.BASIC, Validators.required
] ]
@ -308,7 +319,7 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha
return clientRegistrationFormGroup; return clientRegistrationFormGroup;
} }
private validateScope (control: AbstractControl): ValidationErrors | null { private validateScope(control: AbstractControl): ValidationErrors | null {
const scope: string[] = control.value; const scope: string[] = control.value;
if (!scope || !scope.length) { if (!scope || !scope.length) {
return { return {
@ -347,7 +358,11 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha
mapperConfig.addControl('custom', this.formCustomGroup(predefinedValue?.custom)); mapperConfig.addControl('custom', this.formCustomGroup(predefinedValue?.custom));
} else { } else {
mapperConfig.removeControl('custom'); mapperConfig.removeControl('custom');
mapperConfig.addControl('basic', this.formBasicGroup(predefinedValue?.basic)); if (mapperConfig.get('basic')) {
mapperConfig.setControl('basic', this.formBasicGroup(type, predefinedValue?.basic));
} else {
mapperConfig.addControl('basic', this.formBasicGroup(type, predefinedValue?.basic));
}
} }
} }
@ -490,7 +505,7 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha
} else { } else {
protocol = domainInfo.scheme === DomainSchema.MIXED ? DomainSchema.HTTPS.toLowerCase() : domainInfo.scheme.toLowerCase(); protocol = domainInfo.scheme === DomainSchema.MIXED ? DomainSchema.HTTPS.toLowerCase() : domainInfo.scheme.toLowerCase();
} }
return `${protocol}://${domainInfo.name}/login/oauth2/code/`; return `${protocol}://${domainInfo.name}${this.loginProcessingUrl}`;
} }
return ''; return '';
} }

10
ui-ngx/src/app/modules/home/pages/dashboard/add-widget-dialog.component.html

@ -41,11 +41,6 @@
</fieldset> </fieldset>
</div> </div>
<div mat-dialog-actions fxLayoutAlign="end center"> <div mat-dialog-actions fxLayoutAlign="end center">
<button mat-raised-button color="primary"
type="submit"
[disabled]="(isLoading$ | async) || widgetFormGroup.invalid">
{{ 'action.add' | translate }}
</button>
<button mat-button color="primary" <button mat-button color="primary"
type="button" type="button"
[disabled]="(isLoading$ | async)" [disabled]="(isLoading$ | async)"
@ -53,5 +48,10 @@
cdkFocusInitial> cdkFocusInitial>
{{ 'action.cancel' | translate }} {{ 'action.cancel' | translate }}
</button> </button>
<button mat-raised-button color="primary"
type="submit"
[disabled]="(isLoading$ | async) || widgetFormGroup.invalid">
{{ 'action.add' | translate }}
</button>
</div> </div>
</form> </form>

12
ui-ngx/src/app/modules/home/pages/dashboard/dashboard-settings-dialog.component.html

@ -152,17 +152,17 @@
</fieldset> </fieldset>
</div> </div>
<div mat-dialog-actions fxLayoutAlign="end center"> <div mat-dialog-actions fxLayoutAlign="end center">
<button mat-raised-button color="primary"
type="submit"
[disabled]="(isLoading$ | async) || settingsFormGroup.invalid || gridSettingsFormGroup.invalid
|| (!settingsFormGroup.dirty && !gridSettingsFormGroup.dirty)">
{{ 'action.save' | translate }}
</button>
<button mat-button color="primary" <button mat-button color="primary"
type="button" type="button"
[disabled]="(isLoading$ | async)" [disabled]="(isLoading$ | async)"
(click)="cancel()" cdkFocusInitial> (click)="cancel()" cdkFocusInitial>
{{ 'action.cancel' | translate }} {{ 'action.cancel' | translate }}
</button> </button>
<button mat-raised-button color="primary"
type="submit"
[disabled]="(isLoading$ | async) || settingsFormGroup.invalid || gridSettingsFormGroup.invalid
|| (!settingsFormGroup.dirty && !gridSettingsFormGroup.dirty)">
{{ 'action.save' | translate }}
</button>
</div> </div>
</form> </form>

10
ui-ngx/src/app/modules/home/pages/dashboard/layout/manage-dashboard-layouts-dialog.component.html

@ -54,11 +54,6 @@
</fieldset> </fieldset>
</div> </div>
<div mat-dialog-actions fxLayoutAlign="end center"> <div mat-dialog-actions fxLayoutAlign="end center">
<button mat-raised-button color="primary"
type="submit"
[disabled]="(isLoading$ | async) || layoutsFormGroup.invalid || !layoutsFormGroup.dirty">
{{ 'action.save' | translate }}
</button>
<button mat-button <button mat-button
color="primary" color="primary"
type="button" type="button"
@ -66,5 +61,10 @@
(click)="cancel()" cdkFocusInitial> (click)="cancel()" cdkFocusInitial>
{{ 'action.cancel' | translate }} {{ 'action.cancel' | translate }}
</button> </button>
<button mat-raised-button color="primary"
type="submit"
[disabled]="(isLoading$ | async) || layoutsFormGroup.invalid || !layoutsFormGroup.dirty">
{{ 'action.save' | translate }}
</button>
</div> </div>
</form> </form>

1
ui-ngx/src/app/modules/home/pages/dashboard/make-dashboard-public-dialog.component.html

@ -54,7 +54,6 @@
<div mat-dialog-actions fxLayout="row"> <div mat-dialog-actions fxLayout="row">
<span fxFlex></span> <span fxFlex></span>
<button mat-button color="primary" <button mat-button color="primary"
style="margin-right: 20px;"
type="button" type="button"
[disabled]="(isLoading$ | async)" [disabled]="(isLoading$ | async)"
(click)="close()" cdkFocusInitial> (click)="close()" cdkFocusInitial>

13
ui-ngx/src/app/modules/home/pages/dashboard/manage-dashboard-customers-dialog.component.html

@ -41,18 +41,17 @@
</div> </div>
<div mat-dialog-actions fxLayout="row"> <div mat-dialog-actions fxLayout="row">
<span fxFlex></span> <span fxFlex></span>
<button mat-button mat-raised-button color="primary"
type="submit"
[disabled]="(isLoading$ | async) || dashboardCustomersFormGroup.invalid
|| !dashboardCustomersFormGroup.dirty">
{{ actionName | translate }}
</button>
<button mat-button color="primary" <button mat-button color="primary"
style="margin-right: 20px;"
type="button" type="button"
[disabled]="(isLoading$ | async)" [disabled]="(isLoading$ | async)"
(click)="cancel()" cdkFocusInitial> (click)="cancel()" cdkFocusInitial>
{{ 'action.cancel' | translate }} {{ 'action.cancel' | translate }}
</button> </button>
<button mat-button mat-raised-button color="primary"
type="submit"
[disabled]="(isLoading$ | async) || dashboardCustomersFormGroup.invalid
|| !dashboardCustomersFormGroup.dirty">
{{ actionName | translate }}
</button>
</div> </div>
</form> </form>

10
ui-ngx/src/app/modules/home/pages/dashboard/states/dashboard-state-dialog.component.html

@ -52,16 +52,16 @@
</fieldset> </fieldset>
</div> </div>
<div mat-dialog-actions fxLayoutAlign="end center"> <div mat-dialog-actions fxLayoutAlign="end center">
<button mat-raised-button color="primary"
type="submit"
[disabled]="(isLoading$ | async) || stateFormGroup.invalid || !stateFormGroup.dirty">
{{ (isAdd ? 'action.add' : 'action.save') | translate }}
</button>
<button mat-button color="primary" <button mat-button color="primary"
type="button" type="button"
[disabled]="(isLoading$ | async)" [disabled]="(isLoading$ | async)"
(click)="cancel()" cdkFocusInitial> (click)="cancel()" cdkFocusInitial>
{{ 'action.cancel' | translate }} {{ 'action.cancel' | translate }}
</button> </button>
<button mat-raised-button color="primary"
type="submit"
[disabled]="(isLoading$ | async) || stateFormGroup.invalid || !stateFormGroup.dirty">
{{ (isAdd ? 'action.add' : 'action.save') | translate }}
</button>
</div> </div>
</form> </form>

10
ui-ngx/src/app/modules/home/pages/dashboard/states/manage-dashboard-states-dialog.component.html

@ -136,16 +136,16 @@
</fieldset> </fieldset>
</div> </div>
<div mat-dialog-actions fxLayoutAlign="end center"> <div mat-dialog-actions fxLayoutAlign="end center">
<button mat-raised-button color="primary"
type="submit"
[disabled]="(isLoading$ | async) || statesFormGroup.invalid || !statesFormGroup.dirty">
{{ 'action.save' | translate }}
</button>
<button mat-button color="primary" <button mat-button color="primary"
type="button" type="button"
[disabled]="(isLoading$ | async)" [disabled]="(isLoading$ | async)"
(click)="cancel()" cdkFocusInitial> (click)="cancel()" cdkFocusInitial>
{{ 'action.cancel' | translate }} {{ 'action.cancel' | translate }}
</button> </button>
<button mat-raised-button color="primary"
type="submit"
[disabled]="(isLoading$ | async) || statesFormGroup.invalid || !statesFormGroup.dirty">
{{ 'action.save' | translate }}
</button>
</div> </div>
</form> </form>

2
ui-ngx/src/app/modules/home/pages/device-profile/device-profile-tabs.component.html

@ -70,7 +70,7 @@
</div> </div>
</div> </div>
</mat-tab> </mat-tab>
<mat-tab *ngIf="entity" <mat-tab *ngIf="entity && !isEdit"
label="{{ 'audit-log.audit-logs' | translate }}" #auditLogsTab="matTab"> label="{{ 'audit-log.audit-logs' | translate }}" #auditLogsTab="matTab">
<tb-audit-log-table detailsMode="true" [active]="auditLogsTab.isActive" [auditLogMode]="auditLogModes.ENTITY" [entityId]="entity.id"></tb-audit-log-table> <tb-audit-log-table detailsMode="true" [active]="auditLogsTab.isActive" [auditLogMode]="auditLogModes.ENTITY" [entityId]="entity.id"></tb-audit-log-table>
</mat-tab> </mat-tab>

12
ui-ngx/src/app/modules/home/pages/device/device-credentials-dialog.component.html

@ -36,17 +36,17 @@
</fieldset> </fieldset>
</div> </div>
<div mat-dialog-actions fxLayoutAlign="end center"> <div mat-dialog-actions fxLayoutAlign="end center">
<button *ngIf="!isReadOnly" mat-raised-button color="primary"
type="submit"
[disabled]="(isLoading$ | async) || deviceCredentialsFormGroup.invalid
|| !deviceCredentialsFormGroup.dirty">
{{ 'action.save' | translate }}
</button>
<button mat-button color="primary" <button mat-button color="primary"
type="button" type="button"
[disabled]="(isLoading$ | async)" [disabled]="(isLoading$ | async)"
(click)="cancel()" cdkFocusInitial> (click)="cancel()" cdkFocusInitial>
{{ (isReadOnly ? 'action.close' : 'action.cancel') | translate }} {{ (isReadOnly ? 'action.close' : 'action.cancel') | translate }}
</button> </button>
<button *ngIf="!isReadOnly" mat-raised-button color="primary"
type="submit"
[disabled]="(isLoading$ | async) || deviceCredentialsFormGroup.invalid
|| !deviceCredentialsFormGroup.dirty">
{{ 'action.save' | translate }}
</button>
</div> </div>
</form> </form>

10
ui-ngx/src/app/modules/home/pages/profile/change-password-dialog.component.html

@ -46,16 +46,16 @@
</mat-form-field> </mat-form-field>
</div> </div>
<div mat-dialog-actions fxLayout="row" fxLayoutAlign="end center"> <div mat-dialog-actions fxLayout="row" fxLayoutAlign="end center">
<button mat-raised-button color="primary"
type="submit"
[disabled]="(isLoading$ | async) || changePassword.invalid">
{{ 'profile.change-password' | translate }}
</button>
<button mat-button color="primary" <button mat-button color="primary"
type="button" type="button"
[disabled]="(isLoading$ | async)" [disabled]="(isLoading$ | async)"
[mat-dialog-close]="false" cdkFocusInitial> [mat-dialog-close]="false" cdkFocusInitial>
{{ 'action.cancel' | translate }} {{ 'action.cancel' | translate }}
</button> </button>
<button mat-raised-button color="primary"
type="submit"
[disabled]="(isLoading$ | async) || changePassword.invalid">
{{ 'profile.change-password' | translate }}
</button>
</div> </div>
</form> </form>

10
ui-ngx/src/app/modules/home/pages/rulechain/add-rule-node-dialog.component.html

@ -40,16 +40,16 @@
</fieldset> </fieldset>
</div> </div>
<div mat-dialog-actions fxLayoutAlign="end center"> <div mat-dialog-actions fxLayoutAlign="end center">
<button mat-raised-button color="primary"
type="submit"
[disabled]="(isLoading$ | async) || tbRuleNode.ruleNodeFormGroup.invalid || !tbRuleNode.ruleNodeFormGroup.dirty">
{{ 'action.add' | translate }}
</button>
<button mat-button color="primary" <button mat-button color="primary"
type="button" type="button"
[disabled]="(isLoading$ | async)" [disabled]="(isLoading$ | async)"
(click)="cancel()" cdkFocusInitial> (click)="cancel()" cdkFocusInitial>
{{ 'action.cancel' | translate }} {{ 'action.cancel' | translate }}
</button> </button>
<button mat-raised-button color="primary"
type="submit"
[disabled]="(isLoading$ | async) || tbRuleNode.ruleNodeFormGroup.invalid || !tbRuleNode.ruleNodeFormGroup.dirty">
{{ 'action.add' | translate }}
</button>
</div> </div>
</form> </form>

10
ui-ngx/src/app/modules/home/pages/rulechain/add-rule-node-link-dialog.component.html

@ -39,16 +39,16 @@
</fieldset> </fieldset>
</div> </div>
<div mat-dialog-actions fxLayoutAlign="end center"> <div mat-dialog-actions fxLayoutAlign="end center">
<button mat-raised-button color="primary"
type="submit"
[disabled]="(isLoading$ | async) || ruleNodeLinkFormGroup.invalid || !ruleNodeLinkFormGroup.dirty">
{{ 'action.add' | translate }}
</button>
<button mat-button color="primary" <button mat-button color="primary"
type="button" type="button"
[disabled]="(isLoading$ | async)" [disabled]="(isLoading$ | async)"
(click)="cancel()" cdkFocusInitial> (click)="cancel()" cdkFocusInitial>
{{ 'action.cancel' | translate }} {{ 'action.cancel' | translate }}
</button> </button>
<button mat-raised-button color="primary"
type="submit"
[disabled]="(isLoading$ | async) || ruleNodeLinkFormGroup.invalid || !ruleNodeLinkFormGroup.dirty">
{{ 'action.add' | translate }}
</button>
</div> </div>
</form> </form>

10
ui-ngx/src/app/modules/home/pages/user/add-user-dialog.component.html

@ -41,11 +41,6 @@
</mat-form-field> </mat-form-field>
</div> </div>
<div mat-dialog-actions fxLayoutAlign="end center"> <div mat-dialog-actions fxLayoutAlign="end center">
<button mat-raised-button color="primary"
type="submit"
[disabled]="(isLoading$ | async) || detailsForm.invalid || !detailsForm.dirty">
{{ 'action.add' | translate }}
</button>
<button mat-button color="primary" <button mat-button color="primary"
type="button" type="button"
cdkFocusInitial cdkFocusInitial
@ -53,5 +48,10 @@
(click)="cancel()"> (click)="cancel()">
{{ 'action.cancel' | translate }} {{ 'action.cancel' | translate }}
</button> </button>
<button mat-raised-button color="primary"
type="submit"
[disabled]="(isLoading$ | async) || detailsForm.invalid || !detailsForm.dirty">
{{ 'action.add' | translate }}
</button>
</div> </div>
</form> </form>

12
ui-ngx/src/app/modules/home/pages/widget/save-widget-type-as-dialog.component.html

@ -46,17 +46,17 @@
</fieldset> </fieldset>
</div> </div>
<div mat-dialog-actions fxLayoutAlign="end center"> <div mat-dialog-actions fxLayoutAlign="end center">
<button mat-raised-button color="primary"
type="submit"
[disabled]="(isLoading$ | async) || saveWidgetTypeAsFormGroup.invalid
|| !saveWidgetTypeAsFormGroup.dirty">
{{ 'action.saveAs' | translate }}
</button>
<button mat-button color="primary" <button mat-button color="primary"
type="button" type="button"
[disabled]="(isLoading$ | async)" [disabled]="(isLoading$ | async)"
(click)="cancel()" cdkFocusInitial> (click)="cancel()" cdkFocusInitial>
{{ 'action.cancel' | translate }} {{ 'action.cancel' | translate }}
</button> </button>
<button mat-raised-button color="primary"
type="submit"
[disabled]="(isLoading$ | async) || saveWidgetTypeAsFormGroup.invalid
|| !saveWidgetTypeAsFormGroup.dirty">
{{ 'action.saveAs' | translate }}
</button>
</div> </div>
</form> </form>

1
ui-ngx/src/app/shared/components/dialog/color-picker-dialog.component.html

@ -34,7 +34,6 @@
</button> </button>
<button mat-button <button mat-button
type="submit" type="submit"
style="margin-right: 20px;"
[disabled]="(isLoading$ | async) || colorPickerFormGroup.invalid || !colorPickerFormGroup.dirty"> [disabled]="(isLoading$ | async) || colorPickerFormGroup.invalid || !colorPickerFormGroup.dirty">
{{ 'action.select' | translate }} {{ 'action.select' | translate }}
</button> </button>

11
ui-ngx/src/app/shared/components/dialog/json-object-edit-dialog.component.html

@ -41,17 +41,16 @@
</div> </div>
<div mat-dialog-actions fxLayout="row" fxLayoutAlign="end center"> <div mat-dialog-actions fxLayout="row" fxLayoutAlign="end center">
<span fxFlex></span> <span fxFlex></span>
<button mat-button mat-raised-button color="primary"
type="submit"
[disabled]="(isLoading$ | async) || jsonFormGroup.invalid || !jsonFormGroup.dirty">
{{ 'action.save' | translate }}
</button>
<button mat-button color="primary" <button mat-button color="primary"
style="margin-right: 20px;"
type="button" type="button"
[disabled]="(isLoading$ | async)" [disabled]="(isLoading$ | async)"
(click)="cancel()" cdkFocusInitial> (click)="cancel()" cdkFocusInitial>
{{ 'action.cancel' | translate }} {{ 'action.cancel' | translate }}
</button> </button>
<button mat-button mat-raised-button color="primary"
type="submit"
[disabled]="(isLoading$ | async) || jsonFormGroup.invalid || !jsonFormGroup.dirty">
{{ 'action.save' | translate }}
</button>
</div> </div>
</form> </form>

11
ui-ngx/src/app/shared/components/dialog/node-script-test-dialog.component.html

@ -109,18 +109,17 @@
{{ 'rulenode.test' | translate }} {{ 'rulenode.test' | translate }}
</button> </button>
<span fxFlex></span> <span fxFlex></span>
<button mat-button mat-raised-button color="primary"
type="submit"
[disabled]="(isLoading$ | async) || nodeScriptTestFormGroup.get('script').invalid || !nodeScriptTestFormGroup.get('script').dirty">
{{ 'action.save' | translate }}
</button>
<button mat-button color="primary" <button mat-button color="primary"
style="margin-right: 20px;"
type="button" type="button"
cdkFocusInitial cdkFocusInitial
[disabled]="(isLoading$ | async)" [disabled]="(isLoading$ | async)"
(click)="cancel()"> (click)="cancel()">
{{ 'action.cancel' | translate }} {{ 'action.cancel' | translate }}
</button> </button>
<button mat-button mat-raised-button color="primary"
type="submit"
[disabled]="(isLoading$ | async) || nodeScriptTestFormGroup.get('script').invalid || !nodeScriptTestFormGroup.get('script').dirty">
{{ 'action.save' | translate }}
</button>
</div> </div>
</form> </form>

13
ui-ngx/src/app/shared/components/time/timewindow-panel.component.html

@ -140,19 +140,18 @@
</tb-timeinterval> </tb-timeinterval>
</div> </div>
<div fxLayout="row" class="tb-panel-actions" fxLayoutAlign="end center"> <div fxLayout="row" class="tb-panel-actions" fxLayoutAlign="end center">
<button type="button"
mat-button
[disabled]="(isLoading$ | async)"
(click)="cancel()">
{{ 'action.cancel' | translate }}
</button>
<button type="submit" <button type="submit"
mat-raised-button mat-raised-button
color="primary" color="primary"
[disabled]="(isLoading$ | async) || timewindowForm.invalid || !timewindowForm.dirty"> [disabled]="(isLoading$ | async) || timewindowForm.invalid || !timewindowForm.dirty">
{{ 'action.update' | translate }} {{ 'action.update' | translate }}
</button> </button>
<button type="button"
mat-button
[disabled]="(isLoading$ | async)"
(click)="cancel()"
style="margin-right: 20px;">
{{ 'action.cancel' | translate }}
</button>
</div> </div>
</div> </div>
</fieldset> </fieldset>

44
ui-ngx/src/app/shared/models/device.models.ts

@ -26,7 +26,7 @@ import { EntityInfoData } from '@shared/models/entity.models';
import { KeyFilter } from '@shared/models/query/query.models'; import { KeyFilter } from '@shared/models/query/query.models';
import { TimeUnit } from '@shared/models/time/time.models'; import { TimeUnit } from '@shared/models/time/time.models';
import * as _moment from 'moment-timezone'; import * as _moment from 'moment-timezone';
import { AbstractControl, FormGroup } from '@angular/forms'; import { AbstractControl, FormGroup, ValidationErrors } from '@angular/forms';
export enum DeviceProfileType { export enum DeviceProfileType {
DEFAULT = 'DEFAULT' DEFAULT = 'DEFAULT'
@ -87,7 +87,7 @@ export const deviceProvisionTypeTranslationMap = new Map<DeviceProvisionType, st
[DeviceProvisionType.ALLOW_CREATE_NEW_DEVICES, 'device-profile.provision-strategy-created-new'], [DeviceProvisionType.ALLOW_CREATE_NEW_DEVICES, 'device-profile.provision-strategy-created-new'],
[DeviceProvisionType.CHECK_PRE_PROVISIONED_DEVICES, 'device-profile.provision-strategy-check-pre-provisioned'] [DeviceProvisionType.CHECK_PRE_PROVISIONED_DEVICES, 'device-profile.provision-strategy-check-pre-provisioned']
] ]
) );
export const deviceTransportTypeHintMap = new Map<DeviceTransportType, string>( export const deviceTransportTypeHintMap = new Map<DeviceTransportType, string>(
[ [
@ -303,6 +303,18 @@ export interface AlarmRule {
schedule?: AlarmSchedule; schedule?: AlarmSchedule;
} }
export function alarmRuleValidator(control: AbstractControl): ValidationErrors | null {
const alarmRule: AlarmRule = control.value;
return alarmRuleValid(alarmRule) ? null : {alarmRule: true};
}
function alarmRuleValid(alarmRule: AlarmRule): boolean {
if (!alarmRule || !alarmRule.condition || !alarmRule.condition.condition || !alarmRule.condition.condition.length) {
return false;
}
return true;
}
export interface DeviceProfileAlarm { export interface DeviceProfileAlarm {
id: string; id: string;
alarmType: string; alarmType: string;
@ -312,6 +324,34 @@ export interface DeviceProfileAlarm {
propagateRelationTypes?: Array<string>; propagateRelationTypes?: Array<string>;
} }
export function deviceProfileAlarmValidator(control: AbstractControl): ValidationErrors | null {
const deviceProfileAlarm: DeviceProfileAlarm = control.value;
if (deviceProfileAlarm && deviceProfileAlarm.id && deviceProfileAlarm.alarmType &&
deviceProfileAlarm.createRules) {
const severities = Object.keys(deviceProfileAlarm.createRules);
if (severities.length) {
let alarmRulesValid = true;
for (const severity of severities) {
const alarmRule = deviceProfileAlarm.createRules[severity];
if (!alarmRuleValid(alarmRule)) {
alarmRulesValid = false;
break;
}
}
if (alarmRulesValid) {
if (deviceProfileAlarm.clearRule && !alarmRuleValid(deviceProfileAlarm.clearRule)) {
alarmRulesValid = false;
}
}
if (alarmRulesValid) {
return null;
}
}
}
return {deviceProfileAlarm: true};
}
export interface DeviceProfileData { export interface DeviceProfileData {
configuration: DeviceProfileConfiguration; configuration: DeviceProfileConfiguration;
transportConfiguration: DeviceProfileTransportConfiguration; transportConfiguration: DeviceProfileTransportConfiguration;

1
ui-ngx/src/assets/locale/locale.constant-en_US.json

@ -908,6 +908,7 @@
"create-alarm-pattern": "Create <b>{{alarmType}}</b> alarm", "create-alarm-pattern": "Create <b>{{alarmType}}</b> alarm",
"create-alarm-rules": "Create alarm rules", "create-alarm-rules": "Create alarm rules",
"no-create-alarm-rules": "No create conditions configured", "no-create-alarm-rules": "No create conditions configured",
"add-create-alarm-rule-prompt": "Please add create alarm rule",
"clear-alarm-rule": "Clear alarm rule", "clear-alarm-rule": "Clear alarm rule",
"no-clear-alarm-rule": "No clear condition configured", "no-clear-alarm-rule": "No clear condition configured",
"add-create-alarm-rule": "Add create condition", "add-create-alarm-rule": "Add create condition",

3
ui-ngx/src/styles.scss

@ -331,6 +331,9 @@ pre.tb-highlight {
font-weight: 400; font-weight: 400;
line-height: 18px; line-height: 18px;
color: rgba(0, 0, 0, .38); color: rgba(0, 0, 0, .38);
&.required {
color: rgb(221, 44, 0);
}
} }
.tb-fullscreen { .tb-fullscreen {

Loading…
Cancel
Save