* @copyright Since 2007 PrestaShop SA and Contributors * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) */ namespace PrestaShopBundle\Controller\Admin\Improve\Design; use Mail; use PrestaShop\PrestaShop\Adapter\MailTemplate\MailPreviewVariablesBuilder; use PrestaShop\PrestaShop\Core\Domain\MailTemplate\Command\GenerateThemeMailTemplatesCommand; use PrestaShop\PrestaShop\Core\Employee\ContextEmployeeProviderInterface; use PrestaShop\PrestaShop\Core\Exception\CoreException; use PrestaShop\PrestaShop\Core\Exception\FileNotFoundException; use PrestaShop\PrestaShop\Core\Exception\InvalidArgumentException; use PrestaShop\PrestaShop\Core\Form\FormHandlerInterface; use PrestaShop\PrestaShop\Core\Language\LanguageRepositoryInterface; use PrestaShop\PrestaShop\Core\MailTemplate\Layout\LayoutInterface; use PrestaShop\PrestaShop\Core\MailTemplate\MailTemplateInterface; use PrestaShop\PrestaShop\Core\MailTemplate\MailTemplateRendererInterface; use PrestaShop\PrestaShop\Core\MailTemplate\ThemeCatalogInterface; use PrestaShop\PrestaShop\Core\MailTemplate\ThemeInterface; use PrestaShop\PrestaShop\Core\MailTemplate\Transformation\MailVariablesTransformation; use PrestaShopBundle\Controller\Admin\FrameworkBundleAdminController; use PrestaShopBundle\Form\Admin\Improve\Design\MailTheme\GenerateMailsType; use PrestaShopBundle\Form\Admin\Improve\Design\MailTheme\TranslateMailsBodyType; use PrestaShopBundle\Security\Annotation\AdminSecurity; use PrestaShopBundle\Service\TranslationService; use Symfony\Component\Form\Form; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; /** * Class MailThemeController manages mail theme generation, you can define the shop * mail theme, and regenerate mail in a specific language. * * Accessible via "Design > Mail Theme" */ class MailThemeController extends FrameworkBundleAdminController { /** * Show mail theme settings and generation page. * * @AdminSecurity("is_granted('read', request.get('_legacy_controller'))") * * @param Request $request * * @return Response */ public function indexAction(Request $request) { $legacyController = $request->attributes->get('_legacy_controller'); $generateThemeMailsForm = $this->createForm(GenerateMailsType::class); $translateMailsBodyForm = $this->createForm(TranslateMailsBodyType::class); /** @var ThemeCatalogInterface $themeCatalog */ $themeCatalog = $this->get('prestashop.core.mail_template.theme_catalog'); $mailThemes = $themeCatalog->listThemes(); return $this->render('@PrestaShop/Admin/Improve/Design/MailTheme/index.html.twig', [ 'layoutHeaderToolbarBtn' => [], 'layoutTitle' => $this->trans('Email Theme', 'Admin.Navigation.Menu'), 'enableSidebar' => true, 'help_link' => $this->generateSidebarLink($legacyController), 'mailThemeConfigurationForm' => $this->getMailThemeFormHandler()->getForm()->createView(), 'generateMailsForm' => $generateThemeMailsForm->createView(), 'translateMailsBodyForm' => $translateMailsBodyForm->createView(), 'mailThemes' => $mailThemes, ]); } /** * Manage generation form post and generate mails. * * @AdminSecurity("is_granted('create', request.get('_legacy_controller'))") * * @param Request $request * * @return Response */ public function generateMailsAction(Request $request) { $generateThemeMailsForm = $this->createForm(GenerateMailsType::class); $generateThemeMailsForm->handleRequest($request); if ($generateThemeMailsForm->isSubmitted()) { if (!$generateThemeMailsForm->isValid()) { $this->flashErrors($this->getFormErrorsForJS($generateThemeMailsForm)); return $this->redirectToRoute('admin_mail_theme_index'); } $data = $generateThemeMailsForm->getData(); try { $coreMailsFolder = ''; $modulesMailFolder = ''; //Overwrite theme folder if selected if (!empty($data['theme'])) { if (is_dir($data['theme'] . '/mails')) { $coreMailsFolder = $data['theme'] . '/mails'; } if (is_dir($data['theme'] . '/modules')) { $modulesMailFolder = $data['theme'] . '/modules'; } } $generateCommand = new GenerateThemeMailTemplatesCommand( $data['mailTheme'], $data['language'], $data['overwrite'], $coreMailsFolder, $modulesMailFolder ); $commandBus = $this->getCommandBus(); $commandBus->handle($generateCommand); if ($data['overwrite']) { $this->addFlash( 'success', $this->trans( 'Successfully overwrote email templates for theme %s with locale %s', 'Admin.Notifications.Success', [ $data['mailTheme'], $data['language'], ] ) ); } else { $this->addFlash( 'success', $this->trans( 'Successfully generated email templates for theme %s with locale %s', 'Admin.Notifications.Success', [ $data['mailTheme'], $data['language'], ] ) ); } } catch (CoreException $e) { $this->flashErrors([ $this->trans( sprintf( 'Cannot generate email templates for theme %s with locale %s', $data['mailTheme'], $data['language'] ), 'Admin.Notifications.Error' ), $e->getMessage(), ]); } } return $this->redirectToRoute('admin_mail_theme_index'); } /** * Save mail theme configuration * * @AdminSecurity("is_granted('update', request.get('_legacy_controller'))") * * @param Request $request * * @return Response * * @throws \Exception */ public function saveConfigurationAction(Request $request) { /** @var FormHandlerInterface $formHandler */ $formHandler = $this->getMailThemeFormHandler(); /** @var Form $form */ $form = $formHandler->getForm()->handleRequest($request); if ($form->isSubmitted()) { if (!$form->isValid()) { $this->flashErrors($this->getFormErrorsForJS($form)); return $this->redirectToRoute('admin_mail_theme_index'); } $errors = $formHandler->save($form->getData()); if (empty($errors)) { $this->addFlash( 'success', $this->trans( 'Email theme configuration saved successfully', 'Admin.Notifications.Success' ) ); } else { $this->flashErrors($errors); } } return $this->redirectToRoute('admin_mail_theme_index'); } /** * Preview the list of layouts for a defined theme * * @AdminSecurity("is_granted('read', request.get('_legacy_controller'))") * * @param Request $request * @param string $theme * * @return Response * * @throws InvalidArgumentException */ public function previewThemeAction(Request $request, $theme) { $legacyController = $request->attributes->get('_legacy_controller'); /** @var ThemeCatalogInterface $themeCatalog */ $themeCatalog = $this->get('prestashop.core.mail_template.theme_catalog'); /** @var ThemeInterface $mailTheme */ $mailTheme = $themeCatalog->getByName($theme); return $this->render('@PrestaShop/Admin/Improve/Design/MailTheme/preview.html.twig', [ 'layoutHeaderToolbarBtn' => [], 'layoutTitle' => $this->trans('Preview Theme %s', 'Admin.Design.Feature', [$mailTheme->getName()]), 'enableSidebar' => true, 'help_link' => $this->generateSidebarLink($legacyController), 'mailTheme' => $mailTheme, ]); } /** * This action allows to send a test mail of a specific email template, however the Mail * class used to send emails is not modular enough to allow sending templates on the fly. * This would require either: * - a little modification of the Mail class to add an easy way to send a template content (rather than its name) * - a full refacto of the Mail class which wouldn't be coupled to static files any more * * These modifications will be performed in a future release so for now we can only send test emails * with the current email theme using generated static files. * * @AdminSecurity("is_granted('read', request.get('_legacy_controller'))") * * @param string $theme * @param string $layout * @param string $locale * @param string $module * * @return Response * * @throws InvalidArgumentException */ public function sendTestMailAction($theme, $layout, $locale, $module = '') { if ($this->getConfiguration()->get('PS_MAIL_THEME') !== $theme) { $this->addFlash( 'error', $this->trans( 'Cannot send test email for theme %theme% because it is not your current theme', 'Admin.Notifications.Error', [ '%theme%' => $theme, ] ) ); return $this->redirectToRoute('admin_mail_theme_preview', ['theme' => $theme]); } /** @var ContextEmployeeProviderInterface $employeeProvider */ $employeeProvider = $this->get('prestashop.adapter.data_provider.employee'); $employeeData = $employeeProvider->getData(); /** @var LanguageRepositoryInterface $languageRepository */ $languageRepository = $this->get('prestashop.core.admin.lang.repository'); $language = $languageRepository->getOneByLocaleOrIsoCode($locale); if (null === $language) { throw new InvalidArgumentException(sprintf('Cannot find Language with locale or isoCode %s', $locale)); } if (empty($module)) { $templatePath = _PS_MAIL_DIR_; } else { $templatePath = _PS_MODULE_DIR_ . $module . '/mails/'; } /** @var MailPreviewVariablesBuilder $variablesBuilder */ $variablesBuilder = $this->get('prestashop.adapter.mail_template.preview_variables_builder'); $mailLayout = $this->getMailLayout($theme, $layout, $module); $mailVariables = $variablesBuilder->buildTemplateVariables($mailLayout); $mailSent = Mail::send( $language->getId(), $layout, $this->trans('Test email %template%', 'Admin.Design.Feature', ['%template%' => $layout]), $mailVariables, $employeeData['email'], $employeeData['firstname'] . ' ' . $employeeData['lastname'], $employeeData['email'], $employeeData['firstname'] . ' ' . $employeeData['lastname'], null, null, $templatePath ); if ($mailSent) { $this->addFlash( 'success', $this->trans( 'Test email for layout %layout% was successfully sent to %email%', 'Admin.Notifications.Success', [ '%layout%' => $layout, '%email%' => $employeeData['email'], ] ) ); } else { $this->addFlash( 'error', $this->trans( 'Cannot send test email for layout %layout%', 'Admin.Notifications.Error', [ '%layout%' => $layout, ] ) ); } return $this->redirectToRoute('admin_mail_theme_preview', ['theme' => $theme]); } /** * @AdminSecurity( * "is_granted('update', request.get('_legacy_controller'))", * message="You do not have permission to update this." * ) * * @param Request $request * * @return \Symfony\Component\HttpFoundation\RedirectResponse */ public function translateBodyAction(Request $request) { $translateMailsBodyForm = $this->createForm(TranslateMailsBodyType::class); $translateMailsBodyForm->handleRequest($request); if (!$translateMailsBodyForm->isSubmitted() || !$translateMailsBodyForm->isValid()) { $this->addFlash( 'error', $this->trans( 'Cannot translate emails body content', 'Admin.Notifications.Error' ) ); return $this->redirectToRoute('admin_mail_theme_index'); } $translateData = $translateMailsBodyForm->getData(); $language = $translateData['language']; /** @var TranslationService $translationService */ $translationService = $this->get('prestashop.service.translation'); $locale = $translationService->langToLocale($language); return $this->redirectToRoute('admin_international_translation_overview', [ 'lang' => $language, 'locale' => $locale, 'type' => 'mails_body', ]); } /** * Preview a mail layout from a defined theme * * @AdminSecurity("is_granted('read', request.get('_legacy_controller'))") * * @param string $theme * @param string $layout * @param string $type * @param string $locale * @param string $module * * @return Response * * @throws FileNotFoundException * @throws InvalidArgumentException */ public function previewLayoutAction($theme, $layout, $type, $locale, $module = '') { $renderedLayout = $this->renderLayout($theme, $layout, $type, $locale, $module); return new Response($renderedLayout); } /** * Display the raw source of a theme layout (mainly useful for developers/integrators) * * @AdminSecurity("is_granted('read', request.get('_legacy_controller'))") * * @param string $theme * @param string $layout * @param string $type * @param string $locale * @param string $module * * @return Response * * @throws FileNotFoundException * @throws InvalidArgumentException */ public function rawLayoutAction($theme, $layout, $type, $locale, $module = '') { $renderedLayout = $this->renderLayout($theme, $layout, $type, $locale, $module); $response = new Response($renderedLayout, Response::HTTP_OK, [ 'Content-Type' => 'text/plain', ]); return $response; } /** * Dynamically display an email template, this is usually used by the MailGenerator but this action * allows to display preview before generating the file (handy when you are developing an email theme) * * @param string $themeName * @param string $layoutName * @param string $type * @param string $locale * @param string $module * * @return string * * @throws FileNotFoundException * @throws InvalidArgumentException */ private function renderLayout($themeName, $layoutName, $type, $locale = '', $module = '') { $layout = $this->getMailLayout($themeName, $layoutName, $module); /** @var LanguageRepositoryInterface $languageRepository */ $languageRepository = $this->get('prestashop.core.admin.lang.repository'); if (empty($locale)) { $locale = $this->getContext()->language->locale; } $language = $languageRepository->getOneByLocaleOrIsoCode($locale); if (null === $language) { throw new InvalidArgumentException(sprintf('Cannot find Language with locale or isoCode %s', $locale)); } /** @var MailPreviewVariablesBuilder $variablesBuilder */ $variablesBuilder = $this->get('prestashop.adapter.mail_template.preview_variables_builder'); $mailLayoutVariables = $variablesBuilder->buildTemplateVariables($layout); /** @var MailTemplateRendererInterface $renderer */ $renderer = $this->get('prestashop.core.mail_template.mail_template_renderer'); //Special case for preview, we fill the mail variables $renderer->addTransformation(new MailVariablesTransformation(MailTemplateInterface::HTML_TYPE, $mailLayoutVariables)); $renderer->addTransformation(new MailVariablesTransformation(MailTemplateInterface::TXT_TYPE, $mailLayoutVariables)); switch ($type) { case MailTemplateInterface::HTML_TYPE: $renderedLayout = $renderer->renderHtml($layout, $language); break; case MailTemplateInterface::TXT_TYPE: $renderedLayout = $renderer->renderTxt($layout, $language); break; default: throw new NotFoundHttpException(sprintf('Requested type %s is not managed, please use one of these: %s', $type, implode(',', [MailTemplateInterface::HTML_TYPE, MailTemplateInterface::TXT_TYPE]))); } return $renderedLayout; } /** * @param string $themeName * @param string $layoutName * @param string $module * * @return LayoutInterface * * @throws FileNotFoundException * @throws InvalidArgumentException */ private function getMailLayout($themeName, $layoutName, $module) { /** @var ThemeCatalogInterface $themeCatalog */ $themeCatalog = $this->get('prestashop.core.mail_template.theme_catalog'); /** @var ThemeInterface $theme */ $theme = $themeCatalog->getByName($themeName); /** @var LayoutInterface $layout */ $layout = null; /* @var LayoutInterface $layoutInterface */ foreach ($theme->getLayouts() as $layoutInterface) { if ($layoutInterface->getName() == $layoutName && $layoutInterface->getModuleName() == $module ) { $layout = $layoutInterface; break; } } if (null === $layout) { throw new FileNotFoundException(sprintf('Cannot find layout %s%s in theme %s', empty($module) ? '' : $module . ':', $layoutName, $themeName)); } return $layout; } /** * @return FormHandlerInterface */ private function getMailThemeFormHandler(): FormHandlerInterface { return $this->get('prestashop.admin.mail_theme.form_handler'); } }