From ece340cb496d812af3a51601883beb1867f5c40c Mon Sep 17 00:00:00 2001 From: Karl Monaghan Date: Wed, 3 Jul 2013 10:09:57 +0100 Subject: [PATCH 1/9] Update to 1.0.8 --- controllers/core.php | 20 +- controllers/widgets.php | 108 ++++ json-api.php | 4 +- models/attachment.php | 18 +- models/post.php | 46 +- readme.txt | 115 ++-- singletons/api.php | 33 +- singletons/introspector.php | 31 +- singletons/query.php | 2 +- tests/core.get_author_index-01.phpt | 11 +- tests/core.get_author_posts-01.phpt | 14 +- tests/core.get_author_posts-02.phpt | 14 +- tests/core.get_author_posts-03.phpt | 14 +- tests/core.get_author_posts-04.phpt | 14 - tests/core.get_category_index-01.phpt | 11 +- tests/core.get_category_posts-01.phpt | 13 +- tests/core.get_date_index-01.phpt | 70 ++- tests/core.get_date_posts-01.phpt | 13 +- tests/core.get_date_posts-02.phpt | 13 +- tests/core.get_date_posts-03.phpt | 13 +- tests/core.get_page-01.phpt | 9 +- tests/core.get_page-02.phpt | 15 +- tests/core.get_post-01.phpt | 10 +- tests/core.get_posts-01.phpt | 21 + tests/core.get_posts-02.phpt | 24 + tests/core.get_posts-03.phpt | 23 + tests/core.get_recent_posts-01.phpt | 11 +- tests/core.get_recent_posts-02.phpt | 11 +- tests/core.get_recent_posts-03.phpt | 11 +- tests/core.get_search_posts-01.phpt | 13 +- tests/core.get_tag_index-01.phpt | 793 +++++++++++++++++++++++++- tests/core.get_tag_posts-01.phpt | 13 +- tests/core.info-01.phpt | 21 +- tests/core.info-02.phpt | 47 +- tests/query-01.phpt | 11 +- 35 files changed, 1479 insertions(+), 131 deletions(-) create mode 100644 controllers/widgets.php delete mode 100644 tests/core.get_author_posts-04.phpt create mode 100644 tests/core.get_posts-01.phpt create mode 100644 tests/core.get_posts-02.phpt create mode 100644 tests/core.get_posts-03.phpt diff --git a/controllers/core.php b/controllers/core.php index a3dda64..85285e4 100644 --- a/controllers/core.php +++ b/controllers/core.php @@ -42,6 +42,22 @@ public function get_recent_posts() { return $this->posts_result($posts); } + public function get_posts() { + global $json_api; + $url = parse_url($_SERVER['REQUEST_URI']); + $defaults = array( + 'ignore_sticky_posts' => true + ); + $query = wp_parse_args($url['query']); + unset($query['json']); + unset($query['post_status']); + $query = array_merge($defaults, $query); + $posts = $json_api->introspector->get_posts($query); + $result = $this->posts_result($posts); + $result['query'] = $query; + return $result; + } + public function get_post() { global $json_api, $post; extract($json_api->query->get(array('id', 'slug', 'post_id', 'post_slug'))); @@ -239,10 +255,12 @@ public function get_author_index() { public function get_page_index() { global $json_api; $pages = array(); + $post_type = $json_api->query->post_type ? $json_api->query->post_type : 'page'; + // Thanks to blinder for the fix! $numberposts = empty($json_api->query->count) ? -1 : $json_api->query->count; $wp_posts = get_posts(array( - 'post_type' => 'page', + 'post_type' => $post_type, 'post_parent' => 0, 'order' => 'ASC', 'orderby' => 'menu_order', diff --git a/controllers/widgets.php b/controllers/widgets.php new file mode 100644 index 0000000..372c5bf --- /dev/null +++ b/controllers/widgets.php @@ -0,0 +1,108 @@ +error("No sidebar specified. Include 'sidebar_id' var in your request."); + } else if (!is_active_sidebar($index)) { + $json_api->error("Sidebar '$index' is not active."); + } + + $widget_params = array( + 'before_widget', + 'after_widget', + 'before_title', + 'after_title' + ); + $json_api_params = array(); + foreach ($widget_params as $param) { + if (isset($_REQUEST[$param])) { + $json_api_params[$param] = $_REQUEST[$param]; + } + } + + $widgets = array(); + + global $wp_registered_sidebars, $wp_registered_widgets; + + if ( is_int($index) ) { + $index = "sidebar-$index"; + } else { + $index = sanitize_title($index); + foreach ( (array) $wp_registered_sidebars as $key => $value ) { + if ( sanitize_title($value['name']) == $index ) { + $index = $key; + break; + } + } + } + + $sidebars_widgets = wp_get_sidebars_widgets(); + + if ( empty($wp_registered_sidebars[$index]) || !array_key_exists($index, $sidebars_widgets) || !is_array($sidebars_widgets[$index]) || empty($sidebars_widgets[$index]) ) + return false; + + $sidebar = $wp_registered_sidebars[$index]; + + $did_one = false; + foreach ( (array) $sidebars_widgets[$index] as $id ) { + + if ( !isset($wp_registered_widgets[$id]) ) continue; + + $params = array_merge( + array( array_merge( $sidebar, array('widget_id' => $id, 'widget_name' => $wp_registered_widgets[$id]['name']), $json_api_params ) ), + (array) $wp_registered_widgets[$id]['params'] + ); + + + // Substitute HTML id and class attributes into before_widget + $classname_ = ''; + foreach ( (array) $wp_registered_widgets[$id]['classname'] as $cn ) { + if ( is_string($cn) ) + $classname_ .= '_' . $cn; + elseif ( is_object($cn) ) + $classname_ .= '_' . get_class($cn); + } + $classname_ = ltrim($classname_, '_'); + $params[0]['before_widget'] = sprintf($params[0]['before_widget'], $id, $classname_); + + $params = apply_filters( 'dynamic_sidebar_params', $params ); + + $callback = $wp_registered_widgets[$id]['callback']; + + do_action( 'dynamic_sidebar', $wp_registered_widgets[$id] ); + + if ( is_callable($callback) ) { + ob_start(); + $object = $callback[0]; + $settings = $object->get_settings(); + $widget_params = $wp_registered_widgets[$id]['params']; + $number = $widget_params[0]['number']; + $instance = $settings[$number]; + call_user_func_array($callback, $params); + $widgets[] = array( + 'id' => $id, + 'widget' => trim(ob_get_contents()), + 'params' => $params[0], + 'instance' => $instance + ); + ob_end_clean(); + } + } + + return array( + 'sidebar_id' => $index, + 'widgets' => $widgets + ); + } + +} + +?> diff --git a/json-api.php b/json-api.php index 5249ad4..1eec2a8 100644 --- a/json-api.php +++ b/json-api.php @@ -1,9 +1,9 @@ images = array(); + $home = get_bloginfo('home'); foreach ($sizes as $size) { list($url, $width, $height) = wp_get_attachment_image_src($this->id, $size); - $this->images[$size] = (object) array( - 'url' => $url, - 'width' => $width, - 'height' => $height - ); + $filename = ABSPATH . substr($url, strlen($home) + 1); + if (file_exists($filename)) { + list($measured_width, $measured_height) = getimagesize($filename); + if ($measured_width == $width && + $measured_height == $height) { + $this->images[$size] = (object) array( + 'url' => $url, + 'width' => $width, + 'height' => $height + ); + } + } } } diff --git a/models/post.php b/models/post.php index 4a8c492..b215e87 100644 --- a/models/post.php +++ b/models/post.php @@ -30,6 +30,7 @@ function JSON_API_Post($wp_post = null) { if (!empty($wp_post)) { $this->import_wp_object($wp_post); } + do_action("json_api_{$this->type}_constructor", $this); } function create($values = null) { @@ -136,7 +137,7 @@ function import_wp_object($wp_post) { $this->set_value('title', get_the_title($this->id)); $this->set_value('title_plain', strip_tags(@$this->title)); $this->set_content_value(); - $this->set_value('excerpt', get_the_excerpt()); + $this->set_value('excerpt', apply_filters('the_excerpt', get_the_excerpt())); $this->set_value('date', get_the_time($date_format)); $this->set_value('modified', date($date_format, strtotime($wp_post->post_modified))); $this->set_categories_value(); @@ -148,6 +149,8 @@ function import_wp_object($wp_post) { $this->set_value('comment_status', $wp_post->comment_status); $this->set_thumbnail_value(); $this->set_custom_fields_value(); + $this->set_custom_taxonomies($wp_post->post_type); + do_action("json_api_import_wp_post", $this, $wp_post); } function set_value($key, $value) { @@ -244,19 +247,27 @@ function set_thumbnail_value() { return; } $thumbnail_size = $this->get_thumbnail_size(); - list($thumbnail) = wp_get_attachment_image_src($attachment_id, $thumbnail_size); - $this->thumbnail = $thumbnail; + $this->thumbnail_size = $thumbnail_size; + $attachment = $json_api->introspector->get_attachment($attachment_id); + $image = $attachment->images[$thumbnail_size]; + $this->thumbnail = $image->url; + $this->thumbnail_images = $attachment->images; } function set_custom_fields_value() { global $json_api; - if ($json_api->include_value('custom_fields') && - $json_api->query->custom_fields) { - $keys = explode(',', $json_api->query->custom_fields); + if ($json_api->include_value('custom_fields')) { $wp_custom_fields = get_post_custom($this->id); $this->custom_fields = new stdClass(); - foreach ($keys as $key) { - if (isset($wp_custom_fields[$key])) { + if ($json_api->query->custom_fields) { + $keys = explode(',', $json_api->query->custom_fields); + } + foreach ($wp_custom_fields as $key => $value) { + if ($json_api->query->custom_fields) { + if (in_array($key, $keys)) { + $this->custom_fields->$key = $wp_custom_fields[$key]; + } + } else if (substr($key, 0, 1) != '_') { $this->custom_fields->$key = $wp_custom_fields[$key]; } } @@ -265,6 +276,25 @@ function set_custom_fields_value() { } } + function set_custom_taxonomies($type) { + global $json_api; + $taxonomies = get_taxonomies(array( + 'object_type' => array($type), + 'public' => true, + '_builtin' => false + )); + foreach ($taxonomies as $taxonomy) { + if ($json_api->include_value($taxonomy)) { + $terms = get_the_terms($this->id, $taxonomy); + if (!empty($terms)) { + $this->$taxonomy = array_values($terms); + } else { + $this->$taxonomy = array(); + } + } + } + } + function get_thumbnail_size() { global $json_api; if ($json_api->query->thumbnail_size) { diff --git a/readme.txt b/readme.txt index 4b1677f..6184058 100644 --- a/readme.txt +++ b/readme.txt @@ -3,8 +3,8 @@ Contributors: dphiffer Donate link: https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=DH4MEG99JR2WE Tags: json, api, ajax, cms, admin, integration, moma Requires at least: 2.8 -Tested up to: 3.1 -Stable tag: 1.0.7 +Tested up to: 3.5.1 +Stable tag: 1.0.8 A RESTful API for WordPress @@ -31,32 +31,33 @@ See the [Other Notes](http://wordpress.org/extend/plugins/json-api/other_notes/) == Documentation == -1. General concepts - 1.1. Requests - 1.2. Controllers - 1.3. Responses -2. Request methods - 2.1. Core controller methods - 2.2. Posts controller methods - 2.3. Respond controller methods -3. Request arguments - 3.1. Output-modifying arguments - 3.2. Content-modifying arguments - 3.3. Using include/exclude and redirects -4. Response objects - 4.1. Post response object - 4.2. Category response object - 4.3. Tag response object - 4.4. Author response object - 4.4. Comment response object - 4.5. Attachment response object -5. Extending JSON API - 5.1. Plugin hooks - 5.2. Developing JSON API controllers - 5.3. Configuration options -6. Unit tests - 6.1. Preparing a WordPress test site - 6.2. Running the tests +1. [General concepts](#1.-General-Concepts) + 1.1. [Requests](#1.1.-Requests) + 1.2. [Controllers](#1.2.-Controllers) + 1.3. [Responses](#1.3.-Responses) +2. [Request methods](#2.-Request-methods) + 2.1. [Core controller methods](#2.1.-Core-controller-methods) + 2.2. [Posts controller methods](#2.2.-Pages-controller-methods) + 2.3. [Respond controller methods](#2.3.-Respond-controller-methods) + 2.4. [Widgets controller methods](#2.4.-Widgets-controller-methods) +3. [Request arguments](#3.-Request-arguments) + 3.1. [Output-modifying arguments](#3.1.-Output-modifying-arguments) + 3.2. [Content-modifying arguments](#3.2.-Content-modifying-arguments) + 3.3. [Using include/exclude and redirects](#3.3.-Using-include/exclude-and-redirects) +4. [Response objects](#4.-Response-objects) + 4.1. [Post response object](#4.1.-Post-response-object) + 4.2. [Category response object](#4.2.-Category-response-object) + 4.3. [Tag response object](#4.3.-Tag-response-object) + 4.4. [Author response object](#4.4.-Author-response-object) + 4.5. [Comment response object](#4.5.-Comment-response-object) + 4.6. [Attachment response object](#4.6.-Attachment-response-object) +5. [Extending JSON API](#5.-Extending-JSON-API) + 5.1. [Plugin hooks](#5.1.-Plugin-hooks) + 5.2. [Developing JSON API controllers](#5.2.-Developing-JSON-API-controllers) + 5.3. [Configuration options](#5.3.-Configuration-options) +6. [Unit tests](#6.-Unit-tests) + 6.1. [Preparing a WordPress test site](#6.1.-Preparing-a-WordPress-test-site) + 6.2. [Running the tests](#6.2.-Running-the-tests) == 1. General Concepts == @@ -175,6 +176,7 @@ Request methods are available from the following controllers: * Core controller - basic introspection methods * Posts controller - data manipulation methods for posts * Respond controller - comment/trackback submission methods +* Widgets controller - retrieve sidebar widgets == 2.1. Core controller methods == @@ -189,7 +191,7 @@ Returns information about JSON API. * `controller` - returns detailed information about a specific controller -= Response = += Response to `?json=core.info` = { "status": "ok", @@ -199,8 +201,8 @@ Returns information about JSON API. ] } - -= Response = + += Response to `?json=core.info&controller=core` = { "status": "ok", @@ -222,7 +224,7 @@ Returns an array of recent posts. You can invoke this from the WordPress home pa * `page` - return a specific page number from the results * `post_type` - used to retrieve custom post types -= Response = += Response to `?json=core.get_recent_posts` = { "status": "ok", @@ -235,7 +237,31 @@ Returns an array of recent posts. You can invoke this from the WordPress home pa ... ] } + + +== Method: get_posts == +Returns posts according to WordPress's [`WP_Query` parameters](http://codex.wordpress.org/Class_Reference/WP_Query#Parameters). The one default parameter is `ignore_sticky_posts=1` (this can be overridden). + += Optional arguments = + +* `count` - determines how many posts per page are returned (default value is 10) +* `page` - return a specific page number from the results +* `post_type` - used to retrieve custom post types + +__Further reading__ +See the [`WP_Query` documentation](http://codex.wordpress.org/Class_Reference/WP_Query#Parameters) for a full list of supported parameters. The `post_status` parameter is currently ignored. + += Response to `?json=get_posts&meta_key=enclosure` = + + { + "status": "ok", + "count": 1, + "posts": [ + { ... } + ] + } + == Method: get_post == @@ -593,6 +619,16 @@ Submits a comment to a WordPress post. * `pending` - assigned if the comment submission is pending moderation +== 2.4. Widgets controller methods == + +== Method: get_sidebar == + +Retrieves widgets assigned to a sidebar. + += Required arguments = + +* `sidebar_id` - the name or number of the sidebar to retrieve + == 3. Request arguments == @@ -934,7 +970,7 @@ There are a few necessary steps that need to be carried out before the test suit 2. Configure and install a new copy of WordPress 3. Delete the Hello World post and Sample Page (titled "About" in some versions of WordPress) 4. Enable user-friendly URLs from Settings > Permalinks, use the "Day and name" format -5. Install the JSON API plugin and enable all bundled controllers from Settings > JSON API +5. Install + Activate the JSON API plugin and enable all bundled controllers from Settings > JSON API 6. Import the [Theme Unit Test](http://codex.wordpress.org/Theme_Unit_Test) test data XML file from Settings > Import > WordPress (you will need to install the WordPress Importer plugin) == 6.2. Running the tests == @@ -955,6 +991,16 @@ You should see the test results print out culminating in a summary: == Changelog == += 1.0.8 (2013-06-12): = +* Added `widgets` controller +* Added a generic `get_posts` method to the core controller +* Added a `thumbnail_images` object property to complement `thumbnail` URL +* Attachment image files are now checked to exist and match the expected width/height +* Fixed a bug where `the_excerpt` filter wasn't being applied to the `excerpt` property +* Fixed a bug where the number of child pages was being limited to 5 +* Fixed a bug where custom controller class names couldn't include numerics +* Theme directory check for custom controllers + = 1.0.7 (2011-01-27): = * Created some basic unit tests * Fixed a bug where `get_author_posts` was unable to find users by `slug` @@ -1062,6 +1108,9 @@ You should see the test results print out culminating in a summary: == Upgrade Notice == += 1.0.8 = +Long overdue bugfix/improvement release + = 1.0.7 = Minor bugfix/improvement release diff --git a/singletons/api.php b/singletons/api.php index baeb506..0e75e2e 100644 --- a/singletons/api.php +++ b/singletons/api.php @@ -43,6 +43,7 @@ function template_redirect() { $this->response->setup(); // Run action hooks for method + do_action("json_api", $controller, $method); do_action("json_api-{$controller}-$method"); // Error out if nothing is found @@ -275,14 +276,22 @@ function save_option($id, $value) { function get_controllers() { $controllers = array(); $dir = json_api_dir(); - $dh = opendir("$dir/controllers"); + $this->check_directory_for_controllers("$dir/controllers", $controllers); + $this->check_directory_for_controllers(get_stylesheet_directory(), $controllers); + $controllers = apply_filters('json_api_controllers', $controllers); + return array_map('strtolower', $controllers); + } + + function check_directory_for_controllers($dir, &$controllers) { + $dh = opendir($dir); while ($file = readdir($dh)) { - if (preg_match('/(.+)\.php$/', $file, $matches)) { - $controllers[] = $matches[1]; + if (preg_match('/(.+)\.php$/i', $file, $matches)) { + $src = file_get_contents("$dir/$file"); + if (preg_match("/class\s+JSON_API_{$matches[1]}_Controller/i", $src)) { + $controllers[] = $matches[1]; + } } } - $controllers = apply_filters('json_api_controllers', $controllers); - return array_map('strtolower', $controllers); } function controller_is_active($controller) { @@ -340,9 +349,19 @@ function controller_class($controller) { } function controller_path($controller) { - $dir = json_api_dir(); + $json_api_dir = json_api_dir(); + $json_api_path = "$json_api_dir/controllers/$controller.php"; + $theme_dir = get_stylesheet_directory(); + $theme_path = "$theme_dir/$controller.php"; + if (file_exists($theme_path)) { + $path = $theme_path; + } else if (file_exists($json_api_path)) { + $path = $json_api_path; + } else { + $path = null; + } $controller_class = $this->controller_class($controller); - return apply_filters("{$controller_class}_path", "$dir/controllers/$controller.php"); + return apply_filters("{$controller_class}_path", $path); } function get_nonce_id($controller, $method) { diff --git a/singletons/introspector.php b/singletons/introspector.php index 35fc63a..b77a0a0 100644 --- a/singletons/introspector.php +++ b/singletons/introspector.php @@ -3,16 +3,17 @@ class JSON_API_Introspector { public function get_posts($query = false, $wp_posts = false) { - global $post; + global $post, $wp_query; $this->set_posts_query($query); $output = array(); while (have_posts()) { the_post(); if ($wp_posts) { - $output[] = $post; + $new_post = $post; } else { - $output[] = new JSON_API_Post($post); + $new_post = new JSON_API_Post($post); } + $output[] = $new_post; } return $output; } @@ -215,7 +216,8 @@ public function get_attachments($post_id) { 'post_type' => 'attachment', 'post_parent' => $post_id, 'orderby' => 'menu_order', - 'order' => 'ASC' + 'order' => 'ASC', + 'suppress_filters' => false )); $attachments = array(); if (!empty($wp_attachments)) { @@ -226,16 +228,32 @@ public function get_attachments($post_id) { return $attachments; } + public function get_attachment($attachment_id) { + global $wpdb; + $wp_attachment = $wpdb->get_row( + $wpdb->prepare(" + SELECT * + FROM $wpdb->posts + WHERE ID = %d + ", $attachment_id) + ); + return new JSON_API_Attachment($wp_attachment); + } + public function attach_child_posts(&$post) { $post->children = array(); $wp_children = get_posts(array( 'post_type' => $post->type, 'post_parent' => $post->id, 'order' => 'ASC', - 'orderby' => 'menu_order' + 'orderby' => 'menu_order', + 'numberposts' => -1, + 'suppress_filters' => false )); foreach ($wp_children as $wp_post) { - $post->children[] = new JSON_API_Post($wp_post); + $new_post = new JSON_API_Post($wp_post); + $new_post->parent = $post->id; + $post->children[] = $new_post; } foreach ($post->children as $child) { $this->attach_child_posts($child); @@ -291,6 +309,7 @@ protected function set_posts_query($query = false) { if (!empty($query)) { query_posts($query); + do_action('json_api_query', $wp_query); } } diff --git a/singletons/query.php b/singletons/query.php index f01e906..a45059c 100644 --- a/singletons/query.php +++ b/singletons/query.php @@ -106,7 +106,7 @@ function get_controller() { } if (preg_match('/^[a-zA-Z_]+$/', $json)) { return $this->get_legacy_controller($json); - } else if (preg_match('/^([a-zA-Z_]+)(\/|\.)[a-zA-Z_]+$/', $json, $matches)) { + } else if (preg_match('/^([a-zA-Z0-9_]+)(\/|\.)[a-zA-Z0-9_]+$/', $json, $matches)) { return $matches[1]; } else { return 'core'; diff --git a/tests/core.get_author_index-01.phpt b/tests/core.get_author_index-01.phpt index 5db2dfd..f4b191d 100644 --- a/tests/core.get_author_index-01.phpt +++ b/tests/core.get_author_index-01.phpt @@ -7,8 +7,15 @@ require_once 'HTTP/Client.php'; $http = new HTTP_Client(); $http->get('http://wordpress.test/?json=core.get_author_index'); $response = $http->currentResponse(); -echo $response['body']; +$response = json_decode($response['body']); +$author = $response->authors[0]; + +echo "Response status: $response->status\n"; +echo "Author count: $response->count\n"; +echo "Author name: $author->name\n"; ?> --EXPECT-- -{"status":"ok","count":2,"authors":[{"id":3,"slug":"chip-bennett","name":"Chip Bennett","first_name":"","last_name":"","nickname":"Chip Bennett","url":"","description":""},{"id":4,"slug":"ian-stewart","name":"Ian Stewart","first_name":"","last_name":"","nickname":"Ian Stewart","url":"","description":""}]} +Response status: ok +Author count: 1 +Author name: themedemos diff --git a/tests/core.get_author_posts-01.phpt b/tests/core.get_author_posts-01.phpt index cfed798..c4ec850 100644 --- a/tests/core.get_author_posts-01.phpt +++ b/tests/core.get_author_posts-01.phpt @@ -5,10 +5,18 @@ core.get_author_posts by slug require_once 'HTTP/Client.php'; $http = new HTTP_Client(); -$http->get('http://wordpress.test/?json=core.get_author_posts&slug=chip-bennett'); +$http->get('http://wordpress.test/?json=core.get_author_posts&slug=themedemos'); $response = $http->currentResponse(); -echo $response['body']; +$response = json_decode($response['body']); +$author = $response->author; +$post = $response->posts[0]; + +echo "Response status: $response->status\n"; +echo "Post count: $response->count\n"; +echo "First post title: $post->title\n"; ?> --EXPECT-- -{"status":"ok","count":10,"pages":3,"author":{"id":3,"slug":"chip-bennett","name":"Chip Bennett","first_name":"","last_name":"","nickname":"Chip Bennett","url":"","description":""},"posts":[{"id":358,"type":"post","slug":"readability-test","url":"http:\/\/wordpress.test\/2008\/09\/05\/readability-test\/","status":"publish","title":"Readability Test","title_plain":"Readability Test","content":"

All children, except one, grow up. They soon know that they will grow up, and the way Wendy knew was this. One day when she was two years old she was playing in a garden, and she plucked another flower and ran with it to her mother. I suppose she must have looked rather delightful, for Mrs. Darling put her hand to her heart and cried, “Oh, why can’t you remain like this for ever!” This was all that passed between them on the subject, but henceforth Wendy knew that she must grow up. You always know after you are two. Two is the beginning of the end.<\/p>\n

Read more<\/a><\/p>\n","excerpt":"All children, except one, grow up. They soon know that they will grow up, and the way Wendy knew was this. One day when she was two years old she was playing in a garden, and she plucked another flower … Continue reading →<\/span><\/a>","date":"2008-09-05 00:27:25","modified":"2008-09-05 00:27:25","categories":[{"id":9,"slug":"cat-a","title":"Cat A","description":"","parent":0,"post_count":2}],"tags":[{"id":53,"slug":"chattels","title":"chattels","description":"","post_count":2},{"id":82,"slug":"privation","title":"privation","description":"","post_count":2}],"author":{"id":3,"slug":"chip-bennett","name":"Chip Bennett","first_name":"","last_name":"","nickname":"Chip Bennett","url":"","description":""},"comments":[],"attachments":[],"comment_count":0,"comment_status":"closed"},{"id":188,"type":"post","slug":"layout-test","url":"http:\/\/wordpress.test\/2008\/09\/04\/layout-test\/","status":"publish","title":"Layout Test","title_plain":"Layout Test","content":"

This is a sticky post!!! Make sure it sticks!<\/p>\n

This should then split into other pages with layout, images, HTML tags, and other things.<\/p>\n","excerpt":"This is a sticky post!!! Make sure it sticks! This should then split into other pages with layout, images, HTML tags, and other things.","date":"2008-09-04 23:02:20","modified":"2008-09-04 23:02:20","categories":[{"id":3,"slug":"aciform","title":"aciform","description":"","parent":0,"post_count":2},{"id":9,"slug":"cat-a","title":"Cat A","description":"","parent":0,"post_count":2},{"id":10,"slug":"cat-b","title":"Cat B","description":"","parent":0,"post_count":1},{"id":11,"slug":"cat-c","title":"Cat C","description":"","parent":0,"post_count":1},{"id":41,"slug":"sub","title":"sub","description":"","parent":3,"post_count":1}],"tags":[{"id":93,"slug":"tag1","title":"tag1","description":"","post_count":1},{"id":94,"slug":"tag2","title":"tag2","description":"","post_count":1},{"id":95,"slug":"tag3","title":"tag3","description":"","post_count":1}],"author":{"id":3,"slug":"chip-bennett","name":"Chip Bennett","first_name":"","last_name":"","nickname":"Chip Bennett","url":"","description":""},"comments":[],"attachments":[],"comment_count":0,"comment_status":"closed"},{"id":128,"type":"post","slug":"images-test","url":"http:\/\/wordpress.test\/2008\/09\/03\/images-test\/","status":"publish","title":"Images Test","title_plain":"Images Test","content":"

Image Alignment Tests: Un-Captioned Images<\/h2>\n

Center-align, no caption<\/h3>\n

Center-aligned image with no caption, and text before and after. \"\" ALorem ipsum dolor sit amet, consectetuer adipiscing elit. Sed odio nibh, tincidunt adipiscing, pretium nec, tincidunt id, enim. Fusce scelerisque nunc vitae nisl. Quisque quis urna in velit dictum pellentesque. Vivamus a quam. Curabitur eu tortor id turpis tristique adipiscing. Morbi blandit. Maecenas vel est. Nunc aliquam, orci at accumsan commodo, libero nibh euismod augue, a ullamcorper velit dui et purus. Aenean volutpat, ipsum ac imperdiet fermentum, dui dui suscipit arcu, vitae dictum purus diam ac ligula.<\/p>\n

Left-align, no caption<\/h3>\n

Left-aligned image with no caption, and text before and after. \"\" Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Sed odio nibh, tincidunt adipiscing, pretium nec, tincidunt id, enim. Fusce scelerisque nunc vitae nisl. Quisque quis urna in velit dictum pellentesque. Vivamus a quam. Curabitur eu tortor id turpis tristique adipiscing. Morbi blandit. Maecenas vel est. Nunc aliquam, orci at accumsan commodo, libero nibh euismod augue, a ullamcorper velit dui et purus. Aenean volutpat, ipsum ac imperdiet fermentum, dui dui suscipit arcu, vitae dictum purus diam ac ligula.<\/p>\n

Right-align, no caption<\/h3>\n

Right-aligned image with no caption, and text before and after. \"\" Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Sed odio nibh, tincidunt adipiscing, pretium nec, tincidunt id, enim. Fusce scelerisque nunc vitae nisl. Quisque quis urna in velit dictum pellentesque. Vivamus a quam. Curabitur eu tortor id turpis tristique adipiscing. Morbi blandit. Maecenas vel est. Nunc aliquam, orci at accumsan commodo, libero nibh euismod augue, a ullamcorper velit dui et purus. Aenean volutpat, ipsum ac imperdiet fermentum, dui dui suscipit arcu, vitae dictum purus diam ac ligula.<\/p>\n

No alignment, no caption<\/h3>\n

None-aligned image with no caption, and text before and after. \"\" Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Sed odio nibh, tincidunt adipiscing, pretium nec, tincidunt id, enim. Fusce scelerisque nunc vitae nisl. Quisque quis urna in velit dictum pellentesque. Vivamus a quam. Curabitur eu tortor id turpis tristique adipiscing. Morbi blandit. Maecenas vel est. Nunc aliquam, orci at accumsan commodo, libero nibh euismod augue, a ullamcorper velit dui et purus. Aenean volutpat, ipsum ac imperdiet fermentum, dui dui suscipit arcu, vitae dictum purus diam ac ligula.<\/p>\n","excerpt":"Image Alignment Tests: Un-Captioned Images Center-align, no caption Center-aligned image with no caption, and text before and after. ALorem ipsum dolor sit amet, consectetuer adipiscing elit. Sed odio nibh, tincidunt adipiscing, pretium nec, tincidunt id, enim. Fusce scelerisque nunc vitae … Continue reading →<\/span><\/a>","date":"2008-09-03 09:35:23","modified":"2008-09-03 09:35:23","categories":[],"tags":[],"author":{"id":3,"slug":"chip-bennett","name":"Chip Bennett","first_name":"","last_name":"","nickname":"Chip Bennett","url":"","description":""},"comments":[],"attachments":[{"id":534,"url":"http:\/\/wordpress.test\/wp-content\/uploads\/2010\/08\/test-image-landscape-9001.jpg","slug":"test-image-landscape-900","title":"test-image-landscape-900","description":"","caption":"","parent":128,"mime_type":"image\/jpeg","images":{"full":{"url":"http:\/\/wordpress.test\/wp-content\/uploads\/2010\/08\/test-image-landscape-9001.jpg","width":900,"height":598},"thumbnail":{"url":"http:\/\/wordpress.test\/wp-content\/uploads\/2010\/08\/test-image-landscape-9001.jpg","width":150,"height":99},"medium":{"url":"http:\/\/wordpress.test\/wp-content\/uploads\/2010\/08\/test-image-landscape-9001.jpg","width":300,"height":199},"large":{"url":"http:\/\/wordpress.test\/wp-content\/uploads\/2010\/08\/test-image-landscape-9001.jpg","width":640,"height":425},"post-thumbnail":{"url":"http:\/\/wordpress.test\/wp-content\/uploads\/2010\/08\/test-image-landscape-9001.jpg","width":297,"height":198}}},{"id":535,"url":"http:\/\/wordpress.test\/wp-content\/uploads\/2010\/08\/test-image-landscape1.jpg","slug":"test-image-landscape","title":"test-image-landscape","description":"","caption":"","parent":128,"mime_type":"image\/jpeg","images":{"full":{"url":"http:\/\/wordpress.test\/wp-content\/uploads\/2010\/08\/test-image-landscape1.jpg","width":300,"height":199},"thumbnail":{"url":"http:\/\/wordpress.test\/wp-content\/uploads\/2010\/08\/test-image-landscape1.jpg","width":150,"height":99},"medium":{"url":"http:\/\/wordpress.test\/wp-content\/uploads\/2010\/08\/test-image-landscape1.jpg","width":300,"height":199},"large":{"url":"http:\/\/wordpress.test\/wp-content\/uploads\/2010\/08\/test-image-landscape1.jpg","width":300,"height":199},"post-thumbnail":{"url":"http:\/\/wordpress.test\/wp-content\/uploads\/2010\/08\/test-image-landscape1.jpg","width":298,"height":198}}},{"id":536,"url":"http:\/\/wordpress.test\/wp-content\/uploads\/2010\/08\/test-image-portrait1.jpg","slug":"test-image-portrait","title":"test-image-portrait","description":"","caption":"","parent":128,"mime_type":"image\/jpeg","images":{"full":{"url":"http:\/\/wordpress.test\/wp-content\/uploads\/2010\/08\/test-image-portrait1.jpg","width":199,"height":300},"thumbnail":{"url":"http:\/\/wordpress.test\/wp-content\/uploads\/2010\/08\/test-image-portrait1.jpg","width":99,"height":150},"medium":{"url":"http:\/\/wordpress.test\/wp-content\/uploads\/2010\/08\/test-image-portrait1.jpg","width":199,"height":300},"large":{"url":"http:\/\/wordpress.test\/wp-content\/uploads\/2010\/08\/test-image-portrait1.jpg","width":199,"height":300},"post-thumbnail":{"url":"http:\/\/wordpress.test\/wp-content\/uploads\/2010\/08\/test-image-portrait1.jpg","width":131,"height":198}}},{"id":543,"url":"http:\/\/wordpress.test\/wp-content\/uploads\/2010\/08\/spectacles1.gif","slug":"spectacles","title":"spectacles","description":"","caption":"","parent":128,"mime_type":"image\/gif","images":{"full":{"url":"http:\/\/wordpress.test\/wp-content\/uploads\/2010\/08\/spectacles1.gif","width":165,"height":210},"thumbnail":{"url":"http:\/\/wordpress.test\/wp-content\/uploads\/2010\/08\/spectacles1.gif","width":117,"height":150},"medium":{"url":"http:\/\/wordpress.test\/wp-content\/uploads\/2010\/08\/spectacles1.gif","width":165,"height":210},"large":{"url":"http:\/\/wordpress.test\/wp-content\/uploads\/2010\/08\/spectacles1.gif","width":165,"height":210},"post-thumbnail":{"url":"http:\/\/wordpress.test\/wp-content\/uploads\/2010\/08\/spectacles1.gif","width":155,"height":198}}},{"id":544,"url":"http:\/\/wordpress.test\/wp-content\/uploads\/2010\/08\/boat1.jpg","slug":"boat","title":"boat","description":"","caption":"A picture is worth a thousand words","parent":128,"mime_type":"image\/jpeg","images":{"full":{"url":"http:\/\/wordpress.test\/wp-content\/uploads\/2010\/08\/boat1.jpg","width":435,"height":288},"thumbnail":{"url":"http:\/\/wordpress.test\/wp-content\/uploads\/2010\/08\/boat1.jpg","width":150,"height":99},"medium":{"url":"http:\/\/wordpress.test\/wp-content\/uploads\/2010\/08\/boat1.jpg","width":300,"height":198},"large":{"url":"http:\/\/wordpress.test\/wp-content\/uploads\/2010\/08\/boat1.jpg","width":435,"height":288},"post-thumbnail":{"url":"http:\/\/wordpress.test\/wp-content\/uploads\/2010\/08\/boat1.jpg","width":299,"height":198}}}],"comment_count":0,"comment_status":"closed"},{"id":555,"type":"post","slug":"post-format-test-gallery","url":"http:\/\/wordpress.test\/2008\/06\/10\/post-format-test-gallery\/","status":"publish","title":"Post Format Test: Gallery","title_plain":"Post Format Test: Gallery","content":"\n\t\t