Commit 929724b4 authored by Georgi Mateev's avatar Georgi Mateev
Browse files

[BUGFIX] 1643 MailQueue Fixes:

- Template filter
- Language Filter
- CSV Export
parent 4fd60570
......@@ -112,11 +112,16 @@ class QueueController extends ActionController {
$this->session->setDataByKey('mode', $this->request->getArgument('controller'));
}
$filterTemplate = $_POST['filterTemplate'];
// save the Template filter to the session
if (!isset($_SESSION[$this->session->getSessionKey()]['filterTemplate'])
|| (isset($_POST['filterTemplate']) && $_SESSION['filterTemplate'] !== $_POST['filterTemplate'])) {
$this->session->setDataByKey('filterTemplate', $_POST['filterTemplate']);
}
$filterTemplate = $this->session->getDataByKey('filterTemplate') ?? '';
$filters['filterExtension'] = $filterTemplate;
$filters['filterTemplate'] = $filterTemplate;
if ($_POST['filterTemplate'] !== '') {
if ($filterTemplate !== '') {
$extensionTemplateFilterArray = explode('###', $filterTemplate);
$filters['filterExtension'] = $extensionTemplateFilterArray[0];
$filters['filterTemplate'] = $extensionTemplateFilterArray[1];
......@@ -131,11 +136,10 @@ class QueueController extends ActionController {
$selectedTemplate = \key($registerArray[$selectedExtension]);
}
$queue = $this->mailRepository->findAllEntries($pageUid, 0, $filters);
// create doc header component
$pageInfo = BackendUtility::readPageAccess($pageUid, $GLOBALS['BE_USER']->getPagePermsClause(1));
if ($pageInfo && (int) $pageInfo['is_siteroot'] === 1) {
$queue = $this->mailRepository->findAllEntries($pageUid, 0, $filters);
$this->view->assign('selectedTemplateKey', $selectedTemplate);
$this->view->assign('selectedExtensionKey', $selectedExtension);
$this->view->assign('templates', $registerArray);
......@@ -222,13 +226,12 @@ class QueueController extends ActionController {
* @throws \InvalidArgumentException
*/
public function exportAction(array $filters = []) {
$exportString = BackendService::getCsvFromQueue($filters);
// stop output buffering because it's pointless here
ob_end_clean();
header('Content-Type: application/force-download');
header('Content-Transfer-Encoding: Binary');
header('Content-Disposition: attachment; filename="export.csv"');
header('Content-Length: ' . \strlen($exportString));
echo $exportString;
BackendService::writeCsvFromQueue($filters);
exit(0);
}
}
......@@ -27,6 +27,8 @@ namespace SGalinski\SgMail\Domain\Repository;
***************************************************************/
use SGalinski\SgMail\Service\BackendService;
use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Persistence\Generic\Query;
use TYPO3\CMS\Extbase\Persistence\QueryResultInterface;
......@@ -59,6 +61,7 @@ class MailRepository extends AbstractRepository {
/**
* Find all mails (sent & unsent) for extension key and template name
* We keep the ExtBase function only because of the pagination
*
* @param int $pid
* @param int $limit
......@@ -88,7 +91,10 @@ class MailRepository extends AbstractRepository {
// if this language is no in the sys lang table, then include it with the default lang
if ($filters['filterLanguage'] === 'default') {
foreach (BackendService::getLanguages() as $lang) {
$constraintsAnd[] = $query->logicalNot($query->equals('language', $lang['isocode']));
// Exclude all other system languages than default
if ($lang['isocode'] !== 'default') {
$constraintsAnd[] = $query->logicalNot($query->equals('language', $lang['isocode']));
}
}
} elseif ($filters['filterLanguage'] && $filters['filterLanguage'] !== 0
&& $filters['filterLanguage'] !== '0' && $filters['filterLanguage'] !== ''
......@@ -97,47 +103,51 @@ class MailRepository extends AbstractRepository {
}
$constraintsOr = [];
$search = '%' . $filters['filterSearch'] . '%';
if ($filters['filterFields'] && trim('' !== $filters['filterFields'])) {
foreach ($filters['filterFields'] as $field) {
switch ($field) {
case BackendService::SENDER_FILTER_OPTION :
$constraintsOr[] = $query->like('from_address', $search);
break;
case BackendService::RECIPIENT_FILTER_OPTION :
$constraintsOr[] = $query->like('to_address', $search);
break;
case BackendService::SUBJECT_FILTER_OPTION :
$constraintsOr[] = $query->like('mail_subject', $search);
break;
case BackendService::MAILTEXT_FILTER_OPTION :
$constraintsOr[] = $query->like('mail_body', $search);
break;
case BackendService::CC_FILTER_OPTION :
$constraintsOr[] = $query->like('cc_addresses', $search);
break;
case BackendService::BCC_FILTER_OPTION :
$constraintsOr[] = $query->like('bcc_addresses', $search);
break;
case BackendService::FROM_NAME_FILTER_OPTION :
$constraintsOr[] = $query->like('from_name', $search);
break;
case BackendService::REPLY_TO_NAME_FILTER_OPTION :
$constraintsOr[] = $query->like('reply_to', $search);
break;
if ($filters['filterSearch']) {
$search = '%' . $filters['filterSearch'] . '%';
if ($filters['filterFields'] && trim('' !== $filters['filterFields'])) {
foreach ($filters['filterFields'] as $field) {
switch ($field) {
case BackendService::SENDER_FILTER_OPTION :
$constraintsOr[] = $query->like('from_address', $search);
break;
case BackendService::RECIPIENT_FILTER_OPTION :
$constraintsOr[] = $query->like('to_address', $search);
break;
case BackendService::SUBJECT_FILTER_OPTION :
$constraintsOr[] = $query->like('mail_subject', $search);
break;
case BackendService::MAILTEXT_FILTER_OPTION :
$constraintsOr[] = $query->like('mail_body', $search);
break;
case BackendService::CC_FILTER_OPTION :
$constraintsOr[] = $query->like('cc_addresses', $search);
break;
case BackendService::BCC_FILTER_OPTION :
$constraintsOr[] = $query->like('bcc_addresses', $search);
break;
case BackendService::FROM_NAME_FILTER_OPTION :
$constraintsOr[] = $query->like('from_name', $search);
break;
case BackendService::REPLY_TO_NAME_FILTER_OPTION :
$constraintsOr[] = $query->like('reply_to', $search);
break;
}
}
} else { // if nothing selected, search in all fields
$constraintsOr[] = $query->like('from_address', $search);
$constraintsOr[] = $query->like('to_address', $search);
$constraintsOr[] = $query->like('mail_subject', $search);
$constraintsOr[] = $query->like('mail_body', $search);
$constraintsOr[] = $query->like('cc_addresses', $search);
$constraintsOr[] = $query->like('bcc_addresses', $search);
$constraintsOr[] = $query->like('from_name', $search);
$constraintsOr[] = $query->like('reply_to', $search);
}
} else { // if nothing selected, search in all fields
$constraintsOr[] = $query->like('from_address', $search);
$constraintsOr[] = $query->like('to_address', $search);
$constraintsOr[] = $query->like('mail_subject', $search);
$constraintsOr[] = $query->like('mail_body', $search);
$constraintsOr[] = $query->like('cc_addresses', $search);
$constraintsOr[] = $query->like('bcc_addresses', $search);
$constraintsOr[] = $query->like('from_name', $search);
$constraintsOr[] = $query->like('reply_to', $search);
}
$constraintsAnd[] = $query->logicalOr($constraintsOr);
}
if (count($constraintsOr)) {
$constraintsAnd[] = $query->logicalOr($constraintsOr);
}
$fromTime = strtotime($filters['filterFromDate']);
if ($fromTime) {
......@@ -168,7 +178,131 @@ class MailRepository extends AbstractRepository {
$query->setOrderings(['tstamp' => Query::ORDER_DESCENDING]);
$query->matching($query->logicalAnd($constraintsAnd));
return $query->execute();
}
/**
* Find all mails (sent & unsent) for extension key and template name
* We use Doctrine for better performance
*
* @param int $pid
* @param int $limit
* @param array $filters
* @return \TYPO3\CMS\Core\Database\Query\QueryBuilder
* @throws \InvalidArgumentException
* @throws \TYPO3\CMS\Extbase\Persistence\Exception\InvalidQueryException
*/
public function getDoctrineQueryForFindAllEntries($pid = 0, $limit = NULL, array $filters = []) {
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable(
'tx_sgmail_domain_model_mail'
)
->from('tx_sgmail_domain_model_mail');
$queryBuilder->where($queryBuilder->expr()->eq('deleted', 0));
if ($limit) {
$queryBuilder->setMaxResults($limit);
}
if ($filters['filterExtension'] && $filters['filterExtension'] !== 0
&& $filters['filterExtension'] !== '0' && $filters['filterExtension'] !== ''
) {
$queryBuilder->andWhere($queryBuilder->expr()->eq('extension_key', $filters['filterExtension']));
}
if ($filters['filterTemplate'] && $filters['filterTemplate'] !== 0
&& $filters['filterTemplate'] !== '0' && $filters['filterTemplate'] !== ''
) {
$queryBuilder->andWhere($queryBuilder->expr()->eq('template_name', $filters['filterTemplate']));
}
// if this language is no in the sys lang table, then include it with the default lang
if ($filters['filterLanguage'] === 'default') {
foreach (BackendService::getLanguages() as $lang) {
// Exclude all other system languages than default
if ($lang['isocode'] !== 'default') {
$queryBuilder->andWhere($queryBuilder->expr()->neq('language', $lang['isocode']));
}
}
} elseif ($filters['filterLanguage'] && $filters['filterLanguage'] !== 0
&& $filters['filterLanguage'] !== '0' && $filters['filterLanguage'] !== ''
) {
$queryBuilder->andWhere($queryBuilder->expr()->eq('language', $filters['filterLanguage']));
}
$constraintsOr = [];
if ($filters['filterSearch']) {
$search = '%' . $filters['filterSearch'] . '%';
if ($filters['filterFields'] && trim('' !== $filters['filterFields'])) {
foreach ($filters['filterFields'] as $field) {
switch ($field) {
case BackendService::SENDER_FILTER_OPTION :
$constraintsOr[] = $queryBuilder->expr()->like('from_address', $search);
break;
case BackendService::RECIPIENT_FILTER_OPTION :
$constraintsOr[] = $queryBuilder->expr()->like('to_address', $search);
break;
case BackendService::SUBJECT_FILTER_OPTION :
$constraintsOr[] = $queryBuilder->expr()->like('mail_subject', $search);
break;
case BackendService::MAILTEXT_FILTER_OPTION :
$constraintsOr[] = $queryBuilder->expr()->like('mail_body', $search);
break;
case BackendService::CC_FILTER_OPTION :
$constraintsOr[] = $queryBuilder->expr()->like('cc_addresses', $search);
break;
case BackendService::BCC_FILTER_OPTION :
$constraintsOr[] = $queryBuilder->expr()->like('bcc_addresses', $search);
break;
case BackendService::FROM_NAME_FILTER_OPTION :
$constraintsOr[] = $queryBuilder->expr()->like('from_name', $search);
break;
case BackendService::REPLY_TO_NAME_FILTER_OPTION :
$constraintsOr[] = $queryBuilder->expr()->like('reply_to', $search);
break;
}
}
} else { // if nothing selected, search in all fields
$constraintsOr[] = $queryBuilder->expr()->like('from_address', $search);
$constraintsOr[] = $queryBuilder->expr()->like('to_address', $search);
$constraintsOr[] = $queryBuilder->expr()->like('mail_subject', $search);
$constraintsOr[] = $queryBuilder->expr()->like('mail_body', $search);
$constraintsOr[] = $queryBuilder->expr()->like('cc_addresses', $search);
$constraintsOr[] = $queryBuilder->expr()->like('bcc_addresses', $search);
$constraintsOr[] = $queryBuilder->expr()->like('from_name', $search);
$constraintsOr[] = $queryBuilder->expr()->like('reply_to', $search);
}
}
if (count($constraintsOr)) {
$queryBuilder->andWhere(... $constraintsOr);
}
$fromTime = strtotime($filters['filterFromDate']);
if ($fromTime) {
$queryBuilder->expr()->gte('last_sending_time', $fromTime);
}
$toTime = strtotime($filters['filterToDate']);
if ($toTime) {
$queryBuilder->expr()->lte('last_sending_time', $toTime);
}
if ((int) $filters['filterBlacklist'] !== 1) {
$queryBuilder->expr()->eq('blacklisted', 0);
}
if ($filters['filterSent']) {
switch ($filters['filterSent']) {
case self::SENT:
$queryBuilder->expr()->gt('last_sending_time', 0);
break;
case self::NOT_SENT:
$queryBuilder->expr()->eq('last_sending_time', 0);
break;
}
}
$queryBuilder->expr()->eq('pid', (int) $pid);
return $queryBuilder->orderBy('tstamp', 'desc');
return $queryBuilder->execute();
}
}
......@@ -33,6 +33,7 @@ use SGalinski\SgMail\Domain\Repository\FrontendUserGroupRepository;
use SGalinski\SgMail\Domain\Repository\MailRepository;
use SGalinski\SgMail\Domain\Repository\TemplateRepository;
use SGalinski\SgMail\Utility\ExtensionUtility;
use Symfony\Component\HttpFoundation\StreamedResponse;
use TYPO3\CMS\Backend\Template\Components\ButtonBar;
use TYPO3\CMS\Backend\Template\Components\DocHeaderComponent;
use TYPO3\CMS\Backend\Utility\BackendUtility;
......@@ -381,33 +382,38 @@ class BackendService {
* Generate a csv string from the queues, respecting the given filters
*
* @param array $filters
* @return string
* @return void
* @throws \TYPO3\CMS\Extbase\Persistence\Exception\InvalidQueryException
* @throws \InvalidArgumentException
*/
public static function getCsvFromQueue(array $filters = []): string {
public static function writeCsvFromQueue(array $filters = []) {
$objectManager = GeneralUtility::makeInstance(ObjectManager::class);
/** @var MailRepository $mailRepository */
$mailRepository = $objectManager->get(MailRepository::class);
$pageUid = (int) GeneralUtility::_GP('id');
/** @var array $queue */
$queue = $mailRepository->findAllEntries($pageUid, 0, $filters);
$totalQueueSize = \count($queue);
$exportString = '';
if ($totalQueueSize && $queue) {
$ignoreFields = ['uid', 'pid', 'tstamp',
'password', 'starttime', 'endtime', 'deleted', 'sent', 'priority', 'crdate', 'cruser_id', 'hidden'];
$dateFields = ['tstamp', 'starttime', 'endtime', 'crdate'];
$export = [[]];
$first = TRUE;
/** @var array $mail */
foreach ($queue as $mail) {
if ($first) {
$ignoreFields = ['uid', 'pid', 'tstamp',
'password', 'starttime', 'endtime', 'deleted', 'sent', 'priority', 'crdate', 'cruser_id', 'hidden'];
$dateFields = ['tstamp', 'starttime', 'endtime', 'crdate'];
$doctrineQuery = $mailRepository->getDoctrineQueryForFindAllEntries($pageUid, 20000, $filters);
$doctrineQuery->count('*');
$totalRows = $doctrineQuery->execute()->fetchColumn(0);
$doctrineQuery->select('*');
$batchSize = 10000;
$offset = 0;
$first = TRUE;
$handle = fopen('php://output', 'w+');
while ($offset + $batchSize <= $totalRows) {
$doctrineQuery->setFirstResult($offset);
$doctrineQuery->setMaxResults($batchSize);
$rows = $doctrineQuery->execute();
while ($mail = $rows->fetch()) {
if ($first) { // Write column headers before the first row
$first = FALSE;
$row = [];
foreach ($mail as $field => $value) {
if (!\in_array($field, $ignoreFields, TRUE)) {
$label = isset($GLOBALS['TCA']['tx_sgmail_domain_model_mail']['columns'][$field]) ?
......@@ -415,38 +421,41 @@ class BackendService {
if (strpos($label, 'LLL:') === 0) {
$label = $GLOBALS['LANG']->sL($label);
}
$export[0][] = $label ?: $field;
$row[] = '"' . ($label ?: $field) . '"';
}
}
fwrite(
$handle, trim(
preg_replace(
'/\s\s+/', ' ', strip_tags(
implode(',', $row) . ';' . LF
)
)
)
);
}
}
$line = 1;
/** @var array $mail */
foreach ($queue as $mail) {
$row = [];
foreach ($mail as $field => $value) {
if (!\in_array($field, $ignoreFields, TRUE)) {
if (\in_array($field, $dateFields, TRUE)) {
$export[$line][] = $value ? date('d.m.Y', $value) : '';
$row[] = '"' . ($value ? date('d.m.Y', $value) : '') . '"';
} else {
$export[$line][] = (string) $value;
$row[] = '"' . (string) $value . '"';
}
}
}
$line++;
}
foreach ($export as $line) {
/** @var array $line */
$fields = [];
foreach ($line as $field) {
$fields[] = '"' . $field . '"';
}
$exportString .= implode(',', $fields) . ';' . LF;
fwrite(
$handle, trim(
preg_replace(
'/\s\s+/', ' ', strip_tags(
implode(',', $row) . ';' . LF
)
)
)
);
}
$offset += $batchSize;
}
return trim(preg_replace('/\s\s+/', ' ', strip_tags($exportString)));
}
/**
......
......@@ -41,6 +41,7 @@
</f:for>
</select>
</div>
<div class="form-group">
<label for="filter-languages"><f:translate key="backend.filter.language" /></label>
<f:form.select class="form-control" multiple="0" size="1" property="filterLanguage" optionValueField="key" options="{languages}" id="filter-languages" />
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment