Skip to content

Commit

Permalink
Issue #2995325 by bojanz, mglaman: Order::getCustomer() should guard …
Browse files Browse the repository at this point in the history
…against deleted users
  • Loading branch information
bojanz committed Oct 4, 2018
1 parent 4947ac7 commit 03659c6
Show file tree
Hide file tree
Showing 12 changed files with 49 additions and 38 deletions.
17 changes: 14 additions & 3 deletions modules/order/src/Entity/Order.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Field\BaseFieldDefinition;
use Drupal\user\Entity\User;
use Drupal\user\UserInterface;
use Drupal\profile\Entity\ProfileInterface;

Expand Down Expand Up @@ -120,7 +121,12 @@ public function setStoreId($store_id) {
* {@inheritdoc}
*/
public function getCustomer() {
return $this->get('uid')->entity;
$customer = $this->get('uid')->entity;
// Handle deleted customers.
if (!$customer) {
$customer = User::getAnonymousUser();
}
return $customer;
}

/**
Expand Down Expand Up @@ -516,10 +522,15 @@ public function preSave(EntityStorageInterface $storage) {
}
}

if (!$this->getEmail() && $customer = $this->getCustomer()) {
$customer = $this->getCustomer();
// The customer has been deleted, clear the reference.
if ($this->getCustomerId() && $customer->isAnonymous()) {
$this->set('uid', 0);
}
// Maintain the order email.
if (!$this->getEmail() && $customer->isAuthenticated()) {
$this->setEmail($customer->getEmail());
}

// Maintain the completed timestamp.
$state = $this->getState()->value;
$original_state = isset($this->original) ? $this->original->getState()->value : '';
Expand Down
10 changes: 6 additions & 4 deletions modules/order/src/Entity/OrderInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,10 @@ public function setStoreId($store_id);
/**
* Gets the customer user.
*
* @return \Drupal\user\UserInterface|null
* The customer user entity, or NULL in case the order is anonymous,
* @return \Drupal\user\UserInterface
* The customer user entity. If the order is anonymous (customer
* unspecified or deleted), an anonymous user will be returned. Use
* $customer->isAnonymous() to check.
*/
public function getCustomer();

Expand All @@ -94,8 +96,8 @@ public function setCustomer(UserInterface $account);
/**
* Gets the customer user ID.
*
* @return int|null
* The customer user ID, or NULL in case the order is anonymous.
* @return int
* The customer user ID ('0' if anonymous).
*/
public function getCustomerId();

Expand Down
3 changes: 2 additions & 1 deletion modules/order/src/EventSubscriber/OrderReceiptSubscriber.php
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,8 @@ public function sendOrderReceipt(WorkflowTransitionEvent $event) {
});

// Replicated logic from EmailAction and contact's MailHandler.
if ($customer = $order->getCustomer()) {
$customer = $order->getCustomer();
if ($customer->isAuthenticated()) {
$langcode = $customer->getPreferredLangcode();
}
else {
Expand Down
3 changes: 2 additions & 1 deletion modules/order/src/Form/OrderForm.php
Original file line number Diff line number Diff line change
Expand Up @@ -117,10 +117,11 @@ public function form(array $form, FormStateInterface $form_state) {
$form['meta']['store'] = $this->fieldAsReadOnly($this->t('Store'), $store_link);
}
// Move uid/mail widgets to the sidebar, or provide read-only alternatives.
$customer = $order->getCustomer();
if (isset($form['uid'])) {
$form['uid']['#group'] = 'customer';
}
elseif ($customer = $order->getCustomer()) {
elseif ($customer->isAuthenticated()) {
$customer_link = $customer->toLink()->toString();
$form['customer']['uid'] = $this->fieldAsReadOnly($this->t('Customer'), $customer_link);
}
Expand Down
4 changes: 2 additions & 2 deletions modules/order/src/Form/OrderReassignForm.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,13 @@ public function getFormId() {
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state) {
if (!$this->order->getCustomerId()) {
$customer = $this->order->getCustomer();
if ($customer->isAnonymous()) {
$current_customer = $this->t('anonymous user with the email %email', [
'%email' => $this->order->getEmail(),
]);
}
else {
$customer = $this->order->getCustomer();
// If the display name has been altered to not be the email address,
// show the email as well.
if ($customer->getDisplayName() != $customer->getEmail()) {
Expand Down
2 changes: 1 addition & 1 deletion modules/order/src/OrderAssignment.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public function __construct(EntityTypeManagerInterface $entity_type_manager, Eve
* {@inheritdoc}
*/
public function assign(OrderInterface $order, UserInterface $account) {
if (!empty($order->getCustomerId())) {
if ($order->getCustomer()->isAuthenticated()) {
// Skip orders which already have a customer.
return;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,8 @@ public function evaluate(EntityInterface $entity) {
/** @var \Drupal\commerce_order\Entity\OrderInterface $order */
$order = $entity;
$customer = $order->getCustomer();
$roles = $customer ? $customer->getRoles() : ['anonymous'];

return (bool) array_intersect($this->configuration['roles'], $roles);
return (bool) array_intersect($this->configuration['roles'], $customer->getRoles());
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen
else {
$profile = $this->entityTypeManager->getStorage('profile')->create([
'type' => 'customer',
'uid' => $order->getCustomerId(),
'uid' => $order->getCustomer(),
]);
}

Expand Down
20 changes: 17 additions & 3 deletions modules/order/tests/src/Kernel/Entity/OrderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
use Drupal\commerce_price\Price;
use Drupal\profile\Entity\Profile;
use Drupal\Tests\commerce\Kernel\CommerceKernelTestBase;
use Drupal\user\UserInterface;

/**
* Tests the Order entity.
Expand Down Expand Up @@ -135,6 +136,7 @@ public function testOrder() {
$another_order_item->save();
$another_order_item = $this->reloadEntity($another_order_item);

/** @var \Drupal\commerce_order\Entity\OrderInterface $order */
$order = Order::create([
'type' => 'default',
'state' => 'completed',
Expand All @@ -149,15 +151,22 @@ public function testOrder() {
$this->assertEquals($this->store->id(), $order->getStoreId());
$order->setStoreId(0);
$this->assertEquals(NULL, $order->getStore());
$order->setStoreId([$this->store->id()]);
$order->setStoreId($this->store->id());
$this->assertEquals($this->store, $order->getStore());
$this->assertEquals($this->store->id(), $order->getStoreId());

$this->assertInstanceOf(UserInterface::class, $order->getCustomer());
$this->assertTrue($order->getCustomer()->isAnonymous());
$this->assertEquals(0, $order->getCustomerId());
$order->setCustomer($this->user);
$this->assertEquals($this->user, $order->getCustomer());
$this->assertEquals($this->user->id(), $order->getCustomerId());
$order->setCustomerId(0);
$this->assertEquals(NULL, $order->getCustomer());
$this->assertTrue($order->getCustomer()->isAuthenticated());
// Non-existent/deleted user ID.
$order->setCustomerId(888);
$this->assertInstanceOf(UserInterface::class, $order->getCustomer());
$this->assertTrue($order->getCustomer()->isAnonymous());
$this->assertEquals(888, $order->getCustomerId());
$order->setCustomerId($this->user->id());
$this->assertEquals($this->user, $order->getCustomer());
$this->assertEquals($this->user->id(), $order->getCustomerId());
Expand Down Expand Up @@ -241,6 +250,11 @@ public function testOrder() {

$order->setCompletedTime(635879900);
$this->assertEquals(635879900, $order->getCompletedTime());

// Confirm that saving the order clears an invalid customer ID.
$order->setCustomerId(888);
$order->save();
$this->assertEquals(0, $order->getCustomerId());
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,24 +16,7 @@ class OrderCustomerRoleTest extends UnitTestCase {
/**
* ::covers evaluate.
*/
public function testAnonymousCustomer() {
$condition = new OrderCustomerRole([
'roles' => ['authenticated'],
], 'order_customer_role', ['entity_type' => 'commerce_order']);
$order = $this->prophesize(OrderInterface::class);
$order->getEntityTypeId()->willReturn('commerce_order');
$order->getCustomer()->willReturn(NULL);
$order = $order->reveal();

$this->assertFalse($condition->evaluate($order));
$condition->setConfiguration(['roles' => ['anonymous', 'authenticated']]);
$this->assertTrue($condition->evaluate($order));
}

/**
* ::covers evaluate.
*/
public function testAuthenticatedCustomer() {
public function testEvaluate() {
$condition = new OrderCustomerRole([
'roles' => ['merchant'],
], 'order_customer_role', ['entity_type' => 'commerce_order']);
Expand Down
2 changes: 1 addition & 1 deletion modules/payment/src/Form/PaymentAddForm.php
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ protected function buildPaymentGatewayForm(array $form, FormStateInterface $form
// @todo
// Support adding payments to anonymous orders, by adding support for
// creating payment methods directly on this form.
if (!$this->order->getCustomerId()) {
if ($this->order->getCustomer()->isAnonymous()) {
throw new AccessDeniedHttpException();
}

Expand Down
2 changes: 1 addition & 1 deletion modules/payment/src/PaymentOptionsBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ public function buildOptions(OrderInterface $order, array $payment_gateways = []
$options = [];
// 1) Add options to reuse stored payment methods for known customers.
$customer = $order->getCustomer();
if ($customer) {
if ($customer->isAuthenticated()) {
$billing_countries = $order->getStore()->getBillingCountries();
/** @var \Drupal\commerce_payment\PaymentMethodStorageInterface $payment_method_storage */
$payment_method_storage = $this->entityTypeManager->getStorage('commerce_payment_method');
Expand Down

0 comments on commit 03659c6

Please sign in to comment.