PluginProbe
WebberZone Top 10 — Popular Posts / 4.5.1
WebberZone Top 10 — Popular Posts v4.5.1
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-counter.php

class-counter.php in WebberZone Top 10 — Popular Posts 4.5.1, at includes/class-counter.php

483 lines 14.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Counter 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 * Displays the post view count in content and feeds.
20 *
21 * @since 3.3.0
22 */
23 class Counter {
24
25 /**
26 * Constructor class.
27 *
28 * @since 3.3.0
29 */
30 public function __construct() {
31 Hook_Registry::add_filter( 'the_content', array( __CLASS__, 'the_content' ) );
32 Hook_Registry::add_filter( 'the_excerpt_rss', array( __CLASS__, 'rss_filter' ) );
33 Hook_Registry::add_filter( 'the_content_feed', array( __CLASS__, 'rss_filter' ) );
34 Hook_Registry::add_action( 'wp_ajax_tptn_edit_count_ajax', array( __CLASS__, 'edit_count_ajax' ) );
35 }
36
37 /**
38 * Adds the viewed count to the post content. Filters `the_content`.
39 *
40 * @since 3.3.0
41 *
42 * @param string $content Post content.
43 * @return string Filtered post content.
44 */
45 public static function the_content( $content ) {
46 global $post, $wp_filters;
47
48 // Track the number of times this function is called.
49 static $filter_calls = 0;
50 ++$filter_calls;
51
52 // Check if this is the last call of 'the_content' and only process for the main query.
53 if ( ! ( in_the_loop() && is_main_query() && (int) get_queried_object_id() === (int) $post->ID ) || ( doing_filter( 'the_content' ) && isset( $wp_filters['the_content'] ) && (int) $wp_filters['the_content'] !== $filter_calls ) ) {
54 return $content;
55 }
56
57 // Exclude posts that should not display the viewed count.
58 $exclude_on_post_ids = explode( ',', \tptn_get_option( 'exclude_on_post_ids' ) );
59 if ( isset( $post ) && in_array( $post->ID, $exclude_on_post_ids, true ) ) {
60 return $content;
61 }
62
63 // Determine where to add the viewed count.
64 $add_to = wp_parse_list( \tptn_get_option( 'add_to', array() ) );
65
66 $conditions = array(
67 'single' => is_single(),
68 'page' => is_page(),
69 'home' => is_home(),
70 'category_archives' => is_category(),
71 'tag_archives' => is_tag(),
72 'other_archives' => is_tax() || is_author() || is_date(),
73 );
74
75 foreach ( $conditions as $key => $condition ) {
76 if ( $condition && in_array( $key, $add_to, true ) ) {
77 return $content . self::echo_post_count( 0 );
78 }
79 }
80
81 return $content;
82 }
83
84
85 /**
86 * Filter to display the post count when viewing feeds.
87 *
88 * @since 1.9.8
89 *
90 * @param string $content Post content.
91 * @return string Filtered post content
92 */
93 public static function rss_filter( $content ) {
94 global $post;
95
96 $id = intval( $post->ID );
97
98 $add_to = \tptn_get_option( 'add_to', false );
99
100 if ( ! empty( $add_to['feed'] ) ) {
101 return $content . '<div class="tptn_counter" id="tptn_counter_' . $id . '">' . self::get_post_count( $id ) . '</div>';
102 } else {
103 return $content;
104 }
105 }
106
107 /**
108 * Function to manually display count.
109 *
110 * @since 1.0
111 * @param int|boolean $echo_output Flag to echo the output.
112 * @return string|void Formatted string if $echo_output is set to 0|false
113 */
114 public static function echo_post_count( $echo_output = 1 ) {
115 global $post;
116
117 $home_url = home_url( '/' );
118
119 /**
120 * Filter the script URL of the counter.
121 *
122 * @since 2.0
123 */
124 $home_url = apply_filters( 'tptn_view_counter_script_url', $home_url );
125
126 // Strip any query strings since we don't need them.
127 $home_url = strtok( $home_url, '?' );
128
129 $id = intval( $post->ID );
130
131 if ( \tptn_get_option( 'dynamic_post_count' ) ) {
132 $nonce_action = 'tptn-nonce-' . $id;
133 $nonce = wp_create_nonce( $nonce_action );
134
135 $fetch_url = add_query_arg(
136 array(
137 'top_ten_id' => $id,
138 'view_counter' => 1,
139 '_wpnonce' => $nonce,
140 ),
141 $home_url
142 );
143
144 wp_enqueue_script(
145 'tptn-counter',
146 plugins_url( 'includes/js/top-10-counter.min.js', TOP_TEN_PLUGIN_FILE ),
147 array(),
148 TOP_TEN_VERSION,
149 true
150 );
151
152 $output = sprintf(
153 '<div class="tptn_counter" id="tptn_counter_%1$d" data-tptn-url="%2$s"></div>',
154 $id,
155 esc_url( $fetch_url )
156 );
157 } else {
158 $output = '<div class="tptn_counter" id="tptn_counter_' . $id . '">' . self::get_post_count( $id ) . '</div>';
159 }
160
161 /**
162 * Filter the viewed count script
163 *
164 * @since 2.0.0
165 *
166 * @param string $output Counter viewed count code
167 */
168 $output = apply_filters( 'tptn_view_post_count', $output );
169
170 if ( $echo_output ) {
171 echo $output; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
172 } else {
173 return $output;
174 }
175 }
176
177
178 /**
179 * Return the formatted post count for the supplied ID.
180 *
181 * @since 3.3.0
182 * @since 4.0.0 Added $args parameter.
183 *
184 * @param int|string|\WP_Post $post Post ID or WP_Post object.
185 * @param int|string $blog_id Blog ID.
186 * @param array $args Additional arguments.
187 * @return string Formatted post count
188 */
189 public static function get_post_count( $post = 0, $blog_id = 0, $args = array() ) {
190 if ( $post instanceof \WP_Post ) {
191 $id = $post->ID;
192 } else {
193 $id = absint( $post );
194 }
195
196 if ( $id <= 0 ) {
197 return '';
198 }
199
200 $count_disp_form = isset( $args['count_disp_form'] ) ? $args['count_disp_form'] : stripslashes( \tptn_get_option( 'count_disp_form' ) );
201 $count_disp_form_zero = isset( $args['count_disp_form_zero'] ) ? $args['count_disp_form_zero'] : stripslashes( \tptn_get_option( 'count_disp_form_zero' ) );
202 $total_count = self::get_post_count_only( $id, 'total', $blog_id );
203 $is_singular = is_singular();
204 $is_zero_total_count = ( 0 === (int) $total_count );
205
206 if ( $is_zero_total_count && ! $is_singular ) {
207 $count_disp_form_zero = str_replace(
208 '%totalcount%',
209 (string) $total_count,
210 $count_disp_form_zero
211 );
212 } else {
213 $count_disp_form = str_replace(
214 '%totalcount%',
215 Helpers::number_format_i18n( $is_zero_total_count ? $total_count + 1 : $total_count ),
216 $count_disp_form
217 );
218 }
219
220 foreach ( array( 'daily', 'overall' ) as $type ) {
221 if ( false !== strpos( $count_disp_form, "%{$type}count%" ) || false !== strpos( $count_disp_form_zero, "%{$type}count%" ) ) {
222 $count = self::get_post_count_only( $id, $type );
223 if ( $is_zero_total_count && ! $is_singular ) {
224 $count_disp_form_zero = str_replace(
225 "%{$type}count%",
226 (string) $count,
227 $count_disp_form_zero
228 );
229 } else {
230 $is_zero_cntaccess = ( 0 === (int) $count );
231 $count_disp_form = str_replace(
232 "%{$type}count%",
233 Helpers::number_format_i18n( $is_zero_cntaccess ? $count + 1 : $count ),
234 $count_disp_form
235 );
236 }
237 }
238 }
239
240 /**
241 * Filters the formatted view count displayed for a post.
242 *
243 * @since 2.2.0
244 *
245 * @param string $count_display The formatted count text.
246 */
247 return apply_filters( 'tptn_post_count', ( $is_zero_total_count && ! $is_singular ) ? $count_disp_form_zero : $count_disp_form );
248 }
249
250 /**
251 * Returns the post count.
252 *
253 * @since 3.3.0
254 *
255 * @param int|\WP_Post $post Post ID or WP_Post object.
256 * @param string $counter Which count to return? total, daily or overall.
257 * @param int $blog_id Blog ID.
258 * @param array $args Additional arguments.
259 * @return int|string Post count
260 */
261 public static function get_post_count_only( $post = 0, $counter = 'total', $blog_id = 0, $args = array() ) {
262 global $wpdb;
263
264 if ( $post instanceof \WP_Post ) {
265 $id = $post->ID;
266 } else {
267 $id = absint( $post );
268 }
269
270 $defaults = array(
271 'format_number' => false,
272 'from_date' => '',
273 'to_date' => '',
274 );
275 $args = wp_parse_args( $args, $defaults );
276
277 // Sanitize the attributes.
278 $args = Helpers::sanitize_args( $args );
279
280 if ( empty( $blog_id ) ) {
281 $blog_id = get_current_blog_id();
282 }
283
284 if ( $id > 0 || 'overall' === $counter ) {
285 $post_count = 0;
286 switch ( $counter ) {
287 case 'total':
288 $post_count = Database::get_count( $id, $blog_id, false );
289 break;
290 case 'daily':
291 $date_range = array();
292 if ( ! empty( $args['from_date'] ) ) {
293 $date_range['from_date'] = Helpers::get_from_date( $args['from_date'], 0, 0 );
294 }
295 if ( ! empty( $args['to_date'] ) ) {
296 $date_range['to_date'] = Helpers::get_from_date( $args['to_date'], 0, 0 );
297 }
298 if ( empty( $date_range ) ) {
299 $date_range['from_date'] = Helpers::get_from_date();
300 }
301 $post_count = Database::get_count( $id, $blog_id, true, $date_range );
302 break;
303 case 'overall':
304 $post_count = Database::get_total_count( $blog_id, false );
305 break;
306 }
307 if ( $args['format_number'] ) {
308 $post_count = number_format_i18n( $post_count );
309 }
310
311 /**
312 * Filters the post count returned for a post.
313 *
314 * @since 2.6.0
315 *
316 * @param int|string $post_count Post count.
317 * @param mixed $id Post ID.
318 * @param string $counter Which count to return? total, daily or overall.
319 * @param int $blog_id Blog ID.
320 */
321 return apply_filters( 'tptn_post_count_only', $post_count, $id, $counter, $blog_id );
322 } else {
323 return 0;
324 }
325 }
326
327
328 /**
329 * Delete the counts from the selected table.
330 *
331 * @since 3.3.0
332 *
333 * @param string|array $args {
334 * Optional. Array or string of Query parameters.
335 *
336 * @type bool $daily Set to true for daily table. False for overall.
337 * @type array|string $post_id An array or comma-separated string of post IDs. Empty string or array for all IDs. Default is blank.
338 * @type array|string $blog_id An array or comma-separated string of blog IDs. Empty string or array for all IDs. Default is current blog ID.
339 * @type string $db_date The date before which to delete data. Default is empty string which means all dates. Applies to daily table only.
340 * }
341 * @return int|false The number of rows updated, or false on error.
342 */
343 public static function delete_counts( $args = array() ) {
344 $defaults = array(
345 'daily' => true,
346 'post_id' => '',
347 'blog_id' => get_current_blog_id(),
348 'dp_date' => '',
349 );
350 $args = wp_parse_args( $args, $defaults );
351
352 // Convert the arguments to match Database class format.
353 $db_args = array(
354 'daily' => $args['daily'],
355 'post_ids' => wp_parse_id_list( $args['post_id'] ),
356 'to_date' => $args['dp_date'],
357 );
358
359 // Handle blog_id conversion.
360 if ( ! empty( $args['blog_id'] ) && is_array( $args['blog_id'] ) ) {
361 // If multiple blog IDs, we need to delete for each one.
362 $result = 0;
363 foreach ( $args['blog_id'] as $blog_id ) {
364 $db_args['blog_id'] = $blog_id;
365 $result += Database::delete_counts( $db_args );
366 }
367 return $result;
368 } else {
369 $db_args['blog_id'] = is_array( $args['blog_id'] ) ? reset( $args['blog_id'] ) : $args['blog_id'];
370 return Database::delete_counts( $db_args );
371 }
372 }
373
374
375 /**
376 * Delete post count.
377 *
378 * @since 3.3.0
379 *
380 * @param int $post_id Post ID.
381 * @param int $blog_id Blog ID.
382 * @param bool $daily Daily flag.
383 * @return bool|int Number of rows affected or false if error.
384 */
385 public static function delete_count( $post_id, $blog_id, $daily = false ) {
386 global $wpdb;
387
388 $post_id = intval( $post_id );
389 $blog_id = intval( $blog_id );
390
391 if ( empty( $post_id ) || empty( $blog_id ) ) {
392 return false;
393 }
394
395 $results = self::delete_counts(
396 array(
397 'post_id' => $post_id,
398 'blog_id' => $blog_id,
399 'daily' => $daily,
400 )
401 );
402
403 return $results;
404 }
405
406
407 /**
408 * Function to update the Top 10 count with ajax.
409 *
410 * @since 3.3.0
411 */
412 public static function edit_count_ajax() {
413 // Security check.
414 check_ajax_referer( 'top_ten_admin_nonce', 'top_ten_admin_nonce' );
415
416 if ( ! isset( $_REQUEST['total_count'] ) || ! isset( $_REQUEST['post_id'] ) || ! isset( $_REQUEST['total_count_original'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
417 wp_die();
418 }
419
420 $results = 0;
421
422 $post_id = absint( $_REQUEST['post_id'] ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
423 $blog_id = isset( $_REQUEST['blog_id'] ) ? absint( $_REQUEST['blog_id'] ) : get_current_blog_id(); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
424 $total_count = absint( filter_var( $_REQUEST['total_count'], FILTER_SANITIZE_NUMBER_INT ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.MissingUnslash
425 $total_count_original = absint( filter_var( $_REQUEST['total_count_original'], FILTER_SANITIZE_NUMBER_INT ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.MissingUnslash
426
427 // Check permissions.
428 if ( isset( $_REQUEST['blog_id'] ) && absint( $_REQUEST['blog_id'] ) !== get_current_blog_id() ) {
429 // In network mode (editing a different blog's data), require manage_network capability.
430 if ( ! current_user_can( 'manage_network' ) ) {
431 wp_die();
432 }
433 } else {
434 // Switch to the target blog to check edit permissions.
435 $target_blog_id = isset( $_REQUEST['blog_id'] ) ? absint( $_REQUEST['blog_id'] ) : get_current_blog_id();
436
437 if ( get_current_blog_id() !== $target_blog_id ) {
438 switch_to_blog( $target_blog_id );
439 }
440
441 $can_edit = current_user_can( 'edit_post', $post_id );
442
443 // Switch back if we switched.
444 if ( get_current_blog_id() !== $target_blog_id ) {
445 restore_current_blog();
446 }
447
448 if ( ! $can_edit ) {
449 wp_die();
450 }
451 }
452
453 if ( $total_count_original !== $total_count ) {
454 $results = self::edit_count( $post_id, $blog_id, $total_count );
455 }
456 echo wp_json_encode( $results );
457 wp_die();
458 }
459
460 /**
461 * Edit post count.
462 *
463 * @since 3.3.0
464 *
465 * @param int $post_id Post ID.
466 * @param int $blog_id Blog ID.
467 * @param int $total_count Total count.
468 * @return bool|int Number of rows affected or false if error.
469 */
470 public static function edit_count( $post_id, $blog_id, $total_count ) {
471
472 $post_id = intval( $post_id );
473 $blog_id = intval( $blog_id );
474 $total_count = intval( $total_count );
475
476 if ( empty( $post_id ) || empty( $blog_id ) || empty( $total_count ) ) {
477 return false;
478 }
479
480 return Database::set_count( $post_id, $total_count, $blog_id, false );
481 }
482 }
483