PluginProbe ʕ •ᴥ•ʔ
WP Popular Posts / 7.3.4
WP Popular Posts v7.3.4
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 8 months ago Admin 8 months ago Block 8 months ago Compatibility 8 months ago Container 8 months ago Front 8 months ago Rest 8 months ago Shortcode 8 months ago Traits 8 months ago Widget 8 months ago Bootstrap.php 8 months ago Cache.php 8 months ago Helper.php 8 months ago Image.php 8 months ago Output.php 8 months ago Query.php 8 months ago Settings.php 8 months ago Themer.php 8 months ago Translate.php 8 months ago Upgrader.php 8 months ago WordPressPopularPosts.php 8 months ago deprecated.php 8 months ago template-tags.php 8 months ago
Output.php
1085 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" . '<!-- WP 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('WP 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 // Opening HTML wrapper
253 $this->output .= $this->build_opening_wrapper();
254
255 // Format each post
256 $position = 0;
257
258 foreach( $this->data as $post_object ) {
259 $position++;
260 $this->output .= $this->render_post($post_object, $position);
261 }
262
263 // Closing HTML wrapper
264 $this->output .= $this->build_closing_wrapper();
265
266 if (
267 isset($this->public_options['theme']['name'])
268 && $this->public_options['theme']['name']
269 ) {
270 $this->output .= '</div>';
271 }
272
273 }
274 // Got nothing to show, give 'em the old "Sorry. No data so far." message!
275 else {
276 $this->output = apply_filters('wpp_no_data', '<p class="wpp-no-data">' . __('Sorry. No data so far.', 'wordpress-popular-posts') . '</p>');
277 }
278 }
279
280 /**
281 * Builds the opening HTML wrapper for the list.
282 *
283 * @since 7.3.3
284 * @return string
285 */
286 public function build_opening_wrapper()
287 {
288 // Custom opening HTML wrapper
289 if (
290 isset($this->public_options['markup']['custom_html'])
291 && $this->public_options['markup']['custom_html']
292 && isset($this->public_options['markup']['wpp-start'])
293 && isset($this->public_options['markup']['wpp-end'])
294 ){
295 return "\n" . htmlspecialchars_decode($this->public_options['markup']['wpp-start'], ENT_QUOTES) . "\n";
296 }
297
298 // Default opening HTML wrapper
299 $classes = 'wpp-list';
300
301 if ( $this->public_options['thumbnail']['active'] ) {
302 $classes .= ' wpp-list-with-thumbnails';
303 }
304
305 return "\n<ul class=\"{$classes}\">\n";
306 }
307
308 /**
309 * Builds the closing HTML wrapper for the list.
310 *
311 * @since 7.3.3
312 * @return string
313 */
314 public function build_closing_wrapper()
315 {
316 // Custom closing HTML wrapper
317 if (
318 isset($this->public_options['markup']['custom_html'])
319 && $this->public_options['markup']['custom_html']
320 && isset($this->public_options['markup']['wpp-start'])
321 && isset($this->public_options['markup']['wpp-end'])
322 ){
323 return "\n" . htmlspecialchars_decode($this->public_options['markup']['wpp-end'], ENT_QUOTES) . "\n";
324 }
325
326 // Default closing HTML wrapper
327 return '</ul>' . "\n";
328 }
329
330 /**
331 * Build the HTML markup for a single post.
332 *
333 * @since 4.0.0
334 * @access private
335 * @param object $post_object
336 * @param integer $position
337 * @return string
338 */
339 private function render_post(\stdClass $post_object, int $position = 1)
340 {
341 $is_single = $this->is_single();
342 $post = '';
343 $post_id = $post_object->id;
344 $trid = $this->translate->get_object_id(
345 $post_object->id,
346 get_post_type($post_object->id)
347 );
348
349 if ( $trid && $post_id != $trid ) {
350 $post_id = $trid;
351 }
352
353 $is_current_post = ( $is_single && ($is_single == $post_id || $is_single == $post_object->id) ) ? true : false;
354
355 // Permalink
356 $permalink = esc_url($this->get_permalink($post_object, $post_id));
357
358 // Post title (and title attribute)
359 $post_title_attr = esc_attr(wp_strip_all_tags($this->get_title($post_object, $post_id)));
360 $post_title = $this->get_title($post_object, $post_id);
361
362 if ( $this->public_options['shorten_title']['active'] ) {
363 $length = ( filter_var($this->public_options['shorten_title']['length'], FILTER_VALIDATE_INT) && $this->public_options['shorten_title']['length'] > 0 )
364 ? $this->public_options['shorten_title']['length']
365 : 25;
366
367 $more = $this->public_options['shorten_title']['words'] ? ' ' . $this->more : $this->more;
368 $more = apply_filters('wpp_title_more', $more);
369 $post_title = Helper::truncate($post_title, $length, $this->public_options['shorten_title']['words'], $more);
370 }
371
372 // Thumbnail
373 $post_thumbnail = $this->get_thumbnail($post_id);
374
375 // Post excerpt
376 $post_excerpt = $this->get_excerpt($post_object, $post_id);
377
378 // Post rating
379 $post_rating = $this->get_rating($post_object);
380
381 /**
382 * Post meta
383 */
384
385 // Post date
386 $post_date = $this->get_date($post_object);
387
388 // Post taxonomies
389 $post_taxonomies = $this->get_taxonomies($post_id);
390
391 // Post author
392 $post_author = $this->get_author($post_object, $post_id);
393
394 // Post views count
395 $post_views = $this->get_pageviews($post_object);
396
397 // Post comments count
398 $post_comments = $this->get_comments($post_object);
399
400 // Post meta
401 $meta_arr = $this->get_metadata(
402 $post_object,
403 $post_id,
404 $post_date,
405 $post_taxonomies,
406 $post_author,
407 $post_views,
408 $post_comments
409 );
410
411 if (
412 is_array($meta_arr)
413 && ! empty($meta_arr)
414 && 'views' == $this->public_options['order_by']
415 ) {
416 $keys = ['views', 'comments', 'author', 'date', 'taxonomy'];
417 $new_meta_arr = [];
418
419 foreach($keys as $key) {
420 if ( isset($meta_arr[$key])) {
421 $new_meta_arr[$key] = $meta_arr[$key];
422 }
423 }
424
425 if ( ! empty($new_meta_arr) ) {
426 $meta_arr = $new_meta_arr;
427 }
428 }
429
430 $post_meta_separator = esc_html(apply_filters('wpp_post_meta_separator', ' | '));
431 $post_meta = join($post_meta_separator, $meta_arr);
432
433 $prettify_numbers = apply_filters('wpp_prettify_numbers', true);
434
435 /** Legacy, should be removed */
436 if ( has_filter('wpp_pretiffy_numbers') ) {
437 $prettify_numbers = apply_filters('wpp_pretiffy_numbers', true);
438 }
439
440 // Build custom HTML output
441 if ( $this->public_options['markup']['custom_html'] ) {
442 $data = [
443 'id' => $post_id,
444 'is_current_post' => $is_current_post,
445 '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>',
446 'title_attr' => $post_title_attr,
447 'summary' => $post_excerpt,
448 'stats' => $post_meta,
449 '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>' : '',
450 'img_no_link' => $post_thumbnail,
451 'url' => $permalink,
452 'text_title' => $post_title,
453 'taxonomy' => $post_taxonomies,
454 'taxonomy_copy' => isset($meta_arr['taxonomy']) ? $meta_arr['taxonomy'] : null,
455 '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>' : '',
456 'author_copy' => isset($meta_arr['author']) ? $meta_arr['author'] : null,
457 'author_name' => esc_html($post_author),
458 '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)) : '',
459 '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)),
460 'views_copy' => isset($meta_arr['views']) ? $meta_arr['views'] : null,
461 'comments' => $prettify_numbers ? Helper::prettify_number($post_comments) : number_format_i18n($post_comments),
462 'comments_copy' => isset($meta_arr['comments']) ? $meta_arr['comments'] : null,
463 'date' => $post_date,
464 'date_copy' => isset($meta_arr['date']) ? $meta_arr['date'] : null,
465 'total_items' => count($this->data),
466 'item_position' => $position
467 ];
468 $post = $this->format_content(htmlspecialchars_decode($this->public_options['markup']['post-html'], ENT_QUOTES), $data, $this->public_options['rating']) . "\n";
469 } // Use the "stock" HTML output
470 else {
471 $wpp_post_class = [];
472
473 if ( $is_current_post ) {
474 $wpp_post_class[] = 'current';
475 }
476
477 // Allow themers / plugin developer
478 // to add custom classes to each post
479 $wpp_post_class = apply_filters('wpp_post_class', $wpp_post_class, $post_id);
480
481 $post_thumbnail = ( ! empty($post_thumbnail) )
482 ? "<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"
483 : '';
484
485 $post_excerpt = ( ! empty($post_excerpt) )
486 ? " <span class=\"wpp-excerpt\">{$post_excerpt}</span>\n"
487 : '';
488
489 $post_meta = ( ! empty($post_meta) )
490 ? " <span class=\"wpp-meta post-stats\">{$post_meta}</span>\n"
491 : '';
492
493 $post_rating = ( ! empty($post_rating) )
494 ? " <span class=\"wpp-rating\">{$post_rating}</span>\n"
495 : '';
496
497 $post =
498 '<li' . ( ( is_array($wpp_post_class) && ! empty($wpp_post_class) ) ? ' class="' . esc_attr(implode(' ', $wpp_post_class)) . '"' : '') . ">\n"
499 . $post_thumbnail
500 . "<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"
501 . $post_excerpt
502 . $post_meta
503 . $post_rating
504 . "</li>\n";
505 }
506
507 return apply_filters('wpp_post', $post, $post_object, $this->public_options);
508 }
509
510 /**
511 * Return the processed post/page title.
512 *
513 * @since 3.0.0
514 * @access private
515 * @param object $post_object
516 * @param integer $post_id
517 * @return string
518 */
519 private function get_title(\stdClass $post_object, int $post_id)
520 {
521 $title = '';
522
523 if ( $post_object->id != $post_id ) {
524 $title = get_the_title($post_id);
525 } else {
526 $title = $post_object->title;
527 }
528
529 // Run the_title filter so core/plugin title hooks can
530 // be applied to the post title
531 $title = apply_filters('the_title', $title, $post_object->id);
532
533 return apply_filters('wpp_the_title', $title, $post_object->id, $post_id);
534 }
535
536 /**
537 * Return the permalink.
538 *
539 * @since 4.0.12
540 * @access private
541 * @param object $post_object
542 * @param integer $post_id
543 * @return string
544 */
545 private function get_permalink(\stdClass $post_object, int $post_id) {
546 if ( $post_object->id != $post_id ) {
547 return get_permalink($post_id);
548 }
549
550 return get_permalink($post_object->id);
551 }
552
553 /**
554 * Return the processed thumbnail.
555 *
556 * @since 3.0.0
557 * @access private
558 * @param int $post_id
559 * @return string
560 */
561 private function get_thumbnail(int $post_id)
562 {
563 $thumbnail = '';
564
565 if ( $this->public_options['thumbnail']['active'] ) {
566 $thumbnail = $this->thumbnail->get(
567 $post_id,
568 [
569 $this->public_options['thumbnail']['width'],
570 $this->public_options['thumbnail']['height']
571 ],
572 $this->admin_options['tools']['thumbnail']['source'],
573 $this->public_options['thumbnail']['crop'],
574 $this->public_options['thumbnail']['build']
575 );
576 }
577
578 return $thumbnail;
579 }
580
581 /**
582 * Return post excerpt.
583 *
584 * @since 3.0.0
585 * @access private
586 * @param object $post_object
587 * @param integer $post_id
588 * @return string
589 */
590 private function get_excerpt(\stdClass $post_object, int $post_id)
591 {
592 $excerpt = '';
593
594 if ( $this->public_options['post-excerpt']['active'] ) {
595
596 if ( $post_object->id != $post_id ) {
597 $the_post = get_post($post_id);
598
599 $excerpt = ( empty($the_post->post_excerpt) )
600 ? $the_post->post_content
601 : $the_post->post_excerpt;
602 }
603 else {
604 $excerpt = ( empty($post_object->post_excerpt) )
605 ? $post_object->post_content
606 : $post_object->post_excerpt;
607 }
608
609 // remove caption tags
610 $excerpt = preg_replace('/\[caption.*\[\/caption\]/', '', $excerpt);
611
612 // remove Flash objects
613 $excerpt = preg_replace("/<object[0-9 a-z_?*=\":\-\/\.#\,\\n\\r\\t]+/smi", '', $excerpt);
614
615 // remove iframes
616 $excerpt = preg_replace('/<iframe.*?\/iframe>/i', '', $excerpt);
617
618 // remove WP shortcodes
619 $excerpt = strip_shortcodes($excerpt);
620
621 // remove style/script tags
622 $excerpt = preg_replace('@<(script|style)[^>]*?>.*?</\\1>@si', '', $excerpt);
623
624 // remove blocks that are not appropriate for the excerpt
625 $excerpt = excerpt_remove_blocks($excerpt);
626
627 // remove HTML tags if requested
628 if ( $this->public_options['post-excerpt']['keep_format'] ) {
629 $excerpt = wp_kses(
630 $excerpt,
631 [
632 'a' => [
633 'href' => [],
634 'title' => []
635 ],
636 'em' => [],
637 'strong' => []
638 ]
639 );
640 } else {
641 $excerpt = wp_kses($excerpt, []);
642
643 // remove URLs, too
644 $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);
645 }
646
647 // remove HTML comments
648 $excerpt = preg_replace('/<!--.*?-->/ms', '', $excerpt);
649
650 // remove extra whitespaces
651 $excerpt = preg_replace('/\s\s+/', ' ', $excerpt);
652
653 $excerpt = trim($excerpt);
654
655 }
656
657 // Balance tags, if needed
658 if ( '' !== $excerpt ) {
659
660 $more = $this->public_options['post-excerpt']['words'] ? ' ' . $this->more : $this->more;
661 $more = apply_filters('wpp_excerpt_more', $more);
662 $excerpt = Helper::truncate($excerpt, $this->public_options['post-excerpt']['length'], $this->public_options['post-excerpt']['words'], $more);
663
664 if ( $this->public_options['post-excerpt']['keep_format'] ) {
665 $excerpt = force_balance_tags($excerpt);
666 }
667 }
668
669 return $excerpt;
670 }
671
672 /**
673 * Return post rating.
674 *
675 * @since 3.0.0
676 * @access private
677 * @param object $post_object
678 * @return string
679 */
680 private function get_rating(\stdClass $post_object)
681 {
682 $rating = '';
683
684 if ( function_exists('the_ratings_results') && $this->public_options['rating'] ) {
685 $rating = the_ratings_results($post_object->id);
686 }
687
688 return $rating;
689 }
690
691 /**
692 * Get post date.
693 *
694 * @since 3.0.0
695 * @access private
696 * @param object $post_object
697 * @return string
698 */
699 private function get_date(\stdClass $post_object)
700 {
701 $date = '';
702
703 if ( $this->public_options['stats_tag']['date']['active'] ) {
704 if ( 'relative' == $this->public_options['stats_tag']['date']['format'] ) {
705 $date = sprintf(
706 __('%s ago', 'wordpress-popular-posts'),
707 human_time_diff(
708 strtotime($post_object->date),
709 Helper::timestamp()
710 )
711 );
712 } else {
713 $date = date_i18n(
714 ( 'wp_date_format' == $this->public_options['stats_tag']['date']['format'] ? $this->wp_date_format : $this->public_options['stats_tag']['date']['format'] ),
715 strtotime($post_object->date)
716 );
717 }
718 }
719
720 return apply_filters('wpp_the_date', $date, $post_object->id);
721 }
722
723 /**
724 * Get post taxonomies.
725 *
726 * @since 3.0.0
727 * @access private
728 * @param integer $post_id
729 * @return string
730 */
731 private function get_taxonomies(int $post_id)
732 {
733 $post_tax = '';
734
735 if (
736 (isset($this->public_options['stats_tag']['category']) && $this->public_options['stats_tag']['category'])
737 || $this->public_options['stats_tag']['taxonomy']['active']
738 ) {
739
740 $taxonomy = 'category';
741
742 if (
743 $this->public_options['stats_tag']['taxonomy']['active']
744 && ! empty($this->public_options['stats_tag']['taxonomy']['name'])
745 ) {
746 $taxonomy = $this->public_options['stats_tag']['taxonomy']['name'];
747 }
748
749 $terms = wp_get_post_terms($post_id, $taxonomy);
750
751 if ( ! is_wp_error($terms) ) {
752 // Usage: https://wordpress.stackexchange.com/a/46824
753 if ( has_filter('wpp_post_exclude_terms') ) {
754 $args = apply_filters('wpp_post_exclude_terms', []);
755 $terms = wp_list_filter($terms, $args, 'NOT');
756 }
757
758 $terms = apply_filters('wpp_post_terms', $terms);
759
760 if (
761 is_array($terms)
762 && ! empty($terms)
763 ) {
764 $taxonomy_separator = esc_html(apply_filters('wpp_taxonomy_separator', ', '));
765
766 // We're going to use the taxonomy slug as a CSS class so let's escape it just in case
767 $taxonomy = esc_attr($taxonomy);
768
769 foreach ($terms as $term) {
770 $term_link = get_term_link($term);
771
772 if ( is_wp_error($term_link) ) {
773 continue;
774 }
775
776 $term_link = esc_url($this->translate->url($term_link, $this->translate->get_current_language()));
777 $post_tax .= "<a href=\"{$term_link}\" class=\"wpp-taxonomy {$taxonomy} {$taxonomy}-{$term->term_id}\">" . esc_html($term->name) . '</a>' . $taxonomy_separator;
778 }
779 }
780 }
781
782 if ( '' != $post_tax ) {
783 $post_tax = rtrim($post_tax, $taxonomy_separator);
784 }
785
786 }
787
788 return $post_tax;
789 }
790
791 /**
792 * Get post author.
793 *
794 * @since 3.0.0
795 * @access private
796 * @param object $post_object
797 * @param integer $post_id
798 * @return string
799 */
800 private function get_author(\stdClass $post_object, int $post_id)
801 {
802 $author = ( $this->public_options['stats_tag']['author'] )
803 ? get_the_author_meta('display_name', $post_object->uid != $post_id ? get_post_field('post_author', $post_id) : $post_object->uid)
804 : '';
805
806 return $author;
807 }
808
809 /**
810 * Return post views count.
811 *
812 * @since 3.0.0
813 * @access private
814 * @param object $post_object
815 * @return int|float
816 */
817 private function get_pageviews(\stdClass $post_object)
818 {
819 $pageviews = 0;
820
821 if (
822 (
823 $this->public_options['order_by'] == 'views'
824 || $this->public_options['order_by'] == 'avg'
825 || $this->public_options['stats_tag']['views']
826 )
827 && ( isset($post_object->pageviews) || isset($post_object->avg_views) )
828 ) {
829 $pageviews = ( $this->public_options['order_by'] == 'views' || $this->public_options['order_by'] == 'comments' )
830 ? $post_object->pageviews
831 : $post_object->avg_views;
832 }
833
834 return $pageviews;
835 }
836
837 /**
838 * Return post comment count.
839 *
840 * @since 3.0.0
841 * @access private
842 * @param object $post_object
843 * @return int
844 */
845 private function get_comments(\stdClass $post_object)
846 {
847 $comments = ( ( $this->public_options['order_by'] == 'comments' || $this->public_options['stats_tag']['comment_count'] ) && isset($post_object->comment_count) )
848 ? $post_object->comment_count
849 : 0;
850
851 return $comments;
852 }
853
854 /**
855 * Return post metadata.
856 *
857 * @since 3.0.0
858 * @access private
859 * @param object $post_object
860 * @param integer $post_id
861 * @return array
862 */
863 //private function get_metadata(\stdClass $post_object, $post_id)
864 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 */
865 {
866 $stats = [];
867
868 $prettify_numbers = apply_filters('wpp_prettify_numbers', true);
869
870 /* Legacy, should be removed */
871 if ( has_filter('wpp_pretiffy_numbers') ) {
872 $prettify_numbers = apply_filters('wpp_pretiffy_numbers', true);
873 }
874
875 // comments
876 if ( $this->public_options['stats_tag']['comment_count'] ) {
877 $comments_text = sprintf(
878 _n('%s comment', '%s comments', $comments, 'wordpress-popular-posts'),
879 $prettify_numbers ? Helper::prettify_number($comments) : number_format_i18n($comments)
880 );
881
882 $stats['comments'] = '<span class="wpp-comments">' . $comments_text . '</span>';
883 }
884
885 // views
886 if ( $this->public_options['stats_tag']['views'] ) {
887 if ( $this->public_options['order_by'] == 'avg' ) {
888 $views_text = sprintf(
889 _n('%s view per day', '%s views per day', $pageviews, 'wordpress-popular-posts'),
890 $prettify_numbers ? Helper::prettify_number($pageviews, 2) : number_format_i18n($pageviews, (fmod($pageviews, 1) !== 0.0 ? 2 : 0))
891 );
892 }
893 else {
894 $views_text = sprintf(
895 _n('%s view', '%s views', $pageviews, 'wordpress-popular-posts'),
896 $prettify_numbers ? Helper::prettify_number($pageviews) : number_format_i18n($pageviews)
897 );
898 }
899
900 $stats['views'] = '<span class="wpp-views">' . $views_text . '</span>';
901 }
902
903 // author
904 if ( $this->public_options['stats_tag']['author'] ) {
905 $author_url = get_author_posts_url($post_object->uid != $post_id ? get_post_field('post_author', $post_id) : $post_object->uid);
906 $display_name = '<a href="' . esc_url($this->translate->url($author_url, $this->translate->get_current_language())) . '">' . esc_html($author) . '</a>';
907 $stats['author'] = '<span class="wpp-author">' . sprintf(__('by %s', 'wordpress-popular-posts'), $display_name) . '</span>';
908 }
909
910 // date
911 if ( $this->public_options['stats_tag']['date']['active'] ) {
912 $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>';
913 }
914
915 // taxonomy
916 if ( ($this->public_options['stats_tag']['category'] || $this->public_options['stats_tag']['taxonomy']['active']) && $post_tax != '' ) {
917 $stats['taxonomy'] = '<span class="wpp-category">' . sprintf(__('under %s', 'wordpress-popular-posts'), $post_tax) . '</span>';
918 }
919
920 return $stats;
921 }
922
923 /**
924 * Parse content tags.
925 *
926 * @since 1.4.6
927 * @access private
928 * @param string HTML string with content tags
929 * @param array Post data
930 * @param bool Used to display post rating (if functionality is available)
931 * @return string
932 */
933 private function format_content(string $string, array $data, bool $rating) {
934
935 if ( empty($string) || ( empty($data) || ! is_array($data) ) ) {
936 return false;
937 }
938
939 $params = [];
940 $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';
941 preg_match_all($pattern, $string, $matches);
942
943 array_map('strtolower', $matches[0]);
944
945 if ( in_array('{pid}', $matches[0]) ) {
946 $string = str_replace('{pid}', $data['id'], $string);
947 }
948
949 if ( in_array('{current_class}', $matches[0]) ) {
950 $string = str_replace('{current_class}', ( $data['is_current_post'] ? 'current' : '' ), $string);
951 }
952
953 if ( in_array('{title}', $matches[0]) ) {
954 $string = str_replace('{title}', $data['title'], $string);
955 }
956
957 if ( in_array('{title_attr}', $matches[0]) ) {
958 $string = str_replace('{title_attr}', $data['title_attr'], $string);
959 }
960
961 if ( in_array('{meta}', $matches[0]) || in_array('{stats}', $matches[0]) ) {
962 $string = str_replace(['{meta}', '{stats}'], $data['stats'], $string);
963 }
964
965 if ( in_array('{excerpt}', $matches[0]) || in_array('{summary}', $matches[0]) ) {
966 $string = str_replace(['{excerpt}', '{summary}'], $data['summary'], $string);
967 }
968
969 if ( in_array('{image}', $matches[0]) || in_array('{thumb}', $matches[0]) ) {
970 $string = str_replace(['{image}', '{thumb}'], $data['img'], $string);
971 }
972
973 if ( in_array('{thumb_img}', $matches[0]) ) {
974 $string = str_replace('{thumb_img}', $data['img_no_link'], $string);
975 }
976
977 if ( in_array('{thumb_url}', $matches[0]) && ! empty($data['img_no_link']) ) {
978 $dom = new \DOMDocument();
979
980 if ( $dom->loadHTML($data['img_no_link']) ) {
981 $img_tag = $dom->getElementsByTagName('img');
982
983 if ( $img_tag->length ) {
984 foreach( $img_tag as $node ) {
985 if ( $node->hasAttribute('src') ) {
986 $src = $node->getAttribute('src');
987 $string = str_replace('{thumb_url}', $src, $string);
988 }
989 }
990 }
991 }
992 }
993
994 // WP-PostRatings check
995 if ( $rating ) {
996 if ( function_exists('the_ratings_results') && in_array('{rating}', $matches[0]) ) {
997 $string = str_replace('{rating}', the_ratings_results($data['id']), $string);
998 }
999
1000 if ( function_exists('expand_ratings_template') && in_array('{score}', $matches[0]) ) {
1001 $string = str_replace('{score}', expand_ratings_template('%RATINGS_SCORE%', $data['id']), $string);
1002 // removing the redundant plus sign
1003 $string = str_replace('+', '', $string);
1004 }
1005 }
1006
1007 if ( in_array('{url}', $matches[0]) ) {
1008 $string = str_replace('{url}', $data['url'], $string);
1009 }
1010
1011 if ( in_array('{text_title}', $matches[0]) ) {
1012 $string = str_replace('{text_title}', $data['text_title'], $string);
1013 }
1014
1015 if ( in_array('{author}', $matches[0]) ) {
1016 $string = str_replace('{author}', $data['author'], $string);
1017 }
1018
1019 if ( in_array('{author_copy}', $matches[0]) ) {
1020 $string = str_replace('{author_copy}', $data['author_copy'], $string);
1021 }
1022
1023 if ( in_array('{author_name}', $matches[0]) ) {
1024 $string = str_replace('{author_name}', $data['author_name'], $string);
1025 }
1026
1027 if ( in_array('{author_url}', $matches[0]) ) {
1028 $string = str_replace('{author_url}', $data['author_url'], $string);
1029 }
1030
1031 if ( in_array('{taxonomy}', $matches[0]) || in_array('{category}', $matches[0]) ) {
1032 $string = str_replace(['{taxonomy}', '{category}'], $data['taxonomy'], $string);
1033 }
1034
1035 if ( in_array('{taxonomy_copy}', $matches[0]) || in_array('{category_copy}', $matches[0]) ) {
1036 $string = str_replace(['{taxonomy_copy}', '{category_copy}'], $data['taxonomy_copy'], $string);
1037 }
1038
1039 if ( in_array('{views}', $matches[0]) ) {
1040 $string = str_replace('{views}', $data['views'], $string);
1041 }
1042
1043 if ( in_array('{views_copy}', $matches[0]) ) {
1044 $string = str_replace('{views_copy}', $data['views_copy'], $string);
1045 }
1046
1047 if ( in_array('{comments}', $matches[0]) ) {
1048 $string = str_replace('{comments}', $data['comments'], $string);
1049 }
1050
1051 if ( in_array('{comments_copy}', $matches[0]) ) {
1052 $string = str_replace('{comments_copy}', $data['comments_copy'], $string);
1053 }
1054
1055 if ( in_array('{date}', $matches[0]) ) {
1056 $string = str_replace('{date}', $data['date'], $string);
1057 }
1058
1059 if ( in_array('{date_copy}', $matches[0]) ) {
1060 $string = str_replace('{date_copy}', $data['date_copy'], $string);
1061 }
1062
1063 if ( in_array('{total_items}', $matches[0]) ) {
1064 $string = str_replace('{total_items}', $data['total_items'], $string);
1065 }
1066
1067 if ( in_array('{item_position}', $matches[0]) ) {
1068 $string = str_replace('{item_position}', $data['item_position'], $string);
1069 }
1070
1071 return apply_filters('wpp_parse_custom_content_tags', $string, $data['id']);
1072 }
1073
1074 /**
1075 * Checks whether we're currently seeing a single post/page/CPT.
1076 *
1077 * @since 5.0.0
1078 * @return int
1079 */
1080 public function is_single()
1081 {
1082 return apply_filters('wpp_is_single', Helper::is_single());
1083 }
1084 }
1085