Skip to content

Commit 03e97e3

Browse files
authored
Merge pull request #4042 from craftcms/bugfix/send-email-with-current-site-set-to-order-site
[5.5] Fixed emails not rendering in the site the order was placed in
2 parents 19c9286 + 8c2721d commit 03e97e3

10 files changed

Lines changed: 142 additions & 3 deletions

File tree

CHANGELOG-WIP.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# WIP Release Notes for Craft Commerce
1+
# Release Notes for Craft Commerce 5.5 (WIP)
22

33
### Store Management
44
- It is now possible to suppress order emails when marking an order as complete in the control panel. ([#4144](https://github.com/craftcms/commerce/issues/4144))
@@ -23,6 +23,9 @@
2323
- Added `craft\commerce\events\InventoryMovementEvent`. ([#4063](https://github.com/craftcms/commerce/pull/4063))
2424
- Added `craft\commerce\events\UpdateInventoryLevelEvent`. ([#4063](https://github.com/craftcms/commerce/pull/4063))
2525
- Added `craft\commerce\helpers\Gql::getSchemaContainedProductTypes()`.
26+
- Added `craft\commerce\models\Email::$renderSiteId`.
27+
- Added `craft\commerce\models\Email::getRenderSite()`.
28+
- Added `craft\commerce\records\Email::$renderSiteId`.
2629
- Added `craft\commerce\records\Order::$dateFirstPaid`.
2730
- Added `craft\commerce\services\CatalogPricingRules::hasCatalogPricingRules()`.
2831
- Added `craft\commerce\services\Discounts::appendCouponCode()`. ([#4084](https://github.com/craftcms/commerce/pull/4084))
@@ -31,3 +34,4 @@
3134

3235
### System
3336
- Fixed a bug where purchasables could have a shipping category that was no longer available to their product type. ([#4018](https://github.com/craftcms/commerce/issues/4018))
37+
- Fixed a bug where order emails weren’t always getting rendered for the correct site.

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@
115115
- Improved store query performance. ([#4029](https://github.com/craftcms/commerce/issues/4029))
116116
- Fixed a bug where the purchasable cache was not cleared when stock was updated.
117117
- Fixed a PHP error that could occur when sending emails. ([#4017](https://github.com/craftcms/commerce/issues/4017))
118+
- Fixed a bug where order emails weren’t always getting rendered for the correct site.
118119
- Fixed a SQL error that could occur when upgrading to Commerce 5. ([#4044](https://github.com/craftcms/commerce/issues/4044))
119120
- Fixed a bug where duplicate order references could be generated. ([#4050](https://github.com/craftcms/commerce/issues/4050))
120121
- Fixed a bug where purchasables’ `shippingCategoryId` and `taxCategoryId` properties couldn’t be set via `setAttributes()`. ([#4046](https://github.com/craftcms/commerce/issues/4046))

src/controllers/EmailsController.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
use craft\commerce\records\Email as EmailRecord;
1818
use craft\helpers\App;
1919
use craft\helpers\ArrayHelper;
20+
use craft\models\Site;
2021
use yii\base\ErrorException;
2122
use yii\base\Exception;
2223
use yii\base\InvalidConfigException;
@@ -103,6 +104,11 @@ public function actionEdit(?string $storeHandle = null, int $id = null, Email $e
103104

104105
$variables['emailLanguageOptions'] = array_merge($emailLanguageOptions, LocaleHelper::getSiteAndOtherLanguages());
105106

107+
$variables['emailRenderSiteOptions'] = [
108+
null => Craft::t('commerce', 'The site the order was made in.'),
109+
['optgroup' => Craft::t('commerce', 'Sites')],
110+
] + collect(Craft::$app->getSites()->getAllSites())->mapWithKeys(fn(Site $site) => [$site->id => $site->name])->all();
111+
106112
$variables['readOnly'] = $this->isReadOnlyScreen();
107113

108114
return $this->renderTemplate('commerce/settings/emails/_edit', $variables);
@@ -136,6 +142,8 @@ public function actionSave(): ?Response
136142
$email = new Email();
137143
}
138144

145+
$renderSiteId = $this->request->getBodyParam('renderSiteId');
146+
139147
// Shared attributes
140148
$email->storeId = $storeId;
141149
$email->name = $this->request->getBodyParam('name');
@@ -151,6 +159,7 @@ public function actionSave(): ?Response
151159
$pdfId = $this->request->getBodyParam('pdfId');
152160
$email->pdfId = $pdfId ?: null;
153161
$email->language = $this->request->getBodyParam('language');
162+
$email->renderSiteId = $renderSiteId ? (int)$renderSiteId : null;
154163
$email->setSenderAddress($this->request->getBodyParam('senderAddress'));
155164
$email->setSenderName($this->request->getBodyParam('senderName'));
156165

src/migrations/Install.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,7 @@ public function createTables(): void
271271
'plainTextTemplatePath' => $this->string(),
272272
'pdfId' => $this->integer(),
273273
'language' => $this->string(),
274+
'renderSiteId' => $this->integer(),
274275
'dateCreated' => $this->dateTime()->notNull(),
275276
'dateUpdated' => $this->dateTime()->notNull(),
276277
'uid' => $this->uid(),
@@ -1216,6 +1217,7 @@ public function addForeignKeys(): void
12161217
$this->addForeignKey(null, Table::DONATIONS, ['id'], '{{%elements}}', ['id'], 'CASCADE');
12171218
$this->addForeignKey(null, Table::EMAILS, ['pdfId'], Table::PDFS, ['id'], 'SET NULL');
12181219
$this->addForeignKey(null, Table::EMAILS, ['storeId'], Table::STORES, ['id'], 'CASCADE', 'CASCADE');
1220+
$this->addForeignKey(null, Table::EMAILS, ['renderSiteId'], CraftTable::SITES, ['id'], 'SET NULL');
12191221
$this->addForeignKey(null, Table::EMAIL_DISCOUNTUSES, ['discountId'], Table::DISCOUNTS, ['id'], 'CASCADE', 'CASCADE');
12201222
$this->addForeignKey(null, Table::INVENTORYITEMS, 'purchasableId', Table::PURCHASABLES, 'id', 'CASCADE', null);
12211223
$this->addForeignKey(null, Table::INVENTORYLOCATIONS, 'addressId', CraftTable::ELEMENTS, 'id', 'CASCADE', null);
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
<?php
2+
3+
namespace craft\commerce\migrations;
4+
5+
use Craft;
6+
use craft\commerce\db\Table;
7+
use craft\db\Migration;
8+
use craft\db\Table as CraftTable;
9+
10+
/**
11+
* m250617_105249_add_email_render_site_id migration.
12+
*/
13+
class m250617_105249_add_email_render_site_id extends Migration
14+
{
15+
/**
16+
* @inheritdoc
17+
*/
18+
public function safeUp(): bool
19+
{
20+
if ($this->db->columnExists(Table::EMAILS, 'renderSiteId')) {
21+
return true;
22+
}
23+
24+
$this->addColumn(Table::EMAILS, 'renderSiteId', $this->integer()->after('language'));
25+
26+
// Get the primary site ID
27+
$primarySite = Craft::$app->getSites()->getPrimarySite();
28+
29+
// For all current emails set the `renderSiteId` to the primary site to keep the existing behavior
30+
$this->db->createCommand()->update(
31+
Table::EMAILS,
32+
['renderSiteId' => $primarySite->id],
33+
['renderSiteId' => null]
34+
)->execute();
35+
36+
// Update the project config
37+
$projectConfig = Craft::$app->getProjectConfig();
38+
39+
$emails = $projectConfig->get('commerce.emails') ?? [];
40+
$muteEvents = $projectConfig->muteEvents;
41+
$projectConfig->muteEvents = true;
42+
43+
foreach ($emails as $emailUid => $email) {
44+
$email['renderSite'] = $primarySite->uid;
45+
$projectConfig->set("commerce.emails.$emailUid", $email);
46+
}
47+
48+
$projectConfig->muteEvents = $muteEvents;
49+
50+
// Add foreign key
51+
$this->addForeignKey(null, Table::EMAILS, ['renderSiteId'], CraftTable::SITES, ['id'], 'SET NULL', 'CASCADE');
52+
53+
return true;
54+
}
55+
56+
/**
57+
* @inheritdoc
58+
*/
59+
public function safeDown(): bool
60+
{
61+
echo "m250617_105249_add_email_render_site_id cannot be reverted.\n";
62+
return false;
63+
}
64+
}

src/models/Email.php

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,17 @@
77

88
namespace craft\commerce\models;
99

10+
use Craft;
1011
use craft\commerce\base\HasStoreInterface;
1112
use craft\commerce\base\Model;
1213
use craft\commerce\base\StoreTrait;
1314
use craft\commerce\elements\Order;
1415
use craft\commerce\Plugin;
1516
use craft\commerce\records\Email as EmailRecord;
17+
use craft\errors\SiteNotFoundException;
1618
use craft\helpers\App;
1719
use craft\helpers\UrlHelper;
20+
use craft\models\Site;
1821
use yii\base\InvalidArgumentException;
1922
use yii\base\InvalidConfigException;
2023

@@ -86,6 +89,14 @@ class Email extends Model implements HasStoreInterface
8689
*/
8790
public string $language = EmailRecord::LOCALE_ORDER_LANGUAGE;
8891

92+
/**
93+
* The site the email should be rendered in. Set to `null` to use the site the order was placed in.
94+
*
95+
* @var int|null
96+
* @since 5.5.0
97+
*/
98+
public ?int $renderSiteId = null;
99+
89100
/**
90101
* @var string|null
91102
* @since 5.0.0
@@ -144,7 +155,7 @@ public function extraFields(): array
144155
}
145156

146157
/**
147-
* Determines the language this pdf, if
158+
* Determines the language this email is rendered in.
148159
*
149160
* @param Order|null $order
150161
*/
@@ -163,6 +174,25 @@ public function getRenderLanguage(Order $order = null): string
163174
return $language;
164175
}
165176

177+
/**
178+
* Determines the site this email is rendered in.
179+
*
180+
* @param Order|null $order
181+
* @return Site
182+
* @throws SiteNotFoundException
183+
* @since 5.5.0
184+
*/
185+
public function getRenderSite(Order $order = null): Site
186+
{
187+
$renderSiteId = $this->renderSiteId ?? $order?->orderSiteId;
188+
189+
if ($renderSiteId !== null) {
190+
return Craft::$app->getSites()->getSiteById($renderSiteId);
191+
}
192+
193+
return Craft::$app->getSites()->getPrimarySite();
194+
}
195+
166196
/**
167197
* @inheritdoc
168198
*/
@@ -187,6 +217,7 @@ protected function defineRules(): array
187217
'pdfId',
188218
'plainTextTemplatePath',
189219
'recipientType',
220+
'renderSiteId',
190221
'replyTo',
191222
'senderAddress',
192223
'senderName',
@@ -368,6 +399,7 @@ public function getConfig(): array
368399
'pdf' => $this->getPdf()?->uid,
369400
'plainTextTemplatePath' => $this->plainTextTemplatePath ?? null,
370401
'recipientType' => $this->recipientType,
402+
'renderSite' => $this->renderSiteId ? Craft::$app->getSites()->getSiteById($this->renderSiteId)?->uid ?? null : null,
371403
'replyTo' => $this->replyTo ?: null,
372404
'store' => $this->getStore()->uid,
373405
'subject' => $this->subject,

src/records/Email.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
* @property int $id
2222
* @property string $name
2323
* @property string $recipientType
24+
* @property int $renderSiteId
2425
* @property string $senderAddress
2526
* @property string $senderName
2627
* @property string $subject

src/services/Emails.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,7 @@ public function handleChangedEmail(ConfigEvent $event): void
341341
$emailRecord = $this->_getEmailRecord($emailUid);
342342
$isNewEmail = $emailRecord->getIsNewRecord();
343343
$store = Plugin::getInstance()->getStores()->getStoreByUid($data['store']);
344+
$renderSite = array_key_exists('renderSite', $data) && $data['renderSite'] !== null ? Craft::$app->getSites()->getSiteByUid($data['renderSite']) : null;
344345

345346
$emailRecord->storeId = $store->id;
346347
$emailRecord->name = $data['name'];
@@ -358,6 +359,7 @@ public function handleChangedEmail(ConfigEvent $event): void
358359
$emailRecord->uid = $emailUid;
359360
$emailRecord->pdfId = $pdfUid ? Db::idByUid(Table::PDFS, $pdfUid) : null;
360361
$emailRecord->language = $data['language'] ?? EmailRecord::LOCALE_ORDER_LANGUAGE;
362+
$emailRecord->renderSiteId = $renderSite?->id ?? null;
361363

362364
$emailRecord->save(false);
363365

@@ -477,6 +479,7 @@ public function sendEmail(Email $email, Order $order, ?OrderHistory $orderHistor
477479
$originalLanguage = Craft::$app->language;
478480
$originalFormattingLanguage = Craft::$app->formattingLocale;
479481
$emailLanguage = $email->getRenderLanguage($order);
482+
$emailSite = $email->getRenderSite($order);
480483

481484
Locale::switchAppLanguage($emailLanguage);
482485

@@ -783,6 +786,9 @@ public function sendEmail(Email $email, Order $order, ?OrderHistory $orderHistor
783786
}
784787
}
785788

789+
$originalSiteId = Craft::$app->getSites()->getCurrentSite()->id;
790+
Craft::$app->getSites()->setCurrentSite($emailSite);
791+
786792
// Render HTML body
787793
try {
788794
$body = $view->renderTemplate($templatePath, $renderVariables);
@@ -799,6 +805,7 @@ public function sendEmail(Email $email, Order $order, ?OrderHistory $orderHistor
799805
]);
800806
Craft::error($error, __METHOD__);
801807

808+
Craft::$app->getSites()->setCurrentSite($originalSiteId);
802809
Locale::switchAppLanguage($originalLanguage, $originalFormattingLanguage->id);
803810
$view->setTemplateMode($oldTemplateMode);
804811
$generalConfig->generateTransformsBeforePageLoad = $generateTransformsBeforePageLoad;
@@ -823,6 +830,7 @@ public function sendEmail(Email $email, Order $order, ?OrderHistory $orderHistor
823830
]);
824831
Craft::error($error, __METHOD__);
825832

833+
Craft::$app->getSites()->setCurrentSite($originalSiteId);
826834
Locale::switchAppLanguage($originalLanguage, $originalFormattingLanguage->id);
827835
$view->setTemplateMode($oldTemplateMode);
828836
$generalConfig->generateTransformsBeforePageLoad = $generateTransformsBeforePageLoad;
@@ -850,6 +858,7 @@ public function sendEmail(Email $email, Order $order, ?OrderHistory $orderHistor
850858

851859
Craft::info($notice, __METHOD__);
852860

861+
Craft::$app->getSites()->setCurrentSite($originalSiteId);
853862
Locale::switchAppLanguage($originalLanguage, $originalFormattingLanguage->id);
854863
$view->setTemplateMode($oldTemplateMode);
855864
$generalConfig->generateTransformsBeforePageLoad = $generateTransformsBeforePageLoad;
@@ -869,6 +878,7 @@ public function sendEmail(Email $email, Order $order, ?OrderHistory $orderHistor
869878

870879
Craft::error($error, __METHOD__);
871880

881+
Craft::$app->getSites()->setCurrentSite($originalSiteId);
872882
Locale::switchAppLanguage($originalLanguage, $originalFormattingLanguage->id);
873883
$view->setTemplateMode($oldTemplateMode);
874884
$generalConfig->generateTransformsBeforePageLoad = $generateTransformsBeforePageLoad;
@@ -888,6 +898,7 @@ public function sendEmail(Email $email, Order $order, ?OrderHistory $orderHistor
888898

889899
Craft::error($error, __METHOD__);
890900

901+
Craft::$app->getSites()->setCurrentSite($originalSiteId);
891902
Locale::switchAppLanguage($originalLanguage, $originalFormattingLanguage->id);
892903
$view->setTemplateMode($oldTemplateMode);
893904
$generalConfig->generateTransformsBeforePageLoad = $generateTransformsBeforePageLoad;
@@ -906,6 +917,7 @@ public function sendEmail(Email $email, Order $order, ?OrderHistory $orderHistor
906917
]));
907918
}
908919

920+
Craft::$app->getSites()->setCurrentSite($originalSiteId);
909921
Locale::switchAppLanguage($originalLanguage, $originalFormattingLanguage->id);
910922
$view->setTemplateMode($oldTemplateMode);
911923
$generalConfig->generateTransformsBeforePageLoad = $generateTransformsBeforePageLoad;
@@ -957,6 +969,7 @@ private function _createEmailQuery(): Query
957969
'emails.pdfId',
958970
'emails.plainTextTemplatePath',
959971
'emails.recipientType',
972+
'emails.renderSiteId',
960973
'emails.replyTo',
961974
'emails.senderAddress',
962975
'emails.senderName',

src/templates/settings/emails/_edit.twig

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@
182182

183183
{{ forms.selectField({
184184
label: 'Language'|t('commerce'),
185-
instructions: "The language to be used when email is rendered."|t,
185+
instructions: "The language to be used when email is rendered."|t('commerce'),
186186
id: 'language',
187187
name: 'language',
188188
options : emailLanguageOptions,
@@ -192,6 +192,17 @@
192192
disabled: readOnly,
193193
}) }}
194194

195+
{{ forms.selectField({
196+
label: 'Site'|t('app'),
197+
instructions: "The site to be used when email is rendered."|t('commerce'),
198+
id: 'renderSiteId',
199+
name: 'renderSiteId',
200+
options : emailRenderSiteOptions,
201+
value : email.renderSiteId,
202+
errors: email.getErrors('renderSiteId') ?? null,
203+
disabled: readOnly,
204+
}) }}
205+
195206
{{ forms.lightswitchField({
196207
label: "Enabled?"|t('commerce'),
197208
instructions: 'If disabled, this email will not send.'|t('commerce'),

src/translations/en/commerce.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1140,6 +1140,8 @@
11401140
'The purchasable is related by another element' => 'The purchasable is related by another element',
11411141
'The recipient of the email. Twig code can be used here.' => 'The recipient of the email. Twig code can be used here.',
11421142
'The reply to email address. Leave blank for normal reply to of email sender. Twig code can be used here.' => 'The reply to email address. Leave blank for normal reply to of email sender. Twig code can be used here.',
1143+
'The site the order was made in.' => 'The site the order was made in.',
1144+
'The site to be used when email is rendered.' => 'The site to be used when email is rendered.',
11431145
'The subject line of the email. Twig code can be used here.' => 'The subject line of the email. Twig code can be used here.',
11441146
'The template that the PDF should be generated from.' => 'The template that the PDF should be generated from.',
11451147
'The template to be used for HTML emails.' => 'The template to be used for HTML emails.',

0 commit comments

Comments
 (0)