diff -u b/includes/commerce.controller.inc b/includes/commerce.controller.inc --- b/includes/commerce.controller.inc +++ b/includes/commerce.controller.inc @@ -24,6 +24,28 @@ public function isLocked($entity); /** + * Determines whether the provided entity is read-only. + * + * @param object $entity + * The entity to check. + * + * @return bool + * True if the entity is read only, false otherwise. + */ + public function isReadOnly($entity); + + /** + * Determines whether the provided entity is writeable. + * + * @param object $entity + * The entity to check. + * + * @return bool + * True if the entity is writeable, false otherwise. + */ + public function isWriteable($entity); + + /** * Determines whether the provided entity is cached. * * @param object $entity @@ -79,11 +101,18 @@ protected $lockedEntities = array(); /** - * Stores the flag for if a condition has been passed for requesting locking. + * Stores the ids of read-only entities. + * + * Track which entities where meant to be loaded read-only. + */ + protected $readOnlyEntities = array(); + + /** + * Stores the flag for if a condition has been passed for requesting writing. * - * By default, locking is always requested unless specifically set to false. + * By default, writing is always requested unless specifically set to false. */ - protected $requestLocking = TRUE; + protected $requestWriting = TRUE; /** * Stores whether a request for skipping locking has been set. @@ -97,7 +126,21 @@ * Implements DrupalCommerceEntityControllerInterface::isLocked(). */ public function isLocked($entity) { - return $this->controllerTransaction && isset($this->lockedEntities[$entity->{$this->idKey}]); + return !empty($this->controllerTransaction) && isset($this->lockedEntities[$entity->{$this->idKey}]); + } + + /** + * Implements DrupalCommerceEntityControllerInterface::isReadOnly(). + */ + public function isReadOnly($entity) { + return isset($this->readOnlyEntities[$entity->{$this->idKey}]); + } + + /** + * Implements DrupalCommerceEntityControllerInterface::isWriteable(). + */ + public function isWriteable($entity) { + return !$this->isReadOnly($entity); } /** @@ -135,7 +178,7 @@ $enabled = isset($this->entityInfo['locking mode']) && $this->entityInfo['locking mode'] == 'pessimistic'; $not_skipped = empty($this->requestSkipLocking); - return $enabled && $not_skipped && $this->requestLocking; + return $enabled && $not_skipped && $this->requestWriting; } /** @@ -164,15 +207,30 @@ /** * Overrides DrupalDefaultEntityController::load(). * - * Accepts a condition of locking request, may not necessarily take effect - * as other options can override locking. If anything conflicts, locking always - * take precedence over not locking. + * Accepts a condition of writing request, this will partially determine if + * the order needs to be locked, which may not necessarily take effect + * as other options can override locking. If anything conflicts, locking + * always take precedence over not locking. */ public function load($ids = array(), $conditions = array()) { - // Lock by default if the caller didn't indicate a preference. - $conditions += array('_lock' => TRUE); - $this->requestLocking = $conditions['_lock']; - unset($conditions['_lock']); + // Assume it's a write request by default + // if the caller didn't indicate a preference. + $conditions += array('_write' => TRUE); + $this->requestWriting = $conditions['_write']; + unset($conditions['_write']); + + if ($this->requestWriting) { + foreach (array_intersect_key(array_flip($ids), $this->readOnlyEntities) as $id => $value) { + unset($this->readOnlyEntities[$id]); + } + } + else { + // Store the ids of the entities in the readOnlyEntities array for later + // tracking, flipped for easier management. + if ($ids) { + $this->readOnlyEntities += array_flip($ids); + } + } // If locking has been required, then bypass the internal cache for any // entities that are not already locked. diff -u b/modules/cart/tests/commerce_cart.test b/modules/cart/tests/commerce_cart.test --- b/modules/cart/tests/commerce_cart.test +++ b/modules/cart/tests/commerce_cart.test @@ -509,7 +509,7 @@ $this->drupalPost('node/' . $this->product_node->nid, array(), t('Add to cart')); // Get the order just created. - $orders = commerce_order_load_multiple(array(), array('uid' => $user->uid, 'status' => 'cart', '_lock' => FALSE), TRUE); + $orders = commerce_order_load_multiple(array(), array('uid' => $user->uid, 'status' => 'cart', '_write' => FALSE), TRUE); $order_anonymous = reset($orders); // Access to the cart and check if the product is in it. @@ -528,7 +528,7 @@ $this->drupalPost('user', array('name' => $this->store_customer->name, 'pass' => $this->store_customer->pass_raw), t('Log in')); // Get the order for user just logged in. - $orders = commerce_order_load_multiple(array(), array('uid' => $this->store_customer->uid, 'status' => 'cart', '_lock' => FALSE), TRUE); + $orders = commerce_order_load_multiple(array(), array('uid' => $this->store_customer->uid, 'status' => 'cart', '_write' => FALSE), TRUE); $order_authenticated = reset($orders); // Reset the cache as we don't want to keep the lock. @@ -581,5 +581,5 @@ $this->assertTrue(commerce_order_is_locked($order)); commerce_cart_order_refresh($order); - $this->assertFalse(commerce_order_is_locked($order), 'Cart refresh removed order lock.'); + $this->assertTrue(commerce_order_is_locked($order), 'Cart refresh removed order lock.'); } } diff -u b/modules/checkout/tests/commerce_checkout.test b/modules/checkout/tests/commerce_checkout.test --- b/modules/checkout/tests/commerce_checkout.test +++ b/modules/checkout/tests/commerce_checkout.test @@ -76,7 +76,7 @@ $this->drupalPost('node/' . $this->product_node->nid, array(), t('Add to cart')); // Get the order for the anonymous user. - $orders = commerce_order_load_multiple(array(), array('uid' => $user->uid, 'status' => 'cart', '_lock' => FALSE), TRUE); + $orders = commerce_order_load_multiple(array(), array('uid' => $user->uid, 'status' => 'cart', '_write' => FALSE), TRUE); $this->order = reset($orders); } @@ -160,7 +160,7 @@ $this->assertText('Example payment', t('Example payment method pane is present')); // Load the order to check the status. - $order = commerce_order_load_multiple(array($this->order->order_id), array('_lock' => FALSE), TRUE); + $order = commerce_order_load_multiple(array($this->order->order_id), array('_write' => FALSE), TRUE); // At this point we should be in Checkout Review. $this->assertEqual(reset($order)->status, 'checkout_review', t('Order status is \'Checkout Review\' in the review phase.')); @@ -320,7 +320,7 @@ $this->assertText($user_mail, t('Account information is correct')); // Load the order to check the status. - $order = commerce_order_load_multiple(array($this->order->order_id), array('_lock' => FALSE), TRUE); + $order = commerce_order_load_multiple(array($this->order->order_id), array('_write' => FALSE), TRUE); // At this point we should be in Checkout Review. $this->assertEqual(reset($order)->status, 'checkout_review', t('Order status is \'Checkout Review\' in the review phase.')); diff -u b/modules/order/commerce_order.module b/modules/order/commerce_order.module --- b/modules/order/commerce_order.module +++ b/modules/order/commerce_order.module @@ -749,11 +749,11 @@ * * @param $order_id * The order id. - * @param $lock - * Whether to lock the loaded order. + * @param $write + * Whether it's a write or a read-only request. */ -function commerce_order_load($order_id, $lock = TRUE) { - $orders = commerce_order_load_multiple(array($order_id), array('_lock' => $lock), FALSE); +function commerce_order_load($order_id, $write = TRUE) { + $orders = commerce_order_load_multiple(array($order_id), array('_write' => $write), FALSE); return $orders ? reset($orders) : FALSE; } @@ -762,11 +762,11 @@ * * @param $order_number * The order number. - * @param $lock - * Whether to lock the loaded order. + * @param $write + * Whether it's a write or a read-only request. */ -function commerce_order_load_by_number($order_number, $lock = TRUE) { - $orders = commerce_order_load_multiple(array(), array('order_number' => $order_number, '_lock' => $lock), FALSE); +function commerce_order_load_by_number($order_number, $write = TRUE) { + $orders = commerce_order_load_multiple(array(), array('order_number' => $order_number, '_write' => $write), FALSE); return $orders ? reset($orders) : FALSE; } @@ -830,4 +830,30 @@ /** + * Determines whether or not the given order object is read-only. + * + * @param $order + * A fully loaded order object. + * + * @return + * Boolean indicating whether or not the order object is read-only. + */ +function commerce_order_is_readonly($order) { + return entity_get_controller('commerce_order')->isReadOnly($order); +} + +/** + * Determines whether or not the given order object is writeable + * + * @param $order + * A fully loaded order object. + * + * @return + * Boolean indicating whether or not the order object is writeable. + */ +function commerce_order_is_writeable($order) { + return entity_get_controller('commerce_order')->isWriteable($order); +} + +/** * Determines whether or not the given order object represents the latest * revision of the order. diff -u b/modules/order/tests/commerce_order_ui.test b/modules/order/tests/commerce_order_ui.test --- b/modules/order/tests/commerce_order_ui.test +++ b/modules/order/tests/commerce_order_ui.test @@ -60,7 +60,7 @@ $this->drupalPost(NULL, array('name' => $this->store_customer->name), t('Save order', array(), array('context' => 'a drupal commerce order'))); // Load the order from database for later use. - $orders = commerce_order_load_multiple(array(), array('uid' => $this->store_customer->uid, '_lock' => FALSE)); + $orders = commerce_order_load_multiple(array(), array('uid' => $this->store_customer->uid, '_write' => FALSE)); $this->order = reset($orders); // Enable an additional currency. @@ -147,7 +147,7 @@ $this->drupalPost(NULL, array(), t('Save order', array(), array('context' => 'a drupal commerce order'))); // Reload the order directly from db. - $order = commerce_order_load_multiple(array($this->order->order_id), array('_lock' => FALSE), TRUE); + $order = commerce_order_load_multiple(array($this->order->order_id), array('_write' => FALSE), TRUE); // Check if the product has been added to the order. foreach (entity_metadata_wrapper('commerce_order', reset($order))->commerce_line_items as $delta => $line_item_wrapper) { @@ -180,7 +180,7 @@ $this->drupalPost(NULL, array(), t('Save order', array(), array('context' => 'a drupal commerce order'))); // Reload the order directly from db and wrap it to get the line item ids. - $orders = commerce_order_load_multiple(array($this->order->order_id), array('_lock' => FALSE), TRUE); + $orders = commerce_order_load_multiple(array($this->order->order_id), array('_write' => FALSE), TRUE); $order = reset($orders); $order_wrapper = entity_metadata_wrapper('commerce_order', $order); diff -u b/modules/payment/tests/commerce_payment_ui.test b/modules/payment/tests/commerce_payment_ui.test --- b/modules/payment/tests/commerce_payment_ui.test +++ b/modules/payment/tests/commerce_payment_ui.test @@ -143,7 +143,7 @@ $this->drupalPost(NULL, $post_data, t('Save')); // Reload the order. - $order = commerce_order_load_multiple(array($this->order->order_id), array('_lock' => FALSE), TRUE); + $order = commerce_order_load_multiple(array($this->order->order_id), array('_write' => FALSE), TRUE); // Check order balance, it should be half of total now. $new_balance = commerce_payment_order_balance(reset($order)); only in patch2: unchanged: --- a/modules/cart/commerce_cart.module +++ b/modules/cart/commerce_cart.module @@ -748,6 +748,7 @@ function commerce_cart_commerce_order_load($orders) { // shopping cart order, it hasn't been refreshed already in this request // and it meets the criteria in the shopping cart refresh settings. if (!isset($refreshed[$order->order_id]) && + commerce_order_is_writeable($order) && commerce_cart_order_is_cart($order) && commerce_order_is_latest_revision($order) && commerce_cart_order_can_refresh($order)) {