[TikiWiki-commits] [Git][tikiwiki/tiki][master] [NEW] Handle mail queue warning and Create admin page for manage mail queues
"SoftStart Code \(@softstartcode\) via TikiWiki-cvs" <[email protected]>
| Newsgroups | gmane.comp.cms.tiki.cvs |
|---|---|
| Message-ID | <68c748902f101_2cde6d4551c@gitlab-sidekiq-low-urgency-cpu-bound-v2-669574cd86-lx69c.mail> |
SoftStart Code pushed to branch master at Tiki Wiki CMS Groupware / Tiki
Commits:
930da0e7 by Sandeep D at 2025-09-14T22:50:11+00:00
[NEW] Handle mail queue warning and Create admin page for manage mail queues
---
* [NEW] Handle mail queue warning and Create admin page for manage mail queues
(1) Create a new preference “Number of retries to send emails when there are errors“ mailer_queue_max_retries with a default value of 10.
(2) Update the code that picks up emails from the email queue to ignore emails with a retry count bigger than the value in the setting.
(3) A table with the list of messages in the queue
(4) A summary of the current mail queue status
See merge request tikiwiki/tiki!8549
- - - - -
9 changed files:
- + lib/EmailQueue/EmailQueueLib.php
- lib/core/Tiki/Command/MailQueueSendCommand.php
- lib/prefs/mailer.php
- lib/webmail/tikimaillib.php
- templates/admin/include_general.tpl
- templates/tiki-admin.tpl
- + templates/tiki-admin_email_queue.tpl
- tiki-admin.php
- + tiki-admin_email_queue.php
Changes:
=====================================
lib/EmailQueue/EmailQueueLib.php
=====================================
@@ -0,0 +1,60 @@
+<?php
+
+// (c) Copyright by authors of the Tiki Wiki CMS Groupware Project
+//
+// All Rights Reserved. See copyright.txt for details and a complete list of authors.
+// Licensed under the GNU LESSER GENERAL PUBLIC LICENSE. See license.txt for details.
+namespace Tiki\Lib\EmailQueue;
+
+use TikiLib;
+
+class EmailQueueLib
+{
+ public function listStalledEmailQueues($offset, $maxRecords, $sort_mode, $find)
+ {
+ $tikilib = TikiLib::lib('tiki');
+
+ if ($find) {
+ $mid = ' WHERE (`attempts` > ?)';
+ $bindvars = [$find];
+ } else {
+ $mid = '';
+ $bindvars = [];
+ }
+
+ $query = 'SELECT * FROM `tiki_mail_queue` ' . $mid . ' ORDER BY ' . $tikilib->convertSortMode($sort_mode);
+ $query_cant = 'SELECT COUNT(*) FROM `tiki_mail_queue` ' . $mid;
+ $total_cant = 'SELECT COUNT(*) FROM `tiki_mail_queue`';
+ $result = $tikilib->query($query, $bindvars, $maxRecords, $offset);
+ $cant = $tikilib->getOne($query_cant, $bindvars);
+ $total_cant = $tikilib->getOne($total_cant, []);
+ $ret = [];
+
+ while ($res = $result->fetchRow()) {
+ $ret[] = $res;
+ }
+
+ $retval = [];
+ $retval['data'] = $ret;
+ $retval['total_cant'] = $total_cant;
+ $retval['max_retries'] = $find;
+ $retval['cant'] = $cant;
+ return $retval;
+ }
+
+ public function resetAttemptsOfEmailQueue($queueId)
+ {
+ $tikilib = TikiLib::lib('tiki');
+
+ $query = 'UPDATE `tiki_mail_queue` SET `attempts`= 0 WHERE `messageId` = ? ';
+ return $tikilib->query($query, $queueId);
+ }
+
+ public function deleteEmailQueue($queueId)
+ {
+ $tikilib = TikiLib::lib('tiki');
+
+ $query = 'DELETE FROM `tiki_mail_queue` WHERE `messageId` = ? ';
+ return $tikilib->query($query, $queueId);
+ }
+}
=====================================
lib/core/Tiki/Command/MailQueueSendCommand.php
=====================================
@@ -30,12 +30,13 @@ class MailQueueSendCommand extends Command
$output->writeln('Mail queue processor starting...');
- $messages = \TikiDb::get()->fetchAll('SELECT messageId, message FROM tiki_mail_queue');
+ $where = ! empty($prefs['mailer_queue_max_retries']) ? 'where `attempts` <= ' . $prefs['mailer_queue_max_retries'] : '';
+ $messages = \TikiDb::get()->fetchAll('SELECT messageId, message FROM tiki_mail_queue ' . $where);
foreach ($messages as $message) {
$messageId = $message['messageId'];
$serializedEmail = $message['message'];
- $output->writeln('Sending message ' . $messageId . '...');
+ $output->writeln('Sending message ID: ' . $messageId . '...');
$mail = unserialize($serializedEmail);
$error = '';
if ($mail instanceof Email) {
=====================================
lib/prefs/mailer.php
=====================================
@@ -198,5 +198,12 @@ function prefs_mailer_list()
'default' => '',
'keywords' => 'catchall',
],
+ 'mailer_queue_max_retries' => [
+ 'name' => tra('Number of retries to send emails when there are errors'),
+ 'description' => tra('Email queue process will not pick the email to retry after this number of attempts'),
+ 'type' => 'text',
+ 'default' => '10',
+ 'units' => tra('attempts'),
+ ],
];
}
=====================================
lib/webmail/tikimaillib.php
=====================================
@@ -247,7 +247,7 @@ class TikiMail
}
}
- if ($prefs['mailer_queue'] == 'y') {
+ if ($prefs['mailer_queue'] == 'y' || $prefs['mailer_handler'] === 'smtp') {
$query = "INSERT INTO `tiki_mail_queue` (message) VALUES (?)";
$bindvars = [serialize($this->mail)];
$tikilib->query($query, $bindvars, -1, 0);
=====================================
templates/admin/include_general.tpl
=====================================
@@ -73,6 +73,7 @@
{preference name=http_sslverifypeer}
{preference name=mailer_smtp_helo}
</div>
+ {preference name=mailer_queue_max_retries}
{preference name=mailer_queue}
{preference name=mailer_redirect}
<div class="adminoptionbox mb-3 row clearfix">
=====================================
templates/tiki-admin.tpl
=====================================
@@ -52,6 +52,14 @@
{breadcrumbs type="pagetitle" loc="page" crumbs=$crumbs}
{/if}
*}
+ {if $mailer_queue_requires_update}
+ {remarksbox type="error" title="{tr}Email Queue Problem{/tr}"}
+ <p>{tr}There are messages in the email queue that exceeded the number of retries and will not be retried anymore.{/tr}</p>
+ <p>{tr}You can manage the queue.{/tr}
+ {tr _0='<a class="alert-link" href="tiki-admin_email_queue.php">' _1="</a>"}%0Manage the queue.%1{/tr}
+ </p>
+ {/remarksbox}
+ {/if}
{if $ProblemsLoadingCacheSubSystem}
{remarksbox type="warning" title="{tr _0=$ProblemsLoadingCacheSubSystem}%0 did not load properly{/tr}"}
{tr _0=$ProblemsLoadingCacheSubSystem}Tiki is configured to use <strong>%0 cache system</strong>, however, it was not loaded properly. Check if the extension has been properly loaded.{/tr}<br />
=====================================
templates/tiki-admin_email_queue.tpl
=====================================
@@ -0,0 +1,86 @@
+{* $Id$ *}
+{title help="Mail queues"}{tr}Mail queues{/tr}{/title}
+
+{if empty($prefs.sender_email)}
+ {remarksbox type="warning" title="{tr}Warning{/tr}"}
+ {tr}You need to set <a class="alert-link" href="tiki-admin.php?page=general">Sender Email</a> before creating email queue{/tr}.
+ {/remarksbox}
+{/if}
+
+<br>
+<h2>{tr}Summary of the current mail queue status{/tr}</h2>
+<p>{tr}Total messages in queue: {/tr}<strong>{$total_cant}</strong></p>
+<p>{tr}Total messages stalled: {/tr}<strong>{$cant}</strong></p>
+<p>{tr}Current value for max retries in case of error: {/tr}<strong>{$max_retries}</strong></p>
+<h2>{tr}Mail queues{/tr}</h2>
+
+<form method="get" action="tiki-admin_email_queue.php">
+ {ticket}
+ <div class="table-responsive email-queue-table">
+ <table class="table table-striped table-hover">
+ <tr>
+ <th>
+ {if $mailQueues}
+ {select_all label='Select All' checkbox_names='checked[]'}
+ {/if}
+ </th>
+ <th>{tr}Date{/tr}</th>
+ <th>{tr}Destination{/tr}</th>
+ <th>{tr}Subject{/tr}</th>
+ <th>{tr}Message{/tr}</th>
+ <th></th>
+ </tr>
+ {section name=queue loop=$mailQueues}
+ <tr>
+ <td class="checkbox-cell">
+ <div class="form-check">
+ <input type="checkbox" name="checked[]" value="{$mailQueues[queue].messageId|escape}">
+ </div>
+ </td>
+ <td class="text">{$mailQueues[queue].date|escape}</td>
+ <td class="text">{$mailQueues[queue].destination|escape}</td>
+ <td class="email">{$mailQueues[queue].subject|escape}</td>
+ <td class="text">{$mailQueues[queue].body|truncate:500|escape}</td>
+ <td class="actions">
+ {actions}
+ {strip}
+ <action>
+ <a href="{$smarty.server.SCRIPT_NAME}?{query redeliver={$mailQueues[queue].messageId}}" onclick="confirmPopup('{tr}Redeliver mail queue?{/tr}', '{ticket mode=get}')">
+ {icon name='undo' _menu_text='y' _menu_icon='y' alt="{tr}Redeliver{/tr}"}
+ </a>
+ </action>
+ <action>
+ <a href="{$smarty.server.SCRIPT_NAME}?{query remove={$mailQueues[queue].messageId}}" onclick="confirmPopup('{tr}Delete mail queue?{/tr}', '{ticket mode=get}')">
+ {icon name='remove' _menu_text='y' _menu_icon='y' alt="{tr}Delete{/tr}"}
+ </a>
+ </action>
+ {/strip}
+ {/actions}
+ </td>
+ </tr>
+ {sectionelse}
+ {norecords _colspan=6}
+ {/section}
+ </table>
+ </div>
+ {if $mailQueues}
+ <div class="input-group col-sm-8">
+ <select class="form-select" name="action">
+ <option value="" selected="selected">
+ {tr}Select action to perform with checked{/tr}...
+ </option>
+ <option value="redeliver" class="confirm-popup" data-confirm-text="{tr}Redeliver selected queues?{/tr}">
+ {tr}Redeliver{/tr}
+ </option>
+ <option value="delete" class="confirm-popup" data-confirm-text="{tr}Delete selected queues?{/tr}">
+ {tr}Delete{/tr}
+ </option>
+ </select>
+ <button type="submit" class="btn btn-primary" onclick="confirmPopup()">
+ {tr}OK{/tr}
+ </button>
+ </div>
+ {/if}
+</form>
+
+{pagination_links cant=$cant step=$maxRecords offset=$offset}{/pagination_links}
=====================================
tiki-admin.php
=====================================
@@ -440,6 +440,16 @@ $smarty->assign('missing_patches', $installer->missingPatches());
$smarty->assign('installer_not_locked', $installer->checkInstallerLocked());
$smarty->assign('db_engine_type', getCurrentEngine());
+if (empty($_GET)) {
+ $where = ! empty($prefs['mailer_queue_max_retries']) ? 'where attempts > ' . $prefs['mailer_queue_max_retries'] : '';
+ if (! empty($where)) {
+ $maxRetries = count(\TikiDb::get()->fetchAll('SELECT messageId, message FROM tiki_mail_queue ' . $where));
+ if ($maxRetries) {
+ $smarty->assign('mailer_queue_requires_update', $maxRetries);
+ }
+ }
+}
+
$composerWrapper = new ComposerCli($tikipath);
$smarty->assign('composer_options_diff', $composerWrapper->compareDistRequiredOptions());
=====================================
tiki-admin_email_queue.php
=====================================
@@ -0,0 +1,163 @@
+<?php
+
+// (c) Copyright by authors of the Tiki Wiki CMS Groupware Project
+//
+// All Rights Reserved. See copyright.txt for details and a complete list of authors.
+// Licensed under the GNU LESSER GENERAL PUBLIC LICENSE. See license.txt for details.
+use Tiki\Lib\EmailQueue\EmailQueueLib;
+
+$inputConfiguration = [
+ [
+ 'staticKeyFilters' => [
+ 'offset' => 'digits',
+ 'maxRecords' => 'digits',
+ 'removeevent' => 'digits',
+ 'find' => 'striptags',
+ ],
+ 'staticKeyFiltersForArrays' => [
+ 'checked' => 'alnum',
+ ],
+ ]
+];
+// Initialization
+require_once('tiki-setup.php');
+$access->check_permission(['tiki_p_admin']);
+
+$emailQueuelib = new EmailQueueLib();
+
+$auto_query_args = [
+ 'offset',
+ 'sort_mode',
+ 'find',
+ 'maxRecords'
+];
+
+if (! empty($_REQUEST['remove']) && $access->checkCsrf(true)) {
+ $result = $emailQueuelib->deleteEmailQueue($_REQUEST['remove']);
+ if ($result && $result->numRows()) {
+ Feedback::success(tr('Mail queue event deleted'));
+ } else {
+ Feedback::error(tr('Mail queue event not deleted'));
+ }
+}
+if (! empty($_REQUEST['redeliver']) && $access->checkCsrf(true)) {
+ $result = $emailQueuelib->resetAttemptsOfEmailQueue($_REQUEST['redeliver']);
+ if ($result && $result->numRows()) {
+ Feedback::success(tr('One mail queue has been scheduled to redeliver.'));
+ } else {
+ Feedback::error(tr('One mail queue has not been scheduled to redeliver.'));
+ }
+}
+
+if (
+ isset($_REQUEST['action'])
+ && $_REQUEST['action'] === 'redeliver'
+ && isset($_REQUEST['checked'])
+ && $access->checkCsrf(true)
+) {
+ $i = 0;
+ foreach ($_REQUEST['checked'] as $id) {
+ $result = $emailQueuelib->resetAttemptsOfEmailQueue($id);
+ if ($result && $result->numRows()) {
+ $i++;
+ }
+ }
+ $checkedCount = count($_REQUEST['checked']);
+ if ($checkedCount === $i) {
+ $msg = $i == 1 ? tr('One mail queue has been scheduled to redeliver.') : tr('%0 mail queues has been scheduled to redeliver.', $i);
+ Feedback::success($msg);
+ } elseif ($i < $checkedCount) {
+ Feedback::error(tr('%0 of %1 selected mail queue has been scheduled to redeliver', $i, $checkedCount));
+ }
+}
+
+if (
+ isset($_REQUEST['action'])
+ && $_REQUEST['action'] === 'delete'
+ && isset($_REQUEST['checked'])
+ && $access->checkCsrf(true)
+) {
+ $i = 0;
+ foreach ($_REQUEST['checked'] as $id) {
+ $result = $emailQueuelib->deleteEmailQueue($id);
+ if ($result && $result->numRows()) {
+ $i++;
+ }
+ }
+ $checkedCount = count($_REQUEST['checked']);
+ if ($checkedCount === $i) {
+ $msg = $i == 1 ? tr('One mail queue events deleted') : tr('%0 mail queues deleted', $i);
+ Feedback::success($msg);
+ } elseif ($i < $checkedCount) {
+ Feedback::error(tr('%0 of %1 selected mail queue deleted', $i, $checkedCount));
+ }
+}
+
+if (! isset($_REQUEST['offset'])) {
+ $offset = 0;
+} else {
+ $offset = $_REQUEST['offset'];
+}
+$smarty->assign_by_ref('offset', $offset);
+if (! empty($prefs['zend_email_queue_max_retries'])) {
+ $find = $prefs['zend_email_queue_max_retries'];
+} else {
+ $find = '';
+}
+$smarty->assign_by_ref('find', $find);
+if (! empty($_REQUEST['maxRecords'])) {
+ $maxRecords = $_REQUEST['maxRecords'];
+} else {
+ $maxRecords = 100;
+}
+$sort_mode = 'messageId_desc';
+$smarty->assign_by_ref('maxRecords', $maxRecords);
+$mailQueues = $emailQueuelib->listStalledEmailQueues($offset, $maxRecords, $sort_mode, $find);
+$smarty->assign_by_ref('cant', $mailQueues['cant']);
+$smarty->assign_by_ref('total_cant', $mailQueues['total_cant']);
+$smarty->assign_by_ref('max_retries', $mailQueues['max_retries']);
+
+foreach ($mailQueues['data'] as $key => $message) {
+ try {
+ $mail = unserialize($message['message']);
+ $headers = $mail->getHeaders();
+ $date = '';
+ $subject = '';
+ $destinations = [];
+ foreach ($mail->getTo() as $destination) {
+ $destinations[] = $destination->getAddress();
+ }
+ if ($headers->has('date')) {
+ $date = date('Y-m-d H:i:s', strtotime($headers->get('date')->getFieldValue()));
+ }
+ if ($headers->has('subject')) {
+ $subject = $mail->getSubject();
+ }
+
+ // Ensure body is a string for template truncate modifier
+ $textBody = '';
+ $htmlBody = '';
+ if (method_exists($mail, 'getTextBody')) {
+ $textBody = $mail->getTextBody() ?: '';
+ }
+ if (method_exists($mail, 'getHtmlBody')) {
+ $htmlBody = ! empty($mail->getHtmlBody()) ? trim(strip_tags($mail->getHtmlBody())) : '';
+ }
+ $message['date'] = $date;
+ $message['destination'] = implode(',', $destinations);
+ $message['subject'] = $subject;
+ $message['body'] = $textBody . $htmlBody;
+ $mailQueues['data'][$key] = $message;
+ } catch (Exception $e) {
+ Feedback::error($e->getMessage(), $i, $checkedCount);
+ }
+}
+
+$smarty->assign_by_ref('mailQueues', $mailQueues['data']);
+
+// disallow robots to index page:
+$smarty->assign('metatag_robots', 'NOINDEX, NOFOLLOW');
+
+// Display the template
+$smarty->assign('mid', 'tiki-admin_email_queue.tpl');
+$smarty->display('tiki.tpl');
View it on GitLab: https://gitlab.com/tikiwiki/tiki/-/commit/930da0e75fc7ccabd5149811ab7dd1d63281f849
--
View it on GitLab: https://gitlab.com/tikiwiki/tiki/-/commit/930da0e75fc7ccabd5149811ab7dd1d63281f849
You're receiving this email because of your account on gitlab.com.
_______________________________________________
TikiWiki-cvs mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/tikiwiki-cvs