• Skip to primary navigation
  • Skip to main content
  • Skip to primary sidebar

My Monkey Do

A Log of Coding Solutions

  • Home
  • Web Hosts
  • Tools
  • About

wordpress

Migrating Multisite to Single Site

April 18, 2014 by Webhead

I needed to migrate a site on a multisite installation to a single installation.  This article did the trick for the most part.

 

http://beacon.wharton.upenn.edu/404/2011/02/the-return-from-multisite/

Filed Under: Coding Tagged With: mysql, wordpress

How does WordPress redirect mistyped URLs to the correct one?

March 20, 2014 by Webhead

Well, I’m not sure exactly because it is A LOT of code, but to make it stop, you can use the filter ‘redirect_canonical’ and return false.  like so:


add_filter( 'redirect_canonical', '__return_false');

So say you have a post named “mission” at http://mydomain.com/mission, but for some reason you really want to go to http://mydomain.com/mission-control/ instead of being redirected to the post, returning false with redirect_canonical will allow you to visit the url you want.

‘redirect_canonical’ is actually an SEO feature built into WordPress.  This function redirects duplicate content like www.mydomain.com to mydomain.com among other things.  Disable it with caution.

Filed Under: Coding Tagged With: php, wordpress

Google Analytics Measurement Protocol for Subscriptions

January 11, 2014 by Webhead

Send Analytics Data Directly From Your Server

Google Analytics Measurement Protocol is available for Google’s Universal Analytics.  It was released in the first part of 2013 and is as of this writing still in public beta.  It allows developers to make HTTP requests to send raw data directly to Google Analytics.

Subscription Renewals for eCommerce Sites

I found and used the Measurement Protocol specifically to solve a problem with tracking subscription renewals.  I was using WooCommerce and the Subscription extension as my eCommerce subscription site and found a couple things not working for me when I was using the built-in Analytics eCommerce tracking.

For one, I had Universal Analytics but WooCommerce was using the legacy ga.js analytics.  So nothing was being tracked.

Secondly, the WooCommerce code to track purchases was done on the Thank You page.  While this is fine for normal purchases, I wanted to track renewals via the wp cron jobs also.

How to Use Google Measurement Protocol in WordPress and WooCommerce

First, let me say at the time of this writing there are very little examples on implementing the code.  While Google’s documentation is great, I still had questions.  This post on using the measurement protocol from Stu Miller got me started and almost completed the work for me.  I am in no way an expert at WooCommerce or Subscriptions so I didn’t want to put this code into a plugin.  Also, WooCommerce may soon update their analytics plugin making this code obsolete.

1. The CID – The Client Id

The most confusing thing about the Measurement Protocol is how to link up the user to Google’s “anonymous” user.  As of this writing Google is intending to have a user ID field, but it is not yet available.  From the google groups forum and the documentation, the CID field should be either a UUID Version 4 or their legacy anonymous identifier format “X.Y” where each are 32-bit random numbers.  The X and Y are actually the Google assigned random id and the timestamp which the id was assigned as noted in this stackoverflow post.

So to link a Google Analytics user with your user/subscriber you would need to parse the “_ga” cookie and save the id.timestamp portion like I do in my_ga_parse_user() and my_setup_new_user().

2. The Renewal Order (WooCommerce)

The second most confusing thing (for me anyway) was recording the correct order.  In WooCommerce every subscription renewal creates a new order.  In my case the original order is a 7 day free trial and then $10 every month after that.  So the original order’s total price along with the transaction is $0.  The renewal order should be $10, but analytics was not recording it.  The reason is that when a renewal order is created almost all of the original orders postmeta data is copied into the renewal order.  This means that _ga_tracked is also copied. In my_ga_track_payment() the _ga_tracked postemeta is checked first to make sure an order isn’t recorded more than once.  If this is copied to the renewal order the renewal order will never get tracked.  The function hooked to the  woocommerce_subscriptions_renewal_order_meta_query filter solved this problem.

3. Debugging

I had a tough time trying things out in subscriptions because a renewal order is created once a day at most.  To get around this I created my_ga_debug_next_payment() to speed up the payment process.  This method sets the next payment date to 5 minutes from the current time.  I was using the Stripe payment gateway so I’m not sure if this will work for PayPal or any other gateways.

Here’s the Code


<?php
/**
* See https://groups.google.com/forum/#!msg/google-analytics-measurement-protocol/rE9otWYDFHw/8JlJJV-UmKcJ
* a person from Google says cid should be X.Y format.
* and https://developers.google.com/analytics/devguides/collection/protocol/v1/devguide
* Apparently whether hit is recorded or not 200 is returned.
* Returns true if ga was contacted.
*/
function my_ga_fire_hit( $user_id, $data ) {
if ( !defined( 'GOOGLE_ANALYTICS_ID' ) )
return;

$defaults = array(
'v' => 1,
'tid' => GOOGLE_ANALYTICS_ID,
'cid' => my_ga_user_id($user_id)
);

$payload_data = array_merge($defaults, $data);
$endpoint = 'https://ssl.google-analytics.com/collect?';
$parameters = http_build_query( $payload_data );
$result = wp_remote_post( $endpoint . $parameters );
if ( isset( $result['response'] ) ) {
return ( $result['code'] == 200 );
}
return false;
}

/**
* Save the google analytics UUID.
*/
function my_ga_parse_user($my_user_options) {
if ( is_user_logged_in() && isset($_COOKIE['_ga'])) {
$my_user_options = get_user_meta($user_id, 'my_user_options', true);
if ( empty($my_user_options) ) {
list($version,$domainDepth, $id, $ts) = split('[.]', $_COOKIE["_ga"], 4);
$contents = array('version' => $version, 'domainDepth' => $domainDepth, 'cid' => $id . '.' . $ts);

//3rd spot is the cid according to multiple sources
$my_user_options['ga_cid'] = $contents['cid'];

update_user_meta( $user_id, 'my_user_options', $my_user_options);
}
}
}
add_action('template_redirect', 'my_ga_parse_user');

/**
* Return the ga cid. (or what will be called user_id)
*/
function my_ga_user_id($user_id) {
$my_user_options = get_user_meta($user_id, 'my_user_options', true);
$cid = isset( $my_user_options['ga_cid'] ) ? $my_user_options['ga_cid'] : '';
if ( empty( $cid ) ) {
$my_user_options = my_ga_parse_user($my_user_options);
$cid = isset( $my_user_options['ga_cid'] ) ? $my_user_options['ga_cid'] : '';
}
return $cid;
}

/**
* Don't copy over _ga_tracked meta.
*/
function my_ga_remove_renewal_order_meta( $order_meta_query, $order_id ) {
$order_meta_query .= " AND `meta_key` NOT IN ("
. "'_ga_tracked' ) ";
return $order_meta_query;
}
add_filter ( 'woocommerce_subscriptions_renewal_order_meta_query', 'my_ga_remove_renewal_order_meta', 10, 2);

/**
* Let ga know a subscription payment has been processed.
*/
function my_ga_track_payment( $order_id ) {
$order = new WC_Order( $order_id );
if ( empty($order) || get_post_meta( $order->id, '_ga_tracked', true ) == 1 || !defined('GOOGLE_ANALYTICS_ID') )
return;

$tracking_id = GOOGLE_ANALYTICS_ID;
if ( empty( $tracking_id ) )
return;

$user_id = $order->customer_user;

my_ga_fire_hit( $user_id, array(
't' => 'transaction', // Transaction hit type.
'ti' => $order->get_order_number(), // transaction ID. Required.
'ta' => get_bloginfo('name'), // Transaction affiliation.
'tr' => $order->get_total(), // Transaction revenue.
// Transaction shipping. (not required)
'tt' => $order->get_total_tax() // Transaction tax.
) );

if ( $order->get_items() ) {
foreach ( $order->get_items() as $item ) {
$_product = $order->get_product_from_item( $item );
$_category = "";
if ( isset( $_product->variation_data ) ) {

$_category .= woocommerce_get_formatted_variation( $_product->variation_data, true );

}
else {
$out = array();
$categories = get_the_terms($_product->id, 'product_cat');
if ( $categories ) {
foreach ( $categories as $category ){
$out[] = $category->name;
}
}
$_category .= join( "/", $out);
}
$item_name = $item['name'];
// If we have a child renewal order, we need the original order's ID
if ( WC_Subscriptions_Renewal_Order::is_renewal( $order_id, array( 'order_role' => 'child' ) ) ) {
$original_order_id = WC_Subscriptions_Renewal_Order::get_parent_order_id( $order_id );
$original_order = new WC_Order( $original_order_id );
$order_items = WC_Subscriptions_Order::get_recurring_items( $original_order );
if ( !empty( $order_items ) ) {
$order_item = end($order_items);
$item_name = sprintf( __( 'Renewal of "%s"', 'lis' ), $order_item['name'] );
}
}

my_ga_fire_hit( $user_id, array(
't' => 'item', // Transaction hit type.
'ti' => $order->get_order_number(), // transaction ID. Required.
'in' => $item_name, // Item name. Required.
'ip' => $order->get_item_total( $item ), // Item price.
'iq' => $item['qty'], // Item quantity.
'ic' => $_product->get_sku() ? __( 'SKU:', 'woocommerce' ) . ' ' . $_product->get_sku() : $_product->id, // Item code / SKU.
'iv' => $_category // Item variation / category.
) );
}
}

update_post_meta( $order->id, '_ga_tracked', 1 );

}
add_action( 'woocommerce_payment_complete', 'my_ga_track_payment');

/*
// set next payment ot be in 5 minutes.
function my_ga_debug_next_payment() {
$subscription_key = '1234_5678';
$user_id = '1';

$next_payment_date = WC_Subscriptions_Manager::get_next_payment_date($subscription_key, $user_id, 'timestamp');
$new_next_payment_date = strtotime("+5 minutes");

if ($next_payment_date > strtotime("+7 minutes") ) {
//update the next payment date for referrer
$ret = WC_Subscriptions_Manager::update_next_payment_date($new_next_payment_date, $subscription_key, $user_id);
}

}
add_action('init', 'my_ga_debug_next_payment');
*/

Filed Under: Coding Tagged With: eCommerce, google, google analytics, php, woocommerce, wordpress

WPEngine for WordPress Hosting Worth It?

January 9, 2014 by Webhead

This is not so much of a review, but more of how I came to a decision on WPEngine. 

At $29/ month for the Personal plan I would not have even looked at WPEngine a year ago.  Why pay that much for hosting when you can find $5/month hosting around every corner?  Updates?  I can do that.  Caching? Backups?  check and check.  What does WPEngine have to offer?

While in the proposal stage of acquiring a new client, this client got hacked.  Luckily to fix the hack WordPress just need to be upgraded.  But instead of redesigning the site, the top priority became finding a new host that offers better security.  This client was on a reputable host, so $5/month hosting like HostGator or GoDaddy would not satisfy the client’s peace of mind.

Finding WPEngine

My favorite WordPress focused company, WooThemes, had a recommended hosts page.  Among the hosts were Pressable (previously known as ZippyKid ) and WPEngine.  Pressable was the cheaper of the two offering SSL and CDN on their cheapest plan.  As ZippyKid they also touted having great security as a selling point.  However, as Pressable, they had no info whatsoever about their security.  Not even their “malware scanning”!  So wait, how can we justify to the client that Pressable is any better than their previous host?  Crap!  (I know no host is hack-proof, but we gotta have a reason for choosing a specific host).

Enter research into WPEngine.  The first thing I did on their website was search for “security” in their blog.  What surprised me was the amount of posts on their blog about security.  Do a search on security on their site and you’ll see what I mean.  Not only do they talk about recent security issues, but how the community is dealing with security issues and details on how their security system and processes are protecting their clients.  That in itself gave me peace of mind that these guys prioritize security.  The icing on the cake was reading “WordPress Hacked? We’ll fix it free!“.

Grass is Always Greener on the Other Side

I usually have buyers remorse especially when I could have gotten something cheaper and don’t really utilize all the features I have purchased.  But last year, before I tried WPEngine, I had a client that didn’t have a website yet, needed SSL and was OK with spending some money on quality hosting.  So I went with Pressable with this previous client (especially since the Personal plan at WPEngine doesn’t support SSL).  I have since migrated this client to WPEngine.  Below are some comparisons between Pressable and WPEngine (as of this posting).

Pressable vs WPEngine

  • Backups
    • WPEngine – At the click of a button, free.  Also automatically scheduled daily for  you.
    • Pressable – Inactive by default.  Need to back up to Amazon or Rackspace.  (in other words, not exactly free).
  • Domain settings
    • WPEngine – Doesn’t handle email or any of your domain settings
    • Pressable – If you want, you can direct your nameservers to Pressable and they can adjust your domain settings (like where to point your email) for you.
  • Git Support
    • WPEngine – Deploy your website using git ( i haven’t tried this yet)
    • Pressable – None.
  • Error Logs
    • WPEngine – View your php error logs from the User Portal
    • Pressable – None.
  • Stats
    • WPEngine – Visits and Bandwidth stats for you to monitor and download.
    • Pressable – None.
  • Speed
    • WPEngine – I have tested about a dozen different websites so far and all have a load time of less than 1 second from within the US.
    • Pressable – I have tested one website on here and the load time is above 2.5 seconds.

Both Pressable and WPEngine are great, but dollar for dollar WPEngine seems much more worth it.  If you have multiple clients, the Professional Plan makes it hands down worth it since you can install SSL and use a CDN for free.  Each install breaks down to $10/month.  You may be thinking you can do caching, and backups on your own, but what if something goes wrong with those plugins?  How many times have you actually restored a backup from that plugin?  How many times have you fixed a hack or prevented one?  How much is a peaceful mind worth?  WPEngine can handle all this for you.

Pre-pay for WP ENGINE HOSTING for 1 year and GET 2 MONTHS FREE!

Happily Ever After

As you know, I didn’t go to WPEngine because of the speed, but I was curious to see what differences it could make by simply moving a site over.  I was pleasantly surprised to see the site loaded almost 40% faster.  And believe me, it’s noticeable for this particular site.

To get a more accurate reading of the speed changes you should sign up for a free account at Pingdom and monitor your website for a week or so.  Then sign up to WPEngine (60 day free trial) and again try it at pingdom.  If you’re not happy with the results you can always back out of WPEngine.

Original Site
Moved to WPEngine
Moved to WPEngine

 

Update: More on Security

After using WPEngine for about 6 months I am fully satisfied with the security on WPEngine for WordPress websites.  WPEngine stays on top of plugin vulnerabilities and they scan your installation daily.   here are some examples:

  • TimThumb script:  If you install a theme or plugin with the TimThumb script (a popular PHP script used to resize images known for having some huge vulnerabilities in the past) WPEngine will detect what version it is, and if it’s not the newest version, they will automatically update it.
  • JetPack plugin:  Jetpack recently had a vulnerability in their email sharing feature.  WPEngine was on top of it notified their customers of it while disabling that feature for them.

Also, WPEngine hosting is secure enough to be PCI compliant “as long as no payment card data is stored, hosted, or otherwise processed by WP Engine…”.   See more at WPEngine and PCI Compliance

Update:  Speed

WPEngine now offers a tool to test the speed of your website.

 

Disclaimer:  I have become a WPEngine affiliate because I do believe in their product.  Links to WPEngine on this site are probably affiliate links.

Filed Under: Off the Shelf, Random Thoughts, Server Stuff Tagged With: hosting, optimize, wordpress, wpengine

WordPress has its own magic quotes

November 26, 2013 by Webhead

Apparently WordPress has it’s own magic quotes.  It is not included everywhere so you need to check if the function which WordPress uses exists.

More details in the stack overflow post.

http://stackoverflow.com/questions/16748663/what-does-wordpress-do-if-magic-quotes-are-turned-off-at-server

Filed Under: Coding Tagged With: wordpress

Easy WordPress Localization

November 13, 2013 by Webhead

If you want to localize a plugin or theme or even just change some wording around, or if you are starting from scratch the easiest thing to do is install the Codestyle Localization plugin.

If starting from scratch, just  make sure your plugin or theme loads the file using

[prettify class=”php”] <?php load_plugin_textdomain( $domain, $abs_rel_path, $plugin_rel_path ) ?> [/prettify]

Once that line of code is in the plugin or theme go to Tools->Localization and you should see your plugin or theme listed.

Find your plugin or theme and click on the Add New Language buttton.

Click Rescan.  Have it scan the files.

Click Edit

Click on generate mo-file.

Then you’re all done.

Filed Under: Coding, Off the Shelf, Tools Tagged With: localization, php, wordpress

  • « Go to Previous Page
  • Go to page 1
  • Go to page 2
  • Go to page 3
  • Go to page 4
  • Go to page 5
  • Go to page 6
  • Interim pages omitted …
  • Go to page 12
  • Go to Next Page »

Primary Sidebar

Topics

apache apple block editor chrome cms css debug eCommerce embed firebug firefox git gmail goDaddy google hosting htaccess html html 5 IE crap image iPad iPhone javascript jquery linux localization macOS mac os x ms sql mysql open source optimize php php 7.2 rest api seo svg tinymce woocommerce wordpress wpengine xss yii youtube




Categories

  • Coding
  • Off the Shelf
  • Plugins
  • Random Thoughts
  • Server Stuff
  • Tools
  • Uncategorized