mainwp-child/class/class-mainwp-heatmap-tracker.php
2016-02-15 22:08:39 +01:00

424 lines
14 KiB
PHP

<?php
/**
* Class for tracking click heatmap
*
* Uses $wpdb object
*
* @version 1.0
* @author Jeffri Hong
*/
class MainWP_Heatmap_Tracker {
protected static $instance;
protected $server;
protected $dbVersion = 1000;
/**
* Class constructor
*
* @param boolean $checkDb Do checking the database if set to true
*/
public function __construct( $checkDb = false ) {
self::$instance = $this;
$this->server = get_option( 'mainwp_child_server' );
add_action( 'template_redirect', array( $this, 'trackerJs' ) );
add_action( 'wp_ajax_heatmapSaveClick', array( $this, 'saveClickCallback' ) );
add_action( 'wp_ajax_nopriv_heatmapSaveClick', array( $this, 'saveClickCallback' ) );
}
/**
* Get Instance
*/
public static function getInstance() {
if ( self::$instance instanceof HeatmapTracker ) {
return self::$instance;
}
self::$instance = new HeatmapTracker( true );
return self::$instance;
}
/**
* Parse which page we are on using URL
*/
public function getPageObject( $pageUrl ) {
global $wp_rewrite;
// If post type, we are using url_to_postid function
$postId = url_to_postid( $pageUrl );
if ( $postId ) {
$postType = get_post_type_object( get_post( $postId )->post_type );
return array(
'value' => $postId,
'title' => get_the_title( $postId ),
'type' => get_post( $postId )->post_type,
'label' => ( is_array( $postType->labels ) ? $postType->labels['name'] : $postType->labels->name ),
);
}
$path = str_replace( get_site_url(), '', $pageUrl );
$path = trim( $path, '/' );
// If path is empty, then it is front page
if ( empty( $path ) ) {
return array(
'value' => get_option( 'page_on_front' ) ? get_option( 'page_on_front' ) : '',
'title' => '',
'type' => 'front_page',
'label' => __( 'Home Page' ),
);
}
// Otherwise, we will try to match through rewrite or by query
$rewrite = $wp_rewrite->wp_rewrite_rules();
if ( is_array( $rewrite ) && count( $rewrite ) > 0 ) {
foreach ( $rewrite as $match => $query ) {
if ( preg_match( "#^$match#", $path, $matches ) || preg_match( "#^$match#", urldecode( $path ), $matches ) ) {
$query = preg_replace( '!^.*\?!', '', $query );
$query = addslashes( WP_MatchesMapRegex::apply( $query, $matches ) );
parse_str( $query, $query_vars );
break;
}
}
} else {
$query = preg_replace( '!^.*\?!', '', $path );
parse_str( $query, $query_vars );
}
// Workaround for fail pagename rewrite match
if ( isset( $query_vars['pagename'] ) && strpos( $query_vars['pagename'], '?' ) !== false ) {
$query = preg_replace( '!^.*\?!', '', $query_vars['pagename'] );
parse_str( $query, $query_vars );
}
$querypost = new WP_Query( $query_vars );
if ( $querypost->is_date() ) {
if ( $querypost->query_vars['m'] ) {
$date = $querypost->query_vars['m'];
} else if ( $querypost->is_day() ) {
$date = $querypost->query_vars['year'] . zeroise( $querypost->query_vars['monthnum'], 2 ) . zeroise( $querypost->query_vars['day'], 2 );
} else if ( $querypost->is_month() ) {
$date = $querypost->query_vars['year'] . zeroise( $querypost->query_vars['monthnum'], 2 );
} else if ( $querypost->is_year() ) {
$date = $querypost->query_vars['year'];
}
return array(
'value' => $date,
'title' => '',
'type' => 'archive',
'label' => __( 'Archive' ),
);
} else if ( $querypost->is_category() || $querypost->is_tag() || $querypost->is_tax() ) {
$tax_query = $querypost->tax_query->queries;
$taxonomy = get_taxonomy( $tax_query[0]['taxonomy'] );
if ( 'term_id' === $tax_query[0]['field'] ) {
$term_id = $tax_query[0]['terms'][0];
} else if ( 'slug' === $tax_query[0]['field'] ) {
$term_id = get_term_by( 'slug', $tax_query[0]['terms'][0], $taxonomy->name )->term_id;
}
return array(
'value' => $term_id,
'title' => get_term( $term_id, $taxonomy->name )->name,
'type' => $taxonomy->name,
'label' => ( is_array( $taxonomy->labels->name ) ? $taxonomy->labels['name'] : $taxonomy->labels->name ),
);
} else if ( $querypost->is_search() ) {
return array(
'value' => $querypost->query_vars['s'],
'title' => '',
'type' => 'search',
'label' => __( 'Search' ),
);
} else if ( $querypost->is_home() ) {
return array(
'value' => '',
'title' => '',
'type' => 'home',
'label' => __( 'Blog Home Page' ),
);
}
}
/**
* Save click callback for AJAX processing
*/
public function saveClickCallback() {
if ( ! wp_verify_nonce( $_POST['nonce'], 'heatmapSaveClick' ) ) {
return false;
}
$data = isset( $_POST['data'] ) && is_array( $_POST['data'] ) ? $_POST['data'] : array();
$storeData = get_option( 'mainwp_child_click_data' );
if ( ! is_array( $storeData ) ) {
$storeData = array();
}
foreach ( $data as $d ) {
$coord = isset( $d['coord'] ) && preg_match( '/^\d+,\d+$/', $d['coord'] ) ? explode( ',', $d['coord'] ) : null;
$type = isset( $d['type'] ) && preg_match( '/^(left|right|middle)$/', $d['type'] ) ? $d['type'] : 'left';
$viewport = isset( $d['viewport'] ) && preg_match( '/^\d+,\d+$/', $d['viewport'] ) ? explode( ',', $d['viewport'] ) : null;
$element = isset( $d['element'] ) && preg_match( '/^[A-Za-z0-9#:().>_-]+$/is', $d['element'] ) ? $d['element'] : null;
$attr = array();
if ( isset( $d['url'] ) && $d['url'] ) {
$attr['url'] = esc_url_raw( $d['url'] );
}
if ( isset( $d['title'] ) && $d['title'] ) {
$attr['title'] = sanitize_text_field( $d['title'] );
}
if ( isset( $d['alt'] ) && $d['alt'] ) {
$attr['alt'] = sanitize_text_field( $d['alt'] );
}
if ( isset( $d['text'] ) && $d['text'] ) {
$attr['text'] = sanitize_text_field( $d['text'] );
}
$useragent = $_SERVER['HTTP_USER_AGENT'];
$object = $this->getPageObject( $_SERVER['HTTP_REFERER'] );
if ( ! is_null( $coord ) && ! is_null( $viewport ) && ! is_null( $element ) ) {
$storeData[] = array(
'url' => $_SERVER['HTTP_REFERER'],
'object' => $object,
'coord' => $coord,
'viewport' => $viewport,
'type' => $type,
'element' => $element,
'attr' => $attr,
'useragent' => $useragent,
'date' => current_time( 'mysql' ),
);
}
}
MainWP_Helper::update_option( 'mainwp_child_click_data', $storeData );
// Customize when we need to send the data
$this->sendClick();
exit;
}
public function sendClick() {
$url = $this->server . 'admin-ajax.php';
$clickData = get_option( 'mainwp_child_click_data' );
$key = get_option( 'mainwp_child_pubkey' );
if ( ! is_array( $clickData ) ) {
return false;
}
// send 1000 record per time to fix memory bug
$sendNow = array();
if ( count( $clickData ) > 1000 ) {
for ( $i = 0; $i < 1000; $i ++ ) {
$sendNow[] = $clickData[ $i ];
}
} else {
$sendNow = $clickData;
}
$timestamp = time();
$signature = $this->createSignature( $key, $timestamp, $sendNow );
$params = array(
'headers' => array(
'Referer' => site_url(),
),
'body' => array(
'timestamp' => $timestamp,
'signature' => $signature,
'data' => base64_encode( serialize( $sendNow ) ),
'action' => 'heatmapSendClick',
),
'timeout' => 30,
);
if ( strpos( $url, 'https://' ) === 0 ) {
$params['sslverify'] = false;
}
$request = wp_remote_post( $url, $params );
if ( is_array( $request ) && isset( $request['response']['code'] ) && 200 === (int) $request['response']['code'] ) {
if ( count( $clickData ) > 1000 ) {
$saveData = array();
$clickDataLength = count( $clickData );
for ( $i = 1000; $i < $clickDataLength; $i ++ ) {
$saveData[] = $clickData[ $i ];
}
MainWP_Helper::update_option( 'mainwp_child_click_data', $saveData );
} else {
delete_option( 'mainwp_child_click_data' );
}
}
}
public function checkSignature( $signature, $timestamp, $data ) {
$key = get_option( 'mainwp_child_pubkey' );
if ( ! $key ) {
return false;
}
$createSign = $this->createSignature( $key, $timestamp, $data );
return ( $signature === $createSign );
}
public function createSignature( $key, $timestamp, $data ) {
$datamd5 = md5( $timestamp . base64_encode( serialize( $data ) ) );
$signature = md5( $key . $datamd5 );
return $signature;
}
/**
* Whether the heatmap is requested to display or not
*/
public function displayHeatmap() {
return ( isset( $_REQUEST['heatmap'] ) && '1' === $_REQUEST['heatmap'] );
/*return ( ( isset($_REQUEST['heatmap']) && $_REQUEST['heatmap'] == '1' ) &&
( isset($_REQUEST['signature']) && isset($_REQUEST['timestamp']) && isset($_REQUEST['data']) &&
$this->checkSignature($_REQUEST['signature'], $_REQUEST['timestamp'], $_REQUEST['data']) )
);*/
}
/**
* Add tracker Javascript
*/
public function trackerJs() {
if ( ! is_admin() ) {
wp_enqueue_script( 'jquery' );
wp_enqueue_script( 'heatmapTracker', plugins_url( '/js/tracker.js', dirname( __FILE__ ) ) );
if ( $this->displayHeatmap() ) {
wp_enqueue_script( 'heatmapJs', plugins_url( '/js/heatmap.js', dirname( __FILE__ ) ) );
wp_enqueue_script( 'heatmapInit', plugins_url( '/js/heatmapinit.js', dirname( __FILE__ ) ) );
}
add_action( 'wp_head', array( $this, 'trackerJsInline' ), 1 );
}
}
/**
* Add necessary inline tracker Javascript
*/
public function trackerJsInline() {
echo '<script type="text/javascript">';
echo 'var trackerAjaxUrl="' . admin_url( 'admin-ajax.php' ) . '"; var trackerNonce="' . wp_create_nonce( 'heatmapSaveClick' ) . '";';
if ( $this->displayHeatmap() ) {
wp_deregister_script( 'admin-bar' );
wp_deregister_style( 'admin-bar' );
remove_action( 'wp_footer', 'wp_admin_bar_render', 1000 );
remove_action( 'wp_head', '_admin_bar_bump_cb' );
$pageUrl = sprintf( '%s%s', preg_replace( '#^((http|https)://([^/]+)).*#is', '$1', site_url() ), $_SERVER['REQUEST_URI'] );
$pageUrl = preg_replace( '#(&|\?)heatmap(|_start|_end|_browser|_browser_version|_platform|_width)=?([^&]*)#is', '', $pageUrl );
$page = $this->getPageObject( $pageUrl );
$start = isset( $_GET['heatmap_start'] ) && preg_match( '/^[2][01][0-9]{2}[\/\-][01][0-9][\/\-][0123][0-9]$/is', $_GET['heatmap_start'] ) ? $_GET['heatmap_start'] : null;
$end = isset( $_GET['heatmap_end'] ) && preg_match( '/^[2][01][0-9]{2}[\/\-][01][0-9][\/\-][0123][0-9]$/is', $_GET['heatmap_end'] ) ? $_GET['heatmap_end'] : null;
$browser = isset( $_GET['heatmap_browser'] ) ? strtolower( $_GET['heatmap_browser'] ) : '';
$browserVersion = isset( $_GET['heatmap_browser_version'] ) ? $_GET['heatmap_browser_version'] : '';
$platform = isset( $_GET['heatmap_platform'] ) ? strtolower( $_GET['heatmap_platform'] ) : '';
$width = isset( $_GET['heatmap_width'] ) && is_numeric( $_GET['heatmap_width'] ) ? $_GET['heatmap_width'] : '';
$args = array();
if ( $start ) {
$args['start'] = $start;
}
if ( $end ) {
$args['end'] = $end;
}
if ( $browser ) {
$args['browser'] = $browser;
}
if ( $browser && $browserVersion ) {
$args['browserVersion'] = $browserVersion;
}
if ( $platform ) {
$args['platform'] = $platform;
}
if ( $width ) {
$args['width'] = $width;
}
$this->generateHeatmap( $page['type'], $page['value'], $args );
}
echo '</script>';
}
/**
* Generate heatmap, print click data variable (wrap it on <script></script>)
*
* Available args:
* string $start Start date (d/m/Y)
* string $end End date (d/m/Y)
* string $browser Filter to only click by specified browser, see getBrowser method for list of supported browser name
* string $browserVersion The specific browser version to target at, could use some wildcard (for example: 7.*)
* string $platform Filter to only click by specified platform, see getBrowser method for list of supported platform name
* int $width Filter to width
*
* @param string $object_type Object type
* @param int|string $object_value Object value
* @param array $args Additional arguments
*
*/
public function generateHeatmap( $object_type, $object_value, $args ) {
global $wpdb;
$defaults = array(
'start' => '',
'end' => '',
'browser' => 'all',
'browserVersion' => 'all',
'platform' => 'all',
'width' => 0,
);
$args = wp_parse_args( $args, $defaults );
extract( $args );
$data = array();
$data['object_type'] = $object_type;
$data['object_value'] = $object_value;
$data['start_date'] = $start;
$data['end_date'] = $end;
$data['browser'] = $browser;
$data['platform'] = $platform;
$data['width'] = $width;
$url = $this->server . 'admin-ajax.php';
$key = get_option( 'mainwp_child_pubkey' );
$timestamp = time();
$signature = $this->createSignature( $key, $timestamp, $data );
$params = array(
'headers' => array(
'Referer' => site_url(),
),
'body' => array(
'timestamp' => $timestamp,
'signature' => $signature,
'data' => base64_encode( serialize( $data ) ),
'action' => 'heatmapGetClickData',
),
'timeout' => 60,
);
if ( strpos( $url, 'https://' ) === 0 ) {
$params['sslverify'] = false;
}
$request = wp_remote_post( $url, $params );
if ( is_array( $request ) ) {
$clicks = array();
if (! empty($request['body']) ) {
if (preg_match('/<heatmap>(.*)<\/heatmap>/', $request['body'], $results) > 0) {
$result = $results[1];
$clicks = json_decode($result);
}
}
$clickData = array();
if ( is_array( $clicks ) ) {
foreach ($clicks as $click) {
$clickData[] = array(
'x' => $click->x,
'y' => $click->y,
'w' => $click->w,
'h' => $click->h,
);
}
}
?>
var heatmapClick = <?php echo json_encode( $clickData ) ?>;
var heatmapError = 0;
<?php
} else {
?>
var heatmapError = 1;
<?php
}
}
}
?>