<!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>[2337] 2013/rmccue/trunk/docs: Add guide documentation for using the API</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/2337">2337</a></dd>
<dt>Author</dt> <dd>rmccue</dd>
<dt>Date</dt> <dd>2013-09-21 09:04:26 +0000 (Sat, 21 Sep 2013)</dd>
</dl>
<h3>Log Message</h3>
<pre>Add guide documentation for using the API</pre>
<h3>Modified Paths</h3>
<ul>
<li><a href="#2013rmccuetrunkdocsroutesmd">2013/rmccue/trunk/docs/routes.md</a></li>
</ul>
<h3>Added Paths</h3>
<ul>
<li>2013/rmccue/trunk/docs/guides/</li>
<li><a href="#2013rmccuetrunkdocsguidesextendingmd">2013/rmccue/trunk/docs/guides/extending.md</a></li>
<li><a href="#2013rmccuetrunkdocsguidesgettingstartedmd">2013/rmccue/trunk/docs/guides/getting-started.md</a></li>
<li><a href="#2013rmccuetrunkdocsguidesworkingwithpostsmd">2013/rmccue/trunk/docs/guides/working-with-posts.md</a></li>
</ul>
</div>
<div id="patch">
<h3>Diff</h3>
<a id="2013rmccuetrunkdocsguidesextendingmd"></a>
<div class="addfile"><h4>Added: 2013/rmccue/trunk/docs/guides/extending.md (0 => 2337)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/rmccue/trunk/docs/guides/extending.md (rev 0)
+++ 2013/rmccue/trunk/docs/guides/extending.md 2013-09-21 09:04:26 UTC (rev 2337)
</span><span class="lines">@@ -0,0 +1,309 @@
</span><ins>+Extending the API
+=================
+
+If you're a plugin author, you're probably itching to start adding functionality
+to the API, including working with custom post types. I'm here to guide you
+through the process of extending the API with your data, and to remind you of
+some important points.
+
+
+Before We Start
+---------------
+This guide assumes that you have a fairly decent knowledge of the API already.
+Before starting, make sure you've read the [Getting Started][] guide, and if you
+want to work with custom post types, also read the [Working With Posts][] guide.
+
+You should also have a pretty good knowledge of working with actions and filters
+in WordPress, as well as how plugins work in general.
+
+[Getting Started]: getting-started.md
+[Working with Posts]: working-with-posts.md
+
+
+A Philosophy Lesson
+-------------------
+Right off the bat, let me just say that the REST API is not designed for every
+use under the sun. Following the mantra of [Decisions, not Options][decisions],
+the API is opinionated and designed for the most common use case. You may find
+that what you're trying to do doesn't fit in with the REST philosophy, and
+**that's fine**; the low-level `admin-ajax.php` handler is
+[still available][admin-ajax] for you to use in these scenarios, and you can
+always roll your own API. An example of an API which doesn't fit with this API
+is the heartbeat API; heartbeats aren't inherently designed around resources,
+but events, so are a poor fit for a REST API.
+
+That said, evaluate whether it's better to rethink your structure in terms of
+resources, or to roll your own API. In most cases, thinking about data in terms
+of resources rather than events or actions will not only fit better with the
+API, but also make for cleaner and more maintainable code. For example, although
+publishing a post is an action, it's equivalent to updating a post's status.
+
+Keep in mind that since the REST API is opinionated, you will have to work with
+the API's structure rather than rolling your own. The API is strict about how
+you talk to the client, and you should always use the built-in handlers rather
+than rolling your own. This strict inforcement of data handling helps to make
+the API more pluggable and extensible, plus ensures that you play nice with
+other plugins.
+
+[decisions]: http://wordpress.org/about/philosophy/
+[admin-ajax]: http://codex.wordpress.org/AJAX_in_Plugins
+
+
+Designing Your Entities
+-----------------------
+The first step you should take in working with the API is to think about the
+data that you're working with. You should always think about the data in terms
+of the clients that you're working with; it's your job to handle translation
+back and forth. While it might seem easier to just return internal objects
+directly, this may expose private data, or data that's harder to handle for
+clients; posts internally translate WordPress's date format (YYYY-MM-DD
+HH:MM:SS) to the standard RFC3339 which can be parsed by most date parsers. It's
+worth your time to consider issues like this which can save clients hours of
+work on their end.
+
+You should also consider how other plugins might extend your data, and design
+for this. While things like `comment_status` could become a boolean field,
+plugins might add custom statuses. On the other side of this, you should also
+consider how these fields should be handled by clients. Custom field values that
+a client doesn't understand should have a sensible default handling; for
+example, the `comment_status` field is documented as the following:
+
+> Providers MAY use statuses other than "open" or "closed" to indicate other
+> statuses. Consumers who encounter an unknown or missing comment status SHOULD
+> treat it as "closed".
+
+This ensures that while clients may not know what a field value means, they can
+still operate in a safe manner with the posts.
+
+
+Designing Your Routes and Endpoints
+-----------------------------------
+After you've decided what your entities should look like, it's time to think
+about how you want clients to access and interact with the data. Picking your
+routes is an important part of this, and should be oriented around your data.
+Collections of Entities should be a base route, with individual Entities as
+subroutes of this. The following is a good general rule:
+
+* `/<object>`:
+ * GET returns a collection
+ * POST adds a new item to the collection
+* `/<object>/<id>`:
+ * GET returns an entity
+ * PUT updates the entity
+ * DELETE deletes the entity
+
+(Again, this is the difference between routes and endpoints. There are two
+routes here, but with two and three endpoints respectively. In general, a route
+maps to an Entity/Collection, whereas an endpoint maps to an action on that
+Entity/Collection.)
+
+Since your data is in your plugin, you should also prefix your routes. For
+example, routes for Hello Dolly might look something like:
+
+* `/hello-dolly`: Get all lines from Hello Dolly
+* `/hello-dolly/<n>`: Get the nth line from Hello Dolly
+* `/hello-dolly/random`: Get a random line from Hello Dolly
+
+If you're extending an existing route, you might want to use a flat style of
+prefixing. Plugins like Akismet might implement something like:
+
+* `/posts/<id>/comments/akismet-recheck`: Recheck a post's comments for spam
+
+Note that this is somewhat against the previously discussed philosophy of
+organising around resources; sometimes, you might want to have actions
+available directly. These should always be a POST endpoint, since you're taking
+action on the server in response. You should also avoid this where possible,
+although there's no need to go overboard and create things like an Akismet Queue
+resource just for a single action.
+
+
+Organizing Your Endpoints
+-------------------------
+Now that you've got a roadmap for everything you'd like to implement, it's time
+to start implementing it.
+
+The recommended way to organize your endpoints is to group related endpoints
+together into a class, and extend the base classes built into the API.
+Post-related endpoints should look to extend the `WP_JSON_Posts` class, and use
+the same method naming as it, falling back to it for the post preparation and
+request handling.
+
+Along these lines, keep your methods named as generically as possible; while
+`MyPlugin_API_MyType::getMyTypeItems()` might seem like a good name, it makes it
+harder for other plugins to use; standardising on
+`MyPlugin_API_MyType::getPosts()` with similar arguments to the parent is a
+better idea and allows a nicer fall-through.
+
+You should also aim to keep these related endpoints modular, and make liberal
+use of filters for this. As an example of this, the built-in Posts API is
+filtered by the Media API to add featured image-related data, ensuring that
+there's no dependencies between the two and either can be enabled independently.
+
+
+Creating Your Endpoints
+-----------------------
+Before you actually write the code, there's some important things to know about
+how you write the code. (Remember the opinions I mentioned before? This
+is them.)
+
+Each endpoint should be written in the same way you'd write an internal function
+for your plugin. That is, they take parameters, and return data. They also
+*never* read directly from request globals (such as `$_GET` or `$_POST`), but
+instead read them via the parameters to your function. The REST API serving code
+automatically maps GET and POST parameters to your function's parameters (along
+with some special request-related parameters). This concept might seem a little
+strange at first, but it helps to ensure pluggability and standardisation of
+errors.
+
+For example, an endpoint that takes a required `context` parameter, an optional
+`type` parameter and uses the `X-WP-Example` header would look like this:
+
+ function getMyData( $context, $_headers, $type = 'my-default-value' ) {
+ if ( isset( $_headers['X-WP-EXAMPLE'] ) ) {
+ $my_header_value = $_headers['X-WP-EXAMPLE'];
+ }
+
+ if ( $type !== 'my-default-value' && $type !== 'some-other-value' ) {
+ return new WP_Error( 'myplugin_mydata_invalid_type', __( 'Invalid type.' ), array( 'status' => 400 ) );
+ }
+
+ // ...
+
+ return array( /* ... */ );
+ }
+
+Note that this is intentionally the same way you'd write a function for internal
+consumption; default parameter values are specified as default argument values,
+errors are returned as a WP_Error object (with the special `status` field in the
+data set to an appropriate HTTP status), and the data itself being returned from
+the function.
+
+The following special values are also available, and can be trusted (always
+internal data, not overridable by clients):
+
+* `_method`: The requested HTTP method (`GET`, `HEAD`, `POST`, `PUT`, `DELETE`)
+* `_route`: The route followed to your endpoint (`/posts/(?P<id>\d+)`)
+* `_path`: The actual path that matches your route (`/posts/1`)
+* `_headers`: An associative array of header names to values. Names are always
+ uppercased (HTTP header names are case-insensitive)
+* `_files`: An associative array of upload file data, in the same format
+ as `$_FILES`
+
+The special `data` parameter is also available if your endpoint is set to
+receive JSON or raw data. Note that this can be given by clients either via the
+HTTP body or via the `data` query parameter, which is intentional for backwards
+compatibility. That is, the following requests are the same:
+
+ Content-Type: application/json; charset=utf-8
+
+ {
+ "foo": "bar",
+ "hello": "dolly"
+ }
+
+ ----
+
+ Content-Type: application/x-www-form-urlencoded
+
+ data[foo]=bar&data[hello]=dolly
+
+The return value of your endpoints should be an error object with the status
+field set to an [appropriate value][http-status-codes], or the relevant data.
+The data itself can be any JSON-serializable value: an integer, string, array,
+object or null. In practice, this is almost always an array (for Collections) or
+an object (for Entities).
+
+Return headers can be set via the `WP_JSON_Server::header()` function, or
+similar helper functions (`WP_JSON_Server::send_status()`,
+`WP_JSON_Server::link_header()`, `WP_JSON_Server::query_navigation_headers()`)
+called on the global `$wp_json_server` object, but should *never* be set via the
+direct `header()` or `status_header()` functions.
+
+[http-status-codes]: http://en.wikipedia.org/wiki/List_of_HTTP_status_codes
+
+
+Registering Your Endpoints
+--------------------------
+Now that we've done the bulk of the work, it's time to tell WordPress that we
+have an API to register. If you're following the same structure as the API's
+built-in types, your registration code should look something like this:
+
+ function myplugin_api_init() {
+ global $myplugin_api_mytype;
+
+ $myplugin_api_mytype = new MyPlugin_API_MyType();
+ add_filter( 'json_endpoints', array( $myplugin_api_mytype, 'registerRoutes' ) );
+ }
+ add_action( 'plugins_loaded', 'myplugin_api_init' );
+
+ class MyPlugin_API_MyType {
+ public function registerRoutes( $routes ) {
+ $routes['/myplugin/mytypeitems'] = array(
+ array( array( $this, 'getPosts'), WP_JSON_Server::READABLE ),
+ array( array( $this, 'newPost'), WP_JSON_Server::CREATABLE | WP_JSON_Server::ACCEPT_JSON ),
+ );
+ $routes['/myplugin/mytypeitems/(?P<id>\d+)'] = array(
+ array( array( $this, 'getPost'), WP_JSON_Server::READABLE ),
+ array( array( $this, 'editPost'), WP_JSON_Server::EDITABLE | WP_JSON_Server::ACCEPT_JSON ),
+ array( array( $this, 'deletePost'), WP_JSON_Server::DELETABLE ),
+ );
+
+ return $routes;
+ }
+
+ // ...
+ }
+
+The data passed in and returned by the `json_endpoints` filter should be in the
+format of an array containing a map of route regular expression to endpoint
+lists. An endpoint list is a potentially unlimited array of endpoints' data.
+Endpoint data is a two-element array, with the first element being the endpoint
+callback, and the second element specifying options. In other words, the format
+is:
+
+ $routes['regex expression'] = array(
+ // one or more:
+ array(
+ 'callback_function',
+ 0 // bitwise options flag
+ )
+ );
+
+(Where possible, use named parameters of the form `(?P<name>...)` for variables
+in your route, as these will be automatically replaced with a more friendly
+version in the index.)
+
+The available bitwise options are:
+
+* `WP_JSON_Server::READABLE`: Read endpoint (responds to GET)
+* `WP_JSON_Server::CREATABLE`: Creation endpoint (responds to POST)
+* `WP_JSON_Server::EDITABLE`: Edit endpoint (responds to POST/PUT/PATCH)
+* `WP_JSON_Server::DELETABLE`: Deletion endpoint (responds to DELETE)
+* `WP_JSON_Server::ALLMETHODS`: Generic endpoint (responds to
+ GET/POST/PUT/PATCH/DELETE)
+* `WP_JSON_Server::ACCEPT_RAW`: Accepts raw data in the HTTP body
+* `WP_JSON_Server::ACCEPT_JSON`: Accepts JSON data in the HTTP body
+ (automatically deserialized by the server)
+* `WP_JSON_Server::HIDDEN_ENDPOINT`: Hide the endpoint from the index
+
+Once you've done this, you should now go and check that your route is appearing
+in the index. Whip out a HTTP client and test out that your API endpoints are
+working correctly.
+
+
+Next Steps
+----------
+You should now have a working API built with the REST API. Congratulations!
+You now have a mastery of how the API works, so get out there and start
+expanding your API. You should consider contributing back to the core API and
+improving the built-in APIs.
+
+* [API Philosophy][]: Learn more about the design of the API and the decisions
+ made in the core.
+* [Schema][]: Read up on the schema for the core API and use it to inspire your
+ own entity design.
+* [Internal Implementation][]: Learn about how the REST server works internally.
+
+[API Philosophy]: ../philosophy.md
+[Schema]: ../wp-json.md
+[Internal Implementation]: ../implementation.md
</ins><span class="cx">Property changes on: 2013/rmccue/trunk/docs/guides/extending.md
</span><span class="cx">___________________________________________________________________
</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="2013rmccuetrunkdocsguidesgettingstartedmd"></a>
<div class="addfile"><h4>Added: 2013/rmccue/trunk/docs/guides/getting-started.md (0 => 2337)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/rmccue/trunk/docs/guides/getting-started.md (rev 0)
+++ 2013/rmccue/trunk/docs/guides/getting-started.md 2013-09-21 09:04:26 UTC (rev 2337)
</span><span class="lines">@@ -0,0 +1,433 @@
</span><ins>+Getting Started
+===============
+
+Hi! If you're reading this, you're probably wondering how to get started using
+the REST API. In that case, you've come to the right place! I'm about to show
+you the basics of the API from the ground up.
+
+(If you're in the wrong place, there's not much I can do to help. Sorry!)
+
+
+Before We Start
+---------------
+To interact with the API, you're going to need some tools. The first of these
+you need is a HTTP client/library. These examples use command-line cURL, but any
+HTTP client would do here.
+
+The next tool you need is a JSON parser, and generator if you're sending data.
+I'm going to do this by hand for the command-line examples, but it's recommended
+to use a proper serializer/deserializer in your programming language of choice.
+That said, it's not a bad idea to know how to read and write JSON by hand.
+
+
+Checking for the API
+--------------------
+As our first command, let's go ahead and check the API index. The index tells us
+what routes are available, and a short summary about the site.
+
+The index is always available at the site's address with `/wp-json.php/` on the
+end. Note the trailing slash there; it indicates that we want to access the `/`
+route. My test site is set up at `http://example.com/`, so all my routes will
+start with `http://example.com/wp-json.php` followed by the route (which here is
+just `/`).
+
+Let's fire off the request:
+
+ curl -i http://example.com/wp-json.php/
+
+(By the way, `-i` tells cURL that we want to see the headers as well. I'll strip
+some irrelevant ones for this documentation.)
+
+And here's what we get back:
+
+ HTTP/1.1 200 OK
+ Content-Type: application/json; charset=UTF-8
+
+ {
+ "name": "My WordPress Site",
+ "description": "Just another WordPress site",
+ "URL": "http:\/\/example.com",
+ "routes": {
+ "\/": {
+ "supports": [
+ "HEAD",
+ "GET"
+ ],
+ "meta": {
+ "self": "http:\/\/example.com\/wp-json.php\/"
+ }
+ },
+ "\/posts": {
+ "supports": [
+ "HEAD",
+ "GET",
+ "POST"
+ ],
+ "meta": {
+ "self": "http:\/\/example.com\/wp-json.php\/posts"
+ },
+ "accepts_json": true
+ },
+ "\/posts\/<id>": {
+ "supports": [
+ "HEAD",
+ "GET",
+ "POST",
+ "PUT",
+ "PATCH",
+ "DELETE"
+ ],
+ "accepts_json": true
+ },
+ "\/posts\/<id>\/revisions": {
+ "supports": [
+ "HEAD",
+ "GET"
+ ]
+ },
+ "\/posts\/<id>\/comments": {
+ "supports": [
+ "HEAD",
+ "GET",
+ "POST"
+ ],
+ "accepts_json": true
+ },
+ "\/posts\/<id>\/comments\/<comment>": {
+ "supports": [
+ "HEAD",
+ "GET",
+ "POST",
+ "PUT",
+ "PATCH",
+ "DELETE"
+ ],
+ "accepts_json": true
+ },
+ "\/posts\/types": {
+ "supports": [
+ "HEAD",
+ "GET"
+ ],
+ "meta": {
+ "self": "http:\/\/example.com\/wp-json.php\/posts\/types"
+ }
+ },
+ "\/posts\/types\/<type>": {
+ "supports": [
+ "HEAD",
+ "GET"
+ ]
+ },
+ "\/posts\/statuses": {
+ "supports": [
+ "HEAD",
+ "GET"
+ ],
+ "meta": {
+ "self": "http:\/\/example.com\/wp-json.php\/posts\/statuses"
+ }
+ },
+ "\/taxonomies": {
+ "supports": [
+ "HEAD",
+ "GET"
+ ],
+ "meta": {
+ "self": "http:\/\/example.com\/wp-json.php\/taxonomies"
+ }
+ },
+ "\/taxonomies\/<taxonomy>": {
+ "supports": [
+ "HEAD",
+ "GET",
+ "POST",
+ "PUT",
+ "PATCH",
+ "DELETE"
+ ],
+ "accepts_json": true
+ },
+ "\/taxonomies\/<taxonomy>\/terms": {
+ "supports": [
+ "HEAD",
+ "GET",
+ "POST"
+ ],
+ "accepts_json": true
+ },
+ "\/taxonomies\/<taxonomy>\/terms\/<term>": {
+ "supports": [
+ "HEAD",
+ "GET",
+ "POST",
+ "PUT",
+ "PATCH",
+ "DELETE"
+ ],
+ "accepts_json": true
+ },
+ "\/users": {
+ "supports": [
+ "HEAD",
+ "GET",
+ "POST"
+ ],
+ "meta": {
+ "self": "http:\/\/example.com\/wp-json.php\/users"
+ },
+ "accepts_json": true
+ },
+ "\/users\/me": {
+ "supports": [
+ "HEAD",
+ "GET",
+ "POST",
+ "PUT",
+ "PATCH",
+ "DELETE"
+ ],
+ "meta": {
+ "self": "http:\/\/example.com\/wp-json.php\/users\/me"
+ }
+ },
+ "\/users\/<user>": {
+ "supports": [
+ "HEAD",
+ "GET",
+ "POST"
+ ],
+ "accepts_json": true
+ }
+ },
+ "meta": {
+ "links": {
+ "help": "https:\/\/github.com\/rmccue\/WP-API",
+ "profile": "https:\/\/raw.github.com\/rmccue\/WP-API\/master\/docs\/schema.json"
+ }
+ }
+ }
+
+Wow, that's a lot of data! Let's break it down.
+
+First, let's look at the headers. We get a 200 status code back, indicating that
+we can successfully get the index. Note that it's possible for sites to disable
+the API, which would give us a 404 error. You can check the returned body to
+find out if the API exists at all; a disabled site will give you a JSON object
+with the `json_disabled` error code, while a site without the API at all will
+probably give you an error from the theme.
+
+Next, we can see the `name`, `description` and `URL` fields. These are intended
+to be used if you want to show the site's name to users, such as in settings
+dialogs.
+
+The `routes` object contains the meat of the body. This object has a bunch of
+keys with "routes" (templates for creating URLs) pointing to objects containing
+data (this data tells you about the "endpoint"). Each of these objects has at
+least a `supports` key that contains a list of the HTTP methods supported. The
+`meta` key has a hyperlink to the route, while the `accepts_json` key will also
+be set and be `true` if you can POST/PUT JSON directly to the endpoint.
+
+You might notice that some of the routes have a `<something>` part in them. This
+is a variable part, and it tells you that you can replace it with something,
+such as an object ID.
+
+
+Routes vs Endpoints
+-------------------
+Quick note on terminology: a "route" is a URL that gets passed into the API;
+these look like `/posts` or `/posts/2` and can be written in a generic form as
+`/some/part/<variable_part>`. An "endpoint" on the other hand is the handler
+that actually does something with your request.
+
+For example, the `/posts` route has two endpoints: the Retrieve Posts endpoint
+to handle GET requests, and the Create Post endpoint to handle POST requests.
+
+
+Meta Objects
+------------
+You'll also see in the response that we have a `meta` object at the top level,
+plus a bunch of smaller ones for the routes. Meta objects are mappings of [link
+relations][] to their corresponding URL. These URLs are usually internal URLs to
+help you browse the API, and can be used by clients embracing the [HATEOAS][]
+methodology.
+
+[link relations]: http://www.iana.org/assignments/link-relations/link-relations.xhtml
+[HATEOAS]: http://en.wikipedia.org/wiki/HATEOAS
+
+Here's some common relations you'll see:
+
+* `self`: The canonical URL for the resource. This is usually most helpful in
+ embedded objects (post parent, author, etc)
+* `collection`: The URL for the collection containing the resource. The endpoint
+ at this route usually gives a list, of which, the current resource is a
+ member (for example, `/posts/1` would have a collection of `/posts`)
+* `archive`: The URL for the collection owned by this resource (for example,
+ posts written by an author)
+* `replies`: Responses to a resource (for example, comments on a post, replies
+ to a comment)
+
+
+Getting Posts
+-------------
+Now that we understand some of the basics, let's have a look at the posts route.
+All we need to do is send a GET request to the posts endpoint.
+
+ curl -i http://example.com/wp-json.php/posts
+
+And this time, we get (again trimming headers, you'll have more than this):
+
+ HTTP/1.1 200 OK
+ Last-Modified: Wed, 31 Oct 2012 18:26:17 GMT
+ Link: <http://example.com/wp-json.php/posts/1>; rel="item"; title="Hello world!"
+
+ [
+ {
+ "ID": 1,
+ "title": "Hello world!",
+ "status": "publish",
+ "type": "post",
+ "author": {
+ "ID": 1,
+ "name": "admin",
+ "slug": "admin",
+ "URL": "",
+ "avatar": "http:\/\/0.gravatar.com\/avatar\/c57c8945079831fa3c19caef02e44614&d=404&r=G",
+ "meta": {
+ "links": {
+ "self": "http:\/\/example.com\/wp-json.php\/users\/1",
+ "archives": "http:\/\/example.com\/wp-json.php\/users\/1\/posts"
+ }
+ }
+ },
+ "content": "Welcome to WordPress. This is your first post. Edit or delete it, then start blogging!",
+ "parent": 0,
+ "link": "http:\/\/example.com\/2012\/10\/31\/hello-world\/",
+ "date": "2012-10-31T18:26:17+10:00",
+ "modified": "2012-10-31T18:26:17+10:00",
+ "format": "standard",
+ "terms": {
+ "category": {
+ "ID": 1,
+ "name": "Uncategorized",
+ "slug": "uncategorized",
+ "group": 0,
+ "parent": 0,
+ "count": 1,
+ "meta": {
+ "links": {
+ "collection": "http:\/\/example.com\/wp-json.php\/taxonomy\/category",
+ "self": "http:\/\/example.com\/wp-json.php\/taxonomy\/category\/terms\/1"
+ }
+ }
+ }
+ },
+ "meta": {
+ "links": {
+ "self": "http:\/\/example.com\/wp-json.php\/posts\/1",
+ "author": "http:\/\/example.com\/wp-json.php\/users\/1",
+ "collection": "http:\/\/example.com\/wp-json.php\/posts",
+ "replies": "http:\/\/example.com\/wp-json.php\/posts\/1\/comments",
+ "version-history": "http:\/\/example.com\/wp-json.php\/posts\/1\/revisions"
+ }
+ }
+ }
+ ]
+
+Hopefully this looks fairly self-explanatory. For a full look at all the fields
+you can get back from this, take a look at [Working with Posts][] or the
+[definition][schema] for the Post entity.
+
+You'll notice that in this case we're getting a list back with just a single
+item, one Post. If you have two Posts here, you'll get back two, and so on, up
+to 10. If there are more than 10, pagination will kick in and you'll have to
+handle this in your client.
+
+Pagination details are given in the HTTP headers from the endpoint. The
+`X-WP-Total` header has the total number of posts available to you, while the
+`X-WP-TotalPages` header has the number of pages. While you can build the page
+URL manually, you should use the `next` and `prev` links from the `Link` header
+where possible.
+
+Example of pagination headers:
+
+ X-WP-Total: 492
+ X-WP-TotalPages: 50
+ Link: <http://example.com/wp-json.php/posts?page=4>; rel="next",
+ <http://example.com/wp-json.php/posts?page=2>; rel="prev"
+
+If you want to grab a single post, you can instead send a GET request to the
+post itself. You can grab the URL for this from the `meta.links.self` field, or
+construct it yourself (`/posts/<id>`):
+
+ curl -i http://example.com/wp-json.php/posts/1
+
+
+Editing and Creating Posts
+--------------------------
+Just as we can use a GET request to get a post, we can use PUT to edit a post.
+The easiest way to do this is to send a JSON body back to the server with just
+the fields you want to change. For example, to edit the title and the
+modification date:
+
+ {
+ "title": "Hello Updated World!",
+ "modified": "2013-04-01T14:00:00+10:00"
+ }
+
+Save the data as "updated-post.json", then we can send this to the server with
+the correct headers and authentication. The API uses HTTP Basic authentication:
+
+ curl --data-binary="@updated-post.json" \
+ -H "Content-Type: application/javascript" \
+ --user admin:password \
+ http://example.com/wp-json.php/posts/1
+
+And we should get back a 200 status code, indicating that the post has been
+updated, plus the updated Post in the body.
+
+Note that there are some fields we can't update; ID is an obvious example, but
+others like timezone fields cannot be updated either. Check the [schema][] for
+more details on this.
+
+Similarly to editing posts, you can create posts. We can use the same data from
+before, but this time, we POST it to the main posts route.
+
+ curl --data-binary="@updated-post.json" \
+ -H "Content-Type: application/javascript" \
+ --user admin:password \
+ http://example.com/wp-json.php/posts
+
+We should get a similar response to the editing endpoint, but this time we get
+a 201 Created status code, with a Location header telling us where to access the
+post in future:
+
+ HTTP/1.1 201 Created
+ Location: http://example.com/wp-json.php/posts/2
+
+Finally, we can clean this post up and delete it by sending a DELETE request:
+
+ curl -X DELETE --user admin:password http://example.com/wp-json.php/posts/2
+
+In general, routes follow the same pattern:
+
+* `/<object>`:
+ * GET returns a collection
+ * POST adds a new item to the collection
+* `/<object>/<id>`:
+ * GET returns an entity
+ * PUT updates the entity
+ * DELETE deletes the entity
+
+Next Steps
+----------
+You should now be able to understand the basics of accessing and creating data
+via the API, plus have the ability to apply this to posts. We've only really
+touched on the post-related data so far, and there's plenty more to the API, so
+get exploring!
+
+* [Working With Posts][]: Learn more about Posts and their data
+* [Schema][schema]: View technical information on all the available data
+
+[Working with Posts]: working-with-posts.md
+[schema]: ../wp-json.md
</ins><span class="cx">Property changes on: 2013/rmccue/trunk/docs/guides/getting-started.md
</span><span class="cx">___________________________________________________________________
</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="2013rmccuetrunkdocsguidesworkingwithpostsmd"></a>
<div class="addfile"><h4>Added: 2013/rmccue/trunk/docs/guides/working-with-posts.md (0 => 2337)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/rmccue/trunk/docs/guides/working-with-posts.md (rev 0)
+++ 2013/rmccue/trunk/docs/guides/working-with-posts.md 2013-09-21 09:04:26 UTC (rev 2337)
</span><span class="lines">@@ -0,0 +1,283 @@
</span><ins>+Working with Posts
+==================
+Back in the [Getting Started][] guide we used posts to demonstrate how to work
+with the API, but only touched on some of the details. Let's take a more
+detailed look at the Post API.
+
+
+Before We Start
+---------------
+This guide assumes you have a basic knowledge of the API, as well as the
+prerequisites noted in the [Getting Started] guide. If you haven't already, make
+sure you read that guide to ensure you know the basics.
+
+This guide also assumes that you know how to send requests given how to use
+them, so the examples will be HTTP requests. I recommend reading the cURL manual
+or using a higher level tool if you don't know how to wrangle cURL.
+
+The examples also pretend that your JSON base URL (`wp-json.php` in the main WP
+directory) is located at `/`, which is probably not the case. For example, if
+your base URL is `http://example.com/wp-json.php` and the example request is
+`GET /posts`, you should should actually send the following:
+
+ GET /wp-json.php/posts HTTP/1.1
+ Host: example.com
+
+Higher level HTTP clients can usually handle this for you.
+
+
+Post Entities and Collections
+-----------------------------
+As mentioned previously, we can send a GET request to the main posts route to
+get posts:
+
+ GET /posts HTTP/1.1
+
+This returns a list of Post objects. From a technical point of view, each object
+is called an Entity (in this case, Post Entities), while the endpoint returns a
+Collection (here, a Post Collection).
+
+Post Entities have certain [defined properties][schema], although which are set
+depend on the context. For example, with embedded entities certain properties
+may have different values; while the post parent entity is embedded, the parent
+field inside this is only the ID.
+
+
+Collection Views
+----------------
+Post Collections can be modified by using various query string parameters. One
+that you may already know is the `page` parameter. This parameter is used for
+pagination, and while you can set it manually, it's also sent via the Link
+headers to point you to the next and previous pages. A `context` parameter is
+also available that we'll talk about later.
+
+You can also change the post type via the `type` parameter. It's recommended
+that you check the [available post types](#custom-post-types) before doing this,
+as plugins may change what's available.
+
+The last parameter is the `filter` parameter. This gives you full access to the
+[WP_Query][] parameters, to alter the query to your liking. Depending on the
+level of access you have, not all parameters will be available, so check the
+[schema][] for the available parameters. A good assumption to make is that
+anything you can put in a query on the site itself (such as `?s=...` for
+searches) will be available.
+
+
+Creating and Editing Posts
+--------------------------
+The main posts route also has a creation endpoint:
+
+ POST /posts HTTP/1.1
+ Content-Type: application/json; charset=utf-8
+
+ {
+ "title": "My Post Title"
+ }
+
+Here we send a Post entity that we've created locally to the server. These Post
+entities are the same as Post entities served up by the server, but excluding
+some fields which are immutable. For example, the ID and timezone fields cannot
+be changed.
+
+When editing posts, it's helpful to pass the `edit` context along with your
+request. This gives you extra data useful for editing, such as the unfiltered
+content and title. This is not required, however it is recommended, as the
+normal content and title fields have been filtered to correct HTML (such as via
+the `wpautop` function), making them less than optimal for editing.
+
+You can also pass an `If-Unmodified-Since` header when editing posts with the
+previous modification date to ensure that you don't accidentally overwrite edits
+made in the meantime. Note that while dates in the `modified` field of the post
+are in [RFC3339][] format, this header requires the use of [RFC1123][] (similar
+to [RFC822][]). (Sorry about that, but the HTTP standard requires it.)
+
+[RFC3339]: http://tools.ietf.org/html/rfc3339
+[RFC1123]: http://tools.ietf.org/html/rfc1123
+[RFC822]: http://tools.ietf.org/html/rfc822
+
+
+Custom Post Types
+-----------------
+Custom post types can be queried via the main post routes (`/posts` and
+children) when they have been made public. The type can be set via the `type`
+query parameter.
+
+Before working with custom post types, you should first check that the API has
+support for the post type that you want to work with. This data is available via
+the read-only APIs at `/posts/types`:
+
+ GET /posts/types HTTP/1.1
+
+This should return a list of the available types:
+
+ {
+ "post": {
+ "name": "Posts",
+ "slug": "post",
+ "description": "",
+ "labels": {
+ "name": "Posts",
+ "singular_name": "Post",
+ "add_new": "Add New",
+ "add_new_item": "Add New Post",
+ "edit_item": "Edit Post",
+ "new_item": "New Post",
+ "view_item": "View Post",
+ "search_items": "Search Posts",
+ "not_found": "No posts found.",
+ "not_found_in_trash": "No posts found in Trash.",
+ "parent_item_colon": null,
+ "all_items": "All Posts",
+ "menu_name": "Posts",
+ "name_admin_bar": "Post"
+ },
+ "queryable": true,
+ "searchable": true,
+ "hierarchical": false,
+ "meta": {
+ "links": {
+ "self": "http:\/\/example.com\/wp-json.php\/posts\/types\/post",
+ "archives": "http:\/\/example.com\/wp-json.php\/posts"
+ }
+ }
+ },
+ "page": {
+ "name": "Pages",
+ "slug": "page",
+ "description": "",
+ "labels": {
+ "name": "Pages",
+ "singular_name": "Page",
+ "add_new": "Add New",
+ "add_new_item": "Add New Page",
+ "edit_item": "Edit Page",
+ "new_item": "New Page",
+ "view_item": "View Page",
+ "search_items": "Search Pages",
+ "not_found": "No pages found.",
+ "not_found_in_trash": "No pages found in Trash.",
+ "parent_item_colon": "Parent Page:",
+ "all_items": "All Pages",
+ "menu_name": "Pages",
+ "name_admin_bar": "Page"
+ },
+ "queryable": false,
+ "searchable": true,
+ "hierarchical": true,
+ "meta": {
+ "links": {
+ "self": "http:\/\/example.com\/wp-json.php\/posts\/types\/page"
+ }
+ }
+ },
+ "attachment": {
+ "name": "Media",
+ "slug": "attachment",
+ "description": "",
+ "labels": {
+ "name": "Media",
+ "singular_name": "Media",
+ "add_new": "Add New",
+ "add_new_item": "Add New Post",
+ "edit_item": "Edit Media",
+ "new_item": "New Post",
+ "view_item": "View Attachment Page",
+ "search_items": "Search Posts",
+ "not_found": "No posts found.",
+ "not_found_in_trash": "No posts found in Trash.",
+ "parent_item_colon": null,
+ "all_items": "Media",
+ "menu_name": "Media",
+ "name_admin_bar": "Media"
+ },
+ "queryable": true,
+ "searchable": true,
+ "hierarchical": false,
+ "meta": {
+ "links": {
+ "self": "http:\/\/example.com\/wp-json.php\/posts\/types\/attachment",
+ "archives": "http:\/\/example.com\/wp-json.php\/posts?type=attachment"
+ }
+ }
+ }
+ }
+
+The `meta.links.archives` value gives a nicer way to access given post type
+archives for HATEOAS supporters and should always be used rather than manually
+setting the `type` parameter directly, as CPTs may create their own route
+structure instead. The `labels` fields should also always be used when
+displaying the field, as these are run through WordPress' translations.
+
+A similar API exists for post statuses at `/posts/statuses`:
+
+ {
+ "publish": {
+ "name": "Published",
+ "slug": "publish",
+ "public": true,
+ "protected": false,
+ "private": false,
+ "queryable": true,
+ "show_in_list": true,
+ "meta": {
+ "archives": "http:\/\/example.com\/wp-json.php\/posts"
+ }
+ },
+ "future": {
+ "name": "Scheduled",
+ "slug": "future",
+ "public": false,
+ "protected": true,
+ "private": false,
+ "queryable": false,
+ "show_in_list": true,
+ "meta": [
+ ]
+ },
+ "draft": {
+ "name": "Draft",
+ "slug": "draft",
+ "public": false,
+ "protected": true,
+ "private": false,
+ "queryable": false,
+ "show_in_list": true,
+ "meta": [
+ ]
+ },
+ "pending": {
+ "name": "Pending",
+ "slug": "pending",
+ "public": false,
+ "protected": true,
+ "private": false,
+ "queryable": false,
+ "show_in_list": true,
+ "meta": [
+ ]
+ },
+ "private": {
+ "name": "Private",
+ "slug": "private",
+ "public": false,
+ "protected": false,
+ "private": true,
+ "queryable": false,
+ "show_in_list": true,
+ "meta": [
+ ]
+ }
+ }
+
+
+Next Steps
+----------
+You should now understand more advanced usage of the post-related APIs, and be
+able to implement a fully compliant client for the API. You might now want to
+take a look at the other APIs, or look at documentation on the specifics.
+
+* [Schema][schema]: Full documentation of every parameter for the APIs.
+
+[Getting Started]: getting-started.md
+[schema]: ../wp-json.md
+[WP_Query]: http://codex.wordpress.org/Class_Reference/WP_Query
</ins><span class="cx">Property changes on: 2013/rmccue/trunk/docs/guides/working-with-posts.md
</span><span class="cx">___________________________________________________________________
</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="2013rmccuetrunkdocsroutesmd"></a>
<div class="modfile"><h4>Modified: 2013/rmccue/trunk/docs/routes.md (2336 => 2337)</h4>
<pre class="diff"><span>
<span class="info">--- 2013/rmccue/trunk/docs/routes.md 2013-09-21 06:36:15 UTC (rev 2336)
+++ 2013/rmccue/trunk/docs/routes.md 2013-09-21 09:04:26 UTC (rev 2337)
</span><span class="lines">@@ -1,8 +1,5 @@
</span><del>-Routes
-======
-
</del><span class="cx"> Notes
</span><del>------
</del><ins>+=====
</ins><span class="cx">
</span><span class="cx"> ### Inputting data as an "array"
</span><span class="cx"> Endpoints may allow passing in an array of data, typically used for querying
</span></span></pre>
</div>
</div>
</body>
</html>