<!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>