3 * XML-RPC protocol support for WordPress
6 * @subpackage Publishing
10 * WordPress XMLRPC server implementation.
12 * Implements compatibility for Blogger API, MetaWeblog API, MovableType, and
13 * pingback. Additional WordPress API for managing comments, pages, posts,
16 * As of WordPress 3.5.0, XML-RPC is enabled by default. It can be disabled
17 * via the xmlrpc_enabled filter found in wp_xmlrpc_server::login().
20 * @subpackage Publishing
23 class wp_xmlrpc_server extends IXR_Server {
49 * Flags that the user authentication has failed in this instance of wp_xmlrpc_server.
54 protected $auth_failed = false;
57 * Register all of the XMLRPC methods that XMLRPC server understands.
59 * Sets up server and method property. Passes XMLRPC
60 * methods through the 'xmlrpc_methods' filter to allow plugins to extend
61 * or replace XMLRPC methods.
65 public function __construct() {
66 $this->methods = array(
68 'wp.getUsersBlogs' => 'this:wp_getUsersBlogs',
69 'wp.newPost' => 'this:wp_newPost',
70 'wp.editPost' => 'this:wp_editPost',
71 'wp.deletePost' => 'this:wp_deletePost',
72 'wp.getPost' => 'this:wp_getPost',
73 'wp.getPosts' => 'this:wp_getPosts',
74 'wp.newTerm' => 'this:wp_newTerm',
75 'wp.editTerm' => 'this:wp_editTerm',
76 'wp.deleteTerm' => 'this:wp_deleteTerm',
77 'wp.getTerm' => 'this:wp_getTerm',
78 'wp.getTerms' => 'this:wp_getTerms',
79 'wp.getTaxonomy' => 'this:wp_getTaxonomy',
80 'wp.getTaxonomies' => 'this:wp_getTaxonomies',
81 'wp.getUser' => 'this:wp_getUser',
82 'wp.getUsers' => 'this:wp_getUsers',
83 'wp.getProfile' => 'this:wp_getProfile',
84 'wp.editProfile' => 'this:wp_editProfile',
85 'wp.getPage' => 'this:wp_getPage',
86 'wp.getPages' => 'this:wp_getPages',
87 'wp.newPage' => 'this:wp_newPage',
88 'wp.deletePage' => 'this:wp_deletePage',
89 'wp.editPage' => 'this:wp_editPage',
90 'wp.getPageList' => 'this:wp_getPageList',
91 'wp.getAuthors' => 'this:wp_getAuthors',
92 'wp.getCategories' => 'this:mw_getCategories', // Alias
93 'wp.getTags' => 'this:wp_getTags',
94 'wp.newCategory' => 'this:wp_newCategory',
95 'wp.deleteCategory' => 'this:wp_deleteCategory',
96 'wp.suggestCategories' => 'this:wp_suggestCategories',
97 'wp.uploadFile' => 'this:mw_newMediaObject', // Alias
98 'wp.deleteFile' => 'this:wp_deletePost', // Alias
99 'wp.getCommentCount' => 'this:wp_getCommentCount',
100 'wp.getPostStatusList' => 'this:wp_getPostStatusList',
101 'wp.getPageStatusList' => 'this:wp_getPageStatusList',
102 'wp.getPageTemplates' => 'this:wp_getPageTemplates',
103 'wp.getOptions' => 'this:wp_getOptions',
104 'wp.setOptions' => 'this:wp_setOptions',
105 'wp.getComment' => 'this:wp_getComment',
106 'wp.getComments' => 'this:wp_getComments',
107 'wp.deleteComment' => 'this:wp_deleteComment',
108 'wp.editComment' => 'this:wp_editComment',
109 'wp.newComment' => 'this:wp_newComment',
110 'wp.getCommentStatusList' => 'this:wp_getCommentStatusList',
111 'wp.getMediaItem' => 'this:wp_getMediaItem',
112 'wp.getMediaLibrary' => 'this:wp_getMediaLibrary',
113 'wp.getPostFormats' => 'this:wp_getPostFormats',
114 'wp.getPostType' => 'this:wp_getPostType',
115 'wp.getPostTypes' => 'this:wp_getPostTypes',
116 'wp.getRevisions' => 'this:wp_getRevisions',
117 'wp.restoreRevision' => 'this:wp_restoreRevision',
120 'blogger.getUsersBlogs' => 'this:blogger_getUsersBlogs',
121 'blogger.getUserInfo' => 'this:blogger_getUserInfo',
122 'blogger.getPost' => 'this:blogger_getPost',
123 'blogger.getRecentPosts' => 'this:blogger_getRecentPosts',
124 'blogger.newPost' => 'this:blogger_newPost',
125 'blogger.editPost' => 'this:blogger_editPost',
126 'blogger.deletePost' => 'this:blogger_deletePost',
128 // MetaWeblog API (with MT extensions to structs)
129 'metaWeblog.newPost' => 'this:mw_newPost',
130 'metaWeblog.editPost' => 'this:mw_editPost',
131 'metaWeblog.getPost' => 'this:mw_getPost',
132 'metaWeblog.getRecentPosts' => 'this:mw_getRecentPosts',
133 'metaWeblog.getCategories' => 'this:mw_getCategories',
134 'metaWeblog.newMediaObject' => 'this:mw_newMediaObject',
136 // MetaWeblog API aliases for Blogger API
137 // see http://www.xmlrpc.com/stories/storyReader$2460
138 'metaWeblog.deletePost' => 'this:blogger_deletePost',
139 'metaWeblog.getUsersBlogs' => 'this:blogger_getUsersBlogs',
142 'mt.getCategoryList' => 'this:mt_getCategoryList',
143 'mt.getRecentPostTitles' => 'this:mt_getRecentPostTitles',
144 'mt.getPostCategories' => 'this:mt_getPostCategories',
145 'mt.setPostCategories' => 'this:mt_setPostCategories',
146 'mt.supportedMethods' => 'this:mt_supportedMethods',
147 'mt.supportedTextFilters' => 'this:mt_supportedTextFilters',
148 'mt.getTrackbackPings' => 'this:mt_getTrackbackPings',
149 'mt.publishPost' => 'this:mt_publishPost',
152 'pingback.ping' => 'this:pingback_ping',
153 'pingback.extensions.getPingbacks' => 'this:pingback_extensions_getPingbacks',
155 'demo.sayHello' => 'this:sayHello',
156 'demo.addTwoNumbers' => 'this:addTwoNumbers'
159 $this->initialise_blog_option_info();
162 * Filter the methods exposed by the XML-RPC server.
164 * This filter can be used to add new methods, and remove built-in methods.
168 * @param array $methods An array of XML-RPC methods.
170 $this->methods = apply_filters( 'xmlrpc_methods', $this->methods );
174 * Make private/protected methods readable for backwards compatibility.
179 * @param callable $name Method to call.
180 * @param array $arguments Arguments to pass when calling.
181 * @return array|IXR_Error|false Return value of the callback, false otherwise.
183 public function __call( $name, $arguments ) {
184 if ( '_multisite_getUsersBlogs' === $name ) {
185 return call_user_func_array( array( $this, $name ), $arguments );
193 public function serve_request() {
194 $this->IXR_Server($this->methods);
198 * Test XMLRPC API by saying, "Hello!" to client.
202 * @return string Hello string response.
204 public function sayHello() {
209 * Test XMLRPC API by adding two numbers for client.
213 * @param array $args {
214 * Method arguments. Note: arguments must be ordered as documented.
216 * @type int $number1 A number to add.
217 * @type int $number2 A second number to add.
219 * @return int Sum of the two given numbers.
221 public function addTwoNumbers( $args ) {
224 return $number1 + $number2;
232 * @param string $username User's username.
233 * @param string $password User's password.
234 * @return WP_User|bool WP_User object if authentication passed, false otherwise
236 public function login( $username, $password ) {
238 * Respect old get_option() filters left for back-compat when the 'enable_xmlrpc'
239 * option was deprecated in 3.5.0. Use the 'xmlrpc_enabled' hook instead.
241 $enabled = apply_filters( 'pre_option_enable_xmlrpc', false );
242 if ( false === $enabled ) {
243 $enabled = apply_filters( 'option_enable_xmlrpc', true );
247 * Filter whether XML-RPC is enabled.
249 * This is the proper filter for turning off XML-RPC.
253 * @param bool $enabled Whether XML-RPC is enabled. Default true.
255 $enabled = apply_filters( 'xmlrpc_enabled', $enabled );
258 $this->error = new IXR_Error( 405, sprintf( __( 'XML-RPC services are disabled on this site.' ) ) );
262 if ( $this->auth_failed ) {
263 $user = new WP_Error( 'login_prevented' );
265 $user = wp_authenticate( $username, $password );
268 if ( is_wp_error( $user ) ) {
269 $this->error = new IXR_Error( 403, __( 'Incorrect username or password.' ) );
271 // Flag that authentication has failed once on this wp_xmlrpc_server instance
272 $this->auth_failed = true;
275 * Filter the XML-RPC user login error message.
279 * @param string $error The XML-RPC error message.
280 * @param WP_User $user WP_User object.
282 $this->error = apply_filters( 'xmlrpc_login_error', $this->error, $user );
286 wp_set_current_user( $user->ID );
291 * Check user's credentials. Deprecated.
294 * @deprecated 2.8.0 Use wp_xmlrpc_server::login()
295 * @see wp_xmlrpc_server::login()
297 * @param string $username User's username.
298 * @param string $password User's password.
299 * @return bool Whether authentication passed.
301 public function login_pass_ok( $username, $password ) {
302 return (bool) $this->login( $username, $password );
306 * Escape string or array of strings for database.
310 * @param string|array $data Escape single string or array of strings.
311 * @return string|void Returns with string is passed, alters by-reference
312 * when array is passed.
314 public function escape( &$data ) {
315 if ( ! is_array( $data ) )
316 return wp_slash( $data );
318 foreach ( $data as &$v ) {
319 if ( is_array( $v ) )
321 elseif ( ! is_object( $v ) )
327 * Retrieve custom fields for post.
331 * @param int $post_id Post ID.
332 * @return array Custom fields, if exist.
334 public function get_custom_fields($post_id) {
335 $post_id = (int) $post_id;
337 $custom_fields = array();
339 foreach ( (array) has_meta($post_id) as $meta ) {
340 // Don't expose protected fields.
341 if ( ! current_user_can( 'edit_post_meta', $post_id , $meta['meta_key'] ) )
344 $custom_fields[] = array(
345 "id" => $meta['meta_id'],
346 "key" => $meta['meta_key'],
347 "value" => $meta['meta_value']
351 return $custom_fields;
355 * Set custom fields for post.
359 * @param int $post_id Post ID.
360 * @param array $fields Custom fields.
362 public function set_custom_fields($post_id, $fields) {
363 $post_id = (int) $post_id;
365 foreach ( (array) $fields as $meta ) {
366 if ( isset($meta['id']) ) {
367 $meta['id'] = (int) $meta['id'];
368 $pmeta = get_metadata_by_mid( 'post', $meta['id'] );
369 if ( isset($meta['key']) ) {
370 $meta['key'] = wp_unslash( $meta['key'] );
371 if ( $meta['key'] !== $pmeta->meta_key )
373 $meta['value'] = wp_unslash( $meta['value'] );
374 if ( current_user_can( 'edit_post_meta', $post_id, $meta['key'] ) )
375 update_metadata_by_mid( 'post', $meta['id'], $meta['value'] );
376 } elseif ( current_user_can( 'delete_post_meta', $post_id, $pmeta->meta_key ) ) {
377 delete_metadata_by_mid( 'post', $meta['id'] );
379 } elseif ( current_user_can( 'add_post_meta', $post_id, wp_unslash( $meta['key'] ) ) ) {
380 add_post_meta( $post_id, $meta['key'], $meta['value'] );
386 * Set up blog options property.
388 * Passes property through {@see 'xmlrpc_blog_options'} filter.
392 * @global string $wp_version
394 public function initialise_blog_option_info() {
397 $this->blog_options = array(
399 'software_name' => array(
400 'desc' => __( 'Software Name' ),
402 'value' => 'WordPress'
404 'software_version' => array(
405 'desc' => __( 'Software Version' ),
407 'value' => $wp_version
410 'desc' => __( 'WordPress Address (URL)' ),
412 'option' => 'siteurl'
415 'desc' => __( 'Site Address (URL)' ),
419 'login_url' => array(
420 'desc' => __( 'Login Address (URL)' ),
422 'value' => wp_login_url( )
424 'admin_url' => array(
425 'desc' => __( 'The URL to the admin area' ),
427 'value' => get_admin_url( )
429 'image_default_link_type' => array(
430 'desc' => __( 'Image default link type' ),
432 'option' => 'image_default_link_type'
434 'image_default_size' => array(
435 'desc' => __( 'Image default size' ),
437 'option' => 'image_default_size'
439 'image_default_align' => array(
440 'desc' => __( 'Image default align' ),
442 'option' => 'image_default_align'
445 'desc' => __( 'Template' ),
447 'option' => 'template'
449 'stylesheet' => array(
450 'desc' => __( 'Stylesheet' ),
452 'option' => 'stylesheet'
454 'post_thumbnail' => array(
455 'desc' => __('Post Thumbnail'),
457 'value' => current_theme_supports( 'post-thumbnails' )
461 'time_zone' => array(
462 'desc' => __( 'Time Zone' ),
464 'option' => 'gmt_offset'
466 'blog_title' => array(
467 'desc' => __( 'Site Title' ),
469 'option' => 'blogname'
471 'blog_tagline' => array(
472 'desc' => __( 'Site Tagline' ),
474 'option' => 'blogdescription'
476 'date_format' => array(
477 'desc' => __( 'Date Format' ),
479 'option' => 'date_format'
481 'time_format' => array(
482 'desc' => __( 'Time Format' ),
484 'option' => 'time_format'
486 'users_can_register' => array(
487 'desc' => __( 'Allow new users to sign up' ),
489 'option' => 'users_can_register'
491 'thumbnail_size_w' => array(
492 'desc' => __( 'Thumbnail Width' ),
494 'option' => 'thumbnail_size_w'
496 'thumbnail_size_h' => array(
497 'desc' => __( 'Thumbnail Height' ),
499 'option' => 'thumbnail_size_h'
501 'thumbnail_crop' => array(
502 'desc' => __( 'Crop thumbnail to exact dimensions' ),
504 'option' => 'thumbnail_crop'
506 'medium_size_w' => array(
507 'desc' => __( 'Medium size image width' ),
509 'option' => 'medium_size_w'
511 'medium_size_h' => array(
512 'desc' => __( 'Medium size image height' ),
514 'option' => 'medium_size_h'
516 'medium_large_size_w' => array(
517 'desc' => __( 'Medium-Large size image width' ),
519 'option' => 'medium_large_size_w'
521 'medium_large_size_h' => array(
522 'desc' => __( 'Medium-Large size image height' ),
524 'option' => 'medium_large_size_h'
526 'large_size_w' => array(
527 'desc' => __( 'Large size image width' ),
529 'option' => 'large_size_w'
531 'large_size_h' => array(
532 'desc' => __( 'Large size image height' ),
534 'option' => 'large_size_h'
536 'default_comment_status' => array(
537 'desc' => __( 'Allow people to post comments on new articles' ),
539 'option' => 'default_comment_status'
541 'default_ping_status' => array(
542 'desc' => __( 'Allow link notifications from other blogs (pingbacks and trackbacks) on new articles' ),
544 'option' => 'default_ping_status'
549 * Filter the XML-RPC blog options property.
553 * @param array $blog_options An array of XML-RPC blog options.
555 $this->blog_options = apply_filters( 'xmlrpc_blog_options', $this->blog_options );
559 * Retrieve the blogs of the user.
563 * @param array $args {
564 * Method arguments. Note: arguments must be ordered as documented.
566 * @type string $username Username.
567 * @type string $password Password.
569 * @return array|IXR_Error Array contains:
571 * - 'isPrimary' - whether the blog is the user's primary blog
575 * - 'xmlrpc' - url of xmlrpc endpoint
577 public function wp_getUsersBlogs( $args ) {
578 // If this isn't on WPMU then just use blogger_getUsersBlogs
579 if ( !is_multisite() ) {
580 array_unshift( $args, 1 );
581 return $this->blogger_getUsersBlogs( $args );
584 $this->escape( $args );
586 $username = $args[0];
587 $password = $args[1];
589 if ( !$user = $this->login($username, $password) )
593 * Fires after the XML-RPC user has been authenticated but before the rest of
594 * the method logic begins.
596 * All built-in XML-RPC methods use the action xmlrpc_call, with a parameter
597 * equal to the method's name, e.g., wp.getUsersBlogs, wp.newPost, etc.
601 * @param string $name The method name.
603 do_action( 'xmlrpc_call', 'wp.getUsersBlogs' );
605 $blogs = (array) get_blogs_of_user( $user->ID );
607 $primary_blog_id = 0;
608 $active_blog = get_active_blog_for_user( $user->ID );
609 if ( $active_blog ) {
610 $primary_blog_id = (int) $active_blog->blog_id;
613 foreach ( $blogs as $blog ) {
614 // Don't include blogs that aren't hosted at this site.
615 if ( $blog->site_id != get_current_site()->id )
618 $blog_id = $blog->userblog_id;
620 switch_to_blog( $blog_id );
622 $is_admin = current_user_can( 'manage_options' );
623 $is_primary = ( (int) $blog_id === $primary_blog_id );
626 'isAdmin' => $is_admin,
627 'isPrimary' => $is_primary,
628 'url' => home_url( '/' ),
629 'blogid' => (string) $blog_id,
630 'blogName' => get_option( 'blogname' ),
631 'xmlrpc' => site_url( 'xmlrpc.php', 'rpc' ),
634 restore_current_blog();
641 * Checks if the method received at least the minimum number of arguments.
646 * @param string|array $args Sanitize single string or array of strings.
647 * @param int $count Minimum number of arguments.
648 * @return bool if `$args` contains at least $count arguments.
650 protected function minimum_args( $args, $count ) {
651 if ( count( $args ) < $count ) {
652 $this->error = new IXR_Error( 400, __( 'Insufficient arguments passed to this XML-RPC method.' ) );
660 * Prepares taxonomy data for return in an XML-RPC object.
664 * @param object $taxonomy The unprepared taxonomy data.
665 * @param array $fields The subset of taxonomy fields to return.
666 * @return array The prepared taxonomy data.
668 protected function _prepare_taxonomy( $taxonomy, $fields ) {
670 'name' => $taxonomy->name,
671 'label' => $taxonomy->label,
672 'hierarchical' => (bool) $taxonomy->hierarchical,
673 'public' => (bool) $taxonomy->public,
674 'show_ui' => (bool) $taxonomy->show_ui,
675 '_builtin' => (bool) $taxonomy->_builtin,
678 if ( in_array( 'labels', $fields ) )
679 $_taxonomy['labels'] = (array) $taxonomy->labels;
681 if ( in_array( 'cap', $fields ) )
682 $_taxonomy['cap'] = (array) $taxonomy->cap;
684 if ( in_array( 'menu', $fields ) )
685 $_taxonomy['show_in_menu'] = (bool) $_taxonomy->show_in_menu;
687 if ( in_array( 'object_type', $fields ) )
688 $_taxonomy['object_type'] = array_unique( (array) $taxonomy->object_type );
691 * Filter XML-RPC-prepared data for the given taxonomy.
695 * @param array $_taxonomy An array of taxonomy data.
696 * @param object $taxonomy Taxonomy object.
697 * @param array $fields The subset of taxonomy fields to return.
699 return apply_filters( 'xmlrpc_prepare_taxonomy', $_taxonomy, $taxonomy, $fields );
703 * Prepares term data for return in an XML-RPC object.
707 * @param array|object $term The unprepared term data.
708 * @return array The prepared term data.
710 protected function _prepare_term( $term ) {
712 if ( ! is_array( $_term ) )
713 $_term = get_object_vars( $_term );
715 // For integers which may be larger than XML-RPC supports ensure we return strings.
716 $_term['term_id'] = strval( $_term['term_id'] );
717 $_term['term_group'] = strval( $_term['term_group'] );
718 $_term['term_taxonomy_id'] = strval( $_term['term_taxonomy_id'] );
719 $_term['parent'] = strval( $_term['parent'] );
721 // Count we are happy to return as an integer because people really shouldn't use terms that much.
722 $_term['count'] = intval( $_term['count'] );
725 * Filter XML-RPC-prepared data for the given term.
729 * @param array $_term An array of term data.
730 * @param array|object $term Term object or array.
732 return apply_filters( 'xmlrpc_prepare_term', $_term, $term );
736 * Convert a WordPress date string to an IXR_Date object.
740 * @param string $date Date string to convert.
741 * @return IXR_Date IXR_Date object.
743 protected function _convert_date( $date ) {
744 if ( $date === '0000-00-00 00:00:00' ) {
745 return new IXR_Date( '00000000T00:00:00Z' );
747 return new IXR_Date( mysql2date( 'Ymd\TH:i:s', $date, false ) );
751 * Convert a WordPress GMT date string to an IXR_Date object.
755 * @param string $date_gmt WordPress GMT date string.
756 * @param string $date Date string.
757 * @return IXR_Date IXR_Date object.
759 protected function _convert_date_gmt( $date_gmt, $date ) {
760 if ( $date !== '0000-00-00 00:00:00' && $date_gmt === '0000-00-00 00:00:00' ) {
761 return new IXR_Date( get_gmt_from_date( mysql2date( 'Y-m-d H:i:s', $date, false ), 'Ymd\TH:i:s' ) );
763 return $this->_convert_date( $date_gmt );
767 * Prepares post data for return in an XML-RPC object.
771 * @param array $post The unprepared post data.
772 * @param array $fields The subset of post type fields to return.
773 * @return array The prepared post data.
775 protected function _prepare_post( $post, $fields ) {
776 // Holds the data for this post. built up based on $fields.
777 $_post = array( 'post_id' => strval( $post['ID'] ) );
779 // Prepare common post fields.
780 $post_fields = array(
781 'post_title' => $post['post_title'],
782 'post_date' => $this->_convert_date( $post['post_date'] ),
783 'post_date_gmt' => $this->_convert_date_gmt( $post['post_date_gmt'], $post['post_date'] ),
784 'post_modified' => $this->_convert_date( $post['post_modified'] ),
785 'post_modified_gmt' => $this->_convert_date_gmt( $post['post_modified_gmt'], $post['post_modified'] ),
786 'post_status' => $post['post_status'],
787 'post_type' => $post['post_type'],
788 'post_name' => $post['post_name'],
789 'post_author' => $post['post_author'],
790 'post_password' => $post['post_password'],
791 'post_excerpt' => $post['post_excerpt'],
792 'post_content' => $post['post_content'],
793 'post_parent' => strval( $post['post_parent'] ),
794 'post_mime_type' => $post['post_mime_type'],
795 'link' => get_permalink( $post['ID'] ),
796 'guid' => $post['guid'],
797 'menu_order' => intval( $post['menu_order'] ),
798 'comment_status' => $post['comment_status'],
799 'ping_status' => $post['ping_status'],
800 'sticky' => ( $post['post_type'] === 'post' && is_sticky( $post['ID'] ) ),
804 $post_fields['post_thumbnail'] = array();
805 $thumbnail_id = get_post_thumbnail_id( $post['ID'] );
806 if ( $thumbnail_id ) {
807 $thumbnail_size = current_theme_supports('post-thumbnail') ? 'post-thumbnail' : 'thumbnail';
808 $post_fields['post_thumbnail'] = $this->_prepare_media_item( get_post( $thumbnail_id ), $thumbnail_size );
811 // Consider future posts as published.
812 if ( $post_fields['post_status'] === 'future' )
813 $post_fields['post_status'] = 'publish';
815 // Fill in blank post format.
816 $post_fields['post_format'] = get_post_format( $post['ID'] );
817 if ( empty( $post_fields['post_format'] ) )
818 $post_fields['post_format'] = 'standard';
820 // Merge requested $post_fields fields into $_post.
821 if ( in_array( 'post', $fields ) ) {
822 $_post = array_merge( $_post, $post_fields );
824 $requested_fields = array_intersect_key( $post_fields, array_flip( $fields ) );
825 $_post = array_merge( $_post, $requested_fields );
828 $all_taxonomy_fields = in_array( 'taxonomies', $fields );
830 if ( $all_taxonomy_fields || in_array( 'terms', $fields ) ) {
831 $post_type_taxonomies = get_object_taxonomies( $post['post_type'], 'names' );
832 $terms = wp_get_object_terms( $post['ID'], $post_type_taxonomies );
833 $_post['terms'] = array();
834 foreach ( $terms as $term ) {
835 $_post['terms'][] = $this->_prepare_term( $term );
839 if ( in_array( 'custom_fields', $fields ) )
840 $_post['custom_fields'] = $this->get_custom_fields( $post['ID'] );
842 if ( in_array( 'enclosure', $fields ) ) {
843 $_post['enclosure'] = array();
844 $enclosures = (array) get_post_meta( $post['ID'], 'enclosure' );
845 if ( ! empty( $enclosures ) ) {
846 $encdata = explode( "\n", $enclosures[0] );
847 $_post['enclosure']['url'] = trim( htmlspecialchars( $encdata[0] ) );
848 $_post['enclosure']['length'] = (int) trim( $encdata[1] );
849 $_post['enclosure']['type'] = trim( $encdata[2] );
854 * Filter XML-RPC-prepared date for the given post.
858 * @param array $_post An array of modified post data.
859 * @param array $post An array of post data.
860 * @param array $fields An array of post fields.
862 return apply_filters( 'xmlrpc_prepare_post', $_post, $post, $fields );
866 * Prepares post data for return in an XML-RPC object.
870 * @param object $post_type Post type object.
871 * @param array $fields The subset of post fields to return.
872 * @return array The prepared post type data.
874 protected function _prepare_post_type( $post_type, $fields ) {
876 'name' => $post_type->name,
877 'label' => $post_type->label,
878 'hierarchical' => (bool) $post_type->hierarchical,
879 'public' => (bool) $post_type->public,
880 'show_ui' => (bool) $post_type->show_ui,
881 '_builtin' => (bool) $post_type->_builtin,
882 'has_archive' => (bool) $post_type->has_archive,
883 'supports' => get_all_post_type_supports( $post_type->name ),
886 if ( in_array( 'labels', $fields ) ) {
887 $_post_type['labels'] = (array) $post_type->labels;
890 if ( in_array( 'cap', $fields ) ) {
891 $_post_type['cap'] = (array) $post_type->cap;
892 $_post_type['map_meta_cap'] = (bool) $post_type->map_meta_cap;
895 if ( in_array( 'menu', $fields ) ) {
896 $_post_type['menu_position'] = (int) $post_type->menu_position;
897 $_post_type['menu_icon'] = $post_type->menu_icon;
898 $_post_type['show_in_menu'] = (bool) $post_type->show_in_menu;
901 if ( in_array( 'taxonomies', $fields ) )
902 $_post_type['taxonomies'] = get_object_taxonomies( $post_type->name, 'names' );
905 * Filter XML-RPC-prepared date for the given post type.
909 * @param array $_post_type An array of post type data.
910 * @param object $post_type Post type object.
912 return apply_filters( 'xmlrpc_prepare_post_type', $_post_type, $post_type );
916 * Prepares media item data for return in an XML-RPC object.
920 * @param object $media_item The unprepared media item data.
921 * @param string $thumbnail_size The image size to use for the thumbnail URL.
922 * @return array The prepared media item data.
924 protected function _prepare_media_item( $media_item, $thumbnail_size = 'thumbnail' ) {
925 $_media_item = array(
926 'attachment_id' => strval( $media_item->ID ),
927 'date_created_gmt' => $this->_convert_date_gmt( $media_item->post_date_gmt, $media_item->post_date ),
928 'parent' => $media_item->post_parent,
929 'link' => wp_get_attachment_url( $media_item->ID ),
930 'title' => $media_item->post_title,
931 'caption' => $media_item->post_excerpt,
932 'description' => $media_item->post_content,
933 'metadata' => wp_get_attachment_metadata( $media_item->ID ),
934 'type' => $media_item->post_mime_type
937 $thumbnail_src = image_downsize( $media_item->ID, $thumbnail_size );
938 if ( $thumbnail_src )
939 $_media_item['thumbnail'] = $thumbnail_src[0];
941 $_media_item['thumbnail'] = $_media_item['link'];
944 * Filter XML-RPC-prepared data for the given media item.
948 * @param array $_media_item An array of media item data.
949 * @param object $media_item Media item object.
950 * @param string $thumbnail_size Image size.
952 return apply_filters( 'xmlrpc_prepare_media_item', $_media_item, $media_item, $thumbnail_size );
956 * Prepares page data for return in an XML-RPC object.
960 * @param object $page The unprepared page data.
961 * @return array The prepared page data.
963 protected function _prepare_page( $page ) {
964 // Get all of the page content and link.
965 $full_page = get_extended( $page->post_content );
966 $link = get_permalink( $page->ID );
968 // Get info the page parent if there is one.
970 if ( ! empty( $page->post_parent ) ) {
971 $parent = get_post( $page->post_parent );
972 $parent_title = $parent->post_title;
975 // Determine comment and ping settings.
976 $allow_comments = comments_open( $page->ID ) ? 1 : 0;
977 $allow_pings = pings_open( $page->ID ) ? 1 : 0;
980 $page_date = $this->_convert_date( $page->post_date );
981 $page_date_gmt = $this->_convert_date_gmt( $page->post_date_gmt, $page->post_date );
983 // Pull the categories info together.
984 $categories = array();
985 if ( is_object_in_taxonomy( 'page', 'category' ) ) {
986 foreach ( wp_get_post_categories( $page->ID ) as $cat_id ) {
987 $categories[] = get_cat_name( $cat_id );
991 // Get the author info.
992 $author = get_userdata( $page->post_author );
994 $page_template = get_page_template_slug( $page->ID );
995 if ( empty( $page_template ) )
996 $page_template = 'default';
999 'dateCreated' => $page_date,
1000 'userid' => $page->post_author,
1001 'page_id' => $page->ID,
1002 'page_status' => $page->post_status,
1003 'description' => $full_page['main'],
1004 'title' => $page->post_title,
1006 'permaLink' => $link,
1007 'categories' => $categories,
1008 'excerpt' => $page->post_excerpt,
1009 'text_more' => $full_page['extended'],
1010 'mt_allow_comments' => $allow_comments,
1011 'mt_allow_pings' => $allow_pings,
1012 'wp_slug' => $page->post_name,
1013 'wp_password' => $page->post_password,
1014 'wp_author' => $author->display_name,
1015 'wp_page_parent_id' => $page->post_parent,
1016 'wp_page_parent_title' => $parent_title,
1017 'wp_page_order' => $page->menu_order,
1018 'wp_author_id' => (string) $author->ID,
1019 'wp_author_display_name' => $author->display_name,
1020 'date_created_gmt' => $page_date_gmt,
1021 'custom_fields' => $this->get_custom_fields( $page->ID ),
1022 'wp_page_template' => $page_template
1026 * Filter XML-RPC-prepared data for the given page.
1030 * @param array $_page An array of page data.
1031 * @param WP_Post $page Page object.
1033 return apply_filters( 'xmlrpc_prepare_page', $_page, $page );
1037 * Prepares comment data for return in an XML-RPC object.
1041 * @param object $comment The unprepared comment data.
1042 * @return array The prepared comment data.
1044 protected function _prepare_comment( $comment ) {
1045 // Format page date.
1046 $comment_date_gmt = $this->_convert_date_gmt( $comment->comment_date_gmt, $comment->comment_date );
1048 if ( '0' == $comment->comment_approved ) {
1049 $comment_status = 'hold';
1050 } elseif ( 'spam' == $comment->comment_approved ) {
1051 $comment_status = 'spam';
1052 } elseif ( '1' == $comment->comment_approved ) {
1053 $comment_status = 'approve';
1055 $comment_status = $comment->comment_approved;
1058 'date_created_gmt' => $comment_date_gmt,
1059 'user_id' => $comment->user_id,
1060 'comment_id' => $comment->comment_ID,
1061 'parent' => $comment->comment_parent,
1062 'status' => $comment_status,
1063 'content' => $comment->comment_content,
1064 'link' => get_comment_link($comment),
1065 'post_id' => $comment->comment_post_ID,
1066 'post_title' => get_the_title($comment->comment_post_ID),
1067 'author' => $comment->comment_author,
1068 'author_url' => $comment->comment_author_url,
1069 'author_email' => $comment->comment_author_email,
1070 'author_ip' => $comment->comment_author_IP,
1071 'type' => $comment->comment_type,
1075 * Filter XML-RPC-prepared data for the given comment.
1079 * @param array $_comment An array of prepared comment data.
1080 * @param WP_Comment $comment Comment object.
1082 return apply_filters( 'xmlrpc_prepare_comment', $_comment, $comment );
1086 * Prepares user data for return in an XML-RPC object.
1090 * @param WP_User $user The unprepared user object.
1091 * @param array $fields The subset of user fields to return.
1092 * @return array The prepared user data.
1094 protected function _prepare_user( $user, $fields ) {
1095 $_user = array( 'user_id' => strval( $user->ID ) );
1097 $user_fields = array(
1098 'username' => $user->user_login,
1099 'first_name' => $user->user_firstname,
1100 'last_name' => $user->user_lastname,
1101 'registered' => $this->_convert_date( $user->user_registered ),
1102 'bio' => $user->user_description,
1103 'email' => $user->user_email,
1104 'nickname' => $user->nickname,
1105 'nicename' => $user->user_nicename,
1106 'url' => $user->user_url,
1107 'display_name' => $user->display_name,
1108 'roles' => $user->roles,
1111 if ( in_array( 'all', $fields ) ) {
1112 $_user = array_merge( $_user, $user_fields );
1114 if ( in_array( 'basic', $fields ) ) {
1115 $basic_fields = array( 'username', 'email', 'registered', 'display_name', 'nicename' );
1116 $fields = array_merge( $fields, $basic_fields );
1118 $requested_fields = array_intersect_key( $user_fields, array_flip( $fields ) );
1119 $_user = array_merge( $_user, $requested_fields );
1123 * Filter XML-RPC-prepared data for the given user.
1127 * @param array $_user An array of user data.
1128 * @param WP_User $user User object.
1129 * @param array $fields An array of user fields.
1131 return apply_filters( 'xmlrpc_prepare_user', $_user, $user, $fields );
1135 * Create a new post for any registered post type.
1139 * @link http://en.wikipedia.org/wiki/RSS_enclosure for information on RSS enclosures.
1141 * @param array $args {
1142 * Method arguments. Note: top-level arguments must be ordered as documented.
1144 * @type int $blog_id Blog ID (unused).
1145 * @type string $username Username.
1146 * @type string $password Password.
1147 * @type array $content_struct {
1148 * Content struct for adding a new post. See wp_insert_post() for information on
1149 * additional post fields
1151 * @type string $post_type Post type. Default 'post'.
1152 * @type string $post_status Post status. Default 'draft'
1153 * @type string $post_title Post title.
1154 * @type int $post_author Post author ID.
1155 * @type string $post_excerpt Post excerpt.
1156 * @type string $post_content Post content.
1157 * @type string $post_date_gmt Post date in GMT.
1158 * @type string $post_date Post date.
1159 * @type string $post_password Post password (20-character limit).
1160 * @type string $comment_status Post comment enabled status. Accepts 'open' or 'closed'.
1161 * @type string $ping_status Post ping status. Accepts 'open' or 'closed'.
1162 * @type bool $sticky Whether the post should be sticky. Automatically false if
1163 * `$post_status` is 'private'.
1164 * @type int $post_thumbnail ID of an image to use as the post thumbnail/featured image.
1165 * @type array $custom_fields Array of meta key/value pairs to add to the post.
1166 * @type array $terms Associative array with taxonomy names as keys and arrays
1167 * of term IDs as values.
1168 * @type array $terms_names Associative array with taxonomy names as keys and arrays
1169 * of term names as values.
1170 * @type array $enclosure {
1171 * Array of feed enclosure data to add to post meta.
1173 * @type string $url URL for the feed enclosure.
1174 * @type int $length Size in bytes of the enclosure.
1175 * @type string $type Mime-type for the enclosure.
1179 * @return int|IXR_Error Post ID on success, IXR_Error instance otherwise.
1181 public function wp_newPost( $args ) {
1182 if ( ! $this->minimum_args( $args, 4 ) )
1183 return $this->error;
1185 $this->escape( $args );
1187 $username = $args[1];
1188 $password = $args[2];
1189 $content_struct = $args[3];
1191 if ( ! $user = $this->login( $username, $password ) )
1192 return $this->error;
1194 // convert the date field back to IXR form
1195 if ( isset( $content_struct['post_date'] ) && ! ( $content_struct['post_date'] instanceof IXR_Date ) ) {
1196 $content_struct['post_date'] = $this->_convert_date( $content_struct['post_date'] );
1199 // ignore the existing GMT date if it is empty or a non-GMT date was supplied in $content_struct,
1200 // since _insert_post will ignore the non-GMT date if the GMT date is set
1201 if ( isset( $content_struct['post_date_gmt'] ) && ! ( $content_struct['post_date_gmt'] instanceof IXR_Date ) ) {
1202 if ( $content_struct['post_date_gmt'] == '0000-00-00 00:00:00' || isset( $content_struct['post_date'] ) ) {
1203 unset( $content_struct['post_date_gmt'] );
1205 $content_struct['post_date_gmt'] = $this->_convert_date( $content_struct['post_date_gmt'] );
1209 /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */
1210 do_action( 'xmlrpc_call', 'wp.newPost' );
1212 unset( $content_struct['ID'] );
1214 return $this->_insert_post( $user, $content_struct );
1218 * Helper method for filtering out elements from an array.
1222 * @param int $count Number to compare to one.
1224 private function _is_greater_than_one( $count ) {
1229 * Encapsulate the logic for sticking a post
1230 * and determining if the user has permission to do so
1235 * @param array $post_data
1236 * @param bool $update
1237 * @return void|IXR_Error
1239 private function _toggle_sticky( $post_data, $update = false ) {
1240 $post_type = get_post_type_object( $post_data['post_type'] );
1242 // Private and password-protected posts cannot be stickied.
1243 if ( 'private' === $post_data['post_status'] || ! empty( $post_data['post_password'] ) ) {
1244 // Error if the client tried to stick the post, otherwise, silently unstick.
1245 if ( ! empty( $post_data['sticky'] ) ) {
1246 return new IXR_Error( 401, __( 'Sorry, you cannot stick a private post.' ) );
1250 unstick_post( $post_data['ID'] );
1252 } elseif ( isset( $post_data['sticky'] ) ) {
1253 if ( ! current_user_can( $post_type->cap->edit_others_posts ) ) {
1254 return new IXR_Error( 401, __( 'Sorry, you are not allowed to stick this post.' ) );
1257 $sticky = wp_validate_boolean( $post_data['sticky'] );
1259 stick_post( $post_data['ID'] );
1261 unstick_post( $post_data['ID'] );
1267 * Helper method for wp_newPost() and wp_editPost(), containing shared logic.
1272 * @see wp_insert_post()
1274 * @param WP_User $user The post author if post_author isn't set in $content_struct.
1275 * @param array|IXR_Error $content_struct Post data to insert.
1276 * @return IXR_Error|string
1278 protected function _insert_post( $user, $content_struct ) {
1279 $defaults = array( 'post_status' => 'draft', 'post_type' => 'post', 'post_author' => 0,
1280 'post_password' => '', 'post_excerpt' => '', 'post_content' => '', 'post_title' => '' );
1282 $post_data = wp_parse_args( $content_struct, $defaults );
1284 $post_type = get_post_type_object( $post_data['post_type'] );
1286 return new IXR_Error( 403, __( 'Invalid post type' ) );
1288 $update = ! empty( $post_data['ID'] );
1291 if ( ! get_post( $post_data['ID'] ) )
1292 return new IXR_Error( 401, __( 'Invalid post ID.' ) );
1293 if ( ! current_user_can( 'edit_post', $post_data['ID'] ) )
1294 return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit this post.' ) );
1295 if ( $post_data['post_type'] != get_post_type( $post_data['ID'] ) )
1296 return new IXR_Error( 401, __( 'The post type may not be changed.' ) );
1298 if ( ! current_user_can( $post_type->cap->create_posts ) || ! current_user_can( $post_type->cap->edit_posts ) )
1299 return new IXR_Error( 401, __( 'Sorry, you are not allowed to post on this site.' ) );
1302 switch ( $post_data['post_status'] ) {
1307 if ( ! current_user_can( $post_type->cap->publish_posts ) )
1308 return new IXR_Error( 401, __( 'Sorry, you are not allowed to create private posts in this post type' ) );
1312 if ( ! current_user_can( $post_type->cap->publish_posts ) )
1313 return new IXR_Error( 401, __( 'Sorry, you are not allowed to publish posts in this post type' ) );
1316 if ( ! get_post_status_object( $post_data['post_status'] ) )
1317 $post_data['post_status'] = 'draft';
1321 if ( ! empty( $post_data['post_password'] ) && ! current_user_can( $post_type->cap->publish_posts ) )
1322 return new IXR_Error( 401, __( 'Sorry, you are not allowed to create password protected posts in this post type' ) );
1324 $post_data['post_author'] = absint( $post_data['post_author'] );
1325 if ( ! empty( $post_data['post_author'] ) && $post_data['post_author'] != $user->ID ) {
1326 if ( ! current_user_can( $post_type->cap->edit_others_posts ) )
1327 return new IXR_Error( 401, __( 'You are not allowed to create posts as this user.' ) );
1329 $author = get_userdata( $post_data['post_author'] );
1332 return new IXR_Error( 404, __( 'Invalid author ID.' ) );
1334 $post_data['post_author'] = $user->ID;
1337 if ( isset( $post_data['comment_status'] ) && $post_data['comment_status'] != 'open' && $post_data['comment_status'] != 'closed' )
1338 unset( $post_data['comment_status'] );
1340 if ( isset( $post_data['ping_status'] ) && $post_data['ping_status'] != 'open' && $post_data['ping_status'] != 'closed' )
1341 unset( $post_data['ping_status'] );
1343 // Do some timestamp voodoo.
1344 if ( ! empty( $post_data['post_date_gmt'] ) ) {
1345 // We know this is supposed to be GMT, so we're going to slap that Z on there by force.
1346 $dateCreated = rtrim( $post_data['post_date_gmt']->getIso(), 'Z' ) . 'Z';
1347 } elseif ( ! empty( $post_data['post_date'] ) ) {
1348 $dateCreated = $post_data['post_date']->getIso();
1351 if ( ! empty( $dateCreated ) ) {
1352 $post_data['post_date'] = iso8601_to_datetime( $dateCreated );
1353 $post_data['post_date_gmt'] = get_gmt_from_date( $post_data['post_date'] );
1356 if ( ! isset( $post_data['ID'] ) )
1357 $post_data['ID'] = get_default_post_to_edit( $post_data['post_type'], true )->ID;
1358 $post_ID = $post_data['ID'];
1360 if ( $post_data['post_type'] == 'post' ) {
1361 $error = $this->_toggle_sticky( $post_data, $update );
1367 if ( isset( $post_data['post_thumbnail'] ) ) {
1368 // empty value deletes, non-empty value adds/updates.
1369 if ( ! $post_data['post_thumbnail'] )
1370 delete_post_thumbnail( $post_ID );
1371 elseif ( ! get_post( absint( $post_data['post_thumbnail'] ) ) )
1372 return new IXR_Error( 404, __( 'Invalid attachment ID.' ) );
1373 set_post_thumbnail( $post_ID, $post_data['post_thumbnail'] );
1374 unset( $content_struct['post_thumbnail'] );
1377 if ( isset( $post_data['custom_fields'] ) )
1378 $this->set_custom_fields( $post_ID, $post_data['custom_fields'] );
1380 if ( isset( $post_data['terms'] ) || isset( $post_data['terms_names'] ) ) {
1381 $post_type_taxonomies = get_object_taxonomies( $post_data['post_type'], 'objects' );
1383 // Accumulate term IDs from terms and terms_names.
1386 // First validate the terms specified by ID.
1387 if ( isset( $post_data['terms'] ) && is_array( $post_data['terms'] ) ) {
1388 $taxonomies = array_keys( $post_data['terms'] );
1390 // Validating term ids.
1391 foreach ( $taxonomies as $taxonomy ) {
1392 if ( ! array_key_exists( $taxonomy , $post_type_taxonomies ) )
1393 return new IXR_Error( 401, __( 'Sorry, one of the given taxonomies is not supported by the post type.' ) );
1395 if ( ! current_user_can( $post_type_taxonomies[$taxonomy]->cap->assign_terms ) )
1396 return new IXR_Error( 401, __( 'Sorry, you are not allowed to assign a term to one of the given taxonomies.' ) );
1398 $term_ids = $post_data['terms'][$taxonomy];
1399 $terms[ $taxonomy ] = array();
1400 foreach ( $term_ids as $term_id ) {
1401 $term = get_term_by( 'id', $term_id, $taxonomy );
1404 return new IXR_Error( 403, __( 'Invalid term ID' ) );
1406 $terms[$taxonomy][] = (int) $term_id;
1411 // Now validate terms specified by name.
1412 if ( isset( $post_data['terms_names'] ) && is_array( $post_data['terms_names'] ) ) {
1413 $taxonomies = array_keys( $post_data['terms_names'] );
1415 foreach ( $taxonomies as $taxonomy ) {
1416 if ( ! array_key_exists( $taxonomy , $post_type_taxonomies ) )
1417 return new IXR_Error( 401, __( 'Sorry, one of the given taxonomies is not supported by the post type.' ) );
1419 if ( ! current_user_can( $post_type_taxonomies[$taxonomy]->cap->assign_terms ) )
1420 return new IXR_Error( 401, __( 'Sorry, you are not allowed to assign a term to one of the given taxonomies.' ) );
1423 * For hierarchical taxonomies, we can't assign a term when multiple terms
1424 * in the hierarchy share the same name.
1426 $ambiguous_terms = array();
1427 if ( is_taxonomy_hierarchical( $taxonomy ) ) {
1428 $tax_term_names = get_terms( $taxonomy, array( 'fields' => 'names', 'hide_empty' => false ) );
1430 // Count the number of terms with the same name.
1431 $tax_term_names_count = array_count_values( $tax_term_names );
1433 // Filter out non-ambiguous term names.
1434 $ambiguous_tax_term_counts = array_filter( $tax_term_names_count, array( $this, '_is_greater_than_one') );
1436 $ambiguous_terms = array_keys( $ambiguous_tax_term_counts );
1439 $term_names = $post_data['terms_names'][$taxonomy];
1440 foreach ( $term_names as $term_name ) {
1441 if ( in_array( $term_name, $ambiguous_terms ) )
1442 return new IXR_Error( 401, __( 'Ambiguous term name used in a hierarchical taxonomy. Please use term ID instead.' ) );
1444 $term = get_term_by( 'name', $term_name, $taxonomy );
1447 // Term doesn't exist, so check that the user is allowed to create new terms.
1448 if ( ! current_user_can( $post_type_taxonomies[$taxonomy]->cap->edit_terms ) )
1449 return new IXR_Error( 401, __( 'Sorry, you are not allowed to add a term to one of the given taxonomies.' ) );
1451 // Create the new term.
1452 $term_info = wp_insert_term( $term_name, $taxonomy );
1453 if ( is_wp_error( $term_info ) )
1454 return new IXR_Error( 500, $term_info->get_error_message() );
1456 $terms[$taxonomy][] = (int) $term_info['term_id'];
1458 $terms[$taxonomy][] = (int) $term->term_id;
1464 $post_data['tax_input'] = $terms;
1465 unset( $post_data['terms'], $post_data['terms_names'] );
1467 // Do not allow direct submission of 'tax_input', clients must use 'terms' and/or 'terms_names'.
1468 unset( $post_data['tax_input'], $post_data['post_category'], $post_data['tags_input'] );
1471 if ( isset( $post_data['post_format'] ) ) {
1472 $format = set_post_format( $post_ID, $post_data['post_format'] );
1474 if ( is_wp_error( $format ) )
1475 return new IXR_Error( 500, $format->get_error_message() );
1477 unset( $post_data['post_format'] );
1480 // Handle enclosures.
1481 $enclosure = isset( $post_data['enclosure'] ) ? $post_data['enclosure'] : null;
1482 $this->add_enclosure_if_new( $post_ID, $enclosure );
1484 $this->attach_uploads( $post_ID, $post_data['post_content'] );
1487 * Filter post data array to be inserted via XML-RPC.
1491 * @param array $post_data Parsed array of post data.
1492 * @param array $content_struct Post data array.
1494 $post_data = apply_filters( 'xmlrpc_wp_insert_post_data', $post_data, $content_struct );
1496 $post_ID = $update ? wp_update_post( $post_data, true ) : wp_insert_post( $post_data, true );
1497 if ( is_wp_error( $post_ID ) )
1498 return new IXR_Error( 500, $post_ID->get_error_message() );
1501 return new IXR_Error( 401, __( 'Sorry, your entry could not be posted. Something wrong happened.' ) );
1503 return strval( $post_ID );
1507 * Edit a post for any registered post type.
1509 * The $content_struct parameter only needs to contain fields that
1510 * should be changed. All other fields will retain their existing values.
1514 * @param array $args {
1515 * Method arguments. Note: arguments must be ordered as documented.
1517 * @type int $blog_id Blog ID (unused).
1518 * @type string $username Username.
1519 * @type string $password Password.
1520 * @type int $post_id Post ID.
1521 * @type array $content_struct Extra content arguments.
1523 * @return true|IXR_Error True on success, IXR_Error on failure.
1525 public function wp_editPost( $args ) {
1526 if ( ! $this->minimum_args( $args, 5 ) )
1527 return $this->error;
1529 $this->escape( $args );
1531 $username = $args[1];
1532 $password = $args[2];
1533 $post_id = (int) $args[3];
1534 $content_struct = $args[4];
1536 if ( ! $user = $this->login( $username, $password ) )
1537 return $this->error;
1539 /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */
1540 do_action( 'xmlrpc_call', 'wp.editPost' );
1542 $post = get_post( $post_id, ARRAY_A );
1544 if ( empty( $post['ID'] ) )
1545 return new IXR_Error( 404, __( 'Invalid post ID.' ) );
1547 if ( isset( $content_struct['if_not_modified_since'] ) ) {
1548 // If the post has been modified since the date provided, return an error.
1549 if ( mysql2date( 'U', $post['post_modified_gmt'] ) > $content_struct['if_not_modified_since']->getTimestamp() ) {
1550 return new IXR_Error( 409, __( 'There is a revision of this post that is more recent.' ) );
1554 // Convert the date field back to IXR form.
1555 $post['post_date'] = $this->_convert_date( $post['post_date'] );
1558 * Ignore the existing GMT date if it is empty or a non-GMT date was supplied in $content_struct,
1559 * since _insert_post() will ignore the non-GMT date if the GMT date is set.
1561 if ( $post['post_date_gmt'] == '0000-00-00 00:00:00' || isset( $content_struct['post_date'] ) )
1562 unset( $post['post_date_gmt'] );
1564 $post['post_date_gmt'] = $this->_convert_date( $post['post_date_gmt'] );
1566 $this->escape( $post );
1567 $merged_content_struct = array_merge( $post, $content_struct );
1569 $retval = $this->_insert_post( $user, $merged_content_struct );
1570 if ( $retval instanceof IXR_Error )
1577 * Delete a post for any registered post type.
1581 * @see wp_delete_post()
1583 * @param array $args {
1584 * Method arguments. Note: arguments must be ordered as documented.
1586 * @type int $blog_id Blog ID (unused).
1587 * @type string $username Username.
1588 * @type string $password Password.
1589 * @type int $post_id Post ID.
1591 * @return true|IXR_Error True on success, IXR_Error instance on failure.
1593 public function wp_deletePost( $args ) {
1594 if ( ! $this->minimum_args( $args, 4 ) )
1595 return $this->error;
1597 $this->escape( $args );
1599 $username = $args[1];
1600 $password = $args[2];
1601 $post_id = (int) $args[3];
1603 if ( ! $user = $this->login( $username, $password ) )
1604 return $this->error;
1606 /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */
1607 do_action( 'xmlrpc_call', 'wp.deletePost' );
1609 $post = get_post( $post_id, ARRAY_A );
1610 if ( empty( $post['ID'] ) ) {
1611 return new IXR_Error( 404, __( 'Invalid post ID.' ) );
1614 if ( ! current_user_can( 'delete_post', $post_id ) ) {
1615 return new IXR_Error( 401, __( 'Sorry, you do not have the right to delete this post.' ) );
1618 $result = wp_delete_post( $post_id );
1621 return new IXR_Error( 500, __( 'The post cannot be deleted.' ) );
1632 * The optional $fields parameter specifies what fields will be included
1633 * in the response array. This should be a list of field names. 'post_id' will
1634 * always be included in the response regardless of the value of $fields.
1636 * Instead of, or in addition to, individual field names, conceptual group
1637 * names can be used to specify multiple fields. The available conceptual
1638 * groups are 'post' (all basic fields), 'taxonomies', 'custom_fields',
1643 * @param array $args {
1644 * Method arguments. Note: arguments must be ordered as documented.
1646 * @type int $blog_id Blog ID (unused).
1647 * @type string $username Username.
1648 * @type string $password Password.
1649 * @type int $post_id Post ID.
1650 * @type array $fields The subset of post type fields to return.
1652 * @return array|IXR_Error Array contains (based on $fields parameter):
1658 * - 'post_modified_gmt'
1667 * - 'comment_status'
1676 public function wp_getPost( $args ) {
1677 if ( ! $this->minimum_args( $args, 4 ) )
1678 return $this->error;
1680 $this->escape( $args );
1682 $username = $args[1];
1683 $password = $args[2];
1684 $post_id = (int) $args[3];
1686 if ( isset( $args[4] ) ) {
1690 * Filter the list of post query fields used by the given XML-RPC method.
1694 * @param array $fields Array of post fields. Default array contains 'post', 'terms', and 'custom_fields'.
1695 * @param string $method Method name.
1697 $fields = apply_filters( 'xmlrpc_default_post_fields', array( 'post', 'terms', 'custom_fields' ), 'wp.getPost' );
1700 if ( ! $user = $this->login( $username, $password ) )
1701 return $this->error;
1703 /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */
1704 do_action( 'xmlrpc_call', 'wp.getPost' );
1706 $post = get_post( $post_id, ARRAY_A );
1708 if ( empty( $post['ID'] ) )
1709 return new IXR_Error( 404, __( 'Invalid post ID.' ) );
1711 if ( ! current_user_can( 'edit_post', $post_id ) )
1712 return new IXR_Error( 401, __( 'Sorry, you cannot edit this post.' ) );
1714 return $this->_prepare_post( $post, $fields );
1722 * @see wp_get_recent_posts()
1723 * @see wp_getPost() for more on `$fields`
1724 * @see get_posts() for more on `$filter` values
1726 * @param array $args {
1727 * Method arguments. Note: arguments must be ordered as documented.
1729 * @type int $blog_id Blog ID (unused).
1730 * @type string $username Username.
1731 * @type string $password Password.
1732 * @type array $filter Optional. Modifies the query used to retrieve posts. Accepts 'post_type',
1733 * 'post_status', 'number', 'offset', 'orderby', 's', and 'order'.
1734 * Default empty array.
1735 * @type array $fields Optional. The subset of post type fields to return in the response array.
1737 * @return array|IXR_Error Array contains a collection of posts.
1739 public function wp_getPosts( $args ) {
1740 if ( ! $this->minimum_args( $args, 3 ) )
1741 return $this->error;
1743 $this->escape( $args );
1745 $username = $args[1];
1746 $password = $args[2];
1747 $filter = isset( $args[3] ) ? $args[3] : array();
1749 if ( isset( $args[4] ) ) {
1752 /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */
1753 $fields = apply_filters( 'xmlrpc_default_post_fields', array( 'post', 'terms', 'custom_fields' ), 'wp.getPosts' );
1756 if ( ! $user = $this->login( $username, $password ) )
1757 return $this->error;
1759 /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */
1760 do_action( 'xmlrpc_call', 'wp.getPosts' );
1764 if ( isset( $filter['post_type'] ) ) {
1765 $post_type = get_post_type_object( $filter['post_type'] );
1766 if ( ! ( (bool) $post_type ) )
1767 return new IXR_Error( 403, __( 'The post type specified is not valid' ) );
1769 $post_type = get_post_type_object( 'post' );
1772 if ( ! current_user_can( $post_type->cap->edit_posts ) )
1773 return new IXR_Error( 401, __( 'You are not allowed to edit posts in this post type.' ));
1775 $query['post_type'] = $post_type->name;
1777 if ( isset( $filter['post_status'] ) )
1778 $query['post_status'] = $filter['post_status'];
1780 if ( isset( $filter['number'] ) )
1781 $query['numberposts'] = absint( $filter['number'] );
1783 if ( isset( $filter['offset'] ) )
1784 $query['offset'] = absint( $filter['offset'] );
1786 if ( isset( $filter['orderby'] ) ) {
1787 $query['orderby'] = $filter['orderby'];
1789 if ( isset( $filter['order'] ) )
1790 $query['order'] = $filter['order'];
1793 if ( isset( $filter['s'] ) ) {
1794 $query['s'] = $filter['s'];
1797 $posts_list = wp_get_recent_posts( $query );
1799 if ( ! $posts_list )
1802 // Holds all the posts data.
1805 foreach ( $posts_list as $post ) {
1806 if ( ! current_user_can( 'edit_post', $post['ID'] ) )
1809 $struct[] = $this->_prepare_post( $post, $fields );
1816 * Create a new term.
1820 * @see wp_insert_term()
1822 * @param array $args {
1823 * Method arguments. Note: arguments must be ordered as documented.
1825 * @type int $blog_id Blog ID (unused).
1826 * @type string $username Username.
1827 * @type string $password Password.
1828 * @type array $content_struct Content struct for adding a new term. The struct must contain
1829 * the term 'name' and 'taxonomy'. Optional accepted values include
1830 * 'parent', 'description', and 'slug'.
1832 * @return int|IXR_Error The term ID on success, or an IXR_Error object on failure.
1834 public function wp_newTerm( $args ) {
1835 if ( ! $this->minimum_args( $args, 4 ) )
1836 return $this->error;
1838 $this->escape( $args );
1840 $username = $args[1];
1841 $password = $args[2];
1842 $content_struct = $args[3];
1844 if ( ! $user = $this->login( $username, $password ) )
1845 return $this->error;
1847 /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */
1848 do_action( 'xmlrpc_call', 'wp.newTerm' );
1850 if ( ! taxonomy_exists( $content_struct['taxonomy'] ) )
1851 return new IXR_Error( 403, __( 'Invalid taxonomy' ) );
1853 $taxonomy = get_taxonomy( $content_struct['taxonomy'] );
1855 if ( ! current_user_can( $taxonomy->cap->manage_terms ) )
1856 return new IXR_Error( 401, __( 'You are not allowed to create terms in this taxonomy.' ) );
1858 $taxonomy = (array) $taxonomy;
1860 // hold the data of the term
1861 $term_data = array();
1863 $term_data['name'] = trim( $content_struct['name'] );
1864 if ( empty( $term_data['name'] ) )
1865 return new IXR_Error( 403, __( 'The term name cannot be empty.' ) );
1867 if ( isset( $content_struct['parent'] ) ) {
1868 if ( ! $taxonomy['hierarchical'] )
1869 return new IXR_Error( 403, __( 'This taxonomy is not hierarchical.' ) );
1871 $parent_term_id = (int) $content_struct['parent'];
1872 $parent_term = get_term( $parent_term_id , $taxonomy['name'] );
1874 if ( is_wp_error( $parent_term ) )
1875 return new IXR_Error( 500, $parent_term->get_error_message() );
1877 if ( ! $parent_term )
1878 return new IXR_Error( 403, __( 'Parent term does not exist.' ) );
1880 $term_data['parent'] = $content_struct['parent'];
1883 if ( isset( $content_struct['description'] ) )
1884 $term_data['description'] = $content_struct['description'];
1886 if ( isset( $content_struct['slug'] ) )
1887 $term_data['slug'] = $content_struct['slug'];
1889 $term = wp_insert_term( $term_data['name'] , $taxonomy['name'] , $term_data );
1891 if ( is_wp_error( $term ) )
1892 return new IXR_Error( 500, $term->get_error_message() );
1895 return new IXR_Error( 500, __( 'Sorry, your term could not be created. Something wrong happened.' ) );
1897 return strval( $term['term_id'] );
1905 * @see wp_update_term()
1907 * @param array $args {
1908 * Method arguments. Note: arguments must be ordered as documented.
1910 * @type int $blog_id Blog ID (unused).
1911 * @type string $username Username.
1912 * @type string $password Password.
1913 * @type int $term_id Term ID.
1914 * @type array $content_struct Content struct for editing a term. The struct must contain the
1915 * term ''taxonomy'. Optional accepted values include 'name', 'parent',
1916 * 'description', and 'slug'.
1918 * @return true|IXR_Error True on success, IXR_Error instance on failure.
1920 public function wp_editTerm( $args ) {
1921 if ( ! $this->minimum_args( $args, 5 ) )
1922 return $this->error;
1924 $this->escape( $args );
1926 $username = $args[1];
1927 $password = $args[2];
1928 $term_id = (int) $args[3];
1929 $content_struct = $args[4];
1931 if ( ! $user = $this->login( $username, $password ) )
1932 return $this->error;
1934 /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */
1935 do_action( 'xmlrpc_call', 'wp.editTerm' );
1937 if ( ! taxonomy_exists( $content_struct['taxonomy'] ) )
1938 return new IXR_Error( 403, __( 'Invalid taxonomy' ) );
1940 $taxonomy = get_taxonomy( $content_struct['taxonomy'] );
1942 if ( ! current_user_can( $taxonomy->cap->edit_terms ) )
1943 return new IXR_Error( 401, __( 'You are not allowed to edit terms in this taxonomy.' ) );
1945 $taxonomy = (array) $taxonomy;
1947 // hold the data of the term
1948 $term_data = array();
1950 $term = get_term( $term_id , $content_struct['taxonomy'] );
1952 if ( is_wp_error( $term ) )
1953 return new IXR_Error( 500, $term->get_error_message() );
1956 return new IXR_Error( 404, __( 'Invalid term ID' ) );
1958 if ( isset( $content_struct['name'] ) ) {
1959 $term_data['name'] = trim( $content_struct['name'] );
1961 if ( empty( $term_data['name'] ) )
1962 return new IXR_Error( 403, __( 'The term name cannot be empty.' ) );
1965 if ( ! empty( $content_struct['parent'] ) ) {
1966 if ( ! $taxonomy['hierarchical'] )
1967 return new IXR_Error( 403, __( "This taxonomy is not hierarchical so you can't set a parent." ) );
1969 $parent_term_id = (int) $content_struct['parent'];
1970 $parent_term = get_term( $parent_term_id , $taxonomy['name'] );
1972 if ( is_wp_error( $parent_term ) )
1973 return new IXR_Error( 500, $parent_term->get_error_message() );
1975 if ( ! $parent_term )
1976 return new IXR_Error( 403, __( 'Parent term does not exist.' ) );
1978 $term_data['parent'] = $content_struct['parent'];
1981 if ( isset( $content_struct['description'] ) )
1982 $term_data['description'] = $content_struct['description'];
1984 if ( isset( $content_struct['slug'] ) )
1985 $term_data['slug'] = $content_struct['slug'];
1987 $term = wp_update_term( $term_id , $taxonomy['name'] , $term_data );
1989 if ( is_wp_error( $term ) )
1990 return new IXR_Error( 500, $term->get_error_message() );
1993 return new IXR_Error( 500, __( 'Sorry, editing the term failed.' ) );
2003 * @see wp_delete_term()
2005 * @param array $args {
2006 * Method arguments. Note: arguments must be ordered as documented.
2008 * @type int $blog_id Blog ID (unused).
2009 * @type string $username Username.
2010 * @type string $password Password.
2011 * @type string $taxnomy_name Taxonomy name.
2012 * @type int $term_id Term ID.
2014 * @return bool|IXR_Error True on success, IXR_Error instance on failure.
2016 public function wp_deleteTerm( $args ) {
2017 if ( ! $this->minimum_args( $args, 5 ) )
2018 return $this->error;
2020 $this->escape( $args );
2022 $username = $args[1];
2023 $password = $args[2];
2024 $taxonomy = $args[3];
2025 $term_id = (int) $args[4];
2027 if ( ! $user = $this->login( $username, $password ) )
2028 return $this->error;
2030 /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */
2031 do_action( 'xmlrpc_call', 'wp.deleteTerm' );
2033 if ( ! taxonomy_exists( $taxonomy ) )
2034 return new IXR_Error( 403, __( 'Invalid taxonomy' ) );
2036 $taxonomy = get_taxonomy( $taxonomy );
2038 if ( ! current_user_can( $taxonomy->cap->delete_terms ) )
2039 return new IXR_Error( 401, __( 'You are not allowed to delete terms in this taxonomy.' ) );
2041 $term = get_term( $term_id, $taxonomy->name );
2043 if ( is_wp_error( $term ) )
2044 return new IXR_Error( 500, $term->get_error_message() );
2047 return new IXR_Error( 404, __( 'Invalid term ID' ) );
2049 $result = wp_delete_term( $term_id, $taxonomy->name );
2051 if ( is_wp_error( $result ) )
2052 return new IXR_Error( 500, $term->get_error_message() );
2055 return new IXR_Error( 500, __( 'Sorry, deleting the term failed.' ) );
2067 * @param array $args {
2068 * Method arguments. Note: arguments must be ordered as documented.
2070 * @type int $blog_id Blog ID (unused).
2071 * @type string $username Username.
2072 * @type string $password Password.
2073 * @type string $taxnomy Taxonomy name.
2074 * @type string $term_id Term ID.
2076 * @return array|IXR_Error IXR_Error on failure, array on success, containing:
2081 * - 'term_taxonomy_id'
2087 public function wp_getTerm( $args ) {
2088 if ( ! $this->minimum_args( $args, 5 ) )
2089 return $this->error;
2091 $this->escape( $args );
2093 $username = $args[1];
2094 $password = $args[2];
2095 $taxonomy = $args[3];
2096 $term_id = (int) $args[4];
2098 if ( ! $user = $this->login( $username, $password ) )
2099 return $this->error;
2101 /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */
2102 do_action( 'xmlrpc_call', 'wp.getTerm' );
2104 if ( ! taxonomy_exists( $taxonomy ) )
2105 return new IXR_Error( 403, __( 'Invalid taxonomy' ) );
2107 $taxonomy = get_taxonomy( $taxonomy );
2109 if ( ! current_user_can( $taxonomy->cap->assign_terms ) )
2110 return new IXR_Error( 401, __( 'You are not allowed to assign terms in this taxonomy.' ) );
2112 $term = get_term( $term_id , $taxonomy->name, ARRAY_A );
2114 if ( is_wp_error( $term ) )
2115 return new IXR_Error( 500, $term->get_error_message() );
2118 return new IXR_Error( 404, __( 'Invalid term ID' ) );
2120 return $this->_prepare_term( $term );
2124 * Retrieve all terms for a taxonomy.
2128 * The optional $filter parameter modifies the query used to retrieve terms.
2129 * Accepted keys are 'number', 'offset', 'orderby', 'order', 'hide_empty', and 'search'.
2133 * @param array $args {
2134 * Method arguments. Note: arguments must be ordered as documented.
2136 * @type int $blog_id Blog ID (unused).
2137 * @type string $username Username.
2138 * @type string $password Password.
2139 * @type string $taxnomy Taxonomy name.
2140 * @type array $filter Optional. Modifies the query used to retrieve posts. Accepts 'number',
2141 * 'offset', 'orderby', 'order', 'hide_empty', and 'search'. Default empty array.
2143 * @return array|IXR_Error An associative array of terms data on success, IXR_Error instance otherwise.
2145 public function wp_getTerms( $args ) {
2146 if ( ! $this->minimum_args( $args, 4 ) )
2147 return $this->error;
2149 $this->escape( $args );
2151 $username = $args[1];
2152 $password = $args[2];
2153 $taxonomy = $args[3];
2154 $filter = isset( $args[4] ) ? $args[4] : array();
2156 if ( ! $user = $this->login( $username, $password ) )
2157 return $this->error;
2159 /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */
2160 do_action( 'xmlrpc_call', 'wp.getTerms' );
2162 if ( ! taxonomy_exists( $taxonomy ) )
2163 return new IXR_Error( 403, __( 'Invalid taxonomy' ) );
2165 $taxonomy = get_taxonomy( $taxonomy );
2167 if ( ! current_user_can( $taxonomy->cap->assign_terms ) )
2168 return new IXR_Error( 401, __( 'You are not allowed to assign terms in this taxonomy.' ) );
2172 if ( isset( $filter['number'] ) )
2173 $query['number'] = absint( $filter['number'] );
2175 if ( isset( $filter['offset'] ) )
2176 $query['offset'] = absint( $filter['offset'] );
2178 if ( isset( $filter['orderby'] ) ) {
2179 $query['orderby'] = $filter['orderby'];
2181 if ( isset( $filter['order'] ) )
2182 $query['order'] = $filter['order'];
2185 if ( isset( $filter['hide_empty'] ) )
2186 $query['hide_empty'] = $filter['hide_empty'];
2188 $query['get'] = 'all';
2190 if ( isset( $filter['search'] ) )
2191 $query['search'] = $filter['search'];
2193 $terms = get_terms( $taxonomy->name, $query );
2195 if ( is_wp_error( $terms ) )
2196 return new IXR_Error( 500, $terms->get_error_message() );
2200 foreach ( $terms as $term ) {
2201 $struct[] = $this->_prepare_term( $term );
2208 * Retrieve a taxonomy.
2212 * @see get_taxonomy()
2214 * @param array $args {
2215 * Method arguments. Note: arguments must be ordered as documented.
2217 * @type int $blog_id Blog ID (unused).
2218 * @type string $username Username.
2219 * @type string $password Password.
2220 * @type string $taxnomy Taxonomy name.
2221 * @type array $fields Optional. Array of taxonomy fields to limit to in the return.
2222 * Accepts 'labels', 'cap', 'menu', and 'object_type'.
2223 * Default empty array.
2225 * @return array|IXR_Error An array of taxonomy data on success, IXR_Error instance otherwise.
2227 public function wp_getTaxonomy( $args ) {
2228 if ( ! $this->minimum_args( $args, 4 ) )
2229 return $this->error;
2231 $this->escape( $args );
2233 $username = $args[1];
2234 $password = $args[2];
2235 $taxonomy = $args[3];
2237 if ( isset( $args[4] ) ) {
2241 * Filter the taxonomy query fields used by the given XML-RPC method.
2245 * @param array $fields An array of taxonomy fields to retrieve.
2246 * @param string $method The method name.
2248 $fields = apply_filters( 'xmlrpc_default_taxonomy_fields', array( 'labels', 'cap', 'object_type' ), 'wp.getTaxonomy' );
2251 if ( ! $user = $this->login( $username, $password ) )
2252 return $this->error;
2254 /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */
2255 do_action( 'xmlrpc_call', 'wp.getTaxonomy' );
2257 if ( ! taxonomy_exists( $taxonomy ) )
2258 return new IXR_Error( 403, __( 'Invalid taxonomy' ) );
2260 $taxonomy = get_taxonomy( $taxonomy );
2262 if ( ! current_user_can( $taxonomy->cap->assign_terms ) )
2263 return new IXR_Error( 401, __( 'You are not allowed to assign terms in this taxonomy.' ) );
2265 return $this->_prepare_taxonomy( $taxonomy, $fields );
2269 * Retrieve all taxonomies.
2273 * @see get_taxonomies()
2275 * @param array $args {
2276 * Method arguments. Note: arguments must be ordered as documented.
2278 * @type int $blog_id Blog ID (unused).
2279 * @type string $username Username.
2280 * @type string $password Password.
2281 * @type array $filter Optional. An array of arguments for retrieving taxonomies.
2282 * @type array $fields Optional. The subset of taxonomy fields to return.
2284 * @return array|IXR_Error An associative array of taxonomy data with returned fields determined
2285 * by `$fields`, or an IXR_Error instance on failure.
2287 public function wp_getTaxonomies( $args ) {
2288 if ( ! $this->minimum_args( $args, 3 ) )
2289 return $this->error;
2291 $this->escape( $args );
2293 $username = $args[1];
2294 $password = $args[2];
2295 $filter = isset( $args[3] ) ? $args[3] : array( 'public' => true );
2297 if ( isset( $args[4] ) ) {
2300 /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */
2301 $fields = apply_filters( 'xmlrpc_default_taxonomy_fields', array( 'labels', 'cap', 'object_type' ), 'wp.getTaxonomies' );
2304 if ( ! $user = $this->login( $username, $password ) )
2305 return $this->error;
2307 /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */
2308 do_action( 'xmlrpc_call', 'wp.getTaxonomies' );
2310 $taxonomies = get_taxonomies( $filter, 'objects' );
2312 // holds all the taxonomy data
2315 foreach ( $taxonomies as $taxonomy ) {
2316 // capability check for post_types
2317 if ( ! current_user_can( $taxonomy->cap->assign_terms ) )
2320 $struct[] = $this->_prepare_taxonomy( $taxonomy, $fields );
2329 * The optional $fields parameter specifies what fields will be included
2330 * in the response array. This should be a list of field names. 'user_id' will
2331 * always be included in the response regardless of the value of $fields.
2333 * Instead of, or in addition to, individual field names, conceptual group
2334 * names can be used to specify multiple fields. The available conceptual
2335 * groups are 'basic' and 'all'.
2337 * @uses get_userdata()
2339 * @param array $args {
2340 * Method arguments. Note: arguments must be ordered as documented.
2342 * @type int $blog_id (unused)
2343 * @type string $username
2344 * @type string $password
2345 * @type int $user_id
2346 * @type array $fields (optional)
2348 * @return array|IXR_Error Array contains (based on $fields parameter):
2362 public function wp_getUser( $args ) {
2363 if ( ! $this->minimum_args( $args, 4 ) )
2364 return $this->error;
2366 $this->escape( $args );
2368 $username = $args[1];
2369 $password = $args[2];
2370 $user_id = (int) $args[3];
2372 if ( isset( $args[4] ) ) {
2376 * Filter the default user query fields used by the given XML-RPC method.
2380 * @param array $fields User query fields for given method. Default 'all'.
2381 * @param string $method The method name.
2383 $fields = apply_filters( 'xmlrpc_default_user_fields', array( 'all' ), 'wp.getUser' );
2386 if ( ! $user = $this->login( $username, $password ) )
2387 return $this->error;
2389 /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */
2390 do_action( 'xmlrpc_call', 'wp.getUser' );
2392 if ( ! current_user_can( 'edit_user', $user_id ) )
2393 return new IXR_Error( 401, __( 'Sorry, you cannot edit users.' ) );
2395 $user_data = get_userdata( $user_id );
2398 return new IXR_Error( 404, __( 'Invalid user ID.' ) );
2400 return $this->_prepare_user( $user_data, $fields );
2406 * The optional $filter parameter modifies the query used to retrieve users.
2407 * Accepted keys are 'number' (default: 50), 'offset' (default: 0), 'role',
2408 * 'who', 'orderby', and 'order'.
2410 * The optional $fields parameter specifies what fields will be included
2411 * in the response array.
2414 * @see wp_getUser() for more on $fields and return values
2416 * @param array $args {
2417 * Method arguments. Note: arguments must be ordered as documented.
2419 * @type int $blog_id (unused)
2420 * @type string $username
2421 * @type string $password
2422 * @type array $filter (optional)
2423 * @type array $fields (optional)
2425 * @return array|IXR_Error users data
2427 public function wp_getUsers( $args ) {
2428 if ( ! $this->minimum_args( $args, 3 ) )
2429 return $this->error;
2431 $this->escape( $args );
2433 $username = $args[1];
2434 $password = $args[2];
2435 $filter = isset( $args[3] ) ? $args[3] : array();
2437 if ( isset( $args[4] ) ) {
2440 /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */
2441 $fields = apply_filters( 'xmlrpc_default_user_fields', array( 'all' ), 'wp.getUsers' );
2444 if ( ! $user = $this->login( $username, $password ) )
2445 return $this->error;
2447 /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */
2448 do_action( 'xmlrpc_call', 'wp.getUsers' );
2450 if ( ! current_user_can( 'list_users' ) )
2451 return new IXR_Error( 401, __( 'You are not allowed to browse users.' ) );
2453 $query = array( 'fields' => 'all_with_meta' );
2455 $query['number'] = ( isset( $filter['number'] ) ) ? absint( $filter['number'] ) : 50;
2456 $query['offset'] = ( isset( $filter['offset'] ) ) ? absint( $filter['offset'] ) : 0;
2458 if ( isset( $filter['orderby'] ) ) {
2459 $query['orderby'] = $filter['orderby'];
2461 if ( isset( $filter['order'] ) )
2462 $query['order'] = $filter['order'];
2465 if ( isset( $filter['role'] ) ) {
2466 if ( get_role( $filter['role'] ) === null )
2467 return new IXR_Error( 403, __( 'The role specified is not valid' ) );
2469 $query['role'] = $filter['role'];
2472 if ( isset( $filter['who'] ) ) {
2473 $query['who'] = $filter['who'];
2476 $users = get_users( $query );
2479 foreach ( $users as $user_data ) {
2480 if ( current_user_can( 'edit_user', $user_data->ID ) )
2481 $_users[] = $this->_prepare_user( $user_data, $fields );
2487 * Retrieve information about the requesting user.
2489 * @uses get_userdata()
2491 * @param array $args {
2492 * Method arguments. Note: arguments must be ordered as documented.
2494 * @type int $blog_id (unused)
2495 * @type string $username
2496 * @type string $password
2497 * @type array $fields (optional)
2499 * @return array|IXR_Error (@see wp_getUser)
2501 public function wp_getProfile( $args ) {
2502 if ( ! $this->minimum_args( $args, 3 ) )
2503 return $this->error;
2505 $this->escape( $args );
2507 $username = $args[1];
2508 $password = $args[2];
2510 if ( isset( $args[3] ) ) {
2513 /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */
2514 $fields = apply_filters( 'xmlrpc_default_user_fields', array( 'all' ), 'wp.getProfile' );
2517 if ( ! $user = $this->login( $username, $password ) )
2518 return $this->error;
2520 /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */
2521 do_action( 'xmlrpc_call', 'wp.getProfile' );
2523 if ( ! current_user_can( 'edit_user', $user->ID ) )
2524 return new IXR_Error( 401, __( 'Sorry, you cannot edit your profile.' ) );
2526 $user_data = get_userdata( $user->ID );
2528 return $this->_prepare_user( $user_data, $fields );
2532 * Edit user's profile.
2534 * @uses wp_update_user()
2536 * @param array $args {
2537 * Method arguments. Note: arguments must be ordered as documented.
2539 * @type int $blog_id (unused)
2540 * @type string $username
2541 * @type string $password
2542 * @type array $content_struct It can optionally contain:
2551 * @return true|IXR_Error True, on success.
2553 public function wp_editProfile( $args ) {
2554 if ( ! $this->minimum_args( $args, 4 ) )
2555 return $this->error;
2557 $this->escape( $args );
2559 $username = $args[1];
2560 $password = $args[2];
2561 $content_struct = $args[3];
2563 if ( ! $user = $this->login( $username, $password ) )
2564 return $this->error;
2566 /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */
2567 do_action( 'xmlrpc_call', 'wp.editProfile' );
2569 if ( ! current_user_can( 'edit_user', $user->ID ) )
2570 return new IXR_Error( 401, __( 'Sorry, you cannot edit your profile.' ) );
2572 // holds data of the user
2573 $user_data = array();
2574 $user_data['ID'] = $user->ID;
2576 // only set the user details if it was given
2577 if ( isset( $content_struct['first_name'] ) )
2578 $user_data['first_name'] = $content_struct['first_name'];
2580 if ( isset( $content_struct['last_name'] ) )
2581 $user_data['last_name'] = $content_struct['last_name'];
2583 if ( isset( $content_struct['url'] ) )
2584 $user_data['user_url'] = $content_struct['url'];
2586 if ( isset( $content_struct['display_name'] ) )
2587 $user_data['display_name'] = $content_struct['display_name'];
2589 if ( isset( $content_struct['nickname'] ) )
2590 $user_data['nickname'] = $content_struct['nickname'];
2592 if ( isset( $content_struct['nicename'] ) )
2593 $user_data['user_nicename'] = $content_struct['nicename'];
2595 if ( isset( $content_struct['bio'] ) )
2596 $user_data['description'] = $content_struct['bio'];
2598 $result = wp_update_user( $user_data );
2600 if ( is_wp_error( $result ) )
2601 return new IXR_Error( 500, $result->get_error_message() );
2604 return new IXR_Error( 500, __( 'Sorry, the user cannot be updated.' ) );
2614 * @param array $args {
2615 * Method arguments. Note: arguments must be ordered as documented.
2617 * @type int $blog_id (unused)
2618 * @type int $page_id
2619 * @type string $username
2620 * @type string $password
2622 * @return array|IXR_Error
2624 public function wp_getPage( $args ) {
2625 $this->escape( $args );
2627 $page_id = (int) $args[1];
2628 $username = $args[2];
2629 $password = $args[3];
2631 if ( !$user = $this->login($username, $password) ) {
2632 return $this->error;
2635 $page = get_post($page_id);
2637 return new IXR_Error( 404, __( 'Invalid post ID.' ) );
2639 if ( !current_user_can( 'edit_page', $page_id ) )
2640 return new IXR_Error( 401, __( 'Sorry, you cannot edit this page.' ) );
2642 /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */
2643 do_action( 'xmlrpc_call', 'wp.getPage' );
2645 // If we found the page then format the data.
2646 if ( $page->ID && ($page->post_type == 'page') ) {
2647 return $this->_prepare_page( $page );
2649 // If the page doesn't exist indicate that.
2651 return new IXR_Error( 404, __( 'Sorry, no such page.' ) );
2660 * @param array $args {
2661 * Method arguments. Note: arguments must be ordered as documented.
2663 * @type int $blog_id (unused)
2664 * @type string $username
2665 * @type string $password
2666 * @type int $num_pages
2668 * @return array|IXR_Error
2670 public function wp_getPages( $args ) {
2671 $this->escape( $args );
2673 $username = $args[1];
2674 $password = $args[2];
2675 $num_pages = isset($args[3]) ? (int) $args[3] : 10;
2677 if ( !$user = $this->login($username, $password) )
2678 return $this->error;
2680 if ( !current_user_can( 'edit_pages' ) )
2681 return new IXR_Error( 401, __( 'Sorry, you cannot edit pages.' ) );
2683 /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */
2684 do_action( 'xmlrpc_call', 'wp.getPages' );
2686 $pages = get_posts( array('post_type' => 'page', 'post_status' => 'any', 'numberposts' => $num_pages) );
2687 $num_pages = count($pages);
2689 // If we have pages, put together their info.
2690 if ( $num_pages >= 1 ) {
2691 $pages_struct = array();
2693 foreach ($pages as $page) {
2694 if ( current_user_can( 'edit_page', $page->ID ) )
2695 $pages_struct[] = $this->_prepare_page( $page );
2698 return $pages_struct;
2709 * @see wp_xmlrpc_server::mw_newPost()
2711 * @param array $args {
2712 * Method arguments. Note: arguments must be ordered as documented.
2714 * @type int $blog_id (unused)
2715 * @type string $username
2716 * @type string $password
2717 * @type array $content_struct
2719 * @return int|IXR_Error
2721 public function wp_newPage( $args ) {
2722 // Items not escaped here will be escaped in newPost.
2723 $username = $this->escape( $args[1] );
2724 $password = $this->escape( $args[2] );
2726 if ( !$user = $this->login($username, $password) )
2727 return $this->error;
2729 /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */
2730 do_action( 'xmlrpc_call', 'wp.newPage' );
2732 // Mark this as content for a page.
2733 $args[3]["post_type"] = 'page';
2735 // Let mw_newPost do all of the heavy lifting.
2736 return $this->mw_newPost( $args );
2744 * @param array $args {
2745 * Method arguments. Note: arguments must be ordered as documented.
2747 * @type int $blog_id (unused)
2748 * @type string $username
2749 * @type string $password
2750 * @type int $page_id
2752 * @return true|IXR_Error True, if success.
2754 public function wp_deletePage( $args ) {
2755 $this->escape( $args );
2757 $username = $args[1];
2758 $password = $args[2];
2759 $page_id = (int) $args[3];
2761 if ( !$user = $this->login($username, $password) )
2762 return $this->error;
2764 /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */
2765 do_action( 'xmlrpc_call', 'wp.deletePage' );
2767 // Get the current page based on the page_id and
2768 // make sure it is a page and not a post.
2769 $actual_page = get_post($page_id, ARRAY_A);
2770 if ( !$actual_page || ($actual_page['post_type'] != 'page') )
2771 return new IXR_Error( 404, __( 'Sorry, no such page.' ) );
2773 // Make sure the user can delete pages.
2774 if ( !current_user_can('delete_page', $page_id) )
2775 return new IXR_Error( 401, __( 'Sorry, you do not have the right to delete this page.' ) );
2777 // Attempt to delete the page.
2778 $result = wp_delete_post($page_id);
2780 return new IXR_Error( 500, __( 'Failed to delete the page.' ) );
2783 * Fires after a page has been successfully deleted via XML-RPC.
2787 * @param int $page_id ID of the deleted page.
2788 * @param array $args An array of arguments to delete the page.
2790 do_action( 'xmlrpc_call_success_wp_deletePage', $page_id, $args );
2800 * @param array $args {
2801 * Method arguments. Note: arguments must be ordered as documented.
2803 * @type int $blog_id (unused)
2804 * @type int $page_id
2805 * @type string $username
2806 * @type string $password
2807 * @type string $content
2808 * @type string $publish
2810 * @return array|IXR_Error
2812 public function wp_editPage( $args ) {
2813 // Items will be escaped in mw_editPost.
2814 $page_id = (int) $args[1];
2815 $username = $args[2];
2816 $password = $args[3];
2817 $content = $args[4];
2818 $publish = $args[5];
2820 $escaped_username = $this->escape( $username );
2821 $escaped_password = $this->escape( $password );
2823 if ( !$user = $this->login( $escaped_username, $escaped_password ) ) {
2824 return $this->error;
2827 /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */
2828 do_action( 'xmlrpc_call', 'wp.editPage' );
2830 // Get the page data and make sure it is a page.
2831 $actual_page = get_post($page_id, ARRAY_A);
2832 if ( !$actual_page || ($actual_page['post_type'] != 'page') )
2833 return new IXR_Error( 404, __( 'Sorry, no such page.' ) );
2835 // Make sure the user is allowed to edit pages.
2836 if ( !current_user_can('edit_page', $page_id) )
2837 return new IXR_Error( 401, __( 'Sorry, you do not have the right to edit this page.' ) );
2839 // Mark this as content for a page.
2840 $content['post_type'] = 'page';
2842 // Arrange args in the way mw_editPost understands.
2851 // Let mw_editPost do all of the heavy lifting.
2852 return $this->mw_editPost( $args );
2856 * Retrieve page list.
2860 * @global wpdb $wpdb WordPress database abstraction object.
2862 * @param array $args {
2863 * Method arguments. Note: arguments must be ordered as documented.
2865 * @type int $blog_id (unused)
2866 * @type string $username
2867 * @type string $password
2869 * @return array|IXR_Error
2871 public function wp_getPageList( $args ) {
2874 $this->escape( $args );
2876 $username = $args[1];
2877 $password = $args[2];
2879 if ( !$user = $this->login($username, $password) )
2880 return $this->error;
2882 if ( !current_user_can( 'edit_pages' ) )
2883 return new IXR_Error( 401, __( 'Sorry, you cannot edit pages.' ) );
2885 /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */
2886 do_action( 'xmlrpc_call', 'wp.getPageList' );
2888 // Get list of pages ids and titles
2889 $page_list = $wpdb->get_results("
2891 post_title page_title,
2892 post_parent page_parent_id,
2897 WHERE post_type = 'page'
2901 // The date needs to be formatted properly.
2902 $num_pages = count($page_list);
2903 for ( $i = 0; $i < $num_pages; $i++ ) {
2904 $page_list[$i]->dateCreated = $this->_convert_date( $page_list[$i]->post_date );
2905 $page_list[$i]->date_created_gmt = $this->_convert_date_gmt( $page_list[$i]->post_date_gmt, $page_list[$i]->post_date );
2907 unset($page_list[$i]->post_date_gmt);
2908 unset($page_list[$i]->post_date);
2909 unset($page_list[$i]->post_status);
2916 * Retrieve authors list.
2920 * @param array $args {
2921 * Method arguments. Note: arguments must be ordered as documented.
2923 * @type int $blog_id (unused)
2924 * @type string $username
2925 * @type string $password
2927 * @return array|IXR_Error
2929 public function wp_getAuthors( $args ) {
2930 $this->escape( $args );
2932 $username = $args[1];
2933 $password = $args[2];
2935 if ( !$user = $this->login($username, $password) )
2936 return $this->error;
2938 if ( !current_user_can('edit_posts') )
2939 return new IXR_Error( 401, __( 'Sorry, you cannot edit posts on this site.' ) );
2941 /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */
2942 do_action( 'xmlrpc_call', 'wp.getAuthors' );
2945 foreach ( get_users( array( 'fields' => array('ID','user_login','display_name') ) ) as $user ) {
2947 'user_id' => $user->ID,
2948 'user_login' => $user->user_login,
2949 'display_name' => $user->display_name
2957 * Get list of all tags
2961 * @param array $args {
2962 * Method arguments. Note: arguments must be ordered as documented.
2964 * @type int $blog_id (unused)
2965 * @type string $username
2966 * @type string $password
2968 * @return array|IXR_Error
2970 public function wp_getTags( $args ) {
2971 $this->escape( $args );
2973 $username = $args[1];
2974 $password = $args[2];
2976 if ( !$user = $this->login($username, $password) )
2977 return $this->error;
2979 if ( !current_user_can( 'edit_posts' ) )
2980 return new IXR_Error( 401, __( 'Sorry, you must be able to edit posts on this site in order to view tags.' ) );
2982 /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */
2983 do_action( 'xmlrpc_call', 'wp.getKeywords' );
2987 if ( $all_tags = get_tags() ) {
2988 foreach ( (array) $all_tags as $tag ) {
2990 $struct['tag_id'] = $tag->term_id;
2991 $struct['name'] = $tag->name;
2992 $struct['count'] = $tag->count;
2993 $struct['slug'] = $tag->slug;
2994 $struct['html_url'] = esc_html( get_tag_link( $tag->term_id ) );
2995 $struct['rss_url'] = esc_html( get_tag_feed_link( $tag->term_id ) );
3005 * Create new category.
3009 * @param array $args {
3010 * Method arguments. Note: arguments must be ordered as documented.
3012 * @type int $blog_id (unused)
3013 * @type string $username
3014 * @type string $password
3015 * @type array $category
3017 * @return int|IXR_Error Category ID.
3019 public function wp_newCategory( $args ) {
3020 $this->escape( $args );
3022 $username = $args[1];
3023 $password = $args[2];
3024 $category = $args[3];
3026 if ( !$user = $this->login($username, $password) )
3027 return $this->error;
3029 /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */
3030 do_action( 'xmlrpc_call', 'wp.newCategory' );
3032 // Make sure the user is allowed to add a category.
3033 if ( !current_user_can('manage_categories') )
3034 return new IXR_Error(401, __('Sorry, you do not have the right to add a category.'));
3036 // If no slug was provided make it empty so that
3037 // WordPress will generate one.
3038 if ( empty($category['slug']) )
3039 $category['slug'] = '';
3041 // If no parent_id was provided make it empty
3042 // so that it will be a top level page (no parent).
3043 if ( !isset($category['parent_id']) )
3044 $category['parent_id'] = '';
3046 // If no description was provided make it empty.
3047 if ( empty($category["description"]) )
3048 $category["description"] = "";
3050 $new_category = array(
3051 'cat_name' => $category['name'],
3052 'category_nicename' => $category['slug'],
3053 'category_parent' => $category['parent_id'],
3054 'category_description' => $category['description']
3057 $cat_id = wp_insert_category($new_category, true);
3058 if ( is_wp_error( $cat_id ) ) {
3059 if ( 'term_exists' == $cat_id->get_error_code() )
3060 return (int) $cat_id->get_error_data();
3062 return new IXR_Error(500, __('Sorry, the new category failed.'));
3063 } elseif ( ! $cat_id ) {
3064 return new IXR_Error(500, __('Sorry, the new category failed.'));
3068 * Fires after a new category has been successfully created via XML-RPC.
3072 * @param int $cat_id ID of the new category.
3073 * @param array $args An array of new category arguments.
3075 do_action( 'xmlrpc_call_success_wp_newCategory', $cat_id, $args );
3085 * @param array $args {
3086 * Method arguments. Note: arguments must be ordered as documented.
3088 * @type int $blog_id (unused)
3089 * @type string $username
3090 * @type string $password
3091 * @type int $category_id
3093 * @return bool|IXR_Error See {@link wp_delete_term()} for return info.
3095 public function wp_deleteCategory( $args ) {
3096 $this->escape( $args );
3098 $username = $args[1];
3099 $password = $args[2];
3100 $category_id = (int) $args[3];
3102 if ( !$user = $this->login($username, $password) )
3103 return $this->error;
3105 /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */
3106 do_action( 'xmlrpc_call', 'wp.deleteCategory' );
3108 if ( !current_user_can('manage_categories') )
3109 return new IXR_Error( 401, __( 'Sorry, you do not have the right to delete a category.' ) );
3111 $status = wp_delete_term( $category_id, 'category' );
3113 if ( true == $status ) {
3115 * Fires after a category has been successfully deleted via XML-RPC.
3119 * @param int $category_id ID of the deleted category.
3120 * @param array $args An array of arguments to delete the category.
3122 do_action( 'xmlrpc_call_success_wp_deleteCategory', $category_id, $args );