<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[2206] 2013/rmccue/trunk: Separate the post-related endpoints</title>
</head>
<body>
<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; }
#msg dl a { font-weight: bold}
#msg dl a:link { color:#fc3; }
#msg dl a:active { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://gsoc.trac.wordpress.org/changeset/2206">2206</a></dd>
<dt>Author</dt> <dd>rmccue</dd>
<dt>Date</dt> <dd>2013-08-08 02:45:32 +0000 (Thu, 08 Aug 2013)</dd>
</dl>
<h3>Log Message</h3>
<pre>Separate the post-related endpoints
Fixes <a href="http://gsoc.trac.wordpress.org/ticket/348">#348</a></pre>
<h3>Modified Paths</h3>
<ul>
<li><a href="#2013rmccuetrunklibclasswpjsonserverphp">2013/rmccue/trunk/lib/class-wp-json-server.php</a></li>
<li><a href="#2013rmccuetrunkpluginphp">2013/rmccue/trunk/plugin.php</a></li>
</ul>
<h3>Added Paths</h3>
<ul>
<li><a href="#2013rmccuetrunklibclasswpjsonpostsphp">2013/rmccue/trunk/lib/class-wp-json-posts.php</a></li>
</ul>
</div>
<div id="patch">
<h3>Diff</h3>
<a id="2013rmccuetrunklibclasswpjsonpostsphpfromrev22052013rmccuetrunklibclasswpjsonserverphp"></a>
<div class="copfile"><h4>Copied: 2013/rmccue/trunk/lib/class-wp-json-posts.php (from rev 2205, 2013/rmccue/trunk/lib/class-wp-json-server.php) (0 => 2206)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/rmccue/trunk/lib/class-wp-json-posts.php (rev 0)
+++ 2013/rmccue/trunk/lib/class-wp-json-posts.php 2013-08-08 02:45:32 UTC (rev 2206)
</span><span class="lines">@@ -0,0 +1,1040 @@
</span><ins>+<?php
+
+class WP_JSON_Posts {
+ /**
+ * Retrieve posts.
+ *
+ * @since 3.4.0
+ *
+ * The optional $filter parameter modifies the query used to retrieve posts.
+ * Accepted keys are 'post_type', 'post_status', 'number', 'offset',
+ * 'orderby', and 'order'.
+ *
+ * The optional $fields parameter specifies what fields will be included
+ * in the response array.
+ *
+ * @uses wp_get_recent_posts()
+ * @see WP_JSON_Posts::getPost() for more on $fields
+ * @see get_posts() for more on $filter values
+ *
+ * @param array $filter optional
+ * @param array $fields optional
+ * @return array contains a collection of Post entities.
+ */
+ public function getPosts( $filter = array(), $fields = array(), $type = 'post', $page = 1 ) {
+ global $wp_json_server;
+ if ( empty($fields) || in_array( 'default', $fields ) )
+ $fields = array_merge( $fields, apply_filters( 'json_default_post_fields', array( 'post', 'meta', 'terms' ), 'getPosts' ) );
+
+ $query = array();
+
+ $post_type = get_post_type_object( $type );
+ if ( ! ( (bool) $post_type ) )
+ return new WP_Error( 'json_invalid_post_type', __( 'The post type specified is not valid' ), array( 'status' => 403 ) );
+
+ $query['post_type'] = $post_type->name;
+
+ global $wp;
+ // Allow the same as normal WP
+ $valid_vars = apply_filters('query_vars', $wp->public_query_vars);
+
+ // If the user has the correct permissions, also allow use of internal
+ // query parameters, which are only undesirable on the frontend
+ //
+ // To disable anyway, use `add_filter('json_private_query_vars', '__return_empty_array');`
+
+ if ( current_user_can( $post_type->cap->edit_posts ) ) {
+ $private = apply_filters('json_private_query_vars', $wp->private_query_vars);
+ $valid_vars = array_merge($valid_vars, $private);
+ }
+
+ // Define our own in addition to WP's normal vars
+ $json_valid = array('posts_per_page');
+ $valid_vars = array_merge($valid_vars, $json_valid);
+
+ // Filter and flip for querying
+ $valid_vars = apply_filters('json_query_vars', $valid_vars);
+ $valid_vars = array_flip($valid_vars);
+
+ // Exclude the post_type query var to avoid dodging the permission
+ // check above
+ unset($valid_vars['post_type']);
+
+ foreach ($valid_vars as $var => $index) {
+ if ( isset( $filter[ $var ] ) ) {
+ $query[ $var ] = apply_filters( 'json_query_var-' . $var, $filter[ $var ] );
+ }
+ }
+
+ // Special parameter handling
+ $query['paged'] = absint( $page );
+
+ $post_query = new WP_Query();
+ $posts_list = $post_query->query( $query );
+ $wp_json_server->query_navigation_headers( $post_query );
+
+ if ( ! $posts_list )
+ return array();
+
+ // holds all the posts data
+ $struct = array();
+
+ header( 'Last-Modified: ' . mysql2date( 'D, d M Y H:i:s', get_lastpostmodified( 'GMT' ), 0 ).' GMT' );
+
+ foreach ( $posts_list as $post ) {
+ $post = get_object_vars( $post );
+ $post_type = get_post_type_object( $post['post_type'] );
+ if ( 'publish' !== $post['post_status'] && ! current_user_can( $post_type->cap->read_post, $post['ID'] ) )
+ continue;
+
+ $wp_json_server->link_header( 'item', json_url( '/posts/' . $post['ID'] ), array( 'title' => $post['post_title'] ) );
+ $struct[] = $this->prepare_post( $post, $fields );
+ }
+
+ return $struct;
+ }
+
+ /**
+ * Create a new post for any registered post type.
+ *
+ * @since 3.4.0
+ * @internal 'data' is used here rather than 'content', as get_default_post_to_edit uses $_REQUEST['content']
+ *
+ * @param array $content Content data. Can contain:
+ * - post_type (default: 'post')
+ * - post_status (default: 'draft')
+ * - post_title
+ * - post_author
+ * - post_excerpt
+ * - post_content
+ * - post_date_gmt | post_date
+ * - post_format
+ * - post_password
+ * - comment_status - can be 'open' | 'closed'
+ * - ping_status - can be 'open' | 'closed'
+ * - sticky
+ * - post_thumbnail - ID of a media item to use as the post thumbnail/featured image
+ * - custom_fields - array, with each element containing 'key' and 'value'
+ * - terms - array, with taxonomy names as keys and arrays of term IDs as values
+ * - terms_names - array, with taxonomy names as keys and arrays of term names as values
+ * - enclosure
+ * - any other fields supported by wp_insert_post()
+ * @return array Post data (see {@see WP_JSON_Posts::getPost})
+ */
+ function newPost( $data ) {
+ unset( $data['ID'] );
+
+ $result = $this->insert_post( $data );
+ if ( is_string( $result ) || is_int( $result ) ) {
+ status_header( 201 );
+ header( 'Location: ' . json_url( '/posts/' . $result ) );
+
+ return $this->getPost( $result );
+ }
+ elseif ( $result instanceof IXR_Error ) {
+ return new WP_Error( 'json_insert_error', $result->message, array( 'status' => $result->code ) );
+ }
+ else {
+ return new WP_Error( 'json_insert_error', __( 'An unknown error occurred while creating the post' ), array( 'status' => 500 ) );
+ }
+ }
+
+ /**
+ * Retrieve a post.
+ *
+ * @uses get_post()
+ * @param int $id Post ID
+ * @param array $fields Post fields to return (optional)
+ * @return array Post entity
+ */
+ public function getPost( $id, $fields = array() ) {
+ global $wp_json_server;
+ $id = (int) $id;
+
+ if ( empty( $id ) )
+ return new WP_Error( 'json_post_invalid_id', __( 'Invalid post ID.' ), array( 'status' => 404 ) );
+
+ $post = get_post( $id, ARRAY_A );
+
+ if ( empty( $fields ) || in_array( 'default', $fields ) )
+ $fields = array_merge( $fields, apply_filters( 'json_default_post_fields', array( 'post', 'post-extended', 'meta', 'terms', 'custom_fields' ), 'getPost' ) );
+
+ if ( empty( $post['ID'] ) )
+ return new WP_Error( 'json_post_invalid_id', __( 'Invalid post ID.' ), array( 'status' => 404 ) );
+
+ $post_type = get_post_type_object( $post['post_type'] );
+ if ( 'publish' !== $post['post_status'] && ! current_user_can( $post_type->cap->read_post, $id ) )
+ return new WP_Error( 'json_user_cannot_read', __( 'Sorry, you cannot read this post.' ), array( 'status' => 401 ) );
+
+ // Link headers (see RFC 5988)
+
+ header( 'Last-Modified: ' . mysql2date( 'D, d M Y H:i:s', $post['post_modified_gmt'] ) . 'GMT' );
+
+ $post = $this->prepare_post( $post, $fields );
+ if ( is_wp_error( $post ) )
+ return $post;
+
+ foreach ( $post['meta']['links'] as $rel => $url ) {
+ $wp_json_server->link_header( $rel, $url );
+ }
+ $wp_json_server->link_header( 'alternate', get_permalink( $id ), array( 'type' => 'text/html' ) );
+
+ return $post;
+ }
+
+ /**
+ * Edit a post for any registered post type.
+ *
+ * The $data parameter only needs to contain fields that should be changed.
+ * All other fields will retain their existing values.
+ *
+ * @since 3.4.0
+ * @internal 'data' is used here rather than 'content', as get_default_post_to_edit uses $_REQUEST['content']
+ *
+ * @param int $id Post ID to edit
+ * @param array $data Data construct, see {@see WP_JSON_Posts::newPost}
+ * @param array $_headers Header data
+ * @return true on success
+ */
+ function editPost( $id, $data, $_headers = array() ) {
+ $post = get_post( $id, ARRAY_A );
+
+ if ( empty( $post['ID'] ) )
+ return new WP_Error( 'json_post_invalid_id', __( 'Invalid post ID.' ), array( 'status' => 404 ) );
+
+ if ( isset( $_headers['IF_UNMODIFIED_SINCE'] ) ) {
+ // As mandated by RFC2616, we have to check all of RFC1123, RFC1036
+ // and C's asctime() format (and ignore invalid headers)
+ $formats = array( DateTime::RFC1123, DateTime::RFC1036, 'D M j H:i:s Y' );
+ foreach ( $formats as $format ) {
+ $check = DateTime::createFromFormat( DateTime::RFC1123, $_headers['IF_UNMODIFIED_SINCE'] );
+
+ if ( $check !== false )
+ break;
+ }
+
+ // If the post has been modified since the date provided, return an error.
+ if ( $check && mysql2date( 'U', $post['post_modified_gmt'] ) > $check->format('U') ) {
+ return new WP_Error( 'json_old_revision', __( 'There is a revision of this post that is more recent.' ), array( 'status' => 412 ) );
+ }
+ }
+
+ $data['ID'] = $id;
+
+ $retval = $this->insert_post( $data );
+ if ( is_wp_error( $retval ) ) {
+ return $retval;
+ }
+
+ return $this->getPost( $id );
+ }
+
+ /**
+ * Delete a post for any registered post type
+ *
+ * @uses wp_delete_post()
+ * @param int $id
+ * @return true on success
+ */
+ public function deletePost( $id, $force = false ) {
+ $id = (int) $id;
+ $post = get_post( $id, ARRAY_A );
+
+ if ( empty( $post['ID'] ) )
+ return new WP_Error( 'json_post_invalid_id', __( 'Invalid post ID.' ), array( 'status' => 404 ) );
+
+ $post_type = get_post_type_object( $post['post_type'] );
+ if ( ! current_user_can( $post_type->cap->delete_post, $id ) )
+ return new WP_Error( 'json_user_cannot_delete_post', __( 'Sorry, you are not allowed to delete this post.' ), array( 'status' => 401 ) );
+
+ $result = wp_delete_post( $id, $force );
+
+ if ( ! $result )
+ return new WP_Error( 'json_cannot_delete', __( 'The post cannot be deleted.' ), array( 'status' => 500 ) );
+
+ if ( $force ) {
+ return array( 'message' => __( 'Permanently deleted post' ) );
+ }
+ else {
+ // TODO: return a HTTP 202 here instead
+ return array( 'message' => __( 'Deleted post' ) );
+ }
+ }
+
+ /**
+ * Retrieve comments
+ *
+ * @param int $id Post ID to retrieve comments for
+ * @return array List of Comment entities
+ */
+ public function getComments( $id ) {
+ //$args = array('status' => $status, 'post_id' => $id, 'offset' => $offset, 'number' => $number )l
+ $comments = get_comments( array('post_id' => $id) );
+
+ $struct = array();
+ foreach ( $comments as $comment ) {
+ $struct[] = $this->prepare_comment( $comment, array( 'comment', 'meta' ), 'collection' );
+ }
+ return $struct;
+ }
+
+ /**
+ * Retrieve a single comment
+ *
+ * @param int $comment Comment ID
+ * @return array Comment entity
+ */
+ public function getComment( $comment ) {
+ $comment = get_comment( $comment );
+ $data = $this->prepare_comment( $comment );
+ return $data;
+ }
+
+ /**
+ * Get all public post types
+ *
+ * @uses self::getPostType()
+ * @return array List of post type data
+ */
+ public function getPostTypes() {
+ $data = get_post_types( array(), 'objects' );
+
+ $types = array();
+ foreach ($data as $name => $type) {
+ $type = $this->getPostType( $type, true );
+ if ( is_wp_error( $type ) )
+ continue;
+
+ $types[ $name ] = $type;
+ }
+
+ return $types;
+ }
+
+ /**
+ * Get a post type
+ *
+ * @param string|object $type Type name, or type object (internal use)
+ * @param boolean $_in_collection Is this in a collection? (internal use)
+ * @return array Post type data
+ */
+ public function getPostType( $type, $_in_collection = false ) {
+ if ( ! is_object( $type ) )
+ $type = get_post_type_object($type);
+
+ if ( $type->public === false )
+ return new WP_Error( 'json_cannot_read_type', __( 'Cannot view post type' ), array( 'status' => 403 ) );
+
+ $data = array(
+ 'name' => $type->label,
+ 'slug' => $type->name,
+ 'description' => $type->description,
+ 'labels' => $type->labels,
+ 'queryable' => $type->publicly_queryable,
+ 'searchable' => ! $type->exclude_from_search,
+ 'hierarchical' => $type->hierarchical,
+ 'meta' => array(),
+ );
+
+ if ( $_in_collection )
+ $data['meta']['self'] = json_url( '/posts/types/' . $type->name );
+ else
+ $data['meta']['collection'] = json_url( '/posts/types' );
+
+ if ( $type->publicly_queryable ) {
+ if ($type->name === 'post')
+ $data['meta']['archives'] = json_url( '/posts' );
+ else
+ $data['meta']['archives'] = json_url( add_query_arg( 'type', $type->name, '/posts' ) );
+ }
+
+ return $data;
+ }
+
+ /**
+ * Get the registered post statuses
+ *
+ * @return array List of post status data
+ */
+ public function getPostStatuses() {
+ $statuses = get_post_stati(array(), 'objects');
+
+ $data = array();
+ foreach ($statuses as $status) {
+ if ( $status->internal === true || ! $status->show_in_admin_status_list )
+ continue;
+
+ $data[ $status->name ] = array(
+ 'name' => $status->label,
+ 'slug' => $status->name,
+ 'public' => $status->public,
+ 'protected' => $status->protected,
+ 'private' => $status->private,
+ 'queryable' => $status->publicly_queryable,
+ 'show_in_list' => $status->show_in_admin_all_list,
+ 'meta' => array(),
+ );
+ if ( $type->publicly_queryable ) {
+ if ($type->name === 'publish')
+ $data['meta']['archives'] = json_url( '/posts' );
+ else
+ $data['meta']['archives'] = json_url( add_query_arg( 'type', $type->name, '/posts' ) );
+ }
+ }
+
+ return $data;
+ }
+
+ /**
+ * Prepares post data for return in an XML-RPC object.
+ *
+ * @access protected
+ *
+ * @param array $post The unprepared post data
+ * @param array $fields The subset of post type fields to return
+ * @return array The prepared post data
+ */
+ protected function prepare_post( $post, $fields, $context = 'single' ) {
+ global $wp_json_server;
+
+ // holds the data for this post. built up based on $fields
+ $_post = array(
+ 'ID' => (int) $post['ID'],
+ );
+
+ $post_type = get_post_type_object( $post['post_type'] );
+ if ( 'publish' !== $post['post_status'] && ! current_user_can( $post_type->cap->read_post, $post['ID'] ) )
+ return new WP_Error( 'json_user_cannot_read', __( 'Sorry, you cannot read this post.' ), array( 'status' => 401 ) );
+
+ // prepare common post fields
+ $post_fields = array(
+ 'title' => get_the_title( $post['ID'] ), // $post['post_title'],
+ 'status' => $post['post_status'],
+ 'type' => $post['post_type'],
+ 'author' => (int) $post['post_author'],
+ 'content' => apply_filters( 'the_content', $post['post_content'] ),
+ 'parent' => (int) $post['post_parent'],
+ #'post_mime_type' => $post['post_mime_type'],
+ 'link' => get_permalink( $post['ID'] ),
+ );
+ $post_fields_extended = array(
+ 'slug' => $post['post_name'],
+ 'guid' => apply_filters( 'get_the_guid', $post['guid'] ),
+ 'excerpt' => $this->prepare_excerpt( $post['post_excerpt'] ),
+ 'menu_order' => (int) $post['menu_order'],
+ 'comment_status' => $post['comment_status'],
+ 'ping_status' => $post['ping_status'],
+ 'sticky' => ( $post['post_type'] === 'post' && is_sticky( $post['ID'] ) ),
+ );
+ $post_fields_raw = array(
+ 'title_raw' => $post['post_title'],
+ 'content_raw' => $post['post_content'],
+ 'guid_raw' => $post['guid'],
+ );
+
+ // Dates
+ $timezone = $wp_json_server->get_timezone();
+
+ $date = DateTime::createFromFormat( 'Y-m-d H:i:s', $post['post_date'], $timezone );
+ $post_fields['date'] = $date->format( 'c' );
+ $post_fields_extended['date_tz'] = $date->format( 'e' );
+ $post_fields_extended['date_gmt'] = date( 'c', strtotime( $post['post_date_gmt'] ) );
+
+ $modified = DateTime::createFromFormat( 'Y-m-d H:i:s', $post['post_modified'], $timezone );
+ $post_fields['modified'] = $modified->format( 'c' );
+ $post_fields_extended['modified_tz'] = $modified->format( 'e' );
+ $post_fields_extended['modified_gmt'] = date( 'c', strtotime( $post['post_modified_gmt'] ) );
+
+ // Authorized fields
+ // TODO: Send `Vary: Authorization` to clarify that the data can be
+ // changed by the user's auth status
+ if ( current_user_can( $post_type->cap->edit_post, $post['ID'] ) ) {
+ $post_fields_extended['password'] = $post['post_password'];
+ }
+
+ // Thumbnail
+ /*$post_fields_extended['post_thumbnail'] = array();
+ $thumbnail_id = get_post_thumbnail_id( $post['ID'] );
+ if ( $thumbnail_id ) {
+ $thumbnail_size = current_theme_supports( 'post-thumbnail' ) ? 'post-thumbnail' : 'thumbnail';
+ $post_fields_extended['post_thumbnail'] = $this->_prepare_media_item( get_post( $thumbnail_id ), $thumbnail_size );
+ }*/
+
+ // Consider future posts as published
+ if ( $post_fields['status'] === 'future' )
+ $post_fields['status'] = 'publish';
+
+ // Fill in blank post format
+ $post_fields['format'] = get_post_format( $post['ID'] );
+ if ( empty( $post_fields['format'] ) )
+ $post_fields['format'] = 'standard';
+
+ $post_fields['author'] = $this->prepare_author( $post['post_author'] );
+
+ if ( ( 'single' === $context || 'single-parent' === $context ) && 0 !== $post['post_parent'] ) {
+ // Avoid nesting too deeply
+ // This gives post + post-extended + meta for the main post,
+ // post + meta for the parent and just meta for the grandparent
+ $parent_fields = array( 'meta' );
+ if ( $context === 'single' )
+ $parent_fields[] = 'post';
+ $parent = get_post( $post['post_parent'], ARRAY_A );
+ $post_fields['parent'] = $this->prepare_post( $parent, $parent_fields, 'single-parent' );
+ }
+
+ // Merge requested $post_fields fields into $_post
+ if ( in_array( 'post', $fields ) ) {
+ $_post = array_merge( $_post, $post_fields );
+ } else {
+ $requested_fields = array_intersect_key( $post_fields, array_flip( $fields ) );
+ $_post = array_merge( $_post, $requested_fields );
+ }
+
+ if ( in_array( 'post-extended', $fields ) )
+ $_post = array_merge( $_post, $post_fields_extended );
+
+ if ( in_array( 'post-raw', $fields ) && current_user_can( $post_type->cap->edit_post, $post['ID'] ) )
+ $_post = array_merge( $_post, $post_fields_raw );
+ elseif ( in_array( 'post-raw', $fields ) )
+ return new WP_Error( 'json_cannot_edit', __( 'Sorry, you cannot edit this post' ), array( 'status' => 403 ) );
+
+ // Taxonomies
+ $all_taxonomy_fields = in_array( 'taxonomies', $fields );
+
+ if ( $all_taxonomy_fields || in_array( 'terms', $fields ) ) {
+ $post_type_taxonomies = get_object_taxonomies( $post['post_type'] );
+ $terms = wp_get_object_terms( $post['ID'], $post_type_taxonomies );
+ $_post['terms'] = array();
+ foreach ( $terms as $term ) {
+ $_post['terms'][ $term->taxonomy ] = $this->prepare_term( $term );
+ }
+ }
+
+ if ( in_array( 'custom_fields', $fields ) )
+ $_post['post_meta'] = $this->prepare_meta( $post['ID'] );
+
+ if ( in_array( 'meta', $fields ) ) {
+ $_post['meta'] = array(
+ 'links' => array(
+ 'self' => json_url( '/posts/' . $post['ID'] ),
+ 'author' => json_url( '/users/' . $post['post_author'] ),
+ 'collection' => json_url( '/posts' ),
+ 'replies' => json_url( '/posts/' . $post['ID'] . '/comments' ),
+ 'version-history' => json_url( '/posts/' . $post['ID'] . '/revisions' ),
+ ),
+ );
+
+ if ( ! empty( $post['post_parent'] ) )
+ $_post['meta']['links']['up'] = json_url( '/posts/' . (int) $post['post_parent'] );
+ }
+
+ return apply_filters( 'json_prepare_post', $_post, $post, $fields );
+ }
+
+ /**
+ * Retrieve the post excerpt.
+ *
+ * @return string
+ */
+ protected function prepare_excerpt( $excerpt ) {
+ if ( post_password_required() ) {
+ return __( 'There is no excerpt because this is a protected post.' );
+ }
+
+ return apply_filters( 'the_excerpt', apply_filters( 'get_the_excerpt', $excerpt ) );
+ }
+
+ /**
+ * Prepares term data for return in an XML-RPC object.
+ *
+ * @access protected
+ *
+ * @param array|object $term The unprepared term data
+ * @return array The prepared term data
+ */
+ protected function prepare_term( $term ) {
+ $_term = $term;
+ if ( ! is_array( $_term ) )
+ $_term = get_object_vars( $_term );
+
+ $_term['id'] = $term->term_id;
+ $_term['group'] = $term->term_group;
+ $_term['parent'] = $_term['parent'];
+ $_term['count'] = $_term['count'];
+ #unset($_term['term_id'], )
+
+ $data = array(
+ 'ID' => (int) $term->term_id,
+ 'name' => $term->name,
+ 'slug' => $term->slug,
+ 'group' => (int) $term->term_group,
+ 'parent' => (int) $term->parent,
+ 'count' => (int) $term->count,
+ 'meta' => array(
+ 'links' => array(
+ 'collection' => json_url( '/taxonomy/' . $term->taxonomy ),
+ 'self' => json_url( '/taxonomy/' . $term->taxonomy . '/terms/' . $term->term_id ),
+ ),
+ ),
+ );
+
+ return apply_filters( 'json_prepare_term', $data, $term );
+ }
+
+ /**
+ * Retrieve custom fields for post.
+ *
+ * @since 2.5.0
+ *
+ * @param int $post_id Post ID.
+ * @return array Custom fields, if exist.
+ */
+ protected function prepare_meta( $post_id ) {
+ $post_id = (int) $post_id;
+
+ $custom_fields = array();
+
+ foreach ( (array) has_meta( $post_id ) as $meta ) {
+ // Don't expose protected fields.
+ if ( ! current_user_can( 'edit_post_meta', $post_id, $meta['meta_key'] ) )
+ continue;
+
+ $custom_fields[] = array(
+ 'id' => $meta['meta_id'],
+ 'key' => $meta['meta_key'],
+ 'value' => $meta['meta_value'],
+ );
+ }
+
+ return apply_filters( 'json_prepare_meta', $custom_fields );
+ }
+
+ protected function prepare_author( $author ) {
+ global $wp_json_server;
+
+ $user = get_user_by( 'id', $author );
+
+ $author = array(
+ 'ID' => $user->ID,
+ 'name' => $user->display_name,
+ 'slug' => $user->user_nicename,
+ 'URL' => $user->user_url,
+ 'avatar' => $wp_json_server->get_avatar( $user->user_email ),
+ 'meta' => array(
+ 'links' => array(
+ 'self' => json_url( '/users/' . $user->ID ),
+ 'archives' => json_url( '/users/' . $user->ID . '/posts' ),
+ ),
+ ),
+ );
+
+ if ( current_user_can( 'edit_user', $user->ID ) ) {
+ $author['first_name'] = $user->first_name;
+ $author['last_name'] = $user->last_name;
+ }
+ return $author;
+ }
+
+ /**
+ * Helper method for wp_newPost and wp_editPost, containing shared logic.
+ *
+ * @since 3.4.0
+ * @uses wp_insert_post()
+ *
+ * @param WP_User $user The post author if post_author isn't set in $content_struct.
+ * @param array $content_struct Post data to insert.
+ */
+ protected function insert_post( $data ) {
+ global $wp_json_server;
+
+ $post = array();
+ $update = ! empty( $data['ID'] );
+
+ if ( $update ) {
+ $current_post = get_post( absint( $data['ID'] ) );
+ if ( ! $current_post )
+ return new WP_Error( 'json_post_invalid_id', __( 'Invalid post ID.' ), array( 'status' => 400 ) );
+ $post['ID'] = absint( $data['ID'] );
+ }
+ else {
+ // Defaults
+ $post['post_author'] = 0;
+ $post['post_password'] = '';
+ $post['post_excerpt'] = '';
+ $post['post_content'] = '';
+ $post['post_title'] = '';
+ }
+
+ // Post type
+ if ( ! empty( $data['type'] ) ) {
+ // Changing post type
+ $post_type = get_post_type_object( $data['type'] );
+ if ( ! $post_type )
+ return new WP_Error( 'json_invalid_post_type', __( 'Invalid post type' ), array( 'status' => 400 ) );
+
+ $post['post_type'] = $data['type'];
+ }
+ elseif ( $update ) {
+ // Updating post, use existing post type
+ $current_post = get_post( $data['ID'] );
+ if ( ! $current_post )
+ return new WP_Error( 'json_post_invalid_id', __( 'Invalid post ID.' ), array( 'status' => 400 ) );
+
+ $post_type = get_post_type_object( $current_post->post_type );
+ }
+ else {
+ // Creating new post, use default type
+ $post['post_type'] = apply_filters( 'json_insert_default_post_type', 'post' );
+ $post_type = get_post_type_object( $post['post_type'] );
+ if ( ! $post_type )
+ return new WP_Error( 'json_invalid_post_type', __( 'Invalid post type' ), array( 'status' => 400 ) );
+ }
+
+ // Permissions check
+ if ( $update ) {
+ if ( ! current_user_can( $post_type->cap->edit_post, $data['ID'] ) )
+ return new WP_Error( 'json_cannot_edit', __( 'Sorry, you are not allowed to edit this post.' ), array( 'status' => 401 ) );
+ if ( $post_type->name != get_post_type( $data['ID'] ) )
+ return new WP_Error( 'json_cannot_change_post_type', __( 'The post type may not be changed.' ), array( 'status' => 400 ) );
+ } else {
+ if ( ! current_user_can( $post_type->cap->create_posts ) || ! current_user_can( $post_type->cap->edit_posts ) )
+ return new WP_Error( 'json_cannot_create', __( 'Sorry, you are not allowed to post on this site.' ), array( 'status' => 400 ) );
+ }
+
+ // Post status
+ if ( ! empty( $data['status'] ) ) {
+ $post['post_status'] = $data['status'];
+ switch ( $post['post_status'] ) {
+ case 'draft':
+ case 'pending':
+ break;
+ case 'private':
+ if ( ! current_user_can( $post_type->cap->publish_posts ) )
+ return new WP_Error( 'json_cannot_create_private', __( 'Sorry, you are not allowed to create private posts in this post type' ), array( 'status' => 403 ) );
+ break;
+ case 'publish':
+ case 'future':
+ if ( ! current_user_can( $post_type->cap->publish_posts ) )
+ return new WP_Error( 'json_cannot_publish', __( 'Sorry, you are not allowed to publish posts in this post type' ), array( 'status' => 403 ) );
+ break;
+ default:
+ if ( ! get_post_status_object( $post['post_status'] ) )
+ $post['post_status'] = 'draft';
+ break;
+ }
+ }
+
+ // Post title
+ if ( ! empty( $data['title'] ) ) {
+ $post['post_title'] = $data['title'];
+ }
+
+ // Post date
+ if ( ! empty( $data['date'] ) ) {
+ list( $post['post_date'], $post['post_date_gmt'] ) = $wp_json_server->get_date_with_gmt( $data['date'] );
+ }
+ elseif ( ! empty( $data['date_gmt'] ) ) {
+ list( $post['post_date'], $post['post_date_gmt'] ) = $wp_json_server->get_date_with_gmt( $data['date_gmt'], true );
+ }
+
+ // Post modified
+ if ( ! empty( $data['modified'] ) ) {
+ list( $post['post_modified'], $post['post_modified_gmt'] ) = $wp_json_server->get_date_with_gmt( $data['modified'] );
+ }
+ elseif ( ! empty( $data['modified_gmt'] ) ) {
+ list( $post['post_modified'], $post['post_modified_gmt'] ) = $wp_json_server->get_date_with_gmt( $data['modified_gmt'], true );
+ }
+
+ // Post slug
+ if ( ! empty( $data['name'] ) ) {
+ $post['post_name'] = $data['name'];
+ }
+
+ // Author
+ if ( ! empty( $data['author'] ) ) {
+ // Allow passing an author object
+ if ( is_object( $data['author'] ) ) {
+ if ( empty( $data['author']->ID ) ) {
+ return new WP_Error( 'json_invalid_author', __( 'Invalid author object.' ), array( 'status' => 400 ) );
+ }
+ $data['author'] = absint( $data['author']->ID );
+ }
+ else {
+ $data['author'] = absint( $data['author'] );
+ }
+
+ // Only check edit others' posts if we are another user
+ if ( $data['author'] !== get_current_user_id() ) {
+ if ( ! current_user_can( $post_type->cap->edit_others_posts ) )
+ return new WP_Error( 'json_cannot_edit_others', __( 'You are not allowed to edit posts as this user.' ), array( 'status' => 401 ) );
+
+ $author = get_userdata( $post['post_author'] );
+
+ if ( ! $author )
+ return new WP_Error( 'json_invalid_author', __( 'Invalid author ID.' ), array( 'status' => 400 ) );
+ }
+ }
+
+ // Post password
+ if ( ! empty( $data['password'] ) ) {
+ $post['post_password'] = $data['password'];
+ if ( ! current_user_can( $post_type->cap->publish_posts ) )
+ return new WP_Error( 'json_cannot_create_passworded', __( 'Sorry, you are not allowed to create password protected posts in this post type' ), array( 'status' => 401 ) );
+ }
+
+ // Content and excerpt
+ if ( ! empty( $data['content_raw'] ) ) {
+ $post['post_content'] = $data['content_raw'];
+ }
+ if ( ! empty( $data['excerpt_raw'] ) ) {
+ $post['post_excerpt'] = $data['excerpt_raw'];
+ }
+
+ // Parent
+ if ( ! empty( $data['parent'] ) ) {
+ $parent = get_post( $data['parent'] );
+ $post['post_parent'] = $data['post_parent'];
+ }
+
+ // Menu order
+ if ( ! empty( $data['menu_order'] ) ) {
+ $post['menu_order'] = $data['menu_order'];
+ }
+
+ // Comment status
+ if ( ! empty( $data['comment_status'] ) ) {
+ $post['comment_status'] = $data['comment_status'];
+ }
+
+ // Ping status
+ if ( ! empty( $data['ping_status'] ) ) {
+ $post['ping_status'] = $data['ping_status'];
+ }
+
+ // Post format
+ if ( ! empty( $data['post_format'] ) ) {
+ $formats = get_post_format_slugs();
+ if ( ! in_array( $data['post_format'], $formats ) ) {
+ return new WP_Error( 'json_invalid_post_format', __( 'Invalid post format.' ), array( 'status' => 400 ) );
+ }
+ $post['post_format'] = $data['post_format'];
+ }
+
+ // Post meta
+ // TODO: implement this
+ $post_ID = $update ? wp_update_post( $post, true ) : wp_insert_post( $post, true );
+
+ if ( is_wp_error( $post_ID ) ) {
+ return $post_ID;
+ }
+
+ // Sticky
+ if ( isset( $post['sticky'] ) ) {
+ if ( $post['sticky'] )
+ stick_post( $data['ID'] );
+ else
+ unstick_post( $data['ID'] );
+ }
+
+ // Terms
+ // TODO: implement this
+
+ // Post thumbnail
+ // TODO: implement this as part of #272
+
+ do_action( 'json_insert_post', $post, $data, $update );
+
+ return $post_ID;
+ }
+
+ /**
+ * Parse an RFC3339 timestamp into a DateTime
+ *
+ * @param string $date RFC3339 timestamp
+ * @param boolean $force_utc Force UTC timezone instead of using the timestamp's TZ?
+ * @return DateTime
+ */
+ protected function parse_date( $date, $force_utc = false ) {
+ // Default timezone to the server's current one
+ $timezone = self::get_timezone();
+ if ( $force_utc ) {
+ $date = preg_replace( '/[+-]\d+:?\d+$/', '+00:00', $date );
+ $timezone = new DateTimeZone( 'UTC' );
+ }
+
+ // Strip millisecond precision (a full stop followed by one or more digits)
+ if ( strpos( $date, '.' ) !== false ) {
+ $date = preg_replace( '/\.\d+/', '', $date );
+ }
+ $datetime = DateTime::createFromFormat( DateTime::RFC3339, $date );
+
+ return $datetime;
+ }
+
+ /**
+ * Get a local date with its GMT equivalent, in MySQL datetime format
+ *
+ * @param string $date RFC3339 timestamp
+ * @param boolean $force_utc Should we force UTC timestamp?
+ * @return array Local and UTC datetime strings, in MySQL datetime format (Y-m-d H:i:s)
+ */
+ protected function get_date_with_gmt( $date, $force_utc = false ) {
+ global $wp_json_server;
+
+ $datetime = $wp_json_server->parse_date( $date, $force_utc );
+
+ $datetime->setTimezone( self::get_timezone() );
+ $local = $datetime->format( 'Y-m-d H:i:s' );
+
+ $datetime->setTimezone( new DateTimeZone( 'UTC' ) );
+ $utc = $datetime->format('Y-m-d H:i:s');
+
+ return array( $local, $utc );
+ }
+
+ /**
+ * Retrieve the avatar for a user who provided a user ID or email address.
+ *
+ * {@see get_avatar()} doesn't return just the URL, so we have to
+ * reimplement this here.
+ *
+ * @todo Rework how we do this. Copying it is a hack.
+ *
+ * @since 2.5
+ * @param string $email Email address
+ * @return string <img> tag for the user's avatar
+ */
+ protected function get_avatar( $email ) {
+ if ( ! get_option( 'show_avatars' ) )
+ return false;
+
+ $email_hash = md5( strtolower( trim( $email ) ) );
+
+ if ( is_ssl() ) {
+ $host = 'https://secure.gravatar.com';
+ } else {
+ if ( !empty($email) )
+ $host = sprintf( 'http://%d.gravatar.com', ( hexdec( $email_hash[0] ) % 2 ) );
+ else
+ $host = 'http://0.gravatar.com';
+ }
+
+ $avatar = "$host/avatar/$email_hash&d=404";
+
+ $rating = get_option( 'avatar_rating' );
+ if ( !empty( $rating ) )
+ $avatar .= "&r={$rating}";
+
+ return apply_filters( 'get_avatar', $avatar, $email, '96', '404', '' );
+ }
+
+ /**
+ * Prepares comment data for returning as a JSON response.
+ *
+ * @param stdClass $comment Comment object
+ * @param array $requested_fields Fields to retrieve from the comment
+ * @param string $context Where is the comment being loaded?
+ * @return array Comment data for JSON serialization
+ */
+ protected function prepare_comment( $comment, $requested_fields = array( 'comment', 'meta' ), $context = 'single' ) {
+ global $wp_json_server;
+
+ $fields = array(
+ 'ID' => (int) $comment->comment_ID,
+ 'post' => (int) $comment->comment_post_ID,
+ );
+
+ // Content
+ $fields['content'] = apply_filters( 'comment_text', $comment->comment_content, $comment );
+ // $fields['content_raw'] = $comment->comment_content;
+
+ // Status
+ switch ( $comment->comment_approved ) {
+ case 'hold':
+ case '0':
+ $fields['status'] = 'hold';
+ break;
+
+ case 'approve':
+ case '1':
+ $fields['status'] = 'approved';
+ break;
+
+ case 'spam':
+ case 'trash':
+ default:
+ $fields['status'] = $comment->comment_approved;
+ }
+
+ // Type
+ $fields['type'] = apply_filters( 'get_comment_type', $comment->comment_type );
+ if ( empty( $fields['type'] ) ) {
+ $fields['type'] = 'comment';
+ }
+
+ // Post
+ if ( 'single' === $context ) {
+ $parent_fields = array( 'meta' );
+ if ( $context === 'single' )
+ $parent_fields[] = 'post';
+ $parent = get_post( $post['post_parent'], ARRAY_A );
+ $fields['parent'] = $this->prepare_post( $parent, $parent_fields, 'single-parent' );
+ }
+
+ // Parent
+ if ( ( 'single' === $context || 'single-parent' === $context ) && (int) $comment->comment_parent ) {
+ $parent_fields = array( 'meta' );
+ if ( $context === 'single' )
+ $parent_fields[] = 'comment';
+ $parent = get_comment( $post['post_parent'] );
+ $fields['parent'] = $this->prepare_comment( $parent, $parent_fields, 'single-parent' );
+ }
+
+ // Parent
+ $fields['parent'] = (int) $comment->comment_parent;
+
+ // Author
+ if ( (int) $comment->user_id !== 0 ) {
+ $fields['author'] = $this->prepare_author( (int) $comment->user_id );
+ }
+ else {
+ $fields['author'] = array(
+ 'ID' => 0,
+ 'name' => $comment->comment_author,
+ 'URL' => $comment->comment_author_url,
+ 'avatar' => $wp_json_server->get_avatar( $comment->comment_author_email ),
+ );
+ }
+
+ // Date
+ $timezone = $wp_json_server->get_timezone();
+
+ $date = DateTime::createFromFormat( 'Y-m-d H:i:s', $comment->comment_date, $timezone );
+ $fields['date'] = $date->format( 'c' );
+ $fields['date_tz'] = $date->format( 'e' );
+ $fields['date_gmt'] = date( 'c', strtotime( $post->comment_date_gmt ) );
+
+ // Meta
+ $meta = array(
+ 'links' => array(
+ 'in-reply-to' => json_url( sprintf( '/posts/%d', (int) $comment->comment_post_ID ) )
+ ),
+ );
+ if ( 0 !== (int) $comment->comment_parent ) {
+ $meta['links']['in-reply-to'] .= ',' . json_url( sprintf( '/posts/%d/comments/%d', (int) $comment->comment_post_ID, (int) $comment->comment_parent ) );
+ }
+ if ( 'single' !== $context ) {
+ $meta['links']['self'] = json_url( sprintf( '/posts/%d/comments/%d', (int) $comment->comment_post_ID, (int) $comment->comment_ID ) );
+ }
+
+ // Remove unneeded fields
+ $data = array();
+ if ( in_array( 'comment', $requested_fields ) )
+ $data = array_merge( $data, $fields );
+
+ if ( in_array( 'meta', $requested_fields ) )
+ $data = array_merge( $data, $meta );
+
+ return $data;
+ }
+}
</ins><span class="cx">\ No newline at end of file
</span></span></pre></div>
<a id="2013rmccuetrunklibclasswpjsonpostsphp"></a>
<div class="propset"><h4>Property changes: 2013/rmccue/trunk/lib/class-wp-json-posts.php</h4>
<pre class="diff"><span>
</span></pre></div>
<a id="svnexecutable"></a>
<div class="addfile"><h4>Added: svn:executable</h4></div>
<ins>+*
</ins><span class="cx">\ No newline at end of property
</span><a id="2013rmccuetrunklibclasswpjsonserverphp"></a>
<div class="modfile"><h4>Modified: 2013/rmccue/trunk/lib/class-wp-json-server.php (2205 => 2206)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/rmccue/trunk/lib/class-wp-json-server.php 2013-08-08 02:14:10 UTC (rev 2205)
+++ 2013/rmccue/trunk/lib/class-wp-json-server.php 2013-08-08 02:45:32 UTC (rev 2206)
</span><span class="lines">@@ -212,38 +212,40 @@
</span><span class="cx"> * @return array `'/path/regex' => array( $callback, $bitmask )` or `'/path/regex' => array( array( $callback, $bitmask ), ...)`
</span><span class="cx"> */
</span><span class="cx"> public function getRoutes() {
</span><ins>+ $posts = new WP_JSON_Posts();
+
</ins><span class="cx"> $endpoints = array(
</span><span class="cx"> // Meta endpoints
</span><span class="cx"> '/' => array( array( $this, 'getIndex' ), self::READABLE ),
</span><span class="cx">
</span><span class="cx"> // Post endpoints
</span><span class="cx"> '/posts' => array(
</span><del>- array( array( $this, 'getPosts' ), self::READABLE ),
- array( array( $this, 'newPost' ), self::CREATABLE | self::ACCEPT_JSON ),
</del><ins>+ array( array( $posts, 'getPosts' ), self::READABLE ),
+ array( array( $posts, 'newPost' ), self::CREATABLE | self::ACCEPT_JSON ),
</ins><span class="cx"> ),
</span><span class="cx">
</span><span class="cx"> '/posts/(?P<id>\d+)' => array(
</span><del>- array( array( $this, 'getPost' ), self::READABLE ),
- array( array( $this, 'editPost' ), self::EDITABLE | self::ACCEPT_JSON ),
- array( array( $this, 'deletePost' ), self::DELETABLE ),
</del><ins>+ array( array( $posts, 'getPost' ), self::READABLE ),
+ array( array( $posts, 'editPost' ), self::EDITABLE | self::ACCEPT_JSON ),
+ array( array( $posts, 'deletePost' ), self::DELETABLE ),
</ins><span class="cx"> ),
</span><span class="cx"> '/posts/(?P<id>\d+)/revisions' => array( '__return_null', self::READABLE ),
</span><span class="cx">
</span><span class="cx"> // Comments
</span><span class="cx"> '/posts/(?P<id>\d+)/comments' => array(
</span><del>- array( array( $this, 'getComments' ), self::READABLE ),
</del><ins>+ array( array( $posts, 'getComments' ), self::READABLE ),
</ins><span class="cx"> array( '__return_null', self::CREATABLE | self::ACCEPT_JSON ),
</span><span class="cx"> ),
</span><span class="cx"> '/posts/(?P<id>\d+)/comments/(?P<comment>\d+)' => array(
</span><del>- array( array( $this, 'getComment' ), self::READABLE ),
</del><ins>+ array( array( $posts, 'getComment' ), self::READABLE ),
</ins><span class="cx"> array( '__return_null', self::EDITABLE | self::ACCEPT_JSON ),
</span><span class="cx"> array( '__return_null', self::DELETABLE ),
</span><span class="cx"> ),
</span><span class="cx">
</span><span class="cx"> // Meta-post endpoints
</span><del>- '/posts/types' => array( array( $this, 'getPostTypes' ), self::READABLE ),
- '/posts/types/(?P<type>\w+)' => array( array( $this, 'getPostType' ), self::READABLE ),
- '/posts/statuses' => array( array( $this, 'getPostStatuses' ), self::READABLE ),
</del><ins>+ '/posts/types' => array( array( $posts, 'getPostTypes' ), self::READABLE ),
+ '/posts/types/(?P<type>\w+)' => array( array( $posts, 'getPostType' ), self::READABLE ),
+ '/posts/statuses' => array( array( $posts, 'getPostStatuses' ), self::READABLE ),
</ins><span class="cx">
</span><span class="cx"> // Taxonomies
</span><span class="cx"> '/taxonomies' => array( '__return_null', self::READABLE ),
</span><span class="lines">@@ -458,388 +460,6 @@
</span><span class="cx"> }
</span><span class="cx">
</span><span class="cx"> /**
</span><del>- * Retrieve posts.
- *
- * @since 3.4.0
- *
- * The optional $filter parameter modifies the query used to retrieve posts.
- * Accepted keys are 'post_type', 'post_status', 'number', 'offset',
- * 'orderby', and 'order'.
- *
- * The optional $fields parameter specifies what fields will be included
- * in the response array.
- *
- * @uses wp_get_recent_posts()
- * @see WP_JSON_Server::getPost() for more on $fields
- * @see get_posts() for more on $filter values
- *
- * @param array $filter optional
- * @param array $fields optional
- * @return array contains a collection of Post entities.
- */
- public function getPosts( $filter = array(), $fields = array(), $type = 'post', $page = 1 ) {
- if ( empty($fields) || in_array( 'default', $fields ) )
- $fields = array_merge( $fields, apply_filters( 'json_default_post_fields', array( 'post', 'meta', 'terms' ), 'getPosts' ) );
-
- $query = array();
-
- $post_type = get_post_type_object( $type );
- if ( ! ( (bool) $post_type ) )
- return new WP_Error( 'json_invalid_post_type', __( 'The post type specified is not valid' ), array( 'status' => 403 ) );
-
- $query['post_type'] = $post_type->name;
-
- global $wp;
- // Allow the same as normal WP
- $valid_vars = apply_filters('query_vars', $wp->public_query_vars);
-
- // If the user has the correct permissions, also allow use of internal
- // query parameters, which are only undesirable on the frontend
- //
- // To disable anyway, use `add_filter('json_private_query_vars', '__return_empty_array');`
-
- if ( current_user_can( $post_type->cap->edit_posts ) ) {
- $private = apply_filters('json_private_query_vars', $wp->private_query_vars);
- $valid_vars = array_merge($valid_vars, $private);
- }
-
- // Define our own in addition to WP's normal vars
- $json_valid = array('posts_per_page');
- $valid_vars = array_merge($valid_vars, $json_valid);
-
- // Filter and flip for querying
- $valid_vars = apply_filters('json_query_vars', $valid_vars);
- $valid_vars = array_flip($valid_vars);
-
- // Exclude the post_type query var to avoid dodging the permission
- // check above
- unset($valid_vars['post_type']);
-
- foreach ($valid_vars as $var => $index) {
- if ( isset( $filter[ $var ] ) ) {
- $query[ $var ] = apply_filters( 'json_query_var-' . $var, $filter[ $var ] );
- }
- }
-
- // Special parameter handling
- $query['paged'] = absint( $page );
-
- $post_query = new WP_Query();
- $posts_list = $post_query->query( $query );
- $this->query_navigation_headers( $post_query );
-
- if ( ! $posts_list )
- return array();
-
- // holds all the posts data
- $struct = array();
-
- header( 'Last-Modified: ' . mysql2date( 'D, d M Y H:i:s', get_lastpostmodified( 'GMT' ), 0 ).' GMT' );
-
- foreach ( $posts_list as $post ) {
- $post = get_object_vars( $post );
- $post_type = get_post_type_object( $post['post_type'] );
- if ( 'publish' !== $post['post_status'] && ! current_user_can( $post_type->cap->read_post, $post['ID'] ) )
- continue;
-
- $this->link_header( 'item', json_url( '/posts/' . $post['ID'] ), array( 'title' => $post['post_title'] ) );
- $struct[] = $this->prepare_post( $post, $fields );
- }
-
- return $struct;
- }
-
- /**
- * Create a new post for any registered post type.
- *
- * @since 3.4.0
- * @internal 'data' is used here rather than 'content', as get_default_post_to_edit uses $_REQUEST['content']
- *
- * @param array $content Content data. Can contain:
- * - post_type (default: 'post')
- * - post_status (default: 'draft')
- * - post_title
- * - post_author
- * - post_excerpt
- * - post_content
- * - post_date_gmt | post_date
- * - post_format
- * - post_password
- * - comment_status - can be 'open' | 'closed'
- * - ping_status - can be 'open' | 'closed'
- * - sticky
- * - post_thumbnail - ID of a media item to use as the post thumbnail/featured image
- * - custom_fields - array, with each element containing 'key' and 'value'
- * - terms - array, with taxonomy names as keys and arrays of term IDs as values
- * - terms_names - array, with taxonomy names as keys and arrays of term names as values
- * - enclosure
- * - any other fields supported by wp_insert_post()
- * @return array Post data (see {@see WP_JSON_Server::getPost})
- */
- function newPost( $data ) {
- unset( $data['ID'] );
-
- $result = $this->insert_post( $data );
- if ( is_string( $result ) || is_int( $result ) ) {
- status_header( 201 );
- header( 'Location: ' . json_url( '/posts/' . $result ) );
-
- return $this->getPost( $result );
- }
- elseif ( $result instanceof IXR_Error ) {
- return new WP_Error( 'json_insert_error', $result->message, array( 'status' => $result->code ) );
- }
- else {
- return new WP_Error( 'json_insert_error', __( 'An unknown error occurred while creating the post' ), array( 'status' => 500 ) );
- }
- }
-
- /**
- * Retrieve a post.
- *
- * @uses get_post()
- * @param int $id Post ID
- * @param array $fields Post fields to return (optional)
- * @return array Post entity
- */
- public function getPost( $id, $fields = array() ) {
- $id = (int) $id;
-
- if ( empty( $id ) )
- return new WP_Error( 'json_post_invalid_id', __( 'Invalid post ID.' ), array( 'status' => 404 ) );
-
- $post = get_post( $id, ARRAY_A );
-
- if ( empty( $fields ) || in_array( 'default', $fields ) )
- $fields = array_merge( $fields, apply_filters( 'json_default_post_fields', array( 'post', 'post-extended', 'meta', 'terms', 'custom_fields' ), 'getPost' ) );
-
- if ( empty( $post['ID'] ) )
- return new WP_Error( 'json_post_invalid_id', __( 'Invalid post ID.' ), array( 'status' => 404 ) );
-
- $post_type = get_post_type_object( $post['post_type'] );
- if ( 'publish' !== $post['post_status'] && ! current_user_can( $post_type->cap->read_post, $id ) )
- return new WP_Error( 'json_user_cannot_read', __( 'Sorry, you cannot read this post.' ), array( 'status' => 401 ) );
-
- // Link headers (see RFC 5988)
-
- header( 'Last-Modified: ' . mysql2date( 'D, d M Y H:i:s', $post['post_modified_gmt'] ) . 'GMT' );
-
- $post = $this->prepare_post( $post, $fields );
- if ( is_wp_error( $post ) )
- return $post;
-
- foreach ( $post['meta']['links'] as $rel => $url ) {
- $this->link_header( $rel, $url );
- }
- $this->link_header( 'alternate', get_permalink( $id ), array( 'type' => 'text/html' ) );
-
- return $post;
- }
-
- /**
- * Edit a post for any registered post type.
- *
- * The $data parameter only needs to contain fields that should be changed.
- * All other fields will retain their existing values.
- *
- * @since 3.4.0
- * @internal 'data' is used here rather than 'content', as get_default_post_to_edit uses $_REQUEST['content']
- *
- * @param int $id Post ID to edit
- * @param array $data Data construct, see {@see WP_JSON_Server::newPost}
- * @param array $_headers Header data
- * @return true on success
- */
- function editPost( $id, $data, $_headers = array() ) {
- $post = get_post( $id, ARRAY_A );
-
- if ( empty( $post['ID'] ) )
- return new WP_Error( 'json_post_invalid_id', __( 'Invalid post ID.' ), array( 'status' => 404 ) );
-
- if ( isset( $_headers['IF_UNMODIFIED_SINCE'] ) ) {
- // As mandated by RFC2616, we have to check all of RFC1123, RFC1036
- // and C's asctime() format (and ignore invalid headers)
- $formats = array( DateTime::RFC1123, DateTime::RFC1036, 'D M j H:i:s Y' );
- foreach ( $formats as $format ) {
- $check = DateTime::createFromFormat( DateTime::RFC1123, $_headers['IF_UNMODIFIED_SINCE'] );
-
- if ( $check !== false )
- break;
- }
-
- // If the post has been modified since the date provided, return an error.
- if ( $check && mysql2date( 'U', $post['post_modified_gmt'] ) > $check->format('U') ) {
- return new WP_Error( 'json_old_revision', __( 'There is a revision of this post that is more recent.' ), array( 'status' => 412 ) );
- }
- }
-
- $data['ID'] = $id;
-
- $retval = $this->insert_post( $data );
- if ( is_wp_error( $retval ) ) {
- return $retval;
- }
-
- return $this->getPost( $id );
- }
-
- /**
- * Delete a post for any registered post type
- *
- * @uses wp_delete_post()
- * @param int $id
- * @return true on success
- */
- public function deletePost( $id, $force = false ) {
- $id = (int) $id;
- $post = get_post( $id, ARRAY_A );
-
- if ( empty( $post['ID'] ) )
- return new WP_Error( 'json_post_invalid_id', __( 'Invalid post ID.' ), array( 'status' => 404 ) );
-
- $post_type = get_post_type_object( $post['post_type'] );
- if ( ! current_user_can( $post_type->cap->delete_post, $id ) )
- return new WP_Error( 'json_user_cannot_delete_post', __( 'Sorry, you are not allowed to delete this post.' ), array( 'status' => 401 ) );
-
- $result = wp_delete_post( $id, $force );
-
- if ( ! $result )
- return new WP_Error( 'json_cannot_delete', __( 'The post cannot be deleted.' ), array( 'status' => 500 ) );
-
- if ( $force ) {
- return array( 'message' => __( 'Permanently deleted post' ) );
- }
- else {
- // TODO: return a HTTP 202 here instead
- return array( 'message' => __( 'Deleted post' ) );
- }
- }
-
- /**
- * Retrieve comments
- *
- * @param int $id Post ID to retrieve comments for
- * @return array List of Comment entities
- */
- public function getComments( $id ) {
- //$args = array('status' => $status, 'post_id' => $id, 'offset' => $offset, 'number' => $number )l
- $comments = get_comments( array('post_id' => $id) );
-
- $struct = array();
- foreach ( $comments as $comment ) {
- $struct[] = $this->prepare_comment( $comment, array( 'comment', 'meta' ), 'collection' );
- }
- return $struct;
- }
-
- /**
- * Retrieve a single comment
- *
- * @param int $comment Comment ID
- * @return array Comment entity
- */
- public function getComment( $comment ) {
- $comment = get_comment( $comment );
- $data = $this->prepare_comment( $comment );
- return $data;
- }
-
- /**
- * Get all public post types
- *
- * @uses self::getPostType()
- * @return array List of post type data
- */
- public function getPostTypes() {
- $data = get_post_types( array(), 'objects' );
-
- $types = array();
- foreach ($data as $name => $type) {
- $type = $this->getPostType( $type, true );
- if ( is_wp_error( $type ) )
- continue;
-
- $types[ $name ] = $type;
- }
-
- return $types;
- }
-
- /**
- * Get a post type
- *
- * @param string|object $type Type name, or type object (internal use)
- * @param boolean $_in_collection Is this in a collection? (internal use)
- * @return array Post type data
- */
- public function getPostType( $type, $_in_collection = false ) {
- if ( ! is_object( $type ) )
- $type = get_post_type_object($type);
-
- if ( $type->public === false )
- return new WP_Error( 'json_cannot_read_type', __( 'Cannot view post type' ), array( 'status' => 403 ) );
-
- $data = array(
- 'name' => $type->label,
- 'slug' => $type->name,
- 'description' => $type->description,
- 'labels' => $type->labels,
- 'queryable' => $type->publicly_queryable,
- 'searchable' => ! $type->exclude_from_search,
- 'hierarchical' => $type->hierarchical,
- 'meta' => array(),
- );
-
- if ( $_in_collection )
- $data['meta']['self'] = json_url( '/posts/types/' . $type->name );
- else
- $data['meta']['collection'] = json_url( '/posts/types' );
-
- if ( $type->publicly_queryable ) {
- if ($type->name === 'post')
- $data['meta']['archives'] = json_url( '/posts' );
- else
- $data['meta']['archives'] = json_url( add_query_arg( 'type', $type->name, '/posts' ) );
- }
-
- return $data;
- }
-
- /**
- * Get the registered post statuses
- *
- * @return array List of post status data
- */
- public function getPostStatuses() {
- $statuses = get_post_stati(array(), 'objects');
-
- $data = array();
- foreach ($statuses as $status) {
- if ( $status->internal === true || ! $status->show_in_admin_status_list )
- continue;
-
- $data[ $status->name ] = array(
- 'name' => $status->label,
- 'slug' => $status->name,
- 'public' => $status->public,
- 'protected' => $status->protected,
- 'private' => $status->private,
- 'queryable' => $status->publicly_queryable,
- 'show_in_list' => $status->show_in_admin_all_list,
- 'meta' => array(),
- );
- if ( $type->publicly_queryable ) {
- if ($type->name === 'publish')
- $data['meta']['archives'] = json_url( '/posts' );
- else
- $data['meta']['archives'] = json_url( add_query_arg( 'type', $type->name, '/posts' ) );
- }
- }
-
- return $data;
- }
-
- /**
</del><span class="cx"> * Send a Link header
</span><span class="cx"> *
</span><span class="cx"> * @todo Make this safe for <>"';,
</span><span class="lines">@@ -852,7 +472,7 @@
</span><span class="cx"> * @param string $link Target IRI for the link
</span><span class="cx"> * @param array $other Other parameters to send, as an assocative array
</span><span class="cx"> */
</span><del>- protected function link_header( $rel, $link, $other = array() ) {
</del><ins>+ public function link_header( $rel, $link, $other = array() ) {
</ins><span class="cx"> $header = 'Link: <' . $link . '>; rel="' . $rel . '"';
</span><span class="cx"> foreach ( $other as $key => $value ) {
</span><span class="cx"> if ( 'title' == $key )
</span><span class="lines">@@ -867,7 +487,7 @@
</span><span class="cx"> *
</span><span class="cx"> * @param WP_Query $query
</span><span class="cx"> */
</span><del>- protected function query_navigation_headers( $query ) {
</del><ins>+ public function query_navigation_headers( $query ) {
</ins><span class="cx"> $max_page = $query->max_num_pages;
</span><span class="cx"> $paged = $query->get('paged');
</span><span class="cx">
</span><span class="lines">@@ -899,7 +519,7 @@
</span><span class="cx"> *
</span><span class="cx"> * @return string
</span><span class="cx"> */
</span><del>- protected function get_raw_data() {
</del><ins>+ public function get_raw_data() {
</ins><span class="cx"> global $HTTP_RAW_POST_DATA;
</span><span class="cx">
</span><span class="cx"> // A bug in PHP < 5.2.2 makes $HTTP_RAW_POST_DATA not set by default,
</span><span class="lines">@@ -912,497 +532,13 @@
</span><span class="cx"> }
</span><span class="cx">
</span><span class="cx"> /**
</span><del>- * Prepares post data for return in an XML-RPC object.
- *
- * @access protected
- *
- * @param array $post The unprepared post data
- * @param array $fields The subset of post type fields to return
- * @return array The prepared post data
- */
- protected function prepare_post( $post, $fields, $context = 'single' ) {
- // holds the data for this post. built up based on $fields
- $_post = array(
- 'ID' => (int) $post['ID'],
- );
-
- $post_type = get_post_type_object( $post['post_type'] );
- if ( 'publish' !== $post['post_status'] && ! current_user_can( $post_type->cap->read_post, $post['ID'] ) )
- return new WP_Error( 'json_user_cannot_read', __( 'Sorry, you cannot read this post.' ), array( 'status' => 401 ) );
-
- // prepare common post fields
- $post_fields = array(
- 'title' => get_the_title( $post['ID'] ), // $post['post_title'],
- 'status' => $post['post_status'],
- 'type' => $post['post_type'],
- 'author' => (int) $post['post_author'],
- 'content' => apply_filters( 'the_content', $post['post_content'] ),
- 'parent' => (int) $post['post_parent'],
- #'post_mime_type' => $post['post_mime_type'],
- 'link' => get_permalink( $post['ID'] ),
- );
- $post_fields_extended = array(
- 'slug' => $post['post_name'],
- 'guid' => apply_filters( 'get_the_guid', $post['guid'] ),
- 'excerpt' => $this->prepare_excerpt( $post['post_excerpt'] ),
- 'menu_order' => (int) $post['menu_order'],
- 'comment_status' => $post['comment_status'],
- 'ping_status' => $post['ping_status'],
- 'sticky' => ( $post['post_type'] === 'post' && is_sticky( $post['ID'] ) ),
- );
- $post_fields_raw = array(
- 'title_raw' => $post['post_title'],
- 'content_raw' => $post['post_content'],
- 'guid_raw' => $post['guid'],
- );
-
- // Dates
- $timezone = $this->get_timezone();
-
- $date = DateTime::createFromFormat( 'Y-m-d H:i:s', $post['post_date'], $timezone );
- $post_fields['date'] = $date->format( 'c' );
- $post_fields_extended['date_tz'] = $date->format( 'e' );
- $post_fields_extended['date_gmt'] = date( 'c', strtotime( $post['post_date_gmt'] ) );
-
- $modified = DateTime::createFromFormat( 'Y-m-d H:i:s', $post['post_modified'], $timezone );
- $post_fields['modified'] = $modified->format( 'c' );
- $post_fields_extended['modified_tz'] = $modified->format( 'e' );
- $post_fields_extended['modified_gmt'] = date( 'c', strtotime( $post['post_modified_gmt'] ) );
-
- // Authorized fields
- // TODO: Send `Vary: Authorization` to clarify that the data can be
- // changed by the user's auth status
- if ( current_user_can( $post_type->cap->edit_post, $post['ID'] ) ) {
- $post_fields_extended['password'] = $post['post_password'];
- }
-
- // Thumbnail
- /*$post_fields_extended['post_thumbnail'] = array();
- $thumbnail_id = get_post_thumbnail_id( $post['ID'] );
- if ( $thumbnail_id ) {
- $thumbnail_size = current_theme_supports( 'post-thumbnail' ) ? 'post-thumbnail' : 'thumbnail';
- $post_fields_extended['post_thumbnail'] = $this->_prepare_media_item( get_post( $thumbnail_id ), $thumbnail_size );
- }*/
-
- // Consider future posts as published
- if ( $post_fields['status'] === 'future' )
- $post_fields['status'] = 'publish';
-
- // Fill in blank post format
- $post_fields['format'] = get_post_format( $post['ID'] );
- if ( empty( $post_fields['format'] ) )
- $post_fields['format'] = 'standard';
-
- $post_fields['author'] = $this->prepare_author( $post['post_author'] );
-
- if ( ( 'single' === $context || 'single-parent' === $context ) && 0 !== $post['post_parent'] ) {
- // Avoid nesting too deeply
- // This gives post + post-extended + meta for the main post,
- // post + meta for the parent and just meta for the grandparent
- $parent_fields = array( 'meta' );
- if ( $context === 'single' )
- $parent_fields[] = 'post';
- $parent = get_post( $post['post_parent'], ARRAY_A );
- $post_fields['parent'] = $this->prepare_post( $parent, $parent_fields, 'single-parent' );
- }
-
- // Merge requested $post_fields fields into $_post
- if ( in_array( 'post', $fields ) ) {
- $_post = array_merge( $_post, $post_fields );
- } else {
- $requested_fields = array_intersect_key( $post_fields, array_flip( $fields ) );
- $_post = array_merge( $_post, $requested_fields );
- }
-
- if ( in_array( 'post-extended', $fields ) )
- $_post = array_merge( $_post, $post_fields_extended );
-
- if ( in_array( 'post-raw', $fields ) && current_user_can( $post_type->cap->edit_post, $post['ID'] ) )
- $_post = array_merge( $_post, $post_fields_raw );
- elseif ( in_array( 'post-raw', $fields ) )
- return new WP_Error( 'json_cannot_edit', __( 'Sorry, you cannot edit this post' ), array( 'status' => 403 ) );
-
- // Taxonomies
- $all_taxonomy_fields = in_array( 'taxonomies', $fields );
-
- if ( $all_taxonomy_fields || in_array( 'terms', $fields ) ) {
- $post_type_taxonomies = get_object_taxonomies( $post['post_type'] );
- $terms = wp_get_object_terms( $post['ID'], $post_type_taxonomies );
- $_post['terms'] = array();
- foreach ( $terms as $term ) {
- $_post['terms'][ $term->taxonomy ] = $this->prepare_term( $term );
- }
- }
-
- if ( in_array( 'custom_fields', $fields ) )
- $_post['post_meta'] = $this->prepare_meta( $post['ID'] );
-
- if ( in_array( 'meta', $fields ) ) {
- $_post['meta'] = array(
- 'links' => array(
- 'self' => json_url( '/posts/' . $post['ID'] ),
- 'author' => json_url( '/users/' . $post['post_author'] ),
- 'collection' => json_url( '/posts' ),
- 'replies' => json_url( '/posts/' . $post['ID'] . '/comments' ),
- 'version-history' => json_url( '/posts/' . $post['ID'] . '/revisions' ),
- ),
- );
-
- if ( ! empty( $post['post_parent'] ) )
- $_post['meta']['links']['up'] = json_url( '/posts/' . (int) $post['post_parent'] );
- }
-
- return apply_filters( 'json_prepare_post', $_post, $post, $fields );
- }
-
- /**
- * Retrieve the post excerpt.
- *
- * @return string
- */
- protected function prepare_excerpt( $excerpt ) {
- if ( post_password_required() ) {
- return __( 'There is no excerpt because this is a protected post.' );
- }
-
- return apply_filters( 'the_excerpt', apply_filters( 'get_the_excerpt', $excerpt ) );
- }
-
- /**
- * Prepares term data for return in an XML-RPC object.
- *
- * @access protected
- *
- * @param array|object $term The unprepared term data
- * @return array The prepared term data
- */
- protected function prepare_term( $term ) {
- $_term = $term;
- if ( ! is_array( $_term ) )
- $_term = get_object_vars( $_term );
-
- $_term['id'] = $term->term_id;
- $_term['group'] = $term->term_group;
- $_term['parent'] = $_term['parent'];
- $_term['count'] = $_term['count'];
- #unset($_term['term_id'], )
-
- $data = array(
- 'ID' => (int) $term->term_id,
- 'name' => $term->name,
- 'slug' => $term->slug,
- 'group' => (int) $term->term_group,
- 'parent' => (int) $term->parent,
- 'count' => (int) $term->count,
- 'meta' => array(
- 'links' => array(
- 'collection' => json_url( '/taxonomy/' . $term->taxonomy ),
- 'self' => json_url( '/taxonomy/' . $term->taxonomy . '/terms/' . $term->term_id ),
- ),
- ),
- );
-
- return apply_filters( 'json_prepare_term', $data, $term );
- }
-
- /**
- * Retrieve custom fields for post.
- *
- * @since 2.5.0
- *
- * @param int $post_id Post ID.
- * @return array Custom fields, if exist.
- */
- protected function prepare_meta( $post_id ) {
- $post_id = (int) $post_id;
-
- $custom_fields = array();
-
- foreach ( (array) has_meta( $post_id ) as $meta ) {
- // Don't expose protected fields.
- if ( ! current_user_can( 'edit_post_meta', $post_id, $meta['meta_key'] ) )
- continue;
-
- $custom_fields[] = array(
- 'id' => $meta['meta_id'],
- 'key' => $meta['meta_key'],
- 'value' => $meta['meta_value'],
- );
- }
-
- return apply_filters( 'json_prepare_meta', $custom_fields );
- }
-
- /**
- * Convert a WordPress date string to an array.
- *
- * @access protected
- *
- * @param string $date
- * @return array
- */
- protected function _convert_date( $date ) {
- if ( $date === '0000-00-00 00:00:00' ) {
- return 0;
- }
- return strtotime( $date );
- }
-
- /**
- * Convert a WordPress GMT date string to an array.
- *
- * @access protected
- *
- * @param string $date_gmt
- * @param string $date
- * @return array
- */
- protected function _convert_date_gmt( $date_gmt, $date ) {
- return strtotime( $date_gmt );
- }
-
- protected function prepare_author( $author ) {
- $user = get_user_by( 'id', $author );
-
- $author = array(
- 'ID' => $user->ID,
- 'name' => $user->display_name,
- 'slug' => $user->user_nicename,
- 'URL' => $user->user_url,
- 'avatar' => $this->get_avatar( $user->user_email ),
- 'meta' => array(
- 'links' => array(
- 'self' => json_url( '/users/' . $user->ID ),
- 'archives' => json_url( '/users/' . $user->ID . '/posts' ),
- ),
- ),
- );
-
- if ( current_user_can( 'edit_user', $user->ID ) ) {
- $author['first_name'] = $user->first_name;
- $author['last_name'] = $user->last_name;
- }
- return $author;
- }
-
- /**
- * Helper method for wp_newPost and wp_editPost, containing shared logic.
- *
- * @since 3.4.0
- * @uses wp_insert_post()
- *
- * @param WP_User $user The post author if post_author isn't set in $content_struct.
- * @param array $content_struct Post data to insert.
- */
- protected function insert_post( $data ) {
- $post = array();
- $update = ! empty( $data['ID'] );
-
- if ( $update ) {
- $current_post = get_post( absint( $data['ID'] ) );
- if ( ! $current_post )
- return new WP_Error( 'json_post_invalid_id', __( 'Invalid post ID.' ), array( 'status' => 400 ) );
- $post['ID'] = absint( $data['ID'] );
- }
- else {
- // Defaults
- $post['post_author'] = 0;
- $post['post_password'] = '';
- $post['post_excerpt'] = '';
- $post['post_content'] = '';
- $post['post_title'] = '';
- }
-
- // Post type
- if ( ! empty( $data['type'] ) ) {
- // Changing post type
- $post_type = get_post_type_object( $data['type'] );
- if ( ! $post_type )
- return new WP_Error( 'json_invalid_post_type', __( 'Invalid post type' ), array( 'status' => 400 ) );
-
- $post['post_type'] = $data['type'];
- }
- elseif ( $update ) {
- // Updating post, use existing post type
- $current_post = get_post( $data['ID'] );
- if ( ! $current_post )
- return new WP_Error( 'json_post_invalid_id', __( 'Invalid post ID.' ), array( 'status' => 400 ) );
-
- $post_type = get_post_type_object( $current_post->post_type );
- }
- else {
- // Creating new post, use default type
- $post['post_type'] = apply_filters( 'json_insert_default_post_type', 'post' );
- $post_type = get_post_type_object( $post['post_type'] );
- if ( ! $post_type )
- return new WP_Error( 'json_invalid_post_type', __( 'Invalid post type' ), array( 'status' => 400 ) );
- }
-
- // Permissions check
- if ( $update ) {
- if ( ! current_user_can( $post_type->cap->edit_post, $data['ID'] ) )
- return new WP_Error( 'json_cannot_edit', __( 'Sorry, you are not allowed to edit this post.' ), array( 'status' => 401 ) );
- if ( $post_type->name != get_post_type( $data['ID'] ) )
- return new WP_Error( 'json_cannot_change_post_type', __( 'The post type may not be changed.' ), array( 'status' => 400 ) );
- } else {
- if ( ! current_user_can( $post_type->cap->create_posts ) || ! current_user_can( $post_type->cap->edit_posts ) )
- return new WP_Error( 'json_cannot_create', __( 'Sorry, you are not allowed to post on this site.' ), array( 'status' => 400 ) );
- }
-
- // Post status
- if ( ! empty( $data['status'] ) ) {
- $post['post_status'] = $data['status'];
- switch ( $post['post_status'] ) {
- case 'draft':
- case 'pending':
- break;
- case 'private':
- if ( ! current_user_can( $post_type->cap->publish_posts ) )
- return new WP_Error( 'json_cannot_create_private', __( 'Sorry, you are not allowed to create private posts in this post type' ), array( 'status' => 403 ) );
- break;
- case 'publish':
- case 'future':
- if ( ! current_user_can( $post_type->cap->publish_posts ) )
- return new WP_Error( 'json_cannot_publish', __( 'Sorry, you are not allowed to publish posts in this post type' ), array( 'status' => 403 ) );
- break;
- default:
- if ( ! get_post_status_object( $post['post_status'] ) )
- $post['post_status'] = 'draft';
- break;
- }
- }
-
- // Post title
- if ( ! empty( $data['title'] ) ) {
- $post['post_title'] = $data['title'];
- }
-
- // Post date
- if ( ! empty( $data['date'] ) ) {
- list( $post['post_date'], $post['post_date_gmt'] ) = $this->get_date_with_gmt( $data['date'] );
- }
- elseif ( ! empty( $data['date_gmt'] ) ) {
- list( $post['post_date'], $post['post_date_gmt'] ) = $this->get_date_with_gmt( $data['date_gmt'], true );
- }
-
- // Post modified
- if ( ! empty( $data['modified'] ) ) {
- list( $post['post_modified'], $post['post_modified_gmt'] ) = $this->get_date_with_gmt( $data['modified'] );
- }
- elseif ( ! empty( $data['modified_gmt'] ) ) {
- list( $post['post_modified'], $post['post_modified_gmt'] ) = $this->get_date_with_gmt( $data['modified_gmt'], true );
- }
-
- // Post slug
- if ( ! empty( $data['name'] ) ) {
- $post['post_name'] = $data['name'];
- }
-
- // Author
- if ( ! empty( $data['author'] ) ) {
- // Allow passing an author object
- if ( is_object( $data['author'] ) ) {
- if ( empty( $data['author']->ID ) ) {
- return new WP_Error( 'json_invalid_author', __( 'Invalid author object.' ), array( 'status' => 400 ) );
- }
- $data['author'] = absint( $data['author']->ID );
- }
- else {
- $data['author'] = absint( $data['author'] );
- }
-
- // Only check edit others' posts if we are another user
- if ( $data['author'] !== get_current_user_id() ) {
- if ( ! current_user_can( $post_type->cap->edit_others_posts ) )
- return new WP_Error( 'json_cannot_edit_others', __( 'You are not allowed to edit posts as this user.' ), array( 'status' => 401 ) );
-
- $author = get_userdata( $post['post_author'] );
-
- if ( ! $author )
- return new WP_Error( 'json_invalid_author', __( 'Invalid author ID.' ), array( 'status' => 400 ) );
- }
- }
-
- // Post password
- if ( ! empty( $data['password'] ) ) {
- $post['post_password'] = $data['password'];
- if ( ! current_user_can( $post_type->cap->publish_posts ) )
- return new WP_Error( 'json_cannot_create_passworded', __( 'Sorry, you are not allowed to create password protected posts in this post type' ), array( 'status' => 401 ) );
- }
-
- // Content and excerpt
- if ( ! empty( $data['content_raw'] ) ) {
- $post['post_content'] = $data['content_raw'];
- }
- if ( ! empty( $data['excerpt_raw'] ) ) {
- $post['post_excerpt'] = $data['excerpt_raw'];
- }
-
- // Parent
- if ( ! empty( $data['parent'] ) ) {
- $parent = get_post( $data['parent'] );
- $post['post_parent'] = $data['post_parent'];
- }
-
- // Menu order
- if ( ! empty( $data['menu_order'] ) ) {
- $post['menu_order'] = $data['menu_order'];
- }
-
- // Comment status
- if ( ! empty( $data['comment_status'] ) ) {
- $post['comment_status'] = $data['comment_status'];
- }
-
- // Ping status
- if ( ! empty( $data['ping_status'] ) ) {
- $post['ping_status'] = $data['ping_status'];
- }
-
- // Post format
- if ( ! empty( $data['post_format'] ) ) {
- $formats = get_post_format_slugs();
- if ( ! in_array( $data['post_format'], $formats ) ) {
- return new WP_Error( 'json_invalid_post_format', __( 'Invalid post format.' ), array( 'status' => 400 ) );
- }
- $post['post_format'] = $data['post_format'];
- }
-
- // Post meta
- // TODO: implement this
- $post_ID = $update ? wp_update_post( $post, true ) : wp_insert_post( $post, true );
-
- if ( is_wp_error( $post_ID ) ) {
- return $post_ID;
- }
-
- // Sticky
- if ( isset( $post['sticky'] ) ) {
- if ( $post['sticky'] )
- stick_post( $data['ID'] );
- else
- unstick_post( $data['ID'] );
- }
-
- // Terms
- // TODO: implement this
-
- // Post thumbnail
- // TODO: implement this as part of #272
-
- do_action( 'json_insert_post', $post, $data, $update );
-
- return $post_ID;
- }
-
- /**
</del><span class="cx"> * Parse an RFC3339 timestamp into a DateTime
</span><span class="cx"> *
</span><span class="cx"> * @param string $date RFC3339 timestamp
</span><span class="cx"> * @param boolean $force_utc Force UTC timezone instead of using the timestamp's TZ?
</span><span class="cx"> * @return DateTime
</span><span class="cx"> */
</span><del>- protected function parse_date( $date, $force_utc = false ) {
</del><ins>+ public function parse_date( $date, $force_utc = false ) {
</ins><span class="cx"> // Default timezone to the server's current one
</span><span class="cx"> $timezone = self::get_timezone();
</span><span class="cx"> if ( $force_utc ) {
</span><span class="lines">@@ -1426,7 +562,7 @@
</span><span class="cx"> * @param boolean $force_utc Should we force UTC timestamp?
</span><span class="cx"> * @return array Local and UTC datetime strings, in MySQL datetime format (Y-m-d H:i:s)
</span><span class="cx"> */
</span><del>- protected function get_date_with_gmt( $date, $force_utc = false ) {
</del><ins>+ public function get_date_with_gmt( $date, $force_utc = false ) {
</ins><span class="cx"> $datetime = $this->parse_date( $date, $force_utc );
</span><span class="cx">
</span><span class="cx"> $datetime->setTimezone( self::get_timezone() );
</span><span class="lines">@@ -1450,7 +586,7 @@
</span><span class="cx"> * @param string $email Email address
</span><span class="cx"> * @return string <img> tag for the user's avatar
</span><span class="cx"> */
</span><del>- protected function get_avatar( $email ) {
</del><ins>+ public function get_avatar( $email ) {
</ins><span class="cx"> if ( ! get_option( 'show_avatars' ) )
</span><span class="cx"> return false;
</span><span class="cx">
</span><span class="lines">@@ -1475,119 +611,11 @@
</span><span class="cx"> }
</span><span class="cx">
</span><span class="cx"> /**
</span><del>- * Prepares comment data for returning as a JSON response.
- *
- * @param stdClass $comment Comment object
- * @param array $requested_fields Fields to retrieve from the comment
- * @param string $context Where is the comment being loaded?
- * @return array Comment data for JSON serialization
- */
- protected function prepare_comment( $comment, $requested_fields = array( 'comment', 'meta' ), $context = 'single' ) {
- $fields = array(
- 'ID' => (int) $comment->comment_ID,
- 'post' => (int) $comment->comment_post_ID,
- );
-
- // Content
- $fields['content'] = apply_filters( 'comment_text', $comment->comment_content, $comment );
- // $fields['content_raw'] = $comment->comment_content;
-
- // Status
- switch ( $comment->comment_approved ) {
- case 'hold':
- case '0':
- $fields['status'] = 'hold';
- break;
-
- case 'approve':
- case '1':
- $fields['status'] = 'approved';
- break;
-
- case 'spam':
- case 'trash':
- default:
- $fields['status'] = $comment->comment_approved;
- }
-
- // Type
- $fields['type'] = apply_filters( 'get_comment_type', $comment->comment_type );
- if ( empty( $fields['type'] ) ) {
- $fields['type'] = 'comment';
- }
-
- // Post
- if ( 'single' === $context ) {
- $parent_fields = array( 'meta' );
- if ( $context === 'single' )
- $parent_fields[] = 'post';
- $parent = get_post( $post['post_parent'], ARRAY_A );
- $fields['parent'] = $this->prepare_post( $parent, $parent_fields, 'single-parent' );
- }
-
- // Parent
- if ( ( 'single' === $context || 'single-parent' === $context ) && (int) $comment->comment_parent ) {
- $parent_fields = array( 'meta' );
- if ( $context === 'single' )
- $parent_fields[] = 'comment';
- $parent = get_comment( $post['post_parent'] );
- $fields['parent'] = $this->prepare_comment( $parent, $parent_fields, 'single-parent' );
- }
-
- // Parent
- $fields['parent'] = (int) $comment->comment_parent;
-
- // Author
- if ( (int) $comment->user_id !== 0 ) {
- $fields['author'] = $this->prepare_author( (int) $comment->user_id );
- }
- else {
- $fields['author'] = array(
- 'ID' => 0,
- 'name' => $comment->comment_author,
- 'URL' => $comment->comment_author_url,
- 'avatar' => $this->get_avatar( $comment->comment_author_email ),
- );
- }
-
- // Date
- $timezone = $this->get_timezone();
-
- $date = DateTime::createFromFormat( 'Y-m-d H:i:s', $comment->comment_date, $timezone );
- $fields['date'] = $date->format( 'c' );
- $fields['date_tz'] = $date->format( 'e' );
- $fields['date_gmt'] = date( 'c', strtotime( $post->comment_date_gmt ) );
-
- // Meta
- $meta = array(
- 'links' => array(
- 'in-reply-to' => json_url( sprintf( '/posts/%d', (int) $comment->comment_post_ID ) )
- ),
- );
- if ( 0 !== (int) $comment->comment_parent ) {
- $meta['links']['in-reply-to'] .= ',' . json_url( sprintf( '/posts/%d/comments/%d', (int) $comment->comment_post_ID, (int) $comment->comment_parent ) );
- }
- if ( 'single' !== $context ) {
- $meta['links']['self'] = json_url( sprintf( '/posts/%d/comments/%d', (int) $comment->comment_post_ID, (int) $comment->comment_ID ) );
- }
-
- // Remove unneeded fields
- $data = array();
- if ( in_array( 'comment', $requested_fields ) )
- $data = array_merge( $data, $fields );
-
- if ( in_array( 'meta', $requested_fields ) )
- $data = array_merge( $data, $meta );
-
- return $data;
- }
-
- /**
</del><span class="cx"> * Get the timezone object for the site
</span><span class="cx"> *
</span><span class="cx"> * @return DateTimeZone
</span><span class="cx"> */
</span><del>- protected function get_timezone() {
</del><ins>+ public function get_timezone() {
</ins><span class="cx"> static $zone = null;
</span><span class="cx"> if ($zone !== null)
</span><span class="cx"> return $zone;
</span></span></pre></div>
<a id="2013rmccuetrunkpluginphp"></a>
<div class="modfile"><h4>Modified: 2013/rmccue/trunk/plugin.php (2205 => 2206)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/rmccue/trunk/plugin.php 2013-08-08 02:14:10 UTC (rev 2205)
+++ 2013/rmccue/trunk/plugin.php 2013-08-08 02:45:32 UTC (rev 2206)
</span><span class="lines">@@ -30,6 +30,7 @@
</span><span class="cx"> include_once( ABSPATH . WPINC . '/class-IXR.php' );
</span><span class="cx"> include_once( ABSPATH . WPINC . '/class-wp-xmlrpc-server.php' );
</span><span class="cx"> include_once( dirname( __FILE__ ) . '/lib/class-wp-json-server.php' );
</span><ins>+ include_once( dirname( __FILE__ ) . '/lib/class-wp-json-posts.php' );
</ins><span class="cx">
</span><span class="cx"> /**
</span><span class="cx"> * Whether this is a XMLRPC Request
</span><span class="lines">@@ -46,6 +47,8 @@
</span><span class="cx"> */
</span><span class="cx"> define('JSON_REQUEST', true);
</span><span class="cx">
</span><ins>+ global $wp_json_server;
+
</ins><span class="cx"> // Allow for a plugin to insert a different class to handle requests.
</span><span class="cx"> $wp_json_server_class = apply_filters('wp_json_server_class', 'WP_JSON_Server');
</span><span class="cx"> $wp_json_server = new $wp_json_server_class;
</span></span></pre>
</div>
</div>
</body>
</html>