Web Design and Build

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.

Querying WordPress serialized custom post data Last updated:20 March 2013

Had a bit of a head-scratcher on this one this afternoon…

I have a couple of custom post types set up, category and product, and a number of custom field attached to each of these, managed by Elliot Condon’s outstanding Advanced Custom Fields plugin. In this case, I have a Relationship field defined on product, which allows the user to assign a product to one or more category. All very lovely, but I then started on a situation where I wanted to show all products which were related to a given category.

The WP codex is a little light on examples, here, but I eventually triumphed with the following:

$args = array( 
   'post_type' 		=> 'product', 
   'posts_per_page'	=> -1,
   'meta_query'  	=> array(
			      array(
				     'key'           => 'category',
				     'value'         => '"'.$category.'"',//quotes to make sure category 23 does not match category 123, 230 etc
				     'compare'       => 'LIKE'
			      )
			   )
);
//get products
$wp_query = new WP_Query( $args );

To explain this a little, ‘category’ is the meta_key assigned to the field on product, and $category is the ID of the category that I’m passing in. It took me a little while to understand the SQL generated here. What happens is that meta_query casts the serialized data into characters, like this, where $category = 23:

SELECT wp_posts.* FROM wp_posts INNER JOIN wp_postmeta ON (wp_posts.ID = wp_postmeta.post_id) WHERE 1=1 
AND wp_posts.post_type = 'product' 
AND (wp_posts.post_status = 'publish' OR wp_posts.post_status = 'private') 
AND ( (wp_postmeta.meta_key = 'category' AND CAST(wp_postmeta.meta_value AS CHAR) LIKE '%\"23\"%') ) 
GROUP BY wp_posts.ID 
ORDER BY wp_posts.post_date DESC

Once I realised what was going on it then becomes straightforward to use LIKE to find matching records. However, there’s a potential gotcha in there, which, in my example, would manifest if there was another category with and ID of 123, 230, 2300 etc etc. Without the quotes wrapped around my $category variable here, then the query would return products linked to any of these categories as well. It seems as if meta_query should allow an ‘in_array’ type comparison here, as the existing approach feels a bit clunky.