Virtuemart fails when ps_cart->add() returns false
I'm running into a weird little problem whilst modifying a VM module for someone.
We've altered the ps_cart class functions add() and update() with following bit of code: However, we've noticed that when any of the functions validating the quantity of a product (negative or alphabetical character), the page does not display an error as it is supposed to in the code.
Instead it throws the user to a page listing all products with following link: /index.php?keyword=&category_id=&limitstart=&page=shop.browse&option=com_virtuemart&Itemid=2
We've added an if-conditional checking the quantity of products in the cart, but VM also does the same when you use a negative or alphabetical quantity.
Additionally, when this occurs, Firebug reports "Failed to load source for: http://[website]/index.php" in the response for the POST generated by the add to cart "event".
Does anyone have any idea where the return of ps_cart->add() is evaluated so we could troubleshoot, or does anyone have any ideas as to the cause? Following is the code from ps_cart returning false, we've inserted the middle if-conditional, but as I mentioned before the same occurs with VM's own quantity checks.
// Check for negative quantity
if ($quantity < 0) {
vmRequest::setVar('product_id', $product_id );
$vmLogger->warning( $VM_LANG->_('PHPSHOP_CART_ERROR_NO_NEGATIVE',false)开发者_运维知识库 );
return False;
}
if ($quantity > 1 || $_SESSION['cart']["idx"] >= 1) {
vmRequest::setVar('product_id', $product_id );
$vmLogger->warning( $VM_LANG->_('PHPSHOP_CART_ERROR_ONLY_ONE',false) );
return False;
}
if ( !is_numeric($quantity) ) {
vmRequest::setVar('product_id', $product_id );
$vmLogger->warning( $VM_LANG->_('PHPSHOP_CART_ERROR_NO_VALID_QUANTITY',false) );
return False;
}
Filing this as a bug report would not be much of a solution as we have highly customized VM and upgrading would be an immense pain.
Any help would be most appreciated.
After some research I managed to fix the problem.
For all those looking for more information on how virtuemart handles the actual adding to a cart, I'll explain the process of how I finally determined the problem. The solution is quite simple though.
The form
You may have noticed that most VM forms use PHP_SELF as form action. The form actually submits a load of data you cannot see through hidden input fields to provide the necessary information to the PHP_SELF page saying "the user want to do this [addCart] with these [products]". Example from addtocartform.tpl.php which is the little form onsisting of the 'add to cart' button and quantity controls if switched on:
<form action="<?php echo $mm_action_url ?>index.php" method="post" name="addtocart" id="addtocart<?php echo $i ?>" class="addtocart_form" <?php if( $this->get_cfg( 'useAjaxCartActions', 1 ) && !$notify ) { echo 'onsubmit="handleAddToCart( this.id );"'; } ?>>
<?php echo $ps_product_attribute->show_quantity_box($product_id,$product_id); ?><br />
<input type="submit" class="<?php echo $button_cls ?>" value="<?php echo $button_lbl ?>" title="<?php echo $button_lbl ?>" />
<input type="hidden" name="category_id" value="<?php echo @$_REQUEST['category_id'] ?>" />
<input type="hidden" name="product_id" value="<?php echo $product_id ?>" />
<input type="hidden" name="prod_id[]" value="<?php echo $product_id ?>" />
<input type="hidden" name="page" value="shop.cart" />
<input type="hidden" name="func" value="cartadd" />
<input type="hidden" name="Itemid" value="<?php echo $sess->getShopItemid() ?>" />
<input type="hidden" name="option" value="com_virtuemart" />
<input type="hidden" name="set_price[]" value="" />
<input type="hidden" name="adjust_price[]" value="" />
<input type="hidden" name="master_product[]" value="" />
So along with the quantity controls (added by show_quantity_box()) and the submit button, you can see a load of other data being passed. Among this data is func="cartadd" and product_id=[product id].
Processing the form
Unlike a simple form POST where you expect just data from one pre-determined form, VM uses a file called "virtuemart_parser.php". This file checks the submitted "func" to check what it is the user is trying to do. Then the parser will check if a function as such was registered (remember you can add your own functions in the VM config) and if so, it will execute it. [virtuemart_parser.php line 215-274]
Now, when in our case the function that is called returns boolean false,the parser will execute following code:
$last_page = vmGet( $_SESSION, 'last_page' );
if( $last_page != HOMEPAGE && !empty( $last_page ) && empty($_REQUEST['ignore_last_page']) ) {
$page = $last_page;
}
Basically it checks for the last_page in $_SESSION. In our case we were coming from shop.browse, but the 'last_page' we discovered actually was set to shop.productdetails, hence redirecting us to a list of products without any warning.
Solution
So the solution was rather simple, we just set the last_page var ourselves, leaving us with following modified code:
// Check for negative quantity
if ($quantity < 0) {
$_SESSION['last_page'] = 'shop.browse';
vmRequest::setVar('product_id', $product_id );
$vmLogger->warning( $VM_LANG->_('PHPSHOP_CART_ERROR_NO_NEGATIVE',false) );
return False;
}
if ($quantity > 1 || $_SESSION['cart']["idx"] >= 1) {
//$_SESSION['last_page'] = 'shop.browse';
vmRequest::setVar('product_id', $product_id );
$vmLogger->warning( $VM_LANG->_('PHPSHOP_CART_ERROR_ONLY_ONE',false) );
return False;
}
if ( !is_numeric($quantity) ) {
$_SESSION['last_page'] = 'shop.browse';
vmRequest::setVar('product_id', $product_id );
$vmLogger->warning( $VM_LANG->_('PHPSHOP_CART_ERROR_NO_VALID_QUANTITY',false) );
return False;
}
ps_cart->add() sets $_SESSION['last_page']="shop.productdetails" automatically [line 109]. We changed it to
$_SESSION['last_page'] = $_SESSION['last_page']=="shop.product_details" ? $_SESSION['last_page'] : 'shop.browse';
This way you handle whether the customer added a product from the details page or the category browsing page. If the customer added the product from the brwosing page - that's where it will show an error, if he added the product from the product_details page, he'll get the error at the shop.cart page. [product_details does not seem to have any handling for errors returned by functions]
Hope this was helpful to some.
精彩评论