Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 76
0.00% covered (danger)
0.00%
0 / 1
CRAP
0.00% covered (danger)
0.00%
0 / 1
Jetpack_Top_Posts_Helper
0.00% covered (danger)
0.00%
0 / 76
0.00% covered (danger)
0.00%
0 / 1
272
0.00% covered (danger)
0.00%
0 / 1
 get_top_posts
0.00% covered (danger)
0.00%
0 / 76
0.00% covered (danger)
0.00%
0 / 1
272
1<?php
2/**
3 * Top Posts & Pages block helper.
4 *
5 * @package automattic/jetpack
6 */
7
8use Automattic\Jetpack\Stats\WPCOM_Stats;
9
10/**
11 * Class Jetpack_Top_Posts_Helper
12 */
13class Jetpack_Top_Posts_Helper {
14    /**
15     * Returns user's top posts.
16     *
17     * @param int    $period       Period of days to draw stats from.
18     * @param int    $items_count  Optional. Number of items to display.
19     * @param string $types        Optional. Content types to include.
20     * @return array
21     */
22    public static function get_top_posts( $period, $items_count = null, $types = null ) {
23        $all_time_days = floor( ( time() - strtotime( get_option( 'site_created_date' ) ) ) / ( 60 * 60 * 24 * 365 ) );
24
25        // While we only display ten posts, users can filter out content types.
26        // As such, we should obtain a few spare posts from the Stats endpoint.
27        $posts_to_obtain_count = 30;
28
29        // We should not override cache when displaying the block on the frontend.
30        // But we should allow instant preview of changes when editing the block.
31        $is_rendering_block = ! empty( $types );
32        $override_cache     = ! $is_rendering_block;
33
34        $query_args = array(
35            'max'       => $posts_to_obtain_count,
36            'summarize' => true,
37            'num'       => $period !== 'all-time' ? $period : $all_time_days,
38            'period'    => 'day',
39        );
40
41        // Atomic or self-hosted sites via WPCOM public v1.1 endpoint.
42        if ( ! defined( 'IS_WPCOM' ) || ! IS_WPCOM ) {
43            $data = ( new WPCOM_Stats() )->get_top_posts( $query_args, $override_cache );
44        } else {
45            // Directly access posts on WPCOM, as Simple sites run on the same environment.
46            require_lib( 'jetpack-stats' );
47            if ( class_exists( '\Jetpack\Stats\Top_Posts' ) ) {
48                // @phan-suppress-next-line PhanUndeclaredClassMethod
49                $data = ( new \Jetpack\Stats\Top_Posts() )->get_top_posts( get_current_blog_id(), $query_args );
50            } else {
51                $data = array( 'summary' => array( 'postviews' => array() ) );
52            }
53        }
54
55        if ( is_wp_error( $data ) ) {
56            $data = array( 'summary' => array( 'postviews' => array() ) );
57        }
58
59        // Remove posts that have subsequently been deleted.
60        $data['summary']['postviews'] = array_filter(
61            $data['summary']['postviews'],
62            function ( $item ) {
63                return get_post_status( $item['id'] ) === 'publish';
64            }
65        );
66
67        $posts_retrieved = is_countable( $data['summary']['postviews'] ) ? count( $data['summary']['postviews'] ) : 0;
68
69        // Fallback to random posts if user does not have enough top content.
70        if ( $posts_retrieved < $posts_to_obtain_count ) {
71            $args = array(
72                'numberposts' => $posts_to_obtain_count - $posts_retrieved,
73                'exclude'     => array_column( $data['summary']['postviews'], 'id' ),
74                'orderby'     => 'rand',
75                'post_status' => 'publish',
76            );
77
78            $random_posts = get_posts( $args );
79
80            foreach ( $random_posts as $post ) {
81                $random_posts_data = array(
82                    'id'     => $post->ID,
83                    'href'   => get_permalink( $post->ID ),
84                    'date'   => $post->post_date,
85                    'title'  => $post->post_title,
86                    'type'   => 'post',
87                    'public' => true,
88                );
89
90                $data['summary']['postviews'][] = $random_posts_data;
91            }
92
93            $data['summary']['postviews'] = array_slice( $data['summary']['postviews'], 0, 10 );
94        }
95
96        $top_posts = array();
97
98        foreach ( $data['summary']['postviews'] as $post ) {
99            $post_id   = $post['id'];
100            $thumbnail = get_the_post_thumbnail_url( $post_id );
101
102            if ( ! $thumbnail ) {
103                $post_images = get_attached_media( 'image', $post_id );
104                $post_image  = reset( $post_images );
105                if ( $post_image ) {
106                    $thumbnail = wp_get_attachment_url( $post_image->ID );
107                }
108            }
109
110            if ( $post['public'] ) {
111                $top_posts[] = array(
112                    'id'        => $post_id,
113                    'author'    => get_the_author_meta( 'display_name', get_post_field( 'post_author', $post_id ) ),
114                    'context'   => get_the_category( $post_id ) ? get_the_category( $post_id ) : get_the_tags( $post_id ),
115                    'href'      => $post['href'],
116                    'date'      => get_the_date( '', $post_id ),
117                    'title'     => $post['title'],
118                    'type'      => $post['type'],
119                    'public'    => $post['public'],
120                    'views'     => isset( $post['views'] ) ? $post['views'] : 0,
121                    'thumbnail' => $thumbnail,
122                );
123            }
124        }
125
126        // This applies for rendering the block front-end, but not for editing it.
127        if ( $is_rendering_block ) {
128            $acceptable_types = explode( ',', $types );
129
130            $top_posts = array_filter(
131                $top_posts,
132                function ( $item ) use ( $acceptable_types ) {
133                    return in_array( $item['type'], $acceptable_types, true );
134                }
135            );
136
137            $top_posts = array_slice( $top_posts, 0, $items_count );
138        }
139
140        return $top_posts;
141    }
142}