These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | |||
3 | require_once( dirname( __FILE__ ) . '/json-api-config.php' ); |
||
4 | require_once( dirname( __FILE__ ) . '/sal/class.json-api-links.php' ); |
||
5 | require_once( dirname( __FILE__ ) . '/sal/class.json-api-metadata.php' ); |
||
6 | require_once( dirname( __FILE__ ) . '/sal/class.json-api-date.php' ); |
||
7 | |||
8 | // Endpoint |
||
9 | abstract class WPCOM_JSON_API_Endpoint { |
||
10 | // The API Object |
||
11 | public $api; |
||
12 | |||
13 | // The link-generating utility class |
||
14 | public $links; |
||
15 | |||
16 | public $pass_wpcom_user_details = false; |
||
17 | |||
18 | // One liner. |
||
19 | public $description; |
||
20 | |||
21 | // Object Grouping For Documentation (Users, Posts, Comments) |
||
22 | public $group; |
||
23 | |||
24 | // Stats extra value to bump |
||
25 | public $stat; |
||
26 | |||
27 | // HTTP Method |
||
28 | public $method = 'GET'; |
||
29 | |||
30 | // Minimum version of the api for which to serve this endpoint |
||
31 | public $min_version = '0'; |
||
32 | |||
33 | // Maximum version of the api for which to serve this endpoint |
||
34 | public $max_version = WPCOM_JSON_API__CURRENT_VERSION; |
||
35 | |||
36 | // Path at which to serve this endpoint: sprintf() format. |
||
37 | public $path = ''; |
||
38 | |||
39 | // Identifiers to fill sprintf() formatted $path |
||
40 | public $path_labels = array(); |
||
41 | |||
42 | // Accepted query parameters |
||
43 | public $query = array( |
||
44 | // Parameter name |
||
45 | 'context' => array( |
||
46 | // Default value => description |
||
47 | 'display' => 'Formats the output as HTML for display. Shortcodes are parsed, paragraph tags are added, etc..', |
||
48 | // Other possible values => description |
||
49 | 'edit' => 'Formats the output for editing. Shortcodes are left unparsed, significant whitespace is kept, etc..', |
||
50 | ), |
||
51 | 'http_envelope' => array( |
||
52 | 'false' => '', |
||
53 | 'true' => 'Some environments (like in-browser JavaScript or Flash) block or divert responses with a non-200 HTTP status code. Setting this parameter will force the HTTP status code to always be 200. The JSON response is wrapped in an "envelope" containing the "real" HTTP status code and headers.', |
||
54 | ), |
||
55 | 'pretty' => array( |
||
56 | 'false' => '', |
||
57 | 'true' => 'Output pretty JSON', |
||
58 | ), |
||
59 | 'meta' => "(string) Optional. Loads data from the endpoints found in the 'meta' part of the response. Comma-separated list. Example: meta=site,likes", |
||
60 | 'fields' => '(string) Optional. Returns specified fields only. Comma-separated list. Example: fields=ID,title', |
||
61 | // Parameter name => description (default value is empty) |
||
62 | 'callback' => '(string) An optional JSONP callback function.', |
||
63 | ); |
||
64 | |||
65 | // Response format |
||
66 | public $response_format = array(); |
||
67 | |||
68 | // Request format |
||
69 | public $request_format = array(); |
||
70 | |||
71 | // Is this endpoint still in testing phase? If so, not available to the public. |
||
72 | public $in_testing = false; |
||
73 | |||
74 | // Is this endpoint still allowed if the site in question is flagged? |
||
75 | public $allowed_if_flagged = false; |
||
76 | |||
77 | // Is this endpoint allowed if the site is red flagged? |
||
78 | public $allowed_if_red_flagged = false; |
||
79 | |||
80 | // Is this endpoint allowed if the site is deleted? |
||
81 | public $allowed_if_deleted = false; |
||
82 | |||
83 | /** |
||
84 | * @var string Version of the API |
||
85 | */ |
||
86 | public $version = ''; |
||
87 | |||
88 | /** |
||
89 | * @var string Example request to make |
||
90 | */ |
||
91 | public $example_request = ''; |
||
92 | |||
93 | /** |
||
94 | * @var string Example request data (for POST methods) |
||
95 | */ |
||
96 | public $example_request_data = ''; |
||
97 | |||
98 | /** |
||
99 | * @var string Example response from $example_request |
||
100 | */ |
||
101 | public $example_response = ''; |
||
102 | |||
103 | /** |
||
104 | * @var bool Set to true if the endpoint implements its own filtering instead of the standard `fields` query method |
||
105 | */ |
||
106 | public $custom_fields_filtering = false; |
||
107 | |||
108 | /** |
||
109 | * @var bool Set to true if the endpoint accepts all cross origin requests. You probably should not set this flag. |
||
110 | */ |
||
111 | public $allow_cross_origin_request = false; |
||
112 | |||
113 | /** |
||
114 | * @var bool Set to true if the endpoint can recieve unauthorized POST requests. |
||
115 | */ |
||
116 | public $allow_unauthorized_request = false; |
||
117 | |||
118 | /** |
||
119 | * @var bool Set to true if the endpoint should accept site based (not user based) authentication. |
||
120 | */ |
||
121 | public $allow_jetpack_site_auth = false; |
||
122 | |||
123 | /** |
||
124 | * @var bool Set to true if the endpoint should accept auth from an upload token. |
||
125 | */ |
||
126 | public $allow_upload_token_auth = false; |
||
127 | |||
128 | function __construct( $args ) { |
||
129 | $defaults = array( |
||
130 | 'in_testing' => false, |
||
131 | 'allowed_if_flagged' => false, |
||
132 | 'allowed_if_red_flagged' => false, |
||
133 | 'allowed_if_deleted' => false, |
||
134 | 'description' => '', |
||
135 | 'group' => '', |
||
136 | 'method' => 'GET', |
||
137 | 'path' => '/', |
||
138 | 'min_version' => '0', |
||
139 | 'max_version' => WPCOM_JSON_API__CURRENT_VERSION, |
||
140 | 'force' => '', |
||
141 | 'deprecated' => false, |
||
142 | 'new_version' => WPCOM_JSON_API__CURRENT_VERSION, |
||
143 | 'jp_disabled' => false, |
||
144 | 'path_labels' => array(), |
||
145 | 'request_format' => array(), |
||
146 | 'response_format' => array(), |
||
147 | 'query_parameters' => array(), |
||
148 | 'version' => 'v1', |
||
149 | 'example_request' => '', |
||
150 | 'example_request_data' => '', |
||
151 | 'example_response' => '', |
||
152 | 'required_scope' => '', |
||
153 | 'pass_wpcom_user_details' => false, |
||
154 | 'custom_fields_filtering' => false, |
||
155 | 'allow_cross_origin_request' => false, |
||
156 | 'allow_unauthorized_request' => false, |
||
157 | 'allow_jetpack_site_auth' => false, |
||
158 | 'allow_upload_token_auth' => false, |
||
159 | ); |
||
160 | |||
161 | $args = wp_parse_args( $args, $defaults ); |
||
162 | |||
163 | $this->in_testing = $args['in_testing']; |
||
0 ignored issues
–
show
|
|||
164 | |||
165 | $this->allowed_if_flagged = $args['allowed_if_flagged']; |
||
166 | $this->allowed_if_red_flagged = $args['allowed_if_red_flagged']; |
||
167 | $this->allowed_if_deleted = $args['allowed_if_deleted']; |
||
168 | |||
169 | $this->description = $args['description']; |
||
170 | $this->group = $args['group']; |
||
171 | $this->stat = $args['stat']; |
||
172 | $this->force = $args['force']; |
||
0 ignored issues
–
show
The property
force 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;
![]() |
|||
173 | $this->jp_disabled = $args['jp_disabled']; |
||
0 ignored issues
–
show
The property
jp_disabled 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;
![]() |
|||
174 | |||
175 | $this->method = $args['method']; |
||
176 | $this->path = $args['path']; |
||
177 | $this->path_labels = $args['path_labels']; |
||
178 | $this->min_version = $args['min_version']; |
||
179 | $this->max_version = $args['max_version']; |
||
180 | $this->deprecated = $args['deprecated']; |
||
0 ignored issues
–
show
The property
deprecated 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;
![]() |
|||
181 | $this->new_version = $args['new_version']; |
||
0 ignored issues
–
show
The property
new_version does not seem to exist. Did you mean version ?
An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name. If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading. ![]() |
|||
182 | |||
183 | // Ensure max version is not less than min version |
||
184 | if ( version_compare( $this->min_version, $this->max_version, '>' ) ) { |
||
185 | $this->max_version = $this->min_version; |
||
186 | } |
||
187 | |||
188 | $this->pass_wpcom_user_details = $args['pass_wpcom_user_details']; |
||
189 | $this->custom_fields_filtering = (bool) $args['custom_fields_filtering']; |
||
190 | |||
191 | $this->allow_cross_origin_request = (bool) $args['allow_cross_origin_request']; |
||
192 | $this->allow_unauthorized_request = (bool) $args['allow_unauthorized_request']; |
||
193 | $this->allow_jetpack_site_auth = (bool) $args['allow_jetpack_site_auth']; |
||
194 | $this->allow_upload_token_auth = (bool) $args['allow_upload_token_auth']; |
||
195 | |||
196 | $this->version = $args['version']; |
||
0 ignored issues
–
show
Equals sign not aligned correctly; expected 1 space but found 5 spaces
This check looks for improperly formatted assignments. Every assignment must have exactly one space before and one space after the equals operator. To illustrate: $a = "a";
$ab = "ab";
$abc = "abc";
will have no issues, while $a = "a";
$ab = "ab";
$abc = "abc";
will report issues in lines 1 and 2. ![]() |
|||
197 | |||
198 | $this->required_scope = $args['required_scope']; |
||
0 ignored issues
–
show
The property
required_scope 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;
![]() |
|||
199 | |||
200 | View Code Duplication | if ( $this->request_format ) { |
|
0 ignored issues
–
show
The expression
$this->request_format 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 ![]() |
|||
201 | $this->request_format = array_filter( array_merge( $this->request_format, $args['request_format'] ) ); |
||
202 | } else { |
||
203 | $this->request_format = $args['request_format']; |
||
204 | } |
||
205 | |||
206 | View Code Duplication | if ( $this->response_format ) { |
|
0 ignored issues
–
show
The expression
$this->response_format 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 ![]() |
|||
207 | $this->response_format = array_filter( array_merge( $this->response_format, $args['response_format'] ) ); |
||
208 | } else { |
||
209 | $this->response_format = $args['response_format']; |
||
210 | } |
||
211 | |||
212 | if ( false === $args['query_parameters'] ) { |
||
213 | $this->query = array(); |
||
214 | } elseif ( is_array( $args['query_parameters'] ) ) { |
||
215 | $this->query = array_filter( array_merge( $this->query, $args['query_parameters'] ) ); |
||
216 | } |
||
217 | |||
218 | $this->api = WPCOM_JSON_API::init(); // Auto-add to WPCOM_JSON_API |
||
219 | $this->links = WPCOM_JSON_API_Links::getInstance(); |
||
220 | |||
221 | /** Example Request/Response ******************************************/ |
||
222 | |||
223 | // Examples for endpoint documentation request |
||
224 | $this->example_request = $args['example_request']; |
||
225 | $this->example_request_data = $args['example_request_data']; |
||
226 | $this->example_response = $args['example_response']; |
||
227 | |||
228 | $this->api->add( $this ); |
||
229 | } |
||
230 | |||
231 | // Get all query args. Prefill with defaults |
||
232 | function query_args( $return_default_values = true, $cast_and_filter = true ) { |
||
233 | $args = array_intersect_key( $this->api->query, $this->query ); |
||
234 | |||
235 | if ( !$cast_and_filter ) { |
||
236 | return $args; |
||
237 | } |
||
238 | |||
239 | return $this->cast_and_filter( $args, $this->query, $return_default_values ); |
||
240 | } |
||
241 | |||
242 | // Get POST body data |
||
243 | function input( $return_default_values = true, $cast_and_filter = true ) { |
||
244 | $input = trim( $this->api->post_body ); |
||
245 | $content_type = $this->api->content_type; |
||
246 | if ( $content_type ) { |
||
247 | list ( $content_type ) = explode( ';', $content_type ); |
||
248 | } |
||
249 | $content_type = trim( $content_type ); |
||
250 | switch ( $content_type ) { |
||
251 | case 'application/json' : |
||
252 | case 'application/x-javascript' : |
||
253 | case 'text/javascript' : |
||
254 | case 'text/x-javascript' : |
||
255 | case 'text/x-json' : |
||
256 | case 'text/json' : |
||
257 | $return = json_decode( $input, true ); |
||
258 | |||
259 | if ( function_exists( 'json_last_error' ) ) { |
||
260 | if ( JSON_ERROR_NONE !== json_last_error() ) { // phpcs:ignore PHPCompatibility |
||
261 | return null; |
||
262 | } |
||
263 | } else { |
||
264 | if ( is_null( $return ) && json_encode( null ) !== $input ) { |
||
265 | return null; |
||
266 | } |
||
267 | } |
||
268 | |||
269 | break; |
||
270 | case 'multipart/form-data' : |
||
271 | $return = array_merge( stripslashes_deep( $_POST ), $_FILES ); |
||
272 | break; |
||
273 | case 'application/x-www-form-urlencoded' : |
||
274 | //attempt JSON first, since probably a curl command |
||
275 | $return = json_decode( $input, true ); |
||
276 | |||
277 | if ( is_null( $return ) ) { |
||
278 | wp_parse_str( $input, $return ); |
||
279 | } |
||
280 | |||
281 | break; |
||
282 | default : |
||
0 ignored issues
–
show
There must be no space before the colon in a DEFAULT statement
As per the PSR-2 coding standard, there must not be a space in front of the colon in the default statement. switch ($expr) {
default : //wrong
doSomething();
break;
}
switch ($expr) {
default: //right
doSomething();
break;
}
To learn more about the PSR-2 coding standard, please refer to the PHP-Fig. ![]() |
|||
283 | wp_parse_str( $input, $return ); |
||
0 ignored issues
–
show
The variable
$return seems only to be defined at a later point. Did you maybe move this code here without moving the variable definition?
This error can happen if you refactor code and forget to move the variable initialization. Let’s take a look at a simple example: function someFunction() {
$x = 5;
echo $x;
}
The above code is perfectly fine. Now imagine that we re-order the statements: function someFunction() {
echo $x;
$x = 5;
}
In that case, ![]() |
|||
284 | break; |
||
285 | } |
||
286 | |||
287 | if ( isset( $this->api->query['force'] ) |
||
288 | && 'secure' === $this->api->query['force'] |
||
289 | && isset( $return['secure_key'] ) ) { |
||
290 | $this->api->post_body = $this->get_secure_body( $return['secure_key'] ); |
||
291 | $this->api->query['force'] = false; |
||
292 | return $this->input( $return_default_values, $cast_and_filter ); |
||
293 | } |
||
294 | |||
295 | if ( $cast_and_filter ) { |
||
296 | $return = $this->cast_and_filter( $return, $this->request_format, $return_default_values ); |
||
0 ignored issues
–
show
The variable
$return does not seem to be defined for all execution paths leading up to this point.
If you define a variable conditionally, it can happen that it is not defined for all execution paths. Let’s take a look at an example: function myFunction($a) {
switch ($a) {
case 'foo':
$x = 1;
break;
case 'bar':
$x = 2;
break;
}
// $x is potentially undefined here.
echo $x;
}
In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined. Available Fixes
![]() |
|||
297 | } |
||
298 | return $return; |
||
299 | } |
||
300 | |||
301 | |||
302 | protected function get_secure_body( $secure_key ) { |
||
303 | $response = Jetpack_Client::wpcom_json_api_request_as_blog( |
||
304 | sprintf( '/sites/%d/secure-request', Jetpack_Options::get_option('id' ) ), |
||
305 | '1.1', |
||
306 | array( 'method' => 'POST' ), |
||
307 | array( 'secure_key' => $secure_key ) |
||
0 ignored issues
–
show
array('secure_key' => $secure_key) is of type array<string,?,{"secure_key":"?"}> , but the function expects a string|null .
It seems like the type of the argument is not accepted by the function/method which you are calling. In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug. We suggest to add an explicit type cast like in the following example: function acceptsInteger($int) { }
$x = '123'; // string "123"
// Instead of
acceptsInteger($x);
// we recommend to use
acceptsInteger((integer) $x);
![]() |
|||
308 | ); |
||
309 | if ( 200 !== $response['response']['code'] ) { |
||
310 | return null; |
||
311 | } |
||
312 | return json_decode( $response['body'], true ); |
||
313 | } |
||
314 | |||
315 | function cast_and_filter( $data, $documentation, $return_default_values = false, $for_output = false ) { |
||
316 | $return_as_object = false; |
||
317 | if ( is_object( $data ) ) { |
||
318 | // @todo this should probably be a deep copy if $data can ever have nested objects |
||
0 ignored issues
–
show
|
|||
319 | $data = (array) $data; |
||
320 | $return_as_object = true; |
||
321 | } elseif ( !is_array( $data ) ) { |
||
322 | return $data; |
||
323 | } |
||
324 | |||
325 | $boolean_arg = array( 'false', 'true' ); |
||
326 | $naeloob_arg = array( 'true', 'false' ); |
||
327 | |||
328 | $return = array(); |
||
329 | |||
330 | foreach ( $documentation as $key => $description ) { |
||
331 | if ( is_array( $description ) ) { |
||
332 | // String or boolean array keys only |
||
333 | $whitelist = array_keys( $description ); |
||
334 | |||
335 | if ( $whitelist === $boolean_arg || $whitelist === $naeloob_arg ) { |
||
336 | // Truthiness |
||
337 | if ( isset( $data[$key] ) ) { |
||
338 | $return[$key] = (bool) WPCOM_JSON_API::is_truthy( $data[$key] ); |
||
339 | } elseif ( $return_default_values ) { |
||
340 | $return[$key] = $whitelist === $naeloob_arg; // Default to true for naeloob_arg and false for boolean_arg. |
||
341 | } |
||
342 | } elseif ( isset( $data[$key] ) && isset( $description[$data[$key]] ) ) { |
||
343 | // String Key |
||
344 | $return[$key] = (string) $data[$key]; |
||
345 | } elseif ( $return_default_values ) { |
||
346 | // Default value |
||
347 | $return[$key] = (string) current( $whitelist ); |
||
348 | } |
||
349 | |||
350 | continue; |
||
351 | } |
||
352 | |||
353 | $types = $this->parse_types( $description ); |
||
354 | $type = array_shift( $types ); |
||
355 | |||
356 | // Explicit default - string and int only for now. Always set these reguardless of $return_default_values |
||
357 | if ( isset( $type['default'] ) ) { |
||
358 | if ( !isset( $data[$key] ) ) { |
||
359 | $data[$key] = $type['default']; |
||
360 | } |
||
361 | } |
||
362 | |||
363 | if ( !isset( $data[$key] ) ) { |
||
364 | continue; |
||
365 | } |
||
366 | |||
367 | $this->cast_and_filter_item( $return, $type, $key, $data[$key], $types, $for_output ); |
||
368 | } |
||
369 | |||
370 | if ( $return_as_object ) { |
||
371 | return (object) $return; |
||
372 | } |
||
373 | |||
374 | return $return; |
||
375 | } |
||
376 | |||
377 | /** |
||
378 | * Casts $value according to $type. |
||
379 | * Handles fallbacks for certain values of $type when $value is not that $type |
||
380 | * Currently, only handles fallback between string <-> array (two way), from string -> false (one way), and from object -> false (one way), |
||
381 | * and string -> object (one way) |
||
382 | * |
||
383 | * Handles "child types" - array:URL, object:category |
||
384 | * array:URL means an array of URLs |
||
385 | * object:category means a hash of categories |
||
386 | * |
||
387 | * Handles object typing - object>post means an object of type post |
||
388 | */ |
||
389 | function cast_and_filter_item( &$return, $type, $key, $value, $types = array(), $for_output = false ) { |
||
390 | if ( is_string( $type ) ) { |
||
391 | $type = compact( 'type' ); |
||
392 | } |
||
393 | |||
394 | switch ( $type['type'] ) { |
||
395 | case 'false' : |
||
396 | $return[$key] = false; |
||
397 | break; |
||
398 | case 'url' : |
||
399 | $return[$key] = (string) esc_url_raw( $value ); |
||
400 | break; |
||
401 | case 'string' : |
||
402 | // Fallback string -> array, or for string -> object |
||
403 | if ( is_array( $value ) || is_object( $value ) ) { |
||
404 | if ( !empty( $types[0] ) ) { |
||
405 | $next_type = array_shift( $types ); |
||
406 | return $this->cast_and_filter_item( $return, $next_type, $key, $value, $types, $for_output ); |
||
407 | } |
||
408 | } |
||
409 | |||
410 | // Fallback string -> false |
||
411 | View Code Duplication | if ( !is_string( $value ) ) { |
|
412 | if ( !empty( $types[0] ) && 'false' === $types[0]['type'] ) { |
||
413 | $next_type = array_shift( $types ); |
||
414 | return $this->cast_and_filter_item( $return, $next_type, $key, $value, $types, $for_output ); |
||
415 | } |
||
416 | } |
||
417 | $return[$key] = (string) $value; |
||
418 | break; |
||
419 | case 'html' : |
||
420 | $return[$key] = (string) $value; |
||
421 | break; |
||
422 | case 'safehtml' : |
||
423 | $return[$key] = wp_kses( (string) $value, wp_kses_allowed_html() ); |
||
424 | break; |
||
425 | case 'zip' : |
||
426 | case 'media' : |
||
427 | if ( is_array( $value ) ) { |
||
428 | if ( isset( $value['name'] ) && is_array( $value['name'] ) ) { |
||
429 | // It's a $_FILES array |
||
430 | // Reformat into array of $_FILES items |
||
431 | $files = array(); |
||
432 | |||
433 | foreach ( $value['name'] as $k => $v ) { |
||
434 | $files[$k] = array(); |
||
435 | foreach ( array_keys( $value ) as $file_key ) { |
||
436 | $files[$k][$file_key] = $value[$file_key][$k]; |
||
437 | } |
||
438 | } |
||
439 | |||
440 | $return[$key] = $files; |
||
441 | break; |
||
442 | } |
||
443 | } else { |
||
0 ignored issues
–
show
This
else statement is empty and can be removed.
This check looks for the These if (rand(1, 6) > 3) {
print "Check failed";
} else {
//print "Check succeeded";
}
could be turned into if (rand(1, 6) > 3) {
print "Check failed";
}
This is much more concise to read. ![]() |
|||
444 | // no break - treat as 'array' |
||
445 | } |
||
446 | // nobreak |
||
447 | case 'array' : |
||
448 | // Fallback array -> string |
||
449 | if ( is_string( $value ) ) { |
||
450 | if ( !empty( $types[0] ) ) { |
||
451 | $next_type = array_shift( $types ); |
||
452 | return $this->cast_and_filter_item( $return, $next_type, $key, $value, $types, $for_output ); |
||
453 | } |
||
454 | } |
||
455 | |||
456 | View Code Duplication | if ( isset( $type['children'] ) ) { |
|
457 | $children = array(); |
||
458 | foreach ( (array) $value as $k => $child ) { |
||
459 | $this->cast_and_filter_item( $children, $type['children'], $k, $child, array(), $for_output ); |
||
460 | } |
||
461 | $return[$key] = (array) $children; |
||
462 | break; |
||
463 | } |
||
464 | |||
465 | $return[$key] = (array) $value; |
||
466 | break; |
||
467 | case 'iso 8601 datetime' : |
||
468 | case 'datetime' : |
||
469 | // (string)s |
||
470 | $dates = $this->parse_date( (string) $value ); |
||
471 | if ( $for_output ) { |
||
472 | $return[$key] = $this->format_date( $dates[1], $dates[0] ); |
||
473 | } else { |
||
474 | list( $return[$key], $return["{$key}_gmt"] ) = $dates; |
||
475 | } |
||
476 | break; |
||
477 | case 'float' : |
||
478 | $return[$key] = (float) $value; |
||
479 | break; |
||
480 | case 'int' : |
||
481 | case 'integer' : |
||
482 | $return[$key] = (int) $value; |
||
483 | break; |
||
484 | case 'bool' : |
||
485 | case 'boolean' : |
||
486 | $return[$key] = (bool) WPCOM_JSON_API::is_truthy( $value ); |
||
487 | break; |
||
488 | case 'object' : |
||
489 | // Fallback object -> false |
||
490 | View Code Duplication | if ( is_scalar( $value ) || is_null( $value ) ) { |
|
491 | if ( !empty( $types[0] ) && 'false' === $types[0]['type'] ) { |
||
492 | return $this->cast_and_filter_item( $return, 'false', $key, $value, $types, $for_output ); |
||
493 | } |
||
494 | } |
||
495 | |||
496 | View Code Duplication | if ( isset( $type['children'] ) ) { |
|
497 | $children = array(); |
||
498 | foreach ( (array) $value as $k => $child ) { |
||
499 | $this->cast_and_filter_item( $children, $type['children'], $k, $child, array(), $for_output ); |
||
500 | } |
||
501 | $return[$key] = (object) $children; |
||
502 | break; |
||
503 | } |
||
504 | |||
505 | if ( isset( $type['subtype'] ) ) { |
||
506 | return $this->cast_and_filter_item( $return, $type['subtype'], $key, $value, $types, $for_output ); |
||
507 | } |
||
508 | |||
509 | $return[$key] = (object) $value; |
||
510 | break; |
||
511 | case 'post' : |
||
512 | $return[$key] = (object) $this->cast_and_filter( $value, $this->post_object_format, false, $for_output ); |
||
513 | break; |
||
514 | case 'comment' : |
||
515 | $return[$key] = (object) $this->cast_and_filter( $value, $this->comment_object_format, false, $for_output ); |
||
516 | break; |
||
517 | case 'tag' : |
||
518 | case 'category' : |
||
519 | $docs = array( |
||
520 | 'ID' => '(int)', |
||
521 | 'name' => '(string)', |
||
522 | 'slug' => '(string)', |
||
523 | 'description' => '(HTML)', |
||
524 | 'post_count' => '(int)', |
||
525 | 'feed_url' => '(string)', |
||
526 | 'meta' => '(object)', |
||
527 | ); |
||
528 | if ( 'category' === $type['type'] ) { |
||
529 | $docs['parent'] = '(int)'; |
||
530 | } |
||
531 | $return[$key] = (object) $this->cast_and_filter( $value, $docs, false, $for_output ); |
||
532 | break; |
||
533 | case 'post_reference' : |
||
534 | View Code Duplication | case 'comment_reference' : |
|
535 | $docs = array( |
||
536 | 'ID' => '(int)', |
||
537 | 'type' => '(string)', |
||
538 | 'title' => '(string)', |
||
539 | 'link' => '(URL)', |
||
540 | ); |
||
541 | $return[$key] = (object) $this->cast_and_filter( $value, $docs, false, $for_output ); |
||
542 | break; |
||
543 | View Code Duplication | case 'geo' : |
|
544 | $docs = array( |
||
545 | 'latitude' => '(float)', |
||
546 | 'longitude' => '(float)', |
||
547 | 'address' => '(string)', |
||
548 | ); |
||
549 | $return[$key] = (object) $this->cast_and_filter( $value, $docs, false, $for_output ); |
||
550 | break; |
||
551 | case 'author' : |
||
552 | $docs = array( |
||
553 | 'ID' => '(int)', |
||
554 | 'user_login' => '(string)', |
||
555 | 'login' => '(string)', |
||
556 | 'email' => '(string|false)', |
||
557 | 'name' => '(string)', |
||
558 | 'first_name' => '(string)', |
||
559 | 'last_name' => '(string)', |
||
560 | 'nice_name' => '(string)', |
||
561 | 'URL' => '(URL)', |
||
562 | 'avatar_URL' => '(URL)', |
||
563 | 'profile_URL' => '(URL)', |
||
564 | 'is_super_admin' => '(bool)', |
||
565 | 'roles' => '(array:string)', |
||
566 | 'ip_address' => '(string|false)', |
||
567 | ); |
||
568 | $return[$key] = (object) $this->cast_and_filter( $value, $docs, false, $for_output ); |
||
569 | break; |
||
570 | View Code Duplication | case 'role' : |
|
571 | $docs = array( |
||
572 | 'name' => '(string)', |
||
573 | 'display_name' => '(string)', |
||
574 | 'capabilities' => '(object:boolean)', |
||
575 | ); |
||
576 | $return[$key] = (object) $this->cast_and_filter( $value, $docs, false, $for_output ); |
||
577 | break; |
||
578 | case 'attachment' : |
||
579 | $docs = array( |
||
580 | 'ID' => '(int)', |
||
581 | 'URL' => '(URL)', |
||
582 | 'guid' => '(string)', |
||
583 | 'mime_type' => '(string)', |
||
584 | 'width' => '(int)', |
||
585 | 'height' => '(int)', |
||
586 | 'duration' => '(int)', |
||
587 | ); |
||
588 | $return[$key] = (object) $this->cast_and_filter( |
||
589 | $value, |
||
590 | /** |
||
591 | * Filter the documentation returned for a post attachment. |
||
592 | * |
||
593 | * @module json-api |
||
594 | * |
||
595 | * @since 1.9.0 |
||
596 | * |
||
597 | * @param array $docs Array of documentation about a post attachment. |
||
598 | */ |
||
599 | apply_filters( 'wpcom_json_api_attachment_cast_and_filter', $docs ), |
||
600 | false, |
||
601 | $for_output |
||
602 | ); |
||
603 | break; |
||
604 | case 'metadata' : |
||
605 | $docs = array( |
||
606 | 'id' => '(int)', |
||
607 | 'key' => '(string)', |
||
608 | 'value' => '(string|false|float|int|array|object)', |
||
609 | 'previous_value' => '(string)', |
||
610 | 'operation' => '(string)', |
||
611 | ); |
||
612 | $return[$key] = (object) $this->cast_and_filter( |
||
613 | $value, |
||
614 | /** This filter is documented in class.json-api-endpoints.php */ |
||
615 | apply_filters( 'wpcom_json_api_attachment_cast_and_filter', $docs ), |
||
616 | false, |
||
617 | $for_output |
||
618 | ); |
||
619 | break; |
||
620 | case 'plugin' : |
||
621 | $docs = array( |
||
622 | 'id' => '(safehtml) The plugin\'s ID', |
||
623 | 'slug' => '(safehtml) The plugin\'s Slug', |
||
624 | 'active' => '(boolean) The plugin status.', |
||
625 | 'update' => '(object) The plugin update info.', |
||
626 | 'name' => '(safehtml) The name of the plugin.', |
||
627 | 'plugin_url' => '(url) Link to the plugin\'s web site.', |
||
628 | 'version' => '(safehtml) The plugin version number.', |
||
629 | 'description' => '(safehtml) Description of what the plugin does and/or notes from the author', |
||
630 | 'author' => '(safehtml) The plugin author\'s name', |
||
631 | 'author_url' => '(url) The plugin author web site address', |
||
632 | 'network' => '(boolean) Whether the plugin can only be activated network wide.', |
||
633 | 'autoupdate' => '(boolean) Whether the plugin is auto updated', |
||
634 | 'log' => '(array:safehtml) An array of update log strings.', |
||
635 | 'action_links' => '(array) An array of action links that the plugin uses.', |
||
636 | ); |
||
637 | $return[$key] = (object) $this->cast_and_filter( |
||
638 | $value, |
||
639 | /** |
||
640 | * Filter the documentation returned for a plugin. |
||
641 | * |
||
642 | * @module json-api |
||
643 | * |
||
644 | * @since 3.1.0 |
||
645 | * |
||
646 | * @param array $docs Array of documentation about a plugin. |
||
647 | */ |
||
648 | apply_filters( 'wpcom_json_api_plugin_cast_and_filter', $docs ), |
||
649 | false, |
||
650 | $for_output |
||
651 | ); |
||
652 | break; |
||
653 | case 'plugin_v1_2' : |
||
654 | $docs = class_exists( 'Jetpack_JSON_API_Get_Plugins_v1_2_Endpoint' ) |
||
655 | ? Jetpack_JSON_API_Get_Plugins_v1_2_Endpoint::$_response_format |
||
656 | : Jetpack_JSON_API_Plugins_Endpoint::$_response_format_v1_2; |
||
0 ignored issues
–
show
The property
_response_format_v1_2 cannot be accessed from this context as it is declared private in class Jetpack_JSON_API_Plugins_Endpoint .
This check looks for access to properties that are not accessible from the current context. If you need to make a property accessible to another context you can either raise its visibility level or provide an accessible getter in the defining class. ![]() |
|||
657 | $return[$key] = (object) $this->cast_and_filter( |
||
658 | $value, |
||
659 | /** |
||
660 | * Filter the documentation returned for a plugin. |
||
661 | * |
||
662 | * @module json-api |
||
663 | * |
||
664 | * @since 3.1.0 |
||
665 | * |
||
666 | * @param array $docs Array of documentation about a plugin. |
||
667 | */ |
||
668 | apply_filters( 'wpcom_json_api_plugin_cast_and_filter', $docs ), |
||
669 | false, |
||
670 | $for_output |
||
671 | ); |
||
672 | break; |
||
673 | case 'file_mod_capabilities': |
||
674 | $docs = array( |
||
675 | 'reasons_modify_files_unavailable' => '(array) The reasons why files can\'t be modified', |
||
676 | 'reasons_autoupdate_unavailable' => '(array) The reasons why autoupdates aren\'t allowed', |
||
677 | 'modify_files' => '(boolean) true if files can be modified', |
||
678 | 'autoupdate_files' => '(boolean) true if autoupdates are allowed', |
||
679 | ); |
||
680 | $return[ $key ] = (array) $this->cast_and_filter( $value, $docs, false, $for_output ); |
||
681 | break; |
||
682 | case 'jetpackmodule' : |
||
683 | $docs = array( |
||
684 | 'id' => '(string) The module\'s ID', |
||
685 | 'active' => '(boolean) The module\'s status.', |
||
686 | 'name' => '(string) The module\'s name.', |
||
687 | 'description' => '(safehtml) The module\'s description.', |
||
688 | 'sort' => '(int) The module\'s display order.', |
||
689 | 'introduced' => '(string) The Jetpack version when the module was introduced.', |
||
690 | 'changed' => '(string) The Jetpack version when the module was changed.', |
||
691 | 'free' => '(boolean) The module\'s Free or Paid status.', |
||
692 | 'module_tags' => '(array) The module\'s tags.', |
||
693 | 'override' => '(string) The module\'s override. Empty if no override, otherwise \'active\' or \'inactive\'', |
||
694 | ); |
||
695 | $return[$key] = (object) $this->cast_and_filter( |
||
696 | $value, |
||
697 | /** This filter is documented in class.json-api-endpoints.php */ |
||
698 | apply_filters( 'wpcom_json_api_plugin_cast_and_filter', $docs ), |
||
699 | false, |
||
700 | $for_output |
||
701 | ); |
||
702 | break; |
||
703 | case 'sharing_button' : |
||
704 | $docs = array( |
||
705 | 'ID' => '(string)', |
||
706 | 'name' => '(string)', |
||
707 | 'URL' => '(string)', |
||
708 | 'icon' => '(string)', |
||
709 | 'enabled' => '(bool)', |
||
710 | 'visibility' => '(string)', |
||
711 | ); |
||
712 | $return[$key] = (array) $this->cast_and_filter( $value, $docs, false, $for_output ); |
||
713 | break; |
||
714 | case 'sharing_button_service': |
||
715 | $docs = array( |
||
716 | 'ID' => '(string) The service identifier', |
||
717 | 'name' => '(string) The service name', |
||
718 | 'class_name' => '(string) Class name for custom style sharing button elements', |
||
719 | 'genericon' => '(string) The Genericon unicode character for the custom style sharing button icon', |
||
720 | 'preview_smart' => '(string) An HTML snippet of a rendered sharing button smart preview', |
||
721 | 'preview_smart_js' => '(string) An HTML snippet of the page-wide initialization scripts used for rendering the sharing button smart preview' |
||
722 | ); |
||
723 | $return[$key] = (array) $this->cast_and_filter( $value, $docs, false, $for_output ); |
||
724 | break; |
||
725 | case 'site_keyring': |
||
726 | $docs = array( |
||
727 | 'keyring_id' => '(int) Keyring ID', |
||
728 | 'service' => '(string) The service name', |
||
729 | 'external_user_id' => '(string) External user id for the service' |
||
730 | ); |
||
731 | $return[$key] = (array) $this->cast_and_filter( $value, $docs, false, $for_output ); |
||
732 | break; |
||
733 | case 'taxonomy': |
||
734 | $docs = array( |
||
735 | 'name' => '(string) The taxonomy slug', |
||
736 | 'label' => '(string) The taxonomy human-readable name', |
||
737 | 'labels' => '(object) Mapping of labels for the taxonomy', |
||
738 | 'description' => '(string) The taxonomy description', |
||
739 | 'hierarchical' => '(bool) Whether the taxonomy is hierarchical', |
||
740 | 'public' => '(bool) Whether the taxonomy is public', |
||
741 | 'capabilities' => '(object) Mapping of current user capabilities for the taxonomy', |
||
742 | ); |
||
743 | $return[$key] = (array) $this->cast_and_filter( $value, $docs, false, $for_output ); |
||
744 | break; |
||
745 | |||
746 | default : |
||
0 ignored issues
–
show
There must be no space before the colon in a DEFAULT statement
As per the PSR-2 coding standard, there must not be a space in front of the colon in the default statement. switch ($expr) {
default : //wrong
doSomething();
break;
}
switch ($expr) {
default: //right
doSomething();
break;
}
To learn more about the PSR-2 coding standard, please refer to the PHP-Fig. ![]() |
|||
747 | $method_name = $type['type'] . '_docs'; |
||
748 | if ( method_exists( 'WPCOM_JSON_API_Jetpack_Overrides', $method_name ) ) { |
||
749 | $docs = WPCOM_JSON_API_Jetpack_Overrides::$method_name(); |
||
750 | } |
||
751 | |||
752 | if ( ! empty( $docs ) ) { |
||
753 | $return[$key] = (object) $this->cast_and_filter( |
||
754 | $value, |
||
755 | /** This filter is documented in class.json-api-endpoints.php */ |
||
756 | apply_filters( 'wpcom_json_api_plugin_cast_and_filter', $docs ), |
||
757 | false, |
||
758 | $for_output |
||
759 | ); |
||
760 | } else { |
||
761 | trigger_error( "Unknown API casting type {$type['type']}", E_USER_WARNING ); |
||
762 | } |
||
763 | } |
||
764 | } |
||
765 | |||
766 | function parse_types( $text ) { |
||
767 | if ( !preg_match( '#^\(([^)]+)\)#', ltrim( $text ), $matches ) ) { |
||
768 | return 'none'; |
||
769 | } |
||
770 | |||
771 | $types = explode( '|', strtolower( $matches[1] ) ); |
||
772 | $return = array(); |
||
773 | foreach ( $types as $type ) { |
||
774 | foreach ( array( ':' => 'children', '>' => 'subtype', '=' => 'default' ) as $operator => $meaning ) { |
||
775 | if ( false !== strpos( $type, $operator ) ) { |
||
776 | $item = explode( $operator, $type, 2 ); |
||
777 | $return[] = array( 'type' => $item[0], $meaning => $item[1] ); |
||
778 | continue 2; |
||
779 | } |
||
780 | } |
||
781 | $return[] = compact( 'type' ); |
||
782 | } |
||
783 | |||
784 | return $return; |
||
785 | } |
||
786 | |||
787 | /** |
||
788 | * Checks if the endpoint is publicly displayable |
||
789 | */ |
||
790 | function is_publicly_documentable() { |
||
791 | return '__do_not_document' !== $this->group && true !== $this->in_testing; |
||
792 | } |
||
793 | |||
794 | /** |
||
795 | * Auto generates documentation based on description, method, path, path_labels, and query parameters. |
||
796 | * Echoes HTML. |
||
797 | */ |
||
798 | function document( $show_description = true ) { |
||
799 | global $wpdb; |
||
800 | $original_post = isset( $GLOBALS['post'] ) ? $GLOBALS['post'] : 'unset'; |
||
801 | unset( $GLOBALS['post'] ); |
||
802 | |||
803 | $doc = $this->generate_documentation(); |
||
804 | |||
805 | if ( $show_description ) : |
||
806 | ?> |
||
807 | <caption> |
||
808 | <h1><?php echo wp_kses_post( $doc['method'] ); ?> <?php echo wp_kses_post( $doc['path_labeled'] ); ?></h1> |
||
809 | <p><?php echo wp_kses_post( $doc['description'] ); ?></p> |
||
810 | </caption> |
||
811 | |||
812 | <?php endif; ?> |
||
813 | |||
814 | <?php if ( true === $this->deprecated ) { ?> |
||
815 | <p><strong>This endpoint is deprecated in favor of version <?php echo floatval( $this->new_version ); ?></strong></p> |
||
0 ignored issues
–
show
The property
new_version does not seem to exist. Did you mean version ?
An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name. If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading. ![]() |
|||
816 | <?php } ?> |
||
817 | |||
818 | <section class="resource-info"> |
||
819 | <h2 id="apidoc-resource-info">Resource Information</h2> |
||
820 | |||
821 | <table class="api-doc api-doc-resource-parameters api-doc-resource"> |
||
822 | |||
823 | <thead> |
||
824 | <tr> |
||
825 | <th class="api-index-title" scope="column"> </th> |
||
826 | <th class="api-index-title" scope="column"> </th> |
||
827 | </tr> |
||
828 | </thead> |
||
829 | <tbody> |
||
830 | |||
831 | <tr class="api-index-item"> |
||
832 | <th scope="row" class="parameter api-index-item-title">Method</th> |
||
833 | <td class="type api-index-item-title"><?php echo wp_kses_post( $doc['method'] ); ?></td> |
||
834 | </tr> |
||
835 | |||
836 | <tr class="api-index-item"> |
||
837 | <th scope="row" class="parameter api-index-item-title">URL</th> |
||
838 | <?php |
||
839 | $version = WPCOM_JSON_API__CURRENT_VERSION; |
||
840 | if ( !empty( $this->max_version ) ) { |
||
841 | $version = $this->max_version; |
||
842 | } |
||
843 | ?> |
||
844 | <td class="type api-index-item-title">https://public-api.wordpress.com/rest/v<?php echo floatval( $version ); ?><?php echo wp_kses_post( $doc['path_labeled'] ); ?></td> |
||
845 | </tr> |
||
846 | |||
847 | <tr class="api-index-item"> |
||
848 | <th scope="row" class="parameter api-index-item-title">Requires authentication?</th> |
||
849 | <?php |
||
850 | $requires_auth = $wpdb->get_row( $wpdb->prepare( "SELECT requires_authentication FROM rest_api_documentation WHERE `version` = %s AND `path` = %s AND `method` = %s LIMIT 1", $version, untrailingslashit( $doc['path_labeled'] ), $doc['method'] ) ); |
||
851 | ?> |
||
852 | <td class="type api-index-item-title"><?php echo ( true === (bool) $requires_auth->requires_authentication ? 'Yes' : 'No' ); ?></td> |
||
853 | </tr> |
||
854 | |||
855 | </tbody> |
||
856 | </table> |
||
857 | |||
858 | </section> |
||
859 | |||
860 | <?php |
||
861 | |||
862 | foreach ( array( |
||
863 | 'path' => 'Method Parameters', |
||
864 | 'query' => 'Query Parameters', |
||
865 | 'body' => 'Request Parameters', |
||
866 | 'response' => 'Response Parameters', |
||
867 | ) as $doc_section_key => $label ) : |
||
868 | $doc_section = 'response' === $doc_section_key ? $doc['response']['body'] : $doc['request'][$doc_section_key]; |
||
869 | if ( !$doc_section ) { |
||
0 ignored issues
–
show
The expression
$doc_section 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 ![]() |
|||
870 | continue; |
||
871 | } |
||
872 | |||
873 | $param_label = strtolower( str_replace( ' ', '-', $label ) ); |
||
874 | ?> |
||
875 | |||
876 | <section class="<?php echo $param_label; ?>"> |
||
877 | |||
878 | <h2 id="apidoc-<?php echo esc_attr( $doc_section_key ); ?>"><?php echo wp_kses_post( $label ); ?></h2> |
||
879 | |||
880 | <table class="api-doc api-doc-<?php echo $param_label; ?>-parameters api-doc-<?php echo strtolower( str_replace( ' ', '-', $doc['group'] ) ); ?>"> |
||
881 | |||
882 | <thead> |
||
883 | <tr> |
||
884 | <th class="api-index-title" scope="column">Parameter</th> |
||
885 | <th class="api-index-title" scope="column">Type</th> |
||
886 | <th class="api-index-title" scope="column">Description</th> |
||
887 | </tr> |
||
888 | </thead> |
||
889 | <tbody> |
||
890 | |||
891 | <?php foreach ( $doc_section as $key => $item ) : ?> |
||
892 | |||
893 | <tr class="api-index-item"> |
||
894 | <th scope="row" class="parameter api-index-item-title"><?php echo wp_kses_post( $key ); ?></th> |
||
895 | <td class="type api-index-item-title"><?php echo wp_kses_post( $item['type'] ); // @todo auto-link? ?></td> |
||
0 ignored issues
–
show
|
|||
896 | <td class="description api-index-item-body"><?php |
||
897 | |||
898 | $this->generate_doc_description( $item['description'] ); |
||
899 | |||
900 | ?></td> |
||
901 | </tr> |
||
902 | |||
903 | <?php endforeach; ?> |
||
904 | </tbody> |
||
905 | </table> |
||
906 | </section> |
||
907 | <?php endforeach; ?> |
||
908 | |||
909 | <?php |
||
910 | if ( 'unset' !== $original_post ) { |
||
911 | $GLOBALS['post'] = $original_post; |
||
912 | } |
||
913 | } |
||
914 | |||
915 | function add_http_build_query_to_php_content_example( $matches ) { |
||
916 | $trimmed_match = ltrim( $matches[0] ); |
||
917 | $pad = substr( $matches[0], 0, -1 * strlen( $trimmed_match ) ); |
||
918 | $pad = ltrim( $pad, ' ' ); |
||
919 | $return = ' ' . str_replace( "\n", "\n ", $matches[0] ); |
||
920 | return " http_build_query({$return}{$pad})"; |
||
921 | } |
||
922 | |||
923 | /** |
||
924 | * Recursively generates the <dl>'s to document item descriptions. |
||
925 | * Echoes HTML. |
||
926 | */ |
||
927 | function generate_doc_description( $item ) { |
||
928 | if ( is_array( $item ) ) : ?> |
||
929 | |||
930 | <dl> |
||
931 | <?php foreach ( $item as $description_key => $description_value ) : ?> |
||
932 | |||
933 | <dt><?php echo wp_kses_post( $description_key . ':' ); ?></dt> |
||
934 | <dd><?php $this->generate_doc_description( $description_value ); ?></dd> |
||
935 | |||
936 | <?php endforeach; ?> |
||
937 | |||
938 | </dl> |
||
939 | |||
940 | <?php |
||
941 | else : |
||
942 | echo wp_kses_post( $item ); |
||
943 | endif; |
||
944 | } |
||
945 | |||
946 | /** |
||
947 | * Auto generates documentation based on description, method, path, path_labels, and query parameters. |
||
948 | * Echoes HTML. |
||
949 | */ |
||
950 | function generate_documentation() { |
||
951 | $format = str_replace( '%d', '%s', $this->path ); |
||
952 | $path_labeled = $format; |
||
953 | if ( ! empty( $this->path_labels ) ) { |
||
954 | $path_labeled = vsprintf( $format, array_keys( $this->path_labels ) ); |
||
955 | } |
||
956 | $boolean_arg = array( 'false', 'true' ); |
||
957 | $naeloob_arg = array( 'true', 'false' ); |
||
958 | |||
959 | $doc = array( |
||
960 | 'description' => $this->description, |
||
961 | 'method' => $this->method, |
||
962 | 'path_format' => $this->path, |
||
963 | 'path_labeled' => $path_labeled, |
||
964 | 'group' => $this->group, |
||
965 | 'request' => array( |
||
966 | 'path' => array(), |
||
967 | 'query' => array(), |
||
968 | 'body' => array(), |
||
969 | ), |
||
970 | 'response' => array( |
||
971 | 'body' => array(), |
||
972 | ) |
||
973 | ); |
||
974 | |||
975 | foreach ( array( 'path_labels' => 'path', 'query' => 'query', 'request_format' => 'body', 'response_format' => 'body' ) as $_property => $doc_item ) { |
||
976 | foreach ( (array) $this->$_property as $key => $description ) { |
||
977 | if ( is_array( $description ) ) { |
||
978 | $description_keys = array_keys( $description ); |
||
979 | if ( $boolean_arg === $description_keys || $naeloob_arg === $description_keys ) { |
||
980 | $type = '(bool)'; |
||
981 | } else { |
||
982 | $type = '(string)'; |
||
983 | } |
||
984 | |||
985 | if ( 'response_format' !== $_property ) { |
||
986 | // hack - don't show "(default)" in response format |
||
987 | reset( $description ); |
||
988 | $description_key = key( $description ); |
||
989 | $description[$description_key] = "(default) {$description[$description_key]}"; |
||
990 | } |
||
991 | } else { |
||
992 | $types = $this->parse_types( $description ); |
||
993 | $type = array(); |
||
994 | $default = ''; |
||
995 | |||
996 | if ( 'none' == $types ) { |
||
997 | $types = array(); |
||
998 | $types[]['type'] = 'none'; |
||
999 | } |
||
1000 | |||
1001 | foreach ( $types as $type_array ) { |
||
0 ignored issues
–
show
The expression
$types of type string|array is not guaranteed to be traversable. How about adding an additional type check?
There are different options of fixing this problem.
![]() |
|||
1002 | $type[] = $type_array['type']; |
||
1003 | if ( isset( $type_array['default'] ) ) { |
||
1004 | $default = $type_array['default']; |
||
1005 | if ( 'string' === $type_array['type'] ) { |
||
1006 | $default = "'$default'"; |
||
1007 | } |
||
1008 | } |
||
1009 | } |
||
1010 | $type = '(' . join( '|', $type ) . ')'; |
||
1011 | $noop = ''; // skip an index in list below |
||
0 ignored issues
–
show
$noop is not used, you could remove the assignment.
This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently. $myVar = 'Value';
$higher = false;
if (rand(1, 6) > 3) {
$higher = true;
} else {
$higher = false;
}
Both the ![]() |
|||
1012 | list( $noop, $description ) = explode( ')', $description, 2 ); |
||
0 ignored issues
–
show
The assignment to
$noop is unused. Consider omitting it like so list($first,,$third) .
This checks looks for assignemnts to variables using the Consider the following code example. <?php
function returnThreeValues() {
return array('a', 'b', 'c');
}
list($a, $b, $c) = returnThreeValues();
print $a . " - " . $c;
Only the variables Instead, the list call could have been. list($a,, $c) = returnThreeValues();
![]() |
|||
1013 | $description = trim( $description ); |
||
1014 | if ( $default ) { |
||
1015 | $description .= " Default: $default."; |
||
1016 | } |
||
1017 | } |
||
1018 | |||
1019 | $item = compact( 'type', 'description' ); |
||
1020 | |||
1021 | if ( 'response_format' === $_property ) { |
||
1022 | $doc['response'][$doc_item][$key] = $item; |
||
1023 | } else { |
||
1024 | $doc['request'][$doc_item][$key] = $item; |
||
1025 | } |
||
1026 | } |
||
1027 | } |
||
1028 | |||
1029 | return $doc; |
||
1030 | } |
||
1031 | |||
1032 | function user_can_view_post( $post_id ) { |
||
1033 | $post = get_post( $post_id ); |
||
1034 | if ( !$post || is_wp_error( $post ) ) { |
||
1035 | return false; |
||
1036 | } |
||
1037 | |||
1038 | View Code Duplication | if ( 'inherit' === $post->post_status ) { |
|
1039 | $parent_post = get_post( $post->post_parent ); |
||
1040 | $post_status_obj = get_post_status_object( $parent_post->post_status ); |
||
1041 | } else { |
||
1042 | $post_status_obj = get_post_status_object( $post->post_status ); |
||
1043 | } |
||
1044 | |||
1045 | if ( !$post_status_obj->public ) { |
||
1046 | if ( is_user_logged_in() ) { |
||
1047 | if ( $post_status_obj->protected ) { |
||
1048 | if ( !current_user_can( 'edit_post', $post->ID ) ) { |
||
1049 | return new WP_Error( 'unauthorized', 'User cannot view post', 403 ); |
||
1050 | } |
||
1051 | } elseif ( $post_status_obj->private ) { |
||
1052 | if ( !current_user_can( 'read_post', $post->ID ) ) { |
||
1053 | return new WP_Error( 'unauthorized', 'User cannot view post', 403 ); |
||
1054 | } |
||
1055 | } elseif ( in_array( $post->post_status, array( 'inherit', 'trash' ) ) ) { |
||
1056 | if ( !current_user_can( 'edit_post', $post->ID ) ) { |
||
1057 | return new WP_Error( 'unauthorized', 'User cannot view post', 403 ); |
||
1058 | } |
||
1059 | } elseif ( 'auto-draft' === $post->post_status ) { |
||
0 ignored issues
–
show
This
elseif statement is empty, and could be removed.
This check looks for the bodies of These ![]() |
|||
1060 | //allow auto-drafts |
||
1061 | } else { |
||
1062 | return new WP_Error( 'unauthorized', 'User cannot view post', 403 ); |
||
1063 | } |
||
1064 | } else { |
||
1065 | return new WP_Error( 'unauthorized', 'User cannot view post', 403 ); |
||
1066 | } |
||
1067 | } |
||
1068 | |||
1069 | View Code Duplication | if ( |
|
1070 | -1 == get_option( 'blog_public' ) && |
||
1071 | /** |
||
1072 | * Filter access to a specific post. |
||
1073 | * |
||
1074 | * @module json-api |
||
1075 | * |
||
1076 | * @since 3.4.0 |
||
1077 | * |
||
1078 | * @param bool current_user_can( 'read_post', $post->ID ) Can the current user access the post. |
||
1079 | * @param WP_Post $post Post data. |
||
1080 | */ |
||
1081 | ! apply_filters( |
||
1082 | 'wpcom_json_api_user_can_view_post', |
||
1083 | current_user_can( 'read_post', $post->ID ), |
||
1084 | $post |
||
1085 | ) |
||
1086 | ) { |
||
1087 | return new WP_Error( 'unauthorized', 'User cannot view post', array( 'status_code' => 403, 'error' => 'private_blog' ) ); |
||
1088 | } |
||
1089 | |||
1090 | View Code Duplication | if ( strlen( $post->post_password ) && !current_user_can( 'edit_post', $post->ID ) ) { |
|
1091 | return new WP_Error( 'unauthorized', 'User cannot view password protected post', array( 'status_code' => 403, 'error' => 'password_protected' ) ); |
||
1092 | } |
||
1093 | |||
1094 | return true; |
||
1095 | } |
||
1096 | |||
1097 | /** |
||
1098 | * Returns author object. |
||
1099 | * |
||
1100 | * @param object $author user ID, user row, WP_User object, comment row, post row |
||
1101 | * @param bool $show_email_and_ip output the author's email address and IP address? |
||
1102 | * |
||
1103 | * @return object |
||
1104 | */ |
||
1105 | function get_author( $author, $show_email_and_ip = false ) { |
||
1106 | $ip_address = isset( $author->comment_author_IP ) ? $author->comment_author_IP : ''; |
||
1107 | |||
1108 | if ( isset( $author->comment_author_email ) ) { |
||
1109 | $ID = 0; |
||
1110 | $login = ''; |
||
1111 | $email = $author->comment_author_email; |
||
1112 | $name = $author->comment_author; |
||
1113 | $first_name = ''; |
||
1114 | $last_name = ''; |
||
1115 | $URL = $author->comment_author_url; |
||
1116 | $avatar_URL = $this->api->get_avatar_url( $author ); |
||
1117 | $profile_URL = 'https://en.gravatar.com/' . md5( strtolower( trim( $email ) ) ); |
||
1118 | $nice = ''; |
||
1119 | $site_id = -1; |
||
1120 | |||
1121 | // Comment author URLs and Emails are sent through wp_kses() on save, which replaces "&" with "&" |
||
1122 | // "&" is the only email/URL character altered by wp_kses() |
||
1123 | foreach ( array( 'email', 'URL' ) as $field ) { |
||
1124 | $$field = str_replace( '&', '&', $$field ); |
||
1125 | } |
||
1126 | } else { |
||
1127 | if ( isset( $author->user_id ) && $author->user_id ) { |
||
1128 | $author = $author->user_id; |
||
1129 | } elseif ( isset( $author->user_email ) ) { |
||
1130 | $author = $author->ID; |
||
1131 | } elseif ( isset( $author->post_author ) ) { |
||
1132 | // then $author is a Post Object. |
||
1133 | if ( 0 == $author->post_author ) |
||
1134 | return null; |
||
1135 | /** |
||
1136 | * Filter whether the current site is a Jetpack site. |
||
1137 | * |
||
1138 | * @module json-api |
||
1139 | * |
||
1140 | * @since 3.3.0 |
||
1141 | * |
||
1142 | * @param bool false Is the current site a Jetpack site. Default to false. |
||
1143 | * @param int get_current_blog_id() Blog ID. |
||
1144 | */ |
||
1145 | $is_jetpack = true === apply_filters( 'is_jetpack_site', false, get_current_blog_id() ); |
||
1146 | $post_id = $author->ID; |
||
1147 | if ( $is_jetpack && ( defined( 'IS_WPCOM' ) && IS_WPCOM ) ) { |
||
1148 | $ID = get_post_meta( $post_id, '_jetpack_post_author_external_id', true ); |
||
1149 | $email = get_post_meta( $post_id, '_jetpack_author_email', true ); |
||
1150 | $login = ''; |
||
1151 | $name = get_post_meta( $post_id, '_jetpack_author', true ); |
||
1152 | $first_name = ''; |
||
1153 | $last_name = ''; |
||
1154 | $URL = ''; |
||
1155 | $nice = ''; |
||
1156 | } else { |
||
1157 | $author = $author->post_author; |
||
1158 | } |
||
1159 | } |
||
1160 | |||
1161 | if ( ! isset( $ID ) ) { |
||
1162 | $user = get_user_by( 'id', $author ); |
||
1163 | if ( ! $user || is_wp_error( $user ) ) { |
||
1164 | trigger_error( 'Unknown user', E_USER_WARNING ); |
||
1165 | |||
1166 | return null; |
||
1167 | } |
||
1168 | $ID = $user->ID; |
||
1169 | $email = $user->user_email; |
||
1170 | $login = $user->user_login; |
||
1171 | $name = $user->display_name; |
||
1172 | $first_name = $user->first_name; |
||
1173 | $last_name = $user->last_name; |
||
1174 | $URL = $user->user_url; |
||
1175 | $nice = $user->user_nicename; |
||
1176 | } |
||
1177 | if ( defined( 'IS_WPCOM' ) && IS_WPCOM && ! $is_jetpack ) { |
||
0 ignored issues
–
show
The variable
$is_jetpack does not seem to be defined for all execution paths leading up to this point.
If you define a variable conditionally, it can happen that it is not defined for all execution paths. Let’s take a look at an example: function myFunction($a) {
switch ($a) {
case 'foo':
$x = 1;
break;
case 'bar':
$x = 2;
break;
}
// $x is potentially undefined here.
echo $x;
}
In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined. Available Fixes
![]() |
|||
1178 | $active_blog = get_active_blog_for_user( $ID ); |
||
1179 | $site_id = $active_blog->blog_id; |
||
1180 | if ( $site_id > -1 ) { |
||
1181 | $site_visible = ( |
||
1182 | -1 != $active_blog->public || |
||
1183 | is_private_blog_user( $site_id, get_current_user_id() ) |
||
1184 | ); |
||
1185 | } |
||
1186 | $profile_URL = "https://en.gravatar.com/{$login}"; |
||
0 ignored issues
–
show
The variable
$login does not seem to be defined for all execution paths leading up to this point.
If you define a variable conditionally, it can happen that it is not defined for all execution paths. Let’s take a look at an example: function myFunction($a) {
switch ($a) {
case 'foo':
$x = 1;
break;
case 'bar':
$x = 2;
break;
}
// $x is potentially undefined here.
echo $x;
}
In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined. Available Fixes
![]() |
|||
1187 | } else { |
||
1188 | $profile_URL = 'https://en.gravatar.com/' . md5( strtolower( trim( $email ) ) ); |
||
0 ignored issues
–
show
The variable
$email does not seem to be defined for all execution paths leading up to this point.
If you define a variable conditionally, it can happen that it is not defined for all execution paths. Let’s take a look at an example: function myFunction($a) {
switch ($a) {
case 'foo':
$x = 1;
break;
case 'bar':
$x = 2;
break;
}
// $x is potentially undefined here.
echo $x;
}
In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined. Available Fixes
![]() |
|||
1189 | $site_id = -1; |
||
1190 | } |
||
1191 | |||
1192 | $avatar_URL = $this->api->get_avatar_url( $email ); |
||
1193 | } |
||
1194 | |||
1195 | if ( $show_email_and_ip ) { |
||
1196 | $email = (string) $email; |
||
1197 | $ip_address = (string) $ip_address; |
||
1198 | } else { |
||
1199 | $email = false; |
||
1200 | $ip_address = false; |
||
1201 | } |
||
1202 | |||
1203 | $author = array( |
||
1204 | 'ID' => (int) $ID, |
||
1205 | 'login' => (string) $login, |
||
1206 | 'email' => $email, // (string|bool) |
||
1207 | 'name' => (string) $name, |
||
0 ignored issues
–
show
The variable
$name does not seem to be defined for all execution paths leading up to this point.
If you define a variable conditionally, it can happen that it is not defined for all execution paths. Let’s take a look at an example: function myFunction($a) {
switch ($a) {
case 'foo':
$x = 1;
break;
case 'bar':
$x = 2;
break;
}
// $x is potentially undefined here.
echo $x;
}
In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined. Available Fixes
![]() |
|||
1208 | 'first_name' => (string) $first_name, |
||
0 ignored issues
–
show
The variable
$first_name does not seem to be defined for all execution paths leading up to this point.
If you define a variable conditionally, it can happen that it is not defined for all execution paths. Let’s take a look at an example: function myFunction($a) {
switch ($a) {
case 'foo':
$x = 1;
break;
case 'bar':
$x = 2;
break;
}
// $x is potentially undefined here.
echo $x;
}
In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined. Available Fixes
![]() |
|||
1209 | 'last_name' => (string) $last_name, |
||
0 ignored issues
–
show
The variable
$last_name does not seem to be defined for all execution paths leading up to this point.
If you define a variable conditionally, it can happen that it is not defined for all execution paths. Let’s take a look at an example: function myFunction($a) {
switch ($a) {
case 'foo':
$x = 1;
break;
case 'bar':
$x = 2;
break;
}
// $x is potentially undefined here.
echo $x;
}
In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined. Available Fixes
![]() |
|||
1210 | 'nice_name' => (string) $nice, |
||
0 ignored issues
–
show
The variable
$nice does not seem to be defined for all execution paths leading up to this point.
If you define a variable conditionally, it can happen that it is not defined for all execution paths. Let’s take a look at an example: function myFunction($a) {
switch ($a) {
case 'foo':
$x = 1;
break;
case 'bar':
$x = 2;
break;
}
// $x is potentially undefined here.
echo $x;
}
In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined. Available Fixes
![]() |
|||
1211 | 'URL' => (string) esc_url_raw( $URL ), |
||
0 ignored issues
–
show
The variable
$URL does not seem to be defined for all execution paths leading up to this point.
If you define a variable conditionally, it can happen that it is not defined for all execution paths. Let’s take a look at an example: function myFunction($a) {
switch ($a) {
case 'foo':
$x = 1;
break;
case 'bar':
$x = 2;
break;
}
// $x is potentially undefined here.
echo $x;
}
In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined. Available Fixes
![]() |
|||
1212 | 'avatar_URL' => (string) esc_url_raw( $avatar_URL ), |
||
1213 | 'profile_URL' => (string) esc_url_raw( $profile_URL ), |
||
1214 | 'ip_address' => $ip_address, // (string|bool) |
||
1215 | ); |
||
1216 | |||
1217 | if ( $site_id > -1 ) { |
||
1218 | $author['site_ID'] = (int) $site_id; |
||
1219 | $author['site_visible'] = $site_visible; |
||
0 ignored issues
–
show
The variable
$site_visible does not seem to be defined for all execution paths leading up to this point.
If you define a variable conditionally, it can happen that it is not defined for all execution paths. Let’s take a look at an example: function myFunction($a) {
switch ($a) {
case 'foo':
$x = 1;
break;
case 'bar':
$x = 2;
break;
}
// $x is potentially undefined here.
echo $x;
}
In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined. Available Fixes
![]() |
|||
1220 | } |
||
1221 | |||
1222 | return (object) $author; |
||
1223 | } |
||
1224 | |||
1225 | function get_media_item( $media_id ) { |
||
1226 | $media_item = get_post( $media_id ); |
||
1227 | |||
1228 | if ( !$media_item || is_wp_error( $media_item ) ) |
||
1229 | return new WP_Error( 'unknown_media', 'Unknown Media', 404 ); |
||
1230 | |||
1231 | $response = array( |
||
1232 | 'id' => strval( $media_item->ID ), |
||
1233 | 'date' => (string) $this->format_date( $media_item->post_date_gmt, $media_item->post_date ), |
||
1234 | 'parent' => $media_item->post_parent, |
||
1235 | 'link' => wp_get_attachment_url( $media_item->ID ), |
||
1236 | 'title' => $media_item->post_title, |
||
1237 | 'caption' => $media_item->post_excerpt, |
||
1238 | 'description' => $media_item->post_content, |
||
1239 | 'metadata' => wp_get_attachment_metadata( $media_item->ID ), |
||
1240 | ); |
||
1241 | |||
1242 | if ( defined( 'IS_WPCOM' ) && IS_WPCOM && is_array( $response['metadata'] ) && ! empty( $response['metadata']['file'] ) ) { |
||
1243 | remove_filter( '_wp_relative_upload_path', 'wpcom_wp_relative_upload_path', 10 ); |
||
1244 | $response['metadata']['file'] = _wp_relative_upload_path( $response['metadata']['file'] ); |
||
1245 | add_filter( '_wp_relative_upload_path', 'wpcom_wp_relative_upload_path', 10, 2 ); |
||
1246 | } |
||
1247 | |||
1248 | $response['meta'] = (object) array( |
||
1249 | 'links' => (object) array( |
||
1250 | 'self' => (string) $this->links->get_media_link( $this->api->get_blog_id_for_output(), $media_id ), |
||
1251 | 'help' => (string) $this->links->get_media_link( $this->api->get_blog_id_for_output(), $media_id, 'help' ), |
||
1252 | 'site' => (string) $this->links->get_site_link( $this->api->get_blog_id_for_output() ), |
||
1253 | ), |
||
1254 | ); |
||
1255 | |||
1256 | return (object) $response; |
||
1257 | } |
||
1258 | |||
1259 | function get_media_item_v1_1( $media_id, $media_item = null, $file = null ) { |
||
1260 | |||
1261 | if ( ! $media_item ) { |
||
1262 | $media_item = get_post( $media_id ); |
||
1263 | } |
||
1264 | |||
1265 | if ( ! $media_item || is_wp_error( $media_item ) ) { |
||
1266 | return new WP_Error( 'unknown_media', 'Unknown Media', 404 ); |
||
1267 | } |
||
1268 | |||
1269 | $attachment_file = get_attached_file( $media_item->ID ); |
||
1270 | |||
1271 | $file = basename( $attachment_file ? $attachment_file : $file ); |
||
1272 | $file_info = pathinfo( $file ); |
||
1273 | $ext = isset( $file_info['extension'] ) ? $file_info['extension'] : null; |
||
1274 | |||
1275 | $response = array( |
||
1276 | 'ID' => $media_item->ID, |
||
1277 | 'URL' => wp_get_attachment_url( $media_item->ID ), |
||
1278 | 'guid' => $media_item->guid, |
||
1279 | 'date' => (string) $this->format_date( $media_item->post_date_gmt, $media_item->post_date ), |
||
1280 | 'post_ID' => $media_item->post_parent, |
||
1281 | 'author_ID' => (int) $media_item->post_author, |
||
1282 | 'file' => $file, |
||
1283 | 'mime_type' => $media_item->post_mime_type, |
||
1284 | 'extension' => $ext, |
||
1285 | 'title' => $media_item->post_title, |
||
1286 | 'caption' => $media_item->post_excerpt, |
||
1287 | 'description' => $media_item->post_content, |
||
1288 | 'alt' => get_post_meta( $media_item->ID, '_wp_attachment_image_alt', true ), |
||
1289 | 'icon' => wp_mime_type_icon( $media_item->ID ), |
||
1290 | 'thumbnails' => array() |
||
1291 | ); |
||
1292 | |||
1293 | View Code Duplication | if ( in_array( $ext, array( 'jpg', 'jpeg', 'png', 'gif' ) ) ) { |
|
1294 | $metadata = wp_get_attachment_metadata( $media_item->ID ); |
||
1295 | if ( isset( $metadata['height'], $metadata['width'] ) ) { |
||
1296 | $response['height'] = $metadata['height']; |
||
1297 | $response['width'] = $metadata['width']; |
||
1298 | } |
||
1299 | |||
1300 | if ( isset( $metadata['sizes'] ) ) { |
||
1301 | /** |
||
1302 | * Filter the thumbnail sizes available for each attachment ID. |
||
1303 | * |
||
1304 | * @module json-api |
||
1305 | * |
||
1306 | * @since 3.9.0 |
||
1307 | * |
||
1308 | * @param array $metadata['sizes'] Array of thumbnail sizes available for a given attachment ID. |
||
1309 | * @param string $media_id Attachment ID. |
||
1310 | */ |
||
1311 | $sizes = apply_filters( 'rest_api_thumbnail_sizes', $metadata['sizes'], $media_item->ID ); |
||
1312 | if ( is_array( $sizes ) ) { |
||
1313 | foreach ( $sizes as $size => $size_details ) { |
||
1314 | $response['thumbnails'][ $size ] = dirname( $response['URL'] ) . '/' . $size_details['file']; |
||
1315 | } |
||
1316 | } |
||
1317 | } |
||
1318 | |||
1319 | if ( isset( $metadata['image_meta'] ) ) { |
||
1320 | $response['exif'] = $metadata['image_meta']; |
||
1321 | } |
||
1322 | } |
||
1323 | |||
1324 | if ( in_array( $ext, array( 'mp3', 'm4a', 'wav', 'ogg' ) ) ) { |
||
1325 | $metadata = wp_get_attachment_metadata( $media_item->ID ); |
||
1326 | $response['length'] = $metadata['length']; |
||
1327 | $response['exif'] = $metadata; |
||
1328 | } |
||
1329 | |||
1330 | $is_video = false; |
||
1331 | |||
1332 | if ( |
||
1333 | in_array( $ext, array( 'ogv', 'mp4', 'mov', 'wmv', 'avi', 'mpg', '3gp', '3g2', 'm4v' ) ) |
||
1334 | || |
||
1335 | $response['mime_type'] === 'video/videopress' |
||
1336 | ) { |
||
1337 | $is_video = true; |
||
1338 | } |
||
1339 | |||
1340 | |||
1341 | if ( $is_video ) { |
||
1342 | $metadata = wp_get_attachment_metadata( $media_item->ID ); |
||
1343 | |||
1344 | if ( isset( $metadata['height'], $metadata['width'] ) ) { |
||
1345 | $response['height'] = $metadata['height']; |
||
1346 | $response['width'] = $metadata['width']; |
||
1347 | } |
||
1348 | |||
1349 | if ( isset( $metadata['length'] ) ) { |
||
1350 | $response['length'] = $metadata['length']; |
||
1351 | } |
||
1352 | |||
1353 | // add VideoPress info |
||
1354 | if ( function_exists( 'video_get_info_by_blogpostid' ) ) { |
||
1355 | $info = video_get_info_by_blogpostid( $this->api->get_blog_id_for_output(), $media_item->ID ); |
||
1356 | |||
1357 | // If we failed to get VideoPress info, but it exists in the meta data (for some reason) |
||
1358 | // then let's use that. |
||
1359 | if ( false === $info && isset( $metadata['videopress'] ) ) { |
||
1360 | $info = (object) $metadata['videopress']; |
||
1361 | } |
||
1362 | |||
1363 | // Thumbnails |
||
1364 | if ( function_exists( 'video_format_done' ) && function_exists( 'video_image_url_by_guid' ) ) { |
||
1365 | $response['thumbnails'] = array( 'fmt_hd' => '', 'fmt_dvd' => '', 'fmt_std' => '' ); |
||
1366 | foreach ( $response['thumbnails'] as $size => $thumbnail_url ) { |
||
1367 | if ( video_format_done( $info, $size ) ) { |
||
1368 | $response['thumbnails'][ $size ] = video_image_url_by_guid( $info->guid, $size ); |
||
1369 | } else { |
||
1370 | unset( $response['thumbnails'][ $size ] ); |
||
1371 | } |
||
1372 | } |
||
1373 | } |
||
1374 | |||
1375 | // If we didn't get VideoPress information (for some reason) then let's |
||
1376 | // not try and include it in the response. |
||
1377 | if ( isset( $info->guid ) ) { |
||
1378 | $response['videopress_guid'] = $info->guid; |
||
1379 | $response['videopress_processing_done'] = true; |
||
1380 | if ( '0000-00-00 00:00:00' === $info->finish_date_gmt ) { |
||
1381 | $response['videopress_processing_done'] = false; |
||
1382 | } |
||
1383 | } |
||
1384 | } |
||
1385 | } |
||
1386 | |||
1387 | $response['thumbnails'] = (object) $response['thumbnails']; |
||
1388 | |||
1389 | $response['meta'] = (object) array( |
||
1390 | 'links' => (object) array( |
||
1391 | 'self' => (string) $this->links->get_media_link( $this->api->get_blog_id_for_output(), $media_item->ID ), |
||
1392 | 'help' => (string) $this->links->get_media_link( $this->api->get_blog_id_for_output(), $media_item->ID, 'help' ), |
||
1393 | 'site' => (string) $this->links->get_site_link( $this->api->get_blog_id_for_output() ), |
||
1394 | ), |
||
1395 | ); |
||
1396 | |||
1397 | // add VideoPress link to the meta |
||
1398 | if ( isset ( $response['videopress_guid'] ) ) { |
||
1399 | if ( function_exists( 'video_get_info_by_blogpostid' ) ) { |
||
1400 | $response['meta']->links->videopress = (string) $this->links->get_link( '/videos/%s', $response['videopress_guid'], '' ); |
||
1401 | } |
||
1402 | } |
||
1403 | |||
1404 | if ( $media_item->post_parent > 0 ) { |
||
1405 | $response['meta']->links->parent = (string) $this->links->get_post_link( $this->api->get_blog_id_for_output(), $media_item->post_parent ); |
||
1406 | } |
||
1407 | |||
1408 | return (object) $response; |
||
1409 | } |
||
1410 | |||
1411 | function get_taxonomy( $taxonomy_id, $taxonomy_type, $context ) { |
||
1412 | |||
1413 | $taxonomy = get_term_by( 'slug', $taxonomy_id, $taxonomy_type ); |
||
1414 | /// keep updating this function |
||
1415 | if ( !$taxonomy || is_wp_error( $taxonomy ) ) { |
||
1416 | return new WP_Error( 'unknown_taxonomy', 'Unknown taxonomy', 404 ); |
||
1417 | } |
||
1418 | |||
1419 | return $this->format_taxonomy( $taxonomy, $taxonomy_type, $context ); |
||
1420 | } |
||
1421 | |||
1422 | function format_taxonomy( $taxonomy, $taxonomy_type, $context ) { |
||
1423 | // Permissions |
||
1424 | switch ( $context ) { |
||
1425 | case 'edit' : |
||
1426 | $tax = get_taxonomy( $taxonomy_type ); |
||
1427 | if ( !current_user_can( $tax->cap->edit_terms ) ) |
||
1428 | return new WP_Error( 'unauthorized', 'User cannot edit taxonomy', 403 ); |
||
1429 | break; |
||
1430 | case 'display' : |
||
1431 | if ( -1 == get_option( 'blog_public' ) && ! current_user_can( 'read' ) ) { |
||
1432 | return new WP_Error( 'unauthorized', 'User cannot view taxonomy', 403 ); |
||
1433 | } |
||
1434 | break; |
||
1435 | default : |
||
0 ignored issues
–
show
There must be no space before the colon in a DEFAULT statement
As per the PSR-2 coding standard, there must not be a space in front of the colon in the default statement. switch ($expr) {
default : //wrong
doSomething();
break;
}
switch ($expr) {
default: //right
doSomething();
break;
}
To learn more about the PSR-2 coding standard, please refer to the PHP-Fig. ![]() |
|||
1436 | return new WP_Error( 'invalid_context', 'Invalid API CONTEXT', 400 ); |
||
1437 | } |
||
1438 | |||
1439 | $response = array(); |
||
1440 | $response['ID'] = (int) $taxonomy->term_id; |
||
1441 | $response['name'] = (string) $taxonomy->name; |
||
1442 | $response['slug'] = (string) $taxonomy->slug; |
||
1443 | $response['description'] = (string) $taxonomy->description; |
||
1444 | $response['post_count'] = (int) $taxonomy->count; |
||
1445 | $response['feed_url'] = get_term_feed_link( $taxonomy->term_id, $taxonomy_type ); |
||
1446 | |||
1447 | if ( is_taxonomy_hierarchical( $taxonomy_type ) ) { |
||
1448 | $response['parent'] = (int) $taxonomy->parent; |
||
1449 | } |
||
1450 | |||
1451 | $response['meta'] = (object) array( |
||
1452 | 'links' => (object) array( |
||
1453 | 'self' => (string) $this->links->get_taxonomy_link( $this->api->get_blog_id_for_output(), $taxonomy->slug, $taxonomy_type ), |
||
1454 | 'help' => (string) $this->links->get_taxonomy_link( $this->api->get_blog_id_for_output(), $taxonomy->slug, $taxonomy_type, 'help' ), |
||
1455 | 'site' => (string) $this->links->get_site_link( $this->api->get_blog_id_for_output() ), |
||
1456 | ), |
||
1457 | ); |
||
1458 | |||
1459 | return (object) $response; |
||
1460 | } |
||
1461 | |||
1462 | /** |
||
1463 | * Returns ISO 8601 formatted datetime: 2011-12-08T01:15:36-08:00 |
||
1464 | * |
||
1465 | * @param $date_gmt (string) GMT datetime string. |
||
1466 | * @param $date (string) Optional. Used to calculate the offset from GMT. |
||
1467 | * |
||
1468 | * @return string |
||
1469 | */ |
||
1470 | function format_date( $date_gmt, $date = null ) { |
||
1471 | return WPCOM_JSON_API_Date::format_date( $date_gmt, $date ); |
||
1472 | } |
||
1473 | |||
1474 | /** |
||
1475 | * Parses a date string and returns the local and GMT representations |
||
1476 | * of that date & time in 'YYYY-MM-DD HH:MM:SS' format without |
||
1477 | * timezones or offsets. If the parsed datetime was not localized to a |
||
1478 | * particular timezone or offset we will assume it was given in GMT |
||
1479 | * relative to now and will convert it to local time using either the |
||
1480 | * timezone set in the options table for the blog or the GMT offset. |
||
1481 | * |
||
1482 | * @param datetime string |
||
1483 | * |
||
1484 | * @return array( $local_time_string, $gmt_time_string ) |
||
0 ignored issues
–
show
The doc-type
array( could not be parsed: Expected "|" or "end of type", but got "(" at position 5. (view supported doc-types)
This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types. ![]() |
|||
1485 | */ |
||
1486 | function parse_date( $date_string ) { |
||
1487 | $date_string_info = date_parse( $date_string ); |
||
1488 | if ( is_array( $date_string_info ) && 0 === $date_string_info['error_count'] ) { |
||
1489 | // Check if it's already localized. Can't just check is_localtime because date_parse('oppossum') returns true; WTF, PHP. |
||
1490 | if ( isset( $date_string_info['zone'] ) && true === $date_string_info['is_localtime'] ) { |
||
1491 | $dt_local = clone $dt_utc = new DateTime( $date_string ); |
||
1492 | $dt_utc->setTimezone( new DateTimeZone( 'UTC' ) ); |
||
1493 | return array( |
||
1494 | (string) $dt_local->format( 'Y-m-d H:i:s' ), |
||
1495 | (string) $dt_utc->format( 'Y-m-d H:i:s' ), |
||
1496 | ); |
||
1497 | } |
||
1498 | |||
1499 | // It's parseable but no TZ info so assume UTC |
||
1500 | $dt_local = clone $dt_utc = new DateTime( $date_string, new DateTimeZone( 'UTC' ) ); |
||
1501 | } else { |
||
1502 | // Could not parse time, use now in UTC |
||
1503 | $dt_local = clone $dt_utc = new DateTime( 'now', new DateTimeZone( 'UTC' ) ); |
||
1504 | } |
||
1505 | |||
1506 | // First try to use timezone as it's daylight savings aware. |
||
1507 | $timezone_string = get_option( 'timezone_string' ); |
||
1508 | if ( $timezone_string ) { |
||
1509 | $tz = timezone_open( $timezone_string ); |
||
1510 | if ( $tz ) { |
||
1511 | $dt_local->setTimezone( $tz ); |
||
1512 | return array( |
||
1513 | (string) $dt_local->format( 'Y-m-d H:i:s' ), |
||
1514 | (string) $dt_utc->format( 'Y-m-d H:i:s' ), |
||
1515 | ); |
||
1516 | } |
||
1517 | } |
||
1518 | |||
1519 | // Fallback to GMT offset (in hours) |
||
1520 | // NOTE: TZ of $dt_local is still UTC, we simply modified the timestamp with an offset. |
||
1521 | $gmt_offset_seconds = intval( get_option( 'gmt_offset' ) * 3600 ); |
||
1522 | $dt_local->modify("+{$gmt_offset_seconds} seconds"); |
||
1523 | return array( |
||
1524 | (string) $dt_local->format( 'Y-m-d H:i:s' ), |
||
1525 | (string) $dt_utc->format( 'Y-m-d H:i:s' ), |
||
1526 | ); |
||
1527 | } |
||
1528 | |||
1529 | // Load the functions.php file for the current theme to get its post formats, CPTs, etc. |
||
1530 | function load_theme_functions() { |
||
1531 | // bail if we've done this already (can happen when calling /batch endpoint) |
||
1532 | if ( defined( 'REST_API_THEME_FUNCTIONS_LOADED' ) ) |
||
1533 | return; |
||
1534 | |||
1535 | // VIP context loading is handled elsewhere, so bail to prevent |
||
1536 | // duplicate loading. See `switch_to_blog_and_validate_user()` |
||
1537 | if ( function_exists( 'wpcom_is_vip' ) && wpcom_is_vip() ) { |
||
1538 | return; |
||
1539 | } |
||
1540 | |||
1541 | define( 'REST_API_THEME_FUNCTIONS_LOADED', true ); |
||
1542 | |||
1543 | // the theme info we care about is found either within functions.php or one of the jetpack files. |
||
1544 | $function_files = array( '/functions.php', '/inc/jetpack.compat.php', '/inc/jetpack.php', '/includes/jetpack.compat.php' ); |
||
1545 | |||
1546 | $copy_dirs = array( get_template_directory() ); |
||
1547 | |||
1548 | // Is this a child theme? Load the child theme's functions file. |
||
1549 | if ( get_stylesheet_directory() !== get_template_directory() && wpcom_is_child_theme() ) { |
||
1550 | foreach ( $function_files as $function_file ) { |
||
1551 | if ( file_exists( get_stylesheet_directory() . $function_file ) ) { |
||
1552 | require_once( get_stylesheet_directory() . $function_file ); |
||
1553 | } |
||
1554 | } |
||
1555 | $copy_dirs[] = get_stylesheet_directory(); |
||
1556 | } |
||
1557 | |||
1558 | foreach ( $function_files as $function_file ) { |
||
1559 | if ( file_exists( get_template_directory() . $function_file ) ) { |
||
1560 | require_once( get_template_directory() . $function_file ); |
||
1561 | } |
||
1562 | } |
||
1563 | |||
1564 | // add inc/wpcom.php and/or includes/wpcom.php |
||
1565 | wpcom_load_theme_compat_file(); |
||
1566 | |||
1567 | // Enable including additional directories or files in actions to be copied |
||
1568 | $copy_dirs = apply_filters( 'restapi_theme_action_copy_dirs', $copy_dirs ); |
||
1569 | |||
1570 | // since the stuff we care about (CPTS, post formats, are usually on setup or init hooks, we want to load those) |
||
1571 | $this->copy_hooks( 'after_setup_theme', 'restapi_theme_after_setup_theme', $copy_dirs ); |
||
1572 | |||
1573 | /** |
||
1574 | * Fires functions hooked onto `after_setup_theme` by the theme for the purpose of the REST API. |
||
1575 | * |
||
1576 | * The REST API does not load the theme when processing requests. |
||
1577 | * To enable theme-based functionality, the API will load the '/functions.php', |
||
1578 | * '/inc/jetpack.compat.php', '/inc/jetpack.php', '/includes/jetpack.compat.php files |
||
1579 | * of the theme (parent and child) and copy functions hooked onto 'after_setup_theme' within those files. |
||
1580 | * |
||
1581 | * @module json-api |
||
1582 | * |
||
1583 | * @since 3.2.0 |
||
1584 | */ |
||
1585 | do_action( 'restapi_theme_after_setup_theme' ); |
||
1586 | $this->copy_hooks( 'init', 'restapi_theme_init', $copy_dirs ); |
||
1587 | |||
1588 | /** |
||
1589 | * Fires functions hooked onto `init` by the theme for the purpose of the REST API. |
||
1590 | * |
||
1591 | * The REST API does not load the theme when processing requests. |
||
1592 | * To enable theme-based functionality, the API will load the '/functions.php', |
||
1593 | * '/inc/jetpack.compat.php', '/inc/jetpack.php', '/includes/jetpack.compat.php files |
||
1594 | * of the theme (parent and child) and copy functions hooked onto 'init' within those files. |
||
1595 | * |
||
1596 | * @module json-api |
||
1597 | * |
||
1598 | * @since 3.2.0 |
||
1599 | */ |
||
1600 | do_action( 'restapi_theme_init' ); |
||
1601 | } |
||
1602 | |||
1603 | function copy_hooks( $from_hook, $to_hook, $base_paths ) { |
||
1604 | global $wp_filter; |
||
1605 | foreach ( $wp_filter as $hook => $actions ) { |
||
1606 | |||
1607 | if ( $from_hook != $hook ) { |
||
1608 | continue; |
||
1609 | } |
||
1610 | if ( ! has_action( $hook ) ) { |
||
1611 | continue; |
||
1612 | } |
||
1613 | |||
1614 | foreach ( $actions as $priority => $callbacks ) { |
||
1615 | foreach( $callbacks as $callback_key => $callback_data ) { |
||
1616 | $callback = $callback_data['function']; |
||
1617 | |||
1618 | // use reflection api to determine filename where function is defined |
||
1619 | $reflection = $this->get_reflection( $callback ); |
||
1620 | |||
1621 | if ( false !== $reflection ) { |
||
1622 | $file_name = $reflection->getFileName(); |
||
1623 | foreach( $base_paths as $base_path ) { |
||
1624 | |||
1625 | // only copy hooks with functions which are part of the specified files |
||
1626 | if ( 0 === strpos( $file_name, $base_path ) ) { |
||
1627 | add_action( |
||
1628 | $to_hook, |
||
1629 | $callback_data['function'], |
||
1630 | $priority, |
||
1631 | $callback_data['accepted_args'] |
||
1632 | ); |
||
1633 | } |
||
1634 | } |
||
1635 | } |
||
1636 | } |
||
1637 | } |
||
1638 | } |
||
1639 | } |
||
1640 | |||
1641 | function get_reflection( $callback ) { |
||
1642 | if ( is_array( $callback ) ) { |
||
1643 | list( $class, $method ) = $callback; |
||
1644 | return new ReflectionMethod( $class, $method ); |
||
1645 | } |
||
1646 | |||
1647 | if ( is_string( $callback ) && strpos( $callback, "::" ) !== false ) { |
||
1648 | list( $class, $method ) = explode( "::", $callback ); |
||
1649 | return new ReflectionMethod( $class, $method ); |
||
1650 | } |
||
1651 | |||
1652 | if ( version_compare( PHP_VERSION, "5.3.0", ">=" ) && method_exists( $callback, "__invoke" ) ) { |
||
1653 | return new ReflectionMethod( $callback, "__invoke" ); |
||
1654 | } |
||
1655 | |||
1656 | if ( is_string( $callback ) && strpos( $callback, "::" ) == false && function_exists( $callback ) ) { |
||
0 ignored issues
–
show
|
|||
1657 | return new ReflectionFunction( $callback ); |
||
1658 | } |
||
1659 | |||
1660 | return false; |
||
1661 | } |
||
1662 | |||
1663 | /** |
||
1664 | * Check whether a user can view or edit a post type |
||
1665 | * @param string $post_type post type to check |
||
1666 | * @param string $context 'display' or 'edit' |
||
1667 | * @return bool |
||
1668 | */ |
||
1669 | View Code Duplication | function current_user_can_access_post_type( $post_type, $context='display' ) { |
|
1670 | $post_type_object = get_post_type_object( $post_type ); |
||
1671 | if ( ! $post_type_object ) { |
||
1672 | return false; |
||
1673 | } |
||
1674 | |||
1675 | switch( $context ) { |
||
1676 | case 'edit': |
||
1677 | return current_user_can( $post_type_object->cap->edit_posts ); |
||
1678 | case 'display': |
||
1679 | return $post_type_object->public || current_user_can( $post_type_object->cap->read_private_posts ); |
||
1680 | default: |
||
1681 | return false; |
||
1682 | } |
||
1683 | } |
||
1684 | |||
1685 | View Code Duplication | function is_post_type_allowed( $post_type ) { |
|
1686 | // if the post type is empty, that's fine, WordPress will default to post |
||
1687 | if ( empty( $post_type ) ) { |
||
1688 | return true; |
||
1689 | } |
||
1690 | |||
1691 | // allow special 'any' type |
||
1692 | if ( 'any' == $post_type ) { |
||
1693 | return true; |
||
1694 | } |
||
1695 | |||
1696 | // check for allowed types |
||
1697 | if ( in_array( $post_type, $this->_get_whitelisted_post_types() ) ) { |
||
1698 | return true; |
||
1699 | } |
||
1700 | |||
1701 | if ( $post_type_object = get_post_type_object( $post_type ) ) { |
||
1702 | if ( ! empty( $post_type_object->show_in_rest ) ) { |
||
1703 | return $post_type_object->show_in_rest; |
||
1704 | } |
||
1705 | if ( ! empty( $post_type_object->publicly_queryable ) ) { |
||
1706 | return $post_type_object->publicly_queryable; |
||
1707 | } |
||
1708 | } |
||
1709 | |||
1710 | return ! empty( $post_type_object->public ); |
||
1711 | } |
||
1712 | |||
1713 | /** |
||
1714 | * Gets the whitelisted post types that JP should allow access to. |
||
1715 | * |
||
1716 | * @return array Whitelisted post types. |
||
1717 | */ |
||
1718 | View Code Duplication | protected function _get_whitelisted_post_types() { |
|
1719 | $allowed_types = array( 'post', 'page', 'revision' ); |
||
1720 | |||
1721 | /** |
||
1722 | * Filter the post types Jetpack has access to, and can synchronize with WordPress.com. |
||
1723 | * |
||
1724 | * @module json-api |
||
1725 | * |
||
1726 | * @since 2.2.3 |
||
1727 | * |
||
1728 | * @param array $allowed_types Array of whitelisted post types. Default to `array( 'post', 'page', 'revision' )`. |
||
1729 | */ |
||
1730 | $allowed_types = apply_filters( 'rest_api_allowed_post_types', $allowed_types ); |
||
1731 | |||
1732 | return array_unique( $allowed_types ); |
||
1733 | } |
||
1734 | |||
1735 | function handle_media_creation_v1_1( $media_files, $media_urls, $media_attrs = array(), $force_parent_id = false ) { |
||
1736 | |||
1737 | add_filter( 'upload_mimes', array( $this, 'allow_video_uploads' ) ); |
||
1738 | |||
1739 | $media_ids = $errors = array(); |
||
1740 | $user_can_upload_files = current_user_can( 'upload_files' ) || $this->api->is_authorized_with_upload_token(); |
||
0 ignored issues
–
show
The method
is_authorized_with_upload_token() does not seem to exist on object<WPCOM_JSON_API> .
This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces. This is most likely a typographical error or the method has been renamed. ![]() |
|||
1741 | $media_attrs = array_values( $media_attrs ); // reset the keys |
||
1742 | $i = 0; |
||
1743 | |||
1744 | if ( ! empty( $media_files ) ) { |
||
1745 | $this->api->trap_wp_die( 'upload_error' ); |
||
1746 | foreach ( $media_files as $media_item ) { |
||
1747 | $_FILES['.api.media.item.'] = $media_item; |
||
1748 | View Code Duplication | if ( ! $user_can_upload_files ) { |
|
1749 | $media_id = new WP_Error( 'unauthorized', 'User cannot upload media.', 403 ); |
||
1750 | } else { |
||
1751 | if ( $force_parent_id ) { |
||
1752 | $parent_id = absint( $force_parent_id ); |
||
1753 | } elseif ( ! empty( $media_attrs[$i] ) && ! empty( $media_attrs[$i]['parent_id'] ) ) { |
||
1754 | $parent_id = absint( $media_attrs[$i]['parent_id'] ); |
||
1755 | } else { |
||
1756 | $parent_id = 0; |
||
1757 | } |
||
1758 | $media_id = media_handle_upload( '.api.media.item.', $parent_id ); |
||
1759 | } |
||
1760 | if ( is_wp_error( $media_id ) ) { |
||
1761 | $errors[$i]['file'] = $media_item['name']; |
||
1762 | $errors[$i]['error'] = $media_id->get_error_code(); |
||
1763 | $errors[$i]['message'] = $media_id->get_error_message(); |
||
1764 | } else { |
||
1765 | $media_ids[$i] = $media_id; |
||
1766 | } |
||
1767 | |||
1768 | $i++; |
||
1769 | } |
||
1770 | $this->api->trap_wp_die( null ); |
||
1771 | unset( $_FILES['.api.media.item.'] ); |
||
1772 | } |
||
1773 | |||
1774 | if ( ! empty( $media_urls ) ) { |
||
1775 | foreach ( $media_urls as $url ) { |
||
1776 | View Code Duplication | if ( ! $user_can_upload_files ) { |
|
1777 | $media_id = new WP_Error( 'unauthorized', 'User cannot upload media.', 403 ); |
||
1778 | } else { |
||
1779 | if ( $force_parent_id ) { |
||
1780 | $parent_id = absint( $force_parent_id ); |
||
1781 | } else if ( ! empty( $media_attrs[$i] ) && ! empty( $media_attrs[$i]['parent_id'] ) ) { |
||
1782 | $parent_id = absint( $media_attrs[$i]['parent_id'] ); |
||
1783 | } else { |
||
1784 | $parent_id = 0; |
||
1785 | } |
||
1786 | $media_id = $this->handle_media_sideload( $url, $parent_id ); |
||
1787 | } |
||
1788 | if ( is_wp_error( $media_id ) ) { |
||
1789 | $errors[$i] = array( |
||
1790 | 'file' => $url, |
||
1791 | 'error' => $media_id->get_error_code(), |
||
1792 | 'message' => $media_id->get_error_message(), |
||
1793 | ); |
||
1794 | } elseif ( ! empty( $media_id ) ) { |
||
1795 | $media_ids[$i] = $media_id; |
||
1796 | } |
||
1797 | |||
1798 | $i++; |
||
1799 | } |
||
1800 | } |
||
1801 | |||
1802 | if ( ! empty( $media_attrs ) ) { |
||
1803 | foreach ( $media_ids as $index => $media_id ) { |
||
1804 | if ( empty( $media_attrs[$index] ) ) |
||
1805 | continue; |
||
1806 | |||
1807 | $attrs = $media_attrs[$index]; |
||
1808 | $insert = array(); |
||
1809 | |||
1810 | // Attributes: Title, Caption, Description |
||
1811 | |||
1812 | if ( isset( $attrs['title'] ) ) { |
||
1813 | $insert['post_title'] = $attrs['title']; |
||
1814 | } |
||
1815 | |||
1816 | if ( isset( $attrs['caption'] ) ) { |
||
1817 | $insert['post_excerpt'] = $attrs['caption']; |
||
1818 | } |
||
1819 | |||
1820 | if ( isset( $attrs['description'] ) ) { |
||
1821 | $insert['post_content'] = $attrs['description']; |
||
1822 | } |
||
1823 | |||
1824 | if ( ! empty( $insert ) ) { |
||
1825 | $insert['ID'] = $media_id; |
||
1826 | wp_update_post( (object) $insert ); |
||
1827 | } |
||
1828 | |||
1829 | // Attributes: Alt |
||
1830 | |||
1831 | View Code Duplication | if ( isset( $attrs['alt'] ) ) { |
|
1832 | $alt = wp_strip_all_tags( $attrs['alt'], true ); |
||
1833 | update_post_meta( $media_id, '_wp_attachment_image_alt', $alt ); |
||
1834 | } |
||
1835 | |||
1836 | // Attributes: Artist, Album |
||
1837 | |||
1838 | $id3_meta = array(); |
||
1839 | |||
1840 | View Code Duplication | foreach ( array( 'artist', 'album' ) as $key ) { |
|
1841 | if ( isset( $attrs[ $key ] ) ) { |
||
1842 | $id3_meta[ $key ] = wp_strip_all_tags( $attrs[ $key ], true ); |
||
1843 | } |
||
1844 | } |
||
1845 | |||
1846 | if ( ! empty( $id3_meta ) ) { |
||
1847 | // Before updating metadata, ensure that the item is audio |
||
1848 | $item = $this->get_media_item_v1_1( $media_id ); |
||
1849 | if ( 0 === strpos( $item->mime_type, 'audio/' ) ) { |
||
1850 | wp_update_attachment_metadata( $media_id, $id3_meta ); |
||
1851 | } |
||
1852 | } |
||
1853 | } |
||
1854 | } |
||
1855 | |||
1856 | return array( 'media_ids' => $media_ids, 'errors' => $errors ); |
||
1857 | |||
1858 | } |
||
1859 | |||
1860 | function handle_media_sideload( $url, $parent_post_id = 0, $type = 'any' ) { |
||
1861 | if ( ! function_exists( 'download_url' ) || ! function_exists( 'media_handle_sideload' ) ) |
||
1862 | return false; |
||
1863 | |||
1864 | // if we didn't get a URL, let's bail |
||
1865 | $parsed = @parse_url( $url ); |
||
1866 | if ( empty( $parsed ) ) |
||
1867 | return false; |
||
1868 | |||
1869 | $tmp = download_url( $url ); |
||
1870 | if ( is_wp_error( $tmp ) ) { |
||
1871 | return $tmp; |
||
1872 | } |
||
1873 | |||
1874 | // First check to see if we get a mime-type match by file, otherwise, check to |
||
1875 | // see if WordPress supports this file as an image. If neither, then it is not supported. |
||
1876 | View Code Duplication | if ( ! $this->is_file_supported_for_sideloading( $tmp ) || 'image' === $type && ! file_is_displayable_image( $tmp ) ) { |
|
1877 | @unlink( $tmp ); |
||
0 ignored issues
–
show
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.
If you suppress an error, we recommend checking for the error condition explicitly: // For example instead of
@mkdir($dir);
// Better use
if (@mkdir($dir) === false) {
throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
![]() |
|||
1878 | return new WP_Error( 'invalid_input', 'Invalid file type.', 403 ); |
||
1879 | } |
||
1880 | |||
1881 | // emulate a $_FILES entry |
||
1882 | $file_array = array( |
||
1883 | 'name' => basename( parse_url( $url, PHP_URL_PATH ) ), |
||
1884 | 'tmp_name' => $tmp, |
||
1885 | ); |
||
1886 | |||
1887 | $id = media_handle_sideload( $file_array, $parent_post_id ); |
||
1888 | if ( file_exists( $tmp ) ) { |
||
1889 | @unlink( $tmp ); |
||
0 ignored issues
–
show
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.
If you suppress an error, we recommend checking for the error condition explicitly: // For example instead of
@mkdir($dir);
// Better use
if (@mkdir($dir) === false) {
throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
![]() |
|||
1890 | } |
||
1891 | |||
1892 | if ( is_wp_error( $id ) ) { |
||
1893 | return $id; |
||
1894 | } |
||
1895 | |||
1896 | if ( ! $id || ! is_int( $id ) ) { |
||
1897 | return false; |
||
1898 | } |
||
1899 | |||
1900 | return $id; |
||
1901 | } |
||
1902 | |||
1903 | /** |
||
1904 | * Checks that the mime type of the specified file is among those in a filterable list of mime types. |
||
1905 | * |
||
1906 | * @param string $file Path to file to get its mime type. |
||
1907 | * |
||
1908 | * @return bool |
||
1909 | */ |
||
1910 | View Code Duplication | protected function is_file_supported_for_sideloading( $file ) { |
|
1911 | if ( class_exists( 'finfo' ) ) { // php 5.3+ |
||
1912 | $finfo = new finfo( FILEINFO_MIME ); |
||
1913 | $mime = explode( '; ', $finfo->file( $file ) ); |
||
1914 | $type = $mime[0]; |
||
1915 | |||
1916 | } elseif ( function_exists( 'mime_content_type' ) ) { // PHP 5.2 |
||
1917 | $type = mime_content_type( $file ); |
||
1918 | |||
1919 | } else { |
||
1920 | return false; |
||
1921 | } |
||
1922 | |||
1923 | /** |
||
1924 | * Filter the list of supported mime types for media sideloading. |
||
1925 | * |
||
1926 | * @since 4.0.0 |
||
1927 | * |
||
1928 | * @module json-api |
||
1929 | * |
||
1930 | * @param array $supported_mime_types Array of the supported mime types for media sideloading. |
||
1931 | */ |
||
1932 | $supported_mime_types = apply_filters( 'jetpack_supported_media_sideload_types', array( |
||
1933 | 'image/png', |
||
1934 | 'image/jpeg', |
||
1935 | 'image/gif', |
||
1936 | 'image/bmp', |
||
1937 | 'video/quicktime', |
||
1938 | 'video/mp4', |
||
1939 | 'video/mpeg', |
||
1940 | 'video/ogg', |
||
1941 | 'video/3gpp', |
||
1942 | 'video/3gpp2', |
||
1943 | 'video/h261', |
||
1944 | 'video/h262', |
||
1945 | 'video/h264', |
||
1946 | 'video/x-msvideo', |
||
1947 | 'video/x-ms-wmv', |
||
1948 | 'video/x-ms-asf', |
||
1949 | ) ); |
||
1950 | |||
1951 | // If the type returned was not an array as expected, then we know we don't have a match. |
||
1952 | if ( ! is_array( $supported_mime_types ) ) { |
||
1953 | return false; |
||
1954 | } |
||
1955 | |||
1956 | return in_array( $type, $supported_mime_types ); |
||
1957 | } |
||
1958 | |||
1959 | function allow_video_uploads( $mimes ) { |
||
1960 | // if we are on Jetpack, bail - Videos are already allowed |
||
1961 | if ( ! defined( 'IS_WPCOM' ) || !IS_WPCOM ) { |
||
1962 | return $mimes; |
||
1963 | } |
||
1964 | |||
1965 | // extra check that this filter is only ever applied during REST API requests |
||
1966 | if ( ! defined( 'REST_API_REQUEST' ) || ! REST_API_REQUEST ) { |
||
1967 | return $mimes; |
||
1968 | } |
||
1969 | |||
1970 | // bail early if they already have the upgrade.. |
||
1971 | if ( get_option( 'video_upgrade' ) == '1' ) { |
||
1972 | return $mimes; |
||
1973 | } |
||
1974 | |||
1975 | // lets whitelist to only specific clients right now |
||
1976 | $clients_allowed_video_uploads = array(); |
||
1977 | /** |
||
1978 | * Filter the list of whitelisted video clients. |
||
1979 | * |
||
1980 | * @module json-api |
||
1981 | * |
||
1982 | * @since 3.2.0 |
||
1983 | * |
||
1984 | * @param array $clients_allowed_video_uploads Array of whitelisted Video clients. |
||
1985 | */ |
||
1986 | $clients_allowed_video_uploads = apply_filters( 'rest_api_clients_allowed_video_uploads', $clients_allowed_video_uploads ); |
||
1987 | if ( !in_array( $this->api->token_details['client_id'], $clients_allowed_video_uploads ) ) { |
||
1988 | return $mimes; |
||
1989 | } |
||
1990 | |||
1991 | $mime_list = wp_get_mime_types(); |
||
1992 | |||
1993 | $video_exts = explode( ' ', get_site_option( 'video_upload_filetypes', false, false ) ); |
||
1994 | /** |
||
1995 | * Filter the video filetypes allowed on the site. |
||
1996 | * |
||
1997 | * @module json-api |
||
1998 | * |
||
1999 | * @since 3.2.0 |
||
2000 | * |
||
2001 | * @param array $video_exts Array of video filetypes allowed on the site. |
||
2002 | */ |
||
2003 | $video_exts = apply_filters( 'video_upload_filetypes', $video_exts ); |
||
2004 | $video_mimes = array(); |
||
2005 | |||
2006 | if ( !empty( $video_exts ) ) { |
||
2007 | foreach ( $video_exts as $ext ) { |
||
2008 | foreach ( $mime_list as $ext_pattern => $mime ) { |
||
2009 | if ( $ext != '' && strpos( $ext_pattern, $ext ) !== false ) |
||
2010 | $video_mimes[$ext_pattern] = $mime; |
||
2011 | } |
||
2012 | } |
||
2013 | |||
2014 | $mimes = array_merge( $mimes, $video_mimes ); |
||
2015 | } |
||
2016 | |||
2017 | return $mimes; |
||
2018 | } |
||
2019 | |||
2020 | function is_current_site_multi_user() { |
||
2021 | $users = wp_cache_get( 'site_user_count', 'WPCOM_JSON_API_Endpoint' ); |
||
2022 | if ( false === $users ) { |
||
2023 | $user_query = new WP_User_Query( array( |
||
2024 | 'blog_id' => get_current_blog_id(), |
||
2025 | 'fields' => 'ID', |
||
2026 | ) ); |
||
2027 | $users = (int) $user_query->get_total(); |
||
2028 | wp_cache_set( 'site_user_count', $users, 'WPCOM_JSON_API_Endpoint', DAY_IN_SECONDS ); |
||
2029 | } |
||
2030 | return $users > 1; |
||
2031 | } |
||
2032 | |||
2033 | function allows_cross_origin_requests() { |
||
2034 | return 'GET' == $this->method || $this->allow_cross_origin_request; |
||
2035 | } |
||
2036 | |||
2037 | function allows_unauthorized_requests( $origin, $complete_access_origins ) { |
||
2038 | return 'GET' == $this->method || ( $this->allow_unauthorized_request && in_array( $origin, $complete_access_origins ) ); |
||
2039 | } |
||
2040 | |||
2041 | function get_platform() { |
||
2042 | return wpcom_get_sal_platform( $this->api->token_details ); |
||
2043 | } |
||
2044 | |||
2045 | /** |
||
2046 | * Allows the endpoint to perform logic to allow it to decide whether-or-not it should force a |
||
2047 | * response from the WPCOM API, or potentially go to the Jetpack blog. |
||
2048 | * |
||
2049 | * Override this method if you want to do something different. |
||
2050 | * |
||
2051 | * @param int $blog_id |
||
2052 | * @return bool |
||
2053 | */ |
||
2054 | function force_wpcom_request( $blog_id ) { |
||
2055 | return false; |
||
2056 | } |
||
2057 | |||
2058 | /** |
||
2059 | * Return endpoint response |
||
2060 | * |
||
2061 | * @param ... determined by ->$path |
||
2062 | * |
||
2063 | * @return |
||
2064 | * falsy: HTTP 500, no response body |
||
2065 | * WP_Error( $error_code, $error_message, $http_status_code ): HTTP $status_code, json_encode( array( 'error' => $error_code, 'message' => $error_message ) ) response body |
||
2066 | * $data: HTTP 200, json_encode( $data ) response body |
||
2067 | */ |
||
2068 | abstract function callback( $path = '' ); |
||
2069 | |||
2070 | |||
2071 | } |
||
2072 | |||
2073 | require_once( dirname( __FILE__ ) . '/json-endpoints.php' ); |
||
2074 |
This check looks for improperly formatted assignments.
Every assignment must have exactly one space before and one space after the equals operator.
To illustrate:
will have no issues, while
will report issues in lines 1 and 2.