From f4f01b008626f89ed2acc60734b68dc74c1376a6 Mon Sep 17 00:00:00 2001 From: Patrick Lenk Date: Thu, 24 Oct 2024 14:06:21 +0200 Subject: [PATCH 1/9] [BUGFIX] Prevent undefined array key warnings in StatisticsController --- Classes/Module/StatisticsController.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Classes/Module/StatisticsController.php b/Classes/Module/StatisticsController.php index f1f6da2f7..6f6d3b2b0 100644 --- a/Classes/Module/StatisticsController.php +++ b/Classes/Module/StatisticsController.php @@ -171,9 +171,9 @@ public function indexAction(ModuleTemplate $view): ResponseInterface $itemsPerPage = 100; //@TODO $paginator = GeneralUtility::makeInstance( - ArrayPaginator::class, - $data['dataPageInfo'] ?? [], - $this->currentPageNumber, + ArrayPaginator::class, + $data['dataPageInfo'] ?? [], + $this->currentPageNumber, $itemsPerPage ); @@ -1653,11 +1653,11 @@ public function getLinkLabel( } - if ($this->implodedParams['showContentTitle'] == 1) { + if (isset($this->implodedParams['showContentTitle']) && $this->implodedParams['showContentTitle'] == 1) { $label = $contentTitle; } - if ($this->implodedParams['prependContentTitle'] == 1) { + if (isset($this->implodedParams['prependContentTitle']) && $this->implodedParams['prependContentTitle'] == 1) { $label = $contentTitle . ' (' . $linkedWord . ')'; } From ef9fee13155ac17c5f4ce244ca9f754551157a41 Mon Sep 17 00:00:00 2001 From: Benedikt Imminger Date: Mon, 4 Nov 2024 11:42:38 +0100 Subject: [PATCH 2/9] [TASK] Set default value when array key 'sys_dmail_categories_list' is undefined Fixes https://github.com/kartolo/direct_mail/issues/411 for TYPO3 12 branch Original fix by @hannesbochmann https://github.com/kartolo/direct_mail/commit/09d986039341a00feebdcfc8b3e47d4d2fd280aa --- Classes/Dmailer.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Classes/Dmailer.php b/Classes/Dmailer.php index 07bd932de..98a9c4e59 100644 --- a/Classes/Dmailer.php +++ b/Classes/Dmailer.php @@ -391,7 +391,7 @@ public function sendAdvanced(array $recipientRow, string $tableNameChar): int $this->theParts['html']['content'] = ''; if ($this->flagHtml && (($recipientRow['module_sys_dmail_html'] ?? false) || $tableNameChar == 'P')) { - $tempContentHTML = $this->getBoundaryParts($this->dmailer['boundaryParts_html'], $recipientRow['sys_dmail_categories_list']); + $tempContentHTML = $this->getBoundaryParts($this->dmailer['boundaryParts_html'], $recipientRow['sys_dmail_categories_list'] ?? ''); if ($this->mailHasContent) { $this->theParts['html']['content'] = $this->replaceMailMarkers($tempContentHTML, $recipientRow, $additionalMarkers); $returnCode |= 1; @@ -401,7 +401,7 @@ public function sendAdvanced(array $recipientRow, string $tableNameChar): int // Plain $this->theParts['plain']['content'] = ''; if ($this->flagPlain) { - $tempContentPlain = $this->getBoundaryParts($this->dmailer['boundaryParts_plain'], $recipientRow['sys_dmail_categories_list']); + $tempContentPlain = $this->getBoundaryParts($this->dmailer['boundaryParts_plain'], $recipientRow['sys_dmail_categories_list'] ?? ''); if ($this->mailHasContent) { $tempContentPlain = $this->replaceMailMarkers($tempContentPlain, $recipientRow, $additionalMarkers); if (trim($this->dmailer['sys_dmail_rec']['use_rdct']) || trim($this->dmailer['sys_dmail_rec']['long_link_mode'])) { From dfcdc36b2da4cb7ee8067d7d4210345bf28326f1 Mon Sep 17 00:00:00 2001 From: nollm <42864489+nollm@users.noreply.github.com> Date: Wed, 27 Nov 2024 16:44:54 +0100 Subject: [PATCH 3/9] [BUGFIX] Call to undefined method TYPO3\CMS\Core\Site\Entity\NullSite::getAllLanguages() When $site is not an instance of Site, the fallback is an empty language array --- Classes/SelectCategories.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Classes/SelectCategories.php b/Classes/SelectCategories.php index c24af3f3c..5b298bea4 100644 --- a/Classes/SelectCategories.php +++ b/Classes/SelectCategories.php @@ -17,6 +17,7 @@ use DirectMailTeam\DirectMail\Repository\TempRepository; use TYPO3\CMS\Core\Localization\LanguageService; +use TYPO3\CMS\Core\Site\Entity\Site; use TYPO3\CMS\Core\Utility\GeneralUtility; /** @@ -39,7 +40,7 @@ public function getLocalizedCategories(array &$params): void $lang = $this->getLang(); $site = $params['site']; - $languages = $site->getAllLanguages(); + $languages = ($site instanceof Site) ? $site->getAllLanguages() : []; foreach($languages as $language) { if($language->getLocale()->getLanguageCode() == $lang) { $sysLanguageUid = $language->getLanguageId(); From e3c113bbacb05b97217a1c6cf9f5f7ca0aa29b1a Mon Sep 17 00:00:00 2001 From: nollm <42864489+nollm@users.noreply.github.com> Date: Wed, 18 Dec 2024 15:40:21 +0100 Subject: [PATCH 4/9] [BUGFIX] Send Newsletter: Undefined array key "tag" --- Classes/Dmailer.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Classes/Dmailer.php b/Classes/Dmailer.php index 07bd932de..b606b4a50 100644 --- a/Classes/Dmailer.php +++ b/Classes/Dmailer.php @@ -840,7 +840,7 @@ protected function setContent(MailMessage $mailer): void $this->extractMediaLinks(); foreach ($this->theParts['html']['media'] as $media) { // TODO: why are there table related tags here? - if (in_array($media['tag'], ['img', 'table', 'tr', 'td'], true) && !$media['use_jumpurl'] && !$media['do_not_embed']) { + if (isset($media['tag']) && in_array($media['tag'], ['img', 'table', 'tr', 'td'], true) && !$media['use_jumpurl'] && !$media['do_not_embed']) { if (ini_get('allow_url_fopen')) { $context = GeneralUtility::makeInstance(FetchUtility::class)->getStreamContext(); if (($fp = fopen($media['absRef'], 'r', false, $context)) !== false) { From 08bfa41436e5cde0f4999484ef95809d54dd9a23 Mon Sep 17 00:00:00 2001 From: Patrick Lenk Date: Mon, 24 Feb 2025 11:51:08 +0100 Subject: [PATCH 5/9] [BUGFIX] Prevent undefined array key "path" warning in StatisticsController --- Classes/Module/StatisticsController.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Classes/Module/StatisticsController.php b/Classes/Module/StatisticsController.php index f1f6da2f7..e38d9d9c4 100644 --- a/Classes/Module/StatisticsController.php +++ b/Classes/Module/StatisticsController.php @@ -171,9 +171,9 @@ public function indexAction(ModuleTemplate $view): ResponseInterface $itemsPerPage = 100; //@TODO $paginator = GeneralUtility::makeInstance( - ArrayPaginator::class, - $data['dataPageInfo'] ?? [], - $this->currentPageNumber, + ArrayPaginator::class, + $data['dataPageInfo'] ?? [], + $this->currentPageNumber, $itemsPerPage ); @@ -1574,7 +1574,7 @@ public function getUrlStr(array $urlParts): string $urlstr .= ($urlParts['fragment'] ?? '') ? '#' . $urlParts['fragment'] : ''; } } else { - $urlstr = ((isset($urlParts['host']) && $urlParts['host']) ? $urlParts['scheme'] . '://' . $urlParts['host'] : $baseUrl) . $urlParts['path']; + $urlstr = ((isset($urlParts['host']) && $urlParts['host']) ? $urlParts['scheme'] . '://' . $urlParts['host'] : $baseUrl) . ($urlParts['path'] ?? ''); $urlstr .= ($urlParts['query'] ?? '') ? '?' . $urlParts['query'] : ''; $urlstr .= ($urlParts['fragment'] ?? '') ? '#' . $urlParts['fragment'] : ''; } From 37791b21c04df8d46efafcd209dc4276b2f35472 Mon Sep 17 00:00:00 2001 From: Patrick Lenk Date: Mon, 26 Jan 2026 11:56:32 +0100 Subject: [PATCH 6/9] [TASK] add new backend module group icon * smaller size * support for color scheme Icon src: https://typo3.github.io/TYPO3.Icons/icons/actions/actions-envelope.html --- Resources/Public/Images/module-directmail.svg | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) mode change 100755 => 100644 Resources/Public/Images/module-directmail.svg diff --git a/Resources/Public/Images/module-directmail.svg b/Resources/Public/Images/module-directmail.svg old mode 100755 new mode 100644 index 0f452e7bb..841bcff5a --- a/Resources/Public/Images/module-directmail.svg +++ b/Resources/Public/Images/module-directmail.svg @@ -1,10 +1 @@ - - - - - - - - - + From d10ac5b88d88a875ad37a710e44fa6b4c75f47fb Mon Sep 17 00:00:00 2001 From: Patrick Lenk Date: Tue, 27 Jan 2026 10:42:03 +0100 Subject: [PATCH 7/9] [BUGFIX] make creation of mail from draft scheduler task via cli cronjob work again resolves https://github.com/TYPO3-directmail/direct_mail/issues/401 in v13 --- Classes/DirectMailUtility.php | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/Classes/DirectMailUtility.php b/Classes/DirectMailUtility.php index e15c90b35..41ce47d76 100644 --- a/Classes/DirectMailUtility.php +++ b/Classes/DirectMailUtility.php @@ -30,7 +30,10 @@ use TYPO3\CMS\Core\Type\ContextualFeedbackSeverity; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Core\Utility\MathUtility; +use TYPO3\CMS\Core\Site\SiteFinder; use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer; +use TYPO3\CMS\Core\Http\ServerRequestFactory; +use TYPO3\CMS\Core\Core\SystemEnvironmentBuilder; /** * Static class. @@ -97,6 +100,22 @@ public static function getTypolinkURL( $typolinkPageUrl = 't3://page?uid='; $cObj = GeneralUtility::makeInstance(ContentObjectRenderer::class); + // Ensure a PSR-7 request is available for ContentObjectRenderer when running in CLI/Scheduler + if (!empty($GLOBALS['TYPO3_REQUEST'])) { + $cObj->setRequest($GLOBALS['TYPO3_REQUEST']); + } else { + try { + $requestFactory = GeneralUtility::makeInstance(ServerRequestFactory::class); + $request = $requestFactory->fromGlobals(); + // Provide applicationType so ApplicationType::fromRequest() can detect BE/FE + $request = $request->withAttribute('applicationType', SystemEnvironmentBuilder::REQUESTTYPE_BE); + $GLOBALS['TYPO3_REQUEST'] = $request; + $cObj->setRequest($request); + } catch (\Throwable $e) { + // ignore: if we cannot create a request, typolink may still fail later + } + } + return $cObj->typolink_URL([ 'parameter' => $typolinkPageUrl . $parameter, 'forceAbsoluteUrl' => $forceAbsoluteUrl, @@ -301,7 +320,8 @@ protected static function addUserPass(string $url, array $params): string public static function getFullUrlsForDirectMailRecord(array $row): array { // Finding the domain to use - if (!$_SERVER['HTTP_HOST']) { + // Use empty() to avoid "Undefined array key" when running in CLI/Scheduler + if (empty($_SERVER['HTTP_HOST'])) { // In CLI / Scheduler context, $_SERVER['HTTP_HOST'] can be null $siteFinder = GeneralUtility::makeInstance(SiteFinder::class); $site = $siteFinder->getSiteByPageId((int)$row['page']); From e7def791ecc77cb3d369e25267dab73bf3660dcd Mon Sep 17 00:00:00 2001 From: Patrick Lenk Date: Mon, 23 Feb 2026 10:58:31 +0100 Subject: [PATCH 8/9] [TASK] allow tt_address v10 --- composer.json | 2 +- ext_emconf.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index 29c1757cf..1bd3de141 100644 --- a/composer.json +++ b/composer.json @@ -25,7 +25,7 @@ "typo3/cms-core": "^13.4", "typo3/cms-dashboard": "^13.4", "php": "^8.2 || ^8.3 || ^8.4", - "friendsoftypo3/tt-address": "^9.0" + "friendsoftypo3/tt-address": "^9.0 || ^10.0" }, "require-dev": { "roave/security-advisories": "dev-master" diff --git a/ext_emconf.php b/ext_emconf.php index 549780f68..07c67107b 100644 --- a/ext_emconf.php +++ b/ext_emconf.php @@ -13,7 +13,7 @@ 'depends' => [ 'typo3' => '13.4.0-13.4.99', 'lowlevel' => '13.4.0-13.99.99', - 'tt_address' => '9.0.0-9.0.99', + 'tt_address' => '9.0.0-10.0.99', 'php' => '8.2.0-8.4.99', ], 'conflicts' => [ From 2756df642c0eb5ed8c8ff64953a3151ce857bba4 Mon Sep 17 00:00:00 2001 From: Gregor Date: Wed, 10 Jun 2026 13:41:20 +0200 Subject: [PATCH 9/9] [BUGFIX] Refactor SchedulerUtility for TYPO3 13.4.31 Thanks to Gregor Agnes Original commit: https://github.com/Gregor-Agnes/direct_mail_v13/commit/83149f0342063ae0a4b2070326fda2c2d0a7690d --- Classes/Module/MailerEngineController.php | 9 +++--- Classes/Utility/SchedulerUtility.php | 38 +++++++++++++---------- Configuration/Services.yaml | 3 ++ 3 files changed, 29 insertions(+), 21 deletions(-) diff --git a/Classes/Module/MailerEngineController.php b/Classes/Module/MailerEngineController.php index 969e8a2b2..f82a53006 100644 --- a/Classes/Module/MailerEngineController.php +++ b/Classes/Module/MailerEngineController.php @@ -8,6 +8,8 @@ use DirectMailTeam\DirectMail\Repository\SysDmailMaillogRepository; use DirectMailTeam\DirectMail\Repository\SysDmailRepository; use DirectMailTeam\DirectMail\Utility\SchedulerUtility; +use TYPO3\CMS\Core\Utility\ExtensionManagementUtility; +use TYPO3\CMS\Core\Utility\GeneralUtility; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; use TYPO3\CMS\Backend\Routing\Exception\RouteNotFoundException; @@ -21,8 +23,7 @@ use TYPO3\CMS\Core\Pagination\ArrayPaginator; use TYPO3\CMS\Core\Type\Bitmask\Permission; use TYPO3\CMS\Core\Type\ContextualFeedbackSeverity; -use TYPO3\CMS\Core\Utility\GeneralUtility; -use TYPO3\CMS\Core\Utility\ExtensionManagementUtility; + final class MailerEngineController extends MainController { @@ -154,9 +155,9 @@ public function indexAction(ModuleTemplate $view): ResponseInterface protected function getSchedulerTable(): array { - $schedulerTable = ['taskGroupsWithTasks' => [], 'errorClasses' => []]; + $schedulerTable = []; if (ExtensionManagementUtility::isLoaded('scheduler')) { - $schedulerTable = SchedulerUtility::getDMTable(); + $schedulerTable = GeneralUtility::makeInstance(SchedulerUtility::class)->getDMTable(); } return $schedulerTable; } diff --git a/Classes/Utility/SchedulerUtility.php b/Classes/Utility/SchedulerUtility.php index 222469c20..e9b83bad9 100644 --- a/Classes/Utility/SchedulerUtility.php +++ b/Classes/Utility/SchedulerUtility.php @@ -12,6 +12,8 @@ use DirectMailTeam\DirectMail\Repository\TempRepository; use TYPO3\CMS\Core\Utility\GeneralUtility; +use TYPO3\CMS\Scheduler\Exception\InvalidTaskException; +use TYPO3\CMS\Scheduler\ProgressProviderInterface; use TYPO3\CMS\Scheduler\Service\TaskService; use TYPO3\CMS\Scheduler\Task\AbstractTask; use TYPO3\CMS\Scheduler\Task\TaskSerializer; @@ -19,15 +21,20 @@ class SchedulerUtility { - protected static function isValidTaskObject($task): bool + public function __construct( + protected readonly TaskSerializer $taskSerializer, + protected readonly TaskService $taskService, + ) { + } + + protected function isValidTaskObject($task): bool { return (new TaskValidator())->isValid($task); } - public static function getDMTable(): array + public function getDMTable(): array { - $taskSerializer = GeneralUtility::makeInstance(TaskSerializer::class); - $registeredClasses = GeneralUtility::makeInstance(TaskService::class)->getAvailableTaskTypes(); + $registeredClasses = $this->taskService->getAvailableTaskTypes(); $tasks = GeneralUtility::makeInstance(TempRepository::class)->getDMTasks(); @@ -45,18 +52,18 @@ public static function getDMTable(): array ]; try { - $taskObject = $taskSerializer->deserialize($task['serialized_task_object']); + $taskObject = $this->taskSerializer->deserialize($task['serialized_task_object']); } catch (InvalidTaskException $e) { $taskData['errorMessage'] = $e->getMessage(); - $taskData['class'] = $taskSerializer->extractClassName($task['serialized_task_object']); + $taskData['class'] = $this->taskSerializer->extractClassName($task['serialized_task_object']); $errorClasses[] = $taskData; continue; } - $taskClass = $taskSerializer->resolveClassName($taskObject); + $taskClass = $this->taskSerializer->resolveClassName($taskObject); $taskData['class'] = $taskClass; - if (!self::isValidTaskObject($taskObject)) { + if (!$this->isValidTaskObject($taskObject)) { $taskData['errorMessage'] = 'The class ' . $taskClass . ' is not a valid task'; $errorClasses[] = $taskData; continue; @@ -72,15 +79,6 @@ public static function getDMTable(): array $taskData['progress'] = round((float)$taskObject->getProgress(), 2); } - if (!isset($registeredClasses[$taskClass])) { - $taskData['errorMessage'] = 'The class ' . $taskClass . ' is not a registered task'; - $errorClasses[] = $taskData; - continue; - } - - if ($taskObject instanceof ProgressProviderInterface) { - $taskData['progress'] = round((float)$taskObject->getProgress(), 2); - } $taskData['classTitle'] = $registeredClasses[$taskClass]['title']; $taskData['classExtension'] = $registeredClasses[$taskClass]['extension']; $taskData['additionalInformation'] = $taskObject->getAdditionalInformation(); @@ -89,17 +87,21 @@ public static function getDMTable(): array $taskData['nextExecution'] = (int)$task['nextexecution']; $taskData['type'] = 'single'; $taskData['frequency'] = ''; + if ($taskObject->getType() === AbstractTask::TYPE_RECURRING) { $taskData['type'] = 'recurring'; $taskData['frequency'] = $taskObject->getExecution()->getCronCmd() ?: $taskObject->getExecution()->getInterval(); } + $taskData['multiple'] = (bool)$taskObject->getExecution()->getMultiple(); $taskData['lastExecutionFailure'] = false; + if (!empty($task['lastexecution_failure'])) { $taskData['lastExecutionFailure'] = true; $exceptionArray = @unserialize($task['lastexecution_failure']); $taskData['lastExecutionFailureCode'] = ''; $taskData['lastExecutionFailureMessage'] = ''; + if (is_array($exceptionArray)) { $taskData['lastExecutionFailureCode'] = $exceptionArray['code']; $taskData['lastExecutionFailureMessage'] = $exceptionArray['message']; @@ -108,6 +110,7 @@ public static function getDMTable(): array // If a group is deleted or no group is set it needs to go into "not assigned groups" $groupIndex = $task['isTaskGroupDeleted'] === 1 || $task['isTaskGroupDeleted'] === null ? 0 : (int)$task['task_group']; + if (!isset($taskGroupsWithTasks[$groupIndex])) { $taskGroupsWithTasks[$groupIndex] = [ 'tasks' => [], @@ -117,6 +120,7 @@ public static function getDMTable(): array 'groupHidden' => $task['isTaskGroupHidden'], ]; } + $taskGroupsWithTasks[$groupIndex]['tasks'][] = $taskData; } } diff --git a/Configuration/Services.yaml b/Configuration/Services.yaml index 049bd49d4..d861aafc7 100644 --- a/Configuration/Services.yaml +++ b/Configuration/Services.yaml @@ -7,6 +7,9 @@ services: DirectMailTeam\DirectMail\: resource: '../Classes/*' + DirectMailTeam\DirectMail\Utility\SchedulerUtility: + public: true + DirectMailTeam\DirectMail\Module\ConfigurationController: tags: ['backend.controller'] DirectMailTeam\DirectMail\Module\DmailController: