Skip to content

Commit

Permalink
[!!!][FEATURE] Allow to delete enqueued mails
Browse files Browse the repository at this point in the history
  • Loading branch information
eliashaeussler committed Mar 7, 2024
1 parent 7afc770 commit e6f1376
Show file tree
Hide file tree
Showing 11 changed files with 145 additions and 11 deletions.
44 changes: 35 additions & 9 deletions Classes/Controller/MailqueueModuleController.php
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ public function __invoke(Message\ServerRequestInterface $request): Message\Respo
$transport = $this->mailer->getTransport();
$page = (int)($request->getQueryParams()['page'] ?? $request->getParsedBody()['page'] ?? 1);
$sendId = $request->getQueryParams()['send'] ?? null;
$deleteId = $request->getQueryParams()['delete'] ?? null;

// Force redirect when page selector was used
if ($request->getMethod() === 'POST' && !isset($request->getQueryParams()['page'])) {
Expand All @@ -71,7 +72,7 @@ public function __invoke(Message\ServerRequestInterface $request): Message\Respo
}

if ($transport instanceof Mail\Transport\QueueableTransport) {
$templateVariables = $this->resolveTemplateVariables($transport, $page, $sendId);
$templateVariables = $this->resolveTemplateVariables($transport, $page, $sendId, $deleteId);
} else {
$templateVariables = [
'unsupportedTransport' => $this->getTransportFromMailConfiguration(),
Expand Down Expand Up @@ -100,6 +101,7 @@ public function __invoke(Message\ServerRequestInterface $request): Message\Respo
/**
* @return array{
* delayThreshold: positive-int,
* deleteResult: bool,
* failing: bool,
* longestPendingInterval: non-negative-int,
* pagination: Core\Pagination\SimplePagination,
Expand All @@ -112,16 +114,22 @@ private function resolveTemplateVariables(
Mail\Transport\QueueableTransport $transport,
int $currentPageNumber = 1,
string $sendId = null,
string $deleteId = null,
): array {
$failing = false;
$longestPendingInterval = 0;
$now = time();
$sendResult = null;
$deleteResult = false;

if (is_string($sendId)) {
$sendResult = $this->sendMail($transport, $sendId);
}

if (is_string($deleteId)) {
$deleteResult = $this->deleteMail($transport, $deleteId);
}

foreach ($transport->getMailQueue() as $mailQueueItem) {
if ($mailQueueItem->date !== null) {
$longestPendingInterval = max($longestPendingInterval, $now - $mailQueueItem->date->getTimestamp());
Expand All @@ -142,6 +150,7 @@ private function resolveTemplateVariables(

return [
'delayThreshold' => $this->extensionConfiguration->getQueueDelayThreshold(),
'deleteResult' => $deleteResult,
'failing' => $failing,
'longestPendingInterval' => $longestPendingInterval,
'pagination' => $pagination,
Expand All @@ -153,14 +162,7 @@ private function resolveTemplateVariables(

private function sendMail(Mail\Transport\QueueableTransport $transport, string $queueItemId): Enums\MailState
{
$mailQueueItem = null;

foreach ($transport->getMailQueue() as $item) {
if ($item->id === $queueItemId) {
$mailQueueItem = $item;
break;
}
}
$mailQueueItem = $this->getMailById($transport, $queueItemId);

if ($mailQueueItem === null) {
return Enums\MailState::AlreadySent;
Expand All @@ -175,6 +177,30 @@ private function sendMail(Mail\Transport\QueueableTransport $transport, string $
return Enums\MailState::Sent;
}

private function deleteMail(Mail\Transport\QueueableTransport $transport, string $queueItemId): bool
{
$mailQueueItem = $this->getMailById($transport, $queueItemId);

if ($mailQueueItem === null) {
return false;
}

return $transport->delete($mailQueueItem);
}

private function getMailById(
Mail\Transport\QueueableTransport $transport,
string $queueItemId,
): ?Mail\Queue\MailQueueItem {
foreach ($transport->getMailQueue() as $mailQueueItem) {
if ($mailQueueItem->id === $queueItemId) {
return $mailQueueItem;
}
}

return null;
}

private function addLinkToConfigurationModule(Backend\Template\ModuleTemplate $template): void
{
$buttonBar = $template->getDocHeaderComponent()->getButtonBar();
Expand Down
18 changes: 18 additions & 0 deletions Classes/Mail/Transport/QueueableFileTransport.php
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,24 @@ public function dequeue(Mail\Queue\MailQueueItem $item, Mailer\Transport\Transpo
return true;
}

public function delete(Mail\Queue\MailQueueItem $item): bool
{
$path = $this->path . DIRECTORY_SEPARATOR . $item->id;
$failurePath = $this->getFileVariant($path, self::FILE_SUFFIX_FAILURE_DATA);

// Early return if message no longer exists in queue
if (!file_exists($path)) {
return false;
}

// Remove failure metadata
if (file_exists($failurePath)) {
unlink($failurePath);
}

return unlink($path);
}

public function getMailQueue(): Mail\Queue\MailQueue
{
return new Mail\Queue\MailQueue(
Expand Down
11 changes: 11 additions & 0 deletions Classes/Mail/Transport/QueueableMemoryTransport.php
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,17 @@ public function dequeue(Mail\Queue\MailQueueItem $item, Mailer\Transport\Transpo
return true;
}

public function delete(Mail\Queue\MailQueueItem $item): bool
{
if (!isset($this->queuedMessages[$item->id])) {
return false;
}

unset($this->queuedMessages[$item->id]);

return true;
}

public function getMailQueue(): Mail\Queue\MailQueue
{
return new Mail\Queue\MailQueue(
Expand Down
2 changes: 2 additions & 0 deletions Classes/Mail/Transport/QueueableTransport.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,6 @@ public function enqueue(Mime\RawMessage $message, ?Mailer\Envelope $envelope = n
* @throws Mailer\Exception\TransportExceptionInterface
*/
public function dequeue(Mail\Queue\MailQueueItem $item, Mailer\Transport\TransportInterface $transport): bool;

public function delete(Mail\Queue\MailQueueItem $item): bool;
}
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,12 @@ methods to enqueue and dequeue mails:
with the difference that it only dequeues the given mail queue item and leaves the
rest of the queue untouched.

* ```php
public function delete(Mail\Queue\MailQueueItem $item): bool
```
Deletes the message of a given mail queue item without sending it. The message is
also dequeued from the mail queue.

#### Recoverable transports

Next to the `QueueableTransport` interface there exists an extended interface
Expand Down
15 changes: 15 additions & 0 deletions Resources/Private/Language/locallang.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,9 @@
<trans-unit id="alert.sendResult.sent.message">
<source>The mail was successfully sent.</source>
</trans-unit>
<trans-unit id="alert.deleteResult.deleted.message">
<source>The mail was successfully deleted.</source>
</trans-unit>

<trans-unit id="unsupportedTransport.docs.text">
<source>Read the %s to learn more about mail spoolers in TYPO3.</source>
Expand All @@ -93,6 +96,12 @@
<trans-unit id="modal.failureDetails.header">
<source>Transport failure details</source>
</trans-unit>
<trans-unit id="modal.delete.header">
<source>Delete this mail?</source>
</trans-unit>
<trans-unit id="modal.delete.text">
<source>Are you sure you want to delete '%s'?</source>
</trans-unit>

<trans-unit id="humanDate.now">
<source>now</source>
Expand Down Expand Up @@ -150,6 +159,12 @@
<trans-unit id="button.config">
<source>View mail configuration</source>
</trans-unit>
<trans-unit id="button.delete">
<source>Delete mail</source>
</trans-unit>
<trans-unit id="button.cancel">
<source>Cancel</source>
</trans-unit>

<trans-unit id="pagination.paginatedItems">
<source>Mails %d - %d</source>
Expand Down
6 changes: 6 additions & 0 deletions Resources/Private/Partials/List/Queue.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@

{queue -> f:count() -> f:variable(name: 'count')}

<f:if condition="{deleteResult}">
<f:be.infobox state="0"
message="{f:translate(key: 'alert.deleteResult.deleted.message', extensionName: 'Mailqueue')}"
/>
</f:if>

<f:if condition="{sendResult}">
<f:switch expression="{sendResult.value}">
<f:case value="alreadySent">
Expand Down
11 changes: 10 additions & 1 deletion Resources/Private/Partials/List/QueueItem.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@

<tr class="{f:render(section: 'rowClass', arguments: {state: queueItem.state, date: queueItem.date}) -> f:spaceless()}">
<td class="col-icon align-top">
<f:render partial="Modal/ConfirmDelete" arguments="{queueItem: queueItem, iterator: iterator}" />

<f:switch expression="{queueItem.state.value}">
<f:case value="failed">
<span class="badge badge-danger d-block">{f:translate(key: 'queueItem.state.failed', extensionName: 'Mailqueue')}</span>
Expand Down Expand Up @@ -68,13 +70,20 @@
<div class="btn-group">
<f:if condition="{queueItem.state.value} == 'failed' && {queueItem.failure}">
<button type="button" class="btn btn-default btn-sm" data-bs-toggle="modal"
data-bs-target="#queue-item-{iterator.cycle}-modal"
data-bs-target="#queue-item-{iterator.cycle}-info-modal"
title="{f:translate(key: 'button.info', extensionName: 'Mailqueue')}"
>
<c:icon identifier="actions-info" />
</button>
</f:if>

<button type="button" class="btn btn-default btn-sm" data-bs-toggle="modal"
data-bs-target="#queue-item-{iterator.cycle}-delete-modal"
title="{f:translate(key: 'button.delete', extensionName: 'Mailqueue')}"
>
<c:icon identifier="actions-delete" />
</button>

<f:be.link route="system_mailqueue" parameters="{send: queueItem.id}" class="btn btn-default btn-sm"
title="{f:translate(key: 'button.send', extensionName: 'Mailqueue')}"
>
Expand Down
40 changes: 40 additions & 0 deletions Resources/Private/Partials/Modal/ConfirmDelete.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<html xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers"
xmlns:c="http://typo3.org/ns/TYPO3/CMS/Core/ViewHelpers"
data-namespace-typo3-fluid="true">

<div id="queue-item-{iterator.cycle}-delete-modal"
class="modal fade modal-severity-warning modal-style-default modal-size-default text-start"
tabindex="-1"
aria-modal="true"
role="dialog"
>
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">{f:translate(key: 'modal.delete.header', extensionName: 'Mailqueue')}</h4>
<button type="button" class="close" data-bs-dismiss="modal">
<c:icon identifier="actions-close" size="small" />
<span class="visually-hidden">{f:translate(key: 'aria.close', extensionName: 'Mailqueue')}</span>
</button>
</div>
<div class="modal-body nowrap-disabled">
<p>
<f:translate key="modal.delete.text" extensionName="Mailqueue" arguments="{
0: '{f:if(condition: queueItem.message.message.subject, then: queueItem.message.message.subject, else: queueItem.message.originalMessage.subject)}'
}" />
</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-bs-dismiss="modal">
<f:translate key="button.cancel" extensionName="Mailqueue" />
</button>

<f:be.link route="system_mailqueue" parameters="{delete: queueItem.id}" class="btn btn-warning">
<f:translate key="button.delete" extensionName="Mailqueue" />
</f:be.link>
</div>
</div>
</div>
</div>

</html>
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
xmlns:c="http://typo3.org/ns/TYPO3/CMS/Core/ViewHelpers"
data-namespace-typo3-fluid="true">

<div id="queue-item-{iterator.cycle}-modal"
<div id="queue-item-{iterator.cycle}-info-modal"
class="modal fade modal-severity-danger modal-style-default modal-size-default text-start"
tabindex="-1"
aria-modal="true"
Expand Down
1 change: 1 addition & 0 deletions Resources/Private/Templates/List.html
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ <h1>{f:translate(key: 'header.list', extensionName: 'Mailqueue')}</h1>
<f:else>
<f:render partial="List/Queue" arguments="{
delayThreshold: delayThreshold,
deleteResult: deleteResult,
failing: failing,
longestPendingInterval: longestPendingInterval,
pagination: pagination,
Expand Down

0 comments on commit e6f1376

Please sign in to comment.