Merge branch 'master' into feature/profile-names-api
This commit is contained in:
		
						commit
						506c317985
					
				@ -19,6 +19,9 @@
 | 
			
		||||
    "indoor_simple_temperature_chart_card_with_background",
 | 
			
		||||
    "indoor_temperature_progress_bar",
 | 
			
		||||
    "indoor_temperature_progress_bar_with_background",
 | 
			
		||||
    "indoor_temperature_range_chart",
 | 
			
		||||
    "indoor_temperature_range_chart_with_background",
 | 
			
		||||
    "indoor_temperature_gauge",
 | 
			
		||||
    "indoor_humidity_card",
 | 
			
		||||
    "indoor_humidity_card_with_background",
 | 
			
		||||
    "indoor_horizontal_humidity_card",
 | 
			
		||||
 | 
			
		||||
@ -35,19 +35,6 @@
 | 
			
		||||
    "fluid_pressure_range_chart",
 | 
			
		||||
    "fluid_pressure_range_chart_with_background",
 | 
			
		||||
    "fluid_pressure_gauge",
 | 
			
		||||
    "fluid_temperature_card",
 | 
			
		||||
    "fluid_temperature_card_with_background",
 | 
			
		||||
    "horizontal_fluid_temperature_card",
 | 
			
		||||
    "horizontal_fluid_temperature_card_with_background",
 | 
			
		||||
    "fluid_temperature_chart_card",
 | 
			
		||||
    "fluid_temperature_chart_card_with_background",
 | 
			
		||||
    "simple_fluid_temperature_chart_card",
 | 
			
		||||
    "simple_fluid_temperature_chart_card_with_background",
 | 
			
		||||
    "fluid_temperature_progress_bar",
 | 
			
		||||
    "fluid_temperature_progress_bar_with_background",
 | 
			
		||||
    "fluid_temperature_range_chart",
 | 
			
		||||
    "fluid_temperature_range_chart_with_background",
 | 
			
		||||
    "fluid_temperature_gauge",
 | 
			
		||||
    "pump_vibration_card",
 | 
			
		||||
    "pump_vibration_card_with_background",
 | 
			
		||||
    "horizontal_pump_vibration_card",
 | 
			
		||||
@ -67,6 +54,31 @@
 | 
			
		||||
    "simple_power_consumption_chart_card",
 | 
			
		||||
    "simple_power_consumption_chart_card_with_background",
 | 
			
		||||
    "power_consumption_range_chart",
 | 
			
		||||
    "power_consumption_range_chart_with_background"
 | 
			
		||||
    "power_consumption_range_chart_with_background",
 | 
			
		||||
    "rotational_speed_card",
 | 
			
		||||
    "rotational_speed_card_with_background",
 | 
			
		||||
    "horizontal_rotational_speed_card",
 | 
			
		||||
    "horizontal_rotational_speed_card_with_background",
 | 
			
		||||
    "rotational_speed_chart_card",
 | 
			
		||||
    "rotational_speed_chart_card_with_background",
 | 
			
		||||
    "simple_rotational_speed_chart_card",
 | 
			
		||||
    "simple_rotational_speed_chart_card_with_background",
 | 
			
		||||
    "rotational_speed_progress_bar",
 | 
			
		||||
    "rotational_speed_progress_bar_with_background",
 | 
			
		||||
    "rotational_speed_range_chart",
 | 
			
		||||
    "rotational_speed_range_chart_with_background",
 | 
			
		||||
    "rotational_speed_gauge",
 | 
			
		||||
    "efficiency_card",
 | 
			
		||||
    "efficiency_card_with_background",
 | 
			
		||||
    "horizontal_efficiency_card",
 | 
			
		||||
    "horizontal_efficiency_card_with_background",
 | 
			
		||||
    "efficiency_chart_card",
 | 
			
		||||
    "efficiency_chart_card_with_background",
 | 
			
		||||
    "simple_efficiency_chart_card",
 | 
			
		||||
    "simple_efficiency_chart_card_with_background",
 | 
			
		||||
    "efficiency_progress_bar",
 | 
			
		||||
    "efficiency_progress_bar_with_background",
 | 
			
		||||
    "efficiency_range_chart",
 | 
			
		||||
    "efficiency_range_chart_with_background"
 | 
			
		||||
  ]
 | 
			
		||||
}
 | 
			
		||||
@ -17,6 +17,9 @@
 | 
			
		||||
    "temperature_chart_card_with_background",
 | 
			
		||||
    "simple_temperature_chart_card",
 | 
			
		||||
    "simple_temperature_chart_card_with_background",
 | 
			
		||||
    "temperature_range_chart",
 | 
			
		||||
    "temperature_range_chart_with_background",
 | 
			
		||||
    "temperature_gauge",
 | 
			
		||||
    "humidity_card",
 | 
			
		||||
    "humidity_card_with_background",
 | 
			
		||||
    "horizontal_humidity_card",
 | 
			
		||||
 | 
			
		||||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							@ -353,6 +353,9 @@ public class DefaultTbApiUsageStateService extends AbstractPartitionBasedService
 | 
			
		||||
            String email = tenantService.findTenantById(state.getTenantId()).getEmail();
 | 
			
		||||
            result.forEach((apiFeature, stateValue) -> {
 | 
			
		||||
                ApiUsageRecordState recordState = createApiUsageRecordState((TenantApiUsageState) state, apiFeature, stateValue);
 | 
			
		||||
                if (recordState == null) {
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
                notificationRuleProcessor.process(ApiUsageLimitTrigger.builder()
 | 
			
		||||
                        .tenantId(state.getTenantId())
 | 
			
		||||
                        .state(recordState)
 | 
			
		||||
 | 
			
		||||
@ -15,6 +15,7 @@
 | 
			
		||||
 */
 | 
			
		||||
package org.thingsboard.server.service.notification;
 | 
			
		||||
 | 
			
		||||
import com.google.common.util.concurrent.FutureCallback;
 | 
			
		||||
import lombok.RequiredArgsConstructor;
 | 
			
		||||
import lombok.extern.slf4j.Slf4j;
 | 
			
		||||
import org.springframework.beans.factory.annotation.Autowired;
 | 
			
		||||
@ -79,7 +80,6 @@ import java.util.List;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
import java.util.Set;
 | 
			
		||||
import java.util.UUID;
 | 
			
		||||
import java.util.function.Consumer;
 | 
			
		||||
import java.util.stream.Collectors;
 | 
			
		||||
 | 
			
		||||
@Service
 | 
			
		||||
@ -101,7 +101,7 @@ public class DefaultNotificationCenter extends AbstractSubscriptionService imple
 | 
			
		||||
    private Map<NotificationDeliveryMethod, NotificationChannel> channels;
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public NotificationRequest processNotificationRequest(TenantId tenantId, NotificationRequest request, Consumer<NotificationRequestStats> callback) {
 | 
			
		||||
    public NotificationRequest processNotificationRequest(TenantId tenantId, NotificationRequest request, FutureCallback<NotificationRequestStats> callback) {
 | 
			
		||||
        if (request.getRuleId() == null) {
 | 
			
		||||
            if (!rateLimitService.checkRateLimit(LimitedApi.NOTIFICATION_REQUESTS, tenantId)) {
 | 
			
		||||
                throw new TbRateLimitsException(EntityType.TENANT);
 | 
			
		||||
@ -200,7 +200,7 @@ public class DefaultNotificationCenter extends AbstractSubscriptionService imple
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void processNotificationRequestAsync(NotificationProcessingContext ctx, List<NotificationTarget> targets, Consumer<NotificationRequestStats> callback) {
 | 
			
		||||
    private void processNotificationRequestAsync(NotificationProcessingContext ctx, List<NotificationTarget> targets, FutureCallback<NotificationRequestStats> callback) {
 | 
			
		||||
        notificationExecutor.submit(() -> {
 | 
			
		||||
            NotificationRequestId requestId = ctx.getRequest().getId();
 | 
			
		||||
            for (NotificationTarget target : targets) {
 | 
			
		||||
@ -208,33 +208,39 @@ public class DefaultNotificationCenter extends AbstractSubscriptionService imple
 | 
			
		||||
                    processForTarget(target, ctx);
 | 
			
		||||
                } catch (Exception e) {
 | 
			
		||||
                    log.error("[{}] Failed to process notification request for target {}", requestId, target.getId(), e);
 | 
			
		||||
                    ctx.getStats().setError(e.getMessage());
 | 
			
		||||
                    updateRequestStats(ctx, requestId, ctx.getStats());
 | 
			
		||||
 | 
			
		||||
                    if (callback != null) {
 | 
			
		||||
                        callback.onFailure(e);
 | 
			
		||||
                    }
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            log.debug("[{}] Notification request processing is finished", requestId);
 | 
			
		||||
 | 
			
		||||
            NotificationRequestStats stats = ctx.getStats();
 | 
			
		||||
            try {
 | 
			
		||||
                notificationRequestService.updateNotificationRequest(ctx.getTenantId(), requestId, NotificationRequestStatus.SENT, stats);
 | 
			
		||||
            } catch (Exception e) {
 | 
			
		||||
                log.error("[{}] Failed to update stats for notification request", requestId, e);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            updateRequestStats(ctx, requestId, stats);
 | 
			
		||||
            if (callback != null) {
 | 
			
		||||
                try {
 | 
			
		||||
                    callback.accept(stats);
 | 
			
		||||
                } catch (Exception e) {
 | 
			
		||||
                    log.error("Failed to process callback for notification request {}", requestId, e);
 | 
			
		||||
                }
 | 
			
		||||
                callback.onSuccess(stats);
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void updateRequestStats(NotificationProcessingContext ctx, NotificationRequestId requestId, NotificationRequestStats stats) {
 | 
			
		||||
        try {
 | 
			
		||||
            notificationRequestService.updateNotificationRequest(ctx.getTenantId(), requestId, NotificationRequestStatus.SENT, stats);
 | 
			
		||||
        } catch (Exception e) {
 | 
			
		||||
            log.error("[{}] Failed to update stats for notification request", requestId, e);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void processForTarget(NotificationTarget target, NotificationProcessingContext ctx) {
 | 
			
		||||
        Iterable<? extends NotificationRecipient> recipients;
 | 
			
		||||
        switch (target.getConfiguration().getType()) {
 | 
			
		||||
            case PLATFORM_USERS: {
 | 
			
		||||
                PlatformUsersNotificationTargetConfig targetConfig = (PlatformUsersNotificationTargetConfig) target.getConfiguration();
 | 
			
		||||
                if (targetConfig.getUsersFilter().getType().isForRules()) {
 | 
			
		||||
                if (targetConfig.getUsersFilter().getType().isForRules() && ctx.getRequest().getInfo() instanceof RuleOriginatedNotificationInfo) {
 | 
			
		||||
                    recipients = new PageDataIterable<>(pageLink -> {
 | 
			
		||||
                        return notificationTargetService.findRecipientsForRuleNotificationTargetConfig(ctx.getTenantId(), targetConfig, (RuleOriginatedNotificationInfo) ctx.getRequest().getInfo(), pageLink);
 | 
			
		||||
                    }, 500);
 | 
			
		||||
 | 
			
		||||
@ -232,8 +232,8 @@ public class ImageControllerTest extends AbstractControllerTest {
 | 
			
		||||
        assertThat(previewDescriptor.getMediaType()).isEqualTo("image/png");
 | 
			
		||||
        assertThat(previewDescriptor.getWidth()).isEqualTo(225);
 | 
			
		||||
        assertThat(previewDescriptor.getHeight()).isEqualTo(225);
 | 
			
		||||
        assertThat(previewDescriptor.getSize()).isEqualTo(49817);
 | 
			
		||||
        assertThat(previewDescriptor.getEtag()).isEqualTo("d330b6c158cbf0c40fca0504d39add940693060302ab11b5c0ee79a41e5637d9");
 | 
			
		||||
        assertThat(previewDescriptor.getSize()).isEqualTo(53498);
 | 
			
		||||
        assertThat(previewDescriptor.getEtag()).isEqualTo("c909e20ba942f95f4ed1ddfcf4307ce846b4a689195c629cd85f2517f46e84f9");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void checkSvgImageDescriptor(ImageDescriptor imageDescriptor) {
 | 
			
		||||
 | 
			
		||||
@ -15,6 +15,7 @@
 | 
			
		||||
 */
 | 
			
		||||
package org.thingsboard.server.service.notification;
 | 
			
		||||
 | 
			
		||||
import com.google.common.util.concurrent.FutureCallback;
 | 
			
		||||
import com.google.common.util.concurrent.SettableFuture;
 | 
			
		||||
import lombok.extern.slf4j.Slf4j;
 | 
			
		||||
import org.assertj.core.data.Offset;
 | 
			
		||||
@ -709,7 +710,17 @@ public class NotificationApiTest extends AbstractNotificationApiTest {
 | 
			
		||||
 | 
			
		||||
    private NotificationRequestStats submitNotificationRequestAndWait(NotificationRequest notificationRequest) throws Exception {
 | 
			
		||||
        SettableFuture<NotificationRequestStats> future = SettableFuture.create();
 | 
			
		||||
        notificationCenter.processNotificationRequest(notificationRequest.getTenantId(), notificationRequest, future::set);
 | 
			
		||||
        notificationCenter.processNotificationRequest(notificationRequest.getTenantId(), notificationRequest, new FutureCallback<>() {
 | 
			
		||||
            @Override
 | 
			
		||||
            public void onSuccess(NotificationRequestStats result) {
 | 
			
		||||
                future.set(result);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            @Override
 | 
			
		||||
            public void onFailure(Throwable t) {
 | 
			
		||||
                future.setException(t);
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
        return future.get(30, TimeUnit.SECONDS);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -19,6 +19,7 @@ import lombok.AllArgsConstructor;
 | 
			
		||||
import lombok.Builder;
 | 
			
		||||
import lombok.Data;
 | 
			
		||||
import lombok.NoArgsConstructor;
 | 
			
		||||
import org.thingsboard.server.common.data.id.CustomerId;
 | 
			
		||||
import org.thingsboard.server.common.data.id.EntityId;
 | 
			
		||||
 | 
			
		||||
import java.util.HashMap;
 | 
			
		||||
@ -28,9 +29,10 @@ import java.util.Map;
 | 
			
		||||
@AllArgsConstructor
 | 
			
		||||
@NoArgsConstructor
 | 
			
		||||
@Builder
 | 
			
		||||
public class RuleEngineOriginatedNotificationInfo implements NotificationInfo {
 | 
			
		||||
public class RuleEngineOriginatedNotificationInfo implements RuleOriginatedNotificationInfo {
 | 
			
		||||
 | 
			
		||||
    private EntityId msgOriginator;
 | 
			
		||||
    private CustomerId msgCustomerId;
 | 
			
		||||
    private String msgType;
 | 
			
		||||
    private Map<String, String> msgMetadata;
 | 
			
		||||
    private Map<String, String> msgData;
 | 
			
		||||
@ -43,6 +45,7 @@ public class RuleEngineOriginatedNotificationInfo implements NotificationInfo {
 | 
			
		||||
        templateData.put("originatorType", msgOriginator.getEntityType().getNormalName());
 | 
			
		||||
        templateData.put("originatorId", msgOriginator.getId().toString());
 | 
			
		||||
        templateData.put("msgType", msgType);
 | 
			
		||||
        templateData.put("customerId", msgCustomerId != null ? msgCustomerId.getId().toString() : "");
 | 
			
		||||
        return templateData;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -51,4 +54,9 @@ public class RuleEngineOriginatedNotificationInfo implements NotificationInfo {
 | 
			
		||||
        return msgOriginator;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public CustomerId getAffectedCustomerId() {
 | 
			
		||||
        return msgCustomerId;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -150,8 +150,9 @@ public class DefaultNotificationTargetService extends AbstractEntityService impl
 | 
			
		||||
                    return userService.findAllUsers(pageLink);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            default:
 | 
			
		||||
                throw new IllegalArgumentException("Recipient type not supported");
 | 
			
		||||
        }
 | 
			
		||||
        return new PageData<>();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
@ -178,6 +179,8 @@ public class DefaultNotificationTargetService extends AbstractEntityService impl
 | 
			
		||||
                    return userService.findTenantAdmins(affectedTenantId, pageLink);
 | 
			
		||||
                }
 | 
			
		||||
                break;
 | 
			
		||||
            default:
 | 
			
		||||
                throw new IllegalArgumentException("Recipient type not supported");
 | 
			
		||||
        }
 | 
			
		||||
        return new PageData<>();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -15,6 +15,7 @@
 | 
			
		||||
 */
 | 
			
		||||
package org.thingsboard.rule.engine.api;
 | 
			
		||||
 | 
			
		||||
import com.google.common.util.concurrent.FutureCallback;
 | 
			
		||||
import org.thingsboard.server.common.data.id.NotificationId;
 | 
			
		||||
import org.thingsboard.server.common.data.id.NotificationRequestId;
 | 
			
		||||
import org.thingsboard.server.common.data.id.TenantId;
 | 
			
		||||
@ -26,11 +27,10 @@ import org.thingsboard.server.common.data.notification.targets.platform.UsersFil
 | 
			
		||||
import org.thingsboard.server.common.data.notification.template.NotificationTemplate;
 | 
			
		||||
 | 
			
		||||
import java.util.Set;
 | 
			
		||||
import java.util.function.Consumer;
 | 
			
		||||
 | 
			
		||||
public interface NotificationCenter {
 | 
			
		||||
 | 
			
		||||
    NotificationRequest processNotificationRequest(TenantId tenantId, NotificationRequest notificationRequest, Consumer<NotificationRequestStats> callback);
 | 
			
		||||
    NotificationRequest processNotificationRequest(TenantId tenantId, NotificationRequest notificationRequest, FutureCallback<NotificationRequestStats> callback);
 | 
			
		||||
 | 
			
		||||
    void sendGeneralWebNotification(TenantId tenantId, UsersFilter recipients, NotificationTemplate template);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -15,6 +15,7 @@
 | 
			
		||||
 */
 | 
			
		||||
package org.thingsboard.rule.engine.notification;
 | 
			
		||||
 | 
			
		||||
import com.google.common.util.concurrent.FutureCallback;
 | 
			
		||||
import org.thingsboard.common.util.DonAsynchron;
 | 
			
		||||
import org.thingsboard.common.util.JacksonUtil;
 | 
			
		||||
import org.thingsboard.rule.engine.api.RuleNode;
 | 
			
		||||
@ -23,8 +24,10 @@ import org.thingsboard.rule.engine.api.TbNodeConfiguration;
 | 
			
		||||
import org.thingsboard.rule.engine.api.TbNodeException;
 | 
			
		||||
import org.thingsboard.rule.engine.api.util.TbNodeUtils;
 | 
			
		||||
import org.thingsboard.rule.engine.external.TbAbstractExternalNode;
 | 
			
		||||
import org.thingsboard.server.common.data.EntityType;
 | 
			
		||||
import org.thingsboard.server.common.data.notification.NotificationRequest;
 | 
			
		||||
import org.thingsboard.server.common.data.notification.NotificationRequestConfig;
 | 
			
		||||
import org.thingsboard.server.common.data.notification.NotificationRequestStats;
 | 
			
		||||
import org.thingsboard.server.common.data.notification.info.RuleEngineOriginatedNotificationInfo;
 | 
			
		||||
import org.thingsboard.server.common.data.plugin.ComponentType;
 | 
			
		||||
import org.thingsboard.server.common.msg.TbMsg;
 | 
			
		||||
@ -56,6 +59,8 @@ public class TbNotificationNode extends TbAbstractExternalNode {
 | 
			
		||||
    public void onMsg(TbContext ctx, TbMsg msg) throws ExecutionException, InterruptedException, TbNodeException {
 | 
			
		||||
        RuleEngineOriginatedNotificationInfo notificationInfo = RuleEngineOriginatedNotificationInfo.builder()
 | 
			
		||||
                .msgOriginator(msg.getOriginator())
 | 
			
		||||
                .msgCustomerId(msg.getOriginator().getEntityType() == EntityType.CUSTOMER
 | 
			
		||||
                        && msg.getOriginator().equals(msg.getCustomerId()) ? null : msg.getCustomerId())
 | 
			
		||||
                .msgMetadata(msg.getMetaData().getData())
 | 
			
		||||
                .msgData(JacksonUtil.toFlatMap(JacksonUtil.toJsonNode(msg.getData())))
 | 
			
		||||
                .msgType(msg.getType())
 | 
			
		||||
@ -72,15 +77,23 @@ public class TbNotificationNode extends TbAbstractExternalNode {
 | 
			
		||||
 | 
			
		||||
        var tbMsg = ackIfNeeded(ctx, msg);
 | 
			
		||||
 | 
			
		||||
        DonAsynchron.withCallback(ctx.getNotificationExecutor().executeAsync(() ->
 | 
			
		||||
                        ctx.getNotificationCenter().processNotificationRequest(ctx.getTenantId(), notificationRequest, stats -> {
 | 
			
		||||
                            TbMsgMetaData metaData = tbMsg.getMetaData().copy();
 | 
			
		||||
                            metaData.putValue("notificationRequestResult", JacksonUtil.toString(stats));
 | 
			
		||||
                            tellSuccess(ctx, TbMsg.transformMsgMetadata(tbMsg, metaData));
 | 
			
		||||
                        })),
 | 
			
		||||
                r -> {
 | 
			
		||||
                },
 | 
			
		||||
                e -> tellFailure(ctx, tbMsg, e));
 | 
			
		||||
        var callback = new FutureCallback<NotificationRequestStats>() {
 | 
			
		||||
            @Override
 | 
			
		||||
            public void onSuccess(NotificationRequestStats stats) {
 | 
			
		||||
                TbMsgMetaData metaData = tbMsg.getMetaData().copy();
 | 
			
		||||
                metaData.putValue("notificationRequestResult", JacksonUtil.toString(stats));
 | 
			
		||||
                tellSuccess(ctx, TbMsg.transformMsgMetadata(tbMsg, metaData));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            @Override
 | 
			
		||||
            public void onFailure(Throwable e) {
 | 
			
		||||
                tellFailure(ctx, tbMsg, e);
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        var future = ctx.getNotificationExecutor().executeAsync(() ->
 | 
			
		||||
                ctx.getNotificationCenter().processNotificationRequest(ctx.getTenantId(), notificationRequest, callback));
 | 
			
		||||
        DonAsynchron.withCallback(future, r -> {}, callback::onFailure);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -442,14 +442,12 @@ export const NotificationTargetConfigTypeInfoMap = new Map<NotificationTargetCon
 | 
			
		||||
  ],
 | 
			
		||||
  [NotificationTargetConfigType.ORIGINATOR_ENTITY_OWNER_USERS,
 | 
			
		||||
    {
 | 
			
		||||
      name: 'notification.recipient-type.users-entity-owner',
 | 
			
		||||
      hint: 'notification.recipient-type.users-entity-owner-hint'
 | 
			
		||||
      name: 'notification.recipient-type.users-entity-owner'
 | 
			
		||||
    }
 | 
			
		||||
  ],
 | 
			
		||||
  [NotificationTargetConfigType.AFFECTED_USER,
 | 
			
		||||
    {
 | 
			
		||||
      name: 'notification.recipient-type.affected-user',
 | 
			
		||||
      hint: 'notification.recipient-type.affected-user-hint'
 | 
			
		||||
      name: 'notification.recipient-type.affected-user'
 | 
			
		||||
    }
 | 
			
		||||
  ],
 | 
			
		||||
  [NotificationTargetConfigType.SYSTEM_ADMINISTRATORS,
 | 
			
		||||
 | 
			
		||||
@ -13,6 +13,7 @@ Available template parameters:
 | 
			
		||||
* values from the incoming message data referenced using the data key name;
 | 
			
		||||
* `originatorType` - type of the originator, e.g. 'Device';
 | 
			
		||||
* `originatorId` - id of the originator
 | 
			
		||||
* `customerId` - id of the customer if any
 | 
			
		||||
* `msgType` - type of the message
 | 
			
		||||
* `recipientTitle` - title of the recipient (first and last name if specified, email otherwise);
 | 
			
		||||
* `recipientEmail` - email of the recipient;
 | 
			
		||||
 | 
			
		||||
@ -19,6 +19,7 @@
 | 
			
		||||
    "suspend": "Unterbrechen",
 | 
			
		||||
    "save": "Speichern",
 | 
			
		||||
    "saveAs": "Speichern unter",
 | 
			
		||||
    "move": "Verschieben",
 | 
			
		||||
    "cancel": "Abbrechen",
 | 
			
		||||
    "ok": "OK",
 | 
			
		||||
    "delete": "Löschen",
 | 
			
		||||
@ -27,18 +28,20 @@
 | 
			
		||||
    "no": "Nein",
 | 
			
		||||
    "update": "Aktualisieren",
 | 
			
		||||
    "remove": "Löschen",
 | 
			
		||||
	"select": "Auswählen",
 | 
			
		||||
    "search": "Suche",
 | 
			
		||||
    "clear-search": "Suchanfrage löschen",
 | 
			
		||||
    "assign": "Zuordnen",
 | 
			
		||||
    "unassign": "Zuordnung aufheben",
 | 
			
		||||
    "share": "Teilen",
 | 
			
		||||
    "make-private": "Privat machen",
 | 
			
		||||
    "make-public": "Öffentlich machen",
 | 
			
		||||
    "apply": "Anwenden",
 | 
			
		||||
    "apply-changes": "Änderungen übernehmen",
 | 
			
		||||
    "edit-mode": "Bearbeitungsmodus",
 | 
			
		||||
    "enter-edit-mode": "Zum Bearbeitungsmodus wechseln",
 | 
			
		||||
    "decline-changes": "Änderungen nicht übernehmen",
 | 
			
		||||
    "open": "Öffnen",
 | 
			
		||||
    "decline": "Verwerfen",
 | 
			
		||||
    "close": "Schließen",
 | 
			
		||||
    "back": "Zurück",
 | 
			
		||||
    "run": "Ausführen",
 | 
			
		||||
@ -56,16 +59,27 @@
 | 
			
		||||
    "import": "Importieren",
 | 
			
		||||
    "export": "Exportieren",
 | 
			
		||||
    "share-via": "Teilen mit {{provider}}",
 | 
			
		||||
    "select": "Auswählen",
 | 
			
		||||
    "continue": "Fortsetzen",
 | 
			
		||||
    "discard-changes": "Änderungen verwerfen",
 | 
			
		||||
    "download": "Download",
 | 
			
		||||
    "next": "Nächste",
 | 
			
		||||
    "next-with-label": "Nächste: {{label}}",
 | 
			
		||||
    "read-more": "Mehr dazu",
 | 
			
		||||
    "hide": "Verstecken",
 | 
			
		||||
	"done": "Erledigt",
 | 
			
		||||
	"print": "Drucken",
 | 
			
		||||
	"restore": "Wiederherstellen",
 | 
			
		||||
	"confirm": "Bestätigen"
 | 
			
		||||
	"confirm": "Bestätigen",
 | 
			
		||||
    "more": "Mehr",
 | 
			
		||||
    "less": "Weniger",
 | 
			
		||||
    "skip": "Überspringen",
 | 
			
		||||
    "send": "Senden",
 | 
			
		||||
    "reset": "Zurücksetzen",
 | 
			
		||||
    "show-more": "Zeige mehr",
 | 
			
		||||
    "dont-show-again": "Nicht wieder anzeigen",
 | 
			
		||||
    "see-documentation": "Siehe Dokumentation",
 | 
			
		||||
    "clear": "Leeren"
 | 
			
		||||
  },
 | 
			
		||||
  "aggregation": {
 | 
			
		||||
    "aggregation": "Aggregation",
 | 
			
		||||
@ -80,9 +94,11 @@
 | 
			
		||||
    "none": "kein Wert"
 | 
			
		||||
  },
 | 
			
		||||
  "admin": {
 | 
			
		||||
    "settings": "Einstellungen",
 | 
			
		||||
    "general": "Allgemein",
 | 
			
		||||
    "general-settings": "Allgemeine Einstellungen",
 | 
			
		||||
    "home-settings": "Home Einstellungen",
 | 
			
		||||
    "home": "Home",
 | 
			
		||||
    "outgoing-mail": "E-Mail Versand",
 | 
			
		||||
    "outgoing-mail-settings": "Konfiguration des Postausgangsservers",
 | 
			
		||||
    "system-settings": "Systemeinstellungen",
 | 
			
		||||
@ -103,7 +119,7 @@
 | 
			
		||||
    "timeout-required": "Wartezeit ist erforderlich.",
 | 
			
		||||
    "timeout-invalid": "Das ist keine gültige Wartezeit.",
 | 
			
		||||
    "enable-tls": "TLS aktivieren",
 | 
			
		||||
    "tls-version" : "TLS-Version",
 | 
			
		||||
    "tls-version": "TLS-Version",
 | 
			
		||||
    "enable-proxy": "Proxy aktivieren",
 | 
			
		||||
    "proxy-host": "Proxy Host",
 | 
			
		||||
    "proxy-host-required": "Proxy Host ist erforderlich.",
 | 
			
		||||
@ -114,8 +130,21 @@
 | 
			
		||||
    "proxy-password": "Proxy Passwort",
 | 
			
		||||
    "change-password": "Passwort ändern",
 | 
			
		||||
    "send-test-mail": "Test E-Mail senden",
 | 
			
		||||
    "use-system-mail-settings": "System E-Mail-Server Einstellungen benutzen",
 | 
			
		||||
    "mail-templates": "E-Mail Vorlagen",
 | 
			
		||||
    "mail-template-settings": "Einstellungen E-Mail Vorlagen",
 | 
			
		||||
    "use-system-mail-template-settings": "System E-Mail Vorlagen benutzen",
 | 
			
		||||
    "mail-template": {
 | 
			
		||||
      "mail-template": "E-Mail Vorlage",
 | 
			
		||||
      "test": "E-Mail Nachricht testen",
 | 
			
		||||
      "activation": "Nachricht Benutzerkontoaktivierung",
 | 
			
		||||
      "account-activated": "Nachricht Benutzerkonto aktiviert"
 | 
			
		||||
    },
 | 
			
		||||
    "mail-subject": "E-Mail Betreff",
 | 
			
		||||
    "mail-body": "E-Mail Nachricht",
 | 
			
		||||
    "sms-provider": "SMS Anbieter",
 | 
			
		||||
    "sms-provider-settings": "SMS Anbieter Einstellungen",
 | 
			
		||||
    "use-system-sms-settings": "System SMS Anbieter benutzen",
 | 
			
		||||
    "sms-provider-type": "SMS Anbieter Typ",
 | 
			
		||||
    "sms-provider-type-required": "SMS Anbieter Typ ist erforderlich.",
 | 
			
		||||
    "sms-provider-type-aws-sns": "Amazon SNS",
 | 
			
		||||
@ -138,6 +167,7 @@
 | 
			
		||||
    "password-expiration-period-days-range": "Die Gültigkeitsdauer des Passworts in Tagen kann nicht negativ sein",
 | 
			
		||||
    "password-reuse-frequency-days": "Häufigkeit der Kennwortwiederverwendung in Tagen",
 | 
			
		||||
    "password-reuse-frequency-days-range": "Die Häufigkeit der Kennwortwiederverwendung in Tagen kann nicht negativ sein",
 | 
			
		||||
    "allow-whitespace": "Leerzeichen erlauben",
 | 
			
		||||
    "general-policy": "Allgemeine Politik",
 | 
			
		||||
    "max-failed-login-attempts": "Maximale Anzahl fehlgeschlagener Anmeldeversuche, bevor das Konto gesperrt wird",
 | 
			
		||||
    "minimum-max-failed-login-attempts-range": "Die maximale Anzahl fehlgeschlagener Anmeldeversuche kann nicht negativ sein",
 | 
			
		||||
@ -146,11 +176,14 @@
 | 
			
		||||
  "alarm": {
 | 
			
		||||
    "alarm": "Alarm",
 | 
			
		||||
    "alarms": "Alarme",
 | 
			
		||||
    "all-alarms": "Alle Alarme",
 | 
			
		||||
    "select-alarm": "Alarm auswählen",
 | 
			
		||||
    "no-alarms-matching": "Keine passenden Alarme zu '{{entity}}' wurden gefunden.",
 | 
			
		||||
    "alarm-required": "Alarm ist erforderlich",
 | 
			
		||||
    "alarm-filter": "Alarmfilter",
 | 
			
		||||
    "filter": "Filter",
 | 
			
		||||
    "alarm-status": "Alarm Status",
 | 
			
		||||
    "alarm-status-list": "Alarm Status Liste",
 | 
			
		||||
    "alarm-status-list": "Alarm Statusliste",
 | 
			
		||||
    "any-status": "Jeder Status",
 | 
			
		||||
    "search-status": {
 | 
			
		||||
      "ANY": "Jeder",
 | 
			
		||||
@ -178,6 +211,7 @@
 | 
			
		||||
    "end-time": "Endzeit",
 | 
			
		||||
    "ack-time": "Bestätigungszeit",
 | 
			
		||||
    "clear-time": "Zeit gelöscht",
 | 
			
		||||
    "duration": "Dauer",
 | 
			
		||||
    "alarm-severity-list": "Alarm Schwere Liste",
 | 
			
		||||
    "any-severity": "Jede Schwere",
 | 
			
		||||
    "severity-critical": "Kritisch",
 | 
			
		||||
@ -187,6 +221,7 @@
 | 
			
		||||
    "severity-indeterminate": "Unbestimmt",
 | 
			
		||||
    "acknowledge": "Bestätigen",
 | 
			
		||||
    "clear": "Löschen",
 | 
			
		||||
    "delete": "Löschen",
 | 
			
		||||
    "search": "Alarme suchen",
 | 
			
		||||
    "selected-alarms": "{ count, plural, =1 {1 Alarm} other {# Alarme} } ausgewählt",
 | 
			
		||||
    "no-data": "Keine Daten zum Anzeigen",
 | 
			
		||||
@ -197,11 +232,18 @@
 | 
			
		||||
    "aknowledge-alarms-text": "Sind Sie sicher, dass Sie { count, plural, =1 {1 Alarm} other {# Alarme} } bestätigen möchten?",
 | 
			
		||||
    "aknowledge-alarm-title": "Alarm bestätigen",
 | 
			
		||||
    "aknowledge-alarm-text": "Möchten Sie den Alarm wirklich bestätigen?",
 | 
			
		||||
    "selected-alarms-are-acknowledged": "Ausgewählte Alarme wurden bereits bestätigt",
 | 
			
		||||
    "clear-alarms-title": "{ count, plural, =1 {1 Alarm} other {# Alarme} } löschen",
 | 
			
		||||
    "clear-alarms-text": "Möchten Sie wirklich { count, plural, =1 {1 Alarm} other {# Alarme} } löschen?",
 | 
			
		||||
    "clear-alarm-title": "Alarm löschen",
 | 
			
		||||
    "clear-alarm-text": "Möchten Sie den Alarm wirklich löschen?",
 | 
			
		||||
    "alarm-status-filter": "Alarm Status Filter"
 | 
			
		||||
    "delete-alarms-title": "Lösche { count, plural, =1 {1 Alarm} other {# Alarme} }",
 | 
			
		||||
    "delete-alarms-text": "Sind Sie sicher { count, plural, =1 {1 Alarm} other {# Alarme} } zu löschen?",
 | 
			
		||||
    "selected-alarms-are-cleared": "Ausgewählte Alarme wurden bereits gelöscht",
 | 
			
		||||
    "alarm-status-filter": "Alarm Status Filter",
 | 
			
		||||
    "alarm-filter-title": "Alarmfilter",
 | 
			
		||||
    "assigned": "Zugewiesen",
 | 
			
		||||
    "filter-title": "Filter"
 | 
			
		||||
  },
 | 
			
		||||
  "alias": {
 | 
			
		||||
    "add": "Alias hinzufügen",
 | 
			
		||||
@ -228,14 +270,14 @@
 | 
			
		||||
    "filter-type-edge-type-description": "Rand vom Typ '{{edgeTypes}}'",
 | 
			
		||||
    "filter-type-relations-query": "Beziehungsabfrage",
 | 
			
		||||
    "filter-type-relations-query-description": "{{entities}} mit {{relationType}} Beziehung {{direction}} {{rootEntity}}",
 | 
			
		||||
    "filter-type-edge-search-query": "Edge-Abfrage",
 | 
			
		||||
    "filter-type-edge-search-query-description": "Edge vom Typ {{edgeTypes}} mit {{relationType}} Beziehung {{direction}} {{rootEntity}}",
 | 
			
		||||
    "filter-type-asset-search-query": "Objektabfrage",
 | 
			
		||||
    "filter-type-asset-search-query-description": "Objekte vom Typ {{assetTypes}} mit {{relationType}} Beziehung {{direction}} {{rootEntity}}",
 | 
			
		||||
    "filter-type-device-search-query": "Geräteabfrage",
 | 
			
		||||
    "filter-type-device-search-query-description": "Geräte vom Typ {{deviceTypes}} mit {{relationType}} Beziehung {{direction}} {{rootEntity}}",
 | 
			
		||||
    "filter-type-entity-view-search-query": "Entitätsansichtsabfrage",
 | 
			
		||||
    "filter-type-entity-view-search-query-description": "Entitätsansichten vom Typ {{entityViewTypes}} mit {{relationType}} Beziehung {{direction}} {{rootEntity}}",
 | 
			
		||||
    "filter-type-edge-search-query": "Randabfrage",
 | 
			
		||||
    "filter-type-edge-search-query-description": "Rand vom Typ {{edgeTypes}} mit {{relationType}} Beziehung {{direction}} {{rootEntity}}",
 | 
			
		||||
    "entity-filter": "Entitätsfilter",
 | 
			
		||||
    "resolve-multiple": "Als mehrere Entitäten auflösen",
 | 
			
		||||
    "filter-type": "Filtertyp",
 | 
			
		||||
@ -254,11 +296,16 @@
 | 
			
		||||
    "any-relation": "Jede Beziehung"
 | 
			
		||||
  },
 | 
			
		||||
  "asset": {
 | 
			
		||||
    "all": "Alle",
 | 
			
		||||
    "all-assets": "Alle Objekte",
 | 
			
		||||
    "groups": "Gruppen",
 | 
			
		||||
    "shared": "Geteilt",
 | 
			
		||||
    "asset": "Objekt",
 | 
			
		||||
    "assets": "Objekte",
 | 
			
		||||
    "management": "Objektverwaltung",
 | 
			
		||||
    "view-assets": "Objekte anzeigen",
 | 
			
		||||
    "add": "Objekt hinzufügen",
 | 
			
		||||
    "asset-type-max-length": "Objekttyp sollte kürzer als 256 Zeichen sein",
 | 
			
		||||
    "assign-to-customer": "Einem Kunden zuordnen",
 | 
			
		||||
    "assign-asset-to-customer": "Objekte dem Kunden zuordnen",
 | 
			
		||||
    "assign-asset-to-customer-text": "Bitte wählen Sie die Objekte aus, die dem Kunden zugeordnet werden sollen",
 | 
			
		||||
@ -281,6 +328,8 @@
 | 
			
		||||
    "asset-types": "Objekttypen",
 | 
			
		||||
    "name": "Name",
 | 
			
		||||
    "name-required": "Name ist erforderlich.",
 | 
			
		||||
    "name-max-length": "Name sollte weniger als 256 Zeichen sein",
 | 
			
		||||
    "label-max-length": "Label sollte weniger als 256 Zeichen sein",
 | 
			
		||||
    "description": "Beschreibung",
 | 
			
		||||
    "type": "Typ",
 | 
			
		||||
    "type-required": "Typ ist erforderlich.",
 | 
			
		||||
@ -290,6 +339,7 @@
 | 
			
		||||
    "asset-details": "Objektdetails",
 | 
			
		||||
    "assign-assets": "Objekte zuordnen",
 | 
			
		||||
    "assign-assets-text": "Kunden { count, plural, =1 {1 Objekt} other {# Objekte} } zuordnen",
 | 
			
		||||
    "assign-asset-to-edge-text":"Bitte wählen Sie die Objekte aus, die dem Edge zugeordnet werden sollen",
 | 
			
		||||
    "delete-assets": "Objekte löschen",
 | 
			
		||||
    "unassign-assets": "Objektzuordnungen aufheben",
 | 
			
		||||
    "unassign-assets-action-title": "Kunden { count, plural, =1 {1 Objektzuordnung} other {# Objektzuordnungen} } aufheben",
 | 
			
		||||
@ -316,7 +366,6 @@
 | 
			
		||||
    "name-starts-with": "Name des Objekts beginnt mit",
 | 
			
		||||
    "label": "Bezeichnung",
 | 
			
		||||
    "assign-asset-to-edge": "Objekte dem Rand zuordnen",
 | 
			
		||||
    "assign-asset-to-edge-text":"Bitte wählen Sie die Objekte aus, die dem Rand zugeordnet werden sollen",
 | 
			
		||||
    "unassign-asset-from-edge": "Objekte von Rand entfernen",
 | 
			
		||||
    "unassign-asset-from-edge-title": "Sind Sie sicher, dass Sie die Zuordnung für das Objekt '{{assetName}}' aufheben möchten?",
 | 
			
		||||
    "unassign-asset-from-edge-text": "Nach Bestätigung wird die Zuordnung des Objekts aufgehoben und es ist für den Kunden nicht mehr zugänglich.",
 | 
			
		||||
@ -327,6 +376,7 @@
 | 
			
		||||
  "attribute": {
 | 
			
		||||
    "attributes": "Eigenschaften",
 | 
			
		||||
    "latest-telemetry": "Neueste Telemetrie",
 | 
			
		||||
    "no-latest-telemetry": "Keine Telemetriedaten",
 | 
			
		||||
    "attributes-scope": "Entitätseigenschaftsbereich",
 | 
			
		||||
    "scope-telemetry": "Telemetrie",
 | 
			
		||||
    "scope-latest-telemetry": "Neueste Telemetrie",
 | 
			
		||||
@ -334,11 +384,15 @@
 | 
			
		||||
    "scope-server": "Server Eigenschaften",
 | 
			
		||||
    "scope-shared": "Gemeinsame Eigenschaften",
 | 
			
		||||
    "add": "Eigenschaft hinzufügen",
 | 
			
		||||
    "add-attribute-prompt": "Bitte Attribut hinzufügen",
 | 
			
		||||
    "key": "Schlüssel",
 | 
			
		||||
    "key-max-length": "Der Schlüssel sollte weniger als 256 Zeichen haben",
 | 
			
		||||
    "last-update-time": "Datum der letzten Aktualisierung",
 | 
			
		||||
    "key-required": "Eigenschaftsschlüssel ist erforderlich.",
 | 
			
		||||
    "value": "Wert",
 | 
			
		||||
    "value-required": "Eigenschaftswert ist erforderlich.",
 | 
			
		||||
    "telemetry-key-required": "Telemetrieschlüssel ist erforderlich",
 | 
			
		||||
    "telemetry-value-required": "Telemetriewert ist erforderlich",
 | 
			
		||||
    "delete-attributes-title": "Sind Sie sicher, dass Sie { count, plural, =1 {1 Eigenschaft} other {# Eigenschaften} } löschen möchten?",
 | 
			
		||||
    "delete-attributes-text": "Seien Sie vorsichtig, nach der Bestätigung werden alle ausgewählten Eigenschaften entfernt.",
 | 
			
		||||
    "delete-attributes": "Eigenschaften löschen",
 | 
			
		||||
@ -350,7 +404,12 @@
 | 
			
		||||
    "add-to-dashboard": "Zum Dashboard hinzufügen",
 | 
			
		||||
    "add-widget-to-dashboard": "Widget zum Dashboard hinzufügen",
 | 
			
		||||
    "selected-attributes": "{ count, plural, =1 {1 Eigenschaft} other {# Eigenschaften} } ausgewählt",
 | 
			
		||||
    "selected-telemetry": "{ count, plural, =1 {1 Telemetrieeinheit } other {# Telemetrieeinheiten} } ausgewählt"
 | 
			
		||||
    "selected-telemetry": "{ count, plural, =1 {1 Telemetrieeinheit } other {# Telemetrieeinheiten} } ausgewählt",
 | 
			
		||||
    "no-attributes-text": "Keine Attribute gefunden",
 | 
			
		||||
    "no-telemetry-text": "Keine Telemetriedaten gefunden",
 | 
			
		||||
    "copy-key": "Schlüssel kopieren",
 | 
			
		||||
    "add-telemetry": "Telemetriedaten hinzufügen",
 | 
			
		||||
    "copy-value": "Wert kopieren"
 | 
			
		||||
  },
 | 
			
		||||
  "audit-log": {
 | 
			
		||||
    "audit": "Audit",
 | 
			
		||||
@ -371,17 +430,27 @@
 | 
			
		||||
    "type-credentials-updated": "Anmeldeinformationen wurden aktualisiert",
 | 
			
		||||
    "type-assigned-to-customer": "Kunden Zuordnung",
 | 
			
		||||
    "type-unassigned-from-customer": "Kunden Zuordnung aufgehoben",
 | 
			
		||||
    "type-assigned-to-edge": "Rand Zuordnung",
 | 
			
		||||
    "type-unassigned-from-edge": "Rand Zuordnung aufgehoben",
 | 
			
		||||
    "type-assigned-to-edge": "Zum Edge hinzugefügt",
 | 
			
		||||
    "type-unassigned-from-edge": "Vom Edge entfernt",
 | 
			
		||||
    "type-activated": "Aktiviert",
 | 
			
		||||
    "type-suspended": "Ausgesetzt",
 | 
			
		||||
    "type-credentials-read": "Anmeldeinformationen gelesen",
 | 
			
		||||
    "type-attributes-read": "Eigenschaften gelesen",
 | 
			
		||||
    "type-added-to-entity-group": "Zur Gruppe hinzugefügt",
 | 
			
		||||
    "type-removed-from-entity-group": "Aus der Gruppe entfernt",
 | 
			
		||||
    "type-relation-add-or-update": "Beziehung aktualisiert",
 | 
			
		||||
    "type-relation-delete": "Beziehung gelöscht",
 | 
			
		||||
    "type-relations-delete": "Alle Beziehungen gelöscht",
 | 
			
		||||
    "type-alarm-ack": "Bestätigt",
 | 
			
		||||
    "type-alarm-clear": "Gelöscht",
 | 
			
		||||
    "type-alarm-assign": "Zugeweisen",
 | 
			
		||||
    "type-alarm-unassign": "nicht zugewiesen",
 | 
			
		||||
    "type-added-comment": "Kommentar hinzugefügt",
 | 
			
		||||
    "type-updated-comment": "Kommentar aktualisiert",
 | 
			
		||||
    "type-deleted-comment": "Kommentar gelöscht",
 | 
			
		||||
    "type-rest-api-rule-engine-call": "Regelkette REST API Aufruf",
 | 
			
		||||
    "type-made-public": "Öffentlich machen",
 | 
			
		||||
    "type-made-private": "Privat machen",
 | 
			
		||||
    "type-login": "Anmeldung",
 | 
			
		||||
    "type-logout": "Ausloggen",
 | 
			
		||||
    "type-lockout": "Aussperrung",
 | 
			
		||||
@ -392,7 +461,15 @@
 | 
			
		||||
    "action-data": "Aktionsdaten",
 | 
			
		||||
    "failure-details": "Fehlerdetails",
 | 
			
		||||
    "search": "Audit-Protokolle durchsuchen",
 | 
			
		||||
    "clear-search": "Suche leeren"
 | 
			
		||||
    "clear-search": "Suche leeren",
 | 
			
		||||
    "type-assigned-from-tenant": "Vom Tenant zugewiesen",
 | 
			
		||||
    "type-assigned-to-tenant": "Dem Tenant zugewiesen",
 | 
			
		||||
    "type-provision-success": "Gerätebereitstellung erfolgreich",
 | 
			
		||||
    "type-provision-failure": "Gerätebereitstellung fehlgeschlagen",
 | 
			
		||||
    "type-timeseries-updated": "Telemetriedaten aktualisiert",
 | 
			
		||||
    "type-timeseries-deleted": "Telemetriedaten gelöscht",
 | 
			
		||||
    "type-owner-changed": "Besitzer wurde gewechselt",
 | 
			
		||||
    "type-sms-sent": "SMS gesendet"
 | 
			
		||||
  },
 | 
			
		||||
  "confirm-on-exit": {
 | 
			
		||||
    "message": "Sie haben nicht gespeicherte Änderungen. Möchten Sie diese Seite wirklich verlassen?",
 | 
			
		||||
@ -409,7 +486,10 @@
 | 
			
		||||
    "address2": "Adresse 2",
 | 
			
		||||
    "phone": "Telefon",
 | 
			
		||||
    "email": "E-Mail",
 | 
			
		||||
    "no-address": "Keine Adresse"
 | 
			
		||||
    "no-address": "Keine Adresse",
 | 
			
		||||
    "state-max-length": "Staat sollte weniger als 256 Zeichen haben",
 | 
			
		||||
    "phone-max-length": "Telefonnummer sollte weniger als 256 Zeichen haben",
 | 
			
		||||
    "city-max-length": "Ort sollte weniger als 256 Zeichen haben"
 | 
			
		||||
  },
 | 
			
		||||
  "common": {
 | 
			
		||||
    "username": "Benutzername",
 | 
			
		||||
@ -417,7 +497,12 @@
 | 
			
		||||
    "enter-username": "Benutzername eingeben",
 | 
			
		||||
    "enter-password": "Passwort eingeben",
 | 
			
		||||
    "enter-search": "Suche eingeben",
 | 
			
		||||
    "created-time": "Erstellungszeit"
 | 
			
		||||
    "created-time": "Erstellungszeit",
 | 
			
		||||
    "loading": "wird geladen...",
 | 
			
		||||
    "proceed": "Fortfahren",
 | 
			
		||||
    "open-details-page": "Detailseite öffnen",
 | 
			
		||||
    "not-found": "Nicht gefunden",
 | 
			
		||||
    "documentation": "Dokumentation"
 | 
			
		||||
  },
 | 
			
		||||
  "content-type": {
 | 
			
		||||
    "json": "Json",
 | 
			
		||||
@ -425,6 +510,11 @@
 | 
			
		||||
    "binary": "Binär (Base64)"
 | 
			
		||||
  },
 | 
			
		||||
  "customer": {
 | 
			
		||||
    "all": "Alle",
 | 
			
		||||
    "all-customers": "Alle Kunden",
 | 
			
		||||
    "groups": "Gruppen",
 | 
			
		||||
    "shared": "Geteilt",
 | 
			
		||||
    "hierarchy": "Hierarchie",
 | 
			
		||||
    "customer": "Kunde",
 | 
			
		||||
    "customers": "Kunden",
 | 
			
		||||
    "management": "Kundenverwaltung",
 | 
			
		||||
@ -437,7 +527,6 @@
 | 
			
		||||
    "public-devices": "Öffentliche Geräte",
 | 
			
		||||
    "public-assets": "Öffentliche Objekte",
 | 
			
		||||
    "public-entity-views": "Öffentliche Entitätsansichten",
 | 
			
		||||
    "public-edges": "Öffentliche Rand",
 | 
			
		||||
    "add": "Kunde hinzufügen",
 | 
			
		||||
    "delete": "Kunde löschen",
 | 
			
		||||
    "manage-customer-users": "Kundenbenutzer verwalten",
 | 
			
		||||
@ -446,7 +535,6 @@
 | 
			
		||||
    "manage-public-devices": "Öffentliche Geräte verwalten",
 | 
			
		||||
    "manage-public-dashboards": "Öffentliche Dashboards verwalten",
 | 
			
		||||
    "manage-customer-assets": "Kundenobjekte verwalten",
 | 
			
		||||
    "manage-customer-edges": "Randobjekte verwalten",
 | 
			
		||||
    "manage-public-assets": "Öffentliche Objekte verwalten",
 | 
			
		||||
    "manage-public-edges": "Öffentliche Rand verwalten",
 | 
			
		||||
    "add-customer-text": "Neuen Kunden hinzufügen",
 | 
			
		||||
@ -463,6 +551,7 @@
 | 
			
		||||
    "manage-dashboards": "Dashboards verwalten",
 | 
			
		||||
    "title": "Titel",
 | 
			
		||||
    "title-required": "Titel ist erforderlich.",
 | 
			
		||||
    "title-max-length": "Titel sollte weniger asl 256 Zeichen haben",
 | 
			
		||||
    "description": "Beschreibung",
 | 
			
		||||
    "details": "Details",
 | 
			
		||||
    "events": "Ereignisse",
 | 
			
		||||
@ -1588,26 +1677,98 @@
 | 
			
		||||
    "hours": "Stunden",
 | 
			
		||||
    "minutes": "Minuten",
 | 
			
		||||
    "seconds": "Sekunden",
 | 
			
		||||
    "advanced": "Erweitert"
 | 
			
		||||
    "advanced": "Erweitert",
 | 
			
		||||
    "predefined": {
 | 
			
		||||
      "yesterday": "Gestern",
 | 
			
		||||
      "day-before-yesterday": "Vorgestern",
 | 
			
		||||
      "this-day-last-week": "Dieser Tag letzte Woche",
 | 
			
		||||
      "previous-week": "Letzte Woche (So - Sa)",
 | 
			
		||||
      "previous-week-iso": "Letzte Woche (Mo - So)",
 | 
			
		||||
      "previous-month": "Letzter Monat",
 | 
			
		||||
      "previous-quarter": "Letztes Quartal",
 | 
			
		||||
      "previous-half-year": "Letztes Halbjahr",
 | 
			
		||||
      "previous-year": "Letztes Jahr",
 | 
			
		||||
      "current-hour": "Aktuelle Stunde",
 | 
			
		||||
      "current-day": "Aktueller Tag",
 | 
			
		||||
      "current-day-so-far": "Aktueller Tag bisher",
 | 
			
		||||
      "current-week": "Aktuelle Wiche (So - Sa)",
 | 
			
		||||
      "current-week-iso": "Aktuelle Woche (Mo - So)",
 | 
			
		||||
      "current-week-so-far": "Aktuelle Woche bisher (So - Sa)",
 | 
			
		||||
      "current-week-iso-so-far": "Aktuelle Woche bisher (Mo - So)",
 | 
			
		||||
      "current-month": "Aktueller Monat",
 | 
			
		||||
      "current-month-so-far": "Aktueller Monat bisher",
 | 
			
		||||
      "current-quarter": "Aktuelles Quartal",
 | 
			
		||||
      "current-quarter-so-far": "Aktuelles Quartal bisher",
 | 
			
		||||
      "current-half-year": "Aktuelles Halbjahr",
 | 
			
		||||
      "current-half-year-so-far": "Aktuelles Halbjahr bisher",
 | 
			
		||||
      "current-year": "Aktuelles Jahr",
 | 
			
		||||
      "current-year-so-far": "Aktuelles Jahr bisher"
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  "timeunit": {
 | 
			
		||||
    "milliseconds": "Millisekunden",
 | 
			
		||||
    "seconds": "Sekunden",
 | 
			
		||||
    "minutes": "Minuten",
 | 
			
		||||
    "hours": "Stunden",
 | 
			
		||||
    "days": "Tage"
 | 
			
		||||
  },
 | 
			
		||||
  "timewindow": {
 | 
			
		||||
    "timewindow": "Zeitfenster",
 | 
			
		||||
    "years": "{ years, plural, =1 { Jahr } other {# Jahre } }",
 | 
			
		||||
    "years-short": "{{ years }}J",
 | 
			
		||||
    "months": "{ months, plural, =1 { Monat } other {# Monate } }",
 | 
			
		||||
    "months-short": "{{ months }}M",
 | 
			
		||||
    "weeks": "{ weeks, plural, =1 { Woche } other {# Wochen } }",
 | 
			
		||||
    "weeks-short": "{{ weeks }}W",
 | 
			
		||||
    "days": "{ days, plural, =1 { Tag } other {# Tage } }",
 | 
			
		||||
    "days-short": "{{ days }}T",
 | 
			
		||||
    "hours": "{ hours, plural, =0 { Stunde } =1 {1 Stunde } other {# Stunden } }",
 | 
			
		||||
    "hr": "{{ hr }} Std",
 | 
			
		||||
    "hr-short": "{{ hr }}S",
 | 
			
		||||
    "minutes": "{ minutes, plural, =0 { Minute } =1 {1 Minute } other {# Minuten } }",
 | 
			
		||||
    "min": "{{ min }} Min",
 | 
			
		||||
    "min-short": "{{ min }}m",
 | 
			
		||||
    "seconds": "{ seconds, plural, =0 { Sekunde } =1 {1 Sekunde } other {# Sekunden } }",
 | 
			
		||||
    "sec": "{{ sec }} Sek",
 | 
			
		||||
    "sec-short": "{{ sec }}s",
 | 
			
		||||
    "short": {
 | 
			
		||||
      "days": "{ days, plural, =1 {1 Tag } other {# Tage } }",
 | 
			
		||||
      "hours": "{ hours, plural, =1 {1 Stunde } other {# Stunden } }",
 | 
			
		||||
      "minutes": "{{minutes}} Min ",
 | 
			
		||||
      "seconds": "{{seconds}} Sek "
 | 
			
		||||
    },
 | 
			
		||||
    "realtime": "Echtzeit",
 | 
			
		||||
    "history": "Historie",
 | 
			
		||||
    "last-prefix": "letzte",
 | 
			
		||||
    "period": "von {{ startTime }} bis {{ endTime }}",
 | 
			
		||||
    "edit": "Zeitfenster bearbeiten",
 | 
			
		||||
    "date-range": "Datumsbereich",
 | 
			
		||||
    "for-all-time": "Für immer",
 | 
			
		||||
    "last": "Letzte",
 | 
			
		||||
    "time-period": "Zeitfenster"
 | 
			
		||||
    "time-period": "Zeitfenster",
 | 
			
		||||
    "hide": "Verstecken",
 | 
			
		||||
    "interval": "Intervall",
 | 
			
		||||
    "just-now": "Soeben",
 | 
			
		||||
    "just-now-lower": "soeben",
 | 
			
		||||
    "ago": "vor",
 | 
			
		||||
    "style": "Zeitfenster Style",
 | 
			
		||||
    "icon": "Symbol",
 | 
			
		||||
    "icon-position": "Position Symbol",
 | 
			
		||||
    "icon-position-left": "Links",
 | 
			
		||||
    "icon-position-right": "Rechts",
 | 
			
		||||
    "font": "Schriftart",
 | 
			
		||||
    "color": "Farbe",
 | 
			
		||||
    "displayTypePrefix": "Echtzeit-/Verlaufspräfix anzeigen",
 | 
			
		||||
    "preview": "Vorschau"
 | 
			
		||||
  },
 | 
			
		||||
  "user": {
 | 
			
		||||
    "user": "User",
 | 
			
		||||
    "users": "Users",
 | 
			
		||||
    "customer-users": "Kunden Users",
 | 
			
		||||
    "all": "Alle",
 | 
			
		||||
    "all-users": "Alle Benutzer",
 | 
			
		||||
    "groups": "Gruppen",
 | 
			
		||||
    "user": "Benutzer",
 | 
			
		||||
    "users": "Benutzer",
 | 
			
		||||
    "management": "Benutzerverwaltung",
 | 
			
		||||
    "customer-users": "Kunden-Benutzer",
 | 
			
		||||
    "tenant-admins": "Mandanten-Administratoren",
 | 
			
		||||
    "sys-admin": "System-Administrator",
 | 
			
		||||
    "tenant-admin": "Mandanten-Administrator",
 | 
			
		||||
@ -1618,6 +1779,7 @@
 | 
			
		||||
    "add-user-text": "Neuen Benutzer hinzufügen",
 | 
			
		||||
    "no-users-text": "Keine Benutzer gefunden",
 | 
			
		||||
    "user-details": "Benutzer-Details",
 | 
			
		||||
    "delete-users": "Benutzer löschen",
 | 
			
		||||
    "delete-user-title": "Möchten Sie den Benutzer '{{userEmail}}' wirklich löschen?",
 | 
			
		||||
    "delete-user-text": "Vorsicht, nach Bestätigung werden der Benutzer und alle zugehörigen Daten gelöscht.",
 | 
			
		||||
    "delete-users-title": "Sind Sie sicher, dass Sie { count, plural, =1 {1 Benutzer} other {# Benutzer} } löschen möchten??",
 | 
			
		||||
@ -1649,7 +1811,11 @@
 | 
			
		||||
    "disable-account": "Benutzerkonto deaktivieren",
 | 
			
		||||
    "enable-account": "Benutzerkonto aktivieren",
 | 
			
		||||
    "enable-account-message": "Benutzerkonto wurde erfolgreich aktiviert!",
 | 
			
		||||
    "disable-account-message": "Benutzerkonto wurde erfolgreich deaktiviert!"
 | 
			
		||||
    "disable-account-message": "Benutzerkonto wurde erfolgreich deaktiviert!",
 | 
			
		||||
    "copyId": "Benutzer-Id kopieren",
 | 
			
		||||
    "idCopiedMessage": "Benutzer-Id in die Zwischenablage kopiert",
 | 
			
		||||
    "user-list": "Benutzerliste",
 | 
			
		||||
    "user-list-required": "Benutzerliste ist erforderlich"
 | 
			
		||||
  },
 | 
			
		||||
  "value": {
 | 
			
		||||
    "type": "Wertetyp",
 | 
			
		||||
@ -1813,6 +1979,27 @@
 | 
			
		||||
  },
 | 
			
		||||
  "widgets": {
 | 
			
		||||
    "date-range-navigator": {
 | 
			
		||||
      "date-range-picker-settings": "Einstellungen Datumsbereichsauswahl",
 | 
			
		||||
      "hide-date-range-picker": "Datumsbereichsauswahl verstecken",
 | 
			
		||||
      "picker-one-panel": "Datumsbereichsauswahl in einem Bereich",
 | 
			
		||||
      "picker-auto-confirm": "Automatische Bestätigung der Datumsbereichsauswahl",
 | 
			
		||||
      "picker-show-template": "Vorlage für die Anzeige der Datumsbereichsauswahl",
 | 
			
		||||
      "first-day-of-week": "Erster Tag der Woche",
 | 
			
		||||
      "interval-settings": "Intervalleinstellungen",
 | 
			
		||||
      "hide-interval": "Intervall verstecken",
 | 
			
		||||
      "initial-interval": "Anfangsintervall",
 | 
			
		||||
      "interval-hour": "Stunde",
 | 
			
		||||
      "interval-day": "Tag",
 | 
			
		||||
      "interval-week": "Woche",
 | 
			
		||||
      "interval-two-weeks": "2 Wochen",
 | 
			
		||||
      "interval-month": "Monat",
 | 
			
		||||
      "interval-three-months": "3 Monate",
 | 
			
		||||
      "interval-six-months": "6 Monate",
 | 
			
		||||
      "step-settings": "Schritteinstellungen",
 | 
			
		||||
      "hide-step-size": "Schrittweite verstecken",
 | 
			
		||||
      "initial-step-size": "Anfangsschrittweite",
 | 
			
		||||
      "hide-labels": "Beschriftung verstecken",
 | 
			
		||||
      "use-session-storage": "Sessionspeicher verwenden",
 | 
			
		||||
      "localizationMap": {
 | 
			
		||||
        "Sun": "So.",
 | 
			
		||||
        "Mon": "Mo.",
 | 
			
		||||
@ -1897,6 +2084,7 @@
 | 
			
		||||
  },
 | 
			
		||||
  "icon": {
 | 
			
		||||
    "icon": "Symbol",
 | 
			
		||||
    "icons": "Symbole",
 | 
			
		||||
    "select-icon": "Symbol auswählen",
 | 
			
		||||
    "material-icons": "Material-Symbole",
 | 
			
		||||
    "show-all": "Alle Symbole anzeigen"
 | 
			
		||||
 | 
			
		||||
@ -3331,15 +3331,13 @@
 | 
			
		||||
        "recipient-type": {
 | 
			
		||||
            "affected-tenant-administrators": "Affected tenant administrators",
 | 
			
		||||
            "affected-user": "Affected user",
 | 
			
		||||
            "affected-user-hint": "Affected user hint",
 | 
			
		||||
            "all-users": "All users",
 | 
			
		||||
            "customer-users": "Customer users",
 | 
			
		||||
            "system-administrators": "System administrators",
 | 
			
		||||
            "tenant-administrators": "Tenant administrators",
 | 
			
		||||
            "user-filters": "User filter",
 | 
			
		||||
            "user-list": "User list",
 | 
			
		||||
            "users-entity-owner": "Users of the entity owner",
 | 
			
		||||
            "users-entity-owner-hint": "Users of the entity owner hint"
 | 
			
		||||
            "users-entity-owner": "Users of the entity owner"
 | 
			
		||||
        },
 | 
			
		||||
        "recipients": "Recipients",
 | 
			
		||||
        "notification-recipients": "Notifications / Recipients",
 | 
			
		||||
 | 
			
		||||
@ -3247,15 +3247,13 @@
 | 
			
		||||
        "recipient-type": {
 | 
			
		||||
            "affected-tenant-administrators": "Administradores afectados",
 | 
			
		||||
            "affected-user": "Usuario afectado",
 | 
			
		||||
            "affected-user-hint": "Sugerencia en usuario afectado",
 | 
			
		||||
            "all-users": "Todos los usuarios",
 | 
			
		||||
            "customer-users": "Usuarios del cliente",
 | 
			
		||||
            "system-administrators": "Administradores del sistema",
 | 
			
		||||
            "tenant-administrators": "Administradores de propietarios",
 | 
			
		||||
            "user-filters": "Filtro de usuarios",
 | 
			
		||||
            "user-list": "Lista de usuarios",
 | 
			
		||||
            "users-entity-owner": "Usuarios que sean propietarios de la entidad",
 | 
			
		||||
            "users-entity-owner-hint": "Sugerencia en usuarios propietarios de la entidad"
 | 
			
		||||
            "users-entity-owner": "Usuarios que sean propietarios de la entidad"
 | 
			
		||||
        },
 | 
			
		||||
        "recipients": "Destinatarios",
 | 
			
		||||
        "notification-recipients": "Notificaciones / Destinatarios",
 | 
			
		||||
 | 
			
		||||
@ -3902,7 +3902,6 @@
 | 
			
		||||
        "recipient-type": {
 | 
			
		||||
            "affected-tenant-administrators": "Betrokken tenantbeheerders",
 | 
			
		||||
            "affected-user": "Betrokken gebruiker",
 | 
			
		||||
            "affected-user-hint": "Betrokken gebruikershint",
 | 
			
		||||
            "all-users": "Alle gebruikers",
 | 
			
		||||
            "customer-users": "Klant-gebruikers",
 | 
			
		||||
            "system-administrators": "Systeembeheerders",
 | 
			
		||||
@ -3911,8 +3910,7 @@
 | 
			
		||||
            "user-group-list": "Lijst met gebruikersgroepen",
 | 
			
		||||
            "user-list": "Lijst met gebruikers",
 | 
			
		||||
            "user-role": "Rol van de gebruiker",
 | 
			
		||||
            "users-entity-owner": "Gebruikers van de entiteitseigenaar",
 | 
			
		||||
            "users-entity-owner-hint": "Hint voor gebruikers van de eigenaar van de entiteit"
 | 
			
		||||
            "users-entity-owner": "Gebruikers van de entiteitseigenaar"
 | 
			
		||||
        },
 | 
			
		||||
        "recipients": "Ontvangers",
 | 
			
		||||
        "notification-recipients": "Meldingen / Ontvangers",
 | 
			
		||||
@ -6950,4 +6948,4 @@
 | 
			
		||||
            "zh_TW": "繁體中文"
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user