• 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

php

WordPress Heartbeat API

April 29, 2014 by Webhead

I could not find any extensive documentation on the WordPress Heartbeat API, so I am writing here to remember what I have learned through various tutorials throughout the web.

The Heartbeat API is written in Javascript.  Think of it like a javascript function that gets run every 60 seconds (depending on the interval) which sends data via POST to the server and then the server returns a response.  All of which you can easily hook into.  It was introduced in WordPress 3.6.

Quick Examples

PHP


// server received whatever parameters from when javascript triggered the "heartbeat-send" event.
function myplugin_heartbeat_received( $response, $data ) {
if ( !empty( $data['myplugin_id'] ) ) {
$response['myplugin_got_it'] = true;
}
return $response;
}
add_filter( 'heartbeat_received', 'myplugin_heartbeat_received', 10, 2 );

Javascript


jQuery(document).ready(function($) {
wp.heartbeat.interval= 15;
// set some parameters when it's time to send data to the server
$(document).on('heartbeat-send.myplugin', function(e, data){
data['myplugin_id'] = my_id;
});

// do something when your page gets a response.
$(document).on('heartbeat-tick.myplugin', function(e, data){

if ( data.hasOwnProperty( 'myplugin_got_it' ) ) {
refreshPage();
}
});

});

Heartbeat Pulse Interval

The ‘heartbeat_settings’ filter gets applied in the ‘init’ hook, priority 0 in wp_default_scripts in wp-includes/script-loader.php.  So if you do use the hook on the server-side and want to only run it on certain pages, be prepared to parse the URL because a lot of WP functions aren’t available at that time.  Alternatively you can set the interval in javascript with wp.heartbeat.interval(15) where 15 is seconds.  Note some other tutorials say to use ‘fast’, ‘standard’, ‘slow’, but that didn’t work for me for some reason.

 

Tutorials

I found these tutorials to be the most helpful.

http://code.tutsplus.com/tutorials/the-heartbeat-api-getting-started–wp-32446

http://code.tutsplus.com/tutorials/the-heartbeat-api-changing-the-pulse–wp-32462

http://code.tutsplus.com/tutorials/heartbeat-api-using-heartbeat-in-a-plugin–wp-32496

 

Filed Under: Coding Tagged With: javascript, php, wordpress

edit_users capability on multisite

April 29, 2014 by Webhead

No matter how hard you try, if you add the edit_user capability to a user on multi-site and that user is not a super-admin, the user will not have the edit_user capability.  This is hard-coded into WP_User’s map_meta_cap.  There is a filter so after it’s all done, you can search for it and take it out if you want.

Filed Under: Coding Tagged With: php, 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

Free Web Hosting

November 20, 2013 by Webhead

Today I realized I needed a reliable web host that I can use for development purposes.  Either for demoing to clients or testing things out, it needed to be online, free (because I wouldn’t use it for production), and not go out of business.

Doing some research these are the companies I came across:

Byethost

On a WordPress forum thread one of the comments suggested Byethost.  It’s free, seems to offer a ton of power and space.

http://byethost.com/index.php/free-hosting

Zymic

I don’t know too much about these guys, sorry.

http://www.zymic.com/free-web-hosting/

Koding

This is probably the Facebook for coders.  My first impression of Koding is that it seems to accomplish what GitHub is trying to do.  basically make a social network for coders.  At Koding you get a free VM with full root access.  It’s way more than just a free webhost.  The only drawback (for my current needs) is that the VM shuts down 20 minutes after you log off.  I’ll probably sign up for this later, but for what I need this isn’t it.

000WebHost

These guys are probably the most well known free web hosting company.   They’ve been in the business for a long time and they are well known.  However, be warned, they delete your site with no warning whatsoever.  I created two sites to test them out installing WordPress and some plugins.  A few weeks later I was ready to point my domain there but I couldn’t log in.  The forget password portion simply said the account with my email doesn’t exist.  I assume my account was deleted for inactivity, but I have no idea.

http://www.000webhost.com/features

Filed Under: Server Stuff Tagged With: hosting, php

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
  • Interim pages omitted …
  • Go to page 8
  • Go to Next Page »

Primary Sidebar

Topics

apache apple bootstrap buddypress chrome cloudways cms css debug drupal eCommerce firebug firefox git gmail goDaddy google hosting htaccess html html 5 IE crap image iPad iPhone javascript jquery kinsta linux localization mac os x ms sql mysql open source optimize php php 7.2 svg tinymce woocommerce wordpress wpengine xss yii youtube




Categories

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