PluginProbe
WebberZone Top 10 — Popular Posts / 4.3.0
WebberZone Top 10 — Popular Posts v4.3.0
4.5.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 All 118 releases
top-10 / includes / class-tracker.php

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

396 lines 12.3 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 ) ) {
191 global $wp;
192 }
193
194 if ( empty( $wp->query_vars['top_ten_id'] ) ) {
195 return;
196 }
197
198 if ( array_key_exists( 'top_ten_id', $wp->query_vars ) && array_key_exists( 'activate_counter', $wp->query_vars ) ) {
199
200 $id = absint( $wp->query_vars['top_ten_id'] );
201 $blog_id = absint( $wp->query_vars['top_ten_blog_id'] );
202 $activate_counter = absint( $wp->query_vars['activate_counter'] );
203
204 $is_feed = ! empty( $wp->query_vars['tptn_feed'] );
205 $source = $is_feed ? 1 : 0;
206
207 $str = self::update_count( $id, $blog_id, $activate_counter, $source );
208
209 if ( $is_feed ) {
210 self::output_tracking_pixel(); // Sends GIF and exits.
211 } else {
212 // If the debug parameter is set then we output $str else we send a No Content header.
213 if ( array_key_exists( 'top_ten_debug', $wp->query_vars ) && 1 === absint( $wp->query_vars['top_ten_debug'] ) ) {
214 header( 'content-type: application/x-javascript' );
215 wp_send_json( $str );
216 } else {
217 header( 'HTTP/1.0 204 No Content' );
218 header( 'Cache-Control: max-age=15, s-maxage=0' );
219 }
220
221 // Stop anything else from loading as it is not needed.
222 exit;
223 }
224 } elseif ( array_key_exists( 'top_ten_id', $wp->query_vars ) && array_key_exists( 'view_counter', $wp->query_vars ) ) {
225
226 $id = absint( $wp->query_vars['top_ten_id'] );
227
228 if ( $id > 0 ) {
229
230 $output = Counter::get_post_count( $id );
231
232 header( 'content-type: application/x-javascript' );
233 echo 'document.write("' . $output . '");'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
234
235 // Stop anything else from loading as it is not needed.
236 exit;
237 }
238 } else {
239 return;
240 }
241 }
242
243 /**
244 * Add a tracking pixel to feed content.
245 *
246 * Appends a 1×1 transparent GIF to each feed item. When a feed reader
247 * loads the image, parse_request() intercepts the request, increments
248 * the view count, and serves the GIF. Views are merged into the same
249 * tables as regular web views.
250 *
251 * Note: feed readers that block remote images by default will not trigger
252 * the pixel. The count only increments when the reader actually loads images.
253 *
254 * @since 4.3.0
255 *
256 * @param string $content Feed content.
257 * @return string Feed content with the tracker image appended.
258 */
259 public static function add_feed_tracker( $content ) {
260 global $post;
261
262 if (
263 ! \tptn_get_option( 'track_feed_views' ) ||
264 ! is_feed() ||
265 ! is_object( $post ) ||
266 empty( $post->ID )
267 ) {
268 return $content;
269 }
270
271 /*
272 * In full-text mode WordPress fires both the_excerpt_rss (for <description>)
273 * and the_content_feed (for <content:encoded>) per item. Skip the excerpt hook
274 * in that case so we don't double-count; the_content_feed will add the pixel.
275 * In excerpt-only mode the_content_feed never fires, so the_excerpt_rss handles it.
276 */
277 if ( 'the_excerpt_rss' === current_filter() && ! get_option( 'rss_use_excerpt' ) ) {
278 return $content;
279 }
280
281 $trackers = wp_parse_list( \tptn_get_option( 'trackers' ) );
282 $activate_counter = in_array( 'overall', $trackers, true ) ? 1 : 0;
283 $activate_counter = $activate_counter + ( in_array( 'daily', $trackers, true ) ? 10 : 0 );
284
285 if ( 0 === $activate_counter ) {
286 return $content;
287 }
288
289 $tracker_url = add_query_arg(
290 array(
291 'top_ten_id' => absint( $post->ID ),
292 'top_ten_blog_id' => get_current_blog_id(),
293 'activate_counter' => $activate_counter,
294 'tptn_feed' => 1,
295 ),
296 home_url( '/' )
297 );
298
299 $tracker = sprintf(
300 '<img src="%1$s" width="1" height="1" alt="" style="display:none" />',
301 esc_url( $tracker_url )
302 );
303
304 return $content . $tracker;
305 }
306
307 /**
308 * Output a transparent GIF for feed view tracking requests.
309 *
310 * @since 4.3.0
311 *
312 * @return void
313 */
314 protected static function output_tracking_pixel() {
315 $pixel = 'R0lGODlhAQABAIAAANvf7wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==';
316
317 header( 'Content-Type: image/gif' );
318 header( 'Cache-Control: no-cache, no-store, must-revalidate, max-age=0' );
319 echo base64_decode( $pixel ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode, WordPress.Security.EscapeOutput.OutputNotEscaped
320 exit;
321 }
322
323 /**
324 * Parse the ajax response.
325 *
326 * @since 2.4.0
327 */
328 public static function tracker_parser() {
329
330 $id = isset( $_POST['top_ten_id'] ) ? absint( sanitize_text_field( wp_unslash( $_POST['top_ten_id'] ) ) ) : 0; // phpcs:ignore WordPress.Security.NonceVerification.Missing
331 $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
332 $activate_counter = isset( $_POST['activate_counter'] ) ? absint( sanitize_text_field( wp_unslash( $_POST['activate_counter'] ) ) ) : 0; // phpcs:ignore WordPress.Security.NonceVerification.Missing
333 $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
334
335 $str = self::update_count( $id, $blog_id, $activate_counter, 0 );
336
337 // If the debug parameter is set then we output $str else we send a No Content header.
338 if ( 1 === $top_ten_debug ) {
339 echo esc_html( $str );
340 } else {
341 header( 'HTTP/1.0 204 No Content' );
342 header( 'Cache-Control: max-age=15, s-maxage=0' );
343 }
344
345 wp_die();
346 }
347
348 /**
349 * Function to update the count in the database.
350 *
351 * @since 2.6.0
352 *
353 * @param int $id Post ID.
354 * @param int $blog_id Blog ID.
355 * @param int $activate_counter Activate counter flag.
356 * @param int $source Traffic source: 0 = web, 1 = feed.
357 *
358 * @return string Response on database update.
359 */
360 public static function update_count( $id, $blog_id, $activate_counter, $source = 0 ) {
361
362 $str = '';
363
364 /**
365 * Filter the flag to confirm that counts should be updated in the database.
366 *
367 * @since 4.0.0
368 *
369 * @param bool $flag Flag to confirm that counts should be updated in the database.
370 * @param int $id Post ID.
371 * @param int $blog_id Blog ID.
372 * @param int $activate_counter Activate counter flag.
373 * @param int $source Traffic source: 0 = web, 1 = feed.
374 */
375 $before_update_count = apply_filters( 'tptn_before_update_count', true, $id, $blog_id, $activate_counter, $source );
376
377 if ( $id > 0 && $activate_counter > 0 && $before_update_count ) {
378 $result = Database::append_to_funnel( $id, $blog_id, $activate_counter, $source );
379 $str .= ( false === $result ) ? 'loge' : 'log' . $result;
380 }
381
382 /**
383 * Filter the response on database update.
384 *
385 * @since 2.6.0
386 *
387 * @param string $str Response string.
388 * @param int $id Post ID.
389 * @param int $blog_id Blog ID.
390 * @param int $activate_counter Activate counter flag.
391 * @param int $source Traffic source: 0 = web, 1 = feed.
392 */
393 return apply_filters( 'tptn_update_count', $str, $id, $blog_id, $activate_counter, $source );
394 }
395 }
396