You are here

notify.module in Notify 2.0.x

Hooks for Notify module.

File

notify.module
View source
<?php

/**
 * @file
 * Hooks for Notify module.
 */
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Render\Markup;
use Drupal\node\Entity\NodeType;
use Drupal\node\Entity\Node;
use Drupal\comment\Entity\Comment;
use Drupal\user\Entity\User;
use Drupal\Core\Link;
use Drupal\Core\Url;
use Drupal\Core\Routing\RouteMatchInterface;

/**
 * @file
 * Notify module sends e-mail digests of new content and comments.
 *
 * The notification module allows users to subscribe to periodic e-mails which
 * include all new or revised content and/or comments much like the daily news
 * letters sent by some websites.  Even if this feature is not configured for
 * normal site users, it can be a useful feature for an administrator of a site
 * to monitor content submissions and comment posts.
 */
use Drupal\Core\Mail\MailFormatHelper;
use Drupal\Core\Database\Query\Condition;
use Drupal\Core\Render\Element;
if (!defined('NOTIFY_NODE_TYPE')) {
  define('NOTIFY_NODE_TYPE', 'notify_node_type_');
}

/**
 * Implements hook_help().
 */
function notify_help($route_name, RouteMatchInterface $route_match) {
  switch ($route_name) {
    case 'help.page.notify':
      $output = '<p>' . t('The <strong>Notify</strong> project allows users to subscribe to periodic e-mails which include all new or revised content and/or comments much like the daily news letters sent by some websites.  Even if this feature is not configured for normal site users, it can be a useful feature for an administrator of a site to monitor content submissions and comment posts.') . '</p>';
      $output .= '<p>' . t('The administrator sets the frequency of the e-mails in the notify administration interface. They can also set how many e-mail failures should occur before notify stops sending notifications.  Note that cron must be enabled for notifications to be sent out.') . '</p>';
      $output .= '<p>' . t('For more information please read the <a href=":url"><strong>Notify</strong></a> documentation page.', [
        ':url' => Url::fromUri('https://www.drupal.org/docs/7/modules/notify')
          ->toString(),
      ]) . '</p>';
      return $output;
  }
}

/**
 * Implements hook_cron().
 */
function notify_cron() {
  $config = \Drupal::config('notify.settings');
  $period = $config
    ->get("notify_period");
  if (-2 == $period) {
    return;
  }
  if (-1 == $period) {

    // Never notify.
    return;
  }
  $send_last = $config
    ->get('notify_send_last');
  $cron_next = $config
    ->get('notify_cron_next');
  $doitp = $config
    ->get('notify_users');
  if ($period >= 86400) {
    if ($doitp || $cron_next < \Drupal::time()
      ->getRequestTime()) {
      $doitp = TRUE;
    }
    else {
      return;
    }
  }
  $next = _notify_next_notificaton($send_last);
  if (\Drupal::time()
    ->getRequestTime() > $next || $doitp) {
    list($num_sent, $num_fail) = _notify_send();
    $num_sent += $config
      ->get('notify_num_sent');
    $num_fail += $config
      ->get('notify_num_failed');
    $next = _notify_next_notificaton(\Drupal::time()
      ->getRequestTime());
    \Drupal::configFactory()
      ->getEditable('notify.settings')
      ->set('notify_num_sent', $num_sent)
      ->set('notify_num_failed', $num_fail)
      ->set('notify_cron_next', $next)
      ->save();
  }
}

/**
 * Implements hook_user_cancel().
 */
function notify_user_cancel($edit, $account, $method) {
  global $user;
  \Drupal::database()
    ->delete('notify')
    ->condition('uid', $user->uid)
    ->execute();
}

/**
 * Implements hook_form_FORM_ID_alter().
 */
function notify_form_user_register_form_alter(&$form, &$form_state, $form_id) {
  $config = \Drupal::config('notify.settings');
  $period = $config
    ->get('notify_period');
  if ($period > 0) {
    $form += _notify_user_reg_fields();
  }
}

/**
 * Implements hook_ENTIY_TYPE__insert().
 */
function notify_user_insert(User $account) {
  $uid = $account
    ->id();
  $config = \Drupal::config('notify.settings');
  $id = \Drupal::database()
    ->insert('notify')
    ->fields([
    'uid' => $uid,
    'status' => $config
      ->get('notify_reg_default'),
    'node' => $config
      ->get('notify_def_node'),
    'comment' => $config
      ->get('notify_def_comment'),
  ])
    ->execute();

  // Get permitted node types.
  $nodetypes = $config
    ->get('notify_nodetypes');
  if ($nodetypes != NULL) {
    foreach ($nodetypes as $ntype => $value) {
      if ($value) {
        \Drupal::database()
          ->insert('notify_subscriptions')
          ->fields([
          'uid' => $uid,
          'type' => $ntype,
        ])
          ->execute();
      }
    }
  }
}

/**
 * Implements hook_mail().
 */
function notify_mail($key, &$message, $params) {
  $host = \Drupal::request()
    ->getSchemeAndHttpHost();
  $config = \Drupal::config('notify.settings');
  $siteconfig = \Drupal::config('system.site');
  $username = $params['user']
    ->getDisplayName();
  $useruid = $params['user']
    ->id();
  $sitename = $siteconfig
    ->get('name');
  $sitemail = $siteconfig
    ->get('mail');
  $upl = $params['user']
    ->getPreferredLangcode();
  $messagefrom = $sitename . ' <' . $sitemail . '>';
  $message['headers']['MIME-Version'] = '1.0';

  // Need to do more to send nicely formatted text/html mail.
  // Stay at text/plain for now.
  // $message['headers']['Content-Type'] = 'text/html;charset=utf-8';.
  $message['headers']['Content-Type'] = 'text/plain;charset=utf-8';
  $message['headers']['From'] = $message['headers']['Sender'] = $message['headers']['Errors-To'] = $message['headers']['Reply-to'] = $messagefrom;
  $message['headers']['Return-Path'] = $sitemail;
  $message['subject'] = t('New content notification for @username from @sitename', [
    '@username' => $username,
    '@sitename' => $sitename,
  ], [
    'langcode' => $upl,
  ]);
  $message['body'][] = Markup::create(t('Greetings @user,', [
    '@user' => $username,
  ], [
    'langcode' => $upl,
  ]) . '<br /><br />');
  $message['body'][] = Markup::create(t('this is a notification about new content from @sitename.', [
    '@sitename' => $sitename,
  ], [
    'langcode' => $upl,
  ]) . '<br />');
  $fulltext = t('Click on the links below to see the whole node/comment.', [], [
    'langcode' => $upl,
  ]);
  $message['body'][] = t('This e-mail only lists the titles.', [], [
    'langcode' => $upl,
  ]) . ' ' . $fulltext;
  $message['body'][] = Markup::create($params['content']);
  $message['body'][] = Markup::create('<br />&nbsp;<br />' . t('This is an automatic e-mail from @sitename. To stop receiving these emails, change your notification preferences at @host/user/@useruid/notify.' . '</p>', [
    '@sitename' => $sitename,
    '@host' => $host,
    '@useruid' => $useruid,
  ], [
    'langcode' => $upl,
  ]));
}

/**
 * Implements hook_comment_insert().
 *
 * Add tracked newly created unpublished comments to the unpublished queue.
 */
function notify_comment_insert(EntityInterface $entity) {
  if (!$entity->status->value) {
    \Drupal::database()
      ->insert('notify_unpublished_queue')
      ->fields([
      'nid' => $entity
        ->get('entity_id')
        ->getValue()[0]['target_id'],
      'cid' => $entity
        ->id(),
    ])
      ->execute();
  }
}

/**
 * Implements hook_node_delete().
 *
 * Delete any unpublished contents in the queue associated with the
 * node being deleted.
 */
function notify_node_delete($node) {
  \Drupal::database()
    ->delete('notify_unpublished_queue')
    ->condition('nid', $node
    ->id())
    ->execute();
}

/**
 * Implements hook_comment_delete().
 *
 * Delete the unpublished comment in the queue when the
 * comment itself is deleted.
 */
function notify_comment_delete($comment) {
  \Drupal::database()
    ->delete('notify_unpublished_queue')
    ->condition('cid', $comment
    ->id())
    ->execute();
}

/**
 * Helper function to get array of tracked types.
 *
 * @param array $ntype
 *   Reference to array with tracked types.
 * @param bool $foop
 *   TRUE if we return full list if empty,.
 */
function _notify_get_content_types(&$ntype, $foop) {
  $config = \Drupal::config('notify.settings');
  $ntype = [];
  foreach (NodeType::loadMultiple() as $type => $object) {
    if ($config
      ->get(NOTIFY_NODE_TYPE . $type)) {
      $ntype[] = $type;
    }
  }
  if ($foop && count($ntype) < 1) {
    foreach (NodeType::loadMultiple() as $type => $name) {
      $ntype[] = $type;
    }
  }
}

/**
 * Count the various types of content.
 */
function _notify_count($config) {
  list($res_nodes, $res_comms, $res_nopub, $res_copub, $res_nounp, $res_counp) = _notify_select_content();
  $np = $res_nodes ? count($res_nodes) : 0;
  $cp = $res_comms ? count($res_comms) : 0;
  $nn = $res_nopub ? count($res_nopub) : 0;
  $cn = $res_copub ? count($res_copub) : 0;
  $bu = $res_nounp ? count($res_nounp) : 0;
  $cu = $res_counp ? count($res_counp) : 0;
  return [
    $np,
    $cp,
    $nn,
    $cn,
    $bu,
    $cu,
  ];
}

/**
 * Helper function to set up query objects to select content for
 * counting and sending.
 *
 * Return array has six values:
 * - ordinary published entities: nodes, comments;
 * - in unpublished queue:
 *   published nodes, published comments,
 *   unpublished nodes, unpublished comments,
 *
 * @return array
 *   res_nodes, res_comms, res_nopub, res_copub, res_nounp, res_counp.
 */
function _notify_select_content() {
  $config = \Drupal::config('notify.settings');
  $users = $config
    ->get('notify_users');
  $batch_remain = $users ? count($users) : 0;
  $since = $config
    ->get('notify_send_last');
  if ($batch_remain) {
    $send_start = $config
      ->get('notify_send_start');
  }
  else {
    $send_start = \Drupal::time()
      ->getRequestTime();
  }
  if (!$since) {
    $period = $config
      ->get('notify_period');
    if ($period > 0) {
      $since = $send_start - $period;
    }
  }
  $all = NodeType::loadMultiple();
  $ntype = [];
  foreach ($all as $type => $object) {
    $ntype[] = $type;
  }

  // Build query object to fetch new nodes.
  $q = \Drupal::database()
    ->select('node', 'n');
  $q
    ->join('node_field_data', 'u', 'n.nid = u.nid');
  $q
    ->fields('n', [
    'nid',
  ]);
  if (count($ntype) >= 1) {
    $q
      ->condition('n.type', $ntype, 'IN');
  }
  if ($config
    ->get('notify_include_updates')) {
    $q
      ->condition((new Condition('OR'))
      ->condition((new Condition('AND'))
      ->condition('u.created', $since, '>')
      ->condition('u.created', $send_start, '<='))
      ->condition((new Condition('AND'))
      ->condition('u.changed', $since, '>')
      ->condition('u.changed', $send_start, '<=')));
    $q
      ->orderBy('u.created', 'asc');
    $q->allowRowCount = TRUE;
    $res_nodes = $q
      ->execute()
      ->fetchAll();
  }
  else {
    $q
      ->condition('u.created', $since, '>');
    $q
      ->condition('u.created', $send_start, '<=');
    $q
      ->orderBy('u.created', 'asc');
    $q->allowRowCount = TRUE;
    $res_nodes = $q
      ->execute()
      ->fetchAll();
  }

  // Get published nodes in unpublished queue.
  $q = \Drupal::database()
    ->select('notify_unpublished_queue', 'q');
  $q
    ->join('node', 'n', 'q.nid = n.nid');
  $q
    ->join('node_field_data', 'u', 'q.nid = u.nid');
  $q
    ->fields('q', [
    'nid',
    'cid',
  ]);
  $q
    ->condition('u.status', 1, '=');
  $q
    ->condition('q.cid', 0, '=');
  $q
    ->orderBy('q.nid', 'asc');
  $q->allowRowCount = TRUE;
  $res_nopub = $q
    ->execute()
    ->fetchAll();

  // Get unpublished nodes in unpublished queue.
  $q = \Drupal::database()
    ->select('notify_unpublished_queue', 'q');
  $q
    ->join('node', 'n', 'q.nid = n.nid');
  $q
    ->join('node_field_data', 'u', 'q.nid = u.nid');
  $q
    ->fields('q', [
    'nid',
    'cid',
  ]);
  $q
    ->condition('u.status', 0, '=');
  $q
    ->condition('q.cid', 0, '=');
  $q
    ->orderBy('q.nid', 'asc');
  $q->allowRowCount = TRUE;
  $res_nounp = $q
    ->execute()
    ->fetchAll();
  if (\Drupal::service('module_handler')
    ->moduleExists('comment')) {

    // Fetch new published comments.
    $q = \Drupal::database()
      ->select('comment', 'c');
    $q
      ->join('comment_field_data', 'v', 'c.cid = v.cid');
    $q
      ->join('node', 'n', 'v.entity_id = n.nid');
    $q
      ->fields('c', [
      'cid',
    ]);
    if (count($ntype) >= 1) {
      $q
        ->condition('n.type', $ntype, 'IN');
    }

    // ->condition(((new Condition('AND'))->condition(true)
    // (AND () (OR (AND ()())  (AND ()())))
    if ($config
      ->get('notify_include_updates')) {

      // dpm('incl. updates', 'comms');
      // Only comments attached to nodes are called 'comment'.
      $q
        ->condition((new Condition('AND'))
        ->condition('c.comment_type', 'comment', '=')
        ->condition((new Condition('OR'))
        ->condition((new Condition('AND'))
        ->condition('v.created', $since, '>')
        ->condition('v.created', $send_start, '<='))
        ->condition((new Condition('AND'))
        ->condition('v.changed', $since, '>')
        ->condition('v.changed', $send_start, '<='))));
      $q
        ->orderBy('v.created', 'asc');
      $q->allowRowCount = TRUE;
      $res_comms = $q
        ->execute()
        ->fetchAll();
    }
    else {

      // dpm('no updates', 'comms');.
      $q
        ->condition('c.comment_type', 'comment', '=');
      $q
        ->condition('v.created', $since, '>');
      $q
        ->condition('v.created', $send_start, '<=');
      $q
        ->orderBy('v.created', 'asc');
      $q->allowRowCount = TRUE;
      $res_comms = $q
        ->execute()
        ->fetchAll();
    }

    // Foreach ($res_comms as $row) {
    // dpm($row, 'comms');
    // }
    // Get published comments in unpublished queue.
    $q = \Drupal::database()
      ->select('notify_unpublished_queue', 'q');
    $q
      ->join('comment', 'c', 'q.cid = c.cid');
    $q
      ->join('comment_field_data', 'v', 'q.cid = v.cid');
    $q
      ->fields('q', [
      'nid',
      'cid',
    ]);
    $q
      ->condition('v.status', 1, '=');
    $q
      ->orderBy('q.cid', 'asc');
    $q->allowRowCount = TRUE;
    $res_copub = $q
      ->execute()
      ->fetchAll();

    // Foreach ($res_copub as $row) {
    // dpm($row, 'copub');
    // }
    // Get unpublished comments in unpublished queue.
    $q = \Drupal::database()
      ->select('notify_unpublished_queue', 'q');
    $q
      ->join('comment', 'c', 'q.cid = c.cid');
    $q
      ->join('comment_field_data', 'v', 'q.cid = v.cid');
    $q
      ->fields('q', [
      'nid',
      'cid',
    ]);
    $q
      ->condition('v.status', 0, '=');
    $q
      ->orderBy('q.cid', 'asc');
    $q->allowRowCount = TRUE;
    $res_counp = $q
      ->execute()
      ->fetchAll();

    // Foreach ($res_counp as $row) {
    // dpm($row, 'counp');
    // }.
  }
  else {
    $res_comms = $res_copub = $res_counp = NULL;
  }
  return [
    $res_nodes,
    $res_comms,
    $res_nopub,
    $res_copub,
    $res_nounp,
    $res_counp,
  ];
}

/**
 * Compute the next time a notification should be sent.
 *
 * @param int $send_last
 *   Timestamp of last notification.
 *
 * @return int
 *   -1 never, 0 send instantly, else next time to notify.
 */
function _notify_next_notificaton($send_last) {
  $config = \Drupal::config('notify.settings');
  $period = $config
    ->get('notify_period');

  // Two special cases: Never and instantly.
  if ($period < 0) {
    return -1;
  }
  elseif (!$period) {
    return 0;
  }
  $next_time_to_send = $send_last + $period;
  if ($period < 86400) {
    if (\Drupal::time()
      ->getRequestTime() >= $next_time_to_send) {
      return 0;
    }
    else {
      return $next_time_to_send;
    }
  }

  // Interval >= 1 day.
  $cron_next = $config
    ->get('notify_cron_next');
  if (!$cron_next) {
    $cron_next = _notify_cron_next($next_time_to_send);

    // $config->set('notify_cron_next', $cron_next);
    \Drupal::configFactory()
      ->getEditable('notify.settings')
      ->set('notify_cron_next', $cron_next)
      ->save();
  }
  return $cron_next;
}

/**
 * Function to send next cron.
 */
function _notify_cron_next($next_time_to_send) {
  $config = \Drupal::config('notify.settings');
  $send_hour = $config
    ->get('notify_send_hour');

  // Compute the next as the sending hour today.
  $cron_next = strtotime(date('Y-m-d ', $next_time_to_send) . $send_hour . ':00:00');
  return $cron_next;
}

/**
 * Returns form fields to be added to User Regsitration form.
 */
function _notify_user_reg_fields() {
  $config = \Drupal::config('notify.settings');
  if (!\Drupal::currentUser()
    ->hasPermission('access notify')) {
    return [];
  }

  // Get the variable for how often the notifications are sent out.
  $period = $config
    ->get("notify_period");

  // Add a fieldset containing a checkbox for users to accept getting updates on
  // the registration form.
  $fields['notify_agree'] = [
    '#weight' => $config
      ->get('notify_weightur'),
    '#type' => 'fieldset',
    '#title' => t('E-mail Notifications'),
  ];

  // Add the checkbox to the fieldset.
  $fields['notify_agree']['notify_decision'] = [
    '#type' => 'checkbox',
    '#title' => t('Receive e-mail notifications of new content posted to this site. Notifications are sent every @interval.', [
      '@interval' => \Drupal::service('date.formatter')
        ->formatInterval($period),
    ]),
    '#return_value' => 1,
    '#default_value' => $config
      ->get('notify_reg_default'),
  ];
  return $fields;
}

/**
 * Helper function to send the notification e-mail batch.
 */
function _notify_send() {
  $user = \Drupal::currentUser();
  $config = \Drupal::config('notify.settings');
  $separator = "------------------------------------------------------------------------------<br />";
  $mini_separator = "----<br />";
  $send_start = $config
    ->get('notify_send_start');
  $num_sent = 0;
  $num_fail = 0;
  list($res_nodes, $res_comms, $res_nopub, $res_copub, $res_nounp, $res_counp) = _notify_select_content();
  _notify_get_content_types($defaultlist, TRUE);

  // Get the nodes and comments queued.
  $count = 0;
  $nodes = $comments = [];

  // Ordinary nodes.
  foreach ($res_nodes as $row) {
    $nodes[$row->nid] = Node::load($row->nid);
    $count++;
  }

  // Ordinary comments.
  if ($res_comms) {
    foreach ($res_nopub as $row) {
      if (!isset($nodes[$row->nid])) {
        $nodes[$row->nid] = Node::load($row->nid);
        $count++;
      }
    }
    foreach ($res_comms as $row) {
      $comment = Comment::load($row->cid);
      $comments[$comment
        ->get('entity_id')->target_id][$row->cid] = $comment;
      $count++;
    }
    foreach ($res_copub as $row) {
      if (!isset($comments[$row->nid][$row->cid])) {
        $comments[$row
          ->get('entity_id')->target_id][$row->cid] = Comment::load($row->cid);
        $count++;
      }
    }
  }

  // Published nodes in unpublished queue.
  foreach ($res_nopub as $row) {
    if (!isset($nodes[$row->nid])) {
      $nodes[$row->nid] = Node::load($row->nid);
      $count++;
    }
  }

  // Unpublished nodes in unpublished queue.
  foreach ($res_nounp as $row) {
    if (!isset($nodes[$row->nid])) {
      $nodes[$row->nid] = Node::load($row->nid);
      $count++;
    }
  }
  if ($count) {
    $uresult = $config
      ->get('notify_users');
    if (empty($uresult)) {

      // Set up for sending a new batch. Init all variables.
      $result = \Drupal::database()
        ->select('notify', 'n');
      $result
        ->join('users', 'u', 'n.uid = u.uid');
      $result
        ->join('users_field_data', 'v', 'n.uid = v.uid');
      $result
        ->fields('u', [
        'uid',
      ]);
      $result
        ->fields('v', [
        'name',
        'mail',
      ]);
      $result
        ->fields('n', [
        'node',
        'comment',
        'status',
      ]);
      $result
        ->condition('v.status', 1);
      $result
        ->condition('n.status', 1);
      $result
        ->condition('n.attempts', 5, '<=');
      $result->allowRowCount = TRUE;
      $uresult = $result
        ->execute()
        ->fetchAll(PDO::FETCH_ASSOC);
      \Drupal::configFactory()
        ->getEditable('notify.settings')
        ->set('notify_send_start', \Drupal::time()
        ->getRequestTime())
        ->set('notify_users', $uresult)
        ->set('notify_num_sent', 0)
        ->set('notify_num_failed', 0)
        ->save();
    }
    $batchlimit = $config
      ->get('notify_batch');
    $batchcount = 0;

    // Allow to safely impersonate the recipient so that the node is rendered
    // with correct field permissions.
    $original_user = $user;

    // @todo Fix drupal_save_session() issue.
    if (NULL !== $config
      ->get('notify_skip_nodes')) {
      $notify_skip_nodes = $config
        ->get('notify_skip_nodes');
    }
    else {
      $notify_skip_nodes = [];
    }
    if (NULL !== $config
      ->get('notify_skip_comments')) {
      $notify_skip_comments = $config
        ->get('notify_skip_comments');
    }
    else {
      $notify_skip_comments = [];
    }
    foreach ($uresult as $index => $userrow) {
      if (++$batchcount > $batchlimit) {
        break;
      }
      $userobj = User::load($userrow['uid']);

      // Intentionally replacing the Global $user.
      $user = $userobj;
      $upl = $userobj
        ->getPreferredLangcode();
      $node_body = $comment_body = '';

      // Consider new node content if user has permissions and nodes are ready.
      // $userrow['node']: user subscribes to nodes.
      if ($userrow['node'] && $userobj
        ->hasPermission('access content') && count($nodes)) {
        $node_count = 0;

        // Look at the node.
        foreach ($nodes as $node) {

          // Skip to next if skip flag set for node.
          if (in_array($node
            ->id(), $notify_skip_nodes)) {
            continue;
          }

          // Skip to next if user is not allowed to view this node.
          if (!$userobj
            ->hasPermission('administer nodes') && 0 == $node
            ->isPublished()) {
            continue;
          }
          if (!$userobj
            ->hasPermission('administer nodes') && !in_array($node
            ->getType(), $defaultlist)) {
            continue;
          }
          if (!$config
            ->get('notify_unpublished') && 0 == $node
            ->isPublished()) {
            continue;
          }
          if (!$node
            ->access('view', $userobj)) {
            continue;
          }
          $field = \Drupal::database()
            ->select('notify_subscriptions', 'n')
            ->fields('n', [
            'uid',
            'type',
          ])
            ->condition('uid', $userrow['uid'])
            ->condition('type', $node
            ->getType())
            ->execute()
            ->fetchObject();
          $ml_level = $config
            ->get('notify_multilingual');
          if (!$userobj
            ->hasPermission('administer notify')) {
            if ($ml_level && $node->tnid) {
              if ($node->language != $upl) {
                continue;
              }
            }
            if (2 == $ml_level && 0 == $node->tnid && 'und' != $node->language) {
              continue;
            }
            $ignore_unverified = $config
              ->get('notify_unverified');
            if ($ignore_unverified && !$node->uid) {
              continue;
            }
          }
          $node_revs_list = \Drupal::entityTypeManager()
            ->getStorage('node')
            ->revisionIds($node);
          $nrl_vals = array_values($node_revs_list);
          $vers = array_shift($nrl_vals);
          $node_revision = \Drupal::entityTypeManager()
            ->getStorage('node')
            ->loadRevision($vers);
          $node_body .= '<strong>' . ++$node_count . '.</strong> ' . $node
            ->label() . '. ';
          if (count($node_revs_list) > 1) {
            $update = '(' . t('last updated by @author', [
              '@author' => $node_revision
                ->getOwner()
                ->getDisplayName() ? $node_revision
                ->getOwner()
                ->getDisplayName() : $config
                ->get('anonymous'),
            ], [
              'langcode' => $upl,
            ]) . ') ';
          }
          else {
            $update = '';
          }
          if ($userobj
            ->hasPermission('administer nodes')) {
            $status = $node
              ->isPublished() == 1 ? t('[Published]', [], [
              'langcode' => $upl,
            ]) : t('[Unpublished]', [], [
              'langcode' => $upl,
            ]);
          }
          else {
            $status = '';
          }
          $alias = \Drupal::languageManager()
            ->isMultilingual() ? TRUE : FALSE;
          $options = [
            'alias' => $alias,
            'attributes' => [
              'class' => [
                'read-more',
              ],
            ],
            'absolute' => TRUE,
          ];
          $link = Link::fromTextAndUrl(t('Read more'), Url::fromUri('internal:/node/' . $node
            ->id(), $options))
            ->toString();
          $node_body .= $link . '<br>';
        }

        // Prepend e-mail header as long as user could access at least one node.
        if ($node_count > 0) {
          $node_body = '<p>' . t('<p>Recent new or updated pages - @count', [
            '@count' => \Drupal::translation()
              ->formatPlural($node_count, '1 new post', '@count new posts', [], [
              'langcode' => $upl,
            ]),
          ], [
            'langcode' => $upl,
          ]) . "<br />" . '</p>' . $node_body;
        }
      }

      // Consider new comments if user has permissions and comments are ready.
      if ($userrow['comment'] && $userobj
        ->hasPermission('access comments') && count($comments)) {
        $total_comment_count = 0;
        $node_comment_count = 0;

        // Look at the comment.
        foreach ($comments as $nid => $value) {

          // If we don't already have the node, fetch it.
          if (isset($nodes[$nid])) {
            $node = $nodes[$nid];
          }
          else {
            $node = \Drupal::entityTypeManager()
              ->getStorage('node')
              ->load($nid);
          }
          if (!$node
            ->access('view', $userobj)) {
            continue;
          }
          $comment_count = 0;
          $onecomment = '';

          // Look at the comment.
          foreach ($value as $commobj) {
            if (in_array($commobj
              ->id(), $notify_skip_comments)) {
              continue;
            }
            if (!$userobj
              ->hasPermission('administer comments') && 0 == $commobj
              ->isPublished()) {
              continue;
            }
            if (!$userobj
              ->hasPermission('administer comments') && !in_array($node
              ->getType(), $defaultlist)) {
              continue;
            }
            if (!$config
              ->get('notify_unpublished') && 0 == $commobj
              ->isPublished()) {
              continue;
            }

            // Determine whether to show comment status.
            if ($userobj
              ->hasPermission('administer comments')) {
              $status = $commobj
                ->isPublished() == 1 ? t('[Published]', [], [
                'langcode' => $upl,
              ]) : t('[Unpublished]', [], [
                'langcode' => $upl,
              ]);
            }
            else {
              $status = '';
            }
            $options = [
              'attributes' => [
                'class' => [
                  'read-more',
                ],
              ],
              'fragment' => 'comment-' . $commobj
                ->id(),
              'absolute' => TRUE,
            ];
            $link = Link::fromTextAndUrl(t('Read more'), Url::fromUri('internal:/node/' . $nid, $options))
              ->toString();
            $options2 = [
              'attributes' => [
                'class' => [
                  'read-more',
                ],
              ],
              'fragment' => 'comments-title',
              'absolute' => TRUE,
            ];
            $shortlink = Link::fromTextAndUrl(t('Read more'), Url::fromUri('internal:/node/' . $nid, $options2))
              ->toString();
            $onecomment .= '   ' . ++$comment_count . '. ' . t('@title by @author @status', [
              '@title' => $commobj
                ->label(),
              '@author' => $commobj
                ->getOwner()
                ->getDisplayName() ? $commobj
                ->getOwner()
                ->getDisplayName() : \Drupal::config('anonymous'),
              '@status' => $status,
            ], [
              'langcode' => $upl,
            ]) . $link . "<br />";
            $total_comment_count++;
          }
          if ($comment_count) {
            $comment_body .= '<strong>' . ++$node_comment_count . '.</strong> ';
            $comment_body .= t('@title <span class="counter">(@count)</span>', [
              '@count' => \Drupal::translation()
                ->formatPlural($comment_count, '1 new comment', '@count new comments'),
              '@title' => $node
                ->label(),
              '@type' => $node->type->entity
                ->label(),
              '@author' => $node
                ->getOwner()
                ->getDisplayName() ? $node
                ->getOwner()
                ->getDisplayName() : \Drupal::config('anonymous'),
            ], [
              'langcode' => $upl,
            ]) . ' ' . (isset($shortlink) ? $shortlink : '') . "<br />";
          }
        }
        if ($total_comment_count > 0) {
          $comment_body = '<br />&nbsp;<br /><p>' . t('These are pages that have recent comments - @count', [
            '@count' => \Drupal::translation()
              ->formatPlural($total_comment_count, '1 new comment', '@count new comments'),
          ], [
            'langcode' => $upl,
          ]) . "<br />" . '</p>' . $comment_body;
        }
      }
      $body = $node_body . $comment_body;

      // If there was anything new, send mail.
      if ($body) {
        $watchdog_level = $config
          ->get('notify_watchdog');
        if (\Drupal::service('plugin.manager.mail')
          ->mail('notify', 'notice', $userrow['mail'], $upl, [
          'content' => $body,
          'user' => $userobj,
        ], NULL, TRUE)) {
          if ($watchdog_level == 0) {
            \Drupal::logger('notify')
              ->notice('User %name (%mail) notified successfully.', [
              '%name' => $userrow['name'],
              '%mail' => $userrow['mail'],
            ]);
          }
          $num_sent++;
        }
        else {
          $num_fail++;
          $q = \Drupal::database()
            ->update('notify');
          $q
            ->expression('attempts', 'attempts + 1');
          $q
            ->condition('uid', $userrow['uid']);
          $q
            ->execute();
          if ($watchdog_level <= 2) {
            \Drupal::logger('notify')
              ->notice('User %name (%mail) could not be notified. Mail error.', [
              '%name' => $userrow['name'],
              '%mail' => $userrow['mail'],
            ]);
          }
        }
      }
      unset($uresult[$index]);
      \Drupal::configFactory()
        ->getEditable('notify.settings')
        ->set('notify_users', $uresult)
        ->save();
    }

    // Restore the original user session.
    $user = $original_user;
  }
  $users = $config
    ->get('notify_users');
  $rest = $users ? count($users) : 0;

  // If $rest is empty, then set notify_send_last.
  if (!$rest) {
    $send_start = $config
      ->get('notify_send_start');
    \Drupal::configFactory()
      ->getEditable('notify.settings')
      ->set('notify_send_last', $send_start)
      ->set('notify_cron_next', 0)
      ->save();
    list($res_nodes, $res_comms, $res_nopub, $res_copub, $res_nounp, $res_counp) = _notify_select_content();
    foreach ($res_nopub as $row) {
      $q = \Drupal::database()
        ->delete('notify_unpublished_queue');
      $q
        ->condition('cid', 0);
      $q
        ->condition('nid', $row->nid);
      $q
        ->execute();
    }
    if ($res_copub) {
      foreach ($res_copub as $row) {
        $q = \Drupal::database()
          ->delete('notify_unpublished_queue');
        $q
          ->condition('cid', $row->cid);
        $q
          ->condition('nid', $row->nid);
        $q
          ->execute();
      }
    }
  }
  return [
    $num_sent,
    $num_fail,
  ];
}

Functions

Namesort descending Description
notify_comment_delete Implements hook_comment_delete().
notify_comment_insert Implements hook_comment_insert().
notify_cron Implements hook_cron().
notify_form_user_register_form_alter Implements hook_form_FORM_ID_alter().
notify_help Implements hook_help().
notify_mail Implements hook_mail().
notify_node_delete Implements hook_node_delete().
notify_user_cancel Implements hook_user_cancel().
notify_user_insert Implements hook_ENTIY_TYPE__insert().
_notify_count Count the various types of content.
_notify_cron_next Function to send next cron.
_notify_get_content_types Helper function to get array of tracked types.
_notify_next_notificaton Compute the next time a notification should be sent.
_notify_select_content Helper function to set up query objects to select content for counting and sending.
_notify_send Helper function to send the notification e-mail batch.
_notify_user_reg_fields Returns form fields to be added to User Regsitration form.