PluginProbe
WebberZone Top 10 — Popular Posts / 4.3.2
WebberZone Top 10 — Popular Posts v4.3.2
4.5.1 4.5.0 4.4.3 4.4.2 4.4.1 4.4.0 4.3.4 4.3.3 4.3.2 4.3.1 4.3.0 trunk 1.0 1.0.1 1.1 1.2 1.3 1.4 1.4.1 1.5 1.5.1 1.5.2 1.5.3 1.6 1.6.1 All 117 releases
top-10 / includes / class-tracker.php

class-tracker.php in WebberZone Top 10 — Popular Posts 4.3.2, at includes/class-tracker.php

389 lines 12.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Tracker class.
4 *
5 * @package WebberZone\Top_Ten
6 */
7
8 namespace WebberZone\Top_Ten;
9
10 use WebberZone\Top_Ten\Database;
11 use WebberZone\Top_Ten\Util\Helpers;
12 use WebberZone\Top_Ten\Util\Hook_Registry;
13
14 if ( ! defined( 'WPINC' ) ) {
15 exit;
16 }
17
18 /**
19 * Admin Columns Class.
20 *
21 * @since 3.3.0
22 */
23 class Tracker {
24
25 /**
26 * Constructor class.
27 *
28 * @since 3.3.0
29 */
30 public function __construct() {
31 Hook_Registry::add_action( 'parse_request', array( $this, 'parse_request' ) );
32 Hook_Registry::add_filter( 'query_vars', array( $this, 'query_vars' ) );
33 Hook_Registry::add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
34 Hook_Registry::add_action( 'wp_ajax_nopriv_tptn_tracker', array( $this, 'tracker_parser' ) );
35 Hook_Registry::add_action( 'wp_ajax_tptn_tracker', array( $this, 'tracker_parser' ) );
36 }
37
38 /**
39 * Enqueues the scripts needed by Top 10.
40 *
41 * @since 1.9.7
42 * @return void
43 */
44 public static function enqueue_scripts() {
45 global $post, $ajax_tptn_tracker;
46
47 if ( ! is_object( $post ) ) {
48 return;
49 }
50 if ( 'draft' === $post->post_status || is_customize_preview() ) {
51 return;
52 }
53
54 $track_users = wp_parse_list( \tptn_get_option( 'track_users' ) );
55 $trackers = wp_parse_list( \tptn_get_option( 'trackers' ) );
56
57 if ( is_singular() || \tptn_get_option( 'tracker_all_pages' ) ) {
58
59 $current_user = wp_get_current_user(); // Let's get the current user.
60 $post_author = ( (int) $current_user->ID === (int) $post->post_author ) ? true : false; // Is the current user the post author?
61 $current_user_admin = ( current_user_can( 'manage_options' ) ) ? true : false; // Is the current user an admin?
62 $current_user_editor = ( ( current_user_can( 'edit_others_posts' ) ) && ( ! current_user_can( 'manage_options' ) ) ) ? true : false; // Is the current user an editor?
63 $is_bot = Helpers::is_bot();
64
65 $include_code = true;
66 if ( ( $post_author ) && ( ! in_array( 'authors', $track_users, true ) ) ) {
67 $include_code = false;
68 }
69 if ( ( $current_user_admin ) && ( ! in_array( 'admins', $track_users, true ) ) ) {
70 $include_code = false;
71 }
72 if ( ( $current_user_editor ) && ( ! in_array( 'editors', $track_users, true ) ) ) {
73 $include_code = false;
74 }
75 if ( ( $current_user->exists() ) && ( ! \tptn_get_option( 'logged_in' ) ) ) {
76 $include_code = false;
77 }
78 if ( $is_bot && \tptn_get_option( 'no_bots' ) ) {
79 $include_code = false;
80 }
81
82 if ( $include_code ) {
83
84 $id = is_singular() ? absint( $post->ID ) : 0;
85 $blog_id = get_current_blog_id();
86 $activate_counter = in_array( 'overall', $trackers, true ) ? 1 : 0; // It's 1 if we're updating the overall count.
87 $activate_counter = $activate_counter + ( in_array( 'daily', $trackers, true ) ? 10 : 0 ); // It's 10 if we're updating the daily count.
88 $top_ten_debug = absint( \tptn_get_option( 'debug_mode' ) );
89 $tracker_type = \tptn_get_option( 'tracker_type' );
90
91 switch ( $tracker_type ) {
92 case 'query_based':
93 $home_url = home_url( '/' );
94 break;
95
96 case 'ajaxurl':
97 $home_url = admin_url( 'admin-ajax.php' );
98 break;
99
100 case 'rest_based':
101 $home_url = rest_url( 'top-10/v1/tracker' );
102 break;
103
104 default:
105 $home_url = rest_url( 'top-10/v1/tracker' );
106 break;
107 }
108
109 /**
110 * Filter the URL of the tracker.
111 *
112 * Other tracker types can override the URL processed by the jQuery.post request
113 * The corresponding tracker can use the below variables or append their own to $ajax_tptn_tracker
114 *
115 * @since 2.0
116 *
117 * @param string $home_url URL of the tracker.
118 */
119 $home_url = apply_filters( 'tptn_add_counter_script_url', $home_url );
120
121 // Strip any query strings since we don't need them.
122 $home_url = strtok( $home_url, '?' );
123
124 $ajax_tptn_tracker = array(
125 'ajax_url' => $home_url,
126 'top_ten_id' => $id,
127 'top_ten_blog_id' => $blog_id,
128 'activate_counter' => $activate_counter,
129 'top_ten_debug' => $top_ten_debug,
130 'tptn_rnd' => wp_rand( 1, time() ),
131 );
132
133 /**
134 * Filter the localize script arguments for the Top 10 tracker.
135 *
136 * @since 2.4.0
137 */
138 $ajax_tptn_tracker = apply_filters( 'tptn_tracker_script_args', $ajax_tptn_tracker );
139
140 wp_enqueue_script(
141 'tptn_tracker',
142 plugins_url( 'includes/js/top-10-tracker.min.js', TOP_TEN_PLUGIN_FILE ),
143 array(),
144 TOP_TEN_VERSION,
145 true
146 );
147
148 wp_localize_script( 'tptn_tracker', 'ajax_tptn_tracker', $ajax_tptn_tracker );
149
150 }
151 }
152 }
153
154 /**
155 * Function to add additional queries to query_vars.
156 *
157 * @since 2.0.0
158 *
159 * @param array $vars Query variables array.
160 * @return array Query variables array with Top 10 parameters appended
161 */
162 public static function query_vars( $vars ) {
163 // Add these to the list of queryvars that WP gathers.
164 $vars[] = 'top_ten_id';
165 $vars[] = 'top_ten_blog_id';
166 $vars[] = 'activate_counter';
167 $vars[] = 'view_counter';
168 $vars[] = 'top_ten_debug';
169 $vars[] = 'tptn_feed';
170
171 /**
172 * Function to add additional queries to query_vars.
173 *
174 * @since 2.6.0
175 *
176 * @param array $vars Updated Query variables array with Top 10 queries added.
177 */
178 return apply_filters( 'tptn_query_vars', $vars );
179 }
180
181 /**
182 * Parses the WordPress object to update/display the count.
183 *
184 * @since 2.0.0
185 *
186 * @param \WP $wp Current WordPress environment instance.
187 */
188 public static function parse_request( $wp ) {
189
190 if ( empty( $wp->query_vars['top_ten_id'] ) ) {
191 return;
192 }
193
194 if ( array_key_exists( 'top_ten_id', $wp->query_vars ) && array_key_exists( 'activate_counter', $wp->query_vars ) ) {
195
196 $id = absint( $wp->query_vars['top_ten_id'] );
197 $blog_id = absint( $wp->query_vars['top_ten_blog_id'] );
198 $activate_counter = absint( $wp->query_vars['activate_counter'] );
199
200 $is_feed = ! empty( $wp->query_vars['tptn_feed'] );
201 $source = $is_feed ? 1 : 0;
202
203 $str = self::update_count( $id, $blog_id, $activate_counter, $source );
204
205 if ( $is_feed ) {
206 self::output_tracking_pixel(); // Sends GIF and exits.
207 } else {
208 // If the debug parameter is set then we output $str else we send a No Content header.
209 if ( array_key_exists( 'top_ten_debug', $wp->query_vars ) && 1 === absint( $wp->query_vars['top_ten_debug'] ) ) {
210 header( 'content-type: application/x-javascript' );
211 wp_send_json( $str );
212 } else {
213 header( 'HTTP/1.0 204 No Content' );
214 header( 'Cache-Control: max-age=15, s-maxage=0' );
215 }
216
217 // Stop anything else from loading as it is not needed.
218 exit;
219 }
220 } elseif ( array_key_exists( 'top_ten_id', $wp->query_vars ) && array_key_exists( 'view_counter', $wp->query_vars ) ) {
221
222 $id = absint( $wp->query_vars['top_ten_id'] );
223
224 if ( $id > 0 ) {
225
226 $output = Counter::get_post_count( $id );
227
228 nocache_headers();
229 wp_send_json( array( 'count' => $output ) );
230 }
231 } else {
232 return;
233 }
234 }
235
236 /**
237 * Add a tracking pixel to feed content.
238 *
239 * Appends a 1×1 transparent GIF to each feed item. When a feed reader
240 * loads the image, parse_request() intercepts the request, increments
241 * the view count, and serves the GIF. Views are merged into the same
242 * tables as regular web views.
243 *
244 * Note: feed readers that block remote images by default will not trigger
245 * the pixel. The count only increments when the reader actually loads images.
246 *
247 * @since 4.3.0
248 *
249 * @param string $content Feed content.
250 * @return string Feed content with the tracker image appended.
251 */
252 public static function add_feed_tracker( $content ) {
253 global $post;
254
255 if (
256 ! \tptn_get_option( 'track_feed_views' ) ||
257 ! is_feed() ||
258 ! is_object( $post ) ||
259 empty( $post->ID )
260 ) {
261 return $content;
262 }
263
264 /*
265 * In full-text mode WordPress fires both the_excerpt_rss (for <description>)
266 * and the_content_feed (for <content:encoded>) per item. Skip the excerpt hook
267 * in that case so we don't double-count; the_content_feed will add the pixel.
268 * In excerpt-only mode the_content_feed never fires, so the_excerpt_rss handles it.
269 */
270 if ( 'the_excerpt_rss' === current_filter() && ! get_option( 'rss_use_excerpt' ) ) {
271 return $content;
272 }
273
274 $trackers = wp_parse_list( \tptn_get_option( 'trackers' ) );
275 $activate_counter = in_array( 'overall', $trackers, true ) ? 1 : 0;
276 $activate_counter = $activate_counter + ( in_array( 'daily', $trackers, true ) ? 10 : 0 );
277
278 if ( 0 === $activate_counter ) {
279 return $content;
280 }
281
282 $tracker_url = add_query_arg(
283 array(
284 'top_ten_id' => absint( $post->ID ),
285 'top_ten_blog_id' => get_current_blog_id(),
286 'activate_counter' => $activate_counter,
287 'tptn_feed' => 1,
288 ),
289 home_url( '/' )
290 );
291
292 $tracker = sprintf(
293 '<img src="%1$s" width="1" height="1" alt="" style="display:none" />',
294 esc_url( $tracker_url )
295 );
296
297 return $content . $tracker;
298 }
299
300 /**
301 * Output a transparent GIF for feed view tracking requests.
302 *
303 * @since 4.3.0
304 *
305 * @return void
306 */
307 protected static function output_tracking_pixel() {
308 $pixel = 'R0lGODlhAQABAIAAANvf7wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==';
309
310 header( 'Content-Type: image/gif' );
311 header( 'Cache-Control: no-cache, no-store, must-revalidate, max-age=0' );
312 echo base64_decode( $pixel ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode, WordPress.Security.EscapeOutput.OutputNotEscaped
313 exit;
314 }
315
316 /**
317 * Parse the ajax response.
318 *
319 * @since 2.4.0
320 */
321 public static function tracker_parser() {
322
323 $id = isset( $_POST['top_ten_id'] ) ? absint( sanitize_text_field( wp_unslash( $_POST['top_ten_id'] ) ) ) : 0; // phpcs:ignore WordPress.Security.NonceVerification.Missing
324 $blog_id = isset( $_POST['top_ten_blog_id'] ) ? absint( sanitize_text_field( wp_unslash( $_POST['top_ten_blog_id'] ) ) ) : 0; // phpcs:ignore WordPress.Security.NonceVerification.Missing
325 $activate_counter = isset( $_POST['activate_counter'] ) ? absint( sanitize_text_field( wp_unslash( $_POST['activate_counter'] ) ) ) : 0; // phpcs:ignore WordPress.Security.NonceVerification.Missing
326 $top_ten_debug = isset( $_POST['top_ten_debug'] ) ? absint( sanitize_text_field( wp_unslash( $_POST['top_ten_debug'] ) ) ) : 0; // phpcs:ignore WordPress.Security.NonceVerification.Missing
327
328 $str = self::update_count( $id, $blog_id, $activate_counter, 0 );
329
330 // If the debug parameter is set then we output $str else we send a No Content header.
331 if ( 1 === $top_ten_debug ) {
332 echo esc_html( $str );
333 } else {
334 header( 'HTTP/1.0 204 No Content' );
335 header( 'Cache-Control: max-age=15, s-maxage=0' );
336 }
337
338 wp_die();
339 }
340
341 /**
342 * Function to update the count in the database.
343 *
344 * @since 2.6.0
345 *
346 * @param int $id Post ID.
347 * @param int $blog_id Blog ID.
348 * @param int $activate_counter Activate counter flag.
349 * @param int $source Traffic source: 0 = web, 1 = feed.
350 *
351 * @return string Response on database update.
352 */
353 public static function update_count( $id, $blog_id, $activate_counter, $source = 0 ) {
354
355 $str = '';
356
357 /**
358 * Filter the flag to confirm that counts should be updated in the database.
359 *
360 * @since 4.0.0
361 *
362 * @param bool $flag Flag to confirm that counts should be updated in the database.
363 * @param int $id Post ID.
364 * @param int $blog_id Blog ID.
365 * @param int $activate_counter Activate counter flag.
366 * @param int $source Traffic source: 0 = web, 1 = feed.
367 */
368 $before_update_count = apply_filters( 'tptn_before_update_count', true, $id, $blog_id, $activate_counter, $source );
369
370 if ( $id > 0 && $activate_counter > 0 && $before_update_count ) {
371 $result = Database::append_to_funnel( $id, $blog_id, $activate_counter, $source );
372 $str .= ( false === $result ) ? 'loge' : 'log' . $result;
373 }
374
375 /**
376 * Filter the response on database update.
377 *
378 * @since 2.6.0
379 *
380 * @param string $str Response string.
381 * @param int $id Post ID.
382 * @param int $blog_id Blog ID.
383 * @param int $activate_counter Activate counter flag.
384 * @param int $source Traffic source: 0 = web, 1 = feed.
385 */
386 return apply_filters( 'tptn_update_count', $str, $id, $blog_id, $activate_counter, $source );
387 }
388 }
389