Skip to content

Commit ef7fed1

Browse files
committed
Merge branch '5.6' into feature/inventory-columns
2 parents c6ced90 + 0880b4c commit ef7fed1

47 files changed

Lines changed: 176 additions & 370 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CHANGELOG.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,22 @@
11
# Release Notes for Craft Commerce
22

3+
## Unreleased
4+
5+
- Fixed a bug where the order’s table was showing the incorrect column heading on the Edit User page.
6+
- Fixed a bug where it wasn’t possible to change a variant’s shipping category.
7+
- Fixed an error that occurred when adjusting inventory levels with an adjustment of zero. ([#4212](https://github.com/craftcms/commerce/issues/4212))
8+
- Fixed [high-severity](https://github.com/craftcms/cms/security/policy#severity--remediation) SQL injection vulnerabilities in the control panel. (GHSA-j3x5-mghf-xvfw, GHSA-pmgj-gmm4-jh6j)
9+
- Fixed [low-severity](https://github.com/craftcms/cms/security/policy#severity--remediation) XSS vulnerabilities in the control panel. (GHSA-mqxf-2998-c6cp, GHSA-wj89-2385-gpx3, GHSA-mj32-r678-7mvp)
10+
- Fixed a [high-severity](https://github.com/craftcms/cms/security/policy#severity--remediation) XSS vulnerability in the control panel. (GHSA-cfpv-rmpf-f624)
11+
12+
## 5.5.2 - 2025-12-31
13+
14+
- Improved transaction refund amount validation.
15+
- Fixed a bug where settings were being saved to the project config incorrectly. ([#4006](https://github.com/craftcms/commerce/issues/4006))
16+
- Fixed a PHP error that could occur when saving a shipping rule. ([#4134](https://github.com/craftcms/commerce/issues/4134))
17+
- Fixed a bug where the “New Customers” widget was counting all customers with orders in the date range, rather than only customers whose first order was in the date range.
18+
- Fixed XSS vulnerabilities. (GHSA-w8gw-qm8p-j9j3, GHSA-h9r9-2pxg-cx9m, GHSA-g92v-wpv7-6w22, GHSA-p6w8-q63m-72c8, GHSA-wqc5-485v-3hqh, GHSA-v585-mf6r-rqrc, GHSA-frj9-9rwc-pw9j, GHSA-8478-rmjg-mjj5, GHSA-2h2m-v2mg-656c, GHSA-wq2m-r96q-crrf)
19+
320
## 5.5.1 - 2025-12-04
421

522
- Added `craft\commerce\models\CatalogPricingRule::afterPreparePurchasableQuery()`.

example-templates/dist/shop/customer/order.twig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
</div>
3333
<div class="lg:w-1/2 lg:text-right">
3434
{# Display a link to download a PDF if the order has a PDF URL and a PDF has been configured #}
35-
{% if order.pdfUrl and craft.commerce.pdfs.hasEnabledPdf %}
35+
{% if craft.commerce.pdfs.hasEnabledPdf %}
3636
<a href="{{ order.getPdfUrl('receipt') }}" download target="_blank" class="text-blue-500 hover:text-blue-600">
3737
{{ 'Download PDF'|t }}
3838
</a>

example-templates/src/shop/customer/order.twig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
</div>
3333
<div class="lg:w-1/2 lg:text-right">
3434
{# Display a link to download a PDF if the order has a PDF URL and a PDF has been configured #}
35-
{% if order.pdfUrl and craft.commerce.pdfs.hasEnabledPdf %}
35+
{% if craft.commerce.pdfs.hasEnabledPdf %}
3636
<a href="{{ order.getPdfUrl('receipt') }}" download target="_blank" class="[[classes.a]]">
3737
{{ 'Download PDF'|t }}
3838
</a>

src/controllers/GatewaysController.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,11 +49,11 @@ public function actionIndex(): Response
4949
$missing = $gateway instanceof MissingGateway;
5050
$gateway = [
5151
'id' => $gateway->id,
52-
'title' => Craft::t('site', $gateway->name),
52+
'title' => Html::encode(Craft::t('site', $gateway->name)),
5353
'handle' => Html::encode($gateway->handle),
5454
'type' => [
5555
'missing' => $missing,
56-
'name' => $missing ? $gateway->expectedType : $gateway->displayName(),
56+
'name' => Html::encode($missing ? $gateway->expectedType : $gateway->displayName()),
5757
],
5858
'hasTransactions' => in_array($gateway->id, $gatewayIdsWithTransactions),
5959
];

src/controllers/InventoryController.php

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,24 @@ public function actionInventoryLevelsTableData(): Response
254254
$field = $sort[0]['sortField'];
255255
$direction = $sort[0]['direction'];
256256

257+
// Validate the sorting inputs
258+
if (!in_array($direction, ['asc', 'desc']) ||
259+
!in_array($field, [
260+
'item',
261+
'sku',
262+
'reservedTotal',
263+
'damagedTotal',
264+
'safetyTotal',
265+
'qualityControlTotal',
266+
'committedTotal',
267+
'availableTotal',
268+
'onHandTotal',
269+
'incomingTotal',
270+
])) {
271+
$field = null;
272+
$direction = null;
273+
}
274+
257275
if ($field && $direction) {
258276
if ($field == 'sku') {
259277
$field = 'purchasables.sku';
@@ -281,14 +299,17 @@ public function actionInventoryLevelsTableData(): Response
281299
$purchasable = \Craft::$app->getElements()->getElementById($inventoryLevel['purchasableId'], siteId: Cp::requestedSite()->id);
282300
$inventoryItemDomId = sprintf("edit-$id-link-%s", mt_rand());
283301
if ($purchasable) {
284-
$inventoryLevel['purchasable'] = Cp::chipHtml($purchasable, ['labelHtml' => $purchasable->getDescription(), 'showActionMenu' => !$purchasable->getIsDraft() && $purchasable->canSave($currentUser)]);
302+
// When providing the `labelHtml` option we need to encode it ourselves
303+
$inventoryLevel['purchasable'] = Cp::chipHtml($purchasable, ['labelHtml' => Html::encode($purchasable->getDescription()), 'showActionMenu' => !$purchasable->getIsDraft() && $purchasable->canSave($currentUser)]);
285304
} else {
286-
$inventoryLevel['purchasable'] = $inventoryLevel['description'];
305+
$inventoryLevel['purchasable'] = Html::encode($inventoryLevel['description']);
287306
}
288307
if (PurchasableHelper::isTempSku($inventoryLevel['sku'])) {
289308
$inventoryLevel['sku'] = '';
290309
}
291-
$inventoryLevel['sku'] = Html::tag('span', Html::a($inventoryLevel['sku'], "#", ['id' => "$inventoryItemDomId", 'class' => 'code']));
310+
311+
// Ensure encoded SKU
312+
$inventoryLevel['sku'] = Html::tag('span', Html::a(Html::encode($inventoryLevel['sku']), "#", ['id' => "$inventoryItemDomId", 'class' => 'code']));
292313
$inventoryLevel['id'] = $id;
293314

294315
$view->registerJsWithVars(fn($id, $params, $inventoryLevelsManagerContainerId) => <<<JS
@@ -508,7 +529,7 @@ public function actionUpdateLevels(): Response
508529

509530
// We don't add zero amounts as transactions movements
510531
if ($updateAction === InventoryUpdateQuantityType::ADJUST && $quantity == 0) {
511-
return $this->asSuccess(Craft::t('commerce', 'No inventory changes made.'));
532+
return $this->asFailure(Craft::t('commerce', 'No inventory changes made.'));
512533
}
513534

514535
$errors = [];

src/controllers/InventoryLocationsController.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -331,7 +331,7 @@ public function actionInventoryLocationsTableData(): Response
331331
'id' => $inventoryLocation->id,
332332
'title' => $inventoryLocation->getUiLabel(),
333333
'handle' => $inventoryLocation->handle,
334-
'address' => $inventoryLocation->getAddressLine(),
334+
'address' => Html::encode($inventoryLocation->getAddressLine()),
335335
'url' => $inventoryLocation->getCpEditUrl(),
336336
'delete' => $inventoryLocations->count() > 1 ? $deleteButton : '',
337337
];

src/controllers/OrdersController.php

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -516,8 +516,26 @@ public function actionUserOrdersTable(): Response
516516
$orderQuery->search($search);
517517
}
518518

519+
$orderQuery->orderBy('dateOrdered DESC');
519520
if ($sort) {
520-
[$field, $direction] = explode('|', $sort);
521+
if (is_array($sort)) {
522+
$field = $sort[0]['sortField'];
523+
$direction = $sort[0]['direction'];
524+
} else {
525+
[$field, $direction] = explode('|', $sort);
526+
}
527+
528+
// Validate sorting
529+
if (!in_array($direction, ['asc', 'desc']) ||
530+
!in_array($field, [
531+
'reference',
532+
'dateOrdered',
533+
'totalPrice',
534+
])
535+
) {
536+
$field = null;
537+
$direction = null;
538+
}
521539

522540
if ($field && $direction) {
523541
$orderQuery->orderBy($field . ' ' . $direction);
@@ -528,7 +546,6 @@ public function actionUserOrdersTable(): Response
528546

529547
$orderQuery->offset($offset);
530548
$orderQuery->limit($limit);
531-
$orderQuery->orderBy('dateOrdered DESC');
532549
$orders = $orderQuery->all();
533550

534551
$rows = [];
@@ -733,6 +750,15 @@ public function actionPurchasablesTable(): Response
733750
// Apply sorting if required
734751
if ($sort && strpos($sort, '|')) {
735752
[$column, $direction] = explode('|', $sort);
753+
754+
if (!in_array($column, [
755+
'description',
756+
'sku',
757+
'price',
758+
])) {
759+
$column = null;
760+
}
761+
736762
if ($column && in_array($direction, ['asc', 'desc'], true)) {
737763
$sqlQuery->orderBy([$column => $direction == 'asc' ? SORT_ASC : SORT_DESC]);
738764
}
@@ -1238,11 +1264,11 @@ public function actionTransactionRefund(): Response
12381264
}
12391265
}
12401266

1241-
if (!$amount) {
1267+
if (!$amount || $amount <= 0) {
12421268
$amount = $transaction->getRefundableAmount();
12431269
}
12441270

1245-
if ($amount > $transaction->getRefundableAmount()) {
1271+
if ($amount <= 0 || $amount > $transaction->getRefundableAmount()) {
12461272
$error = Craft::t('commerce', 'Can not refund amount greater than the remaining amount');
12471273
if ($this->request->getAcceptsJson()) {
12481274
return $this->asFailure($error);

src/controllers/SettingsController.php

Lines changed: 4 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -46,26 +46,13 @@ public function actionEdit(): Response
4646
public function actionSaveSettings(): ?Response
4747
{
4848
$this->requirePostRequest();
49-
50-
$params = $this->request->getBodyParams();
51-
$data = $params['settings'];
52-
53-
$settings = Plugin::getInstance()->getSettings();
54-
$settings->weightUnits = $data['weightUnits'] ?? key($settings->getWeightUnitsOptions());
55-
$settings->dimensionUnits = $data['dimensionUnits'] ?? key($settings->getDimensionUnits());
56-
$settings->updateBillingDetailsUrl = $data['updateBillingDetailsUrl'] ?? $settings->updateBillingDetailsUrl;
57-
$settings->defaultView = $data['defaultView'] ?? $settings->defaultView;
58-
59-
if (!$settings->validate()) {
60-
$this->setFailFlash(Craft::t('commerce', 'Couldn’t save settings.'));
61-
return $this->renderTemplate('commerce/settings/general/index', compact('settings'));
62-
}
63-
64-
$pluginSettingsSaved = Craft::$app->getPlugins()->savePluginSettings(Plugin::getInstance(), $settings->toArray());
49+
$plugin = Plugin::getInstance();
50+
$settings = $this->request->getBodyParam('settings');
51+
$pluginSettingsSaved = Craft::$app->getPlugins()->savePluginSettings($plugin, $settings);
6552

6653
if (!$pluginSettingsSaved) {
6754
$this->setFailFlash(Craft::t('commerce', 'Couldn’t save settings.'));
68-
return $this->renderTemplate('commerce/settings/general/index', compact('settings'));
55+
return $this->renderTemplate('commerce/settings/general/index', ['settings' => $plugin->getSettings()]);
6956
}
7057

7158
$this->setSuccessFlash(Craft::t('commerce', 'Settings saved.'));

src/controllers/ShippingCategoriesController.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ public function actionIndex(?string $storeHandle = null): Response
4646
// Generate table data with chips
4747
$tableData = [];
4848
foreach ($shippingCategories as $shippingCategory) {
49-
$label = Craft::t('site', $shippingCategory->name);
49+
$label = Html::encode(Craft::t('site', $shippingCategory->name));
5050
$tableData[] = [
5151
'id' => $shippingCategory->id,
5252
'title' => $label,
@@ -57,7 +57,7 @@ public function actionIndex(?string $storeHandle = null): Response
5757
]),
5858
'url' => $shippingCategory->getCpEditUrl(),
5959
'handle' => $shippingCategory->handle,
60-
'description' => Craft::t('site', $shippingCategory->description),
60+
'description' => Html::encode(Craft::t('site', $shippingCategory->description)),
6161
'default' => $shippingCategory->default,
6262
'_showDelete' => (count($shippingCategories) > 1 && !$shippingCategory->default),
6363
];

src/controllers/ShippingMethodsController.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ public function actionIndex(?string $storeHandle = null): Response
4444
// Generate table data with chips
4545
$tableData = [];
4646
foreach ($shippingMethods as $shippingMethod) {
47-
$label = Craft::t('site', $shippingMethod->name);
47+
$label = Html::encode(Craft::t('site', $shippingMethod->name));
4848
$tableData[] = [
4949
'id' => $shippingMethod->id,
5050
'title' => $label,

0 commit comments

Comments
 (0)