Skip to content

Commit 03659c6

Browse files
committed
Issue #2995325 by bojanz, mglaman: Order::getCustomer() should guard against deleted users
1 parent 4947ac7 commit 03659c6

File tree

12 files changed

+49
-38
lines changed

12 files changed

+49
-38
lines changed

modules/order/src/Entity/Order.php

+14-3
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
use Drupal\Core\Entity\EntityStorageInterface;
1010
use Drupal\Core\Entity\EntityTypeInterface;
1111
use Drupal\Core\Field\BaseFieldDefinition;
12+
use Drupal\user\Entity\User;
1213
use Drupal\user\UserInterface;
1314
use Drupal\profile\Entity\ProfileInterface;
1415

@@ -120,7 +121,12 @@ public function setStoreId($store_id) {
120121
* {@inheritdoc}
121122
*/
122123
public function getCustomer() {
123-
return $this->get('uid')->entity;
124+
$customer = $this->get('uid')->entity;
125+
// Handle deleted customers.
126+
if (!$customer) {
127+
$customer = User::getAnonymousUser();
128+
}
129+
return $customer;
124130
}
125131

126132
/**
@@ -516,10 +522,15 @@ public function preSave(EntityStorageInterface $storage) {
516522
}
517523
}
518524

519-
if (!$this->getEmail() && $customer = $this->getCustomer()) {
525+
$customer = $this->getCustomer();
526+
// The customer has been deleted, clear the reference.
527+
if ($this->getCustomerId() && $customer->isAnonymous()) {
528+
$this->set('uid', 0);
529+
}
530+
// Maintain the order email.
531+
if (!$this->getEmail() && $customer->isAuthenticated()) {
520532
$this->setEmail($customer->getEmail());
521533
}
522-
523534
// Maintain the completed timestamp.
524535
$state = $this->getState()->value;
525536
$original_state = isset($this->original) ? $this->original->getState()->value : '';

modules/order/src/Entity/OrderInterface.php

+6-4
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,10 @@ public function setStoreId($store_id);
7676
/**
7777
* Gets the customer user.
7878
*
79-
* @return \Drupal\user\UserInterface|null
80-
* The customer user entity, or NULL in case the order is anonymous,
79+
* @return \Drupal\user\UserInterface
80+
* The customer user entity. If the order is anonymous (customer
81+
* unspecified or deleted), an anonymous user will be returned. Use
82+
* $customer->isAnonymous() to check.
8183
*/
8284
public function getCustomer();
8385

@@ -94,8 +96,8 @@ public function setCustomer(UserInterface $account);
9496
/**
9597
* Gets the customer user ID.
9698
*
97-
* @return int|null
98-
* The customer user ID, or NULL in case the order is anonymous.
99+
* @return int
100+
* The customer user ID ('0' if anonymous).
99101
*/
100102
public function getCustomerId();
101103

modules/order/src/EventSubscriber/OrderReceiptSubscriber.php

+2-1
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,8 @@ public function sendOrderReceipt(WorkflowTransitionEvent $event) {
130130
});
131131

132132
// Replicated logic from EmailAction and contact's MailHandler.
133-
if ($customer = $order->getCustomer()) {
133+
$customer = $order->getCustomer();
134+
if ($customer->isAuthenticated()) {
134135
$langcode = $customer->getPreferredLangcode();
135136
}
136137
else {

modules/order/src/Form/OrderForm.php

+2-1
Original file line numberDiff line numberDiff line change
@@ -117,10 +117,11 @@ public function form(array $form, FormStateInterface $form_state) {
117117
$form['meta']['store'] = $this->fieldAsReadOnly($this->t('Store'), $store_link);
118118
}
119119
// Move uid/mail widgets to the sidebar, or provide read-only alternatives.
120+
$customer = $order->getCustomer();
120121
if (isset($form['uid'])) {
121122
$form['uid']['#group'] = 'customer';
122123
}
123-
elseif ($customer = $order->getCustomer()) {
124+
elseif ($customer->isAuthenticated()) {
124125
$customer_link = $customer->toLink()->toString();
125126
$form['customer']['uid'] = $this->fieldAsReadOnly($this->t('Customer'), $customer_link);
126127
}

modules/order/src/Form/OrderReassignForm.php

+2-2
Original file line numberDiff line numberDiff line change
@@ -49,13 +49,13 @@ public function getFormId() {
4949
* {@inheritdoc}
5050
*/
5151
public function buildForm(array $form, FormStateInterface $form_state) {
52-
if (!$this->order->getCustomerId()) {
52+
$customer = $this->order->getCustomer();
53+
if ($customer->isAnonymous()) {
5354
$current_customer = $this->t('anonymous user with the email %email', [
5455
'%email' => $this->order->getEmail(),
5556
]);
5657
}
5758
else {
58-
$customer = $this->order->getCustomer();
5959
// If the display name has been altered to not be the email address,
6060
// show the email as well.
6161
if ($customer->getDisplayName() != $customer->getEmail()) {

modules/order/src/OrderAssignment.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ public function __construct(EntityTypeManagerInterface $entity_type_manager, Eve
4242
* {@inheritdoc}
4343
*/
4444
public function assign(OrderInterface $order, UserInterface $account) {
45-
if (!empty($order->getCustomerId())) {
45+
if ($order->getCustomer()->isAuthenticated()) {
4646
// Skip orders which already have a customer.
4747
return;
4848
}

modules/order/src/Plugin/Commerce/Condition/OrderCustomerRole.php

+1-2
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,8 @@ public function evaluate(EntityInterface $entity) {
6363
/** @var \Drupal\commerce_order\Entity\OrderInterface $order */
6464
$order = $entity;
6565
$customer = $order->getCustomer();
66-
$roles = $customer ? $customer->getRoles() : ['anonymous'];
6766

68-
return (bool) array_intersect($this->configuration['roles'], $roles);
67+
return (bool) array_intersect($this->configuration['roles'], $customer->getRoles());
6968
}
7069

7170
}

modules/order/src/Plugin/Field/FieldWidget/BillingProfileWidget.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen
8181
else {
8282
$profile = $this->entityTypeManager->getStorage('profile')->create([
8383
'type' => 'customer',
84-
'uid' => $order->getCustomerId(),
84+
'uid' => $order->getCustomer(),
8585
]);
8686
}
8787

modules/order/tests/src/Kernel/Entity/OrderTest.php

+17-3
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
use Drupal\commerce_price\Price;
1111
use Drupal\profile\Entity\Profile;
1212
use Drupal\Tests\commerce\Kernel\CommerceKernelTestBase;
13+
use Drupal\user\UserInterface;
1314

1415
/**
1516
* Tests the Order entity.
@@ -135,6 +136,7 @@ public function testOrder() {
135136
$another_order_item->save();
136137
$another_order_item = $this->reloadEntity($another_order_item);
137138

139+
/** @var \Drupal\commerce_order\Entity\OrderInterface $order */
138140
$order = Order::create([
139141
'type' => 'default',
140142
'state' => 'completed',
@@ -149,15 +151,22 @@ public function testOrder() {
149151
$this->assertEquals($this->store->id(), $order->getStoreId());
150152
$order->setStoreId(0);
151153
$this->assertEquals(NULL, $order->getStore());
152-
$order->setStoreId([$this->store->id()]);
154+
$order->setStoreId($this->store->id());
153155
$this->assertEquals($this->store, $order->getStore());
154156
$this->assertEquals($this->store->id(), $order->getStoreId());
155157

158+
$this->assertInstanceOf(UserInterface::class, $order->getCustomer());
159+
$this->assertTrue($order->getCustomer()->isAnonymous());
160+
$this->assertEquals(0, $order->getCustomerId());
156161
$order->setCustomer($this->user);
157162
$this->assertEquals($this->user, $order->getCustomer());
158163
$this->assertEquals($this->user->id(), $order->getCustomerId());
159-
$order->setCustomerId(0);
160-
$this->assertEquals(NULL, $order->getCustomer());
164+
$this->assertTrue($order->getCustomer()->isAuthenticated());
165+
// Non-existent/deleted user ID.
166+
$order->setCustomerId(888);
167+
$this->assertInstanceOf(UserInterface::class, $order->getCustomer());
168+
$this->assertTrue($order->getCustomer()->isAnonymous());
169+
$this->assertEquals(888, $order->getCustomerId());
161170
$order->setCustomerId($this->user->id());
162171
$this->assertEquals($this->user, $order->getCustomer());
163172
$this->assertEquals($this->user->id(), $order->getCustomerId());
@@ -241,6 +250,11 @@ public function testOrder() {
241250

242251
$order->setCompletedTime(635879900);
243252
$this->assertEquals(635879900, $order->getCompletedTime());
253+
254+
// Confirm that saving the order clears an invalid customer ID.
255+
$order->setCustomerId(888);
256+
$order->save();
257+
$this->assertEquals(0, $order->getCustomerId());
244258
}
245259

246260
/**

modules/order/tests/src/Unit/Plugin/Commerce/Condition/OrderCustomerRoleTest.php

+1-18
Original file line numberDiff line numberDiff line change
@@ -16,24 +16,7 @@ class OrderCustomerRoleTest extends UnitTestCase {
1616
/**
1717
* ::covers evaluate.
1818
*/
19-
public function testAnonymousCustomer() {
20-
$condition = new OrderCustomerRole([
21-
'roles' => ['authenticated'],
22-
], 'order_customer_role', ['entity_type' => 'commerce_order']);
23-
$order = $this->prophesize(OrderInterface::class);
24-
$order->getEntityTypeId()->willReturn('commerce_order');
25-
$order->getCustomer()->willReturn(NULL);
26-
$order = $order->reveal();
27-
28-
$this->assertFalse($condition->evaluate($order));
29-
$condition->setConfiguration(['roles' => ['anonymous', 'authenticated']]);
30-
$this->assertTrue($condition->evaluate($order));
31-
}
32-
33-
/**
34-
* ::covers evaluate.
35-
*/
36-
public function testAuthenticatedCustomer() {
19+
public function testEvaluate() {
3720
$condition = new OrderCustomerRole([
3821
'roles' => ['merchant'],
3922
], 'order_customer_role', ['entity_type' => 'commerce_order']);

modules/payment/src/Form/PaymentAddForm.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ protected function buildPaymentGatewayForm(array $form, FormStateInterface $form
101101
// @todo
102102
// Support adding payments to anonymous orders, by adding support for
103103
// creating payment methods directly on this form.
104-
if (!$this->order->getCustomerId()) {
104+
if ($this->order->getCustomer()->isAnonymous()) {
105105
throw new AccessDeniedHttpException();
106106
}
107107

modules/payment/src/PaymentOptionsBuilder.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ public function buildOptions(OrderInterface $order, array $payment_gateways = []
5151
$options = [];
5252
// 1) Add options to reuse stored payment methods for known customers.
5353
$customer = $order->getCustomer();
54-
if ($customer) {
54+
if ($customer->isAuthenticated()) {
5555
$billing_countries = $order->getStore()->getBillingCountries();
5656
/** @var \Drupal\commerce_payment\PaymentMethodStorageInterface $payment_method_storage */
5757
$payment_method_storage = $this->entityTypeManager->getStorage('commerce_payment_method');

0 commit comments

Comments
 (0)