From 27572acf1ae15c31b6b9df6b0ceffedf7689e7a5 Mon Sep 17 00:00:00 2001
From: Georgi Mateev <gmateev@exactag.com>
Date: Thu, 6 Feb 2020 15:41:14 +0200
Subject: [PATCH] [BUGFIX] 1604 Fixed changes to the API, internal refactoring

- Added static runtime cache
---
 Classes/Controller/MailController.php       |   1 -
 Classes/Controller/NewsletterController.php |   6 +-
 Classes/Service/MailTemplateService.php     | 367 +++++++++++---------
 3 files changed, 199 insertions(+), 175 deletions(-)

diff --git a/Classes/Controller/MailController.php b/Classes/Controller/MailController.php
index 613d82fe..b00caa5b 100644
--- a/Classes/Controller/MailController.php
+++ b/Classes/Controller/MailController.php
@@ -379,7 +379,6 @@ class MailController extends ActionController {
 			foreach ((array) $parameters['templates'] as $key => $template) {
 				$mailTemplateService->setLanguage($key);
 				$mailTemplateService->setToAddresses($parameters['emailAddress']);
-//				$mailTemplateService->setFromAddress($template['fromMail']);
 				$mailTemplateService->setTemplateName($parameters['selectedTemplate']);
 				$mailTemplateService->setExtensionKey($parameters['selectedExtension']);
 				$mailTemplateService->setPreviewMarkers();
diff --git a/Classes/Controller/NewsletterController.php b/Classes/Controller/NewsletterController.php
index 58d123f2..8618c144 100644
--- a/Classes/Controller/NewsletterController.php
+++ b/Classes/Controller/NewsletterController.php
@@ -426,8 +426,6 @@ class NewsletterController extends ActionController {
 		$mailTemplateService->setOverwrittenFromMail($parameter['fromMail']);
 		$mailTemplateService->setSubject($parameter['subject']);
 		$mailTemplateService->setOverwrittenReplyTo($parameter['replyTo']);
-		$siteRootId = $mailTemplateService->getSiteRootId();
-		$templateObject = $mailTemplateService->getTemplate($siteRootId);
 
 		if (!$this->request->getArgument('sendRealEmails')) {
 			// Send test emails
@@ -456,7 +454,7 @@ class NewsletterController extends ActionController {
 						'www' => '	www.example.com',
 					]
 				]);
-				$mailIsSend = $mailTemplateService->sendEmail(TRUE, $templateObject);
+				$mailIsSend = $mailTemplateService->sendEmail(TRUE);
 			}
 
 		} else {
@@ -487,7 +485,7 @@ class NewsletterController extends ActionController {
 						$mailTemplateService->setMarkers(['user' => $recipient]);
 						// no real error handling here, one must check the MailQueue
 
-						$mailTemplateService->sendEmail(FALSE, $templateObject);
+						$mailTemplateService->sendEmail(FALSE);
 					} catch (\Exception $e) {
 						// Invalid email address could not be loaded to queue
 						$errorRecipients[] = $recipient['uid'] . ' - '
diff --git a/Classes/Service/MailTemplateService.php b/Classes/Service/MailTemplateService.php
index fe4a828a..55aba627 100644
--- a/Classes/Service/MailTemplateService.php
+++ b/Classes/Service/MailTemplateService.php
@@ -63,6 +63,16 @@ class MailTemplateService {
 	const REGISTER_FILE = 'Register.php';
 	const CONFIG_PATH = 'Configuration/MailTemplates';
 
+	/**
+	 * @var Template
+	 */
+	private static $template;
+
+	/**
+	 * @var array
+	 */
+	private static $mailObjectCache = [];
+
 	/**
 	 * @var string $toAddresses
 	 */
@@ -211,6 +221,97 @@ class MailTemplateService {
 	 */
 	private $subjectToSend;
 
+	/**
+	 * @param Template $template
+	 */
+	public static function setTemplate(Template $template): void {
+		self::$template = $template;
+	}
+
+	/**
+	 * @return string
+	 */
+	public function getOverwrittenBcc(): string {
+		return $this->overwrittenBcc;
+	}
+
+	/**
+	 * @param string $overwrittenBcc
+	 */
+	public function setOverwrittenBcc(string $overwrittenBcc): void {
+		$this->overwrittenBcc = $overwrittenBcc;
+	}
+
+	/**
+	 * @return string
+	 */
+	public function getOverwrittenCc(): string {
+		return $this->overwrittenCc;
+	}
+
+	/**
+	 * @param string $overwrittenCc
+	 */
+	public function setOverwrittenCc(string $overwrittenCc): void {
+		$this->overwrittenCc = $overwrittenCc;
+	}
+
+	/**
+	 * @return string
+	 */
+	public function getOverwrittenFromName(): string {
+		return $this->overwrittenFromName;
+	}
+
+	/**
+	 * @param string $overwrittenFromName
+	 */
+	public function setOverwrittenFromName(string $overwrittenFromName): void {
+		$this->overwrittenFromName = $overwrittenFromName;
+	}
+
+	/**
+	 * @return string
+	 */
+	public function getOverwrittenFromMail(): string {
+		return $this->overwrittenFromMail;
+	}
+
+	/**
+	 * @param string $overwrittenFromMail
+	 */
+	public function setOverwrittenFromMail(string $overwrittenFromMail): void {
+		$this->overwrittenFromMail = $overwrittenFromMail;
+	}
+
+	/**
+	 * @return string
+	 */
+	public function getOverwrittenReplyTo(): string {
+		return $this->overwrittenReplyTo;
+	}
+
+	/**
+	 * @param string $overwrittenReplyTo
+	 */
+	public function setOverwrittenReplyTo(string $overwrittenReplyTo): void {
+		$this->overwrittenReplyTo = $overwrittenReplyTo;
+	}
+
+	/**
+	 * @return string
+	 */
+	public function getOverwrittenToAddresses(): string {
+		return $this->overwrittenToAddresses;
+	}
+
+	/**
+	 * @param string $overwrittenToAddresses
+	 */
+	public function setOverwrittenToAddresses(string $overwrittenToAddresses): void {
+		$this->overwrittenToAddresses = $overwrittenToAddresses;
+	}
+
 	/**
 	 * MailTemplateService constructor.
 	 *
@@ -401,6 +502,34 @@ class MailTemplateService {
 		return $this;
 	}
 
+	/**
+	 * @return string|string[]|null
+	 */
+	public function getMailBodyToSend() {
+		return $this->mailBodyToSend;
+	}
+
+	/**
+	 * @param string|string[]|null $mailBodyToSend
+	 */
+	public function setMailBodyToSend($mailBodyToSend): void {
+		$this->mailBodyToSend = $mailBodyToSend;
+	}
+
+	/**
+	 * @return string
+	 */
+	public function getSubjectToSend(): string {
+		return $this->subjectToSend;
+	}
+
+	/**
+	 * @param string $subjectToSend
+	 */
+	public function setSubjectToSend(string $subjectToSend): void {
+		$this->subjectToSend = $subjectToSend;
+	}
+
 	/**
 	 * @param array $markers
 	 * @return MailTemplateService
@@ -674,28 +803,37 @@ class MailTemplateService {
 			$pageUid = $this->pid;
 		}
 
-		//TODO: move to page repository
 		if ($pageUid === 0) {
-			$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable(
-				'pages'
-			);
-			$rootPageRows = $queryBuilder->select('*')
-				->from('pages')
-				->where(
-					$queryBuilder->expr()->eq(
-						'is_siteroot', 1
-					)
+			$pageUid = $this->getAnyRootPageUid();
+		}
+		return $pageUid;
+	}
+
+	/**
+	 * Gets the page uid of any root page in the page tree
+	 *
+	 * @return int
+	 */
+	private function getAnyRootPageUid() {
+		$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable(
+			'pages'
+		);
+		$rootPageRows = $queryBuilder->select('*')
+			->from('pages')
+			->where(
+				$queryBuilder->expr()->eq(
+					'is_siteroot', 1
 				)
-				->andWhere(
-					$queryBuilder->expr()->eq(
-						'hidden', 0
-					)
+			)
+			->andWhere(
+				$queryBuilder->expr()->eq(
+					'hidden', 0
 				)
-				->execute()->fetchAll();
+			)
+			->execute()->fetchAll();
 
-			if ($rootPageRows && \count($rootPageRows)) {
-				$pageUid = (int) $rootPageRows[0]['uid'];
-			}
+		if ($rootPageRows && \count($rootPageRows)) {
+			$pageUid = (int) $rootPageRows[0]['uid'];
 		}
 		return $pageUid;
 	}
@@ -707,7 +845,7 @@ class MailTemplateService {
 	 * @return null|object|Template|FALSE
 	 * @throws \Exception
 	 */
-	public function getTemplate(int $siteRootId) {
+	public function getTemplate($siteRootId) {
 		$isTemplateBlacklisted = self::isTemplateBlacklisted($this->extensionKey, $this->templateName, $siteRootId);
 		if ($isTemplateBlacklisted) {
 			throw new \Exception('The template is blacklisted');
@@ -765,12 +903,14 @@ class MailTemplateService {
 	}
 
 	/**
-	 * @param $template
+	 * Sets the values to send the mail with from the template or register service
+	 *
+	 * @param Template|null $template
 	 * @param $registerService
 	 * @param $defaultTemplateContent
 	 * @param $siteRootId
 	 */
-	protected function setMailValuesFromObjects($template, $registerService, $defaultTemplateContent, $siteRootId): void {
+	protected function extractValuesForMail($template, $registerService, $defaultTemplateContent, $siteRootId): void {
 		/** @var StandaloneView $emailView */
 		$emailView = $this->objectManager->get(StandaloneView::class);
 		$emailView->assignMultiple($this->markers);
@@ -814,10 +954,7 @@ class MailTemplateService {
 
 		if ($this->fromAddress === '') {
 			$fromMail = $this->parseMarkers(
-				$this->getTemplateSource(
-					(empty($this->overwrittenFromMail) ? $template->getFromMail() : $this->overwrittenFromMail),
-					$layoutId, $siteRootId
-				),
+				empty($this->overwrittenFromMail) ? $template->getFromMail() : $this->overwrittenFromMail,
 				$emailView
 			);
 			$this->setFromAddress($fromMail);
@@ -825,10 +962,7 @@ class MailTemplateService {
 
 		if ($this->fromName === '') {
 			$fromName = $this->parseMarkers(
-				$this->getTemplateSource(
-					(empty($this->overwrittenFromName) ? $template->getFromName() : $this->overwrittenFromName),
-					$layoutId, $siteRootId
-				),
+				(empty($this->overwrittenFromName) ? $template->getFromName() : $this->overwrittenFromName),
 				$emailView
 			);
 			$this->setFromName($fromName);
@@ -836,10 +970,7 @@ class MailTemplateService {
 
 		if ($this->replyToAddress === '') {
 			$replyTo = $this->parseMarkers(
-				$this->getTemplateSource(
-					(empty($this->overwrittenReplyTo) ? $template->getReplyTo() : $this->overwrittenReplyTo),
-					$layoutId, $siteRootId
-				),
+				(empty($this->overwrittenReplyTo) ? $template->getReplyTo() : $this->overwrittenReplyTo),
 				$emailView
 			);
 			$this->setReplyToAddress($replyTo);
@@ -847,10 +978,7 @@ class MailTemplateService {
 
 		if (empty($this->ccAddresses)) {
 			$cc = $this->parseMarkers(
-				$this->getTemplateSource(
-					(empty($this->overwrittenCc) ? $template->getCc() : $this->overwrittenCc),
-					$layoutId, $siteRootId
-				),
+				(empty($this->overwrittenCc) ? $template->getCc() : $this->overwrittenCc),
 				$emailView
 			);
 			$this->setCcAddresses($cc);
@@ -858,10 +986,7 @@ class MailTemplateService {
 
 		if (empty($this->bccAddresses)) {
 			$bcc = $this->parseMarkers(
-				$this->getTemplateSource(
-					(empty($this->overwrittenBcc) ? $template->getBcc() : $this->overwrittenBcc),
-					$layoutId, $siteRootId
-				),
+				(empty($this->overwrittenBcc) ? $template->getBcc() : $this->overwrittenBcc),
 				$emailView
 			);
 			$this->setBccAddresses($bcc);
@@ -869,10 +994,7 @@ class MailTemplateService {
 
 		if ($this->toAddresses === '') {
 			$toAddress = $this->parseMarkers(
-				$this->getTemplateSource(
-					(empty($this->overwrittenToAddresses) ? $template->getToAddress() : $this->overwrittenToAddresses),
-					$layoutId, $siteRootId
-				),
+				(empty($this->overwrittenToAddresses) ? $template->getToAddress() : $this->overwrittenToAddresses),
 				$emailView
 			);
 			$this->setToAddresses($toAddress);
@@ -913,18 +1035,19 @@ class MailTemplateService {
 	 * @throws \TYPO3\CMS\Extbase\Persistence\Exception\IllegalObjectTypeException
 	 * @throws \TYPO3\CMS\Extbase\Persistence\Exception\UnknownObjectException
 	 */
-	public function sendEmail($isPreview = FALSE, Template $template = NULL): bool {
+	public function sendEmail($isPreview = FALSE): bool {
 		if ($isPreview) { //TODO: remove this from here
 			$this->setIgnoreMailQueue(TRUE);
 		}
 		$success = FALSE;
 
 		// Get page ID
+		// TODO: this doesn't belong here. The API user needs to provide the UID
 		$siteRootId = $this->getSiteRootId();
-		if ($template === NULL) {
+		if (self::$template === NULL) {
 			// Get template
 			try {
-				$template = $this->getTemplate($siteRootId);
+				self::$template = $this->getTemplate($siteRootId);
 			} catch (\Exception $e) {
 				return FALSE;
 			}
@@ -932,22 +1055,23 @@ class MailTemplateService {
 		$registerService = GeneralUtility::makeInstance(RegisterService::class);
 
 		// get default template content from register array
-		$defaultTemplateContent = $this->getDefaultTemplateContent($template, $registerService);
+		$defaultTemplateContent = $this->getDefaultTemplateContent(self::$template, $registerService);
 
 		// set the ToAddress if there are no placeholders in it
 		// TODO: does this belong here?
-		if ($template !== NULL && \filter_var($template->getToAddress(), FILTER_VALIDATE_EMAIL)) {
-			$this->setToAddresses(\trim($template->getToAddress()));
+		if (self::$template !== NULL && \filter_var(self::$template->getToAddress(), FILTER_VALIDATE_EMAIL)) {
+			$this->setToAddresses(\trim(self::$template->getToAddress()));
 		}
 
-		$this->setMailValuesFromObjects($template, $registerService, $defaultTemplateContent, $siteRootId);
+		$this->extractValuesForMail(self::$template, $registerService, $defaultTemplateContent, $siteRootId);
 
 		$mail = $this->addMailToMailQueue(
 			$this->extensionKey, $this->templateName, $this->getSubjectToSend(), $this->getMailBodyToSend(), $this->priority,
 			0, 0, $this->language, $siteRootId
 		);
+		self::$mailObjectCache[$mail->getUid()] = $mail; // add it to cache to avoid extra DB queries
 		if ($this->ignoreMailQueue) {
-			$success = $this->sendMailFromQueue($mail->getUid(), $mail);
+			$success = $this->sendMailFromQueue($mail->getUid());
 		}
 
 		//TODO: this can be avoided if the sending logic is decoupled from this function
@@ -1085,24 +1209,38 @@ class MailTemplateService {
 		return $mail;
 	}
 
+	/**
+	 * Get the mail object by uid and check if it's blacklisted
+	 * @param $uid
+	 */
+	private function getMailObjectByUid($uid) {
+		$mailRepository = $this->objectManager->get(MailRepository::class);
+		$mailObject = $mailRepository->findOneByUid($uid);
+		if (!$mailObject || $mailObject->getBlacklisted()) {
+			return FALSE;
+		}
+		return $mailObject;
+	}
+
 	/**
 	 * Send a Mail from the queue, identified by its id
 	 *
 	 * @param int $uid
-	 * @param null $mailToSend
 	 * @return bool|NULL
 	 * @throws \TYPO3\CMS\Core\Resource\Exception\ResourceDoesNotExistException
 	 * @throws \TYPO3\CMS\Extbase\Persistence\Exception\IllegalObjectTypeException
 	 * @throws \TYPO3\CMS\Extbase\Persistence\Exception\UnknownObjectException
 	 */
-	public function sendMailFromQueue($uid, $mailToSend = NULL): bool {
+	public function sendMailFromQueue($uid): bool {
 		$mailRepository = $this->objectManager->get(MailRepository::class);
 		/** @var Mail $mailToSend */
-		if ($mailToSend === NULL) {
-			$mailToSend = $mailRepository->findOneByUid($uid);
-			if (!$mailToSend || $mailToSend->getBlacklisted()) {
+		if (!isset(self::$mailObjectCache[$uid]) || !self::$mailObjectCache[$uid] instanceof Mail) {
+			$mailToSend = $this->getMailObjectByUid($uid);
+			if ($mailToSend === FALSE) {
 				return FALSE;
 			}
+		} else {
+			$mailToSend = self::$mailObjectCache[$uid];
 		}
 
 		$this->mailMessage->setBody($mailToSend->getMailBody(), 'text/html');
@@ -1153,121 +1291,10 @@ class MailTemplateService {
 		} else {
 			$this->mailMessage->getFailedRecipients();
 		}
+		unset(self::$mailObjectCache[$uid]); // free the memory
 		return $success;
 	}
 
-	/**
-	 * @return string
-	 */
-	public function getOverwrittenBcc(): string {
-		return $this->overwrittenBcc;
-	}
-
-	/**
-	 * @param string $overwrittenBcc
-	 */
-	public function setOverwrittenBcc(string $overwrittenBcc): void {
-		$this->overwrittenBcc = $overwrittenBcc;
-	}
-
-	/**
-	 * @return string
-	 */
-	public function getOverwrittenCc(): string {
-		return $this->overwrittenCc;
-	}
-
-	/**
-	 * @param string $overwrittenCc
-	 */
-	public function setOverwrittenCc(string $overwrittenCc): void {
-		$this->overwrittenCc = $overwrittenCc;
-	}
-
-	/**
-	 * @return string
-	 */
-	public function getOverwrittenFromName(): string {
-		return $this->overwrittenFromName;
-	}
-
-	/**
-	 * @param string $overwrittenFromName
-	 */
-	public function setOverwrittenFromName(string $overwrittenFromName): void {
-		$this->overwrittenFromName = $overwrittenFromName;
-	}
-
-	/**
-	 * @return string
-	 */
-	public function getOverwrittenFromMail(): string {
-		return $this->overwrittenFromMail;
-	}
-
-	/**
-	 * @param string $overwrittenFromMail
-	 */
-	public function setOverwrittenFromMail(string $overwrittenFromMail): void {
-		$this->overwrittenFromMail = $overwrittenFromMail;
-	}
-
-	/**
-	 * @return string
-	 */
-	public function getOverwrittenReplyTo(): string {
-		return $this->overwrittenReplyTo;
-	}
-
-	/**
-	 * @param string $overwrittenReplyTo
-	 */
-	public function setOverwrittenReplyTo(string $overwrittenReplyTo): void {
-		$this->overwrittenReplyTo = $overwrittenReplyTo;
-	}
-
-	/**
-	 * @return string
-	 */
-	public function getOverwrittenToAddresses(): string {
-		return $this->overwrittenToAddresses;
-	}
-
-	/**
-	 * @param string $overwrittenToAddresses
-	 */
-	public function setOverwrittenToAddresses(string $overwrittenToAddresses): void {
-		$this->overwrittenToAddresses = $overwrittenToAddresses;
-	}
-
-	/**
-	 * @return string|string[]|null
-	 */
-	public function getMailBodyToSend() {
-		return $this->mailBodyToSend;
-	}
-
-	/**
-	 * @param string|string[]|null $mailBodyToSend
-	 */
-	public function setMailBodyToSend($mailBodyToSend): void {
-		$this->mailBodyToSend = $mailBodyToSend;
-	}
-
-	/**
-	 * @return string
-	 */
-	public function getSubjectToSend(): string {
-		return $this->subjectToSend;
-	}
-
-	/**
-	 * @param string $subjectToSend
-	 */
-	public function setSubjectToSend(string $subjectToSend): void {
-		$this->subjectToSend = $subjectToSend;
-	}
-
 	/**
 	 * Sets the fromMail property of the mailTemplateService.
 	 * Checks validity and uses all available fallbacks
-- 
GitLab