PluginProbe ʕ •ᴥ•ʔ
WP Popular Posts / 7.3.2
WP Popular Posts v7.3.2
4.0.8 4.0.9 4.1.0 4.1.1 4.1.2 4.2.0 4.2.1 4.2.2 5.0.0 5.0.1 5.0.2 5.1.0 5.2.0 5.2.1 5.2.2 5.2.3 5.2.4 5.3.0 5.3.1 5.3.2 5.3.3 5.3.4 5.3.5 5.3.6 5.4.0 5.4.1 5.4.2 5.5.0 5.5.1 6.0.0 6.0.1 6.0.2 6.0.3 6.0.4 6.0.5 6.1.0 6.1.1 6.1.2 6.1.3 6.1.4 6.2.0 6.2.1 6.3.0 6.3.1 6.3.2 6.3.3 6.3.4 6.4.0 6.4.1 6.4.2 7.0.0 7.0.1 7.1.0 7.2.0 7.3.0 7.3.1 7.3.2 7.3.3 7.3.4 7.3.5 7.3.6 7.3.7 7.3.8 7.4.0 trunk 2.3.7 3.0.0 3.0.1 3.0.2 3.0.3 3.1.0 3.1.1 3.2.0 3.2.1 3.2.2 3.2.3 3.3.0 3.3.1 3.3.2 3.3.3 3.3.4 4.0.0 4.0.1 4.0.10 4.0.11 4.0.12 4.0.13 4.0.2 4.0.3 4.0.5 4.0.6
wordpress-popular-posts / src / Output.php
wordpress-popular-posts / src Last commit date
Activation 1 year ago Admin 1 year ago Block 1 year ago Compatibility 1 year ago Container 1 year ago Front 1 year ago Rest 1 year ago Shortcode 1 year ago Traits 1 year ago Widget 1 year ago Bootstrap.php 1 year ago Cache.php 1 year ago Helper.php 1 year ago Image.php 1 year ago Output.php 1 year ago Query.php 1 year ago Settings.php 1 year ago Themer.php 1 year ago Translate.php 1 year ago WordPressPopularPosts.php 1 year ago deprecated.php 1 year ago template-tags.php 1 year ago
Output.php
1067 lines
1 <?php
2 /**
3 * This class formats the HTML output of every popular posts listing.
4 *
5 *
6 * @package WordPressPopularPosts
7 * @author Hector Cabrera <me@cabrerahector.com>
8 */
9
10 namespace WordPressPopularPosts;
11
12 class Output {
13
14 /**
15 * Popular posts data.
16 *
17 * @since 4.0.0
18 * @var string
19 */
20 private $data;
21
22 /**
23 * HTML output.
24 *
25 * @since 4.0.0
26 * @var string
27 */
28 private $output;
29
30 /**
31 * Widget / shortcode settings.
32 *
33 * @since 4.0.0
34 * @var array
35 */
36 private $public_options = [];
37
38 /**
39 * Administrative settings.
40 *
41 * @since 2.3.3
42 * @var array
43 */
44 private $admin_options = [];
45
46 /**
47 * Default excerpt 'more' string.
48 *
49 * @since 4.2.1
50 * @var string
51 */
52 private $more;
53
54 /**
55 * Image object
56 *
57 * @since 4.0.2
58 * @var WordPressPopularPosts\Image
59 */
60 private $thumbnail;
61
62 /**
63 * Translate object.
64 *
65 * @var \WordPressPopularPosts\Translate $translate
66 * @access private
67 */
68 private $translate;
69
70 /**
71 * Themer object.
72 *
73 * @var \WordPressPopularPosts\Themer $themer
74 * @access private
75 */
76 private $themer;
77
78 /**
79 * WordPress Date format.
80 *
81 * @var string
82 * @access private
83 */
84 private $wp_date_format;
85
86 /**
87 * Constructor.
88 *
89 * @since 4.0.0
90 * @param array $public_options
91 * @param array $admin_options
92 * @param WordPressPopularPosts\Image $thumbnail
93 * @param WordPressPopularPosts\Translate $translate
94 * @param WordPressPopularPosts\Themer $themer
95 */
96 public function __construct(array $public_options, array $admin_options, Image $thumbnail, Translate $translate, Themer $themer)
97 {
98 $this->public_options = $public_options;
99 $this->admin_options = $admin_options;
100 $this->thumbnail = $thumbnail;
101 $this->translate = $translate;
102 $this->themer = $themer;
103
104 $this->more = '...';
105
106 $this->wp_date_format = get_option('date_format');
107
108 if ( ! $this->wp_date_format ) {
109 $this->wp_date_format = 'F j, Y';
110 }
111 }
112
113 /**
114 * Sets data.
115 *
116 * @since 5.0.0
117 * @param array
118 */
119 public function set_data(array $data = [])
120 {
121 $this->data = $data;
122 }
123
124 /**
125 * Sets public options.
126 *
127 * @since 5.0.0
128 * @param array
129 */
130 public function set_public_options(array $public_options = [])
131 {
132 $this->public_options = Helper::merge_array_r(
133 Settings::get('widget_options'),
134 $public_options
135 );
136 }
137
138 /**
139 * Output the HTML.
140 *
141 * @since 4.0.0
142 */
143 public function output()
144 {
145 echo $this->get_output(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- At this point everything has been escaped / sanitized already
146 }
147
148 /**
149 * Return the HTML.
150 *
151 * @since 4.0.0
152 * @return string
153 */
154 public function get_output()
155 {
156 $this->output = ( WP_DEBUG ? "\n" . '<!-- WordPress Popular Posts v' . WPP_VERSION . ( $this->admin_options['tools']['cache']['active'] ? ' - cached' : '' ) . ' -->' . "\n" : '' ) . $this->output;
157
158 // Attempt to close open tags
159 $this->output = force_balance_tags($this->output);
160
161 /**
162 * @ToDo
163 *
164 * Look into \Dom\HTMLDocument (PHP 8.4 apparently) to see
165 * if it's a good alternative to the code below.
166 */
167
168 if ( extension_loaded('mbstring') && function_exists('mb_encode_numericentity') ) {
169 // Process special characters
170 $html = htmlspecialchars_decode(mb_encode_numericentity(htmlentities(trim($this->output), ENT_QUOTES, 'UTF-8'), [0x80, 0x10FFFF, 0, ~0], 'UTF-8'));
171
172 // Remove empty tags
173 $clean_html = '';
174 $html = '<!DOCTYPE html><html><head><meta charset="UTF-8" /></head><body>' . $html . '</body></html>';
175
176 $dom = new \DOMDocument();
177 $dom->loadHTML($html, LIBXML_NOERROR | LIBXML_NOWARNING | LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
178 $xpath = new \DOMXPath($dom);
179
180 while ( ($node_list = $xpath->query('//*[not(*) and not(@*) and not(text()[normalize-space()])]')) && $node_list->length ) {
181 foreach ($node_list as $node) {
182 $node->parentNode->removeChild($node);
183 }
184 }
185
186 $body = $dom->getElementsByTagName('body')->item(0);
187
188 if ( isset($body->childNodes) ) {
189 foreach( $body->childNodes as $node ) {
190 $clean_html .= $dom->saveHTML($node);
191 }
192 }
193
194 $this->output = trim($clean_html);
195 } else {
196 if ( defined('WP_DEBUG') && WP_DEBUG ) {
197 trigger_error('WordPress Popular Posts - looks like PHP\'s mbstring extension isn\'t enabled on this site. Please enable it for the plugin to be able to properly format your popular post list.', E_USER_WARNING);
198 }
199 }
200
201 // Sanitize HTML
202 $this->output = Helper::sanitize_html($this->output, $this->public_options);
203
204 return $this->output;
205 }
206
207 /**
208 * Build the HTML output.
209 *
210 * @since 4.0.0
211 */
212 public function build_output()
213 {
214 // Got some posts, format 'em!
215 if ( ! empty($this->data) ) {
216
217 $this->output = '';
218
219 // Allow WP themers / coders access to raw data
220 // so they can build their own output
221 if ( has_filter('wpp_custom_html') ) {
222 $this->output .= apply_filters('wpp_custom_html', $this->data, $this->public_options);
223 return;
224 }
225
226 if (
227 isset($this->public_options['theme']['name'])
228 && $this->public_options['theme']['name']
229 ) {
230 $this->output .= '<div class="popular-posts-sr">';
231
232 if ( @file_exists(get_stylesheet_directory() . '/wordpress-popular-posts/themes/' . $this->public_options['theme']['name'] . '/style.css') ) {
233 $theme_stylesheet = get_stylesheet_directory() . '/wordpress-popular-posts/themes/' . $this->public_options['theme']['name'] . '/style.css';
234 } else {
235 $theme_stylesheet = $this->themer->get_theme($this->public_options['theme']['name'])['path'] . '/style.css';
236 }
237
238 $theme_css_rules = wp_strip_all_tags(file_get_contents($theme_stylesheet), true); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents -- We're loading a local file
239 $additional_styles = '';
240
241 if ( has_filter('wpp_additional_theme_styles') ) {
242 $additional_styles = wp_strip_all_tags(apply_filters('wpp_additional_theme_styles', '', $this->public_options['theme']['name']), true);
243
244 if ( $additional_styles ) {
245 $additional_styles = ' /* additional rules */ ' . $additional_styles;
246 }
247 }
248
249 $this->output .= '<style>' . $theme_css_rules . $additional_styles . '</style>';
250 }
251
252 /* Open HTML wrapper */
253 // Output a custom wrapper
254 if (
255 isset($this->public_options['markup']['custom_html'])
256 && $this->public_options['markup']['custom_html']
257 && isset($this->public_options['markup']['wpp-start'])
258 && isset($this->public_options['markup']['wpp-end'])
259 ){
260 $this->output .= "\n" . htmlspecialchars_decode($this->public_options['markup']['wpp-start'], ENT_QUOTES) . "\n";
261 }
262 // Output the default wrapper
263 else {
264
265 $classes = 'wpp-list';
266
267 if ( $this->public_options['thumbnail']['active'] ) {
268 $classes .= ' wpp-list-with-thumbnails';
269 }
270
271 $this->output .= "\n<ul class=\"{$classes}\">\n";
272
273 }
274
275 $position = 0;
276
277 // Format each post
278 foreach( $this->data as $post_object ) {
279 $position++;
280 $this->output .= $this->render_post($post_object, $position);
281 }
282
283 /* Close HTML wrapper */
284 // Output a custom wrapper
285 if (
286 isset($this->public_options['markup']['custom_html'])
287 && $this->public_options['markup']['custom_html']
288 && isset($this->public_options['markup']['wpp-start'])
289 && isset($this->public_options['markup']['wpp-end'])
290 ){
291 $this->output .= "\n" . htmlspecialchars_decode($this->public_options['markup']['wpp-end'], ENT_QUOTES) . "\n";
292 }
293 // Output default wrapper
294 else {
295 $this->output .= '</ul>' . "\n";
296 }
297
298 if (
299 isset($this->public_options['theme']['name'])
300 && $this->public_options['theme']['name']
301 ) {
302 $this->output .= '</div>';
303 }
304
305 }
306 // Got nothing to show, give 'em the old "Sorry. No data so far." message!
307 else {
308 $this->output = apply_filters('wpp_no_data', '<p class="wpp-no-data">' . __('Sorry. No data so far.', 'wordpress-popular-posts') . '</p>');
309 }
310 }
311
312 /**
313 * Build the HTML markup for a single post.
314 *
315 * @since 4.0.0
316 * @access private
317 * @param object $post_object
318 * @param integer $position
319 * @return string
320 */
321 private function render_post(\stdClass $post_object, int $position = 1)
322 {
323 $is_single = $this->is_single();
324 $post = '';
325 $post_id = $post_object->id;
326 $trid = $this->translate->get_object_id(
327 $post_object->id,
328 get_post_type($post_object->id)
329 );
330
331 if ( $trid && $post_id != $trid ) {
332 $post_id = $trid;
333 }
334
335 $is_current_post = ( $is_single && ($is_single == $post_id || $is_single == $post_object->id) ) ? true : false;
336
337 // Permalink
338 $permalink = esc_url($this->get_permalink($post_object, $post_id));
339
340 // Post title (and title attribute)
341 $post_title_attr = esc_attr(wp_strip_all_tags($this->get_title($post_object, $post_id)));
342 $post_title = $this->get_title($post_object, $post_id);
343
344 if ( $this->public_options['shorten_title']['active'] ) {
345 $length = ( filter_var($this->public_options['shorten_title']['length'], FILTER_VALIDATE_INT) && $this->public_options['shorten_title']['length'] > 0 )
346 ? $this->public_options['shorten_title']['length']
347 : 25;
348
349 $more = $this->public_options['shorten_title']['words'] ? ' ' . $this->more : $this->more;
350 $more = apply_filters('wpp_title_more', $more);
351 $post_title = Helper::truncate($post_title, $length, $this->public_options['shorten_title']['words'], $more);
352 }
353
354 // Thumbnail
355 $post_thumbnail = $this->get_thumbnail($post_id);
356
357 // Post excerpt
358 $post_excerpt = $this->get_excerpt($post_object, $post_id);
359
360 // Post rating
361 $post_rating = $this->get_rating($post_object);
362
363 /**
364 * Post meta
365 */
366
367 // Post date
368 $post_date = $this->get_date($post_object);
369
370 // Post taxonomies
371 $post_taxonomies = $this->get_taxonomies($post_id);
372
373 // Post author
374 $post_author = $this->get_author($post_object, $post_id);
375
376 // Post views count
377 $post_views = $this->get_pageviews($post_object);
378
379 // Post comments count
380 $post_comments = $this->get_comments($post_object);
381
382 // Post meta
383 $meta_arr = $this->get_metadata(
384 $post_object,
385 $post_id,
386 $post_date,
387 $post_taxonomies,
388 $post_author,
389 $post_views,
390 $post_comments
391 );
392
393 if (
394 is_array($meta_arr)
395 && ! empty($meta_arr)
396 && 'views' == $this->public_options['order_by']
397 ) {
398 $keys = ['views', 'comments', 'author', 'date', 'taxonomy'];
399 $new_meta_arr = [];
400
401 foreach($keys as $key) {
402 if ( isset($meta_arr[$key])) {
403 $new_meta_arr[$key] = $meta_arr[$key];
404 }
405 }
406
407 if ( ! empty($new_meta_arr) ) {
408 $meta_arr = $new_meta_arr;
409 }
410 }
411
412 $post_meta_separator = esc_html(apply_filters('wpp_post_meta_separator', ' | '));
413 $post_meta = join($post_meta_separator, $meta_arr);
414
415 $prettify_numbers = apply_filters('wpp_prettify_numbers', true);
416
417 /** Legacy, should be removed */
418 if ( has_filter('wpp_pretiffy_numbers') ) {
419 $prettify_numbers = apply_filters('wpp_pretiffy_numbers', true);
420 }
421
422 // Build custom HTML output
423 if ( $this->public_options['markup']['custom_html'] ) {
424 $data = [
425 'id' => $post_id,
426 'is_current_post' => $is_current_post,
427 'title' => '<a href="' . $permalink . '" ' . ($post_title_attr !== $post_title ? 'title="' . $post_title_attr . '" ' : '' ) . 'class="wpp-post-title" target="' . esc_attr($this->admin_options['tools']['link']['target']) . '">' . $post_title . '</a>',
428 'title_attr' => $post_title_attr,
429 'summary' => $post_excerpt,
430 'stats' => $post_meta,
431 'img' => ( ! empty($post_thumbnail) ) ? '<a href="' . $permalink . '" ' . ($post_title_attr !== $post_title ? 'title="' . $post_title_attr . '" ' : '' ) . 'target="' . esc_attr($this->admin_options['tools']['link']['target']) . '">' . $post_thumbnail . '</a>' : '',
432 'img_no_link' => $post_thumbnail,
433 'url' => $permalink,
434 'text_title' => $post_title,
435 'taxonomy' => $post_taxonomies,
436 'taxonomy_copy' => isset($meta_arr['taxonomy']) ? $meta_arr['taxonomy'] : null,
437 'author' => ( ! empty($post_author) ) ? '<a href="' . esc_url(get_author_posts_url($post_object->uid != $post_id ? get_post_field('post_author', $post_id) : $post_object->uid )) . '">' . esc_html($post_author) . '</a>' : '',
438 'author_copy' => isset($meta_arr['author']) ? $meta_arr['author'] : null,
439 'author_name' => esc_html($post_author),
440 'author_url' => ( ! empty($post_author) ) ? esc_url(get_author_posts_url($post_object->uid != $post_id ? get_post_field('post_author', $post_id) : $post_object->uid)) : '',
441 'views' => ( $this->public_options['order_by'] == 'views' || $this->public_options['order_by'] == 'comments' ) ? ($prettify_numbers ? Helper::prettify_number($post_views) : number_format_i18n($post_views)) : ($prettify_numbers ? Helper::prettify_number($post_views, 2) : number_format_i18n($post_views, 2)),
442 'views_copy' => isset($meta_arr['views']) ? $meta_arr['views'] : null,
443 'comments' => $prettify_numbers ? Helper::prettify_number($post_comments) : number_format_i18n($post_comments),
444 'comments_copy' => isset($meta_arr['comments']) ? $meta_arr['comments'] : null,
445 'date' => $post_date,
446 'date_copy' => isset($meta_arr['date']) ? $meta_arr['date'] : null,
447 'total_items' => count($this->data),
448 'item_position' => $position
449 ];
450 $post = $this->format_content(htmlspecialchars_decode($this->public_options['markup']['post-html'], ENT_QUOTES), $data, $this->public_options['rating']) . "\n";
451 } // Use the "stock" HTML output
452 else {
453 $wpp_post_class = [];
454
455 if ( $is_current_post ) {
456 $wpp_post_class[] = 'current';
457 }
458
459 // Allow themers / plugin developer
460 // to add custom classes to each post
461 $wpp_post_class = apply_filters('wpp_post_class', $wpp_post_class, $post_id);
462
463 $post_thumbnail = ( ! empty($post_thumbnail) )
464 ? "<a href=\"{$permalink}\" " . ($post_title_attr !== $post_title ? "title=\"{$post_title_attr}\" " : '') . 'target="' . esc_attr($this->admin_options['tools']['link']['target']) . "\">{$post_thumbnail}</a>\n"
465 : '';
466
467 $post_excerpt = ( ! empty($post_excerpt) )
468 ? " <span class=\"wpp-excerpt\">{$post_excerpt}</span>\n"
469 : '';
470
471 $post_meta = ( ! empty($post_meta) )
472 ? " <span class=\"wpp-meta post-stats\">{$post_meta}</span>\n"
473 : '';
474
475 $post_rating = ( ! empty($post_rating) )
476 ? " <span class=\"wpp-rating\">{$post_rating}</span>\n"
477 : '';
478
479 $post =
480 '<li' . ( ( is_array($wpp_post_class) && ! empty($wpp_post_class) ) ? ' class="' . esc_attr(implode(' ', $wpp_post_class)) . '"' : '') . ">\n"
481 . $post_thumbnail
482 . "<a href=\"{$permalink}\" " . ($post_title_attr !== $post_title ? "title=\"{$post_title_attr}\" " : '') . 'class="wpp-post-title" target="' . esc_attr($this->admin_options['tools']['link']['target']) . "\">{$post_title}</a>\n"
483 . $post_excerpt
484 . $post_meta
485 . $post_rating
486 . "</li>\n";
487 }
488
489 return apply_filters('wpp_post', $post, $post_object, $this->public_options);
490 }
491
492 /**
493 * Return the processed post/page title.
494 *
495 * @since 3.0.0
496 * @access private
497 * @param object $post_object
498 * @param integer $post_id
499 * @return string
500 */
501 private function get_title(\stdClass $post_object, int $post_id)
502 {
503 $title = '';
504
505 if ( $post_object->id != $post_id ) {
506 $title = get_the_title($post_id);
507 } else {
508 $title = $post_object->title;
509 }
510
511 // Run the_title filter so core/plugin title hooks can
512 // be applied to the post title
513 $title = apply_filters('the_title', $title, $post_object->id);
514
515 return apply_filters('wpp_the_title', $title, $post_object->id, $post_id);
516 }
517
518 /**
519 * Return the permalink.
520 *
521 * @since 4.0.12
522 * @access private
523 * @param object $post_object
524 * @param integer $post_id
525 * @return string
526 */
527 private function get_permalink(\stdClass $post_object, int $post_id) {
528 if ( $post_object->id != $post_id ) {
529 return get_permalink($post_id);
530 }
531
532 return get_permalink($post_object->id);
533 }
534
535 /**
536 * Return the processed thumbnail.
537 *
538 * @since 3.0.0
539 * @access private
540 * @param int $post_id
541 * @return string
542 */
543 private function get_thumbnail(int $post_id)
544 {
545 $thumbnail = '';
546
547 if ( $this->public_options['thumbnail']['active'] ) {
548 $thumbnail = $this->thumbnail->get(
549 $post_id,
550 [
551 $this->public_options['thumbnail']['width'],
552 $this->public_options['thumbnail']['height']
553 ],
554 $this->admin_options['tools']['thumbnail']['source'],
555 $this->public_options['thumbnail']['crop'],
556 $this->public_options['thumbnail']['build']
557 );
558 }
559
560 return $thumbnail;
561 }
562
563 /**
564 * Return post excerpt.
565 *
566 * @since 3.0.0
567 * @access private
568 * @param object $post_object
569 * @param integer $post_id
570 * @return string
571 */
572 private function get_excerpt(\stdClass $post_object, int $post_id)
573 {
574 $excerpt = '';
575
576 if ( $this->public_options['post-excerpt']['active'] ) {
577
578 if ( $post_object->id != $post_id ) {
579 $the_post = get_post($post_id);
580
581 $excerpt = ( empty($the_post->post_excerpt) )
582 ? $the_post->post_content
583 : $the_post->post_excerpt;
584 }
585 else {
586 $excerpt = ( empty($post_object->post_excerpt) )
587 ? $post_object->post_content
588 : $post_object->post_excerpt;
589 }
590
591 // remove caption tags
592 $excerpt = preg_replace('/\[caption.*\[\/caption\]/', '', $excerpt);
593
594 // remove Flash objects
595 $excerpt = preg_replace("/<object[0-9 a-z_?*=\":\-\/\.#\,\\n\\r\\t]+/smi", '', $excerpt);
596
597 // remove iframes
598 $excerpt = preg_replace('/<iframe.*?\/iframe>/i', '', $excerpt);
599
600 // remove WP shortcodes
601 $excerpt = strip_shortcodes($excerpt);
602
603 // remove style/script tags
604 $excerpt = preg_replace('@<(script|style)[^>]*?>.*?</\\1>@si', '', $excerpt);
605
606 // remove blocks that are not appropriate for the excerpt
607 $excerpt = excerpt_remove_blocks($excerpt);
608
609 // remove HTML tags if requested
610 if ( $this->public_options['post-excerpt']['keep_format'] ) {
611 $excerpt = wp_kses(
612 $excerpt,
613 [
614 'a' => [
615 'href' => [],
616 'title' => []
617 ],
618 'em' => [],
619 'strong' => []
620 ]
621 );
622 } else {
623 $excerpt = wp_kses($excerpt, []);
624
625 // remove URLs, too
626 $excerpt = preg_replace('_^(?:(?:https?|ftp)://)(?:\S+(?::\S*)?@)?(?:(?!10(?:\.\d{1,3}){3})(?!127(?:\.\d{1,3}){3})(?!169\.254(?:\.\d{1,3}){2})(?!192\.168(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\x{00a1}-\x{ffff}0-9]+-?)*[a-z\x{00a1}-\x{ffff}0-9]+)(?:\.(?:[a-z\x{00a1}-\x{ffff}0-9]+-?)*[a-z\x{00a1}-\x{ffff}0-9]+)*(?:\.(?:[a-z\x{00a1}-\x{ffff}]{2,})))(?::\d{2,5})?(?:/[^\s]*)?$_iuS', '', $excerpt);
627 }
628
629 // remove HTML comments
630 $excerpt = preg_replace('/<!--.*?-->/ms', '', $excerpt);
631
632 // remove extra whitespaces
633 $excerpt = preg_replace('/\s\s+/', ' ', $excerpt);
634
635 $excerpt = trim($excerpt);
636
637 }
638
639 // Balance tags, if needed
640 if ( '' !== $excerpt ) {
641
642 $more = $this->public_options['post-excerpt']['words'] ? ' ' . $this->more : $this->more;
643 $more = apply_filters('wpp_excerpt_more', $more);
644 $excerpt = Helper::truncate($excerpt, $this->public_options['post-excerpt']['length'], $this->public_options['post-excerpt']['words'], $more);
645
646 if ( $this->public_options['post-excerpt']['keep_format'] ) {
647 $excerpt = force_balance_tags($excerpt);
648 }
649 }
650
651 return $excerpt;
652 }
653
654 /**
655 * Return post rating.
656 *
657 * @since 3.0.0
658 * @access private
659 * @param object $post_object
660 * @return string
661 */
662 private function get_rating(\stdClass $post_object)
663 {
664 $rating = '';
665
666 if ( function_exists('the_ratings_results') && $this->public_options['rating'] ) {
667 $rating = the_ratings_results($post_object->id);
668 }
669
670 return $rating;
671 }
672
673 /**
674 * Get post date.
675 *
676 * @since 3.0.0
677 * @access private
678 * @param object $post_object
679 * @return string
680 */
681 private function get_date(\stdClass $post_object)
682 {
683 $date = '';
684
685 if ( $this->public_options['stats_tag']['date']['active'] ) {
686 if ( 'relative' == $this->public_options['stats_tag']['date']['format'] ) {
687 $date = sprintf(
688 __('%s ago', 'wordpress-popular-posts'),
689 human_time_diff(
690 strtotime($post_object->date),
691 Helper::timestamp()
692 )
693 );
694 } else {
695 $date = date_i18n(
696 ( 'wp_date_format' == $this->public_options['stats_tag']['date']['format'] ? $this->wp_date_format : $this->public_options['stats_tag']['date']['format'] ),
697 strtotime($post_object->date)
698 );
699 }
700 }
701
702 return apply_filters('wpp_the_date', $date, $post_object->id);
703 }
704
705 /**
706 * Get post taxonomies.
707 *
708 * @since 3.0.0
709 * @access private
710 * @param integer $post_id
711 * @return string
712 */
713 private function get_taxonomies(int $post_id)
714 {
715 $post_tax = '';
716
717 if (
718 (isset($this->public_options['stats_tag']['category']) && $this->public_options['stats_tag']['category'])
719 || $this->public_options['stats_tag']['taxonomy']['active']
720 ) {
721
722 $taxonomy = 'category';
723
724 if (
725 $this->public_options['stats_tag']['taxonomy']['active']
726 && ! empty($this->public_options['stats_tag']['taxonomy']['name'])
727 ) {
728 $taxonomy = $this->public_options['stats_tag']['taxonomy']['name'];
729 }
730
731 $terms = wp_get_post_terms($post_id, $taxonomy);
732
733 if ( ! is_wp_error($terms) ) {
734 // Usage: https://wordpress.stackexchange.com/a/46824
735 if ( has_filter('wpp_post_exclude_terms') ) {
736 $args = apply_filters('wpp_post_exclude_terms', []);
737 $terms = wp_list_filter($terms, $args, 'NOT');
738 }
739
740 $terms = apply_filters('wpp_post_terms', $terms);
741
742 if (
743 is_array($terms)
744 && ! empty($terms)
745 ) {
746 $taxonomy_separator = esc_html(apply_filters('wpp_taxonomy_separator', ', '));
747
748 // We're going to use the taxonomy slug as a CSS class so let's escape it just in case
749 $taxonomy = esc_attr($taxonomy);
750
751 foreach ($terms as $term) {
752 $term_link = get_term_link($term);
753
754 if ( is_wp_error($term_link) ) {
755 continue;
756 }
757
758 $term_link = esc_url($this->translate->url($term_link, $this->translate->get_current_language()));
759 $post_tax .= "<a href=\"{$term_link}\" class=\"wpp-taxonomy {$taxonomy} {$taxonomy}-{$term->term_id}\">" . esc_html($term->name) . '</a>' . $taxonomy_separator;
760 }
761 }
762 }
763
764 if ( '' != $post_tax ) {
765 $post_tax = rtrim($post_tax, $taxonomy_separator);
766 }
767
768 }
769
770 return $post_tax;
771 }
772
773 /**
774 * Get post author.
775 *
776 * @since 3.0.0
777 * @access private
778 * @param object $post_object
779 * @param integer $post_id
780 * @return string
781 */
782 private function get_author(\stdClass $post_object, int $post_id)
783 {
784 $author = ( $this->public_options['stats_tag']['author'] )
785 ? get_the_author_meta('display_name', $post_object->uid != $post_id ? get_post_field('post_author', $post_id) : $post_object->uid)
786 : '';
787
788 return $author;
789 }
790
791 /**
792 * Return post views count.
793 *
794 * @since 3.0.0
795 * @access private
796 * @param object $post_object
797 * @return int|float
798 */
799 private function get_pageviews(\stdClass $post_object)
800 {
801 $pageviews = 0;
802
803 if (
804 (
805 $this->public_options['order_by'] == 'views'
806 || $this->public_options['order_by'] == 'avg'
807 || $this->public_options['stats_tag']['views']
808 )
809 && ( isset($post_object->pageviews) || isset($post_object->avg_views) )
810 ) {
811 $pageviews = ( $this->public_options['order_by'] == 'views' || $this->public_options['order_by'] == 'comments' )
812 ? $post_object->pageviews
813 : $post_object->avg_views;
814 }
815
816 return $pageviews;
817 }
818
819 /**
820 * Return post comment count.
821 *
822 * @since 3.0.0
823 * @access private
824 * @param object $post_object
825 * @return int
826 */
827 private function get_comments(\stdClass $post_object)
828 {
829 $comments = ( ( $this->public_options['order_by'] == 'comments' || $this->public_options['stats_tag']['comment_count'] ) && isset($post_object->comment_count) )
830 ? $post_object->comment_count
831 : 0;
832
833 return $comments;
834 }
835
836 /**
837 * Return post metadata.
838 *
839 * @since 3.0.0
840 * @access private
841 * @param object $post_object
842 * @param integer $post_id
843 * @return array
844 */
845 //private function get_metadata(\stdClass $post_object, $post_id)
846 private function get_metadata(\stdClass $post_object, int $post_id, string $date, string $post_tax, string $author, $pageviews, int $comments) /** @TODO: starting PHP 8.0 $pageviews can be declared as mixed $pageviews */
847 {
848 $stats = [];
849
850 $prettify_numbers = apply_filters('wpp_prettify_numbers', true);
851
852 /* Legacy, should be removed */
853 if ( has_filter('wpp_pretiffy_numbers') ) {
854 $prettify_numbers = apply_filters('wpp_pretiffy_numbers', true);
855 }
856
857 // comments
858 if ( $this->public_options['stats_tag']['comment_count'] ) {
859 $comments_text = sprintf(
860 _n('%s comment', '%s comments', $comments, 'wordpress-popular-posts'),
861 $prettify_numbers ? Helper::prettify_number($comments) : number_format_i18n($comments)
862 );
863
864 $stats['comments'] = '<span class="wpp-comments">' . $comments_text . '</span>';
865 }
866
867 // views
868 if ( $this->public_options['stats_tag']['views'] ) {
869 if ( $this->public_options['order_by'] == 'avg' ) {
870 $views_text = sprintf(
871 _n('%s view per day', '%s views per day', $pageviews, 'wordpress-popular-posts'),
872 $prettify_numbers ? Helper::prettify_number($pageviews, 2) : number_format_i18n($pageviews, (fmod($pageviews, 1) !== 0.0 ? 2 : 0))
873 );
874 }
875 else {
876 $views_text = sprintf(
877 _n('%s view', '%s views', $pageviews, 'wordpress-popular-posts'),
878 $prettify_numbers ? Helper::prettify_number($pageviews) : number_format_i18n($pageviews)
879 );
880 }
881
882 $stats['views'] = '<span class="wpp-views">' . $views_text . '</span>';
883 }
884
885 // author
886 if ( $this->public_options['stats_tag']['author'] ) {
887 $author_url = get_author_posts_url($post_object->uid != $post_id ? get_post_field('post_author', $post_id) : $post_object->uid);
888 $display_name = '<a href="' . esc_url($this->translate->url($author_url, $this->translate->get_current_language())) . '">' . esc_html($author) . '</a>';
889 $stats['author'] = '<span class="wpp-author">' . sprintf(__('by %s', 'wordpress-popular-posts'), $display_name) . '</span>';
890 }
891
892 // date
893 if ( $this->public_options['stats_tag']['date']['active'] ) {
894 $stats['date'] = '<span class="wpp-date">' . ( 'relative' == $this->public_options['stats_tag']['date']['format'] ? sprintf(__('posted %s', 'wordpress-popular-posts'), $date) : sprintf(__('posted on %s', 'wordpress-popular-posts'), $date) ) . '</span>';
895 }
896
897 // taxonomy
898 if ( ($this->public_options['stats_tag']['category'] || $this->public_options['stats_tag']['taxonomy']['active']) && $post_tax != '' ) {
899 $stats['taxonomy'] = '<span class="wpp-category">' . sprintf(__('under %s', 'wordpress-popular-posts'), $post_tax) . '</span>';
900 }
901
902 return $stats;
903 }
904
905 /**
906 * Parse content tags.
907 *
908 * @since 1.4.6
909 * @access private
910 * @param string HTML string with content tags
911 * @param array Post data
912 * @param bool Used to display post rating (if functionality is available)
913 * @return string
914 */
915 private function format_content(string $string, array $data, bool $rating) {
916
917 if ( empty($string) || ( empty($data) || ! is_array($data) ) ) {
918 return false;
919 }
920
921 $params = [];
922 $pattern = '/\{(pid|current_class|excerpt|summary|meta|stats|title|title_attr|image|thumb|thumb_img|thumb_url|rating|score|url|text_title|author|author_copy|author_name|author_url|taxonomy|taxonomy_copy|category|category_copy|views|views_copy|comments|comments_copy|date|date_copy|total_items|item_position)\}/i';
923 preg_match_all($pattern, $string, $matches);
924
925 array_map('strtolower', $matches[0]);
926
927 if ( in_array('{pid}', $matches[0]) ) {
928 $string = str_replace('{pid}', $data['id'], $string);
929 }
930
931 if ( in_array('{current_class}', $matches[0]) ) {
932 $string = str_replace('{current_class}', ( $data['is_current_post'] ? 'current' : '' ), $string);
933 }
934
935 if ( in_array('{title}', $matches[0]) ) {
936 $string = str_replace('{title}', $data['title'], $string);
937 }
938
939 if ( in_array('{title_attr}', $matches[0]) ) {
940 $string = str_replace('{title_attr}', $data['title_attr'], $string);
941 }
942
943 if ( in_array('{meta}', $matches[0]) || in_array('{stats}', $matches[0]) ) {
944 $string = str_replace(['{meta}', '{stats}'], $data['stats'], $string);
945 }
946
947 if ( in_array('{excerpt}', $matches[0]) || in_array('{summary}', $matches[0]) ) {
948 $string = str_replace(['{excerpt}', '{summary}'], $data['summary'], $string);
949 }
950
951 if ( in_array('{image}', $matches[0]) || in_array('{thumb}', $matches[0]) ) {
952 $string = str_replace(['{image}', '{thumb}'], $data['img'], $string);
953 }
954
955 if ( in_array('{thumb_img}', $matches[0]) ) {
956 $string = str_replace('{thumb_img}', $data['img_no_link'], $string);
957 }
958
959 if ( in_array('{thumb_url}', $matches[0]) && ! empty($data['img_no_link']) ) {
960 $dom = new \DOMDocument();
961
962 if ( $dom->loadHTML($data['img_no_link']) ) {
963 $img_tag = $dom->getElementsByTagName('img');
964
965 if ( $img_tag->length ) {
966 foreach( $img_tag as $node ) {
967 if ( $node->hasAttribute('src') ) {
968 $src = $node->getAttribute('src');
969 $string = str_replace('{thumb_url}', $src, $string);
970 }
971 }
972 }
973 }
974 }
975
976 // WP-PostRatings check
977 if ( $rating ) {
978 if ( function_exists('the_ratings_results') && in_array('{rating}', $matches[0]) ) {
979 $string = str_replace('{rating}', the_ratings_results($data['id']), $string);
980 }
981
982 if ( function_exists('expand_ratings_template') && in_array('{score}', $matches[0]) ) {
983 $string = str_replace('{score}', expand_ratings_template('%RATINGS_SCORE%', $data['id']), $string);
984 // removing the redundant plus sign
985 $string = str_replace('+', '', $string);
986 }
987 }
988
989 if ( in_array('{url}', $matches[0]) ) {
990 $string = str_replace('{url}', $data['url'], $string);
991 }
992
993 if ( in_array('{text_title}', $matches[0]) ) {
994 $string = str_replace('{text_title}', $data['text_title'], $string);
995 }
996
997 if ( in_array('{author}', $matches[0]) ) {
998 $string = str_replace('{author}', $data['author'], $string);
999 }
1000
1001 if ( in_array('{author_copy}', $matches[0]) ) {
1002 $string = str_replace('{author_copy}', $data['author_copy'], $string);
1003 }
1004
1005 if ( in_array('{author_name}', $matches[0]) ) {
1006 $string = str_replace('{author_name}', $data['author_name'], $string);
1007 }
1008
1009 if ( in_array('{author_url}', $matches[0]) ) {
1010 $string = str_replace('{author_url}', $data['author_url'], $string);
1011 }
1012
1013 if ( in_array('{taxonomy}', $matches[0]) || in_array('{category}', $matches[0]) ) {
1014 $string = str_replace(['{taxonomy}', '{category}'], $data['taxonomy'], $string);
1015 }
1016
1017 if ( in_array('{taxonomy_copy}', $matches[0]) || in_array('{category_copy}', $matches[0]) ) {
1018 $string = str_replace(['{taxonomy_copy}', '{category_copy}'], $data['taxonomy_copy'], $string);
1019 }
1020
1021 if ( in_array('{views}', $matches[0]) ) {
1022 $string = str_replace('{views}', $data['views'], $string);
1023 }
1024
1025 if ( in_array('{views_copy}', $matches[0]) ) {
1026 $string = str_replace('{views_copy}', $data['views_copy'], $string);
1027 }
1028
1029 if ( in_array('{comments}', $matches[0]) ) {
1030 $string = str_replace('{comments}', $data['comments'], $string);
1031 }
1032
1033 if ( in_array('{comments_copy}', $matches[0]) ) {
1034 $string = str_replace('{comments_copy}', $data['comments_copy'], $string);
1035 }
1036
1037 if ( in_array('{date}', $matches[0]) ) {
1038 $string = str_replace('{date}', $data['date'], $string);
1039 }
1040
1041 if ( in_array('{date_copy}', $matches[0]) ) {
1042 $string = str_replace('{date_copy}', $data['date_copy'], $string);
1043 }
1044
1045 if ( in_array('{total_items}', $matches[0]) ) {
1046 $string = str_replace('{total_items}', $data['total_items'], $string);
1047 }
1048
1049 if ( in_array('{item_position}', $matches[0]) ) {
1050 $string = str_replace('{item_position}', $data['item_position'], $string);
1051 }
1052
1053 return apply_filters('wpp_parse_custom_content_tags', $string, $data['id']);
1054 }
1055
1056 /**
1057 * Checks whether we're currently seeing a single post/page/CPT.
1058 *
1059 * @since 5.0.0
1060 * @return int
1061 */
1062 public function is_single()
1063 {
1064 return apply_filters('wpp_is_single', Helper::is_single());
1065 }
1066 }
1067