PluginProbe
WebberZone Top 10 — Popular Posts / 4.3.4
WebberZone Top 10 — Popular Posts v4.3.4
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 / frontend / class-display.php

class-display.php in WebberZone Top 10 — Popular Posts 4.3.4, at includes/frontend/class-display.php

1,213 lines 36.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Display class.
4 *
5 * @package WebberZone\Top_Ten\Frontend
6 */
7
8 namespace WebberZone\Top_Ten\Frontend;
9
10 use WebberZone\Top_Ten\Database;
11 use WebberZone\Top_Ten\Util\Helpers;
12
13 if ( ! defined( 'WPINC' ) ) {
14 die;
15 }
16
17 /**
18 * Display class
19 *
20 * @since 3.3.0
21 */
22 class Display {
23
24 /**
25 * Constructor class.
26 *
27 * @since 3.3.0
28 */
29 public function __construct() {
30 }
31
32 /**
33 * Function to return formatted list of popular posts.
34 *
35 * @since 3.3.0
36 *
37 * @param mixed $args Arguments array.
38 * @return string HTML output of the popular posts.
39 */
40 public static function pop_posts( $args ) {
41 global $tptn_settings, $post;
42
43 $defaults = array(
44 'daily' => false,
45 'instance_id' => 1,
46 'is_widget' => false,
47 'is_shortcode' => false,
48 'is_manual' => false,
49 'is_block' => false,
50 'echo' => false,
51 'strict_limit' => false,
52 'heading' => 1,
53 'offset' => 0,
54 'extra_class' => '',
55 );
56
57 // Merge the $defaults array with the $tptn_settings array.
58 $defaults = array_merge( $defaults, \tptn_settings_defaults(), $tptn_settings );
59
60 // Parse incomming $args into an array and merge it with $defaults.
61 $args = wp_parse_args( $args, $defaults );
62
63 // Sanitize the attributes.
64 $args = Helpers::sanitize_args( $args );
65
66 // Short circuit flag.
67 $short_circuit = false;
68
69 /**
70 * Allow a short circuit flag to be set to exit at this stage. Set to true to exit.
71 *
72 * @since 4.0.0
73 *
74 * @param bool $short_circuit Short circuit filter.
75 * @param array $args Arguments array.
76 * @param \WP_Post $post Current Post object.
77 */
78 $short_circuit = apply_filters( 'get_tptn_short_circuit', $short_circuit, $args, $post );
79
80 if ( $short_circuit ) {
81 return ''; // Exit without adding popular posts.
82 }
83
84 $output = '';
85
86 /**
87 * Fires before the output processing begins.
88 *
89 * @since 3.1.0
90 *
91 * @param string $output Formatted list of top posts.
92 * @param array $args Array of arguments.
93 * @param \WP_Post $post Current Post object.
94 */
95 do_action( 'pre_tptn_pop_posts', $output, $args, $post );
96
97 // Check exclusions.
98 if ( self::exclude_on( $post, $args ) ) {
99 return '';
100 }
101
102 // Check if the cache is enabled and if the output exists. If so, return the output.
103 if ( $args['cache'] ) {
104 $cache_name = \WebberZone\Top_Ten\Util\Cache::get_key( $args );
105
106 $output = get_transient( $cache_name );
107
108 if ( false !== $output ) {
109
110 /**
111 * Filter the output
112 *
113 * @since 1.9.8.5
114 *
115 * @param string $output Formatted list of top posts
116 * @param array $args Array of arguments
117 */
118 return apply_filters( 'tptn_pop_posts', $output, $args );
119 }
120 }
121
122 // Get thumbnail size.
123 list( $args['thumb_width'], $args['thumb_height'] ) = Media_Handler::get_thumb_size( $args['thumb_size'] );
124
125 // Retrieve the popular posts.
126 $results = self::get_posts( $args );
127
128 $counter = 0;
129
130 // Override tptn_styles if post_thumb_op is text_only.
131 $args['tptn_styles'] = ( 'text_only' === $args['post_thumb_op'] ) ? 'text_only' : $args['tptn_styles'];
132 $style_array = Styles_Handler::get_style( $args['tptn_styles'] );
133
134 $post_classes = array(
135 'main' => $args['daily'] ? 'tptn_posts_daily ' : 'tptn_posts ',
136 'widget' => $args['is_widget'] ? 'tptn_posts_widget tptn_posts_widget-' . $args['instance_id'] : '',
137 'shortcode' => $args['is_shortcode'] ? 'tptn_posts_shortcode' : '',
138 'block' => $args['is_block'] ? 'tptn_posts_block' : '',
139 'extra_class' => $args['extra_class'],
140 'style' => ! empty( $style_array['name'] ) ? str_replace( '-pro', '', 'tptn-' . $style_array['name'] ) : '',
141 );
142 $post_classes = join( ' ', $post_classes );
143
144 /**
145 * Filter the classes added to the div wrapper of the Top 10.
146 *
147 * @since 2.1.0
148 *
149 * @param string $post_classes Post classes string.
150 */
151 $post_classes = apply_filters( 'tptn_post_class', $post_classes );
152
153 $output .= '<div class="' . $post_classes . '">';
154
155 if ( $results ) {
156
157 $output .= self::heading_title( $args );
158
159 $output .= self::before_list( $args );
160
161 foreach ( $results as $result ) {
162 $switched_blog = false;
163 if ( is_multisite() && ! empty( $result->blog_id ) && (int) get_current_blog_id() !== (int) $result->blog_id ) {
164 add_action( 'switch_blog', 'wz_switch_site_rewrite' );
165 switch_to_blog( $result->blog_id );
166 $switched_blog = true;
167 }
168
169 // Store the count. We'll need this later.
170 $count = $args['daily'] ? 'daily' : 'total';
171 $visits = empty( $result->visits ) ? \WebberZone\Top_Ten\Counter::get_post_count_only( $result->ID, $count ) : (int) $result->visits;
172
173 $result = get_post( $result );
174
175 $output .= self::before_list_item( $args, $result );
176
177 $output .= self::list_link( $args, $result );
178
179 if ( $args['show_author'] ) {
180 $output .= self::get_the_author( $args, $result );
181 }
182
183 if ( $args['show_date'] ) {
184 $output .= '<span class="tptn_date"> ' . self::get_the_date( $args, $result ) . '</span> ';
185 }
186
187 if ( $args['show_excerpt'] ) {
188 $output .= '<span class="tptn_excerpt"> ' . self::get_the_excerpt( $result->ID, $args['excerpt_length'] ) . '</span>';
189 }
190
191 if ( $args['disp_list_count'] ) {
192
193 $output .= ' <span class="tptn_list_count">' . self::get_list_count( $args, $result, $visits ) . '</span>';
194 }
195
196 $tptn_list = '';
197 /**
198 * Filter to add content to the end of each item in the list.
199 *
200 * @since 3.3.0
201 *
202 * @param string $tptn_list Empty string at the end of each list item.
203 * @param object $result Object of the current post result
204 * @param array $args Array of arguments
205 */
206 $output .= apply_filters( 'tptn_list', $tptn_list, $result, $args );
207
208 // Opening span created in tptn_list_link().
209 if ( 'inline' === $args['post_thumb_op'] || 'text_only' === $args['post_thumb_op'] ) {
210 $output .= '</span>';
211 }
212
213 $output .= self::after_list_item( $args, $result );
214
215 ++$counter;
216
217 if ( $counter === (int) $args['limit'] ) {
218 break; // End loop when popular posts limit is reached.
219 }
220
221 if ( $switched_blog ) {
222 restore_current_blog();
223 remove_action( 'switch_blog', 'wz_switch_site_rewrite' );
224 }
225 }
226 if ( $args['show_credit'] ) {
227
228 $output .= self::before_list_item( $args, $result );
229
230 $output .= sprintf(
231 /* translators: 1. Top 10 plugin page link, 2. Link attributes. */
232 __( 'Popular posts by <a href="%1$s" rel="nofollow" %2$s>Top 10 plugin</a>', 'top-10' ),
233 esc_url( 'https://webberzone.com/plugins/top-10/' ),
234 self::link_attributes( $args, $result )
235 );
236
237 $output .= self::after_list_item( $args, $result );
238 }
239
240 $output .= self::after_list( $args );
241
242 $clearfix = '<div class="tptn_clear"></div>';
243
244 /**
245 * Filter the clearfix div tag. This is included after the closing tag to clear any miscellaneous floating elements;
246 *
247 * @since 3.3.0
248 *
249 * @param string $clearfix Contains: <div style="clear:both"></div>
250 */
251 $output .= apply_filters( 'tptn_clearfix', $clearfix );
252
253 } else {
254 $output .= ( $args['blank_output'] ) ? '' : $args['blank_output_text'];
255 }
256 $output .= '</div>';
257
258 // Check if the cache is enabled and if the output exists. If so, return the output.
259 if ( $args['cache'] ) {
260 /**
261 * Filter the cache time which allows a function to override this
262 *
263 * @since 3.3.0
264 *
265 * @param int $cache_time Cache time in seconds
266 * @param array $args Array of all the arguments
267 */
268 $cache_time = apply_filters( 'tptn_cache_time', $args['cache_time'], $args );
269
270 $output .= "<br /><!-- Cached output. Cached time is {$cache_time} seconds -->";
271
272 set_transient( $cache_name, $output, $cache_time );
273 }
274
275 /**
276 * Filter already documented in top-10.php
277 */
278 return apply_filters( 'tptn_pop_posts', $output, $args );
279 }
280
281
282 /**
283 * Function to retrieve the popular posts.
284 *
285 * @since 3.3.0
286 *
287 * @param mixed $args Arguments list.
288 */
289 public static function get_pop_posts( $args = array() ) {
290 global $wpdb, $tptn_settings;
291
292 // Initialise some variables.
293 $fields = array();
294 $where = '';
295 $join = '';
296 $groupby = '';
297 $orderby = '';
298 $limits = '';
299
300 $defaults = array(
301 'daily' => false,
302 'strict_limit' => true,
303 'posts_only' => false,
304 'offset' => 0,
305 );
306
307 // Merge the $defaults array with the $tptn_settings array.
308 $defaults = array_merge( $defaults, \tptn_settings_defaults(), $tptn_settings );
309
310 // Parse incomming $args into an array and merge it with $defaults.
311 $args = wp_parse_args( $args, $defaults );
312
313 $table_name = Database::get_table( $args['daily'] );
314
315 $limit = ( $args['strict_limit'] ) ? $args['limit'] : ( $args['limit'] * 5 );
316 $offset = isset( $args['offset'] ) ? $args['offset'] : 0;
317
318 // If post_types is empty or contains a query string then use parse_str else consider it comma-separated.
319 if ( ! empty( $args['post_types'] ) && is_array( $args['post_types'] ) ) {
320 $post_types = $args['post_types'];
321 } elseif ( ! empty( $args['post_types'] ) && false === strpos( $args['post_types'], '=' ) ) {
322 $post_types = explode( ',', $args['post_types'] );
323 } else {
324 parse_str( $args['post_types'], $post_types ); // Save post types in $post_types variable.
325 }
326
327 // If post_types is empty or if we want all the post types.
328 if ( empty( $post_types ) || 'all' === $args['post_types'] ) {
329 $post_types = get_post_types(
330 array(
331 'public' => true,
332 )
333 );
334 }
335
336 $blog_id = get_current_blog_id();
337
338 $from_date = Helpers::get_from_date( null, $args['daily_range'], $args['hour_range'] );
339
340 /**
341 *
342 * We're going to create a mySQL query that is fully extendable which would look something like this:
343 * "SELECT $fields FROM $wpdb->posts $join WHERE 1=1 $where $groupby $orderby $limits"
344 */
345
346 // Fields to return.
347 $fields[] = "{$table_name}.postnumber";
348 $fields[] = ( $args['daily'] ) ? "SUM({$table_name}.cntaccess) as visits" : "{$table_name}.cntaccess as visits";
349 $fields[] = "{$wpdb->posts}.ID";
350
351 $fields = implode( ', ', $fields );
352
353 // Create the JOIN clause.
354 $join = " INNER JOIN {$wpdb->posts} ON {$table_name}.postnumber={$wpdb->posts}.ID ";
355
356 // Create the base WHERE clause.
357 $where .= $wpdb->prepare( " AND {$table_name}.blog_id = %d ", $blog_id ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
358 $where .= " AND ({$wpdb->posts}.post_status = 'publish' OR {$wpdb->posts}.post_status = 'inherit') "; // Show published posts and attachments.
359
360 if ( $args['daily'] ) {
361 $where .= $wpdb->prepare( " AND {$table_name}.dp_date >= %s ", $from_date ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
362 }
363
364 // Convert exclude post IDs string to array so it can be filtered.
365 $exclude_post_ids = explode( ',', $args['exclude_post_ids'] );
366
367 /** This filter is documented in class-top-ten-query.php */
368 $exclude_post_ids = apply_filters( 'tptn_exclude_post_ids', $exclude_post_ids );
369
370 // Convert it back to string.
371 $exclude_post_ids = implode( ',', array_filter( $exclude_post_ids ) );
372
373 if ( $exclude_post_ids ) { // phpcs:ignore WordPress.PHP.StrictComparisons.LooseComparison
374 $where .= " AND $wpdb->posts.ID NOT IN ({$exclude_post_ids}) ";
375 }
376 $where .= " AND $wpdb->posts.post_type IN ('" . join( "', '", $post_types ) . "') "; // Array of post types.
377
378 // How old should the posts be?
379 if ( $args['how_old'] ) {
380 $how_old_date = Helpers::get_from_date( null, $args['how_old'] + 1, 0 );
381
382 $where .= $wpdb->prepare( " AND $wpdb->posts.post_date > %s ", $how_old_date );
383 }
384
385 if ( isset( $args['include_cat_ids'] ) && ! empty( $args['include_cat_ids'] ) ) {
386 $include_cat_ids = $args['include_cat_ids'];
387
388 $where .= " AND $wpdb->posts.ID IN ( SELECT object_id FROM $wpdb->term_relationships WHERE term_taxonomy_id IN ($include_cat_ids) )";
389 }
390
391 // Create the base GROUP BY clause.
392 if ( $args['daily'] ) {
393 $groupby = ' postnumber ';
394 }
395
396 // Create the base ORDER BY clause.
397 $orderby = ' visits DESC ';
398
399 // Create the base LIMITS clause.
400 $limits .= $wpdb->prepare( ' LIMIT %d, %d ', $offset, $limit );
401
402 /**
403 * Filter the SELECT clause of the query.
404 *
405 * @param string $fields The SELECT clause of the query.
406 */
407 $fields = apply_filters( 'tptn_posts_fields', $fields );
408
409 /**
410 * Filter the JOIN clause of the query.
411 *
412 * @param string $join The JOIN clause of the query.
413 */
414 $join = apply_filters( 'tptn_posts_join', $join );
415
416 /**
417 * Filter the WHERE clause of the query.
418 *
419 * @param string $where The WHERE clause of the query.
420 */
421 $where = apply_filters( 'tptn_posts_where', $where );
422
423 /**
424 * Filter the GROUP BY clause of the query.
425 *
426 * @param string $groupby The GROUP BY clause of the query.
427 */
428 $groupby = apply_filters( 'tptn_posts_groupby', $groupby );
429
430 /**
431 * Filter the ORDER BY clause of the query.
432 *
433 * @param string $orderby The ORDER BY clause of the query.
434 */
435 $orderby = apply_filters( 'tptn_posts_orderby', $orderby );
436
437 /**
438 * Filter the LIMIT clause of the query.
439 *
440 * @param string $limits The LIMIT clause of the query.
441 */
442 $limits = apply_filters( 'tptn_posts_limits', $limits );
443
444 if ( ! empty( $groupby ) ) {
445 $groupby = " GROUP BY {$groupby} ";
446 }
447 if ( ! empty( $orderby ) ) {
448 $orderby = " ORDER BY {$orderby} ";
449 }
450
451 $sql = "SELECT DISTINCT $fields FROM {$table_name} $join WHERE 1=1 $where $groupby $orderby $limits";
452
453 if ( $args['posts_only'] ) { // Return the array of posts only if the variable is set.
454 $results = $wpdb->get_results( $sql, ARRAY_A ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared
455
456 /**
457 * Filter the array of top post IDs.
458 *
459 * @since 1.9.8.5
460 *
461 * @param array $tptn_pop_posts_array Posts array.
462 * @param mixed $args Arguments list
463 */
464 return apply_filters( 'tptn_pop_posts_array', $results, $args );
465 }
466
467 $results = $wpdb->get_results( $sql ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared
468
469 /**
470 * Filter object containing post IDs of popular posts
471 *
472 * @since 2.1.0
473 *
474 * @param object $results Top 10 popular posts object
475 * @param mixed $args Arguments list
476 */
477 return apply_filters( 'get_tptn_pop_posts', $results, $args );
478 }
479
480
481 /**
482 * Function to echo popular posts.
483 *
484 * @since 3.3.0
485 *
486 * @param mixed $args Arguments list.
487 */
488 public function show_pop_posts( $args = null ) {
489 if ( is_array( $args ) ) {
490 $args['manual'] = 1;
491 } else {
492 $args .= '&is_manual=1';
493 }
494
495 echo $this->pop_posts( $args ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
496 }
497
498
499 /**
500 * Function to show daily popular posts.
501 *
502 * @since 3.3.0
503 *
504 * @param mixed $args Arguments list.
505 */
506 public function show_daily_pop_posts( $args = null ) {
507 if ( is_array( $args ) || ! isset( $args ) ) {
508 $args['daily'] = 1;
509 } else {
510 $args .= '&daily=1';
511 }
512
513 $this->show_pop_posts( $args );
514 }
515
516 /**
517 * Retrieves an array of the popular posts.
518 *
519 * The defaults are as follows:
520 *
521 * @since 3.3.0
522 *
523 * @see Top_Ten_Query::prepare_query_args()
524 *
525 * @param array $args Optional. Arguments to retrieve posts. See WP_Query::parse_query() for all available arguments.
526 * @return \WP_Post[]|int[] Array of post objects or post IDs.
527 */
528 public static function get_posts( $args = array() ) {
529
530 $get_posts = new \Top_Ten_Query( $args );
531
532 // Apply PHP-side exclusions and limit the results.
533 $filtered_posts = self::filter_posts_by_exclusions( $get_posts->posts, $args );
534
535 /**
536 * Filter array of post IDs or objects.
537 *
538 * @since 3.0.0
539 *
540 * @param \WP_Post[]|int[] $filtered_posts Array of post objects or post IDs after filtering.
541 * @param array $args Arguments to retrieve posts.
542 */
543 return apply_filters( 'get_tptn_posts', $filtered_posts, $args );
544 }
545
546 /**
547 * Returns the link attributes.
548 *
549 * @since 3.3.0
550 *
551 * @param array $args Array of arguments.
552 * @param \WP_Post $result Result object.
553 * @return string Space separated list of link attributes
554 */
555 public static function link_attributes( $args, $result ) {
556
557 $rel_attribute = ( $args['link_nofollow'] ) ? ' rel="nofollow" ' : ' ';
558
559 $target_attribute = ( $args['link_new_window'] ) ? ' target="_blank" ' : ' ';
560
561 $link_attributes = array(
562 'rel_attribute' => $rel_attribute,
563 'target_attribute' => $target_attribute,
564 );
565
566 /**
567 * Filter the title of the popular posts list
568 *
569 * @since 3.3.0
570 *
571 * @param array $link_attributes Array of link attributes
572 * @param array $args Array of arguments
573 * @param \WP_Post $result Result object
574 */
575 $link_attributes = apply_filters( 'tptn_link_attributes', $link_attributes, $args, $result );
576
577 // Convert it to a string.
578 $link_attributes = implode( ' ', $link_attributes );
579
580 return $link_attributes;
581 }
582
583
584 /**
585 * Returns the heading of the popular posts.
586 *
587 * @since 3.3.0
588 *
589 * @param array $args Array of arguments.
590 * @return string Space separated list of link attributes
591 */
592 public static function heading_title( $args ) {
593
594 $title = '';
595
596 if ( $args['heading'] && ! $args['is_widget'] ) {
597 $title = $args['daily'] ? $args['title_daily'] : $args['title'];
598 }
599
600 /**
601 * Filter the title of the Top posts.
602 *
603 * @since 1.9.5
604 *
605 * @param string $title Title/heading of the popular posts list
606 * @param array $args Array of arguments
607 */
608 return apply_filters( 'tptn_heading_title', $title, $args );
609 }
610
611
612 /**
613 * Returns the opening tag of the popular posts list.
614 *
615 * @since 3.3.0
616 *
617 * @param array $args Array of arguments.
618 * @return string Space separated list of link attributes
619 */
620 public static function before_list( $args ) {
621
622 $before_list = $args['before_list'];
623
624 /**
625 * Filter the opening tag of the popular posts list
626 *
627 * @since 1.9.10.1
628 *
629 * @param string $before_list Opening tag set in the Settings Page
630 * @param array $args Array of arguments
631 */
632 return apply_filters( 'tptn_before_list', $before_list, $args );
633 }
634
635
636 /**
637 * Returns the closing tag of the popular posts list.
638 *
639 * @since 3.3.0
640 *
641 * @param array $args Array of arguments.
642 * @return string Space separated list of link attributes
643 */
644 public static function after_list( $args ) {
645
646 $after_list = $args['after_list'];
647
648 /**
649 * Filter the closing tag of the popular posts list
650 *
651 * @since 1.9.10.1
652 *
653 * @param string $after_list Closing tag set in the Settings Page
654 * @param array $args Array of arguments
655 */
656 return apply_filters( 'tptn_after_list', $after_list, $args );
657 }
658
659
660 /**
661 * Returns the opening tag of each list item.
662 *
663 * @since 3.3.0
664 *
665 * @param array $args Array of arguments.
666 * @param \WP_Post $result Object of the current post result.
667 * @return string Space separated list of link attributes
668 */
669 public static function before_list_item( $args, $result ) {
670
671 $before_list_item = $args['before_list_item'];
672
673 /**
674 * Filter the opening tag of each list item.
675 *
676 * @since 1.9.10.1
677 *
678 * @param string $before_list_item Tag before each list item. Can be defined in the Settings page.
679 * @param object $result Object of the current post result
680 * @param array $args Array of arguments
681 */
682 return apply_filters( 'tptn_before_list_item', $before_list_item, $result, $args );
683 }
684
685
686 /**
687 * Returns the closing tag of each list item.
688 *
689 * @since 3.3.0
690 *
691 * @param array $args Array of arguments.
692 * @param \WP_Post $result Object of the current post result.
693 * @return string Space separated list of link attributes
694 */
695 public static function after_list_item( $args, $result ) {
696
697 $after_list_item = $args['after_list_item'];
698
699 /**
700 * Filter the closing tag of each list item.
701 *
702 * @since 1.9.10.1
703 *
704 * @param string $after_list_item Tag after each list item. Can be defined in the Settings page.
705 * @param object $result Object of the current post result
706 * @param array $args Array of arguments
707 */
708 return apply_filters( 'tptn_after_list_item', $after_list_item, $result, $args ); // Pass the post object to the filter.
709 }
710
711
712 /**
713 * Returns the title of each list item.
714 *
715 * @since 3.3.0
716 *
717 * @param array $args Array of arguments.
718 * @param \WP_Post $result Object of the current post result.
719 * @return string Space separated list of link attributes
720 */
721 public static function get_the_title( $args, $result ) {
722
723 $title = Helpers::trim_char( get_the_title( $result->ID ), $args['title_length'] ); // Get the post title and crop it if needed.
724
725 /**
726 * Filter the post title of each list item.
727 *
728 * @since 2.0.0
729 *
730 * @param string $title Title of the post.
731 * @param object $result Object of the current post result
732 * @param array $args Array of arguments
733 */
734 return apply_filters( 'tptn_post_title', $title, $result, $args );
735 }
736
737
738 /**
739 * Returns the author of each list item.
740 *
741 * @since 3.3.0
742 *
743 * @param array $args Array of arguments.
744 * @param \WP_Post $result Object of the current post result.
745 * @return string Space separated list of link attributes
746 */
747 public static function get_the_author( $args, $result ) {
748
749 $author_info = get_userdata( (int) $result->post_author );
750 $author_link = ( false === $author_info ) ? '' : get_author_posts_url( $author_info->ID );
751 $author_name = ( false === $author_info ) ? '' : ucwords( trim( stripslashes( $author_info->display_name ) ) );
752
753 /**
754 * Filter the author name.
755 *
756 * @since 1.9.1
757 *
758 * @param string $author_name Proper name of the post author.
759 * @param object $author_info WP_User object of the post author
760 */
761 $author_name = apply_filters( 'tptn_author_name', $author_name, $author_info );
762
763 if ( ! empty( $author_name ) ) {
764 $tptn_author = '<span class="tptn_author"> ' . __( ' by ', 'top-10' ) . '<a href="' . $author_link . '">' . $author_name . '</a></span> ';
765 } else {
766 $tptn_author = '';
767 }
768
769 /**
770 * Filter the text with the author details.
771 *
772 * @since 2.0.0
773 *
774 * @param string $tptn_author Formatted string with author details and link
775 * @param object $author_info WP_User object of the post author
776 * @param object $result Object of the current post result
777 * @param array $args Array of arguments
778 */
779 return apply_filters( 'tptn_author', $tptn_author, $author_info, $result, $args );
780 }
781
782
783 /**
784 * Returns the formatted list item with link and and thumbnail for each list item.
785 *
786 * @since 3.3.0
787 *
788 * @param array $args Array of arguments.
789 * @param \WP_Post $result Object of the current post result.
790 * @return string Space separated list of link attributes
791 */
792 public static function list_link( $args, $result ) {
793
794 $output = '';
795 $title = self::get_the_title( $args, $result );
796 $link_attributes = self::link_attributes( $args, $result );
797
798 if ( 'after' === $args['post_thumb_op'] ) {
799 $output .= '<a href="' . get_permalink( $result->ID ) . '" ' . $link_attributes . ' class="tptn_link">'; // Add beginning of link.
800 $output .= '<span class="tptn_title">' . $title . '</span>'; // Add title if post thumbnail is to be displayed after.
801 $output .= '</a>'; // Close the link.
802 }
803
804 if ( 'inline' === $args['post_thumb_op'] || 'after' === $args['post_thumb_op'] || 'thumbs_only' === $args['post_thumb_op'] ) {
805 $output .= '<a href="' . get_permalink( $result->ID ) . '" ' . $link_attributes . ' class="tptn_link">'; // Add beginning of link.
806
807 if ( 'text_only' !== $args['tptn_styles'] ) {
808 $output .= Media_Handler::get_the_post_thumbnail(
809 array(
810 'post' => $result,
811 'size' => $args['thumb_size'],
812 'thumb_meta' => $args['thumb_meta'],
813 'thumb_html' => $args['thumb_html'],
814 'thumb_default' => $args['thumb_default'],
815 'thumb_default_show' => $args['thumb_default_show'],
816 'scan_images' => $args['scan_images'],
817 'class' => 'tptn_thumb',
818 )
819 );
820 }
821
822 $output .= '</a>'; // Close the link.
823 }
824
825 if ( 'inline' === $args['post_thumb_op'] || 'text_only' === $args['post_thumb_op'] ) {
826 $output .= '<span class="tptn_after_thumb">';
827 $output .= '<a href="' . get_permalink( $result->ID ) . '" ' . $link_attributes . ' class="tptn_link">'; // Add beginning of link.
828 $output .= '<span class="tptn_title">' . $title . '</span>'; // Add title when required by settings.
829 $output .= '</a>'; // Close the link.
830 }
831
832 /**
833 * Filter Formatted list item with link and and thumbnail.
834 *
835 * @since 3.3.0
836 *
837 * @param string $output Formatted list item with link and and thumbnail
838 * @param object $result Object of the current post result
839 * @param array $args Array of arguments
840 */
841 return apply_filters( 'tptn_list_link', $output, $result, $args );
842 }
843
844
845 /**
846 * Returns the title of each list item.
847 *
848 * @since 3.3.0
849 *
850 * @param array $args Array of arguments.
851 * @param \WP_Post $result Object of the current post result.
852 * @return string Formatted post date
853 */
854 public static function get_the_date( $args, $result ) {
855
856 $date = mysql2date( get_option( 'date_format', 'd/m/y' ), $result->post_date );
857
858 /**
859 * Filter the post title of each list item.
860 *
861 * @since 2.6.0
862 *
863 * @param string $date Title of the post.
864 * @param object $result Object of the current post result
865 * @param array $args Array of arguments
866 */
867 return apply_filters( 'tptn_date', $date, $result, $args );
868 }
869
870
871 /**
872 * Returns the title of each list item.
873 *
874 * @since 3.3.0
875 *
876 * @param array $args Array of arguments.
877 * @param \WP_Post $result Object of the current post result.
878 * @param int $visits Number of visits.
879 * @return string Formatted post date
880 */
881 public static function get_list_count( $args, $result, $visits ) {
882
883 $tptn_list_count = '(' . Helpers::number_format_i18n( $visits ) . ')';
884
885 /**
886 * Filter the formatted list count text.
887 *
888 * @since 2.1.0
889 *
890 * @param string $tptn_list_count Formatted list count
891 * @param int $visits Post count
892 * @param \WP_Post $result Post object
893 * @param array $args Array of arguments.
894 */
895 return apply_filters( 'tptn_list_count', $tptn_list_count, $visits, $result, $args );
896 }
897
898
899 /**
900 * Function to create an excerpt for the post.
901 *
902 * @since 3.3.0
903 * @since 4.0.0 Added $more_link_text parameter. $post parameter can now be a WP_Post instance.
904 *
905 * @param int|\WP_Post $post Post ID or WP_Post instance.
906 * @param int|string $excerpt_length Length of the excerpt in words.
907 * @param bool $use_excerpt Use excerpt instead of content.
908 * @param string|null $more_link_text Read More text. Pass null to use the default "(more&hellip;)" link. Default '' (no Read More link).
909 * @return string Post Excerpt
910 */
911 public static function get_the_excerpt( $post, $excerpt_length = 0, $use_excerpt = true, $more_link_text = '' ) {
912 $content = '';
913
914 $post = get_post( $post );
915 if ( empty( $post ) ) {
916 return '';
917 }
918 if ( $use_excerpt ) {
919 $content = $post->post_excerpt;
920 }
921 if ( empty( $content ) ) {
922 $content = $post->post_content;
923 }
924
925 $output = strip_shortcodes( $content );
926 $output = wp_strip_all_tags( $output, true );
927
928 /**
929 * Filters excerpt generated by CRP before it is trimmed.
930 *
931 * @since 4.0.0
932 *
933 * @param string $output Formatted excerpt.
934 * @param \WP_Post $post Source Post instance.
935 * @param int $excerpt_length Length of the excerpt.
936 * @param boolean $use_excerpt Use the excerpt?
937 * @param string $content Content that is used to create the excerpt.
938 */
939 $output = apply_filters( 'tptn_excerpt_pre_trim', $output, $post, $excerpt_length, $use_excerpt, $content );
940
941 /**
942 * Filters the Read More text of the CRP excerpt.
943 *
944 * @since 3.0.0
945 *
946 * @param string|null $more_link_text Read More text. Null uses the default "(more&hellip;)" link.
947 * @param \WP_Post $post Source Post instance.
948 */
949 $more_link_text = apply_filters( 'tptn_excerpt_more_link_text', $more_link_text, $post );
950
951 if ( null === $more_link_text ) {
952 $more_link_text = sprintf(
953 '<span aria-label="%1$s">%2$s</span>',
954 sprintf(
955 /* translators: %s: Post title. */
956 __( 'Continue reading %s', 'top-10' ),
957 the_title_attribute(
958 array(
959 'echo' => false,
960 'post' => $post,
961 )
962 )
963 ),
964 __( '(more&hellip;)', 'top-10' )
965 );
966 }
967
968 if ( ! empty( $more_link_text ) ) {
969 $more_link_element = ' <a href="' . get_permalink( $post ) . "#more-{$post->ID}\" class=\"tptn_read_more_link\">$more_link_text</a>";
970 } else {
971 $more_link_element = '';
972 }
973
974 /**
975 * Filters the Read More link text of the CRP excerpt.
976 *
977 * @since 4.0.0
978 *
979 * @param string $more_link_element Read More link element.
980 * @param string $more_link_text Read More text.
981 * @param \WP_Post $post Source Post instance.
982 */
983 $more_link_element = apply_filters( 'tptn_excerpt_more_link', $more_link_element, $more_link_text, $post );
984
985 if ( $excerpt_length > 0 ) {
986 $more_link_element = empty( $more_link_element ) ? null : $more_link_element;
987
988 $output = wp_trim_words( $output, $excerpt_length, $more_link_element );
989 }
990
991 if ( post_password_required( $post ) ) {
992 $output = __( 'There is no excerpt because this is a protected post.', 'top-10' );
993 }
994
995 /**
996 * Filters excerpt generated by CRP.
997 *
998 * @since 1.9.10.1
999 * @since 4.0.0 Changed second parameter to WP_Post instance instead of ID.
1000 *
1001 * @param string $output Formatted excerpt.
1002 * @param \WP_Post $post Source Post instance.
1003 * @param int $excerpt_length Length of the excerpt.
1004 * @param boolean $use_excerpt Use the excerpt?
1005 */
1006 return apply_filters( 'tptn_excerpt', $output, $post, $excerpt_length, $use_excerpt );
1007 }
1008
1009 /**
1010 * Get the default thumbnail.
1011 *
1012 * @since 4.0.0
1013 *
1014 * @return string Default thumbnail.
1015 */
1016 public static function get_default_thumbnail() {
1017 return defined( 'TOP_TEN_DEFAULT_THUMBNAIL_URL' ) ? TOP_TEN_DEFAULT_THUMBNAIL_URL : TOP_TEN_PLUGIN_URL . 'default.png';
1018 }
1019
1020 /**
1021 * Processes exclusion settings to return if the popular posts should not be displayed on the current post.
1022 *
1023 * @since 3.3.0
1024 *
1025 * @param int|\WP_Post|null $post Post ID or post object. Defaults to global $post. Default null.
1026 * @param array $args Parameters in a query string format.
1027 * @return bool True if any exclusion setting is matched.
1028 */
1029 public static function exclude_on( $post = null, $args = array() ) {
1030 $post = get_post( $post );
1031 if ( ! $post ) {
1032 return false;
1033 }
1034
1035 // If this post ID is in the DO NOT DISPLAY list.
1036 $exclude_on_post_ids = isset( $args['exclude_on_post_ids'] ) ? $args['exclude_on_post_ids'] : \tptn_get_option( 'exclude_on_post_ids' );
1037 $exclude_on_post_ids = wp_parse_id_list( $exclude_on_post_ids );
1038 if ( in_array( (int) $post->ID, $exclude_on_post_ids, true ) ) {
1039 return true;
1040 }
1041
1042 // If this post type is in the DO NOT DISPLAY list.
1043 $exclude_on_post_types = isset( $args['exclude_on_post_types'] ) ? $args['exclude_on_post_types'] : \tptn_get_option( 'exclude_on_post_types' );
1044 $exclude_on_post_types = wp_parse_list( $exclude_on_post_types );
1045
1046 if ( in_array( $post->post_type, $exclude_on_post_types, true ) ) {
1047 return true;
1048 }
1049
1050 // If this post's category is in the DO NOT DISPLAY list.
1051 $exclude_on_categories = isset( $args['exclude_on_categories'] ) ? $args['exclude_on_categories'] : \tptn_get_option( 'exclude_on_categories' );
1052 $exclude_on_categories = wp_parse_id_list( $exclude_on_categories );
1053 $post_categories = get_the_terms( $post->ID, 'category' );
1054 $categories = array();
1055 if ( ! empty( $post_categories ) && ! is_wp_error( $post_categories ) ) {
1056 $categories = wp_list_pluck( $post_categories, 'term_taxonomy_id' );
1057 }
1058 if ( ! empty( array_intersect( $exclude_on_categories, $categories ) ) ) {
1059 return true;
1060 }
1061
1062 // If the DO NOT DISPLAY meta field is set.
1063 if ( ( isset( $args['is_shortcode'] ) && ! $args['is_shortcode'] ) &&
1064 ( isset( $args['is_manual'] ) && ! $args['is_manual'] ) &&
1065 ( isset( $args['is_block'] ) && ! $args['is_block'] ) ) {
1066 $tptn_post_meta = get_post_meta( $post->ID, 'tptn_post_meta', true );
1067
1068 if ( isset( $tptn_post_meta['disable_here'] ) ) {
1069 $disable_here = $tptn_post_meta['disable_here'];
1070 } else {
1071 $disable_here = 0;
1072 }
1073
1074 if ( $disable_here ) {
1075 return true;
1076 }
1077 }
1078
1079 return false;
1080 }
1081
1082 /**
1083 * Get static excluded post IDs that should affect caching.
1084 *
1085 * These are exclusions that are consistent across requests for the same configuration.
1086 *
1087 * @since 4.2.0
1088 *
1089 * @param array $args Arguments array.
1090 * @return array Array of post IDs to exclude statically.
1091 */
1092 public static function get_static_excluded_ids( $args ) {
1093 global $wpdb;
1094
1095 $exclude_post_ids = empty( $args['exclude_post_ids'] ) ? array() : wp_parse_id_list( $args['exclude_post_ids'] );
1096
1097 // Prepare the query to find all posts with 'exclude_this_post' set in the meta.
1098 $post_metas = $wpdb->get_results( // phpcs:ignore WordPress.DB.DirectDatabaseQuery
1099 $wpdb->prepare(
1100 "SELECT post_id, meta_value FROM {$wpdb->postmeta} WHERE meta_key = %s",
1101 'tptn_post_meta'
1102 ),
1103 ARRAY_A
1104 );
1105
1106 foreach ( $post_metas as $post_meta ) {
1107 $meta_value = maybe_unserialize( $post_meta['meta_value'] );
1108
1109 if ( $meta_value['exclude_this_post'] ) {
1110 $exclude_post_ids[] = (int) $post_meta['post_id'];
1111 }
1112 }
1113
1114 // Exclude page_on_front and page_for_posts.
1115 if ( 'page' === get_option( 'show_on_front' ) && \tptn_get_option( 'exclude_front' ) ) {
1116 $page_on_front = get_option( 'page_on_front' );
1117 $page_for_posts = get_option( 'page_for_posts' );
1118 if ( $page_on_front > 0 ) {
1119 $exclude_post_ids[] = $page_on_front;
1120 }
1121 if ( $page_for_posts > 0 ) {
1122 $exclude_post_ids[] = $page_for_posts;
1123 }
1124 }
1125
1126 /**
1127 * Filter static exclude post IDs array.
1128 *
1129 * @since 4.2.0
1130 *
1131 * @param array $exclude_post_ids Array of post IDs.
1132 * @param array $args Arguments array.
1133 */
1134 $exclude_post_ids = apply_filters( 'tptn_static_exclude_post_ids', $exclude_post_ids, $args );
1135
1136 /**
1137 * Filter exclude post IDs array for backward compatibility.
1138 *
1139 * @since 2.2.0
1140 * @since 3.0.0 Added $args
1141 *
1142 * @param array $exclude_post_ids Array of post IDs.
1143 * @param array $args Arguments array.
1144 */
1145 $exclude_post_ids = apply_filters( 'tptn_exclude_post_ids', $exclude_post_ids, $args );
1146
1147 return array_unique( $exclude_post_ids );
1148 }
1149
1150 /**
1151 * Get dynamic excluded post IDs that are per-request and should not affect caching.
1152 *
1153 * @since 4.2.0
1154 *
1155 * @param array $args Arguments array.
1156 * @return array Array of post IDs to exclude dynamically.
1157 */
1158 public static function get_dynamic_excluded_ids( $args ) {
1159 $exclude_post_ids = array();
1160
1161 if ( ! empty( $args['exclude_current_post'] ) ) {
1162 $exclude_post_ids[] = (int) get_the_ID();
1163 }
1164
1165 /**
1166 * Filter dynamic exclude post IDs array.
1167 *
1168 * @since 4.2.0
1169 *
1170 * @param array $exclude_post_ids Array of post IDs.
1171 * @param array $args Arguments array.
1172 */
1173 $exclude_post_ids = apply_filters( 'tptn_dynamic_exclude_post_ids', $exclude_post_ids, $args );
1174
1175 return array_unique( $exclude_post_ids );
1176 }
1177
1178 /**
1179 * Filter posts array by excluded IDs and return requested limit.
1180 *
1181 * @since 4.2.0
1182 *
1183 * @param \WP_Post[] $posts Array of post objects.
1184 * @param array $args Arguments array.
1185 * @return \WP_Post[] Filtered array of post objects.
1186 */
1187 protected static function filter_posts_by_exclusions( $posts, $args ): array {
1188 if ( empty( $posts ) ) {
1189 return $posts;
1190 }
1191
1192 $static_excluded_ids = self::get_static_excluded_ids( $args );
1193 $dynamic_excluded_ids = self::get_dynamic_excluded_ids( $args );
1194 $all_excluded_ids = array_unique( array_merge( $static_excluded_ids, $dynamic_excluded_ids ) );
1195
1196 if ( empty( $all_excluded_ids ) ) {
1197 return array_slice( $posts, 0, (int) $args['limit'] );
1198 }
1199
1200 // Filter posts by excluded IDs.
1201 $filtered_posts = array_filter(
1202 $posts,
1203 function ( $post ) use ( $all_excluded_ids ) {
1204 return ! in_array( $post->ID, $all_excluded_ids, true );
1205 }
1206 );
1207
1208 // Re-index array and slice to requested limit.
1209 $filtered_posts = array_values( $filtered_posts );
1210 return array_slice( $filtered_posts, 0, (int) $args['limit'] );
1211 }
1212 }
1213