[ Index ]

PHP Cross Reference of Joomla 4.2.2 documentation

title

Body

[close]

/plugins/system/tasknotification/ -> tasknotification.php (source)

   1  <?php
   2  
   3  /**
   4   * @package     Joomla.Plugins
   5   * @subpackage  System.Tasknotification
   6   *
   7   * @copyright   (C) 2021 Open Source Matters, Inc. <https://www.joomla.org>
   8   * @license     GNU General Public License version 2 or later; see LICENSE.txt
   9  
  10   * @phpcs:disable PSR1.Classes.ClassDeclaration.MissingNamespace
  11   */
  12  
  13  use Joomla\CMS\Application\CMSApplication;
  14  use Joomla\CMS\Factory;
  15  use Joomla\CMS\Filesystem\File;
  16  use Joomla\CMS\Filesystem\Path;
  17  use Joomla\CMS\Form\Form;
  18  use Joomla\CMS\Language\Text;
  19  use Joomla\CMS\Log\Log;
  20  use Joomla\CMS\Mail\MailTemplate;
  21  use Joomla\CMS\Plugin\CMSPlugin;
  22  use Joomla\CMS\User\UserFactoryInterface;
  23  use Joomla\Component\Scheduler\Administrator\Task\Status;
  24  use Joomla\Component\Scheduler\Administrator\Task\Task;
  25  use Joomla\Database\DatabaseInterface;
  26  use Joomla\Event\Event;
  27  use Joomla\Event\EventInterface;
  28  use Joomla\Event\SubscriberInterface;
  29  use PHPMailer\PHPMailer\Exception as MailerException;
  30  
  31  // phpcs:disable PSR1.Files.SideEffects
  32  \defined('_JEXEC') or die;
  33  // phpcs:enable PSR1.Files.SideEffects
  34  
  35  /**
  36   * This plugin implements email notification functionality for Tasks configured through the Scheduler component.
  37   * Notification configuration is supported on a per-task basis, which can be set-up through the Task item form, made
  38   * possible by injecting the notification fields into the item form with a `onContentPrepareForm` listener.<br/>
  39   *
  40   * Notifications can be set-up on: task success, failure, fatal failure (task running too long or crashing the request),
  41   * or on _orphaned_ task routines (missing parent plugin - either uninstalled, disabled or no longer offering a routine
  42   * with the same ID).
  43   *
  44   * @since 4.1.0
  45   */
  46  class PlgSystemTasknotification extends CMSPlugin implements SubscriberInterface
  47  {
  48      /**
  49       * The task notification form. This form is merged into the task item form by {@see
  50       * injectTaskNotificationFieldset()}.
  51       *
  52       * @var string
  53       * @since 4.1.0
  54       */
  55      private const TASK_NOTIFICATION_FORM = 'task_notification';
  56  
  57      /**
  58       * @var  CMSApplication
  59       * @since  4.1.0
  60       */
  61      protected $app;
  62  
  63      /**
  64       * @var  DatabaseInterface
  65       * @since  4.1.0
  66       */
  67      protected $db;
  68  
  69      /**
  70       * @var boolean
  71       * @since 4.1.0
  72       */
  73      protected $autoloadLanguage = true;
  74  
  75  
  76      /**
  77       * @inheritDoc
  78       *
  79       * @return array
  80       *
  81       * @since 4.1.0
  82       */
  83      public static function getSubscribedEvents(): array
  84      {
  85          return [
  86              'onContentPrepareForm'  => 'injectTaskNotificationFieldset',
  87              'onTaskExecuteSuccess'  => 'notifySuccess',
  88              'onTaskExecuteFailure'  => 'notifyFailure',
  89              'onTaskRoutineNotFound' => 'notifyOrphan',
  90              'onTaskRecoverFailure'  => 'notifyFatalRecovery',
  91          ];
  92      }
  93  
  94      /**
  95       * Inject fields to support configuration of post-execution notifications into the task item form.
  96       *
  97       * @param   EventInterface  $event  The onContentPrepareForm event.
  98       *
  99       * @return boolean True if successful.
 100       *
 101       * @since 4.1.0
 102       */
 103      public function injectTaskNotificationFieldset(EventInterface $event): bool
 104      {
 105          /** @var Form $form */
 106          $form = $event->getArgument('0');
 107  
 108          if ($form->getName() !== 'com_scheduler.task') {
 109              return true;
 110          }
 111  
 112          $formFile = __DIR__ . "/forms/" . self::TASK_NOTIFICATION_FORM . '.xml';
 113  
 114          try {
 115              $formFile = Path::check($formFile);
 116          } catch (Exception $e) {
 117              // Log?
 118              return false;
 119          }
 120  
 121          if (!File::exists($formFile)) {
 122              return false;
 123          }
 124  
 125          return $form->loadFile($formFile);
 126      }
 127  
 128      /**
 129       * Send out email notifications on Task execution failure if task configuration allows it.
 130       *
 131       * @param   Event  $event  The onTaskExecuteFailure event.
 132       *
 133       * @return void
 134       *
 135       * @since 4.1.0
 136       * @throws Exception
 137       */
 138      public function notifyFailure(Event $event): void
 139      {
 140          /** @var Task $task */
 141          $task = $event->getArgument('subject');
 142  
 143          if (!(int) $task->get('params.notifications.failure_mail', 1)) {
 144              return;
 145          }
 146  
 147          // @todo safety checks, multiple files [?]
 148          $outFile = $event->getArgument('subject')->snapshot['output_file'] ?? '';
 149          $data    = $this->getDataFromTask($event->getArgument('subject'));
 150          $this->sendMail('plg_system_tasknotification.failure_mail', $data, $outFile);
 151      }
 152  
 153      /**
 154       * Send out email notifications on orphaned task if task configuration allows.<br/>
 155       * A task is `orphaned` if the task's parent plugin has been removed/disabled, or no longer offers a task
 156       * with the same routine ID.
 157       *
 158       * @param   Event  $event  The onTaskRoutineNotFound event.
 159       *
 160       * @return void
 161       *
 162       * @since 4.1.0
 163       * @throws Exception
 164       */
 165      public function notifyOrphan(Event $event): void
 166      {
 167          /** @var Task $task */
 168          $task = $event->getArgument('subject');
 169  
 170          if (!(int) $task->get('params.notifications.orphan_mail', 1)) {
 171              return;
 172          }
 173  
 174          $data = $this->getDataFromTask($event->getArgument('subject'));
 175          $this->sendMail('plg_system_tasknotification.orphan_mail', $data);
 176      }
 177  
 178      /**
 179       * Send out email notifications on Task execution success if task configuration allows.
 180       *
 181       * @param   Event  $event  The onTaskExecuteSuccess event.
 182       *
 183       * @return void
 184       *
 185       * @since 4.1.0
 186       * @throws Exception
 187       */
 188      public function notifySuccess(Event $event): void
 189      {
 190          /** @var Task $task */
 191          $task = $event->getArgument('subject');
 192  
 193          if (!(int) $task->get('params.notifications.success_mail', 0)) {
 194              return;
 195          }
 196  
 197          // @todo safety checks, multiple files [?]
 198          $outFile = $event->getArgument('subject')->snapshot['output_file'] ?? '';
 199          $data    = $this->getDataFromTask($event->getArgument('subject'));
 200          $this->sendMail('plg_system_tasknotification.success_mail', $data, $outFile);
 201      }
 202  
 203      /**
 204       * Send out email notifications on fatal recovery of task execution if task configuration allows.<br/>
 205       * Fatal recovery indicated that the task either crashed the parent process or its execution lasted longer
 206       * than the global task timeout (this is configurable through the Scheduler component configuration).
 207       * In the latter case, the global task timeout should be adjusted so that this false positive can be avoided.
 208       * This stands as a limitation of the Scheduler's current task execution implementation, which doesn't involve
 209       * keeping track of the parent PHP process which could enable keeping track of the task's status.
 210       *
 211       * @param   Event  $event  The onTaskRecoverFailure event.
 212       *
 213       * @return void
 214       *
 215       * @since 4.1.0
 216       * @throws Exception
 217       */
 218      public function notifyFatalRecovery(Event $event): void
 219      {
 220          /** @var Task $task */
 221          $task = $event->getArgument('subject');
 222  
 223          if (!(int) $task->get('params.notifications.fatal_failure_mail', 1)) {
 224              return;
 225          }
 226  
 227          $data = $this->getDataFromTask($event->getArgument('subject'));
 228          $this->sendMail('plg_system_tasknotification.fatal_recovery_mail', $data);
 229      }
 230  
 231      /**
 232       * @param   Task  $task  A task object
 233       *
 234       * @return array  An array of data to bind to a mail template.
 235       *
 236       * @since 4.1.0
 237       */
 238      private function getDataFromTask(Task $task): array
 239      {
 240          $lockOrExecTime = Factory::getDate($task->get('locked') ?? $task->get('last_execution'))->format(Text::_('DATE_FORMAT_LC2'));
 241  
 242          return [
 243              'TASK_ID'        => $task->get('id'),
 244              'TASK_TITLE'     => $task->get('title'),
 245              'EXIT_CODE'      => $task->getContent()['status'] ?? Status::NO_EXIT,
 246              'EXEC_DATE_TIME' => $lockOrExecTime,
 247              'TASK_OUTPUT'    => $task->getContent()['output_body'] ?? '',
 248          ];
 249      }
 250  
 251      /**
 252       * @param   string  $template    The mail template.
 253       * @param   array   $data        The data to bind to the mail template.
 254       * @param   string  $attachment  The attachment to send with the mail (@todo multiple)
 255       *
 256       * @return void
 257       *
 258       * @since 4.1.0
 259       * @throws Exception
 260       */
 261      private function sendMail(string $template, array $data, string $attachment = ''): void
 262      {
 263          $app = $this->app;
 264          $db  = $this->db;
 265  
 266          /** @var UserFactoryInterface $userFactory */
 267          $userFactory = Factory::getContainer()->get('user.factory');
 268  
 269          // Get all users who are not blocked and have opted in for system mails.
 270          $query = $db->getQuery(true);
 271  
 272          $query->select($db->qn(['name', 'email', 'sendEmail', 'id']))
 273              ->from($db->quoteName('#__users'))
 274              ->where($db->quoteName('sendEmail') . ' = 1')
 275              ->where($db->quoteName('block') . ' = 0');
 276  
 277          $db->setQuery($query);
 278  
 279          try {
 280              $users = $db->loadObjectList();
 281          } catch (RuntimeException $e) {
 282              return;
 283          }
 284  
 285          if ($users === null) {
 286              Log::add(Text::_('PLG_SYSTEM_TASK_NOTIFICATION_USER_FETCH_FAIL'), Log::ERROR);
 287  
 288              return;
 289          }
 290  
 291          $mailSent = false;
 292  
 293          // Mail all matching users who also have the `core.manage` privilege for com_scheduler.
 294          foreach ($users as $user) {
 295              $user = $userFactory->loadUserById($user->id);
 296  
 297              if ($user->authorise('core.manage', 'com_scheduler')) {
 298                  try {
 299                      $mailer = new MailTemplate($template, $app->getLanguage()->getTag());
 300                      $mailer->addTemplateData($data);
 301                      $mailer->addRecipient($user->email);
 302  
 303                      if (
 304                          !empty($attachment)
 305                          && File::exists($attachment)
 306                          && is_file($attachment)
 307                      ) {
 308                          // @todo we allow multiple files [?]
 309                          $attachName = pathinfo($attachment, PATHINFO_BASENAME);
 310                          $mailer->addAttachment($attachName, $attachment);
 311                      }
 312  
 313                      $mailer->send();
 314                      $mailSent = true;
 315                  } catch (MailerException $exception) {
 316                      Log::add(Text::_('PLG_SYSTEM_TASK_NOTIFICATION_NOTIFY_SEND_EMAIL_FAIL'), Log::ERROR);
 317                  }
 318              }
 319          }
 320  
 321          if (!$mailSent) {
 322              Log::add(Text::_('PLG_SYSTEM_TASK_NOTIFICATION_NO_MAIL_SENT'), Log::WARNING);
 323          }
 324      }
 325  }


Generated: Wed Sep 7 05:41:13 2022 Chilli.vc Blog - For Webmaster,Blog-Writer,System Admin and Domainer