templateName = $templateName;
$this->extensionKey = $extensionKey;
$this->markers = $markers;
$this->markerLabels = $markerLabels;
$this->registerService = GeneralUtility::makeInstance(RegisterService::class);
if (version_compare(VersionNumberUtility::getCurrentTypo3Version(), '10.4.0')) {
$this->objectManager = GeneralUtility::makeInstance(ObjectManager::class);
/** @var TypoScriptSettingsService $typoScriptSettingsService */
$typoScriptSettingsService = $this->objectManager->get(TypoScriptSettingsService::class);
} else {
$typoScriptSettingsService = GeneralUtility::makeInstance(TypoScriptSettingsService::class);
}
$tsSettings = $typoScriptSettingsService->getSettings(0, 'tx_sgmail');
// use defaultMailFromAddress if it is provided in LocalConfiguration.php; use the sg_mail TS setting as fallback
if (filter_var($GLOBALS['TYPO3_CONF_VARS']['MAIL']['defaultMailFromAddress'], FILTER_VALIDATE_EMAIL)) {
$this->fromAddress = $GLOBALS['TYPO3_CONF_VARS']['MAIL']['defaultMailFromAddress'];
} else if (!filter_var($tsSettings['mail']['default']['from'], FILTER_VALIDATE_EMAIL)) {
$this->fromAddress = 'noreply@example.org';
} else {
$this->fromAddress = $tsSettings['mail']['default']['from'];
}
if ($GLOBALS['TYPO3_CONF_VARS']['MAIL']['defaultMailFromName']) {
$this->fromName = $GLOBALS['TYPO3_CONF_VARS']['MAIL']['defaultMailFromName'];
}
$bccAddresses = GeneralUtility::trimExplode(',', $tsSettings['mail']['default']['bcc']);
$ccAddresses = GeneralUtility::trimExplode(',', $tsSettings['mail']['default']['cc']);
foreach ($bccAddresses as $index => $email) {
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
unset($bccAddresses[$index]);
}
}
foreach ($ccAddresses as $index => $email) {
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
unset($ccAddresses[$index]);
}
}
$this->bccAddresses = implode(',', $bccAddresses);
$this->ccAddresses = implode(',', $ccAddresses);
}
/**
* Return default markers for sg_mail
*
* @param string $translationKey
* @param array $marker
* @param string $extensionKey
* @return array
*/
public static function getDefaultTemplateMarker(string $translationKey, array $marker, $extensionKey = 'sg_mail'): array {
$languagePath = 'LLL:EXT:' . $extensionKey . '/Resources/Private/Language/locallang.xlf:' . $translationKey;
// Need the key for translations
if (trim($extensionKey) === '') {
return [];
}
$generatedMarker = [];
foreach ($marker as $markerName) {
$generatedMarker[] = [
'marker' => $markerName,
'value' => $languagePath . '.example.' . $markerName,
'description' => $languagePath . '.description.' . $markerName,
'backend_translation_key' => $translationKey . '.example.' . $markerName,
'extension_key' => $extensionKey
];
}
return $generatedMarker;
}
/**
* Adds a file resource as attachment
*
* @param FileReference|null $fileReference
* @param File|null $file
*
* @return MailTemplateService
*/
public function addFileResourceAttachment(
FileReference $fileReference = NULL, FileInterface $file = NULL
): MailTemplateService {
if (!$file) {
if (!$fileReference) {
return $this;
}
$originalResource = $fileReference->getOriginalResource();
if (!$originalResource) {
return $this;
}
$file = $originalResource->getOriginalFile();
if (!$file) {
return $this;
}
}
$coreFileReferenceMailFile = GeneralUtility::makeInstance(ResourceFactory::class)
->createFileReferenceObject(
[
'uid_local' => $file->getUid(),
'table_local' => 'sys_file',
'uid' => uniqid('NEW_MAIL', TRUE)
]
);
$newFileReference = GeneralUtility::makeInstance(FileReference::class);
$newFileReference->setOriginalResource($coreFileReferenceMailFile);
$this->markers[] = $newFileReference;
return $this;
}
/**
* Allows adding a file from filePath,
* YOU MUST USE setIgnoreMailQueue(TRUE)
* because we CAN NOT guarantee access to the added files
* when sending mails from the Queue, since this allows temporary files etc.
*
* @param string $path
* @param string|null $name
* @param string|null $contentType
*/
public function addFileFromFilePathForDirectSending(string $path, string $name = NULL, string $contentType = NULL) {
$this->mailMessage->attachFromPath($path, $name, $contentType);
}
/**
* Set preview markers for the template editor
*
* @throws NoSuchCacheException
*/
public function setPreviewMarkers(): void {
$previewMarkers = [];
/** @var array $markerArray */
$markerArray = $this->registerService->getRegisterArray()[$this->extensionKey][$this->templateName]['marker'];
foreach ($markerArray as $marker) {
$markerPath = GeneralUtility::trimExplode('.', $marker['marker']);
$temporaryMarkerArray = [];
foreach (array_reverse($markerPath) as $index => $markerPathSegment) {
if ($index === 0) {
if ($marker['markerLabel']) {
$markerPathSegment = $marker['markerLabel'];
}
if ($marker['backend_translation_key']) {
$temporaryMarkerArray[$markerPathSegment] = LocalizationUtility::translate(
$marker['backend_translation_key'], $marker['extension_key']
);
} else {
$temporaryMarkerArray[$markerPathSegment] = $marker['value'];
}
} else {
$temporaryMarkerArray = [$markerPathSegment => $temporaryMarkerArray];
}
}
$previewMarkers[] = $temporaryMarkerArray;
}
$this->setMarkers(array_merge_recursive(...$previewMarkers));
}
/**
* Send the Email
*
* @param bool $isPreview
* @param bool $isNewsletter
* @return bool email was sent or added to mail queue successfully?
* @throws NoSuchCacheException
* @throws ResourceDoesNotExistException
* @throws IllegalObjectTypeException
* @throws UnknownObjectException
*/
public function sendEmail(bool $isPreview = FALSE, bool $isNewsletter = FALSE): bool {
try {
$template = $this->getTemplate();
} catch (\Exception $e) {
return FALSE;
}
// Load the values from the template if they are set
if (!$isNewsletter && $template !== NULL) {
$this->loadTemplateValues($template);
}
// get default template content from register array
$defaultTemplateContent = $this->getDefaultTemplateContent($template);
$this->parseValuesForMail($template, $defaultTemplateContent);
$mail = $this->addMailToMailQueue();
self::$mailObjectCache[$mail->getUid()] = $mail; // add it to cache to avoid extra DB queries
if ($this->ignoreMailQueue) {
return $this->sendMailFromQueue($mail->getUid());
}
return TRUE;
}
/**
* Get the template object
*
* @return null|Template
* @throws \Exception
*/
private function getTemplate(): ?Template {
$isTemplateBlacklisted = $this->registerService->isTemplateBlacklisted(
$this->extensionKey,
$this->templateName,
$this->pid
);
if ($isTemplateBlacklisted) {
throw new \Exception('The template is blacklisted');
}
$templateHash = $this->getTemplateHash();
if (
isset(self::$templateObjectCache[$templateHash])
&& self::$templateObjectCache[$templateHash] instanceof Template
) {
return self::$templateObjectCache[$templateHash];
}
/** @var Template $template */
$template = $this->getTemplateRepository()->findByTemplateProperties(
$this->extensionKey, $this->templateName, [$this->siteLanguage], $this->pid
)->getFirst();
if ($template === NULL && !empty($this->siteLanguage->getFallbackLanguageIds())) {
$site = GeneralUtility::makeInstance(SiteFinder::class)->getSiteByPageId($this->pid);
$fallbackLanguages = [];
foreach ($this->siteLanguage->getFallbackLanguageIds() as $fallbackLanguageId) {
$fallbackLanguages[] = $site->getLanguageById($fallbackLanguageId);
}
$template = $this->getTemplateRepository()->findByTemplateProperties(
$this->extensionKey, $this->templateName, $fallbackLanguages, $this->pid
)->getFirst();
} elseif($template === NULL) {
$templateArray = $this->registerService->findTemplate(
$this->extensionKey,
$this->templateName,
$this->siteLanguage
);
$templateArray['pid'] = $this->pid;
$template = $this->getTemplateRepository()->create($templateArray);
}
self::$templateObjectCache[$templateHash] = $template;
return $template;
}
/**
* Get the hash for the object cache
*
* @return string
*/
private function getTemplateHash(): string {
return md5(
$this->extensionKey . '_' .
$this->templateName . '_' .
$this->pid . '_' .
$this->siteLanguage->getLanguageId()
);
}
/**
* use all values from the given template
*
* @param Template $template
*/
public function loadTemplateValues(Template $template): void {
$this->pid = $template->getPid();
$this->extensionKey = $template->getExtensionKey();
$this->templateName = $template->getTemplateName();
$this->siteLanguage = $template->getSiteLanguage();
$fromName = trim($template->getFromName());
if ($fromName === '') {
$fromName = $this->fromName;
}
if ($fromName === '' && $GLOBALS['TYPO3_CONF_VARS']['MAIL']['defaultMailFromName']) {
$fromName = $GLOBALS['TYPO3_CONF_VARS']['MAIL']['defaultMailFromName'];
}
$fromMail = $this->getValidFromMail(trim($template->getFromMail()));
// The setters check if the value is empty or not
$this->setFromAddress($fromMail, $fromName);
$this->setCcAddresses($template->getCc());
$this->setBccAddresses($template->getBcc());
$this->setReplyToAddress($template->getReplyTo());
$this->setFromName($fromName);
}
/**
* Sets the fromMail property of the mailTemplateService.
* Checks validity and uses all available fallbacks
*
* @param string $fromMail
* @return string
*/
private function getValidFromMail(string $fromMail): string {
$fromMail = trim($fromMail);
if (!filter_var($fromMail, FILTER_VALIDATE_EMAIL)) {
$fromMail = $this->fromAddress;
}
if (!filter_var($fromMail, FILTER_VALIDATE_EMAIL)) {
$fromMail = $GLOBALS['TYPO3_CONF_VARS']['MAIL']['defaultMailFromAddress'];
if (!filter_var($GLOBALS['TYPO3_CONF_VARS']['MAIL']['defaultMailFromAddress'], FILTER_VALIDATE_EMAIL)) {
$fromMail = 'noreply@example.com';
}
}
return $fromMail;
}
/**
* Get the default content for this template
*
* @param Template|null $template
* @return bool|string
* @throws Exception
* @throws NoSuchCacheException
*/
protected function getDefaultTemplateContent(Template $template = NULL) {
$defaultTemplateContent =
$this->registerService->getRegisterArray()[$this->extensionKey][$this->templateName]['templateContent'];
// If there is no template for this language, use the default template
if ($template === NULL && $defaultTemplateContent === NULL) {
$templatePath =
$this->registerService->getRegisterArray()[$this->extensionKey][$this->templateName]['templatePath'];
// only standard template file is considered since version 4.1
$defaultTemplateFile = $templatePath . 'template.html';
if (file_exists($defaultTemplateFile)) {
$defaultTemplateContent = file_get_contents($defaultTemplateFile);
} else {
// use configured default html template
if (version_compare(VersionNumberUtility::getCurrentTypo3Version(), '10.4.0', '<')) {
/** @var TypoScriptSettingsService $typoScriptSettingsService */
$typoScriptSettingsService = $this->objectManager->get(TypoScriptSettingsService::class);
} else {
$typoScriptSettingsService = GeneralUtility::makeInstance(TypoScriptSettingsService::class);
}
$tsSettings = $typoScriptSettingsService->getSettings(0, 'tx_sgmail');
$defaultTemplateFile = GeneralUtility::getFileAbsFileName(
$tsSettings['mail']['defaultHtmlTemplate']
);
if (file_exists($defaultTemplateFile)) {
$defaultTemplateContent = file_get_contents($defaultTemplateFile);
} else {
return FALSE;
}
}
} elseif ($template !== NULL) {
$defaultTemplateContent = $template->getContent();
} else {
$defaultTemplateContent = '';
}
return $defaultTemplateContent;
}
/**
* @param string $toAddresses
* @return MailTemplateService
*/
public function setToAddresses(string $toAddresses): MailTemplateService {
$normalizedToAddresses = trim(preg_replace('~\x{00a0}~iu', ' ', $toAddresses));
$this->toAddresses = $normalizedToAddresses;
return $this;
}
/**
* Sets the values to send the mail with from the template or register service
*
* @param Template|null $template
* @param string $defaultTemplateContent
* @throws Exception
* @throws NoSuchCacheException
*/
protected function parseValuesForMail(
Template $template = NULL, string $defaultTemplateContent = ''
): void {
if (version_compare(VersionNumberUtility::getCurrentTypo3Version(), '10.4.0', '<')) {
/** @var StandaloneView $emailView */
$emailView = $this->objectManager->get(StandaloneView::class);
} else {
$emailView = GeneralUtility::makeInstance(StandaloneView::class);
}
$emailView->assignMultiple($this->markers);
$emailView->assign('all_fields', $this->getAllMarker($this->markers));
$emailView->assign('all_fields_html', $this->getAllMarkerHTML($this->markers));
//TODO: make this as the lines below the next block
$overwrittenEmailBody = $this->getOverwrittenEmailBody();
$overwrittenSubject = '';
if ($this->subject !== '' && $this->subject !== NULL) {
$overwrittenSubject = $this->subject;
}
// parse markers
if ($template !== NULL) {
$subject = $this->parseMarkers(
trim(empty($overwrittenSubject) ? $template->getSubject() : $overwrittenSubject),
$emailView
);
$layoutId = $template->getLayout();
$templateContent = $template->getContent();
$this->setSubjectToSend($subject);
} else {
$subject = $this->registerService->getRegisterArray()[$this->extensionKey][$this->templateName]['subject'];
if (is_array($subject)) {
$subject = trim(
$this->registerService->getRegisterArray()
[$this->extensionKey][$this->templateName]['subject'][$this->siteLanguage->getTypo3Language()]
);
}
$subject = $this->parseMarkers(
(empty($overwrittenSubject) ? $subject : $overwrittenSubject),
$emailView
);
$layoutId = 0;
$templateContent = $defaultTemplateContent;
}
$this->setSubjectToSend($subject);
// Parse the markers
if ($this->fromName) {
$this->setFromName($this->parseMarkers($this->fromName, $emailView));
}
if ($this->fromAddress) {
$this->setFromAddress($this->parseMarkers($this->fromAddress, $emailView));
}
if ($this->replyToAddress) {
$this->setReplyToAddress($this->parseMarkers($this->replyToAddress, $emailView));
}
if ($this->ccAddresses) {
$this->setCcAddresses($this->parseMarkers($this->ccAddresses, $emailView));
}
if ($this->bccAddresses) {
$this->setBccAddresses($this->parseMarkers($this->bccAddresses, $emailView));
}
if ($this->toAddresses) {
$this->setToAddresses($this->parseMarkers($this->toAddresses, $emailView));
}
// reset template source back to default
$emailView->setTemplateSource(
empty($overwrittenEmailBody) ? $templateContent : $overwrittenEmailBody
);
// insert
tags, but replace every instance of three or more successive breaks with just two.
$emailBody = $emailView->render();
$emailBody = nl2br($emailBody);
$emailBody = preg_replace('/(
[\s]*){3,}/', '
', $emailBody);
$layout = $this->getLayoutRepository()->findByUidOrDefault(
$layoutId,
$this->pid,
$this->siteLanguage->getLanguageId()
);
$emailHTMLHead = '';
if ($layout) {
$emailHTMLHead = $layout->getHeadContent();
$emailBody = str_replace('###CONTENT###', $emailBody, $layout->getContent());
}
$this->mailBodyToSend = '
' . $key . ' | ' . $value . ' |
---|---|
' . $key . '.' . $innerKey . ' | ' . $innerValue . ' |
' . $key . ' | ' . $valueAsString . ' |
' . $key . ' | ' . $value->format('d.m.Y') . ' |
' . $key . ' | ' . $value->__toString() . ' |