View source
<?php
namespace Drupal\message_subscribe;
use Drupal\comment\CommentInterface;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Entity\RevisionLogInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Queue\QueueFactory;
use Drupal\Core\Session\AccountInterface;
use Drupal\flag\FlagServiceInterface;
use Drupal\message\MessageInterface;
use Drupal\message_notify\MessageNotifier;
use Drupal\message_subscribe\Exception\MessageSubscribeException;
use Drupal\og\MembershipManagerInterface;
use Drupal\user\EntityOwnerInterface;
use Psr\Log\LoggerInterface;
class Subscribers implements SubscribersInterface {
protected $config;
protected $entityTypeManager;
protected $flagService;
protected $messageNotifier;
protected $moduleHandler;
protected $membershipManager;
protected $queue;
protected $logger;
protected $debug = FALSE;
public function __construct(FlagServiceInterface $flag_service, ConfigFactoryInterface $config_factory, EntityTypeManagerInterface $entity_type_manager, MessageNotifier $message_notifier, ModuleHandlerInterface $module_handler, QueueFactory $queue) {
$this->config = $config_factory
->get('message_subscribe.settings');
$this->entityTypeManager = $entity_type_manager;
$this->flagService = $flag_service;
$this->messageNotifier = $message_notifier;
$this->moduleHandler = $module_handler;
$this->queue = $queue
->get('message_subscribe');
$this->debug = $this->config
->get('debug_mode');
}
public function setMembershipManager(MembershipManagerInterface $membership_manager) {
$this->membershipManager = $membership_manager;
}
public function setLoggerChannel(LoggerInterface $logger) {
$this->logger = $logger;
}
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');
$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.');
}
$context = $context ?: $this
->getBasicContext($entity, $subscribe_options['skip context'], $context);
$subscribe_options['skip context'] = TRUE;
$task = [
'message' => $message,
'entity' => clone $entity,
'notify_options' => $notify_options,
'subscribe_options' => $subscribe_options,
'context' => $context,
];
$this->queue
->createItem($task);
return;
}
$message->message_subscribe = [];
$uids = [];
if ($subscribe_options['uids']) {
$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))) {
return;
}
$this
->debug('Preparing to process subscriptions for users: @uids', [
'@uids' => implode(', ', array_keys($uids)),
]);
foreach ($uids as $uid => $delivery_candidate) {
$last_uid = $uid;
$cloned_message = $message
->createDuplicate();
$cloned_message->original_message = $message;
$cloned_message
->setOwnerId($delivery_candidate
->getAccountId());
$this->moduleHandler
->alter('message_subscribe_message', $cloned_message, $delivery_candidate);
$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,
]);
if ($use_queue && $subscribe_options['queue']['end time'] && time() < $subscribe_options['queue']['end time']) {
continue 2;
}
}
}
if ($use_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,
]);
$this->queue
->createItem($task);
}
}
public function getSubscribers(EntityInterface $entity, MessageInterface $message, array $options = [], array &$context = []) {
$context = !empty($context) ? $context : $this
->getBasicContext($entity, !empty($options['skip context']), $context);
$notify_message_owner = isset($options['notify message owner']) ? $options['notify message owner'] : $this->config
->get('notify_own_actions');
$uids = [];
foreach ($this->moduleHandler
->getImplementations('message_subscribe_get_subscribers') as $module) {
$function = $module . '_message_subscribe_get_subscribers';
$result = $function($message, $options, $context);
$this
->debug('Found @uids from @function', [
'@uids' => implode(', ', array_keys($result)),
'@function' => $function,
]);
$uids += $result;
}
if (empty($options['notify blocked users']) && !empty($uids)) {
$query = $this->entityTypeManager
->getStorage('user')
->getQuery();
$results = $query
->condition('status', 1)
->condition('uid', array_keys($uids), 'IN')
->execute();
if (!empty($results)) {
$uids = array_intersect_key($uids, $results);
}
else {
$uids = [];
}
}
foreach ($uids as $uid => $values) {
if (!$notify_message_owner && $this
->isEntityOwner($entity, $uid)) {
$this
->debug('Removing @uid from recipient list since they are the entity owner.', [
'@uid' => $uid,
]);
unset($uids[$uid]);
}
if (!empty($options['entity access'])) {
$account = $this->entityTypeManager
->getStorage('user')
->load($uid);
if (!$entity
->access('view', $account)) {
$this
->debug('Removing @uid from recipient list since they do not have view access.', [
'@uid' => $uid,
]);
unset($uids[$uid]);
}
}
}
$this
->debug('Recipients after access filter and entity owner filter: @uids', [
'@uids' => implode(', ', array_keys($uids)),
]);
$values = [
'context' => $context,
'entity_type' => $entity
->getEntityTypeId(),
'entity' => $entity,
'message' => $message,
'subscribe_options' => $options,
];
$this
->addDefaultNotifiers($uids);
$this
->debug('Recipient list after default notifiers: @uids', [
'@uids' => implode(', ', array_keys($uids)),
]);
$this->moduleHandler
->alter('message_subscribe_get_subscribers', $uids, $values);
ksort($uids);
$this
->debug('Recipient list after ksort and alter hook: @uids', [
'@uids' => implode(', ', array_keys($uids)),
]);
return $uids;
}
protected function isEntityOwner(EntityInterface $entity, $uid) {
$is_owner = FALSE;
if ($entity instanceof RevisionLogInterface) {
$is_owner = $entity
->getRevisionUserId() == $uid;
}
elseif ($entity instanceof EntityOwnerInterface) {
$is_owner = $entity
->getOwnerId() == $uid;
}
return $is_owner;
}
public function getFlags($entity_type = NULL, $bundle = NULL, AccountInterface $account = NULL) {
$flags = $this->flagService
->getAllFlags($entity_type, $bundle);
if ($account) {
foreach ($flags as $flag_id => $flag) {
if (!$flag
->actionAccess('flag', $account)
->isAllowed() && !$flag
->actionAccess('unflag', $account)
->isAllowed()) {
unset($flags[$flag_id]);
}
}
}
$ms_flags = [];
$prefix = $this->config
->get('flag_prefix') . '_';
foreach ($flags as $flag_name => $flag) {
if (strpos($flag_name, $prefix) === 0) {
$ms_flags[$flag_name] = $flag;
}
}
return $ms_flags;
}
public function getBasicContext(EntityInterface $entity, $skip_detailed_context = FALSE, array $context = []) {
if (empty($context)) {
$id = $entity
->id();
$context[$entity
->getEntityTypeId()][$id] = $id;
}
if ($skip_detailed_context) {
return $context;
}
$context += [
'node' => [],
'user' => [],
'taxonomy_term' => [],
];
if ($entity instanceof CommentInterface) {
$context['node'][$entity
->getCommentedEntityId()] = $entity
->getCommentedEntityId();
$context['user'][$entity
->getOwnerId()] = $entity
->getOwnerId();
}
if (empty($context['node'])) {
return $context;
}
$nodes = $this->entityTypeManager
->getStorage('node')
->loadMultiple($context['node']);
if ($this->moduleHandler
->moduleExists('og')) {
foreach ($nodes as $node) {
foreach ($this->membershipManager
->getGroupIds($node) as $group_type => $gids) {
foreach ($gids as $gid) {
$context[$group_type][$gid] = $gid;
}
}
}
$nodes = $this->entityTypeManager
->getStorage('node')
->loadMultiple($context['node']);
}
foreach ($nodes as $node) {
$context['user'][$node
->getOwnerId()] = $node
->getOwnerId();
if ($this->moduleHandler
->moduleExists('taxonomy')) {
foreach ($node
->getFieldDefinitions() as $field) {
if ($field
->getType() != 'entity_reference' || $field
->getSetting('target_type') != 'taxonomy_term') {
continue;
}
foreach ($node
->get($field
->getName()) as $tid) {
$context['taxonomy_term'][$tid->target_id] = $tid->target_id;
}
}
}
}
return $context;
}
protected function addDefaultNotifiers(array &$uids) {
$notifiers = $this->config
->get('default_notifiers');
if (empty($notifiers)) {
return;
}
foreach (array_keys($uids) as $uid) {
foreach ($notifiers as $notifier) {
$uids[$uid]
->addNotifier($notifier);
}
}
}
protected function debug($message, array $context = []) {
if (!$this->debug) {
return;
}
$this->logger
->debug($message, $context);
}
}