Communication
Publishing a message
Since version 2.5, you have been able to publish messages to one or more tenants or to a specific version of a cluster. A common use case is the announcement of an upcoming update.
A message is composed of a title and a detail. Publication consists of sending a pub sub with the topic name TenantManagementMessage.
From the tenants screen
You can publish a message to a selection of tenants from the tenants screen using the dedicated button : 
From the cluster screen
You can publish a message to a selection of versions from the cluster screen using the dedicated button : 
From the API
It is also possible to send a message by calling the following API /api/v1/methods/messagepublication. The parameters to be supplied are described in the MessagePublicationArguments.
Receiving the message in your cluster
From the module NeosNotificationCenter
The module incorporates a message receiver. It displays messages in the form of warning toast. To find out more, see the documentation on notification center.
From your own server method
To receive the message in your cluster, you must subscribe to the event TenantManagementMessage in a server method. The arguments are described in the MessagePublicationArguments class. To find out more about event subscription, see the documentation on pub/sub.
Here is an example of a server method that receives the message and displays it in the console.
// Injected services
private readonly IUserNotification _userNotification;
private readonly IApplicationInfo _applicationInfo;
private readonly INeosTenantInfoAccessor _tenantAccessor;
private readonly INeosLogger<ITenantManagementMessageReception> _logger;
public Task ExecuteAsync(Neos.UserNotifications.Application.Abstractions.DataObjects.TenantManagementMessageArguments tenantManagementMessageArguments, CancellationToken cancellationToken)
{
// Get information about the current tenant and cluster
string? currentTenantIdentifier = _tenantAccessor.NeosTenantInfo?.Identifier;
string currentClusterName = _applicationInfo.ClusterName;
string currentClusterVersion = _applicationInfo.ClusterVersion;
// Log the received message and its targeting details
_logger.LogInformation($"Received message in cluster {currentClusterName}-{currentClusterVersion}/{currentTenantIdentifier} (" +
$"tenants: {string.Join(", ", tenantManagementMessageArguments.Tenants.Select(t => t.Identifier))}; " +
$"clusters: {string.Join(", ", tenantManagementMessageArguments.Clusters.Select(c => c.Name + "-" + c.Version))}; " +
$"title: {tenantManagementMessageArguments.Title}; message: {tenantManagementMessageArguments.Message})");
if (currentTenantIdentifier != null &&
// Determine if the message is for the current tenant
// If no tenant identifiers are specified, the message is for all tenants
(tenantManagementMessageArguments.Tenants.Length == 0 ||
tenantManagementMessageArguments.Tenants.Select(t => t.Identifier).Contains(currentTenantIdentifier)) &&
// Determine if the message is for the current cluster
// If no cluster names are specified, the message is for all clusters
(tenantManagementMessageArguments.Clusters.Length == 0 ||
(tenantManagementMessageArguments.Clusters.Select(t => t.Name).Contains(currentClusterName) &&
tenantManagementMessageArguments.Clusters.Select(t => t.Version).Contains(currentClusterVersion))))
{
// Send notification to all users in the current tenant
UserNotificationOptions notificationOptions = new UserNotificationOptions(
tenantManagementMessageArguments.Title,
tenantManagementMessageArguments.Message,
UserNotificationCategories.TenantManagementMessageReception)
.WithSeverity(UserNotificationSeverity.Warning);
notificationOptions.ToAllUsersInCurrentTenant();
await _userNotification.SendNotificationAsync(notificationOptions, cancellationToken);
}
}