Skip to content

Commit

Permalink
Merge pull request #799 from wernerkrauss/fix-793-794
Browse files Browse the repository at this point in the history
Improve email generation and testing

fixes #793
fixes #794
  • Loading branch information
wernerkrauss committed Oct 27, 2023
2 parents f0f7cbb + 7dca346 commit 6be2a2b
Show file tree
Hide file tree
Showing 3 changed files with 150 additions and 35 deletions.
69 changes: 62 additions & 7 deletions docs/en/02_Customisation/Emails.md
Expand Up @@ -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

Expand All @@ -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
<?php

namespace My\Namespace\Extensions;

use SilverStripe\Control\Email\Email;
use SilverStripe\Core\Extension;

class ShopEmails extends Extension
{
public function updateClientEmail(Email $email): void
{

$confirmationmessage = 'some text';

$email->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/)
95 changes: 75 additions & 20 deletions src/Checkout/OrderEmailNotifier.php
Expand Up @@ -10,8 +10,9 @@
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\Core\Injector\Injector;
use SilverStripe\View\SSViewer;

/**
* Handles email notifications to customers and / or admins.
Expand All @@ -22,6 +23,7 @@ class OrderEmailNotifier
{
use Injectable;
use Configurable;
use Extensible;

/**
* @var Order $order
Expand All @@ -33,6 +35,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
*
Expand All @@ -41,6 +49,7 @@ class OrderEmailNotifier
public function __construct(Order $order)
{
$this->order = $order;
$this->current_themes = SSViewer::get_themes();
}

/**
Expand All @@ -53,6 +62,11 @@ public function setDebugMode($bool)
return $this;
}

public function getOrder(): Order
{
return $this->order;
}

/**
* @param string $template
* @param string $subject
Expand Down Expand Up @@ -89,24 +103,60 @@ 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 $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
*/
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);
}

$this->extend('updateClientEmail', $email);

if ($this->debugMode) {
$ret = $this->debug($email);
} else {
$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);

$this->extend('updateAdminEmail', $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;
}

/**
Expand Down Expand Up @@ -143,14 +193,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);
}

/**
Expand Down Expand Up @@ -178,6 +221,8 @@ public function sendReceipt()
*/
public function sendCancelNotification()
{
SSViewer::set_themes(Config::inst()->get(SSViewer::class, 'themes'));

$email = Email::create()
->setSubject(_t(
'SilverShop\ShopEmail.CancelSubject',
Expand All @@ -189,23 +234,29 @@ public function sendCancelNotification()
->setTo(Email::config()->admin_email)
->setBody($this->order->renderWith(Order::class));

$this->extend('updateCancelNotificationEmail', $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 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
*/
public function sendStatusChange($title, $note = null)
public function sendStatusChange($title = null, $note = null)
{
SSViewer::set_themes(Config::inst()->get(SSViewer::class, 'themes'));
$latestLog = null;

if (!$note) {
Expand Down Expand Up @@ -244,6 +295,8 @@ public function sendStatusChange($title, $note = null)
]
);

$this->extend('updateStatusChangeEmail', $email);

if ($this->debugMode) {
$result = $this->debug($email);
} else {
Expand All @@ -255,7 +308,7 @@ public function sendStatusChange($title, $note = null)
$latestLog->SentToCustomer = true;
$latestLog->write();
}

SSViewer::set_themes($this->current_themes);
return $result;
}

Expand All @@ -269,9 +322,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 "<h2>Email HTML template: $template</h2>\n" .
"<pre>$headers</pre>" .
Expand Down
21 changes: 13 additions & 8 deletions src/Tasks/ShopEmailPreviewTask.php
Expand Up @@ -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;

/**
Expand All @@ -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'
];

/**
Expand All @@ -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 '<h2>Choose Email</h2>';
echo '<ul>';
foreach ($this->previewableEmails as $key => $method) {
foreach ($previewableEmails as $key => $method) {
echo '<li><a href="' . $url . '/' . $method . '">' . $method . '</a></li>';
}
echo '</ul><hr>';

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();
}
Expand Down

0 comments on commit 6be2a2b

Please sign in to comment.