summaryrefslogtreecommitdiff
blob: 50186677f32ee8c7fd349fd58589416679001610 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
<?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName

new WPCOM_JSON_API_GET_Post_Counts_V1_1_Endpoint(
	array(
		'description'      => 'Get number of posts in the post type groups by post status',
		'group'            => 'sites',
		'stat'             => 'sites:X:post-counts:X',
		'force'            => 'wpcom',
		'method'           => 'GET',
		'min_version'      => '1.1',
		'max_version'      => '1.2',
		'path'             => '/sites/%s/post-counts/%s',
		'path_labels'      => array(
			'$site'      => '(int|string) Site ID or domain',
			'$post_type' => '(string) Post Type',
		),

		'query_parameters' => array(
			'context' => false,
			'author'  => '(int) author ID',
		),

		'example_request'  => 'https://public-api.wordpress.com/rest/v1.2/sites/en.blog.wordpress.com/post-counts/page',

		'response_format'  => array(
			'counts' => array(
				'all'  => '(array) Number of posts by any author in the post type grouped by post status',
				'mine' => '(array) Number of posts by the current user in the post type grouped by post status',
			),
		),
	)
);

/**
 * GET Post Counts v1_1 endpoint class.
 */
class WPCOM_JSON_API_GET_Post_Counts_V1_1_Endpoint extends WPCOM_JSON_API_Endpoint {

	/**
	 * Whitelist array.
	 *
	 * @var allowlist
	 */
	private $allowlist = array( 'publish' );

	/**
	 * Build SQL query
	 *
	 * This function must `$wpdb->prepare` the query. The return is expected to be prepared by consuming functions.
	 *
	 * @param string $post_type - post type.
	 * @param int    $user_id - the user ID.
	 * @return string SQL query
	 */
	private function buildCountsQuery( $post_type = 'post', $user_id = null ) {
		global $wpdb;

		$query  = 'SELECT post_status as status, count(*) as count ';
		$query .= "FROM {$wpdb->posts} ";
		$query .= 'WHERE post_type = %s ';
		if ( isset( $user_id ) ) {
			$query .= 'AND post_author = %d ';
		}

		$query .= 'GROUP BY status';

		// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- This is properly prepared, except the query is constructed in the variable, throwing the PHPCS error.
		return $wpdb->prepare( $query, $post_type, $user_id );
	}

	/**
	 * Retrive counts using wp_cache
	 *
	 * @param string $post_type - thge post type.
	 * @param int    $id - the ID.
	 */
	private function retrieveCounts( $post_type, $id = null ) {
		if ( ! isset( $id ) ) {
			$counts = array();
			foreach ( (array) wp_count_posts( $post_type ) as $status => $count ) {
				if ( in_array( $status, $this->allowlist, true ) && $count > 0 ) {
					$counts[ $status ] = (int) $count;
				}
			}

			return $counts;
		}

		global $wpdb;
		$key    = 'rest-api-' . $id . '-' . _count_posts_cache_key( $post_type );
		$counts = wp_cache_get( $key, 'counts' );

		if ( false === $counts ) {
			// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- buildCountsQuery prepares the query.
			$results = $wpdb->get_results( $this->buildCountsQuery( $post_type, $id ) );
			$counts  = $this->filterStatusesByWhiteslist( $results );
			wp_cache_set( $key, $counts, 'counts' );
		}

		return $counts;
	}

	/**
	 * Filter statuses by whiteslist.
	 *
	 * @param array $in - the post we're checking.
	 */
	private function filterStatusesByWhiteslist( $in ) {
		$return = array();
		foreach ( $in as $result ) {
			if ( in_array( $result->status, $this->allowlist, true ) ) {
				$return[ $result->status ] = (int) $result->count;
			}
		}
		return $return;
	}

	/**
	 *
	 * API callback.
	 *
	 * /sites/%s/post-counts/%s
	 *
	 * @param string $path - the path.
	 * @param int    $blog_id - the blog ID.
	 * @param string $post_type - the post type.
	 */
	public function callback( $path = '', $blog_id = 0, $post_type = 'post' ) {
		if ( ! get_current_user_id() ) {
			return new WP_Error( 'authorization_required', __( 'An active access token must be used to retrieve post counts.', 'jetpack' ), 403 );
		}

		$blog_id = $this->api->switch_to_blog_and_validate_user( $this->api->get_blog_id( $blog_id ), false );

		if ( is_wp_error( $blog_id ) ) {
			return $blog_id;
		}

		// @todo see if we can use a strict comparison here.
		// phpcs:ignore WordPress.PHP.StrictInArray.MissingTrueStrict
		if ( ! in_array( $post_type, array( 'post', 'revision', 'page', 'any' ), true ) && defined( 'IS_WPCOM' ) && IS_WPCOM ) {
			$this->load_theme_functions();
		}

		if ( ! post_type_exists( $post_type ) ) {
			return new WP_Error( 'unknown_post_type', __( 'Unknown post type requested.', 'jetpack' ), 404 );
		}

		$args    = $this->query_args();
		$mine_ID = get_current_user_id(); // phpcs:ignore WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase

		if ( current_user_can( 'edit_posts' ) ) {
			array_push( $this->allowlist, 'draft', 'future', 'pending', 'private', 'trash' );
		}

		$return = array(
			'counts' => (array) array(
				'all'  => (object) $this->retrieveCounts( $post_type ),
				'mine' => (object) $this->retrieveCounts( $post_type, $mine_ID ), // phpcs:ignore WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase
			),
		);

		// Author.
		if ( isset( $args['author'] ) ) {
			$author_ID                  = $args['author']; // phpcs:ignore WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase
			$return['counts']['author'] = (object) $this->retrieveCounts( $post_type, $author_ID ); // phpcs:ignore WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase
		}

		return (object) $return;
	}
}