muser-8.x-1.x-dev/modules/custom/muser_system/src/ScheduledEmails.php

modules/custom/muser_system/src/ScheduledEmails.php
<?php

namespace Drupal\muser_system;

use Drupal\node\Entity\Node;
use Drupal\user\Entity\User;
use Drupal\flag\Entity\Flagging;
use Drupal\Core\Datetime\DrupalDateTime;
use Drupal\datetime\Plugin\Field\FieldType\DateTimeItemInterface;
use Drupal\Core\State\StateInterface;
use Drupal\Component\Datetime\Time;

/**
 * Controller for running scheduled tasks.
 */
class ScheduledEmails {

  /**
   * Current round node.
   *
   * @var \Drupal\node\Entity\Node
   */
  private $round;

  /**
   * Configuration for emails to send out.
   *
   * @var array
   */
  protected $emailTypes = [
    'post_projects_start' => [
      'field' => 'field_post_projects_start_email',
      'date_field' => 'field_post_projects',
      'key' => 'value',
    ],
    'post_projects_end' => [
      'field' => 'field_post_projects_end_email',
      'date_field' => 'field_post_projects',
      'key' => 'end_value',
    ],
    'review_applications_start' => [
      'field' => 'field_review_apps_start_email',
      'date_field' => 'field_accept_applications',
      'key' => 'value',
    ],
    'review_applications_end' => [
      'field' => 'field_review_apps_end_email',
      'date_field' => 'field_accept_applications',
      'key' => 'end_value',
    ],
    'contract_reminder_mentor_start' => [
      'field' => 'field_contract_start_mentor_mail',
      'date_field' => 'field_sign_contracts',
      'key' => 'value',
    ],
    'contract_reminder_mentor_end' => [
      'field' => 'field_contract_end_mentor_mail',
      'date_field' => 'field_sign_contracts',
      'key' => 'end_value',
    ],
    'contract_reminder_student_start' => [
      'field' => 'field_contract_start_studnt_mail',
      'date_field' => 'field_sign_contracts',
      'key' => 'value',
    ],
    'contract_reminder_student_end' => [
      'field' => 'field_contract_end_studnt_mail',
      'date_field' => 'field_sign_contracts',
      'key' => 'end_value',
    ],
    'after_round' => [
      'field' => 'field_after_round_email',
      'date_field' => 'field_accept_applications',
      'key' => 'end_value',
    ],
    'student_accepted' => [
      'field' => 'field_accepted_student_email',
      'date_field' => 'field_accept_applications',
      'key' => 'end_value',
    ],
    'student_rejected' => [
      'field' => 'field_rejected_student_email',
      'date_field' => 'field_accept_applications',
      'key' => 'end_value',
    ],
  ];

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

  /**
   * @var \Drupal\Core\State\StateInterface
   */
  protected $state;

  /**
   * @var Time
   */
  protected $dateTime;

  /**
   * ScheduledEmails constructor.
   *
   * @param $config
   * @param \Drupal\Core\State\StateInterface $state
   * @param \Drupal\Component\Datetime\Time $date_time
   * @param \Drupal\node\Entity\Node|NULL $round
   */
  public function __construct($config, StateInterface $state, Time $date_time, Node $round = NULL) {
    $this->config = $config;
    $this->state = $state;
    $this->dateTime = $date_time;
    if (!$round) {
      if ($nid = muser_project_get_current_round()) {
        $this->round = Node::load($nid);
      }
    }
    else {
      $this->round = $round;
    }
  }

  /**
   * Send all scheduled emails.
   *
   * @return bool
   * @throws \Drupal\Core\Entity\EntityStorageException
   * @throws \Drupal\Core\TypedData\Exception\MissingDataException
   */
  public function sendEmails() {

    if (!$this->round) {
      return FALSE;
    }

    foreach ($this->emailTypes as $type => $info) {

      if ($this->round->{$info['field']}->value) {
        // Already sent.
        continue;
      }

      if (!_muser_system_contracts_enabled() && strpos($type, 'contract_reminder_') === 0) {
        // Contracts are disabled.
        \Drupal::logger('muser_system')->info('Not sending @type email as contracts are disabled.', ['@type' => $type]);
        continue;
      }

      $field_data = $this->round->get($info['date_field'])->get(0)->getValue();

      if ($offset = $this->config->get($type . '_email_offset')) {
        $offset = muser_system_reverse_time_offset($offset);
        if (strtotime($offset) === FALSE) {
          // Offset is invalid.
          \Drupal::logger('muser_system')
            ->error('Invalid offset "@offset" for @type email.', [
              '@offset' => $offset,
              '@type' => $type,
            ]);
          continue;
        }
      }
      else {
        $offset = 'now';
      } // Got an offset?

      $current_time = new DrupalDateTime($offset);
      $round_date = new DrupalDateTime($field_data[$info['key']], DateTimeItemInterface::STORAGE_TIMEZONE);
      $diff = $round_date->diff($current_time);

      $done = FALSE;

      if (!$diff->invert) {

        // In past, need to send mail.
        \Drupal::logger('muser_system')->info('Scheduled @type email must be processed.', ['@type' => $type]);

        // Allow global override of scheduled email sending.
        if (\Drupal::service('settings')::get('do_not_send_scheduled_emails')) {
          \Drupal::logger('muser_system')->info('Scheduled @type email sending prevented by a settings override.', ['@type' => $type]);
          $done = TRUE;
        }
        else {

          switch ($type) {

            case 'after_round':
            case 'student_accepted':
            case 'student_rejected':
            case 'contract_reminder_student_start':
            case 'contract_reminder_student_end':
              $done = $this->processStudentEmail($type);
              break;

            default:
              $done = $this->processMentorEmail($type);
          }

        } // Prevent email sending?

        if ($done) {
          $this->round->{$info['field']}->value = 1;
          $this->round->save();
        }

      }

    } // Loop thru possible emails.

    $this->state->set('muser_system.scheduled_emails_checked', $this->dateTime->getRequestTime());

    return $done;

  }

  /**
   * @param $type
   *
   * @return bool
   */
  protected function processStudentEmail($type) {

    \Drupal::logger('muser_system')->info('Processing student email type "@type".', ['@type' => $type]);

    switch ($type) {
      case 'after_round':
        $results = $this->getParticipatingStudents();
        break;

      case 'student_accepted':
        $results = $this->getApplicantStudents('accepted');
        break;

      case 'student_rejected':
        $results = $this->getApplicantStudents('rejected');
        break;

      case 'contract_reminder_student_start':
      case 'contract_reminder_student_end':
        $results = $this->getApplicantStudents('accepted', TRUE);
        break;

      default:
        return FALSE;
    }

    $count = 0;
    foreach ($results as $row) {
      $account = User::load($row->uid);
      if (!empty($row->fid)) {
        $flagging = Flagging::load($row->fid);
      }
      else {
        $flagging = NULL;
      }
      $this->sendScheduledEmail($type, $account, $flagging);
      $count++;
    } // Loop thru students.

    \Drupal::logger('muser_system')
      ->info('Processed student email type "@type". Emails to send: @count', [
        '@type' => $type,
        '@count' => $count,
      ]);

    // @todo Determine what to return here.
    return TRUE;

  }

  /**
   * @return \Drupal\Core\Database\StatementInterface|null
   */
  protected function getParticipatingStudents() {
    // Get all students who have participated in this round.
    // Check for opt-outs here.
    // @todo Determine if we should get all students who have favorited anything or just submitted.
    $query = \Drupal::database()->select('muser_applications', 'ma');
    $query->addField('ma', 'application_uid', 'uid');
    $query->join('users_field_data', 'ufd', 'ufd.uid = ma.application_uid');
    $query->join('user__roles', 'ur', "ur.entity_id = ma.application_uid AND ur.bundle = 'user'");
    $query->join('user__field_do_not_send_emails', 'no_send', "no_send.entity_id = ma.application_uid AND no_send.bundle = 'user'");
    $query->condition('ur.roles_target_id', 'student')
      ->condition('ufd.status', 1)
      ->condition('ma.round_nid', $this->round->id())
      ->condition('no_send.field_do_not_send_emails_value', 1, '<>');
    $results = $query->distinct()->execute();
    return $results;
  }

  /**
   * @return \Drupal\Core\Database\StatementInterface|null
   */
  protected function getApplicantStudents($status, $contracted_projects_only = FALSE) {
    // Get all students accepted or rejected students for this round.
    $query = \Drupal::database()->select('muser_applications', 'ma');
    $query->addField('ma', 'application_uid', 'uid');
    $query->addField('ma', 'fid');
    $query->join('users_field_data', 'ufd', 'ufd.uid = ma.application_uid');
    $query->join('user__roles', 'ur', "ur.entity_id = ma.application_uid AND ur.bundle = 'user'");
    $query->condition('ur.roles_target_id', 'student')
      ->condition('ufd.status', 1)
      ->condition('ma.is_submitted', 1)
      ->condition('ma.round_nid', $this->round->id());
    if ($status == 'accepted') {
      $query->condition('ma.status', 'accepted');
    }
    else {
      $query->condition('ma.status', 'accepted', '<>');
    }
    if ($contracted_projects_only) {
      // Get only applications requiring student to sign.
      $query->condition('ma.is_contracted', 1);
      $query->condition('ma.contract_signed_student', 0);
    }
    $query->orderBy('ma.fid');
    $results = $query->distinct()->execute();
    return $results;
  }

  /**
   * @param $type
   *
   * @return bool
   */
  protected function processMentorEmail($type) {

    \Drupal::logger('muser_system')->info('Processing mentor email type "@type".', ['@type' => $type]);

    switch ($type) {

      case 'post_projects_start':
      case 'post_projects_end':
        $results = $this->getAllMentors();
        break;

      case 'review_applications_start':
      case 'review_applications_end':
        $results = $this->getMentorsWithApplicationsToReview();
        break;

      case 'contract_reminder_mentor_start':
      case 'contract_reminder_mentor_end':
        $results = $this->getMentorsWithContractedProjects();
        break;

      default:
        return FALSE;
    }

    $count = 0;
    foreach ($results as $row) {
      $account = User::load($row->uid);
      if (!empty($row->fid)) {
        $flagging = Flagging::load($row->fid);
      }
      else {
        $flagging = NULL;
      }
      $this->sendScheduledEmail($type, $account, $flagging);
      $count++;
    } // Loop thru mentors.

    \Drupal::logger('muser_system')
      ->info('Processed mentor email type "@type". Emails to send: @count', [
        '@type' => $type,
        '@count' => $count,
      ]);

    // @todo Determine what to return here.
    return TRUE;

  }

  /**
   * @return \Drupal\Core\Database\StatementInterface|null
   */
  protected function getAllMentors() {
    // Get all users with "mentor" role.
    // Check for opt-outs here.
    $query = \Drupal::database()->select('users_field_data', 'ufd');
    $query->addField('ufd', 'uid');
    $query->join('user__roles', 'ur', "ur.entity_id = ufd.uid AND ur.bundle = 'user'");
    $query->join('user__field_do_not_send_emails', 'no_send', "no_send.entity_id = ufd.uid AND no_send.bundle = 'user'");
    $query->condition('ur.roles_target_id', 'mentor')
      ->condition('ufd.status', 1)
      ->condition('no_send.field_do_not_send_emails_value', 1, '<>');
    $results = $query->distinct()->execute();
    return $results;
  }

  /**
   * @return \Drupal\Core\Database\StatementInterface|null
   */
  protected function getMentorsWithApplicationsToReview() {
    // Get all mentors with applications requiring review.
    // Do not check for opt-outs-- always send.
    $query = \Drupal::database()->select('muser_applications_counts', 'mac');
    $query->addField('mac', 'project_uid', 'uid');
    $query->join('node_field_data', 'nfd', 'nfd.nid = mac.project_nid');
    $query->join('users_field_data', 'ufd', 'ufd.uid = mac.project_uid');
    $query->condition('nfd.status', 1)
      ->condition('ufd.status', 1)
      ->condition('mac.round_nid', $this->round->id())
      ->condition('mac.no_decision', 0, '>');
    $results = $query->distinct()->execute();
    return $results;
  }

  /**
   * @return \Drupal\Core\Database\StatementInterface|null
   */
  protected function getMentorsWithContractedProjects() {
    // Get all accepted applications still requiring signed contracts for this
    // round.
    $query = \Drupal::database()->select('muser_applications', 'ma');
    $query->addField('ma', 'project_uid', 'uid');
    $query->addField('ma', 'fid');
    $query->join('users_field_data', 'ufd', 'ufd.uid = ma.application_uid');
    $query->condition('ufd.status', 1)
      ->condition('ma.is_submitted', 1)
      ->condition('ma.round_nid', $this->round->id())
      ->condition('ma.status', 'accepted')
      ->condition('ma.is_contracted', 1)
      ->condition('ma.contract_signed_mentor', 0);
    $query->orderBy('ma.fid');
    $results = $query->distinct()->execute();
    return $results;
  }

  /**
   * @return \Drupal\Core\Database\StatementInterface|null
   */
  protected function getMentorsWithProjectsInRound() {
    // Get all mentors who have participated in this round.
    // Do not check for opt-outs-- always send.
    $query = \Drupal::database()->select('node_field_data', 'fd');
    $query->addField('fd', 'uid');
    $query->join('node__field_project', 'fp', 'fp.field_project_target_id = fd.nid');
    $query->join('node__field_round', 'fr', 'fr.entity_id = fp.entity_id');
    $query->join('users_field_data', 'ufd', 'ufd.uid = fd.uid');
    $query->condition('fd.status', 1)
      ->condition('ufd.status', 1)
      ->condition('fr.field_round_target_id', $this->round->id());
    $results = $query->distinct()->execute();
    return $results;
  }

  /**
   * @param $type
   * @param $account
   * @param null $flagging
   */
  protected function sendScheduledEmail($type, $account, $flagging = NULL) {

    $site_config = \Drupal::config('system.site');

    $language = \Drupal::languageManager()->getCurrentLanguage()->getId();

    /** @var \Drupal\Core\Utility\Token $token_service */
    $token_service = \Drupal::token();
    $muser_data['round'] = muser_project_get_current_round(TRUE);
    $muser_data['flagging'] = $flagging;
    $params['from'] = muser_system_format_email($site_config->get('name'), $site_config->get('mail'));
    $params['subject'] = $token_service->replace($this->config->get($type . '_email_subject'), [
      'user' => $account,
      'node' => $this->round,
      'muser' => $muser_data,
    ]);
    $params['message'] = $token_service->replace($this->config->get($type . '_email_body'), [
      'user' => $account,
      'node' => $this->round,
      'muser' => $muser_data,
    ]);
    $to = muser_system_format_email($account->getDisplayName(), $account->getEmail());

    /** @var \Drupal\Core\Mail\MailManager $mailer */
    $mailer = \Drupal::service('plugin.manager.mail');
    $module = 'muser_system';
    $key = $type;
    $send = TRUE;

    $result = $mailer->mail($module, $key, $to, $language, $params, NULL, $send);
    $values = [
      '@type' => $type,
      '@recipient' => $to,
    ];
    if (empty($result['send'])) {
      // Trying to queue message.
      if ($result['queued'] !== TRUE) {
        \Drupal::logger('muser_system')->error('Error queuing scheduled @type email to @recipient.', $values);
      }
      else {
        \Drupal::logger('muser_system')->info('Scheduled @type email queued for @recipient.', $values);
      }
    }
    else {
      // Sending message.
      if ($result['result'] !== TRUE) {
        \Drupal::logger('muser_system')->error('Error sending scheduled @type email to @recipient.', $values);
      }
      else {
        \Drupal::logger('muser_system')->info('Scheduled @type email sent to @recipient.', $values);
      }
    }

  }

}

Главная | Обратная связь

drupal hosting | друпал хостинг | it patrol .inc