PluginProbe ʕ •ᴥ•ʔ
WP Popular Posts / 6.3.0
WP Popular Posts v6.3.0
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
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 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 $allowed_tags = wp_kses_allowed_html('post');
194
195 if ( isset($allowed_tags['form']) ) {
196 unset($allowed_tags['form']);
197 }
198
199 if (
200 isset($this->public_options['theme']['name'])
201 && $this->public_options['theme']['name']
202 ) {
203 $allowed_tags['style'] = [
204 'id' => 1,
205 'nonce' => 1,
206 ];
207 }
208
209 $allowed_tags['img']['decoding'] = true;
210 $allowed_tags['img']['srcset'] = true;
211
212 $this->output = wp_kses($this->output, $allowed_tags);
213
214 return $this->output;
215 }
216
217 /**
218 * Build the HTML output.
219 *
220 * @since 4.0.0
221 */
222 public function build_output()
223 {
224 // Got some posts, format 'em!
225 if ( ! empty($this->data) ) {
226
227 $this->output = '';
228
229 // Allow WP themers / coders access to raw data
230 // so they can build their own output
231 if ( has_filter('wpp_custom_html') ) {
232 $this->output .= apply_filters('wpp_custom_html', $this->data, $this->public_options);
233 return;
234 }
235
236 if (
237 isset($this->public_options['theme']['name'])
238 && $this->public_options['theme']['name']
239 ) {
240 $this->output .= '<div class="popular-posts-sr">';
241
242 if ( @file_exists(get_stylesheet_directory() . '/wordpress-popular-posts/themes/' . $this->public_options['theme']['name'] . '/style.css') ) {
243 $theme_stylesheet = get_stylesheet_directory() . '/wordpress-popular-posts/themes/' . $this->public_options['theme']['name'] . '/style.css';
244 } else {
245 $theme_stylesheet = $this->themer->get_theme($this->public_options['theme']['name'])['path'] . '/style.css';
246 }
247
248 $theme_css_rules = wp_strip_all_tags(file_get_contents($theme_stylesheet), true);
249 $additional_styles = '';
250
251 if ( has_filter('wpp_additional_theme_styles') ) {
252 $additional_styles = wp_strip_all_tags(apply_filters('wpp_additional_theme_styles', '', $this->public_options['theme']['name']), true);
253
254 if ( $additional_styles ) {
255 $additional_styles = ' /* additional rules */ ' . $additional_styles;
256 }
257 }
258
259 $this->output .= '<style>' . $theme_css_rules . $additional_styles . '</style>';
260 }
261
262 /* Open HTML wrapper */
263 // Output a custom wrapper
264 if (
265 isset($this->public_options['markup']['custom_html'])
266 && $this->public_options['markup']['custom_html']
267 && isset($this->public_options['markup']['wpp-start'])
268 && isset($this->public_options['markup']['wpp-end'])
269 ){
270 $this->output .= "\n" . htmlspecialchars_decode($this->public_options['markup']['wpp-start'], ENT_QUOTES) . "\n";
271 }
272 // Output the default wrapper
273 else {
274
275 $classes = 'wpp-list';
276
277 if ( $this->public_options['thumbnail']['active'] ) {
278 $classes .= ' wpp-list-with-thumbnails';
279 }
280
281 $this->output .= "\n<ul class=\"{$classes}\">\n";
282
283 }
284
285 $position = 0;
286
287 // Format each post
288 foreach( $this->data as $post_object ) {
289 $position++;
290 $this->output .= $this->render_post($post_object, $position);
291 }
292
293 /* Close HTML wrapper */
294 // Output a custom wrapper
295 if (
296 isset($this->public_options['markup']['custom_html'])
297 && $this->public_options['markup']['custom_html']
298 && isset($this->public_options['markup']['wpp-start'])
299 && isset($this->public_options['markup']['wpp-end'])
300 ){
301 $this->output .= "\n" . htmlspecialchars_decode($this->public_options['markup']['wpp-end'], ENT_QUOTES) . "\n";
302 }
303 // Output default wrapper
304 else {
305 $this->output .= '</ul>' . "\n";
306 }
307
308 if (
309 isset($this->public_options['theme']['name'])
310 && $this->public_options['theme']['name']
311 ) {
312 $this->output .= '</div>';
313 }
314
315 }
316 // Got nothing to show, give 'em the old "Sorry. No data so far." message!
317 else {
318 $this->output = apply_filters('wpp_no_data', '<p class="wpp-no-data">' . __('Sorry. No data so far.', 'wordpress-popular-posts') . '</p>');
319 }
320 }
321
322 /**
323 * Build the HTML markup for a single post.
324 *
325 * @since 4.0.0
326 * @access private
327 * @param object $post_object
328 * @param integer $position
329 * @return string
330 */
331 private function render_post(\stdClass $post_object, int $position = 1)
332 {
333 $is_single = $this->is_single();
334 $post = '';
335 $post_id = $post_object->id;
336 $trid = $this->translate->get_object_id(
337 $post_object->id,
338 get_post_type($post_object->id)
339 );
340
341 if ( $post_id != $trid ) {
342 $post_id = $trid;
343 }
344
345 $is_current_post = ( $is_single && ($is_single == $post_id || $is_single == $post_object->id) ) ? true : false;
346
347 // Permalink
348 $permalink = esc_url($this->get_permalink($post_object, $post_id));
349
350 // Post title (and title attribute)
351 $post_title_attr = esc_attr(wp_strip_all_tags($this->get_title($post_object, $post_id)));
352 $post_title = $this->get_title($post_object, $post_id);
353
354 if ( $this->public_options['shorten_title']['active'] ) {
355 $length = ( filter_var($this->public_options['shorten_title']['length'], FILTER_VALIDATE_INT) && $this->public_options['shorten_title']['length'] > 0 )
356 ? $this->public_options['shorten_title']['length']
357 : 25;
358
359 $more = $this->public_options['shorten_title']['words'] ? ' ' . $this->more : $this->more;
360 $more = apply_filters('wpp_title_more', $more);
361 $post_title = Helper::truncate($post_title, $length, $this->public_options['shorten_title']['words'], $more);
362 }
363
364 // Thumbnail
365 $post_thumbnail = $this->get_thumbnail($post_id);
366
367 // Post excerpt
368 $post_excerpt = $this->get_excerpt($post_object, $post_id);
369
370 // Post rating
371 $post_rating = $this->get_rating($post_object);
372
373 /**
374 * Post meta
375 */
376
377 // Post date
378 $post_date = $this->get_date($post_object);
379
380 // Post taxonomies
381 $post_taxonomies = $this->get_taxonomies($post_id);
382
383 // Post author
384 $post_author = $this->get_author($post_object, $post_id);
385
386 // Post views count
387 $post_views = $this->get_pageviews($post_object);
388
389 // Post comments count
390 $post_comments = $this->get_comments($post_object);
391
392 // Post meta
393 $meta_arr = $this->get_metadata(
394 $post_object,
395 $post_id,
396 $post_date,
397 $post_taxonomies,
398 $post_author,
399 $post_views,
400 $post_comments
401 );
402
403 if (
404 is_array($meta_arr)
405 && ! empty($meta_arr)
406 && 'views' == $this->public_options['order_by']
407 ) {
408 $keys = ['views', 'comments', 'author', 'date', 'taxonomy'];
409 $new_meta_arr = [];
410
411 foreach($keys as $key) {
412 if ( isset($meta_arr[$key])) {
413 $new_meta_arr[$key] = $meta_arr[$key];
414 }
415 }
416
417 if ( ! empty($new_meta_arr) ) {
418 $meta_arr = $new_meta_arr;
419 }
420 }
421
422 $post_meta_separator = esc_html(apply_filters('wpp_post_meta_separator', ' | '));
423 $post_meta = join($post_meta_separator, $meta_arr);
424
425 $prettify_numbers = apply_filters('wpp_pretiffy_numbers', true);
426
427 // Build custom HTML output
428 if ( $this->public_options['markup']['custom_html'] ) {
429 $data = [
430 'id' => $post_id,
431 'is_current_post' => $is_current_post,
432 '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>',
433 'title_attr' => $post_title_attr,
434 'summary' => $post_excerpt,
435 'stats' => $post_meta,
436 '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>' : '',
437 'img_no_link' => $post_thumbnail,
438 'url' => $permalink,
439 'text_title' => $post_title,
440 'taxonomy' => $post_taxonomies,
441 'taxonomy_copy' => isset($meta_arr['taxonomy']) ? $meta_arr['taxonomy'] : null,
442 '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>' : '',
443 'author_copy' => isset($meta_arr['author']) ? $meta_arr['author'] : null,
444 'author_name' => esc_html($post_author),
445 '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)) : '',
446 '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)),
447 'views_copy' => isset($meta_arr['views']) ? $meta_arr['views'] : null,
448 'comments' => $prettify_numbers ? Helper::prettify_number($post_comments) : number_format_i18n($post_comments),
449 'comments_copy' => isset($meta_arr['comments']) ? $meta_arr['comments'] : null,
450 'date' => $post_date,
451 'date_copy' => isset($meta_arr['date']) ? $meta_arr['date'] : null,
452 'total_items' => count($this->data),
453 'item_position' => $position
454 ];
455 $post = $this->format_content(htmlspecialchars_decode($this->public_options['markup']['post-html'], ENT_QUOTES), $data, $this->public_options['rating']) . "\n";
456 } // Use the "stock" HTML output
457 else {
458 $wpp_post_class = [];
459
460 if ( $is_current_post ) {
461 $wpp_post_class[] = 'current';
462 }
463
464 // Allow themers / plugin developer
465 // to add custom classes to each post
466 $wpp_post_class = apply_filters('wpp_post_class', $wpp_post_class, $post_id);
467
468 $post_thumbnail = ( ! empty($post_thumbnail) )
469 ? "<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"
470 : '';
471
472 $post_excerpt = ( ! empty($post_excerpt) )
473 ? " <span class=\"wpp-excerpt\">{$post_excerpt}</span>\n"
474 : '';
475
476 $post_meta = ( ! empty($post_meta) )
477 ? " <span class=\"wpp-meta post-stats\">{$post_meta}</span>\n"
478 : '';
479
480 $post_rating = ( ! empty($post_rating) )
481 ? " <span class=\"wpp-rating\">{$post_rating}</span>\n"
482 : '';
483
484 $post =
485 '<li' . ( ( is_array($wpp_post_class) && ! empty($wpp_post_class) ) ? ' class="' . esc_attr(implode(' ', $wpp_post_class)) . '"' : '') . ">\n"
486 . $post_thumbnail
487 . "<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"
488 . $post_excerpt
489 . $post_meta
490 . $post_rating
491 . "</li>\n";
492 }
493
494 return apply_filters('wpp_post', $post, $post_object, $this->public_options);
495 }
496
497 /**
498 * Return the processed post/page title.
499 *
500 * @since 3.0.0
501 * @access private
502 * @param object $post_object
503 * @param integer $post_id
504 * @return string
505 */
506 private function get_title(\stdClass $post_object, int $post_id)
507 {
508 $title = '';
509
510 if ( $post_object->id != $post_id ) {
511 $title = get_the_title($post_id);
512 } else {
513 $title = $post_object->title;
514 }
515
516 // Run the_title filter so core/plugin title hooks can
517 // be applied to the post title
518 $title = apply_filters('the_title', $title, $post_object->id);
519
520 return apply_filters('wpp_the_title', $title, $post_object->id, $post_id);
521 }
522
523 /**
524 * Return the permalink.
525 *
526 * @since 4.0.12
527 * @access private
528 * @param object $post_object
529 * @param integer $post_id
530 * @return string
531 */
532 private function get_permalink(\stdClass $post_object, int $post_id) {
533 if ( $post_object->id != $post_id ) {
534 return get_permalink($post_id);
535 }
536
537 return get_permalink($post_object->id);
538 }
539
540 /**
541 * Return the processed thumbnail.
542 *
543 * @since 3.0.0
544 * @access private
545 * @param int $post_id
546 * @return string
547 */
548 private function get_thumbnail(int $post_id)
549 {
550 $thumbnail = '';
551
552 if ( $this->public_options['thumbnail']['active'] ) {
553 $thumbnail = $this->thumbnail->get(
554 $post_id,
555 [
556 $this->public_options['thumbnail']['width'],
557 $this->public_options['thumbnail']['height']
558 ],
559 $this->admin_options['tools']['thumbnail']['source'],
560 $this->public_options['thumbnail']['crop'],
561 $this->public_options['thumbnail']['build']
562 );
563 }
564
565 return $thumbnail;
566 }
567
568 /**
569 * Return post excerpt.
570 *
571 * @since 3.0.0
572 * @access private
573 * @param object $post_object
574 * @param integer $post_id
575 * @return string
576 */
577 private function get_excerpt(\stdClass $post_object, int $post_id)
578 {
579 $excerpt = '';
580
581 if ( $this->public_options['post-excerpt']['active'] ) {
582
583 if ( $post_object->id != $post_id ) {
584 $the_post = get_post($post_id);
585
586 $excerpt = ( empty($the_post->post_excerpt) )
587 ? $the_post->post_content
588 : $the_post->post_excerpt;
589 }
590 else {
591 $excerpt = ( empty($post_object->post_excerpt) )
592 ? $post_object->post_content
593 : $post_object->post_excerpt;
594 }
595
596 // remove caption tags
597 $excerpt = preg_replace('/\[caption.*\[\/caption\]/', '', $excerpt);
598
599 // remove Flash objects
600 $excerpt = preg_replace("/<object[0-9 a-z_?*=\":\-\/\.#\,\\n\\r\\t]+/smi", '', $excerpt);
601
602 // remove iframes
603 $excerpt = preg_replace('/<iframe.*?\/iframe>/i', '', $excerpt);
604
605 // remove WP shortcodes
606 $excerpt = strip_shortcodes($excerpt);
607
608 // remove style/script tags
609 $excerpt = preg_replace('@<(script|style)[^>]*?>.*?</\\1>@si', '', $excerpt);
610
611 // remove blocks that are not appropriate for the excerpt
612 $excerpt = excerpt_remove_blocks($excerpt);
613
614 // remove HTML tags if requested
615 if ( $this->public_options['post-excerpt']['keep_format'] ) {
616 $excerpt = wp_kses(
617 $excerpt,
618 [
619 'a' => [
620 'href' => [],
621 'title' => []
622 ],
623 'em' => [],
624 'strong' => []
625 ]
626 );
627 } else {
628 $excerpt = wp_kses($excerpt, []);
629
630 // remove URLs, too
631 $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);
632 }
633
634 // remove HTML comments
635 $excerpt = preg_replace('/<!--.*?-->/ms', '', $excerpt);
636
637 // remove extra whitespaces
638 $excerpt = preg_replace('/\s\s+/', ' ', $excerpt);
639
640 $excerpt = trim($excerpt);
641
642 }
643
644 // Balance tags, if needed
645 if ( '' !== $excerpt ) {
646
647 $more = $this->public_options['post-excerpt']['words'] ? ' ' . $this->more : $this->more;
648 $more = apply_filters('wpp_excerpt_more', $more);
649 $excerpt = Helper::truncate($excerpt, $this->public_options['post-excerpt']['length'], $this->public_options['post-excerpt']['words'], $more);
650
651 if ( $this->public_options['post-excerpt']['keep_format'] ) {
652 $excerpt = force_balance_tags($excerpt);
653 }
654 }
655
656 return $excerpt;
657 }
658
659 /**
660 * Return post rating.
661 *
662 * @since 3.0.0
663 * @access private
664 * @param object $post_object
665 * @return string
666 */
667 private function get_rating(\stdClass $post_object)
668 {
669 $rating = '';
670
671 if ( function_exists('the_ratings_results') && $this->public_options['rating'] ) {
672 $rating = the_ratings_results($post_object->id);
673 }
674
675 return $rating;
676 }
677
678 /**
679 * Get post date.
680 *
681 * @since 3.0.0
682 * @access private
683 * @param object $post_object
684 * @return string
685 */
686 private function get_date(\stdClass $post_object)
687 {
688 $date = '';
689
690 if ( $this->public_options['stats_tag']['date']['active'] ) {
691 if ( 'relative' == $this->public_options['stats_tag']['date']['format'] ) {
692 $date = sprintf(
693 __('%s ago', 'wordpress-popular-posts'),
694 human_time_diff(
695 strtotime($post_object->date),
696 current_time('timestamp')
697 )
698 );
699 } else {
700 $date = date_i18n(
701 ( 'wp_date_format' == $this->public_options['stats_tag']['date']['format'] ? $this->wp_date_format : $this->public_options['stats_tag']['date']['format'] ),
702 strtotime($post_object->date)
703 );
704 }
705 }
706
707 return apply_filters('wpp_the_date', $date, $post_object->id);
708 }
709
710 /**
711 * Get post taxonomies.
712 *
713 * @since 3.0.0
714 * @access private
715 * @param integer $post_id
716 * @return string
717 */
718 private function get_taxonomies(int $post_id)
719 {
720 $post_tax = '';
721
722 if (
723 (isset($this->public_options['stats_tag']['category']) && $this->public_options['stats_tag']['category'])
724 || $this->public_options['stats_tag']['taxonomy']['active']
725 ) {
726
727 $taxonomy = 'category';
728
729 if (
730 $this->public_options['stats_tag']['taxonomy']['active']
731 && ! empty($this->public_options['stats_tag']['taxonomy']['name'])
732 ) {
733 $taxonomy = $this->public_options['stats_tag']['taxonomy']['name'];
734 }
735
736 $terms = wp_get_post_terms($post_id, $taxonomy);
737
738 if ( ! is_wp_error($terms) ) {
739 // Usage: https://wordpress.stackexchange.com/a/46824
740 if ( has_filter('wpp_post_exclude_terms') ) {
741 $args = apply_filters('wpp_post_exclude_terms', []);
742 $terms = wp_list_filter($terms, $args, 'NOT');
743 }
744
745 $terms = apply_filters('wpp_post_terms', $terms);
746
747 if (
748 is_array($terms)
749 && ! empty($terms)
750 ) {
751 $taxonomy_separator = esc_html(apply_filters('wpp_taxonomy_separator', ', '));
752
753 // We're going to use the taxonomy slug as a CSS class so let's escape it just in case
754 $taxonomy = esc_attr($taxonomy);
755
756 foreach ($terms as $term) {
757 $term_link = get_term_link($term);
758
759 if ( is_wp_error($term_link) ) {
760 continue;
761 }
762
763 $term_link = esc_url($this->translate->url($term_link, $this->translate->get_current_language()));
764 $post_tax .= "<a href=\"{$term_link}\" class=\"wpp-taxonomy {$taxonomy} {$taxonomy}-{$term->term_id}\">" . esc_html($term->name) . '</a>' . $taxonomy_separator;
765 }
766 }
767 }
768
769 if ( '' != $post_tax ) {
770 $post_tax = rtrim($post_tax, $taxonomy_separator);
771 }
772
773 }
774
775 return $post_tax;
776 }
777
778 /**
779 * Get post author.
780 *
781 * @since 3.0.0
782 * @access private
783 * @param object $post_object
784 * @param integer $post_id
785 * @return string
786 */
787 private function get_author(\stdClass $post_object, int $post_id)
788 {
789 $author = ( $this->public_options['stats_tag']['author'] )
790 ? get_the_author_meta('display_name', $post_object->uid != $post_id ? get_post_field('post_author', $post_id) : $post_object->uid)
791 : '';
792
793 return $author;
794 }
795
796 /**
797 * Return post views count.
798 *
799 * @since 3.0.0
800 * @access private
801 * @param object $post_object
802 * @return int|float
803 */
804 private function get_pageviews(\stdClass $post_object)
805 {
806 $pageviews = 0;
807
808 if (
809 (
810 $this->public_options['order_by'] == 'views'
811 || $this->public_options['order_by'] == 'avg'
812 || $this->public_options['stats_tag']['views']
813 )
814 && ( isset($post_object->pageviews) || isset($post_object->avg_views) )
815 ) {
816 $pageviews = ( $this->public_options['order_by'] == 'views' || $this->public_options['order_by'] == 'comments' )
817 ? $post_object->pageviews
818 : $post_object->avg_views;
819 }
820
821 return $pageviews;
822 }
823
824 /**
825 * Return post comment count.
826 *
827 * @since 3.0.0
828 * @access private
829 * @param object $post_object
830 * @return int
831 */
832 private function get_comments(\stdClass $post_object)
833 {
834 $comments = ( ( $this->public_options['order_by'] == 'comments' || $this->public_options['stats_tag']['comment_count'] ) && isset($post_object->comment_count) )
835 ? $post_object->comment_count
836 : 0;
837
838 return $comments;
839 }
840
841 /**
842 * Return post metadata.
843 *
844 * @since 3.0.0
845 * @access private
846 * @param object $post_object
847 * @param integer $post_id
848 * @return array
849 */
850 //private function get_metadata(\stdClass $post_object, $post_id)
851 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 */
852 {
853 $stats = [];
854
855 $prettify_numbers = apply_filters('wpp_pretiffy_numbers', true);
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