Major refactoring for mobile app notifications
This commit is contained in:
parent
b256f3a8cd
commit
7bf58e3891
@ -25,6 +25,7 @@ import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.security.core.annotation.AuthenticationPrincipal;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
@ -41,6 +42,7 @@ import org.thingsboard.rule.engine.api.MailService;
|
||||
import org.thingsboard.server.common.data.EntityType;
|
||||
import org.thingsboard.server.common.data.User;
|
||||
import org.thingsboard.server.common.data.UserEmailInfo;
|
||||
import org.thingsboard.server.common.data.UserMobileInfo;
|
||||
import org.thingsboard.server.common.data.alarm.Alarm;
|
||||
import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
|
||||
import org.thingsboard.server.common.data.exception.ThingsboardException;
|
||||
@ -81,6 +83,7 @@ import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import static org.thingsboard.server.common.data.query.EntityKeyType.ENTITY_FIELD;
|
||||
import static org.thingsboard.server.controller.ControllerConstants.ALARM_ID_PARAM_DESCRIPTION;
|
||||
@ -584,6 +587,20 @@ public class UserController extends BaseController {
|
||||
return userSettingsService.reportUserDashboardAction(currentUser.getTenantId(), currentUser.getId(), dashboardId, action);
|
||||
}
|
||||
|
||||
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
|
||||
@GetMapping("/user/mobile/info")
|
||||
public UserMobileInfo getMobileInfo(@AuthenticationPrincipal SecurityUser securityUser) {
|
||||
return Optional.ofNullable(userService.findMobileInfo(securityUser.getTenantId(), securityUser.getId()))
|
||||
.orElseGet(UserMobileInfo::new);
|
||||
}
|
||||
|
||||
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
|
||||
@PostMapping("/user/mobile/info")
|
||||
public void saveMobileInfo(@RequestBody UserMobileInfo mobileInfo,
|
||||
@AuthenticationPrincipal SecurityUser securityUser) {
|
||||
userService.saveMobileInfo(securityUser.getTenantId(), securityUser.getId(), mobileInfo);
|
||||
}
|
||||
|
||||
private void checkNotReserved(String strType, UserSettingsType type) throws ThingsboardException {
|
||||
if (type.isReserved()) {
|
||||
throw new ThingsboardException("Settings with type: " + strType + " are reserved for internal use!", ThingsboardErrorCode.BAD_REQUEST_PARAMS);
|
||||
|
||||
@ -58,7 +58,7 @@ public class NotificationProcessingContext {
|
||||
|
||||
@Builder
|
||||
public NotificationProcessingContext(TenantId tenantId, NotificationRequest request, Set<NotificationDeliveryMethod> deliveryMethods,
|
||||
NotificationTemplate template, NotificationSettings settings) {
|
||||
NotificationTemplate template, NotificationSettings settings) {
|
||||
this.tenantId = tenantId;
|
||||
this.request = request;
|
||||
this.deliveryMethods = deliveryMethods;
|
||||
|
||||
@ -15,54 +15,65 @@
|
||||
*/
|
||||
package org.thingsboard.server.service.notification.channels;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.google.firebase.messaging.FirebaseMessagingException;
|
||||
import com.google.firebase.messaging.MessagingErrorCode;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.thingsboard.rule.engine.api.notification.FirebaseService;
|
||||
import org.thingsboard.server.common.data.User;
|
||||
import org.thingsboard.server.common.data.UserMobileInfo;
|
||||
import org.thingsboard.server.common.data.id.TenantId;
|
||||
import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod;
|
||||
import org.thingsboard.server.common.data.notification.settings.MobileNotificationDeliveryMethodConfig;
|
||||
import org.thingsboard.server.common.data.notification.settings.MobileAppNotificationDeliveryMethodConfig;
|
||||
import org.thingsboard.server.common.data.notification.settings.NotificationSettings;
|
||||
import org.thingsboard.server.common.data.notification.template.MobileDeliveryMethodNotificationTemplate;
|
||||
import org.thingsboard.server.common.data.notification.template.MobileAppDeliveryMethodNotificationTemplate;
|
||||
import org.thingsboard.server.dao.notification.NotificationSettingsService;
|
||||
import org.thingsboard.server.dao.user.UserService;
|
||||
import org.thingsboard.server.service.notification.NotificationProcessingContext;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class MobileNotificationChannel implements NotificationChannel<User, MobileDeliveryMethodNotificationTemplate> {
|
||||
public class MobileAppNotificationChannel implements NotificationChannel<User, MobileAppDeliveryMethodNotificationTemplate> {
|
||||
|
||||
private final FirebaseService firebaseService;
|
||||
private final UserService userService;
|
||||
private final NotificationSettingsService notificationSettingsService;
|
||||
|
||||
@Override
|
||||
public void sendNotification(User recipient, MobileDeliveryMethodNotificationTemplate processedTemplate, NotificationProcessingContext ctx) throws Exception {
|
||||
String fcmToken = Optional.ofNullable(recipient.getAdditionalInfo())
|
||||
.map(info -> info.get("fcmToken")).filter(JsonNode::isTextual).map(JsonNode::asText)
|
||||
.orElse(null);
|
||||
if (StringUtils.isEmpty(fcmToken)) {
|
||||
throw new RuntimeException("User doesn't have the mobile app installed");
|
||||
}
|
||||
public void sendNotification(User recipient, MobileAppDeliveryMethodNotificationTemplate processedTemplate, NotificationProcessingContext ctx) throws Exception {
|
||||
UserMobileInfo mobileInfo = userService.findMobileInfo(recipient.getTenantId(), recipient.getId());
|
||||
String fcmToken = Optional.ofNullable(mobileInfo)
|
||||
.map(UserMobileInfo::getFcmToken)
|
||||
.orElseThrow(() -> new IllegalArgumentException("User doesn't use the mobile app"));
|
||||
|
||||
MobileNotificationDeliveryMethodConfig config = ctx.getDeliveryMethodConfig(NotificationDeliveryMethod.MOBILE);
|
||||
firebaseService.sendMessage(ctx.getTenantId(), config.getFirebaseServiceAccountCredentials(),
|
||||
fcmToken, processedTemplate.getSubject(), processedTemplate.getBody());
|
||||
MobileAppNotificationDeliveryMethodConfig config = ctx.getDeliveryMethodConfig(NotificationDeliveryMethod.MOBILE_APP);
|
||||
try {
|
||||
firebaseService.sendMessage(ctx.getTenantId(), config.getFirebaseServiceAccountCredentials(),
|
||||
fcmToken, processedTemplate.getSubject(), processedTemplate.getBody());
|
||||
} catch (FirebaseMessagingException e) {
|
||||
if (e.getMessagingErrorCode() == MessagingErrorCode.UNREGISTERED) {
|
||||
// the token is no longer valid
|
||||
mobileInfo.setFcmToken(null);
|
||||
userService.saveMobileInfo(recipient.getTenantId(), recipient.getId(), mobileInfo);
|
||||
throw new IllegalArgumentException("User doesn't use the mobile app");
|
||||
}
|
||||
throw new RuntimeException("Failed to send message via FCM: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void check(TenantId tenantId) throws Exception {
|
||||
NotificationSettings settings = notificationSettingsService.findNotificationSettings(tenantId);
|
||||
if (!settings.getDeliveryMethodsConfigs().containsKey(NotificationDeliveryMethod.MOBILE)) {
|
||||
if (!settings.getDeliveryMethodsConfigs().containsKey(NotificationDeliveryMethod.MOBILE_APP)) {
|
||||
throw new RuntimeException("Push-notifications to mobile are not configured");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public NotificationDeliveryMethod getDeliveryMethod() {
|
||||
return NotificationDeliveryMethod.MOBILE;
|
||||
return NotificationDeliveryMethod.MOBILE_APP;
|
||||
}
|
||||
|
||||
}
|
||||
@ -48,7 +48,7 @@ public class DefaultFirebaseService implements FirebaseService {
|
||||
.build();
|
||||
|
||||
@Override
|
||||
public void sendMessage(TenantId tenantId, String credentials, String fcmToken, String title, String body) {
|
||||
public void sendMessage(TenantId tenantId, String credentials, String fcmToken, String title, String body) throws FirebaseMessagingException {
|
||||
FirebaseContext firebaseContext = contexts.asMap().compute(tenantId.toString(), (key, context) -> {
|
||||
if (context == null) {
|
||||
return new FirebaseContext(key, credentials);
|
||||
@ -65,11 +65,7 @@ public class DefaultFirebaseService implements FirebaseService {
|
||||
.build())
|
||||
.setToken(fcmToken)
|
||||
.build();
|
||||
try {
|
||||
firebaseContext.getMessaging().send(message);
|
||||
} catch (FirebaseMessagingException e) {
|
||||
throw new RuntimeException("Failed to send message via FCM: " + e.getMessage(), e);
|
||||
}
|
||||
firebaseContext.getMessaging().send(message);
|
||||
}
|
||||
|
||||
public static class FirebaseContext {
|
||||
|
||||
@ -17,6 +17,7 @@ package org.thingsboard.server.dao.user;
|
||||
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import org.thingsboard.server.common.data.User;
|
||||
import org.thingsboard.server.common.data.UserMobileInfo;
|
||||
import org.thingsboard.server.common.data.id.CustomerId;
|
||||
import org.thingsboard.server.common.data.id.TenantId;
|
||||
import org.thingsboard.server.common.data.id.TenantProfileId;
|
||||
@ -89,4 +90,8 @@ public interface UserService extends EntityDaoService {
|
||||
|
||||
void setLastLoginTs(TenantId tenantId, UserId userId);
|
||||
|
||||
void saveMobileInfo(TenantId tenantId, UserId userId, UserMobileInfo mobileInfo);
|
||||
|
||||
UserMobileInfo findMobileInfo(TenantId tenantId, UserId userId);
|
||||
|
||||
}
|
||||
|
||||
@ -0,0 +1,24 @@
|
||||
/**
|
||||
* Copyright © 2016-2023 The Thingsboard Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.thingsboard.server.common.data;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class UserMobileInfo {
|
||||
private String fcmToken;
|
||||
private long fcmTokenTimestamp;
|
||||
}
|
||||
@ -26,9 +26,10 @@ public enum NotificationDeliveryMethod {
|
||||
SMS("SMS"),
|
||||
SLACK("Slack"),
|
||||
MICROSOFT_TEAMS("Microsoft Teams"),
|
||||
MOBILE("mobile");
|
||||
MOBILE_APP("mobile app");
|
||||
|
||||
@Getter
|
||||
private final String name;
|
||||
// TODO: private final boolean allowUseSystemSettings;
|
||||
|
||||
}
|
||||
|
||||
@ -21,7 +21,7 @@ import org.thingsboard.server.common.data.notification.NotificationDeliveryMetho
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
|
||||
@Data
|
||||
public class MobileNotificationDeliveryMethodConfig implements NotificationDeliveryMethodConfig {
|
||||
public class MobileAppNotificationDeliveryMethodConfig implements NotificationDeliveryMethodConfig {
|
||||
|
||||
private String firebaseServiceAccountCredentialsFileName;
|
||||
@NotEmpty
|
||||
@ -29,7 +29,7 @@ public class MobileNotificationDeliveryMethodConfig implements NotificationDeliv
|
||||
|
||||
@Override
|
||||
public NotificationDeliveryMethod getMethod() {
|
||||
return NotificationDeliveryMethod.MOBILE;
|
||||
return NotificationDeliveryMethod.MOBILE_APP;
|
||||
}
|
||||
|
||||
}
|
||||
@ -28,7 +28,7 @@ import java.io.Serializable;
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "method")
|
||||
@JsonSubTypes({
|
||||
@Type(name = "SLACK", value = SlackNotificationDeliveryMethodConfig.class),
|
||||
@Type(name = "MOBILE", value = MobileNotificationDeliveryMethodConfig.class)
|
||||
@Type(name = "MOBILE_APP", value = MobileAppNotificationDeliveryMethodConfig.class)
|
||||
})
|
||||
public interface NotificationDeliveryMethodConfig extends Serializable {
|
||||
|
||||
|
||||
@ -28,7 +28,8 @@ public class NotificationSettings implements Serializable {
|
||||
|
||||
@NotNull
|
||||
@Valid
|
||||
// location on the screen, shown notifications count, timings of displaying
|
||||
private Map<NotificationDeliveryMethod, NotificationDeliveryMethodConfig> deliveryMethodsConfigs;
|
||||
|
||||
// TODO: disable option, location on the screen, shown notifications count, timings of displaying
|
||||
|
||||
}
|
||||
|
||||
@ -25,7 +25,7 @@ import java.util.Set;
|
||||
@RequiredArgsConstructor
|
||||
public enum NotificationTargetType {
|
||||
|
||||
PLATFORM_USERS(Set.of(NotificationDeliveryMethod.WEB, NotificationDeliveryMethod.EMAIL, NotificationDeliveryMethod.SMS, NotificationDeliveryMethod.MOBILE)),
|
||||
PLATFORM_USERS(Set.of(NotificationDeliveryMethod.WEB, NotificationDeliveryMethod.EMAIL, NotificationDeliveryMethod.SMS, NotificationDeliveryMethod.MOBILE_APP)),
|
||||
SLACK(Set.of(NotificationDeliveryMethod.SLACK)),
|
||||
MICROSOFT_TEAMS(Set.of(NotificationDeliveryMethod.MICROSOFT_TEAMS));
|
||||
|
||||
|
||||
@ -35,7 +35,7 @@ import java.util.List;
|
||||
@Type(name = "SMS", value = SmsDeliveryMethodNotificationTemplate.class),
|
||||
@Type(name = "SLACK", value = SlackDeliveryMethodNotificationTemplate.class),
|
||||
@Type(name = "MICROSOFT_TEAMS", value = MicrosoftTeamsDeliveryMethodNotificationTemplate.class),
|
||||
@Type(name = "MOBILE", value = MobileDeliveryMethodNotificationTemplate.class)
|
||||
@Type(name = "MOBILE_APP", value = MobileAppDeliveryMethodNotificationTemplate.class)
|
||||
})
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
|
||||
@ -28,7 +28,7 @@ import java.util.List;
|
||||
@NoArgsConstructor
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
public class MobileDeliveryMethodNotificationTemplate extends DeliveryMethodNotificationTemplate implements HasSubject {
|
||||
public class MobileAppDeliveryMethodNotificationTemplate extends DeliveryMethodNotificationTemplate implements HasSubject {
|
||||
|
||||
@NotEmpty
|
||||
private String subject;
|
||||
@ -38,19 +38,19 @@ public class MobileDeliveryMethodNotificationTemplate extends DeliveryMethodNoti
|
||||
TemplatableValue.of(this::getSubject, this::setSubject)
|
||||
);
|
||||
|
||||
public MobileDeliveryMethodNotificationTemplate(MobileDeliveryMethodNotificationTemplate other) {
|
||||
public MobileAppDeliveryMethodNotificationTemplate(MobileAppDeliveryMethodNotificationTemplate other) {
|
||||
super(other);
|
||||
this.subject = other.subject;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NotificationDeliveryMethod getMethod() {
|
||||
return NotificationDeliveryMethod.MOBILE;
|
||||
return NotificationDeliveryMethod.MOBILE_APP;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MobileDeliveryMethodNotificationTemplate copy() {
|
||||
return new MobileDeliveryMethodNotificationTemplate(this);
|
||||
public MobileAppDeliveryMethodNotificationTemplate copy() {
|
||||
return new MobileAppDeliveryMethodNotificationTemplate(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -19,7 +19,14 @@ import lombok.Getter;
|
||||
|
||||
public enum UserSettingsType {
|
||||
|
||||
GENERAL, VISITED_DASHBOARDS(true), QUICK_LINKS, DOC_LINKS, DASHBOARDS, GETTING_STARTED, NOTIFICATIONS;
|
||||
GENERAL,
|
||||
VISITED_DASHBOARDS(true),
|
||||
QUICK_LINKS,
|
||||
DOC_LINKS,
|
||||
DASHBOARDS,
|
||||
GETTING_STARTED,
|
||||
NOTIFICATIONS,
|
||||
MOBILE(true);
|
||||
|
||||
@Getter
|
||||
private final boolean reserved;
|
||||
|
||||
@ -135,6 +135,11 @@ public class JpaUserDao extends JpaAbstractDao<UserEntity, User> implements User
|
||||
DaoUtil.toPageable(pageLink)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unassignFcmToken(TenantId tenantId, String fcmToken) {
|
||||
userRepository.unassignFcmToken(fcmToken);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long countByTenantId(TenantId tenantId) {
|
||||
return userRepository.countByTenantId(tenantId.getId());
|
||||
|
||||
@ -18,8 +18,10 @@ package org.thingsboard.server.dao.sql.user;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.Modifying;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.thingsboard.server.common.data.security.Authority;
|
||||
import org.thingsboard.server.dao.model.sql.UserEntity;
|
||||
|
||||
@ -71,4 +73,9 @@ public interface UserRepository extends JpaRepository<UserEntity, UUID> {
|
||||
|
||||
Long countByTenantId(UUID tenantId);
|
||||
|
||||
@Query(value = "UPDATE user_settings SET settings = (settings::jsonb - 'fcmToken')::text WHERE type = 'MOBILE'", nativeQuery = true)
|
||||
@Modifying
|
||||
@Transactional
|
||||
void unassignFcmToken(@Param("fcmToken") String fcmToken);
|
||||
|
||||
}
|
||||
|
||||
@ -101,4 +101,6 @@ public interface UserDao extends Dao<User>, TenantEntityDao {
|
||||
|
||||
PageData<User> findByAuthorityAndTenantProfilesIds(Authority authority, List<TenantProfileId> tenantProfilesIds, PageLink pageLink);
|
||||
|
||||
void unassignFcmToken(TenantId tenantId, String fcmToken);
|
||||
|
||||
}
|
||||
|
||||
@ -21,6 +21,7 @@ import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.security.authentication.DisabledException;
|
||||
@ -30,6 +31,7 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
import org.thingsboard.common.util.JacksonUtil;
|
||||
import org.thingsboard.server.common.data.EntityType;
|
||||
import org.thingsboard.server.common.data.User;
|
||||
import org.thingsboard.server.common.data.UserMobileInfo;
|
||||
import org.thingsboard.server.common.data.audit.ActionType;
|
||||
import org.thingsboard.server.common.data.id.CustomerId;
|
||||
import org.thingsboard.server.common.data.id.EntityId;
|
||||
@ -43,6 +45,8 @@ import org.thingsboard.server.common.data.page.PageLink;
|
||||
import org.thingsboard.server.common.data.security.Authority;
|
||||
import org.thingsboard.server.common.data.security.UserCredentials;
|
||||
import org.thingsboard.server.common.data.security.event.UserCredentialsInvalidationEvent;
|
||||
import org.thingsboard.server.common.data.settings.UserSettings;
|
||||
import org.thingsboard.server.common.data.settings.UserSettingsType;
|
||||
import org.thingsboard.server.dao.entity.AbstractEntityService;
|
||||
import org.thingsboard.server.dao.entity.EntityCountService;
|
||||
import org.thingsboard.server.dao.eventsourcing.ActionEntityEvent;
|
||||
@ -85,6 +89,7 @@ public class UserServiceImpl extends AbstractEntityService implements UserServic
|
||||
private final UserDao userDao;
|
||||
private final UserCredentialsDao userCredentialsDao;
|
||||
private final UserAuthSettingsDao userAuthSettingsDao;
|
||||
private final UserSettingsService userSettingsService;
|
||||
private final DataValidator<User> userValidator;
|
||||
private final DataValidator<UserCredentials> userCredentialsValidator;
|
||||
private final ApplicationEventPublisher eventPublisher;
|
||||
@ -391,6 +396,22 @@ public class UserServiceImpl extends AbstractEntityService implements UserServic
|
||||
saveUser(tenantId, user);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveMobileInfo(TenantId tenantId, UserId userId, UserMobileInfo mobileInfo) {
|
||||
if (StringUtils.isNotEmpty(mobileInfo.getFcmToken())) {
|
||||
// unassigning fcm token from other users, in case we didn't clean up it on log out or mobile app uninstall
|
||||
userDao.unassignFcmToken(tenantId, mobileInfo.getFcmToken());
|
||||
}
|
||||
userSettingsService.updateUserSettings(tenantId, userId, UserSettingsType.MOBILE, JacksonUtil.valueToTree(mobileInfo));
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserMobileInfo findMobileInfo(TenantId tenantId, UserId userId) {
|
||||
return Optional.ofNullable(userSettingsService.findUserSettings(tenantId, userId, UserSettingsType.MOBILE))
|
||||
.map(UserSettings::getSettings).map(settings -> JacksonUtil.treeToValue(settings, UserMobileInfo.class))
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int increaseFailedLoginAttempts(TenantId tenantId, UserId userId) {
|
||||
log.trace("Executing onUserLoginIncorrectCredentials [{}]", userId);
|
||||
|
||||
@ -19,6 +19,6 @@ import org.thingsboard.server.common.data.id.TenantId;
|
||||
|
||||
public interface FirebaseService {
|
||||
|
||||
void sendMessage(TenantId tenantId, String credentials, String fcmToken, String title, String body);
|
||||
void sendMessage(TenantId tenantId, String credentials, String fcmToken, String title, String body) throws Exception;
|
||||
|
||||
}
|
||||
|
||||
@ -75,14 +75,14 @@
|
||||
<!-- <div tb-help="mobileSettings"></div>-->
|
||||
</mat-card-header>
|
||||
<mat-card-content>
|
||||
<section formGroupName="MOBILE" style="margin-bottom: 16px;">
|
||||
<section formGroupName="MOBILE_APP" style="margin-bottom: 16px;">
|
||||
<tb-file-input formControlName="firebaseServiceAccountCredentials"
|
||||
dropLabel="{{ 'admin.select-firebase-service-account-file' | translate }}"
|
||||
label="{{ 'admin.firebase-service-account-file' | translate }}"
|
||||
accept=".json,application/json"
|
||||
allowedExtensions="json"
|
||||
[existingFileName]="notificationSettingsForm.get('deliveryMethodsConfigs.MOBILE.firebaseServiceAccountCredentialsFileName')?.value"
|
||||
(fileNameChanged)="notificationSettingsForm?.get('deliveryMethodsConfigs.MOBILE.firebaseServiceAccountCredentialsFileName').patchValue($event)">
|
||||
[existingFileName]="notificationSettingsForm.get('deliveryMethodsConfigs.MOBILE_APP.firebaseServiceAccountCredentialsFileName')?.value"
|
||||
(fileNameChanged)="notificationSettingsForm?.get('deliveryMethodsConfigs.MOBILE_APP.firebaseServiceAccountCredentialsFileName').patchValue($event)">
|
||||
</tb-file-input>
|
||||
</section>
|
||||
<div fxLayout="row" fxLayoutAlign="end center" fxLayout.xs="column" fxLayoutAlign.xs="end">
|
||||
|
||||
@ -117,7 +117,7 @@ export class SmsProviderComponent extends PageComponent implements HasConfirmFor
|
||||
SLACK: this.fb.group({
|
||||
botToken: ['']
|
||||
}),
|
||||
MOBILE: this.fb.group({
|
||||
MOBILE_APP: this.fb.group({
|
||||
firebaseServiceAccountCredentialsFileName: [''],
|
||||
firebaseServiceAccountCredentials: ['']
|
||||
})
|
||||
|
||||
@ -446,9 +446,9 @@
|
||||
</form>
|
||||
</mat-step>
|
||||
<mat-step *ngIf="!notificationRequestForm.get('useTemplate').value &&
|
||||
notificationRequestForm.get('template.configuration.deliveryMethodsTemplates.MOBILE.enabled').value"
|
||||
notificationRequestForm.get('template.configuration.deliveryMethodsTemplates.MOBILE_APP.enabled').value"
|
||||
[stepControl]="mobileTemplateForm">
|
||||
<ng-template matStepLabel>{{ 'notification.delivery-method.mobile' | translate }}</ng-template>
|
||||
<ng-template matStepLabel>{{ 'notification.delivery-method.mobile-app' | translate }}</ng-template>
|
||||
<div class="tb-hint-available-params mat-body-2">
|
||||
<span class="content">{{ 'notification.input-field-support-templatization' | translate}}</span>
|
||||
<span tb-help-popup="{{ notificationTemplateTypeTranslateMap.get(notificationType.GENERAL).helpId }}"
|
||||
@ -523,14 +523,14 @@
|
||||
{{ preview.processedTemplates.SLACK.body }}
|
||||
</div>
|
||||
</section>
|
||||
<section class="preview-group notification" *ngIf="preview.processedTemplates.MOBILE?.enabled">
|
||||
<section class="preview-group notification" *ngIf="preview.processedTemplates.MOBILE_APP?.enabled">
|
||||
<div fxLayout="row" fxLayoutGap="8px" fxLayoutAlign="start center">
|
||||
<mat-icon class="tb-mat-18" svgIcon="mdi:cellphone-text"></mat-icon>
|
||||
<div class="group-title" translate>notification.delivery-method.mobile-preview</div>
|
||||
<div class="group-title" translate>notification.delivery-method.mobile-app-preview</div>
|
||||
</div>
|
||||
<div class="notification-content">
|
||||
<div class="subject">{{ preview.processedTemplates.MOBILE.subject }}</div>
|
||||
<div>{{ preview.processedTemplates.MOBILE.body }}</div>
|
||||
<div class="subject">{{ preview.processedTemplates.MOBILE_APP.subject }}</div>
|
||||
<div>{{ preview.processedTemplates.MOBILE_APP.body }}</div>
|
||||
</div>
|
||||
</section>
|
||||
<section class="preview-group notification" *ngIf="preview.processedTemplates.MICROSOFT_TEAMS?.enabled">
|
||||
|
||||
@ -306,7 +306,7 @@ export class SentNotificationDialogComponent extends
|
||||
allowConfigureDeliveryMethod(deliveryMethod: NotificationDeliveryMethod): boolean {
|
||||
const tenantAllowConfigureDeliveryMethod = new Set([
|
||||
NotificationDeliveryMethod.SLACK,
|
||||
NotificationDeliveryMethod.MOBILE
|
||||
NotificationDeliveryMethod.MOBILE_APP
|
||||
]);
|
||||
if (deliveryMethod === NotificationDeliveryMethod.WEB) {
|
||||
return false;
|
||||
@ -329,7 +329,7 @@ export class SentNotificationDialogComponent extends
|
||||
return '/settings/outgoing-mail';
|
||||
case NotificationDeliveryMethod.SMS:
|
||||
case NotificationDeliveryMethod.SLACK:
|
||||
case NotificationDeliveryMethod.MOBILE:
|
||||
case NotificationDeliveryMethod.MOBILE_APP:
|
||||
return '/settings/notifications';
|
||||
}
|
||||
}
|
||||
|
||||
@ -148,7 +148,7 @@ export abstract class TemplateConfiguration<T, R = any> extends DialogComponent<
|
||||
[NotificationDeliveryMethod.SMS, this.smsTemplateForm],
|
||||
[NotificationDeliveryMethod.SLACK, this.slackTemplateForm],
|
||||
[NotificationDeliveryMethod.MICROSOFT_TEAMS, this.microsoftTeamsTemplateForm],
|
||||
[NotificationDeliveryMethod.MOBILE, this.mobileTemplateForm]
|
||||
[NotificationDeliveryMethod.MOBILE_APP, this.mobileTemplateForm]
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
@ -364,9 +364,9 @@
|
||||
</div>
|
||||
</form>
|
||||
</mat-step>
|
||||
<mat-step *ngIf="templateNotificationForm.get('configuration.deliveryMethodsTemplates.MOBILE.enabled').value"
|
||||
<mat-step *ngIf="templateNotificationForm.get('configuration.deliveryMethodsTemplates.MOBILE_APP.enabled').value"
|
||||
[stepControl]="mobileTemplateForm">
|
||||
<ng-template matStepLabel>{{ 'notification.delivery-method.mobile' | translate }}</ng-template>
|
||||
<ng-template matStepLabel>{{ 'notification.delivery-method.mobile-app' | translate }}</ng-template>
|
||||
<div class="tb-hint-available-params mat-body-2">
|
||||
<span class="content">{{ 'notification.input-field-support-templatization' | translate}}</span>
|
||||
<span tb-help-popup="{{ notificationTemplateTypeTranslateMap.get(templateNotificationForm.get('notificationType').value).helpId }}"
|
||||
|
||||
@ -380,7 +380,7 @@ export enum NotificationDeliveryMethod {
|
||||
EMAIL = 'EMAIL',
|
||||
SLACK = 'SLACK',
|
||||
MICROSOFT_TEAMS = 'MICROSOFT_TEAMS',
|
||||
MOBILE = 'MOBILE'
|
||||
MOBILE_APP = 'MOBILE_APP'
|
||||
}
|
||||
|
||||
export const NotificationDeliveryMethodTranslateMap = new Map<NotificationDeliveryMethod, string>([
|
||||
@ -389,7 +389,7 @@ export const NotificationDeliveryMethodTranslateMap = new Map<NotificationDelive
|
||||
[NotificationDeliveryMethod.EMAIL, 'notification.delivery-method.email'],
|
||||
[NotificationDeliveryMethod.SLACK, 'notification.delivery-method.slack'],
|
||||
[NotificationDeliveryMethod.MICROSOFT_TEAMS, 'notification.delivery-method.microsoft-teams'],
|
||||
[NotificationDeliveryMethod.MOBILE, 'notification.delivery-method.mobile']
|
||||
[NotificationDeliveryMethod.MOBILE_APP, 'notification.delivery-method.mobile-app']
|
||||
]);
|
||||
|
||||
export enum NotificationRequestStatus {
|
||||
|
||||
@ -3271,8 +3271,8 @@
|
||||
"sms-preview": "SMS notification preview",
|
||||
"web": "Web",
|
||||
"web-preview": "Web notification preview",
|
||||
"mobile": "Mobile",
|
||||
"mobile-preview": "Mobile notification preview"
|
||||
"mobile-app": "Mobile app",
|
||||
"mobile-app-preview": "Mobile app notification preview"
|
||||
},
|
||||
"delivery-method-not-configure-click": "Delivery method is not configured. Click to setup.",
|
||||
"delivery-method-not-configure-contact": "Delivery method is not configured. Contact your system administrator.",
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user