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