You are here

public function Subscribers::sendMessage in Message Subscribe 8

Process a message and send to subscribed users.

$subscribe_options['uids'] = array(
  1 => array(
    'notifiers' => array(
      'email',
    ),
  ),
);
$context = array(
  'node' => array(
    1,
  ),
  // The node author.
  'user' => array(
    10,
  ),
  // Related taxonomy terms.
  'taxonomy_term' => array(
    100,
    200,
    300,
  ),
);

Parameters

\Drupal\Core\Entity\EntityInterface $entity: The entity object to process subscriptions and send notifications for.

\Drupal\message\MessageInterface $message: The message object.

array $notify_options: (optional) An array of options to be passed to the message notifier service. See `\Drupal\message_notify\MessageNotifier::send()`.

array $subscribe_options: (optional) Array with the following optional values:

  • 'save message' (defaults to TRUE) Determine if the Message should be saved.
  • 'skip context' (defaults to FALSE) determine if extracting basic context should be skipped in `self::getSubscribers()`.
  • 'last uid' (defaults to 0) Only query UIDs greater than this UID.
  • 'uids': Array of user IDs to be processed. Setting this, will cause skipping `self::getSubscribers()` to get the subscribed users.
  • 'range': (defaults to FALSE) limit the number of items to fetch in the subscribers query.
  • 'end time': The timestamp of the time limit for the function to execute. Defaults to FALSE, meaning there is no time limitation.
  • 'use queue': Determine if queue API should be used to
  • 'queue': Set to TRUE to indicate the processing is done via a queue worker.
  • 'entity access: (defaults to TRUE) determine if access to view the entity should be applied when getting the list of subscribed users.
  • 'notify blocked users' (defaults to the global setting in `message_subscribe.settings`) determine whether blocked users should be notified. Typically this should be used in conjunction with 'entity access' to ensure that blocked users don't receive notifications about entities which they used to have access to before they were blocked.
  • 'notify message owner' (defaults to the global setting in `message_subscribe.settings`) determines if the user that created the entity gets notified of their own action. If TRUE the author will get notified.

array $context: (optional) array keyed with the entity type and array of entity IDs as the value. For example, if the event is related to a node entity, the implementing module might pass along with the node itself, the node author and related taxonomy terms.

Example usage.

Overrides SubscribersInterface::sendMessage

File

src/Subscribers.php, line 142

Class

Subscribers
A message subscribers service.

Namespace

Drupal\message_subscribe

Code

public function sendMessage(EntityInterface $entity, MessageInterface $message, array $notify_options = [], array $subscribe_options = [], array $context = []) {
  $use_queue = isset($subscribe_options['use queue']) ? $subscribe_options['use queue'] : $this->config
    ->get('use_queue');
  $notify_message_owner = isset($subscribe_options['notify message owner']) ? $subscribe_options['notify message owner'] : $this->config
    ->get('notify_own_actions');

  // Save message by default.
  $subscribe_options += [
    'save message' => TRUE,
    'skip context' => FALSE,
    'last uid' => 0,
    'uids' => [],
    'range' => $use_queue ? 100 : FALSE,
    'end time' => FALSE,
    'use queue' => $use_queue,
    'queue' => FALSE,
    'entity access' => TRUE,
    'notify blocked users' => FALSE,
    'notify message owner' => $notify_message_owner,
  ];
  if (empty($message
    ->id()) && $subscribe_options['save message']) {
    $message
      ->save();
  }
  if ($use_queue && empty($subscribe_options['queue'])) {
    if (empty($message
      ->id())) {
      throw new MessageSubscribeException('Cannot add a non-saved message to the queue.');
    }

    // Get the context once, so we don't need to process it every time
    // a worker claims the item.
    $context = $context ?: $this
      ->getBasicContext($entity, $subscribe_options['skip context'], $context);

    // Context is already set, skip when processing queue item.
    $subscribe_options['skip context'] = TRUE;

    // Add item to the queue.
    $task = [
      'message' => $message,
      // Clone the entity first to avoid any oddness with serialization.
      // @see https://www.drupal.org/project/drupal/issues/2971157
      'entity' => clone $entity,
      'notify_options' => $notify_options,
      'subscribe_options' => $subscribe_options,
      'context' => $context,
    ];

    // Exit now, as messages will be processed via queue API.
    $this->queue
      ->createItem($task);
    return;
  }
  $message->message_subscribe = [];

  // Retrieve all users subscribed.
  $uids = [];
  if ($subscribe_options['uids']) {

    // We got a list of user IDs directly from the implementing module,
    // However we need to adhere to the range.
    $uids = $subscribe_options['range'] ? array_slice($subscribe_options['uids'], 0, $subscribe_options['range'], TRUE) : $subscribe_options['uids'];
  }
  if (empty($uids) && !($uids = $this
    ->getSubscribers($entity, $message, $subscribe_options, $context))) {

    // If we use a queue, it will be deleted.
    return;
  }
  $this
    ->debug('Preparing to process subscriptions for users: @uids', [
    '@uids' => implode(', ', array_keys($uids)),
  ]);
  foreach ($uids as $uid => $delivery_candidate) {
    $last_uid = $uid;

    // Clone the message in case it will need to be saved, it won't
    // overwrite the existing one.
    $cloned_message = $message
      ->createDuplicate();

    // Push a copy of the original message into the new one. The key
    // `original` is not used here as that has special meaning and can prevent
    // field values from being saved.
    // @see SqlContentEntityStorage::saveToDedicatedTables().
    $cloned_message->original_message = $message;

    // Set the owner to this user.
    $cloned_message
      ->setOwnerId($delivery_candidate
      ->getAccountId());

    // Allow modules to alter the message for the specific user.
    $this->moduleHandler
      ->alter('message_subscribe_message', $cloned_message, $delivery_candidate);

    // Send the message using the required notifiers.
    $this
      ->debug('Preparing delivery for uid @user with notifiers @notifiers', [
      '@user' => $uid,
      '@notifiers' => implode(', ', $delivery_candidate
        ->getNotifiers()),
    ]);
    foreach ($delivery_candidate
      ->getNotifiers() as $notifier_name) {
      $options = !empty($notify_options[$notifier_name]) ? $notify_options[$notifier_name] : [];
      $options += [
        'save on fail' => FALSE,
        'save on success' => FALSE,
        'context' => $context,
      ];
      $result = $this->messageNotifier
        ->send($cloned_message, $options, $notifier_name);
      $this
        ->debug($result ? 'Successfully sent message via notifier @notifier to user @uid' : 'Failed to send message via notifier @notifier to user @uid', [
        '@notifier' => $notifier_name,
        '@uid' => $uid,
      ]);

      // Check we didn't timeout.
      if ($use_queue && $subscribe_options['queue']['end time'] && time() < $subscribe_options['queue']['end time']) {
        continue 2;
      }
    }
  }
  if ($use_queue) {

    // Add item to the queue.
    $task = [
      'message' => $message,
      'entity' => $entity,
      'notify_options' => $notify_options,
      'subscribe_options' => $subscribe_options,
      'context' => $context,
    ];
    $task['subscribe_options']['last uid'] = $last_uid;
    $this
      ->debug('Queuing new batch with last uid of @uid', [
      '@uid' => $last_uid,
    ]);

    // Create a new queue item, with the last user ID.
    $this->queue
      ->createItem($task);
  }
}