Completed
Push — update/crm-integration-fixes ( a3fc2b )
by Jeremy
14:51 queued 06:26
created

class.wpcom-json-api-list-media-v1-1-endpoint.php (2 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
new WPCOM_JSON_API_List_Media_v1_1_Endpoint( array(
4
	'description' => 'Get a list of items in the media library.',
5
	'group'       => 'media',
6
	'stat'        => 'media',
7
	'min_version' => '1.1',
8
	'max_version' => '1.1',
9
	'method'      => 'GET',
10
	'path'        => '/sites/%s/media/',
11
	'path_labels' => array(
12
		'$site' => '(int|string) Site ID or domain',
13
	),
14
15
	'query_parameters' => array(
16
		'number'    => '(int=20) The number of media items to return. Limit: 100.',
17
		'offset'    => '(int=0) 0-indexed offset.',
18
		'page'     => '(int) Return the Nth 1-indexed page of posts. Takes precedence over the <code>offset</code> parameter.',
19
		'page_handle' => '(string) A page handle, returned from a previous API call as a <code>meta.next_page</code> property. This is the most efficient way to fetch the next page of results.',
20
		'order'    => array(
21
			'DESC' => 'Return files in descending order. For dates, that means newest to oldest.',
22
			'ASC'  => 'Return files in ascending order. For dates, that means oldest to newest.',
23
		),
24
		'order_by' => array(
25
			'date'          => 'Order by the uploaded time of each file.',
26
			'title'         => "Order lexicographically by file titles.",
27
			'ID'            => 'Order by media ID.',
28
		),
29
		'search'    => '(string) Search query.',
30
		'post_ID'   => '(int) Default is showing all items. The post where the media item is attached. 0 shows unattached media items.',
31
		'mime_type' => "(string) Default is empty. Filter by mime type (e.g., 'image/jpeg', 'application/pdf'). Partial searches also work (e.g. passing 'image' will search for all image files).",
32
		'after'     => '(ISO 8601 datetime) Return media items uploaded after the specified datetime.',
33
		'before'    => '(ISO 8601 datetime) Return media items uploaded before the specified datetime.',
34
	),
35
36
	'response_format' => array(
37
		'media' => '(array) Array of media objects',
38
		'found' => '(int) The number of total results found',
39
		'meta'  => '(object) Meta data',
40
	),
41
42
	'example_request'      => 'https://public-api.wordpress.com/rest/v1.1/sites/82974409/media',
43
	'example_request_data' =>  array(
44
		'headers' => array(
45
			'authorization' => 'Bearer YOUR_API_TOKEN'
46
		)
47
	)
48
) );
49
50
class WPCOM_JSON_API_List_Media_v1_1_Endpoint extends WPCOM_JSON_API_Endpoint {
51
52
	public $date_range = array();
53
	public $page_handle = array();
54
55
	function callback( $path = '', $blog_id = 0 ) {
56
		$blog_id = $this->api->switch_to_blog_and_validate_user( $this->api->get_blog_id( $blog_id ) );
57
		if ( is_wp_error( $blog_id ) ) {
58
			return $blog_id;
59
		}
60
61
		//upload_files can probably be used for other endpoints but we want contributors to be able to use media too
62
		if ( ! current_user_can( 'edit_posts' ) ) {
63
			return new WP_Error( 'unauthorized', 'User cannot view media', 403 );
0 ignored issues
show
The call to WP_Error::__construct() has too many arguments starting with 'unauthorized'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
64
		}
65
66
		$args = $this->query_args();
67
		$is_eligible_for_page_handle = true;
68
69
		if ( $args['number'] < 1 ) {
70
			$args['number'] = 20;
71
		} elseif ( 100 < $args['number'] ) {
72
			return new WP_Error( 'invalid_number',  'The NUMBER parameter must be less than or equal to 100.', 400 );
0 ignored issues
show
The call to WP_Error::__construct() has too many arguments starting with 'invalid_number'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
73
		}
74
75
		if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
76
			$this->load_theme_functions();
77
		}
78
79
		if ( isset( $args['before'] ) ) {
80
			$this->date_range['before'] = $args['before'];
81
		}
82
		if ( isset( $args['after'] ) ) {
83
			$this->date_range['after'] = $args['after'];
84
		}
85
86
		$query = array(
87
			'post_type'      => 'attachment',
88
			'post_status'    => 'inherit',
89
			'post_parent'    => isset( $args['post_ID'] ) ? $args['post_ID'] : null,
90
			'posts_per_page' => $args['number'],
91
			'post_mime_type' => isset( $args['mime_type'] ) ? $args['mime_type'] : null,
92
			'order'          => isset( $args['order'] ) ? $args['order'] : 'DESC',
93
			'orderby'        => isset( $args['order_by'] ) ? $args['order_by'] : 'date',
94
			's'              => isset( $args['search'] ) ? $args['search'] : null,
95
		);
96
97 View Code Duplication
		if ( isset( $args['page'] ) ) {
98
			if ( $args['page'] < 1 ) {
99
				$args['page'] = 1;
100
			}
101
102
			$query['paged'] = $args['page'];
103
			if ( $query['paged'] !== 1 ) {
104
				$is_eligible_for_page_handle = false;
105
			}
106
		} else {
107
			if ( $args['offset'] < 0 ) {
108
				$args['offset'] = 0;
109
			}
110
111
			$query['offset'] = $args['offset'];
112
			if ( $query['offset'] !== 0 ) {
113
				$is_eligible_for_page_handle = false;
114
			}
115
		}
116
117 View Code Duplication
		if ( isset( $args['page_handle'] ) ) {
118
			$page_handle = wp_parse_args( $args['page_handle'] );
119
			if ( isset( $page_handle['value'] ) && isset( $page_handle['id'] ) ) {
120
				// we have a valid looking page handle
121
				$this->page_handle = $page_handle;
122
				add_filter( 'posts_where', array( $this, 'handle_where_for_page_handle' ) );
123
			}
124
		}
125
126
		if ( $this->date_range ) {
127
			add_filter( 'posts_where', array( $this, 'handle_date_range' ) );
128
		}
129
130
		$this->performed_query = $query;
131
		add_filter( 'posts_orderby', array( $this, 'handle_orderby_for_page_handle' ) );
132
133
		$media = new WP_Query( $query );
134
135
		remove_filter( 'posts_orderby', array( $this, 'handle_orderby_for_page_handle' ) );
136
137
		if ( $this->date_range ) {
138
			remove_filter( 'posts_where', array( $this, 'handle_date_range' ) );
139
			$this->date_range = array();
140
		}
141
142
		if ( $this->page_handle ) {
143
			remove_filter( 'posts_where', array( $this, 'handle_where_for_page_handle' ) );
144
		}
145
146
		$response = array();
147
148
		foreach ( $media->posts as $item ) {
149
			$response[] = $this->get_media_item_v1_1( $item->ID );
150
		}
151
152
		$return = array(
153
			'found' => (int) $media->found_posts,
154
			'media' => $response
155
		);
156
157
		if ( $is_eligible_for_page_handle && $return['media'] ) {
158
			$last_post = end( $return['media'] );
159
			reset( $return['media'] );
160
161
			if ( ( $return['found'] > count( $return['media'] ) ) && $last_post ) {
162
				$return['meta'] = array();
163
				$return['meta']['next_page'] = $this->build_page_handle( $last_post, $query );
164
			}
165
		}
166
167
		return $return;
168
	}
169
170 View Code Duplication
	function build_page_handle( $post, $query ) {
171
		$column = $query['orderby'];
172
		if ( ! $column ) {
173
			$column = 'date';
174
		}
175
		return build_query( array( 'value' => urlencode( $post->$column ), 'id' => $post->ID ) );
176
	}
177
178 View Code Duplication
	function handle_where_for_page_handle( $where ) {
179
		global $wpdb;
180
181
		$column = $this->performed_query['orderby'];
182
		if ( ! $column ) {
183
			$column = 'date';
184
		}
185
		$order = $this->performed_query['order'];
186
		if ( ! $order ) {
187
			$order = 'DESC';
188
		}
189
190
		if ( ! in_array( $column, array( 'ID', 'title', 'date', 'modified', 'comment_count' ) ) ) {
191
			return $where;
192
		}
193
194
		if ( ! in_array( $order, array( 'DESC', 'ASC' ) ) ) {
195
			return $where;
196
		}
197
198
		$db_column = '';
199
		$db_value = '';
200
		switch( $column ) {
201
			case 'ID':
202
				$db_column = 'ID';
203
				$db_value = '%d';
204
				break;
205
			case 'title':
206
				$db_column = 'post_title';
207
				$db_value = '%s';
208
				break;
209
			case 'date':
210
				$db_column = 'post_date';
211
				$db_value = 'CAST( %s as DATETIME )';
212
				break;
213
			case 'modified':
214
				$db_column = 'post_modified';
215
				$db_value = 'CAST( %s as DATETIME )';
216
				break;
217
			case 'comment_count':
218
				$db_column = 'comment_count';
219
				$db_value = '%d';
220
				break;
221
		}
222
223
		if ( 'DESC'=== $order ) {
224
			$db_order = '<';
225
		} else {
226
			$db_order = '>';
227
		}
228
229
		// Add a clause that limits the results to items beyond the passed item, or equivalent to the passed item
230
		// but with an ID beyond the passed item. When we're ordering by the ID already, we only ask for items
231
		// beyond the passed item.
232
		$where .= $wpdb->prepare( " AND ( ( `$wpdb->posts`.`$db_column` $db_order $db_value ) ", $this->page_handle['value'] );
233
		if ( $db_column !== 'ID' ) {
234
			$where .= $wpdb->prepare( "OR ( `$wpdb->posts`.`$db_column` = $db_value AND `$wpdb->posts`.ID $db_order %d )", $this->page_handle['value'], $this->page_handle['id'] );
235
		}
236
		$where .= ' )';
237
238
		return $where;
239
	}
240
241 View Code Duplication
	function handle_date_range( $where ) {
242
		global $wpdb;
243
244
		switch ( count( $this->date_range ) ) {
245
		case 2 :
246
			$where .= $wpdb->prepare(
247
				" AND `$wpdb->posts`.post_date BETWEEN CAST( %s AS DATETIME ) AND CAST( %s AS DATETIME ) ",
248
				$this->date_range['after'],
249
				$this->date_range['before']
250
			);
251
			break;
252
		case 1 :
253
			if ( isset( $this->date_range['before'] ) ) {
254
				$where .= $wpdb->prepare(
255
					" AND `$wpdb->posts`.post_date <= CAST( %s AS DATETIME ) ",
256
					$this->date_range['before']
257
				);
258
			} else {
259
				$where .= $wpdb->prepare(
260
					" AND `$wpdb->posts`.post_date >= CAST( %s AS DATETIME ) ",
261
					$this->date_range['after']
262
				);
263
			}
264
			break;
265
		}
266
267
		return $where;
268
	}
269
270 View Code Duplication
	function handle_orderby_for_page_handle( $orderby ) {
271
		global $wpdb;
272
		if ( $this->performed_query['orderby'] === 'ID' ) {
273
			// bail if we're already ordering by ID
274
			return $orderby;
275
		}
276
277
		if ( $orderby ) {
278
			$orderby .= ' ,';
279
		}
280
		$order = $this->performed_query['order'];
281
		if ( ! $order ) {
282
			$order = 'DESC';
283
		}
284
		$orderby .= " `$wpdb->posts`.ID $order";
285
		return $orderby;
286
	}
287
288
}
289