Completed
Push — update/dialogue-focus-on-conte... ( 9f1745...fa862f )
by
unknown
80:03 queued 71:18
created

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

Upgrade to new PHP Analysis Engine

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

1
<?php
2
3
abstract class WPCOM_JSON_API_Post_v1_1_Endpoint extends WPCOM_JSON_API_Endpoint {
4
	public $post_object_format = array(
5
		// explicitly document and cast all output
6
		'ID'                => '(int) The post ID.',
7
		'site_ID'           => '(int) The site ID.',
8
		'author'            => '(object>author) The author of the post.',
9
		'date'              => "(ISO 8601 datetime) The post's creation time.",
10
		'modified'          => "(ISO 8601 datetime) The post's most recent update time.",
11
		'title'             => '(HTML) <code>context</code> dependent.',
12
		'URL'               => '(URL) The full permalink URL to the post.',
13
		'short_URL'         => '(URL) The wp.me short URL.',
14
		'content'           => '(HTML) <code>context</code> dependent.',
15
		'excerpt'           => '(HTML) <code>context</code> dependent.',
16
		'slug'              => '(string) The name (slug) for the post, used in URLs.',
17
		'guid'              => '(string) The GUID for the post.',
18
		'status'            => array(
19
			'publish'           => 'The post is published.',
20
			'draft'             => 'The post is saved as a draft.',
21
			'pending'           => 'The post is pending editorial approval.',
22
			'private'           => 'The post is published privately',
23
			'future'            => 'The post is scheduled for future publishing.',
24
			'trash'             => 'The post is in the trash.',
25
			'auto-draft'        => 'The post is a placeholder for a new post.',
26
		),
27
		'sticky'            => '(bool) Is the post sticky?',
28
		'password'          => '(string) The plaintext password protecting the post, or, more likely, the empty string if the post is not password protected.',
29
		'parent'            => "(object>post_reference|false) A reference to the post's parent, if it has one.",
30
		'type'              => "(string) The post's post_type. Post types besides post, page and revision need to be whitelisted using the <code>rest_api_allowed_post_types</code> filter.",
31
		'discussion'        => '(object) Hash of discussion options for the post',
32
		'likes_enabled'     => "(bool) Is the post open to likes?",
33
		'sharing_enabled'   => "(bool) Should sharing buttons show on this post?",
34
		'like_count'        => '(int) The number of likes for this post.',
35
		'i_like'            => '(bool) Does the current user like this post?',
36
		'is_reblogged'      => '(bool) Did the current user reblog this post?',
37
		'is_following'      => '(bool) Is the current user following this blog?',
38
		'global_ID'         => '(string) A unique WordPress.com-wide representation of a post.',
39
		'featured_image'    => '(URL) The URL to the featured image for this post if it has one.',
40
		'post_thumbnail'    => '(object>attachment) The attachment object for the featured image if it has one.',
41
		'format'            => array(), // see constructor
42
		'geo'               => '(object>geo|false)',
43
		'menu_order'        => '(int) (Pages Only) The order pages should appear in.',
44
		'page_template'     => '(string) (Pages Only) The page template this page is using.',
45
		'publicize_URLs'    => '(array:URL) Array of Twitter and Facebook URLs published by this post.',
46
		'terms'             => '(object) Hash of taxonomy names mapping to a hash of terms keyed by term name.',
47
		'tags'              => '(object:tag) Hash of tags (keyed by tag name) applied to the post.',
48
		'categories'        => '(object:category) Hash of categories (keyed by category name) applied to the post.',
49
		'attachments'       => '(object:attachment) Hash of post attachments (keyed by attachment ID). Returns the most recent 20 attachments. Use the `/sites/$site/media` endpoint to query the attachments beyond the default of 20 that are returned here.',
50
		'attachment_count'  => '(int) The total number of attachments for this post. Use the `/sites/$site/media` endpoint to query the attachments beyond the default of 20 that are returned here.',
51
		'metadata'          => '(array) Array of post metadata keys and values. All unprotected meta keys are available by default for read requests. Both unprotected and protected meta keys are available for authenticated requests with access. Protected meta keys can be made available with the <code>rest_api_allowed_public_metadata</code> filter.',
52
		'meta'              => '(object) API result meta data',
53
		'capabilities'      => '(object) List of post-specific permissions for the user; publish_post, edit_post, delete_post',
54
		'revisions'         => '(array) List of post revision IDs. Only available for posts retrieved with context=edit.',
55
		'other_URLs'        => '(object) List of URLs for this post. Permalink and slug suggestions.',
56
	);
57
58
	// public $response_format =& $this->post_object_format;
59
60 View Code Duplication
	function __construct( $args ) {
61
		if ( is_array( $this->post_object_format ) && isset( $this->post_object_format['format'] ) ) {
62
			$this->post_object_format['format'] = get_post_format_strings();
63
		}
64
		if ( !$this->response_format ) {
65
			$this->response_format =& $this->post_object_format;
66
		}
67
		parent::__construct( $args );
68
	}
69
70
	/**
71
	 * Get a post by a specified field and value
72
	 *
73
	 * @param string $field
74
	 * @param string $field_value
75
	 * @param string $context Post use context (e.g. 'display')
76
	 * @return array Post
77
	 **/
78
	function get_post_by( $field, $field_value, $context = 'display' ) {
79
80
		// validate input
81
		if ( ! in_array( $field, array( 'ID', 'name' ) ) ) {
82
			return new WP_Error( 'invalid_field', 'Invalid API FIELD', 400 );
0 ignored issues
show
The call to WP_Error::__construct() has too many arguments starting with 'invalid_field'.

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

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

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

Loading history...
83
		}
84
85
		if ( ! in_array( $context, array( 'display', 'edit' ) ) ) {
86
			return new WP_Error( 'invalid_context', 'Invalid API CONTEXT', 400 );
0 ignored issues
show
The call to WP_Error::__construct() has too many arguments starting with 'invalid_context'.

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

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

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

Loading history...
87
		}
88
89 View Code Duplication
		if ( 'display' === $context ) {
90
			$args = $this->query_args();
91
			if ( isset( $args['content_width'] ) && $args['content_width'] ) {
92
				$GLOBALS['content_width'] = (int) $args['content_width'];
93
			}
94
		}
95
96 View Code Duplication
		if ( strpos( $_SERVER['HTTP_USER_AGENT'], 'wp-windows8' ) ) {
97
			remove_shortcode( 'gallery', 'gallery_shortcode' );
98
			add_shortcode( 'gallery', array( &$this, 'win8_gallery_shortcode' ) );
99
		}
100
101
		// fetch SAL post
102
		$post = $this->get_sal_post_by( $field, $field_value, $context );
103
104
		if ( is_wp_error( $post ) ) {
105
			return $post;
106
		}
107
108
		$GLOBALS['post'] = $post;
109
110
		// TODO: not sure where this one should go
111
		if ( 'display' === $context ) {
112
			setup_postdata( $post );
113
		}
114
115
		$keys_to_render = array_keys( $this->post_object_format );
116
		if ( isset( $this->api->query[ 'fields' ] ) ) {
117
			$limit_to_fields = array_map( 'trim', explode( ',', $this->api->query['fields'] ) );
118
			$keys_to_render = array_intersect( $keys_to_render, $limit_to_fields );
119
		}
120
121
		// always include some keys because processors require it to validate access
122
		$keys_to_render = array_unique( array_merge( $keys_to_render, array( 'type', 'status', 'password' ) ) );
123
124
		$response = $this->render_response_keys( $post, $context, $keys_to_render );
125
126
		unset( $GLOBALS['post'] );
127
128
		return $response;
129
	}
130
131
	protected function get_sal_post_by( $field, $field_value, $context ) {
132
		global $blog_id;
133
134
		$site = $this->get_platform()->get_site( $blog_id );
135
136
		$post = ( $field === 'name' ) ?
137
			$site->get_post_by_name( $field_value, $context ) : 
138
			$site->get_post_by_id( $field_value, $context );
139
140
		return $post;
141
	}
142
143
	private function render_response_keys( $post, $context, $keys ) {
144
		foreach ( $keys as $key ) {
145
			switch ( $key ) {
146
			case 'ID' :
147
				// explicitly cast all output
148
				$response[$key] = (int) $post->ID;
149
				break;
150
			case 'site_ID' :
151
				$response[$key] = $post->site->get_id();
152
				break;
153
			case 'author' :
154
				$response[$key] = $post->get_author();
155
				break;
156
			case 'date' :
157
				$response[$key] = $post->get_date();
158
				break;
159
			case 'modified' :
160
				$response[$key] = $post->get_modified_date();
161
				break;
162
			case 'title' :
163
				$response[$key] = $post->get_title();
164
				break;
165
			case 'URL' :
166
				$response[$key] = $post->get_url();
167
				break;
168
			case 'short_URL' :
169
				$response[$key] = $post->get_shortlink();
170
				break;
171
			case 'content' :
172
				$response[$key] = $post->get_content();
173
				break;
174
			case 'excerpt' :
175
				$response[$key] = $post->get_excerpt();
176
				break;
177
			case 'status' :
178
				$response[$key] = $post->get_status();
179
				break;
180
			case 'sticky' :
181
				$response[$key] = $post->is_sticky();
182
				break;
183
			case 'slug' :
184
				$response[$key] = $post->get_slug();
185
				break;
186
			case 'guid' :
187
				$response[$key] = $post->get_guid();
188
				break;
189
			case 'password' :
190
				$response[$key] = $post->get_password();
191
				break;
192
			case 'parent' : // (object|false)
193
				$response[$key] = $post->get_parent();
194
				break;
195
			case 'type' :
196
				$response[$key] = $post->get_type();
197
				break;
198
			case 'discussion' :
199
				$response[$key] = $post->get_discussion();
200
				break;
201
			case 'likes_enabled' :
202
				$response[$key] = $post->is_likes_enabled();
203
				break;
204
			case 'sharing_enabled' :
205
				$response[$key] = $post->is_sharing_enabled();
206
				break;
207
			case 'like_count' :
208
				$response[$key] = $post->get_like_count();
209
				break;
210
			case 'i_like'     :
211
				$response[$key] = $post->is_liked();
212
				break;
213
			case 'is_reblogged':
214
				$response[$key] = $post->is_reblogged();
215
				break;
216
			case 'is_following':
217
				$response[$key] = $post->is_following();
218
				break;
219
			case 'global_ID':
220
				$response[$key] = $post->get_global_id();
221
				break;
222
			case 'featured_image' :
223
				$response[$key] = $post->get_featured_image();
224
				break;
225
			case 'post_thumbnail' :
226
				$response[$key] = $post->get_post_thumbnail();
227
				break;
228
			case 'format' :
229
				$response[$key] = $post->get_format();
230
				break;
231
			case 'geo' : // (object|false)
232
				$response[$key] = $post->get_geo();
233
				break;
234
			case 'menu_order':
235
				$response[$key] = $post->get_menu_order();
236
				break;
237
			case 'page_template':
238
				$response[$key] = $post->get_page_template();
239
				break;
240
			case 'publicize_URLs' :
241
				$response[$key] = $post->get_publicize_urls();
242
				break;
243
			case 'terms':
244
				$response[$key] = $post->get_terms();
245
				break;
246
			case 'tags' :
247
				$response[$key] = $post->get_tags();
248
				break;
249
			case 'categories':
250
				$response[$key] = $post->get_categories();
251
				break;
252
			case 'attachments':
253
				list( $attachments, $attachment_count ) = $post->get_attachments_and_count();
254
				$response[$key] = $attachments;
255
				$response['attachment_count'] = $attachment_count;
256
				break;
257
			case 'metadata' : // (array|false)
258
				$response[$key] = $post->get_metadata();
259
				break;
260
			case 'meta' :
261
				$response[$key] = $post->get_meta();
262
				break;
263
			case 'capabilities' :
264
				$response[$key] = $post->get_current_user_capabilities();
265
				break;
266
			case 'revisions' :
267
				$revisions = $post->get_revisions();
268
				if ( $revisions ) {
269
					$response[$key] = $revisions;
270
				}
271
				break;
272
			case 'other_URLs' :
273
				$response[$key] = $post->get_other_urls();
274
				break;
275
			}
276
		}
277
278
		return $response;
279
	}
280
281
	function filter_response( $response ) {
282
283
		// Do minimal processing if the caller didn't request it
284
		if ( ! isset( $_REQUEST['meta_fields'] ) ) {
285
			return $response;
286
		}
287
288
		// Retrieve an array of field paths, such as: [`autosave.modified`, `autosave.post_ID`]
289
		$fields = explode( ',', $_REQUEST['meta_fields'] );
290
291
		foreach ( $response['posts'] as $post ) {
292
293
			if ( ! isset( $post['meta'] ) || ! isset( $post['meta']->data ) || (! is_array( $post['meta']->data ) && ! is_object( $post['meta']->data ) ) ) {
294
				continue;
295
			}
296
			
297
			$newmeta = [];
298
			foreach ( $post['meta']->data as $field_key => $field_value ) {
299
300
				foreach ( $field_value as $subfield_key => $subfield_value ) {
301
					$key_path = $field_key . '.' . $subfield_key;
302
303
					if ( in_array( $key_path, $fields ) ) {
304
						$newmeta[ $field_key ][ $subfield_key ] = $subfield_value;
305
					}
306
				}
307
			}
308
309
			$post['meta']->data = $newmeta;
310
		}
311
312
		return $response;
313
	}
314
	
315
	// TODO: factor this out
316 View Code Duplication
	function get_blog_post( $blog_id, $post_id, $context = 'display' ) {
317
		$blog_id = $this->api->get_blog_id( $blog_id );
318
		if ( !$blog_id || is_wp_error( $blog_id ) ) {
319
			return $blog_id;
320
		}
321
		switch_to_blog( $blog_id );
322
		$post = $this->get_post_by( 'ID', $post_id, $context );
323
		restore_current_blog();
324
		return $post;
325
	}
326
327 View Code Duplication
	function win8_gallery_shortcode( $attr ) {
328
		global $post;
329
330
		static $instance = 0;
331
		$instance++;
332
333
		$output = '';
334
335
		// We're trusting author input, so let's at least make sure it looks like a valid orderby statement
336
		if ( isset( $attr['orderby'] ) ) {
337
			$attr['orderby'] = sanitize_sql_orderby( $attr['orderby'] );
338
			if ( !$attr['orderby'] )
339
				unset( $attr['orderby'] );
340
		}
341
342
		extract( shortcode_atts( array(
343
			'order'     => 'ASC',
344
			'orderby'   => 'menu_order ID',
345
			'id'        => $post->ID,
346
			'include'   => '',
347
			'exclude'   => '',
348
			'slideshow' => false
349
		), $attr, 'gallery' ) );
350
351
		// Custom image size and always use it
352
		add_image_size( 'win8app-column', 480 );
353
		$size = 'win8app-column';
354
355
		$id = (int) $id;
356
		if ( 'RAND' === $order )
357
			$orderby = 'none';
358
359
		if ( !empty( $include ) ) {
360
			$include      = preg_replace( '/[^0-9,]+/', '', $include );
361
			$_attachments = get_posts( array( 'include' => $include, 'post_status' => 'inherit', 'post_type' => 'attachment', 'post_mime_type' => 'image', 'order' => $order, 'orderby' => $orderby ) );
362
			$attachments  = array();
363
			foreach ( $_attachments as $key => $val ) {
364
				$attachments[$val->ID] = $_attachments[$key];
365
			}
366
		} elseif ( !empty( $exclude ) ) {
367
			$exclude     = preg_replace( '/[^0-9,]+/', '', $exclude );
368
			$attachments = get_children( array( 'post_parent' => $id, 'exclude' => $exclude, 'post_status' => 'inherit', 'post_type' => 'attachment', 'post_mime_type' => 'image', 'order' => $order, 'orderby' => $orderby ) );
369
		} else {
370
			$attachments = get_children( array( 'post_parent' => $id, 'post_status' => 'inherit', 'post_type' => 'attachment', 'post_mime_type' => 'image', 'order' => $order, 'orderby' => $orderby ) );
371
		}
372
373
		if ( ! empty( $attachments ) ) {
374
			foreach ( $attachments as $id => $attachment ) {
375
				$link = isset( $attr['link'] ) && 'file' === $attr['link'] ? wp_get_attachment_link( $id, $size, false, false ) : wp_get_attachment_link( $id, $size, true, false );
376
377
				if ( $captiontag && trim($attachment->post_excerpt) ) {
378
					$output .= "<div class='wp-caption aligncenter'>$link
379
						<p class='wp-caption-text'>" . wptexturize($attachment->post_excerpt) . "</p>
380
						</div>";
381
				} else {
382
					$output .= $link . ' ';
383
				}
384
			}
385
		}
386
	}
387
}
388