From 663bdb0e46b35e2c5ca27943ef44ad0ced4b983c Mon Sep 17 00:00:00 2001 From: Chihiro Adachi <8196725+chihiro-adachi@users.noreply.github.com> Date: Mon, 15 Apr 2024 08:30:49 +0900 Subject: [PATCH 1/4] =?UTF-8?q?=E3=83=A1=E3=83=BC=E3=83=AB=E3=81=AE?= =?UTF-8?q?=E7=99=BB=E9=8C=B2=E3=83=BB=E7=B7=A8=E9=9B=86=E3=83=BB=E5=89=8A?= =?UTF-8?q?=E9=99=A4=E5=87=A6=E7=90=86=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Admin/Setting/Shop/MailController.php | 80 +++++++-- src/Eccube/Entity/MailTemplate.php | 29 ++++ src/Eccube/Form/Type/Admin/MailType.php | 64 ++++++- src/Eccube/Resource/locale/messages.en.yaml | 5 + src/Eccube/Resource/locale/messages.ja.yaml | 5 + .../template/admin/Setting/Shop/mail.twig | 156 ++++++++++++------ 6 files changed, 271 insertions(+), 68 deletions(-) diff --git a/src/Eccube/Controller/Admin/Setting/Shop/MailController.php b/src/Eccube/Controller/Admin/Setting/Shop/MailController.php index 4abe761aeee..ad455ce1ab7 100644 --- a/src/Eccube/Controller/Admin/Setting/Shop/MailController.php +++ b/src/Eccube/Controller/Admin/Setting/Shop/MailController.php @@ -55,6 +55,7 @@ public function __construct(MailTemplateRepository $mailTemplateRepository) */ public function index(Request $request, Environment $twig, CacheUtil $cacheUtil, MailTemplate $Mail = null) { + $Mail = $Mail ?? new MailTemplate(); $builder = $this->formFactory ->createBuilder(MailType::class, $Mail); @@ -68,17 +69,20 @@ public function index(Request $request, Environment $twig, CacheUtil $cacheUtil, $this->eventDispatcher->dispatch($event, EccubeEvents::ADMIN_SETTING_SHOP_MAIL_INDEX_INITIALIZE); $form = $builder->getForm(); - $form['template']->setData($Mail); - $htmlFileName = $Mail ? $this->getHtmlFileName($Mail->getFileName()) : null; // 更新時 - if (!is_null($Mail)) { + if (null !== $Mail->getId()) { + $form['template']->setData($Mail); + // テンプレートファイルの取得 $source = $twig->getLoader() ->getSourceContext($Mail->getFileName()) ->getCode(); $form->get('tpl_data')->setData($source); + + $htmlFileName = $this->getHtmlFileName($Mail->getFileName()); + if ($twig->getLoader()->exists($htmlFileName)) { $source = $twig->getLoader() ->getSourceContext($htmlFileName) @@ -91,14 +95,9 @@ public function index(Request $request, Environment $twig, CacheUtil $cacheUtil, if ('POST' === $request->getMethod()) { $form->handleRequest($request); - // 新規登録は現時点では未実装とする. - if (is_null($Mail)) { - $this->addError('admin.common.save_error', 'admin'); - - return $this->redirectToRoute('admin_setting_shop_mail'); - } - - if ($form->isValid()) { + if ($form->isSubmitted() && $form->isValid()) { + $Mail = $form->getData(); + $this->entityManager->persist($Mail); $this->entityManager->flush(); // ファイル生成・更新 @@ -112,9 +111,17 @@ public function index(Request $request, Environment $twig, CacheUtil $cacheUtil, // HTMLファイル用 $htmlMailData = $form->get('html_tpl_data')->getData(); + $htmlFileName = $this->getHtmlFileName($Mail->getFileName()); + if (!is_null($htmlMailData)) { $htmlMailData = StringUtil::convertLineFeed($htmlMailData); $fs->dumpFile($templatePath.'/'.$htmlFileName, $htmlMailData); + } else { + // 空登録の場合は削除 + $htmlFilePath = $templatePath.'/'.$htmlFileName; + if ($this->validateFilePath($htmlFilePath) && is_file($htmlFilePath) ) { + $fs->remove($htmlFilePath); + } } $event = new EventArgs( @@ -139,7 +146,8 @@ public function index(Request $request, Environment $twig, CacheUtil $cacheUtil, return [ 'form' => $form->createView(), - 'id' => is_null($Mail) ? null : $Mail->getId(), + 'id' => $Mail->getId(), + 'Mail' => $Mail, ]; } @@ -168,6 +176,40 @@ public function preview(Request $request) ]; } + /** + * @Route("/%eccube_admin_route%/setting/shop/mail/{id}/delete", requirements={"id" = "\d+"}, name="admin_setting_shop_mail_delete", methods={"DELETE"}) + */ + public function delete(Request $request, MailTemplate $Mail) + { + $this->isTokenValid(); + + if (!$Mail->isDeletable()) { + return $this->redirectToRoute('admin_setting_shop_mail'); + } + + log_info('メールテンプレート削除開始', [$Mail->getId()]); + + $this->entityManager->remove($Mail); + $this->entityManager->flush(); + + $fs = new Filesystem(); + $templatePath = $this->getParameter('eccube_theme_front_dir'); + $filePath = $templatePath.'/'.$Mail->getFileName(); + if ($this->validateFilePath($filePath) && is_file($filePath)) { + $fs->remove($filePath); + } + $htmlFilePath = $templatePath.'/'.$this->getHtmlFileName($Mail->getFileName()); + if ($this->validateFilePath($htmlFilePath) && is_file($htmlFilePath)) { + $fs->remove($htmlFilePath); + } + + $this->addSuccess('admin.common.delete_complete', 'admin'); + + log_info('メールテンプレート削除完了', [$Mail->getId()]); + + return $this->redirectToRoute('admin_setting_shop_mail'); + } + /** * HTML用テンプレート名を取得する * @@ -183,4 +225,18 @@ protected function getHtmlFileName($fileName) return $targetTemplate['dirname'].DIRECTORY_SEPARATOR.$targetTemplate['filename'].$suffix.'.'.$targetTemplate['extension']; } + + /** + * テンプレートディレクトリ配下のパスかどうかを検証する + * + * @param $path + * @return bool + */ + protected function validateFilePath($path) + { + $templatePath = realpath($this->getParameter('eccube_theme_front_dir')); + $path = realpath($path); + + return \str_starts_with($path, $templatePath); + } } diff --git a/src/Eccube/Entity/MailTemplate.php b/src/Eccube/Entity/MailTemplate.php index 03155117a95..1fc6eb3fd2c 100644 --- a/src/Eccube/Entity/MailTemplate.php +++ b/src/Eccube/Entity/MailTemplate.php @@ -89,6 +89,15 @@ public function __toString() */ private $Creator; + /** + * テンプレートの削除可否。 + * + * @var bool + * + * @ORM\Column(name="deletable", type="boolean", options={"default":false})) + */ + private bool $deletable = false; + /** * Get id. * @@ -242,5 +251,25 @@ public function getCreator() { return $this->Creator; } + + + /** + * @return bool + */ + public function isDeletable(): bool + { + return $this->deletable; + } + + /** + * @param bool $deletable + * @return $this + */ + public function setDeletable(bool $deletable): self + { + $this->deletable = $deletable; + + return $this; + } } } diff --git a/src/Eccube/Form/Type/Admin/MailType.php b/src/Eccube/Form/Type/Admin/MailType.php index 58afcd295b5..454ddce8f2a 100644 --- a/src/Eccube/Form/Type/Admin/MailType.php +++ b/src/Eccube/Form/Type/Admin/MailType.php @@ -13,16 +13,33 @@ namespace Eccube\Form\Type\Admin; +use Eccube\Common\EccubeConfig; +use Eccube\Entity\MailTemplate; use Eccube\Form\Type\Master\MailTemplateType; use Eccube\Form\Validator\TwigLint; +use Eccube\Repository\MailTemplateRepository; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\Extension\Core\Type\TextareaType; use Symfony\Component\Form\Extension\Core\Type\TextType; use Symfony\Component\Form\FormBuilderInterface; +use Symfony\Component\Form\FormError; +use Symfony\Component\Form\FormEvent; +use Symfony\Component\Form\FormEvents; +use Symfony\Component\OptionsResolver\OptionsResolver; use Symfony\Component\Validator\Constraints as Assert; class MailType extends AbstractType { + private MailTemplateRepository $mailTemplateRepository; + + private EccubeConfig $eccubeConfig; + + public function __construct(MailTemplateRepository $mailTemplateRepository, EccubeConfig $eccubeConfig) + { + $this->mailTemplateRepository = $mailTemplateRepository; + $this->eccubeConfig = $eccubeConfig; + } + /** * {@inheritdoc} */ @@ -30,16 +47,20 @@ public function buildForm(FormBuilderInterface $builder, array $options) { $builder ->add('template', MailTemplateType::class, [ - 'required' => true, + 'required' => false, 'mapped' => false, + ]) + ->add('name', TextType::class, [ 'constraints' => [ new Assert\NotBlank(), + new Assert\Length(['max' => $this->eccubeConfig['eccube_stext_len']]), ], ]) ->add('mail_subject', TextType::class, [ 'required' => true, 'constraints' => [ new Assert\NotBlank(), + new Assert\Length(['max' => $this->eccubeConfig['eccube_stext_len']]), ], ]) ->add('tpl_data', TextareaType::class, [ @@ -59,6 +80,47 @@ public function buildForm(FormBuilderInterface $builder, array $options) ], ]) ; + + $builder->addEventListener(FormEvents::POST_SET_DATA, function (FormEvent $event) { + $data = $event->getData(); + if (null === $data->getId()) { + $form = $event->getForm(); + $form->add('file_name', TextType::class, [ + 'constraints' => [ + new Assert\NotBlank(), + new Assert\Regex(['pattern' => '/^[0-9a-z_-]+$/']), + new Assert\Length(['max' => $this->eccubeConfig['eccube_stext_len']]), + ], + ]); + } + }); + + $builder->addEventListener(FormEvents::POST_SUBMIT, function (FormEvent $event) { + $data = $event->getData(); + if (null === $data->getId()) { + $filename = 'Mail/'.$data->getFileName().'.twig'; + $MailTemplate = $this->mailTemplateRepository->findBy(['file_name' => $filename]); + if ($MailTemplate) { + $form = $event->getForm(); + $form['file_name']->addError(new FormError(trans('admin.setting.shop.mail.file_exists'))); + } else { + $data->setFileName('Mail/'.$data->getFileName().'.twig'); + } + + $data->setDeletable(true); + } + }); + + } + + /** + * {@inheritdoc} + */ + public function configureOptions(OptionsResolver $resolver) + { + $resolver->setDefaults([ + 'data_class' => MailTemplate::class, + ]); } /** diff --git a/src/Eccube/Resource/locale/messages.en.yaml b/src/Eccube/Resource/locale/messages.en.yaml index ecd21c78965..fab1a0e5651 100644 --- a/src/Eccube/Resource/locale/messages.en.yaml +++ b/src/Eccube/Resource/locale/messages.en.yaml @@ -1212,11 +1212,16 @@ admin.setting.shop.tax.apply_date.available_error: The same date and time cannot admin.setting.shop.mail.mail_template_edit: Edit Templates admin.setting.shop.mail.mail_template: Template +admin.setting.shop.mail.mail_template_name: Template name +admin.setting.shop.mail.mail_file_name: File name admin.setting.shop.mail.mail_subject: Title admin.setting.shop.mail.mail_body: Body admin.setting.shop.mail.mail_text: Text admin.setting.shop.mail.mail_html: HTML admin.setting.shop.mail.preview: Preview +admin.setting.shop.mail.file_exists: This file name is already in use. +admin.setting.shop.mail.delete__confirm_title: Delete a mail template +admin.setting.shop.mail.delete__confirm_message: Are you sure to delete this mail template? #------------------------------------------------------------------------------------ # Settings : Store Settings : CSV Outputs diff --git a/src/Eccube/Resource/locale/messages.ja.yaml b/src/Eccube/Resource/locale/messages.ja.yaml index f504a9c1365..1be8039866d 100644 --- a/src/Eccube/Resource/locale/messages.ja.yaml +++ b/src/Eccube/Resource/locale/messages.ja.yaml @@ -1212,11 +1212,16 @@ admin.setting.shop.tax.apply_date.available_error: 同時刻の適用日時を admin.setting.shop.mail.mail_template_edit: テンプレート編集 admin.setting.shop.mail.mail_template: テンプレート +admin.setting.shop.mail.mail_template_name: テンプレート名 +admin.setting.shop.mail.mail_file_name: ファイル名 admin.setting.shop.mail.mail_subject: 件名 admin.setting.shop.mail.mail_body: 本文 admin.setting.shop.mail.mail_text: テキスト admin.setting.shop.mail.mail_html: HTML admin.setting.shop.mail.preview: プレビュー +admin.setting.shop.mail.file_exists: このファイル名はすでに使用されています。 +admin.setting.shop.mail.delete__confirm_title: メールテンプレートを削除します。 +admin.setting.shop.mail.delete__confirm_message: メールテンプレートを削除してよろしいですか? #------------------------------------------------------------------------------------ # 設定:店舗設定:CSV出力項目設定 diff --git a/src/Eccube/Resource/template/admin/Setting/Shop/mail.twig b/src/Eccube/Resource/template/admin/Setting/Shop/mail.twig index 8b7745a115c..1f8c84d27a6 100644 --- a/src/Eccube/Resource/template/admin/Setting/Shop/mail.twig +++ b/src/Eccube/Resource/template/admin/Setting/Shop/mail.twig @@ -56,18 +56,16 @@ file that was distributed with this source code. showInvisibles: true }); - {% if form.html_tpl_data.vars.value %} - var html_editor = ace.edit('html_editor'); - html_editor.session.setMode('ace/mode/twig'); - html_editor.setTheme('ace/theme/tomorrow'); - html_editor.setValue("{{ form.html_tpl_data.vars.value|escape('js') }}"); - html_editor.setOptions({ - enableBasicAutocompletion: true, - enableSnippets: true, - enableLiveAutocompletion: true, - showInvisibles: true - }); - {% endif %} + var html_editor = ace.edit('html_editor'); + html_editor.session.setMode('ace/mode/twig'); + html_editor.setTheme('ace/theme/tomorrow'); + html_editor.setValue("{{ form.html_tpl_data.vars.value|escape('js') }}"); + html_editor.setOptions({ + enableBasicAutocompletion: true, + enableSnippets: true, + enableLiveAutocompletion: true, + showInvisibles: true + }); $("#editor").resizable({ resize: function (event, ui) { @@ -83,9 +81,7 @@ file that was distributed with this source code. $('#form1').on('submit', function() { $('#mail_tpl_data').val(editor.getValue()); - {% if form.html_tpl_data.vars.value %} - $('#mail_html_tpl_data').val(html_editor.getValue()); - {% endif %} + $('#mail_html_tpl_data').val(html_editor.getValue()); }); // HTMLメールモーダル表示 @@ -139,9 +135,43 @@ file that was distributed with this source code. {{ 'admin.setting.shop.mail.mail_template'|trans }} + {% if Mail.isDeletable %} +