Completed
Push — sync/json-endpoints-19apr2017 ( 4d1744 )
by
unknown
64:46 queued 53:25
created

class.wpcom-json-api-list-media-v1-1-endpoint.php (3 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
class WPCOM_JSON_API_List_Media_v1_1_Endpoint extends WPCOM_JSON_API_Endpoint {
4
5
	public $date_range = array();
6
	public $page_handle = array();
7
8
	function callback( $path = '', $blog_id = 0 ) {
9
		$blog_id = $this->api->switch_to_blog_and_validate_user( $this->api->get_blog_id( $blog_id ) );
10
		if ( is_wp_error( $blog_id ) ) {
11
			return $blog_id;
12
		}
13
14
		//upload_files can probably be used for other endpoints but we want contributors to be able to use media too
15
		if ( ! current_user_can( 'edit_posts' ) ) {
16
			return new WP_Error( 'unauthorized', 'User cannot view media', 403 );
17
		}
18
19
		$args = $this->query_args();
20
		$is_eligible_for_page_handle = true;
21
22
		if ( $args['number'] < 1 ) {
23
			$args['number'] = 20;
24
		} elseif ( 100 < $args['number'] ) {
25
			return new WP_Error( 'invalid_number',  'The NUMBER parameter must be less than or equal to 100.', 400 );
26
		}
27
28
		if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
29
			$this->load_theme_functions();
30
		}
31
32
		if ( isset( $args['before'] ) ) {
33
			$this->date_range['before'] = $args['before'];
34
		}
35
		if ( isset( $args['after'] ) ) {
36
			$this->date_range['after'] = $args['after'];
37
		}
38
39
		$query = array(
40
			'post_type'      => 'attachment',
41
			'post_status'    => 'inherit',
42
			'post_parent'    => isset( $args['post_ID'] ) ? $args['post_ID'] : null,
43
			'posts_per_page' => $args['number'],
44
			'post_mime_type' => isset( $args['mime_type'] ) ? $args['mime_type'] : null,
45
			'order'          => isset( $args['order'] ) ? $args['order'] : 'DESC',
46
			'orderby'        => isset( $args['order_by'] ) ? $args['order_by'] : 'date',
47
			's'              => isset( $args['search'] ) ? $args['search'] : null,
48
		);
49
50 View Code Duplication
		if ( isset( $args['page'] ) ) {
51
			if ( $args['page'] < 1 ) {
52
				$args['page'] = 1;
53
			}
54
55
			$query['paged'] = $args['page'];
56
			if ( $query['paged'] !== 1 ) {
57
				$is_eligible_for_page_handle = false;
58
			}
59
		} else {
60
			if ( $args['offset'] < 0 ) {
61
				$args['offset'] = 0;
62
			}
63
64
			$query['offset'] = $args['offset'];
65
			if ( $query['offset'] !== 0 ) {
66
				$is_eligible_for_page_handle = false;
67
			}
68
		}
69
70 View Code Duplication
		if ( isset( $args['page_handle'] ) ) {
71
			$page_handle = wp_parse_args( $args['page_handle'] );
72
			if ( isset( $page_handle['value'] ) && isset( $page_handle['id'] ) ) {
73
				// we have a valid looking page handle
74
				$this->page_handle = $page_handle;
75
				add_filter( 'posts_where', array( $this, 'handle_where_for_page_handle' ) );
76
			}
77
		}
78
79
		if ( $this->date_range ) {
80
			add_filter( 'posts_where', array( $this, 'handle_date_range' ) );
81
		}
82
83
		$this->performed_query = $query;
0 ignored issues
show
The property performed_query does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
84
		add_filter( 'posts_orderby', array( $this, 'handle_orderby_for_page_handle' ) );
85
86
		$media = new WP_Query( $query );
87
88
		remove_filter( 'posts_orderby', array( $this, 'handle_orderby_for_page_handle' ) );
89
90
		if ( $this->date_range ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->date_range of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
91
			remove_filter( 'posts_where', array( $this, 'handle_date_range' ) );
92
			$this->date_range = array();
93
		}
94
95
		if ( $this->page_handle ) {
96
			remove_filter( 'posts_where', array( $this, 'handle_where_for_page_handle' ) );
97
		}
98
99
		$response = array();
100
101
		// @start-hide-in-jetpack                   
102
		// gm2016 - image classification hack
103
		// fetches images from image classification and merges results
104
		if ( ! empty( $args['search'] ) ) { 
105
			jetpack_require_lib( 'classification/class-user-image-classification');
106
			if ( User_Image_Classification::is_valid_blog( $blog_id ) ) {
107
				$images = User_Image_Classification::get_images_classified( $args['search'] );
108
				$media->posts = array_merge( $media->posts, $images );
109
				$media->found_posts = $media->found_posts + count( $images );
110
			}
111
		}
112
		// @end-hide-in-jetpack 	
0 ignored issues
show
There is some trailing whitespace on this line which should be avoided as per coding-style.
Loading history...
113
114
		foreach ( $media->posts as $item ) {
115
			$response[] = $this->get_media_item_v1_1( $item->ID );
116
		}
117
118
		$return = array(
119
			'found' => (int) $media->found_posts,
120
			'media' => $response
121
		);
122
123
		if ( $is_eligible_for_page_handle && $return['media'] ) {
124
			$last_post = end( $return['media'] );
125
			reset( $return['media'] );
126
127
			if ( ( $return['found'] > count( $return['media'] ) ) && $last_post ) {
128
				$return['meta'] = array();
129
				$return['meta']['next_page'] = $this->build_page_handle( $last_post, $query );
130
			}
131
		}
132
133
		return $return;
134
	}
135
136 View Code Duplication
	function build_page_handle( $post, $query ) {
137
		$column = $query['orderby'];
138
		if ( ! $column ) {
139
			$column = 'date';
140
		}
141
		return build_query( array( 'value' => urlencode( $post->$column ), 'id' => $post->ID ) );
142
	}
143
144 View Code Duplication
	function handle_where_for_page_handle( $where ) {
145
		global $wpdb;
146
147
		$column = $this->performed_query['orderby'];
148
		if ( ! $column ) {
149
			$column = 'date';
150
		}
151
		$order = $this->performed_query['order'];
152
		if ( ! $order ) {
153
			$order = 'DESC';
154
		}
155
156
		if ( ! in_array( $column, array( 'ID', 'title', 'date', 'modified', 'comment_count' ) ) ) {
157
			return $where;
158
		}
159
160
		if ( ! in_array( $order, array( 'DESC', 'ASC' ) ) ) {
161
			return $where;
162
		}
163
164
		$db_column = '';
165
		$db_value = '';
166
		switch( $column ) {
167
			case 'ID':
168
				$db_column = 'ID';
169
				$db_value = '%d';
170
				break;
171
			case 'title':
172
				$db_column = 'post_title';
173
				$db_value = '%s';
174
				break;
175
			case 'date':
176
				$db_column = 'post_date';
177
				$db_value = 'CAST( %s as DATETIME )';
178
				break;
179
			case 'modified':
180
				$db_column = 'post_modified';
181
				$db_value = 'CAST( %s as DATETIME )';
182
				break;
183
			case 'comment_count':
184
				$db_column = 'comment_count';
185
				$db_value = '%d';
186
				break;
187
		}
188
189
		if ( 'DESC'=== $order ) {
190
			$db_order = '<';
191
		} else {
192
			$db_order = '>';
193
		}
194
195
		// Add a clause that limits the results to items beyond the passed item, or equivalent to the passed item
196
		// but with an ID beyond the passed item. When we're ordering by the ID already, we only ask for items
197
		// beyond the passed item.
198
		$where .= $wpdb->prepare( " AND ( ( `$wpdb->posts`.`$db_column` $db_order $db_value ) ", $this->page_handle['value'] );
199
		if ( $db_column !== 'ID' ) {
200
			$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'] );
201
		}
202
		$where .= ' )';
203
204
		return $where;
205
	}
206
207 View Code Duplication
	function handle_date_range( $where ) {
208
		global $wpdb;
209
210
		switch ( count( $this->date_range ) ) {
211
		case 2 :
212
			$where .= $wpdb->prepare(
213
				" AND `$wpdb->posts`.post_date BETWEEN CAST( %s AS DATETIME ) AND CAST( %s AS DATETIME ) ",
214
				$this->date_range['after'],
215
				$this->date_range['before']
216
			);
217
			break;
218
		case 1 :
219
			if ( isset( $this->date_range['before'] ) ) {
220
				$where .= $wpdb->prepare(
221
					" AND `$wpdb->posts`.post_date <= CAST( %s AS DATETIME ) ",
222
					$this->date_range['before']
223
				);
224
			} else {
225
				$where .= $wpdb->prepare(
226
					" AND `$wpdb->posts`.post_date >= CAST( %s AS DATETIME ) ",
227
					$this->date_range['after']
228
				);
229
			}
230
			break;
231
		}
232
233
		return $where;
234
	}
235
236 View Code Duplication
	function handle_orderby_for_page_handle( $orderby ) {
237
		global $wpdb;
238
		if ( $this->performed_query['orderby'] === 'ID' ) {
239
			// bail if we're already ordering by ID
240
			return $orderby;
241
		}
242
243
		if ( $orderby ) {
244
			$orderby .= ' ,';
245
		}
246
		$order = $this->performed_query['order'];
247
		if ( ! $order ) {
248
			$order = 'DESC';
249
		}
250
		$orderby .= " `$wpdb->posts`.ID $order";
251
		return $orderby;
252
	}
253
254
}
255