From 629a8270dd86e4ecfa6da7b8e4a23d07045cbd58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Werner=20Krau=C3=9F?= Date: Wed, 18 Oct 2023 17:46:07 +0200 Subject: [PATCH 1/5] emails should use configured themes fixes #793 --- src/Checkout/OrderEmailNotifier.php | 72 ++++++++++++++++++++++------- 1 file changed, 56 insertions(+), 16 deletions(-) diff --git a/src/Checkout/OrderEmailNotifier.php b/src/Checkout/OrderEmailNotifier.php index cb019617..bdef8d7a 100644 --- a/src/Checkout/OrderEmailNotifier.php +++ b/src/Checkout/OrderEmailNotifier.php @@ -11,7 +11,7 @@ use SilverStripe\Core\Config\Config; use SilverStripe\Core\Config\Configurable; use SilverStripe\Core\Injector\Injectable; -use SilverStripe\Core\Injector\Injector; +use SilverStripe\View\SSViewer; /** * Handles email notifications to customers and / or admins. @@ -33,6 +33,12 @@ class OrderEmailNotifier */ protected $debugMode = false; + /** + * list of current themes for switching when sending emails + * @var array|string[] + */ + private array $current_themes; + /** * Assign the order to a local variable * @@ -41,6 +47,7 @@ class OrderEmailNotifier public function __construct(Order $order) { $this->order = $order; + $this->current_themes = SSViewer::get_themes(); } /** @@ -89,24 +96,56 @@ protected function buildEmail($template, $subject) /** * Send a mail of the order to the client (and another to the admin). * - * @param string $template - the class name of the email you wish to send + * @param string $template - the template of the email you wish to send * @param string $subject - subject of the email * @param bool $copyToAdmin - true by default, whether it should send a copy to the admin * * @return bool|string */ - public function sendEmail($template, $subject, $copyToAdmin = true) + public function sendEmail(string $template, string $subject, bool $copyToAdmin = true) { + SSViewer::set_themes(Config::inst()->get(SSViewer::class, 'themes')); + $email = $this->buildEmail($template, $subject); if ($copyToAdmin) { $email->setBcc(Email::config()->admin_email); } if ($this->debugMode) { - return $this->debug($email); + $ret = $this->debug($email); } else { - return $email->send(); + $ret = $email->send(); } + + SSViewer::set_themes($this->current_themes); + return $ret; + } + + /** + * Send a mail to the admin). + * + * @param string $template - the template of the email you wish to send + * @param string $subject - subject of the email + * @param bool $copyToAdmin - true by default, whether it should send a copy to the admin + * + * @return bool|string + */ + public function sendAdminEmail(string $template, string $subject) + { + SSViewer::set_themes(Config::inst()->get(SSViewer::class, 'themes')); + + $email = $this->buildEmail($template, $subject) + ->setTo(Email::config()->admin_email); + + + if ($this->debugMode) { + $ret = $this->debug($email); + } else { + $ret = $email->send(); + } + + SSViewer::set_themes($this->current_themes); + return $ret; } /** @@ -143,14 +182,7 @@ public function sendAdminNotification() ['OrderNo' => $this->order->Reference] ); - $email = $this->buildEmail('SilverShop/Model/Order_AdminNotificationEmail', $subject) - ->setTo(Email::config()->admin_email); - - if ($this->debugMode) { - return $this->debug($email); - } else { - return $email->send(); - } + return $this->sendAdminEmail('SilverShop/Model/Order_AdminNotificationEmail', $subject); } /** @@ -178,6 +210,8 @@ public function sendReceipt() */ public function sendCancelNotification() { + SSViewer::set_themes(Config::inst()->get(SSViewer::class, 'themes')); + $email = Email::create() ->setSubject(_t( 'SilverShop\ShopEmail.CancelSubject', @@ -190,10 +224,13 @@ public function sendCancelNotification() ->setBody($this->order->renderWith(Order::class)); if ($this->debugMode) { - return $this->debug($email); + $ret = $this->debug($email); } else { - return $email->send(); + $ret = $email->send(); } + + SSViewer::set_themes($this->current_themes); + return $ret; } /** @@ -206,6 +243,7 @@ public function sendCancelNotification() */ public function sendStatusChange($title, $note = null) { + SSViewer::set_themes(Config::inst()->get(SSViewer::class, 'themes')); $latestLog = null; if (!$note) { @@ -255,7 +293,7 @@ public function sendStatusChange($title, $note = null) $latestLog->SentToCustomer = true; $latestLog->write(); } - + SSViewer::set_themes($this->current_themes); return $result; } @@ -269,9 +307,11 @@ public function sendStatusChange($title, $note = null) */ protected function debug(Email $email) { + SSViewer::set_themes(Config::inst()->get(SSViewer::class, 'themes')); $email->render(); $template = $email->getHTMLTemplate(); $headers = $email->getSwiftMessage()->getHeaders()->toString(); + SSViewer::set_themes($this->current_themes); return "

Email HTML template: $template

\n" . "
$headers
" . From 400dfb41cb155faff46da2ddbcdc7e09e26dc842 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Werner=20Krau=C3=9F?= Date: Wed, 18 Oct 2023 17:52:23 +0200 Subject: [PATCH 2/5] add hooks to extend customer and admin emails --- src/Checkout/OrderEmailNotifier.php | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/Checkout/OrderEmailNotifier.php b/src/Checkout/OrderEmailNotifier.php index bdef8d7a..b2998fd4 100644 --- a/src/Checkout/OrderEmailNotifier.php +++ b/src/Checkout/OrderEmailNotifier.php @@ -10,6 +10,7 @@ use SilverStripe\Control\Email\Email; use SilverStripe\Core\Config\Config; use SilverStripe\Core\Config\Configurable; +use SilverStripe\Core\Extensible; use SilverStripe\Core\Injector\Injectable; use SilverStripe\View\SSViewer; @@ -22,6 +23,7 @@ class OrderEmailNotifier { use Injectable; use Configurable; + use Extensible; /** * @var Order $order @@ -111,6 +113,9 @@ public function sendEmail(string $template, string $subject, bool $copyToAdmin = if ($copyToAdmin) { $email->setBcc(Email::config()->admin_email); } + + $this->extend('updateClientEmail', $email); + if ($this->debugMode) { $ret = $this->debug($email); } else { @@ -137,6 +142,7 @@ public function sendAdminEmail(string $template, string $subject) $email = $this->buildEmail($template, $subject) ->setTo(Email::config()->admin_email); + $this->extend('updateAdminEmail', $email); if ($this->debugMode) { $ret = $this->debug($email); @@ -223,6 +229,8 @@ public function sendCancelNotification() ->setTo(Email::config()->admin_email) ->setBody($this->order->renderWith(Order::class)); + $this->extend('updateCancelNotificationEmail', $email); + if ($this->debugMode) { $ret = $this->debug($email); } else { @@ -241,7 +249,7 @@ public function sendCancelNotification() * * @return bool|string */ - public function sendStatusChange($title, $note = null) + public function sendStatusChange($title = null, $note = null) { SSViewer::set_themes(Config::inst()->get(SSViewer::class, 'themes')); $latestLog = null; @@ -282,6 +290,8 @@ public function sendStatusChange($title, $note = null) ] ); + $this->extend('updateStatusChangeEmail', $email); + if ($this->debugMode) { $result = $this->debug($email); } else { From 29af1dec0a08882e11dd34a7c4bb0451e773e574 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Werner=20Krau=C3=9F?= Date: Wed, 18 Oct 2023 18:04:29 +0200 Subject: [PATCH 3/5] OrderEmailNotifier: add getOrder() to get the order from an extension when updating email data --- src/Checkout/OrderEmailNotifier.php | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/Checkout/OrderEmailNotifier.php b/src/Checkout/OrderEmailNotifier.php index b2998fd4..2f351ae0 100644 --- a/src/Checkout/OrderEmailNotifier.php +++ b/src/Checkout/OrderEmailNotifier.php @@ -62,6 +62,11 @@ public function setDebugMode($bool) return $this; } + public function getOrder(): Order + { + return $this->order; + } + /** * @param string $template * @param string $subject @@ -98,9 +103,9 @@ protected function buildEmail($template, $subject) /** * Send a mail of the order to the client (and another to the admin). * - * @param string $template - the template of the email you wish to send - * @param string $subject - subject of the email - * @param bool $copyToAdmin - true by default, whether it should send a copy to the admin + * @param string $template - the template of the email you wish to send + * @param string $subject - subject of the email + * @param bool $copyToAdmin - true by default, whether it should send a copy to the admin * * @return bool|string */ @@ -129,9 +134,9 @@ public function sendEmail(string $template, string $subject, bool $copyToAdmin = /** * Send a mail to the admin). * - * @param string $template - the template of the email you wish to send - * @param string $subject - subject of the email - * @param bool $copyToAdmin - true by default, whether it should send a copy to the admin + * @param string $template - the template of the email you wish to send + * @param string $subject - subject of the email + * @param bool $copyToAdmin - true by default, whether it should send a copy to the admin * * @return bool|string */ @@ -245,7 +250,7 @@ public function sendCancelNotification() * Send an email to the customer containing the latest note of {@link OrderStatusLog} and the current status. * * @param string $title Subject for email - * @param string $note Optional note-content (instead of using the OrderStatusLog) + * @param string $note Optional note-content (instead of using the OrderStatusLog) * * @return bool|string */ From 3e26643f9ad1af8cf7c49afe042b497bde8408a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Werner=20Krau=C3=9F?= Date: Wed, 18 Oct 2023 18:21:25 +0200 Subject: [PATCH 4/5] configurable email preview task use config variable to add your own emails; needs a "send" method in OrderEmailNotifier --- src/Tasks/ShopEmailPreviewTask.php | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/src/Tasks/ShopEmailPreviewTask.php b/src/Tasks/ShopEmailPreviewTask.php index 366fbbae..1929ad2c 100644 --- a/src/Tasks/ShopEmailPreviewTask.php +++ b/src/Tasks/ShopEmailPreviewTask.php @@ -6,6 +6,7 @@ use SilverShop\Model\Order; use SilverStripe\Control\Director; use SilverStripe\Control\HTTPRequest; +use SilverStripe\Core\Config\Config; use SilverStripe\Dev\BuildTask; /** @@ -26,10 +27,12 @@ class ShopEmailPreviewTask extends BuildTask protected $description = 'Previews shop emails'; - protected $previewableEmails = [ + private static $previewable_emails = [ 'Confirmation', 'Receipt', - 'AdminNotification' + 'AdminNotification', + 'CancelNotification', + 'StatusChange' ]; /** @@ -41,26 +44,28 @@ public function run($request) $params = $request->allParams(); $url = Director::absoluteURL("dev/{$params['Action']}/{$params['TaskName']}", true); $debug = true; - + if ($request->getVar('debug')) { $debug = $request->getVar('debug'); } - + + $previewableEmails = Config::inst()->get(self::class, 'previewable_emails') ?? []; + echo '

Choose Email

'; echo '
    '; - foreach ($this->previewableEmails as $key => $method) { + foreach ($previewableEmails as $key => $method) { echo '
  • ' . $method . '
  • '; } echo '

'; - if ($email && in_array($email, $this->previewableEmails)) { + if ($email && in_array($email, $previewableEmails)) { $order = Order::get()->first(); $notifier = OrderEmailNotifier::create($order); - + if ($debug) { $notifier->setDebugMode(true); } - + $method = "send$email"; echo $notifier->$method(); } From 7dca346d2b1e43482291e32698bb7b93d6cda3b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Werner=20Krau=C3=9F?= Date: Wed, 18 Oct 2023 18:43:41 +0200 Subject: [PATCH 5/5] Update documentation on handling emails --- docs/en/02_Customisation/Emails.md | 69 +++++++++++++++++++++++++++--- 1 file changed, 62 insertions(+), 7 deletions(-) diff --git a/docs/en/02_Customisation/Emails.md b/docs/en/02_Customisation/Emails.md index c16a9b37..34804a70 100644 --- a/docs/en/02_Customisation/Emails.md +++ b/docs/en/02_Customisation/Emails.md @@ -22,7 +22,11 @@ SilverShop\Extension\ShopConfigExtension: ## Modifying templates -Update email content by overriding the templates inside the `templates/email/` folder. Just create a corresponding folder/template in your mysite folder, such as `mysite/templates/email/Order_RecieptEmail`. +Update email content by overriding the templates inside the `templates/email/` folder. +Silvershop respects the frontend theme and tries to use those templates. +Just create a corresponding folder/template in your theme or app folder, such as `themes/mytheme/templates/email/Order_RecieptEmail.ss` or `app/templates/email/Order_RecieptEmail.ss`. + +Remember: `app/templates` overrules `themes/mytheme/templates`. ## Modifying subject lines @@ -35,17 +39,68 @@ en: ShopEmail: ConfirmationSubject: 'My Website Order #{OrderNo} confirmation' ReceiptSubject: 'My Website Order #{OrderNo} receipt' - CancelSubject: 'My Website Order #{OrderNo} cancelled by member' + CancelSubject: 'My Website Order #{OrderNo} cancelled by member' ``` ## Making your own Notifier -There may be times when you want to add custom logic around the e-mail. For instance if your purchase requires your user to enter e-mail addresses and each of those addresses receives a different confirmation e-mail. In this situation make a `MyCustomOrderEmailNotifier` that can optionally extend `OrderEmailNotifier` and add custom logic into it then declare it to replace the current notifier +There may be times when you want to add custom logic around the email. For instance if your purchase requires your user to enter email addresses and each of those addresses receives a different confirmation email. Or if you want to add custom content to the emails sent. + +In this situation you can use `OrderEmailNotifier`'s exension hooks to modify all emails, e.g.: + +```php +addData([ + 'EmailConfirmationMessage' => $confirmationmessage, + ]); + } + + public function updateAdminEmail(Email $email): void + { + if($replyTo = $this->getOwner()->getOrder()->getLatestEmail()){ + $email->setReplyTo($replyTo); + } + } +} +``` + +Now add this extension to `OrderEmailNotifier` in one of your confing files: ```yaml -# in mysite/config.yml -Injector: - OrderEmailNotifier: - class: MyCustomOrderEmailNotifier +SilverShop\Checkout\OrderEmailNotifier: + extensions: + depotemails: My\Namespace\Extensions\ShopEmails +``` + +You can also add your own `sendFooEmail()` methods to that extension. + +## Previewing and Testing Emails + +Silvershop has a task to preview the generated emails, `SilverShop\Tasks\ShopEmailPreviewTask`. + +In case you send customer specific emails (e.g. for sending download links), you can add those to the task's config: + +```yaml +SilverShop\Tasks\ShopEmailPreviewTask: + previewable_emails: + download: DownloadInformation ``` +Now you need to add a method `sendDownloadInformation` to your `ShopEmails` notification, similar to `OrderEmailNotifier`'s `sendConfimation()` or `sendReceipt()` methods. + +## See also + +* [Silverstripe CMS Documentation - Email](https://docs.silverstripe.org/en/5/developer_guides/email/)