first commit
This commit is contained in:
49
plugins/system/tasknotification/forms/task_notification.xml
Normal file
49
plugins/system/tasknotification/forms/task_notification.xml
Normal file
@ -0,0 +1,49 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<form>
|
||||
<fields name="params">
|
||||
<fields name="notifications">
|
||||
<fieldset name="notifications">
|
||||
<field
|
||||
name="success_mail"
|
||||
type="radio"
|
||||
label="PLG_SYSTEM_TASK_NOTIFICATION_LABEL_SUCCESS_MAIL_TOGGLE"
|
||||
layout="joomla.form.field.radio.switcher"
|
||||
default="0"
|
||||
>
|
||||
<option value="0">JDISABLED</option>
|
||||
<option value="1">JENABLED</option>
|
||||
</field>
|
||||
<field
|
||||
name="failure_mail"
|
||||
type="radio"
|
||||
label="PLG_SYSTEM_TASK_NOTIFICATION_LABEL_FAILURE_MAIL_TOGGLE"
|
||||
layout="joomla.form.field.radio.switcher"
|
||||
default="1"
|
||||
>
|
||||
<option value="0">JDISABLED</option>
|
||||
<option value="1">JENABLED</option>
|
||||
</field>
|
||||
<field
|
||||
name="fatal_failure_mail"
|
||||
type="radio"
|
||||
label="PLG_SYSTEM_TASK_NOTIFICATION_LABEL_FATAL_FAILURE_MAIL_TOGGLE"
|
||||
layout="joomla.form.field.radio.switcher"
|
||||
default="1"
|
||||
>
|
||||
<option value="0">JDISABLED</option>
|
||||
<option value="1">JENABLED</option>
|
||||
</field>
|
||||
<field
|
||||
name="orphan_mail"
|
||||
type="radio"
|
||||
label="PLG_SYSTEM_TASK_NOTIFICATION_LABEL_ORPHANED_TASK_MAIL_TOGGLE"
|
||||
layout="joomla.form.field.radio.switcher"
|
||||
default="1"
|
||||
>
|
||||
<option value="0">JDISABLED</option>
|
||||
<option value="1">JENABLED</option>
|
||||
</field>
|
||||
</fieldset>
|
||||
</fields>
|
||||
</fields>
|
||||
</form>
|
||||
50
plugins/system/tasknotification/services/provider.php
Normal file
50
plugins/system/tasknotification/services/provider.php
Normal file
@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Joomla.Plugin
|
||||
* @subpackage System.tasknotification
|
||||
*
|
||||
* @copyright (C) 2023 Open Source Matters, Inc. <https://www.joomla.org>
|
||||
* @license GNU General Public License version 2 or later; see LICENSE.txt
|
||||
*/
|
||||
|
||||
\defined('_JEXEC') or die;
|
||||
|
||||
use Joomla\CMS\Extension\PluginInterface;
|
||||
use Joomla\CMS\Factory;
|
||||
use Joomla\CMS\Plugin\PluginHelper;
|
||||
use Joomla\CMS\User\UserFactoryInterface;
|
||||
use Joomla\Database\DatabaseInterface;
|
||||
use Joomla\DI\Container;
|
||||
use Joomla\DI\ServiceProviderInterface;
|
||||
use Joomla\Event\DispatcherInterface;
|
||||
use Joomla\Plugin\System\TaskNotification\Extension\TaskNotification;
|
||||
|
||||
return new class () implements ServiceProviderInterface {
|
||||
/**
|
||||
* Registers the service provider with a DI container.
|
||||
*
|
||||
* @param Container $container The DI container.
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @since 4.4.0
|
||||
*/
|
||||
public function register(Container $container): void
|
||||
{
|
||||
$container->set(
|
||||
PluginInterface::class,
|
||||
function (Container $container) {
|
||||
$plugin = new TaskNotification(
|
||||
$container->get(DispatcherInterface::class),
|
||||
(array) PluginHelper::getPlugin('system', 'tasknotification')
|
||||
);
|
||||
$plugin->setApplication(Factory::getApplication());
|
||||
$plugin->setDatabase($container->get(DatabaseInterface::class));
|
||||
$plugin->setUserFactory($container->get(UserFactoryInterface::class));
|
||||
|
||||
return $plugin;
|
||||
}
|
||||
);
|
||||
}
|
||||
};
|
||||
@ -0,0 +1,317 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Joomla.Plugins
|
||||
* @subpackage System.tasknotification
|
||||
*
|
||||
* @copyright (C) 2021 Open Source Matters, Inc. <https://www.joomla.org>
|
||||
* @license GNU General Public License version 2 or later; see LICENSE.txt
|
||||
*/
|
||||
|
||||
namespace Joomla\Plugin\System\TaskNotification\Extension;
|
||||
|
||||
use Joomla\CMS\Event\Model;
|
||||
use Joomla\CMS\Factory;
|
||||
use Joomla\CMS\Log\Log;
|
||||
use Joomla\CMS\Mail\MailTemplate;
|
||||
use Joomla\CMS\Plugin\CMSPlugin;
|
||||
use Joomla\CMS\User\UserFactoryAwareTrait;
|
||||
use Joomla\Component\Scheduler\Administrator\Task\Status;
|
||||
use Joomla\Component\Scheduler\Administrator\Task\Task;
|
||||
use Joomla\Database\DatabaseAwareTrait;
|
||||
use Joomla\Event\Event;
|
||||
use Joomla\Event\SubscriberInterface;
|
||||
use Joomla\Filesystem\Path;
|
||||
use PHPMailer\PHPMailer\Exception as MailerException;
|
||||
|
||||
// phpcs:disable PSR1.Files.SideEffects
|
||||
\defined('_JEXEC') or die;
|
||||
// phpcs:enable PSR1.Files.SideEffects
|
||||
|
||||
/**
|
||||
* This plugin implements email notification functionality for Tasks configured through the Scheduler component.
|
||||
* Notification configuration is supported on a per-task basis, which can be set-up through the Task item form, made
|
||||
* possible by injecting the notification fields into the item form with a `onContentPrepareForm` listener.<br/>
|
||||
*
|
||||
* Notifications can be set-up on: task success, failure, fatal failure (task running too long or crashing the request),
|
||||
* or on _orphaned_ task routines (missing parent plugin - either uninstalled, disabled or no longer offering a routine
|
||||
* with the same ID).
|
||||
*
|
||||
* @since 4.1.0
|
||||
*/
|
||||
final class TaskNotification extends CMSPlugin implements SubscriberInterface
|
||||
{
|
||||
use DatabaseAwareTrait;
|
||||
use UserFactoryAwareTrait;
|
||||
|
||||
/**
|
||||
* The task notification form. This form is merged into the task item form by {@see
|
||||
* injectTaskNotificationFieldset()}.
|
||||
*
|
||||
* @var string
|
||||
* @since 4.1.0
|
||||
*/
|
||||
private const TASK_NOTIFICATION_FORM = 'task_notification';
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @since 4.1.0
|
||||
*/
|
||||
public static function getSubscribedEvents(): array
|
||||
{
|
||||
return [
|
||||
'onContentPrepareForm' => 'injectTaskNotificationFieldset',
|
||||
'onTaskExecuteSuccess' => 'notifySuccess',
|
||||
'onTaskExecuteFailure' => 'notifyFailure',
|
||||
'onTaskRoutineNotFound' => 'notifyOrphan',
|
||||
'onTaskRecoverFailure' => 'notifyFatalRecovery',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Inject fields to support configuration of post-execution notifications into the task item form.
|
||||
*
|
||||
* @param Model\PrepareFormEvent $event The onContentPrepareForm event.
|
||||
*
|
||||
* @return boolean True if successful.
|
||||
*
|
||||
* @since 4.1.0
|
||||
*/
|
||||
public function injectTaskNotificationFieldset(Model\PrepareFormEvent $event): bool
|
||||
{
|
||||
$form = $event->getForm();
|
||||
|
||||
if ($form->getName() !== 'com_scheduler.task') {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Load translations
|
||||
$this->loadLanguage();
|
||||
|
||||
$formFile = JPATH_PLUGINS . '/' . $this->_type . '/' . $this->_name . '/forms/' . self::TASK_NOTIFICATION_FORM . '.xml';
|
||||
|
||||
try {
|
||||
$formFile = Path::check($formFile);
|
||||
} catch (\Exception $e) {
|
||||
// Log?
|
||||
return false;
|
||||
}
|
||||
|
||||
$formFile = Path::clean($formFile);
|
||||
|
||||
if (!is_file($formFile)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $form->loadFile($formFile);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send out email notifications on Task execution failure if task configuration allows it.
|
||||
*
|
||||
* @param Event $event The onTaskExecuteFailure event.
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @since 4.1.0
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function notifyFailure(Event $event): void
|
||||
{
|
||||
/** @var Task $task */
|
||||
$task = $event->getArgument('subject');
|
||||
|
||||
if (!(int) $task->get('params.notifications.failure_mail', 1)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Load translations
|
||||
$this->loadLanguage();
|
||||
|
||||
// @todo safety checks, multiple files [?]
|
||||
$outFile = $event->getArgument('subject')->snapshot['output_file'] ?? '';
|
||||
$data = $this->getDataFromTask($event->getArgument('subject'));
|
||||
$this->sendMail('plg_system_tasknotification.failure_mail', $data, $outFile);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send out email notifications on orphaned task if task configuration allows.<br/>
|
||||
* A task is `orphaned` if the task's parent plugin has been removed/disabled, or no longer offers a task
|
||||
* with the same routine ID.
|
||||
*
|
||||
* @param Event $event The onTaskRoutineNotFound event.
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @since 4.1.0
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function notifyOrphan(Event $event): void
|
||||
{
|
||||
/** @var Task $task */
|
||||
$task = $event->getArgument('subject');
|
||||
|
||||
if (!(int) $task->get('params.notifications.orphan_mail', 1)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Load translations
|
||||
$this->loadLanguage();
|
||||
|
||||
$data = $this->getDataFromTask($event->getArgument('subject'));
|
||||
$this->sendMail('plg_system_tasknotification.orphan_mail', $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send out email notifications on Task execution success if task configuration allows.
|
||||
*
|
||||
* @param Event $event The onTaskExecuteSuccess event.
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @since 4.1.0
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function notifySuccess(Event $event): void
|
||||
{
|
||||
/** @var Task $task */
|
||||
$task = $event->getArgument('subject');
|
||||
|
||||
if (!(int) $task->get('params.notifications.success_mail', 0)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Load translations
|
||||
$this->loadLanguage();
|
||||
|
||||
// @todo safety checks, multiple files [?]
|
||||
$outFile = $event->getArgument('subject')->snapshot['output_file'] ?? '';
|
||||
$data = $this->getDataFromTask($event->getArgument('subject'));
|
||||
$this->sendMail('plg_system_tasknotification.success_mail', $data, $outFile);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send out email notifications on fatal recovery of task execution if task configuration allows.<br/>
|
||||
* Fatal recovery indicated that the task either crashed the parent process or its execution lasted longer
|
||||
* than the global task timeout (this is configurable through the Scheduler component configuration).
|
||||
* In the latter case, the global task timeout should be adjusted so that this false positive can be avoided.
|
||||
* This stands as a limitation of the Scheduler's current task execution implementation, which doesn't involve
|
||||
* keeping track of the parent PHP process which could enable keeping track of the task's status.
|
||||
*
|
||||
* @param Event $event The onTaskRecoverFailure event.
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @since 4.1.0
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function notifyFatalRecovery(Event $event): void
|
||||
{
|
||||
/** @var Task $task */
|
||||
$task = $event->getArgument('subject');
|
||||
|
||||
if (!(int) $task->get('params.notifications.fatal_failure_mail', 1)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Load translations
|
||||
$this->loadLanguage();
|
||||
|
||||
$data = $this->getDataFromTask($event->getArgument('subject'));
|
||||
$this->sendMail('plg_system_tasknotification.fatal_recovery_mail', $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Task $task A task object
|
||||
*
|
||||
* @return array An array of data to bind to a mail template.
|
||||
*
|
||||
* @since 4.1.0
|
||||
*/
|
||||
private function getDataFromTask(Task $task): array
|
||||
{
|
||||
$lockOrExecTime = Factory::getDate($task->get('locked') ?? $task->get('last_execution'))->format($this->getApplication()->getLanguage()->_('DATE_FORMAT_LC2'));
|
||||
|
||||
return [
|
||||
'TASK_ID' => $task->get('id'),
|
||||
'TASK_TITLE' => $task->get('title'),
|
||||
'EXIT_CODE' => $task->getContent()['status'] ?? Status::NO_EXIT,
|
||||
'EXEC_DATE_TIME' => $lockOrExecTime,
|
||||
'TASK_OUTPUT' => $task->getContent()['output_body'] ?? '',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $template The mail template.
|
||||
* @param array $data The data to bind to the mail template.
|
||||
* @param string $attachment The attachment to send with the mail (@todo multiple)
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @since 4.1.0
|
||||
* @throws \Exception
|
||||
*/
|
||||
private function sendMail(string $template, array $data, string $attachment = ''): void
|
||||
{
|
||||
$app = $this->getApplication();
|
||||
$db = $this->getDatabase();
|
||||
|
||||
// Get all users who are not blocked and have opted in for system mails.
|
||||
$query = $db->getQuery(true);
|
||||
|
||||
$query->select($db->quoteName(['name', 'email', 'sendEmail', 'id']))
|
||||
->from($db->quoteName('#__users'))
|
||||
->where($db->quoteName('sendEmail') . ' = 1')
|
||||
->where($db->quoteName('block') . ' = 0');
|
||||
|
||||
$db->setQuery($query);
|
||||
|
||||
try {
|
||||
$users = $db->loadObjectList();
|
||||
} catch (\RuntimeException $e) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($users === null) {
|
||||
Log::add($this->getApplication()->getLanguage()->_('PLG_SYSTEM_TASK_NOTIFICATION_USER_FETCH_FAIL'), Log::ERROR);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$mailSent = false;
|
||||
|
||||
// Mail all matching users who also have the `core.manage` privilege for com_scheduler.
|
||||
foreach ($users as $user) {
|
||||
$user = $this->getUserFactory()->loadUserById($user->id);
|
||||
|
||||
if ($user->authorise('core.manage', 'com_scheduler')) {
|
||||
try {
|
||||
$mailer = new MailTemplate($template, $app->getLanguage()->getTag());
|
||||
$mailer->addTemplateData($data);
|
||||
$mailer->addRecipient($user->email);
|
||||
|
||||
if (
|
||||
!empty($attachment)
|
||||
&& is_file($attachment)
|
||||
) {
|
||||
// @todo we allow multiple files [?]
|
||||
$attachName = pathinfo($attachment, PATHINFO_BASENAME);
|
||||
$mailer->addAttachment($attachName, $attachment);
|
||||
}
|
||||
|
||||
$mailer->send();
|
||||
$mailSent = true;
|
||||
} catch (MailerException $exception) {
|
||||
Log::add($this->getApplication()->getLanguage()->_('PLG_SYSTEM_TASK_NOTIFICATION_NOTIFY_SEND_EMAIL_FAIL'), Log::ERROR);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!$mailSent) {
|
||||
Log::add($this->getApplication()->getLanguage()->_('PLG_SYSTEM_TASK_NOTIFICATION_NO_MAIL_SENT'), Log::WARNING);
|
||||
}
|
||||
}
|
||||
}
|
||||
22
plugins/system/tasknotification/tasknotification.xml
Normal file
22
plugins/system/tasknotification/tasknotification.xml
Normal file
@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<extension type="plugin" group="system" method="upgrade">
|
||||
<name>plg_system_task_notification</name>
|
||||
<author>Joomla! Project</author>
|
||||
<creationDate>2021-09</creationDate>
|
||||
<copyright>(C) 2021 Open Source Matters, Inc.</copyright>
|
||||
<license>GNU General Public License version 2 or later; see LICENSE.txt</license>
|
||||
<authorEmail>admin@joomla.org</authorEmail>
|
||||
<authorUrl>www.joomla.org</authorUrl>
|
||||
<version>4.1</version>
|
||||
<description>PLG_SYSTEM_TASK_NOTIFICATION_XML_DESCRIPTION</description>
|
||||
<namespace path="src">Joomla\Plugin\System\TaskNotification</namespace>
|
||||
<files>
|
||||
<folder>forms</folder>
|
||||
<folder plugin="tasknotification">services</folder>
|
||||
<folder>src</folder>
|
||||
</files>
|
||||
<languages>
|
||||
<language tag="en-GB">language/en-GB/plg_system_tasknotification.ini</language>
|
||||
<language tag="en-GB">language/en-GB/plg_system_tasknotification.sys.ini</language>
|
||||
</languages>
|
||||
</extension>
|
||||
Reference in New Issue
Block a user