You are here

SubscriptionManager.php in Simplenews 8

File

src/Subscription/SubscriptionManager.php
View source
<?php

namespace Drupal\simplenews\Subscription;

use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\DestructableInterface;
use Drupal\Core\Language\LanguageManagerInterface;
use Psr\Log\LoggerInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Utility\Token;
use Drupal\simplenews\Entity\Subscriber;
use Drupal\simplenews\Mail\MailerInterface;
use Drupal\simplenews\NewsletterInterface;
use Drupal\simplenews\SubscriberInterface;

/**
 * Default subscription manager.
 */
class SubscriptionManager implements SubscriptionManagerInterface, DestructableInterface {

  /**
   * Whether confirmations should be combined.
   *
   * @var bool
   */
  protected $combineConfirmations = FALSE;

  /**
   * Combined confirmations.
   *
   * @var array
   */
  protected $confirmations = [];

  /**
   * @var array
   */
  protected $subscribedCache = [];

  /**
   * @var \Drupal\simplenews\Mail\MailerInterface
   */
  protected $mailer;

  /**
   * @var \Drupal\Core\Language\LanguageManagerInterface
   */
  protected $languageManager;

  /**
   * @var \Drupal\Core\Config\ImmutableConfig
   */
  protected $config;

  /**
   * @var \Drupal\Core\Utility\Token
   */
  protected $token;

  /**
   * @var \Psr\Log\LoggerInterface
   */
  protected $logger;

  /**
   * @var \Drupal\Core\Session\AccountInterface
   */
  protected $currentUser;

  /**
   * Constructs a SubscriptionManager.
   *
   * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
   *   The language manager.
   * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
   *   The config factory.
   * @param \Drupal\simplenews\Mail\MailerInterface $mailer
   *   The simplenews manager.
   * @param \Drupal\Core\Utility\Token $token
   *   The token service.
   * @param \Psr\Log\LoggerInterface $logger
   *   The simplenews logger channel.
   */
  public function __construct(LanguageManagerInterface $language_manager, ConfigFactoryInterface $config_factory, MailerInterface $mailer, Token $token, LoggerInterface $logger, AccountInterface $current_user) {
    $this->languageManager = $language_manager;
    $this->config = $config_factory
      ->get('simplenews.settings');
    $this->mailer = $mailer;
    $this->token = $token;
    $this->logger = $logger;
    $this->currentUser = $current_user;
  }

  /**
   * {@inheritdoc}
   */
  public function subscribe($mail, $newsletter_id, $confirm = NULL, $source = 'unknown', $preferred_langcode = NULL) {

    // Get current subscriptions if any.
    $subscriber = simplenews_subscriber_load_by_mail($mail);

    // If user is not subscribed to ANY newsletter, create a subscription account
    if (!$subscriber) {

      // To subscribe a user:
      //   - Fetch the users uid.
      //   - Determine the user preferred language.
      //   - Add the user to the database.
      //   - Get the full subscription object based on the mail address.
      // Note that step 3 gets subscription data based on mail address because the uid can be 0 (for anonymous users)
      $account = user_load_by_mail($mail);

      // If the site is multilingual:
      //  - Anonymous users are subscribed with their preferred language
      //    equal to the language of the current page.
      //  - Registered users will be subscribed with their default language as
      //    set in their account settings.
      // By default the preferred language is not set.
      if ($this->languageManager
        ->isMultilingual()) {
        if ($account) {
          $preferred_langcode = $account
            ->getPreferredLangcode();
        }
        else {
          $preferred_langcode = isset($preferred_langcode) ? $preferred_langcode : $this->languageManager
            ->getCurrentLanguage();
        }
      }
      else {
        $preferred_langcode = '';
      }
      $subscriber = Subscriber::create(array());
      $subscriber
        ->setMail($mail);
      if ($account) {
        $subscriber
          ->setUserId($account
          ->id());
      }
      $subscriber
        ->setLangcode($preferred_langcode);
      $subscriber
        ->setStatus(SubscriberInterface::ACTIVE);
      $subscriber
        ->save();
    }
    $newsletter = simplenews_newsletter_load($newsletter_id);

    // If confirmation is not explicitly specified, use the newsletter
    // configuration.
    if ($confirm === NULL) {
      $confirm = $this
        ->requiresConfirmation($newsletter, $subscriber
        ->getUserId());
    }
    if ($confirm) {

      // Create an unconfirmed subscription object if it doesn't exist yet.
      if (!$subscriber
        ->isSubscribed($newsletter_id)) {
        $subscriber
          ->subscribe($newsletter_id, SIMPLENEWS_SUBSCRIPTION_STATUS_UNCONFIRMED, $source);
        $subscriber
          ->save();
      }
      $this
        ->addConfirmation('subscribe', $subscriber, $newsletter);
    }
    elseif (!$subscriber
      ->isSubscribed($newsletter_id)) {

      // Subscribe the user if not already subscribed.
      $subscriber
        ->subscribe($newsletter_id, SIMPLENEWS_SUBSCRIPTION_STATUS_SUBSCRIBED, $source);
      $subscriber
        ->save();
    }
    return $this;
  }

  /**
   * {@inheritdoc}
   */
  public function unsubscribe($mail, $newsletter_id, $confirm = NULL, $source = 'unknown') {
    $subscriber = simplenews_subscriber_load_by_mail($mail);
    if (!$subscriber) {
      throw new \Exception('The subscriber does not exist.');
    }

    // The unlikely case that a user is unsubscribed from a non existing mailing list is logged
    if (!($newsletter = simplenews_newsletter_load($newsletter_id))) {
      $this->logger
        ->error('Attempt to unsubscribe from non existing mailing list ID %id', array(
        '%id' => $newsletter_id,
      ));
      return $this;
    }

    // If confirmation is not explicitly specified, use the newsletter
    // configuration.
    if ($confirm === NULL) {
      $confirm = $this
        ->requiresConfirmation($newsletter, $subscriber
        ->getUserId());
    }
    if ($confirm) {

      // Make sure the mail address is set.
      if (empty($subscriber)) {
        $subscriber = Subscriber::create(array());
        $subscriber
          ->setMail($mail);
        $subscriber
          ->save();
      }
      $this
        ->addConfirmation('unsubscribe', $subscriber, $newsletter);
    }
    elseif ($subscriber && $subscriber
      ->isSubscribed($newsletter_id)) {

      // Unsubscribe the user from the mailing list.
      $subscriber
        ->unsubscribe($newsletter_id, $source);
      $subscriber
        ->save();
    }
    return $this;
  }

  /**
   * {@inheritdoc}
   */
  public function isSubscribed($mail, $newsletter_id) {
    if (!isset($this->subscribedCache[$mail][$newsletter_id])) {
      $subscriber = simplenews_subscriber_load_by_mail($mail);

      // Check that a subscriber was found, he is active and subscribed to the
      // requested newsletter_id.
      $this->subscribedCache[$mail][$newsletter_id] = $subscriber && $subscriber
        ->getStatus() && $subscriber
        ->isSubscribed($newsletter_id);
    }
    return $this->subscribedCache[$mail][$newsletter_id];
  }

  /**
   * {@inheritdoc}
   */
  public function getChangesList(SubscriberInterface $subscriber, $changes = NULL, $langcode = NULL) {
    if (empty($langcode)) {
      $language = $this->languageManager
        ->getCurrentLanguage();
      $langcode = $language
        ->getId();
    }
    if (empty($changes)) {
      $changes = $subscriber
        ->getChanges();
    }
    $changes_list = array();
    foreach ($changes as $newsletter_id => $action) {
      $subscribed = $subscriber
        ->isSubscribed($newsletter_id);

      // Get text for each possible combination.
      if ($action == 'subscribe' && !$subscribed) {
        $line = $this->config
          ->get('subscription.confirm_combined_line_subscribe_unsubscribed');
      }
      elseif ($action == 'subscribe' && $subscribed) {
        $line = $this->config
          ->get('subscription.confirm_combined_line_subscribe_subscribed');
      }
      elseif ($action == 'unsubscribe' && !$subscribed) {
        $line = $this->config
          ->get('subscription.confirm_combined_line_unsubscribe_unsubscribed');
      }
      elseif ($action == 'unsubscribe' && $subscribed) {
        $line = $this->config
          ->get('subscription.confirm_combined_line_unsubscribe_subscribed');
      }
      $newsletter_context = array(
        'simplenews_subscriber' => $subscriber,
        'newsletter' => simplenews_newsletter_load($newsletter_id),
      );
      $changes_list[$newsletter_id] = $this->token
        ->replace($line, $newsletter_context, array(
        'sanitize' => FALSE,
      ));
    }
    return $changes_list;
  }

  /**
   * {@inheritdoc}
   */
  public function sendConfirmations() {
    foreach ($this->confirmations as $mail => $changes) {
      $subscriber = simplenews_subscriber_load_by_mail($mail);
      if (!$subscriber) {
        $subscriber = Subscriber::create(array());
        $subscriber
          ->setMail($mail);
        $subscriber
          ->setLangcode($this->languageManager
          ->getCurrentLanguage());
        $subscriber
          ->save();
      }
      $subscriber
        ->setChanges($changes);
      $this->mailer
        ->sendCombinedConfirmation($subscriber);

      // Save the changes in the subscriber if there is a real subscriber object.
      if ($subscriber && $subscriber
        ->id()) {
        $subscriber
          ->save();
      }
    }
    $sent = !empty($this->confirmations);
    $this->confirmations = array();
    return $sent;
  }

  /**
   * {@inheritdoc}
   */
  public function reset() {
    $this->subscribedCache = [];
  }

  /**
   * {@inheritdoc}
   */
  public function destruct() {

    // Ensure that confirmations are always sent even if API calls did not do it
    // explicitly. It is still possible to do so, e.g. to be able to know if
    // confirmations were sent or not.
    $this
      ->sendConfirmations();
  }

  /**
   * Add a mail confirmation or fetch them.
   *
   * @param string $action
   *   The confirmation type, either subscribe or unsubscribe.
   * @param \Drupal\simplenews\SubscriberInterface $subscriber
   *   The subscriber object.
   * @param \Drupal\simplenews\NewsletterInterface $newsletter
   *   The newsletter object.
   */
  protected function addConfirmation($action, SubscriberInterface $subscriber, NewsletterInterface $newsletter) {
    $this->confirmations[$subscriber
      ->getMail()][$newsletter
      ->id()] = $action;
  }

  /**
   * Checks whether confirmation is required for this newsletter and user.
   *
   * @param \Drupal\simplenews\NewsletterInterface $newsletter
   *   The newsletter entity.
   * @param int $uid
   *   The user ID that belongs to the email.
   *
   * @return bool
   *   TRUE if confirmation is required, FALSE if not.
   */
  protected function requiresConfirmation(NewsletterInterface $newsletter, $uid) {

    // If user is currently logged in, don't send confirmation.
    // Other addresses receive a confirmation if double opt-in is selected.
    if ($this->currentUser
      ->id() && $uid && $this->currentUser
      ->id() == $uid) {
      return FALSE;
    }
    else {
      return $newsletter->opt_inout == 'double';
    }
  }

}

Classes

Namesort descending Description
SubscriptionManager Default subscription manager.