<?php /* * Plugin Name: Ajax Pageview * Description: Contabiliza os pageviews por ajax * Author: hacklab/ * Author URI: https://hacklab.com.br/ * Version: 2.0.3 */ class AjaxPageviews { /** * * @var wpdb */ static $wpdb; static $post_types = []; static function init() { global $wpdb; self::$wpdb = $wpdb; add_action('wp_enqueue_scripts', [__CLASS__, 'scripts']); add_action('wp_ajax_nopriv_ajaxpv', [__CLASS__, 'action']); add_action('wp_loaded', [__CLASS__, 'set_post_types']); add_action('add_meta_boxes', [__CLASS__, 'add_meta_box_pageviews']); self::db_updates(); } static function scripts() { if(!is_user_logged_in() && (is_single() || is_home() || is_front_page())){ wp_enqueue_script('ajax-pageview', plugin_dir_url(__FILE__) . '/ajax-pv.js', 'jquery', false, true); wp_localize_script('ajax-pageview', 'ajaxurl', admin_url('admin-ajax.php')); wp_localize_script('ajax-pageview', 'ajaxpv', base_convert(get_the_ID(), 10, 36)); $post_type_hash = self::post_type_hash(is_front_page() ? 'frontpage' : get_post_type()); wp_localize_script('ajax-pageview', 'ajaxpt', $post_type_hash); } } static function activation() { // https://codex.wordpress.org/Creating_Tables_with_Plugins global $wpdb; $table_name = $wpdb->prefix . "pageviews"; $charset_collate = $wpdb->get_charset_collate(); $sql = "CREATE TABLE $table_name ( id bigint NOT NULL AUTO_INCREMENT, time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL, post_type varchar(20) NOT NULL, post_id bigint(20) unsigned NOT NULL, PRIMARY KEY (id) ) $charset_collate;"; require_once( ABSPATH . 'wp-admin/includes/upgrade.php' ); dbDelta($sql); } static function post_type_hash($post_type){ return md5(__METHOD__ . $post_type); } static function set_post_types(){ $post_types = get_post_types(); $post_types['frontpage'] = 'frontpage'; foreach($post_types as $post_type){ $hash = self::post_type_hash($post_type); self::$post_types[$hash] = $post_type; } } static function action() { if (isset($_POST['ajaxpv']) && isset($_POST['ajaxpt'])) { if (!self::is_bot()) { $post_id = base_convert($_POST['ajaxpv'], 36, 10); $post_type = isset(self::$post_types[$_POST['ajaxpt']]) ? self::$post_types[$_POST['ajaxpt']] : null; if ($post_type && self::get_post_id_from_referer() == $post_id) { self::add_pageview($post_id, $post_type); } } } } static function add_pageview($post_id, $post_type) { $table_name = self::$wpdb->prefix . "pageviews"; self::$wpdb->insert($table_name, ['post_id' => $post_id, 'post_type' => $post_type]); } static function get_post_id_from_referer() { if (isset($_SERVER['HTTP_REFERER'])) { return url_to_postid($_SERVER['HTTP_REFERER']); } } static function get_top_viewed($num = 10, $args = []){ $args += [ 'post_type' => 'post', 'from' => date('Y-m-d', strtotime('-14 days')), 'to' => null ]; $cache_key = md5(__METHOD__ . json_encode($args)); $found = false; $result = wp_cache_get($cache_key, __CLASS__, false, $found); if($found){ return $result; } $table_name = self::$wpdb->prefix . "pageviews"; $where = []; if($args['post_type']){ if(!is_array($args['post_type'])){ $args['post_type'] = [$args['post_type']]; } $args['post_type'] = array_map(function($item) { return "'$item'"; }, $args['post_type']); $args['post_type'] = implode(",", $args['post_type']); $where[] = "post_type IN ({$args['post_type']})"; } if($args['from']){ $where[] = "time >= '{$args['from']}'"; } if($args['to']){ $where[] = "time <= '{$args['to']}'"; } $where = implode(' AND ', $where); if($where){ $where = "WHERE $where"; } $limit = ''; if($num != -1){ $limit = "LIMIT {$num}"; } $sql = " SELECT COUNT(id) AS num, post_id FROM {$table_name} {$where} GROUP BY post_id ORDER BY num DESC $limit"; $ids = self::$wpdb->get_results($sql); wp_cache_add($cache_key, $ids, __CLASS__, 15 * MINUTE_IN_SECONDS); return $ids; } /** * Retrieves the top viewed posts by terms. * * @param int $num The number of top viewed posts to retrieve. * @param array $args { * Optional. An array of arguments to customize the query. * * @type string|array $post_type The post type(s) to include. * @type string $taxonomy The taxonomy to filter by. * @type string|array $terms The term(s) IDs to filter by. * @type string $from The start date for the date range. * @type string $to The end date for the date range. * @type string $co_author The guest author to filter by. * * @return array An array of post IDs ordered by the number of views. */ static function get_top_viewed_by_terms( $num = 10, $args = [] ) { $args += [ 'post_type' => 'post', 'taxonomy' => null, 'terms' => null, 'from' => date( 'Y-m-d', strtotime( '-14 days' ) ), 'to' => null, 'co_author' => null, 'post_from' => null, 'post_to' => null ]; $cache_key = md5( __METHOD__ . json_encode( $args ) ); $found = false; $result = wp_cache_get( $cache_key, __CLASS__, false, $found ); if ( $found ) { return $result; } $table_name = self::$wpdb->prefix . "pageviews"; $where = []; $join = ""; if ( $args['post_from'] || $args['post_to'] || $args['post_type'] ) { $join = "INNER JOIN " . self::$wpdb->prefix . "posts p ON pv.post_id = p.ID"; } if ( $args['post_type'] ) { if ( ! is_array( $args['post_type'] ) ) { $args['post_type'] = [$args['post_type']]; } $args['post_type'] = array_map( function( $item ) { return "'$item'"; }, $args['post_type'] ); $args['post_type'] = implode( ",", $args['post_type'] ); $where[] = "p.post_type IN ({$args['post_type']})"; } if ( $args['post_from'] && $args['post_to'] ) { $where[] = "p.post_date BETWEEN '{$args['post_from']}' AND '{$args['post_to']}'"; } else if ($args['post_from']) { $where[] = "p.post_date >= '{$args['post_from']}'"; } else if ($args['post_to']) { $where[] = "p.post_date <= '{$args['post_to']}'"; } if ( $args['from'] ) { $where[] = "pv.time >= '{$args['from']}'"; } if ( $args['to'] ) { $where[] = "pv.time <= '{$args['to']}'"; } if ( $args['co_author'] ) { if ( substr( $args['co_author'], 0, 4 ) !== 'cap-' ) { $args['co_author'] = 'cap-' . $args['co_author']; } $co_author_subquery = " SELECT p.ID FROM " . self::$wpdb->prefix . "posts p INNER JOIN " . self::$wpdb->prefix . "pageviews pv ON p.ID = pv.post_id INNER JOIN " . self::$wpdb->prefix . "term_relationships tr ON p.ID = tr.object_id INNER JOIN " . self::$wpdb->prefix . "term_taxonomy tt ON tr.term_taxonomy_id = tt.term_taxonomy_id INNER JOIN " . self::$wpdb->prefix . "terms t ON tt.term_id = t.term_id WHERE p.post_status = 'publish' AND tt.taxonomy = 'author' AND t.slug = '{$args["co_author"]}' GROUP BY p.ID ORDER BY COUNT(pv.id) DESC"; $where[] = "pv.post_id IN ($co_author_subquery)"; } if ( $args["taxonomy"] && $args["terms"] ) { if ( ! is_array( $args["terms"] ) ) { $args["terms"] = [$args["terms"]]; } $args['terms'] = implode( ",", array_map( 'intval', $args['terms'] ) ); $subquery_taxonomy = " SELECT object_id FROM " . self::$wpdb->prefix . "term_relationships INNER JOIN " . self::$wpdb->prefix . "term_taxonomy ON " . self::$wpdb->prefix . "term_taxonomy.term_taxonomy_id = " . self::$wpdb->prefix . "term_relationships.term_taxonomy_id INNER JOIN " . self::$wpdb->prefix . "terms ON " . self::$wpdb->prefix . "terms.term_id = " . self::$wpdb->prefix . "term_taxonomy.term_id WHERE " . self::$wpdb->prefix . "term_taxonomy.taxonomy = '" . esc_sql( $args["taxonomy"] ) . "' AND " . self::$wpdb->prefix . "terms.term_id IN (". $args["terms"] . ")"; $where[] = "pv.post_id IN ($subquery_taxonomy)"; } $where = implode(' AND ', $where); if ( $where ) { $where = "WHERE $where"; } $limit = $num != -1 ? "LIMIT {$num}" : ""; $sql = " SELECT COUNT(pv.id) AS num, pv.post_id" . ($join ? ", p.post_title, p.post_date" : "") . " FROM {$table_name} pv {$join} {$where} GROUP BY pv.post_id ORDER BY num DESC $limit"; $ids = self::$wpdb->get_results( $sql ); wp_cache_add( $cache_key, $ids, __CLASS__, 15 * MINUTE_IN_SECONDS ); return $ids; } static function get_top_viewed_co_authors( $num = 10 ) { global $wpdb; $table_name = $wpdb->prefix . "pageviews"; $posts_table = $wpdb->prefix . "posts"; $postmeta_table = $wpdb->prefix . "postmeta"; $sql = $wpdb->prepare(" SELECT ga.ID, ga.post_title, COUNT(pv.id) AS views FROM $table_name pv INNER JOIN $posts_table p ON pv.post_id = p.ID INNER JOIN $posts_table ga ON ga.ID = p.post_author INNER JOIN $postmeta_table pm ON ga.ID = pm.post_id WHERE ga.post_type = 'guest-author' AND pm.meta_key = 'colunista' AND pm.meta_value = '1' GROUP BY ga.ID ORDER BY views DESC LIMIT %d ", $num); $results = $wpdb->get_results( $sql ); return $results; } static function get_views_by_post_id( $post_id, $from = null, $to = null ) { global $wpdb; self::$wpdb = $wpdb; $table_name = self::$wpdb->prefix . "pageviews"; $where_clauses = ["post_id = %d"]; $params = [$post_id]; if ( ! empty( $from ) ) { $where_clauses[] = "time >= %s"; $params[] = $from; } if ( !empty( $to ) ) { $where_clauses[] = "time <= %s"; $params[] = $to; } $where_sql = implode( " AND ", $where_clauses ); $sql = $wpdb->prepare( "SELECT COUNT(id) AS views FROM {$table_name} WHERE {$where_sql}", $params ); $result = $wpdb->get_var( $sql ); return $result ? (int) $result : 0; } static function db_updates(){ if(!defined( 'DOING_CRON' )){ return false; } $wpdb = self::$wpdb; $table_name = $wpdb->prefix . "pageviews"; $updates = [ 'id to bigint' => function() use($wpdb, $table_name) { $wpdb->query("ALTER TABLE $table_name MODIFY id BIGINT UNSIGNED AUTO_INCREMENT;"); } ]; foreach($updates as $key => $fn){ $option_name = __METHOD__ . ':' . $key; if(!get_option($option_name)){ $fn(); add_option($option_name, true); } } } static function get_supported_post_types() { $default_post_types = ['post', 'page']; return apply_filters( 'ajax_pv/supported_post_types', $default_post_types ); } static function add_meta_box_pageviews() { $post_types = self::get_supported_post_types(); add_meta_box( 'ajax-pageviews', __( 'Ajax Pageviews', 'ajax-pv' ), [__CLASS__, 'add_meta_box_pageviews_callback'], $post_types, 'normal', 'default' ); } static function add_meta_box_pageviews_callback( $post ) { if ( ! empty( $post->ID ) ) { $pageviews_partial = AjaxPageviews::get_views_by_post_id( $post->ID, date( 'Y-m-d', strtotime( '-14 days' ) ) ); $pageviews_complete = AjaxPageviews::get_views_by_post_id( $post->ID ); echo '<p>' . __( 'Visualizações nos últimos 15 dias:', 'ajax-pv' ) . ' <b>' . $pageviews_partial . '</b></p>'; echo '<p>' . __( 'Visualizações em todo período:', 'ajax-pv' ) . ' <b>' . $pageviews_complete . '</b></p>'; } } static function is_bot() { if(!isset($_SERVER['HTTP_USER_AGENT']) || preg_match('/bot|crawl|curl|dataprovider|search|get|spider|find|java|majesticsEO|google|yahoo|teoma|contaxe|yandex|libwww-perl|facebookexternalhit/i', $_SERVER['HTTP_USER_AGENT'])) { return true; } return false; } } add_action('init', function(){ AjaxPageviews::init(); }); register_activation_hook(__FILE__, ['AjaxPageviews', 'activation']);