diff --git a/.github/workflows/code-style.yml b/.github/workflows/code-style.yml index 5a291f7..b243bde 100644 --- a/.github/workflows/code-style.yml +++ b/.github/workflows/code-style.yml @@ -24,7 +24,7 @@ jobs: strategy: fail-fast: false matrix: - version-selection: [ 'highest' ] + version-selection: [ 'lowest', 'highest' ] steps: - name: Checkout uses: actions/checkout@v4 diff --git a/composer.json b/composer.json index 75a4270..01234b4 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,6 @@ { "name": "frosh/frosh-platform-template-mail", - "version": "3.0.7", + "version": "4.0.0", "type": "shopware-platform-plugin", "description": "Load mail templates from theme", "keywords": [ @@ -45,10 +45,13 @@ } }, "require": { - "shopware/core": "~6.5.0 || ~6.6.0 || ~6.7.0" + "shopware/core": "~6.5.8 || ~6.6.0 || ~6.7.0" }, "scripts": { "format": "docker run --rm -v $(pwd):/ext shopware/shopware-cli:latest extension format /ext", - "check": "docker run --rm -v $(pwd):/ext shopware/shopware-cli:latest extension validate --full /ext" + "check": [ + "docker run --rm -v $(pwd):/ext shopware/shopware-cli:latest extension validate --check-against lowest --full /ext", + "docker run --rm -v $(pwd):/ext shopware/shopware-cli:latest extension validate --check-against highest --full /ext" + ] } } diff --git a/src/DTO/TemplateData.php b/src/DTO/TemplateData.php new file mode 100644 index 0000000..4726738 --- /dev/null +++ b/src/DTO/TemplateData.php @@ -0,0 +1,20 @@ +subject !== null + && $this->html !== null + && $this->plain !== null; + } +} diff --git a/src/DTO/TemplateType.php b/src/DTO/TemplateType.php new file mode 100644 index 0000000..43a8049 --- /dev/null +++ b/src/DTO/TemplateType.php @@ -0,0 +1,19 @@ + 'subject.', + self::HTML => 'html.', + self::PLAIN => 'plain.', + }; + } +} diff --git a/src/DTO/TypeData.php b/src/DTO/TypeData.php new file mode 100644 index 0000000..434be57 --- /dev/null +++ b/src/DTO/TypeData.php @@ -0,0 +1,12 @@ + getSalesChannelId(); - + ): TemplateData { $cacheKey = hash( 'xxh128', - $type - . $technicalName + $technicalName . $mailTemplateId - . $salesChannelId + . $businessEvent->getSalesChannelId() . $businessEvent->getContext()->getLanguageId() - . $returnFolder, ); - return $this->cache->get($cacheKey, function (ItemInterface $cacheItem) use ($type, $technicalName, $businessEvent, $returnFolder, $mailTemplateId) { + return $this->cache->get($cacheKey, function (ItemInterface $cacheItem) use ($technicalName, $businessEvent, $mailTemplateId) { $cacheItem->expiresAfter(3600); - return $this->mailFinderService->findTemplateByTechnicalName($type, $technicalName, $businessEvent, $returnFolder, $mailTemplateId); + return $this->mailFinderService->getTemplateDataByTechnicalName($technicalName, $businessEvent, $mailTemplateId); }); } } diff --git a/src/Services/MailFinderService.php b/src/Services/MailFinderService.php index 094f95b..9d09131 100644 --- a/src/Services/MailFinderService.php +++ b/src/Services/MailFinderService.php @@ -5,6 +5,9 @@ namespace Frosh\TemplateMail\Services; use Doctrine\DBAL\Connection; +use Frosh\TemplateMail\DTO\TemplateData; +use Frosh\TemplateMail\DTO\TemplateType; +use Frosh\TemplateMail\DTO\TypeData; use Frosh\TemplateMail\Services\MailLoader\LoaderInterface; use Shopware\Core\Framework\Uuid\Uuid; use Symfony\Component\DependencyInjection\Attribute\AsAlias; @@ -16,10 +19,6 @@ #[AsAlias] class MailFinderService implements MailFinderServiceInterface { - final public const TYPE_HTML = 'html.'; - final public const TYPE_PLAIN = 'plain.'; - final public const TYPE_SUBJECT = 'subject.'; - /** * @param LoaderInterface[] $availableLoaders * @param array $bundles @@ -36,55 +35,56 @@ public function __construct( ) { } - public function findTemplateByTechnicalName( - string $type, + public function getTemplateDataByTechnicalName( string $technicalName, TemplateMailContext $businessEvent, - bool $returnFolder = false, ?string $mailTemplateId = null, - ): ?string { + ): TemplateData { + $templateData = new TemplateData(); + $paths = $this->filesystemLoader->getPaths(); - $searchFolder = $this->searchPathProvider->buildPaths($businessEvent); + $searchFolders = $this->searchPathProvider->buildPaths($businessEvent); $themePath = $this->findPathOfThemeFromPluginOrApp($businessEvent->getSalesChannelId()) ?? $this->findPathOfThemeFromSymfonyBundle($businessEvent->getSalesChannelId()); if (\is_string($themePath)) { - usort($paths, static function ($a, $b) use ($themePath) { - if (str_contains($a, $themePath)) { - return -1; - } - - if (str_contains($b, $themePath)) { - return 1; - } - - return 0; - }); + \usort( + $paths, + static fn ($a, $b) => \str_contains($b, $themePath) <=> \str_contains($a, $themePath) + ); } foreach ($paths as $path) { - foreach ($this->availableLoaders as $availableLoader) { - $supportedExtensions = $availableLoader->supportedExtensions(); + $viewMailFolderPath = $path . '/email'; + if (!\is_dir($viewMailFolderPath)) { + continue; + } + + foreach ($this->availableLoaders as $loader) { + $supportedExtensions = $loader->supportedExtensions(); foreach ($supportedExtensions as $supportedExtension) { - foreach ($searchFolder as $folder) { - $filePath = $path . '/email/' . $folder . '/' . $technicalName . '/' . $mailTemplateId . '/' . $type . $supportedExtension; - if (file_exists($filePath) && $content = $availableLoader->load($filePath)) { - return $returnFolder ? $filePath : $content; + foreach ($searchFolders as $folder) { + $folderPath = $viewMailFolderPath . '/' . $folder . '/' . $technicalName; + if (!\is_dir($folderPath)) { + continue; } - $filePath = $path . '/email/' . $folder . '/' . $technicalName . '/' . $type . $supportedExtension; - if (file_exists($filePath) && $content = $availableLoader->load($filePath)) { - return $returnFolder ? $filePath : $content; + $templateData->subject ??= $this->loadTypeData(TemplateType::SUBJECT, $loader, $supportedExtension, $folderPath, $mailTemplateId); + $templateData->html ??= $this->loadTypeData(TemplateType::HTML, $loader, $supportedExtension, $folderPath, $mailTemplateId); + $templateData->plain ??= $this->loadTypeData(TemplateType::PLAIN, $loader, $supportedExtension, $folderPath, $mailTemplateId); + + if ($templateData->isAllSet()) { + return $templateData; } } } } } - return null; + return $templateData; } public function findPathOfThemeFromPluginOrApp(string $salesChannelId): ?string @@ -128,4 +128,30 @@ public function findPathOfThemeFromSymfonyBundle(string $salesChannelId): ?strin return null; } + + private function loadTypeData( + TemplateType $type, + LoaderInterface $loader, + string $supportedExtension, + string $folderPath, + ?string $mailTemplateId + ): ?TypeData { + $filePath = $folderPath . '/' . $mailTemplateId . '/' . $type->filePart() . $supportedExtension; + if (\is_file($filePath) && $content = $loader->load($filePath)) { + return new TypeData( + $filePath, + $content, + ); + } + + $filePath = $folderPath . '/' . $type->filePart() . $supportedExtension; + if (\is_file($filePath) && $content = $loader->load($filePath)) { + return new TypeData( + $filePath, + $content, + ); + } + + return null; + } } diff --git a/src/Services/MailFinderServiceInterface.php b/src/Services/MailFinderServiceInterface.php index 84a7e0f..54bb594 100644 --- a/src/Services/MailFinderServiceInterface.php +++ b/src/Services/MailFinderServiceInterface.php @@ -4,13 +4,13 @@ namespace Frosh\TemplateMail\Services; +use Frosh\TemplateMail\DTO\TemplateData; + interface MailFinderServiceInterface { - public function findTemplateByTechnicalName( - string $type, + public function getTemplateDataByTechnicalName( string $technicalName, TemplateMailContext $businessEvent, - bool $returnFolder = false, ?string $mailTemplateId = null, - ): ?string; + ): TemplateData; } diff --git a/src/Services/SearchPathProvider.php b/src/Services/SearchPathProvider.php index d820ebf..cb2a373 100644 --- a/src/Services/SearchPathProvider.php +++ b/src/Services/SearchPathProvider.php @@ -26,8 +26,9 @@ public function buildPaths(TemplateMailContext $businessEvent): array { $searchFolder = [$businessEvent->getContext()->getLanguageId(), 'global']; - if ($businessEvent->getContext()->getSource() instanceof SalesChannelApiSource) { - array_unshift($searchFolder, $businessEvent->getContext()->getSource()->getSalesChannelId()); + $contextSource = $businessEvent->getContext()->getSource(); + if ($contextSource instanceof SalesChannelApiSource) { + array_unshift($searchFolder, $contextSource->getSalesChannelId()); } $criteria = new Criteria($businessEvent->getContext()->getLanguageIdChain()); diff --git a/src/Subscriber/FlowSubscriber.php b/src/Subscriber/FlowSubscriber.php index a6f72ee..9d8007f 100644 --- a/src/Subscriber/FlowSubscriber.php +++ b/src/Subscriber/FlowSubscriber.php @@ -4,17 +4,16 @@ namespace Frosh\TemplateMail\Subscriber; -use Frosh\TemplateMail\Services\MailFinderService; use Frosh\TemplateMail\Services\MailFinderServiceInterface; use Frosh\TemplateMail\Services\TemplateMailContext; use Shopware\Core\Content\Flow\Events\FlowSendMailActionEvent; use Shopware\Core\Content\MailTemplate\Aggregate\MailTemplateType\MailTemplateTypeCollection; -use Shopware\Core\Content\MailTemplate\Aggregate\MailTemplateType\MailTemplateTypeEntity; use Shopware\Core\Defaults; use Shopware\Core\Framework\Adapter\Translation\AbstractTranslator; use Shopware\Core\Framework\Adapter\Translation\Translator; use Shopware\Core\Framework\Context; use Shopware\Core\Framework\DataAbstractionLayer\EntityRepository; +use Shopware\Core\Framework\DataAbstractionLayer\PartialEntity; use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria; use Shopware\Core\Framework\Validation\DataBag\DataBag; use Shopware\Core\System\Language\LanguageCollection; @@ -63,10 +62,6 @@ public function onFlowSendMailActionEvent(FlowSendMailActionEvent $event): void private function sendMail(DataBag $dataBag, string $mailTemplateTypeId, Context $context): void { - /** @var MailTemplateTypeEntity $mailTemplateType */ - $mailTemplateType = $this->mailTemplateTypeRepository->search(new Criteria([$mailTemplateTypeId]), $context)->first(); - - $technicalName = $mailTemplateType->getTechnicalName(); $templateId = $dataBag->get('templateId', null); \assert($templateId === null || \is_string($templateId)); @@ -74,29 +69,32 @@ private function sendMail(DataBag $dataBag, string $mailTemplateTypeId, Context $this->fixTranslator($event); - $html = $this->mailFinderService->findTemplateByTechnicalName(MailFinderService::TYPE_HTML, $technicalName, $event, false, $templateId); - $plain = $this->mailFinderService->findTemplateByTechnicalName(MailFinderService::TYPE_PLAIN, $technicalName, $event, false, $templateId); - $subject = $this->mailFinderService->findTemplateByTechnicalName(MailFinderService::TYPE_SUBJECT, $technicalName, $event, false, $templateId); + $technicalName = $this->getTechnicalName($mailTemplateTypeId, $context); + $templateData = $this->mailFinderService->getTemplateDataByTechnicalName($technicalName, $event, $templateId); - if ($html) { - $dataBag->set('contentHtml', $html); + if ($templateData->html !== null) { + $dataBag->set('contentHtml', $templateData->html->content); } - if ($plain) { - $dataBag->set('contentPlain', $plain); + if ($templateData->plain !== null) { + $dataBag->set('contentPlain', $templateData->plain->content); } - $salesChannelId = $dataBag->get('salesChannelId') ?: null; - if ($subject && \is_string($salesChannelId)) { - $debugMode = $this->systemConfigService->getBool('FroshPlatformTemplateMail.config.debugMode', $salesChannelId); - if ($debugMode) { - $subject = \sprintf( - 'DEBUG: %s (%s - %s - %s)', - $subject, - $this->getSalesChannelName($salesChannelId, $context), - $this->getLocaleCode($context->getLanguageId(), $context), - $technicalName, - ); + if ($templateData->subject !== null) { + $subject = $templateData->subject->content; + $salesChannelId = $dataBag->get('salesChannelId') ?: null; + + if (\is_string($salesChannelId)) { + $debugMode = $this->systemConfigService->getBool('FroshPlatformTemplateMail.config.debugMode', $salesChannelId); + if ($debugMode) { + $subject = \sprintf( + 'DEBUG: %s (%s - %s - %s)', + $subject, + $this->getSalesChannelName($salesChannelId, $context), + $this->getLocaleCode($context->getLanguageId(), $context), + $technicalName, + ); + } } $dataBag->set('subject', $subject); } @@ -157,4 +155,21 @@ private function getSalesChannelName(string $salesChannelId, Context $context): return $name; } + + private function getTechnicalName(string $mailTemplateTypeId, Context $context): string + { + $criteria = new Criteria([$mailTemplateTypeId]); + $criteria->addFields(['technicalName']); + + /** @var PartialEntity|null $mailTemplateType */ + $mailTemplateType = $this->mailTemplateTypeRepository->search($criteria, $context)->first(); + + $technicalName = $mailTemplateType?->get('technicalName'); + + if (!\is_string($technicalName)) { + throw new \RuntimeException('technicalName could not be determined'); + } + + return $technicalName; + } } diff --git a/src/Subscriber/MailTemplateSubscriber.php b/src/Subscriber/MailTemplateSubscriber.php index 1f5a0c3..b676f39 100644 --- a/src/Subscriber/MailTemplateSubscriber.php +++ b/src/Subscriber/MailTemplateSubscriber.php @@ -4,7 +4,6 @@ namespace Frosh\TemplateMail\Subscriber; -use Frosh\TemplateMail\Services\MailFinderService; use Frosh\TemplateMail\Services\MailFinderServiceInterface; use Frosh\TemplateMail\Services\TemplateMailContext; use Shopware\Core\Content\MailTemplate\Aggregate\MailTemplateType\MailTemplateTypeCollection; @@ -67,16 +66,29 @@ public function onMailTemplatesLoaded(EntityLoadedEvent $event): void } $technicalName = $mailTemplateType->getTechnicalName(); - $html = $this->mailFinderService->findTemplateByTechnicalName(MailFinderService::TYPE_HTML, $technicalName, $businessEvent, true, $mailTemplateEntity->getId()); - $plain = $this->mailFinderService->findTemplateByTechnicalName(MailFinderService::TYPE_PLAIN, $technicalName, $businessEvent, true, $mailTemplateEntity->getId()); - $subject = $this->mailFinderService->findTemplateByTechnicalName(MailFinderService::TYPE_SUBJECT, $technicalName, $businessEvent, true, $mailTemplateEntity->getId()); + + $templateData = $this->mailFinderService->getTemplateDataByTechnicalName($technicalName, $businessEvent, $mailTemplateEntity->getId()); + if ($templateData->subject !== null) { + $mailTemplateEntity->setSubject($templateData->subject->content); + $mailTemplateEntity->addTranslated('subject', $templateData->subject->content); + } + + if ($templateData->html !== null) { + $mailTemplateEntity->setContentHtml($templateData->html->content); + $mailTemplateEntity->addTranslated('contentHtml', $templateData->html->content); + } + + if ($templateData->plain !== null) { + $mailTemplateEntity->setContentPlain($templateData->plain->content); + $mailTemplateEntity->addTranslated('contentPlain', $templateData->plain->content); + } $mailTemplateEntity->addExtension( 'froshTemplateMail', new ArrayStruct([ - 'html' => $html, - 'plain' => $plain, - 'subject' => $subject, + 'subject' => $templateData->subject?->filePath, + 'html' => $templateData->html?->filePath, + 'plain' => $templateData->plain?->filePath, 'technicalName' => $technicalName, ]), );