Web Design and Build

Multiple images on WP e-Commerce product pages Last updated:21 April 2014

Dismayed this morning to find that WP e-Commerce does not support multiple images on a product page unless you pay for the “gold cart” option. Since I’m basically stingy, I did it myself. Here’s how:

Original fix, WP3.5, WPEC 3.8.7.6

I replaced this code in wpsc-single_product.php:

   <?php if ( wpsc_the_product_thumbnail() ) : ?>
      <a rel="<?php echo wpsc_the_product_title(); ?>" class="<?php echo wpsc_the_product_image_link_classes(); ?>" href="<?php echo wpsc_the_product_image(); ?>">
         <img class="product_image" id="product_image_<?php echo wpsc_the_product_id(); ?>" alt="<?php echo wpsc_the_product_title(); ?>" title="<?php echo wpsc_the_product_title(); ?>" src="<?php echo wpsc_the_product_thumbnail(get_option('product_image_width'),get_option('product_image_height'),'','single'); ?>"/>
      </a>
      <?php if ( function_exists( 'gold_shpcrt_display_gallery' ) )
         echo gold_shpcrt_display_gallery( wpsc_the_product_id() );
      ?>
   <?php else: ?>

with this code:

   <?php if ( wpsc_the_product_thumbnail()) ://if the product has any images...
      if (has_post_thumbnail()): //...display the thumbnail if there is one...?>
         <a rel="lightbox[<?php echo wpsc_the_product_title(); ?>]" class="<?php echo wpsc_the_product_image_link_classes(); ?>" href="<?php echo wpsc_the_product_image(); ?>">
         <?php echo get_the_post_thumbnail(wpsc_the_product_id(),'thumbnail',array('alt' => wpsc_the_product_title(),'title' => wpsc_the_product_title() ));?>
         </a>
      <?php endif;
      sb_get_images_for_product(wpsc_the_product_id());//...and then display all the rest of the images
else: ?>

I’ve used the standard WP function get_the_post_thumbnail() instead of the WP e-commerce function wpsc_the_product_thumbnail() simply to ensure a consistent display of thumbnails. So, I’m displaying the product thumbnail and then using a custom function sb_get_images_for_product() to retrieve any other images associated with the product. This function effectively replaces the gold_shpcrt_display_gallery() function for which you have to pay. I saved sb_get_images_for_product() in the functions.php (located in the theme folder). This function retrieves all images attached to the post and then displays any which are not the thumbnail – we’ve already displayed that. Here’s the code (last tested against WP 3.5):

function sb_get_images_for_product($id){
   global $wpdb;
   $post_thumbnail = get_post_thumbnail_id();//read the thumbnail id
   $attachments = $wpdb->get_results($wpdb->prepare("SELECT * FROM $wpdb->posts WHERE post_parent = $id AND post_type = 'attachment' ORDER BY menu_order ASC",$id));
   foreach ($attachments as $attachment){
      if ($attachment->ID <> $post_thumbnail){//if we haven't already got the attachment as the post thumbnail
         $image_attributes = wp_get_attachment_image_src($attachment->ID,'thumbnail');?>
	<a rel="lightbox[<?php echo wpsc_the_product_title(); ?>]" href="<?php echo $attachment->guid; ?>" class="<?php echo wpsc_the_product_image_link_classes(); ?>">
	<img src="<?php echo $image_attributes[0]; ?>" alt="<?php echo wpsc_the_product_title(); ?>"/>
	</a>
   <?php }		
   }
}

Note that WP 3.3 changed the way images work and messed up the WP e-commerce product images on version 3.8.7.4, with the result that it's possible to have a product without a product thumbnail. This is fixed with WP e-commerce version 3.8.7.6. However, I've added the if (has_post_thumbnail()) condition in the first code snippet above to trap this. Thanks to Brian for flagging this up.

Updated 21 April 2014, tested against WP 3.9, WPSC 3.8.13.4

Somewhere along the line, in conjunction with core WP galleries, WPEC has changed the way additional images are handled, and the fix above no longer works. Now try making this replacement in wpsc-single_product.php. Change this:

<?php
if ( function_exists( 'gold_shpcrt_display_gallery' ) )
   echo gold_shpcrt_display_gallery( wpsc_the_product_id() );
?>

with this:

<?php
	sb_get_images_for_product(wpsc_the_product_id());//...and then display all the rest of the images
?>

and add this to your functions.php:

function sb_get_images_for_product($id){
   $post_thumbnail = get_post_thumbnail_id();//read the thumbnail id
   
   $atts = get_post_meta($id,'_wpsc_product_gallery');
   
   $attachments = $atts[0];
   
   foreach ($attachments as $attachment){
      if ($attachment<> $post_thumbnail){//if we haven't already got the attachment as the post thumbnail
         $image_attributes = wp_get_attachment_image_src($attachment,'post-thumbnail');
		 $full_image_attributes = wp_get_attachment_image_src($attachment,'full');
		 ?>
	<a rel="lightbox[<?php echo wpsc_the_product_title(); ?>]" href="<?php echo $full_image_attributes[0]; ?>" class="<?php echo wpsc_the_product_image_link_classes(); ?>">
	<img src="<?php echo $image_attributes[0]; ?>" alt="<?php echo wpsc_the_product_title(); ?>"/>
	</a>
   <?php }		
   }
}

jQuery UI slider on touchscreen devices Last updated:12 March 2014

I’ve been meaning to have a look at updating my vertical scrollbar code so it works on a touchscreen. Finally found time this weekend to have a look…with rather limited results. I created three versions of the page, one using touchpunch, one with ‘touchpunch improved‘, and one with a basic mapping of touch events to mouse events.

Device OS Browser TP TP improved Mapping
Versus tablet Android 4.0.4 Android browser Fails Fails Fails
Samsung Galaxy Ace Android 2.3.6 Android browser Fails Fails Fails
Apple iPhone 3G iOS 4.2.1 Safari Works Works Works
Samsung Galaxy Ace 2 Android 4.1.2 Android browser Works Works Works

Conclusion so far…Touchpunch works on iOS and later Android versions only. If you have access to a different device, test the different pages and let me know the result.

Exporting to csv – ’ replacing ‘ Last updated:2 March 2014

Came across an interesting and frustrating issue last week. A client was exporting selected data from a WordPress database (actually using this plugin), and the user was seeing odd characters in the csv output. The most obvious was the replacement of single apostrophes: ‘ with this: ’.

Clearly an encoding problem but I couldn’t immediately see why. Pages displaying the data were fine, web pages and the database were all set to use utf-8. I finally solved the problem by forcing the encoding of the csv file via:

$text = mb_convert_encoding($text,'windows-1252');

Hope this helps someone else…

Opencart SSL issues with reverse proxy Last updated:28 February 2014

I got involved sorting out an interesting problem this week. A user (not one of my clients) had implemented SSL on his server, but his Opencart secure pages (Login, My Account etc) were displaying without any css being applied. This manifested in part because the href attribute on the secure page was displayed as http:// not https://.

Now, this value is controlled within Opencart by checking the value of $_SERVER['HTTPS'], set by the server. If this value is set, then Opencart sets the href as https://. If it isn’t it doesn’t.

All well and good, but in this case (123-reg) the server is set up with SSL Reversed Proxy & Load Balanced SSL Proxy, and this results in the server setting $_SERVER['HTTP_X_FORWARDED_SSL'] instead of $_SERVER['HTTPS'] – which is why the problem manifests.

The fix is relatively straightforward in that the checks need to be repointed at the new $_SERVER index. This can be fixed by changing /catalog/controller/common/header.php, catalog/model/tool/image.php for basic stores, more files dependent on the payment gateways. However, it’s simpler I think to make a modification to /system/library/request.php. This is straightforward since Opencart creates its own server variable (accessed through $this->request->server, so this can be changed independently of $_SERVER. Here’s a vQmod example:

<modification>
	<id>Reverse Proxy Handler</id>
	<version>for OC 1.5.6</version>
	<vqmver>2.2.1</vqmver>
	<author>Simon Battersby www.simonbattersby.com</author>
	<file name="/system/library/request.php">
		<operation>
			<search position="replace" ><![CDATA[$this->server = $_SERVER;]]></search>
			<add><![CDATA[
				if(isset($_SERVER['HTTP_X_FORWARDED_SSL']) && !isset($_SERVER['HTTPS'])){
					$https = array('HTTPS'=>$_SERVER['HTTP_X_FORWARDED_SSL']);
				} else {
					$https = array();
				}
				$this->server = array_merge($_SERVER,$https);
			]]></add>
		</operation>
	</file>
</modification>

All this is doing is checking if $_SERVER['HTTP_X_FORWARDED_SSL'] is set instead of $_SERVER['HTTPS'], and if it is set, adding the value ‘HTTPS’ to Opencart’s local server setting. If you use this, let me know if it works throughout, as I don’t have access to a reverse proxy SSL server.

Opencart cart not cleared after Paypal purchase Last updated:23 February 2014

Yesterday we had a customer complaining that when they logged on to Opencart to place an order, their previous order was still showing in their cart.

On investigation, this occurs where a customer makes a Paypal payment, but then does not return to Opencart after completing the Paypal transaction. In fact most of our customers seem to do this – why bother returning, I suppose. However, it’s the checkout/success page which clears the customer’s cart. In the absence of this, the cart remains stored against the customer’s record, and hence appears next time they log on.

Now, this has never been mentioned before, and this particular Opencart installation has been live for over 18 months, so it’s obviously not a massive issue, but it’s still not quite what you’d want. We’re still running a (heavily customised) OC 1.4.9.5, but in fact the problem remains on OC 1.5.4, and presumably, although I haven’t tested, with any other payment gateway where the payment is handled offsite.

It seemed to me that it would be better to clear the cart when the IPN message is returned from Paypal (this is what sets the order status and updates stock etc). The important cases are where a payment comes through as Completed, or as Pending (complete but waiting Paypal confirmation), so I edited catalog/controller/payment/pp_standard.php, adding the code in red:

		switch($data['payment_status']){
			case 'Completed':
				if ($this->order_info['customer_id']){//only run this if the customer has created an account, not for guest checkouts
					$this->model_checkout_order->clearCustomerCart($this->order_info['customer_id']);
				}
				if ($verified) {
					if ($this->order_info['order_status_id'] == '0') {
						$this->model_checkout_order->confirm($order_id, $this->config->get('pp_standard_order_status_id'), 'Thank you for your Paypal payment');
					} elseif (isset($data['payment_type']) && $data['payment_type'] == 'echeck') {
						$this->model_checkout_order->update($order_id, $this->config->get('pp_standard_order_status_id'), $data['payment_status'], TRUE);
					} elseif ($this->order_info['order_status_id'] != $this->config->get('pp_standard_order_status_id')) {
						$this->model_checkout_order->update($order_id, $this->config->get('pp_standard_order_status_id'), $data['payment_status'], FALSE);
					}

and similarly, a little further down:

			case 'Pending':
				if ($this->order_info['customer_id']){
					$this->model_checkout_order->clearCustomerCart($this->order_info['customer_id']);
				}
				if ($this->order_info['order_status_id'] == '0') {
					$this->model_checkout_order->confirm($order_id, $this->config->get('pp_standard_order_status_id_pending'), 'This payment is subject to a review by Paypal');
				} else {
					$this->model_checkout_order->update($order_id, $this->config->get('pp_standard_order_status_id_pending'), $comment, TRUE);
				}
				break;

In turn, this calls the following code appended to catalog/model/checkout/order.php:

	public function clearCustomerCart($customer_id){
	
		$this->db->query("UPDATE `" . DB_PREFIX . "customer` SET cart = '' WHERE customer_id = '" . (int)$customer_id . "'");
		$this->session->data['cart'] = array();
	
	}

I’ve shown here direct edits in OC1.4.9.5, but you could just as easily apply this via vQmod. This fix has now been working for several weeks with no problem.

vQmod for OC1.5.x

This code works with minor adjustments in OC1.5.x. Here’s a vQmod, tested in OC1.5.6.1:

<modification>
	<id>vQmods for Opencart mobile theme</id>
	<version>For OC 1.5.6</version>
	<vqmver>1.0</vqmver>
	<author>Simon Battersby</author>
	<file name="catalog/controller/payment/pp_standard.php">
		<operation>
			<search position="after"><![CDATA[$order_status_id = $this->config->get('pp_standard_completed_status_id');]]></search>
			<add><![CDATA[
				//Completed status
				$this->clearCustomerCart($order_info['customer_id']);
			]]></add>
		</operation>
		<operation>
			<search position="after"><![CDATA[case 'Pending':]]></search>
			<add><![CDATA[
				//Completed status
				$this->clearCustomerCart($order_info['customer_id']);
			]]></add>
		</operation>
		<operation>
			<search position="after" offset="2"><![CDATA[curl_close($curl);]]></search>
			<add><![CDATA[
				//function to clear cart
				public function clearCustomerCart($customer_id){
				
					if($customer_id){
						$this->db->query("UPDATE `" . DB_PREFIX . "customer` SET cart = '' WHERE customer_id = '" . (int)$customer_id . "'");
						$this->session->data['cart'] = array();
					}
				
				}			
			]]></add>
		</operation>		
	</file>
</modification>

Linked to this, I also noticed that the default session expiry, set via php.ini is 138 days:

session.gc_maxlifetime = 12000000;

This seems excessive….I’ve set mine to the more reasonable 24 hours.