3 * XML-RPC protocol support for WordPress
9 * WordPress XMLRPC server implementation.
11 * Implements compatability for Blogger API, MetaWeblog API, MovableType, and
12 * pingback. Additional WordPress API for managing comments, pages, posts,
15 * Since WordPress 2.6.0, WordPress XMLRPC server can be disabled in the
16 * administration panels.
19 * @subpackage Publishing
22 class wp_xmlrpc_server extends IXR_Server {
25 * Register all of the XMLRPC methods that XMLRPC server understands.
27 * PHP4 constructor and sets up server and method property. Passes XMLRPC
28 * methods through the 'xmlrpc_methods' filter to allow plugins to extend
29 * or replace XMLRPC methods.
33 * @return wp_xmlrpc_server
35 function wp_xmlrpc_server() {
36 $this->methods = array(
38 'wp.getUsersBlogs' => 'this:wp_getUsersBlogs',
39 'wp.getPage' => 'this:wp_getPage',
40 'wp.getPages' => 'this:wp_getPages',
41 'wp.newPage' => 'this:wp_newPage',
42 'wp.deletePage' => 'this:wp_deletePage',
43 'wp.editPage' => 'this:wp_editPage',
44 'wp.getPageList' => 'this:wp_getPageList',
45 'wp.getAuthors' => 'this:wp_getAuthors',
46 'wp.getCategories' => 'this:mw_getCategories', // Alias
47 'wp.getTags' => 'this:wp_getTags',
48 'wp.newCategory' => 'this:wp_newCategory',
49 'wp.deleteCategory' => 'this:wp_deleteCategory',
50 'wp.suggestCategories' => 'this:wp_suggestCategories',
51 'wp.uploadFile' => 'this:mw_newMediaObject', // Alias
52 'wp.getCommentCount' => 'this:wp_getCommentCount',
53 'wp.getPostStatusList' => 'this:wp_getPostStatusList',
54 'wp.getPageStatusList' => 'this:wp_getPageStatusList',
55 'wp.getPageTemplates' => 'this:wp_getPageTemplates',
56 'wp.getOptions' => 'this:wp_getOptions',
57 'wp.setOptions' => 'this:wp_setOptions',
58 'wp.getComment' => 'this:wp_getComment',
59 'wp.getComments' => 'this:wp_getComments',
60 'wp.deleteComment' => 'this:wp_deleteComment',
61 'wp.editComment' => 'this:wp_editComment',
62 'wp.newComment' => 'this:wp_newComment',
63 'wp.getCommentStatusList' => 'this:wp_getCommentStatusList',
64 'wp.getMediaItem' => 'this:wp_getMediaItem',
65 'wp.getMediaLibrary' => 'this:wp_getMediaLibrary',
66 'wp.getPostFormats' => 'this:wp_getPostFormats',
69 'blogger.getUsersBlogs' => 'this:blogger_getUsersBlogs',
70 'blogger.getUserInfo' => 'this:blogger_getUserInfo',
71 'blogger.getPost' => 'this:blogger_getPost',
72 'blogger.getRecentPosts' => 'this:blogger_getRecentPosts',
73 'blogger.getTemplate' => 'this:blogger_getTemplate',
74 'blogger.setTemplate' => 'this:blogger_setTemplate',
75 'blogger.newPost' => 'this:blogger_newPost',
76 'blogger.editPost' => 'this:blogger_editPost',
77 'blogger.deletePost' => 'this:blogger_deletePost',
79 // MetaWeblog API (with MT extensions to structs)
80 'metaWeblog.newPost' => 'this:mw_newPost',
81 'metaWeblog.editPost' => 'this:mw_editPost',
82 'metaWeblog.getPost' => 'this:mw_getPost',
83 'metaWeblog.getRecentPosts' => 'this:mw_getRecentPosts',
84 'metaWeblog.getCategories' => 'this:mw_getCategories',
85 'metaWeblog.newMediaObject' => 'this:mw_newMediaObject',
87 // MetaWeblog API aliases for Blogger API
88 // see http://www.xmlrpc.com/stories/storyReader$2460
89 'metaWeblog.deletePost' => 'this:blogger_deletePost',
90 'metaWeblog.getTemplate' => 'this:blogger_getTemplate',
91 'metaWeblog.setTemplate' => 'this:blogger_setTemplate',
92 'metaWeblog.getUsersBlogs' => 'this:blogger_getUsersBlogs',
95 'mt.getCategoryList' => 'this:mt_getCategoryList',
96 'mt.getRecentPostTitles' => 'this:mt_getRecentPostTitles',
97 'mt.getPostCategories' => 'this:mt_getPostCategories',
98 'mt.setPostCategories' => 'this:mt_setPostCategories',
99 'mt.supportedMethods' => 'this:mt_supportedMethods',
100 'mt.supportedTextFilters' => 'this:mt_supportedTextFilters',
101 'mt.getTrackbackPings' => 'this:mt_getTrackbackPings',
102 'mt.publishPost' => 'this:mt_publishPost',
105 'pingback.ping' => 'this:pingback_ping',
106 'pingback.extensions.getPingbacks' => 'this:pingback_extensions_getPingbacks',
108 'demo.sayHello' => 'this:sayHello',
109 'demo.addTwoNumbers' => 'this:addTwoNumbers'
112 $this->initialise_blog_option_info( );
113 $this->methods = apply_filters('xmlrpc_methods', $this->methods);
116 function serve_request() {
117 $this->IXR_Server($this->methods);
121 * Test XMLRPC API by saying, "Hello!" to client.
125 * @param array $args Method Parameters.
128 function sayHello($args) {
133 * Test XMLRPC API by adding two numbers for client.
137 * @param array $args Method Parameters.
140 function addTwoNumbers($args) {
143 return $number1 + $number2;
147 * Check user's credentials.
151 * @param string $user_login User's username.
152 * @param string $user_pass User's password.
153 * @return bool Whether authentication passed.
154 * @deprecated use wp_xmlrpc_server::login
155 * @see wp_xmlrpc_server::login
157 function login_pass_ok($user_login, $user_pass) {
158 if ( !get_option( 'enable_xmlrpc' ) ) {
159 $this->error = new IXR_Error( 405, sprintf( __( 'XML-RPC services are disabled on this site. An admin user can enable them at %s'), admin_url('options-writing.php') ) );
163 if (!user_pass_ok($user_login, $user_pass)) {
164 $this->error = new IXR_Error(403, __('Bad login/pass combination.'));
175 * @param string $username User's username.
176 * @param string $password User's password.
177 * @return mixed WP_User object if authentication passed, false otherwise
179 function login($username, $password) {
180 if ( !get_option( 'enable_xmlrpc' ) ) {
181 $this->error = new IXR_Error( 405, sprintf( __( 'XML-RPC services are disabled on this site. An admin user can enable them at %s'), admin_url('options-writing.php') ) );
185 $user = wp_authenticate($username, $password);
187 if (is_wp_error($user)) {
188 $this->error = new IXR_Error(403, __('Bad login/pass combination.'));
192 wp_set_current_user( $user->ID );
197 * Sanitize string or array of strings for database.
201 * @param string|array $array Sanitize single string or array of strings.
202 * @return string|array Type matches $array and sanitized for the database.
204 function escape(&$array) {
207 if (!is_array($array)) {
208 return($wpdb->escape($array));
210 foreach ( (array) $array as $k => $v ) {
211 if ( is_array($v) ) {
212 $this->escape($array[$k]);
213 } else if ( is_object($v) ) {
216 $array[$k] = $wpdb->escape($v);
223 * Retrieve custom fields for post.
227 * @param int $post_id Post ID.
228 * @return array Custom fields, if exist.
230 function get_custom_fields($post_id) {
231 $post_id = (int) $post_id;
233 $custom_fields = array();
235 foreach ( (array) has_meta($post_id) as $meta ) {
236 // Don't expose protected fields.
237 if ( strpos($meta['meta_key'], '_wp_') === 0 ) {
241 $custom_fields[] = array(
242 "id" => $meta['meta_id'],
243 "key" => $meta['meta_key'],
244 "value" => $meta['meta_value']
248 return $custom_fields;
252 * Set custom fields for post.
256 * @param int $post_id Post ID.
257 * @param array $fields Custom fields.
259 function set_custom_fields($post_id, $fields) {
260 $post_id = (int) $post_id;
262 foreach ( (array) $fields as $meta ) {
263 if ( isset($meta['id']) ) {
264 $meta['id'] = (int) $meta['id'];
266 if ( isset($meta['key']) ) {
267 update_meta($meta['id'], $meta['key'], $meta['value']);
270 delete_meta($meta['id']);
274 $_POST['metakeyinput'] = $meta['key'];
275 $_POST['metavalue'] = $meta['value'];
282 * Set up blog options property.
284 * Passes property through 'xmlrpc_blog_options' filter.
288 function initialise_blog_option_info( ) {
291 $this->blog_options = array(
293 'software_name' => array(
294 'desc' => __( 'Software Name' ),
296 'value' => 'WordPress'
298 'software_version' => array(
299 'desc' => __( 'Software Version' ),
301 'value' => $wp_version
304 'desc' => __( 'Site URL' ),
306 'option' => 'siteurl'
310 'time_zone' => array(
311 'desc' => __( 'Time Zone' ),
313 'option' => 'gmt_offset'
315 'blog_title' => array(
316 'desc' => __( 'Site Title' ),
318 'option' => 'blogname'
320 'blog_tagline' => array(
321 'desc' => __( 'Site Tagline' ),
323 'option' => 'blogdescription'
325 'date_format' => array(
326 'desc' => __( 'Date Format' ),
328 'option' => 'date_format'
330 'time_format' => array(
331 'desc' => __( 'Time Format' ),
333 'option' => 'time_format'
335 'users_can_register' => array(
336 'desc' => __( 'Allow new users to sign up' ),
338 'option' => 'users_can_register'
340 'thumbnail_size_w' => array(
341 'desc' => __( 'Thumbnail Width' ),
343 'option' => 'thumbnail_size_w'
345 'thumbnail_size_h' => array(
346 'desc' => __( 'Thumbnail Height' ),
348 'option' => 'thumbnail_size_h'
350 'thumbnail_crop' => array(
351 'desc' => __( 'Crop thumbnail to exact dimensions' ),
353 'option' => 'thumbnail_crop'
355 'medium_size_w' => array(
356 'desc' => __( 'Medium size image width' ),
358 'option' => 'medium_size_w'
360 'medium_size_h' => array(
361 'desc' => __( 'Medium size image height' ),
363 'option' => 'medium_size_h'
365 'large_size_w' => array(
366 'desc' => __( 'Large size image width' ),
368 'option' => 'large_size_w'
370 'large_size_h' => array(
371 'desc' => __( 'Large size image height' ),
373 'option' => 'large_size_h'
377 $this->blog_options = apply_filters( 'xmlrpc_blog_options', $this->blog_options );
381 * Retrieve the blogs of the user.
385 * @param array $args Method parameters.
388 function wp_getUsersBlogs( $args ) {
389 global $current_site;
390 // If this isn't on WPMU then just use blogger_getUsersBlogs
391 if ( !is_multisite() ) {
392 array_unshift( $args, 1 );
393 return $this->blogger_getUsersBlogs( $args );
396 $this->escape( $args );
398 $username = $args[0];
399 $password = $args[1];
401 if ( !$user = $this->login($username, $password) )
405 do_action( 'xmlrpc_call', 'wp.getUsersBlogs' );
407 $blogs = (array) get_blogs_of_user( $user->ID );
410 foreach ( $blogs as $blog ) {
411 // Don't include blogs that aren't hosted at this site
412 if ( $blog->site_id != $current_site->id )
415 $blog_id = $blog->userblog_id;
416 switch_to_blog($blog_id);
417 $is_admin = current_user_can('manage_options');
420 'isAdmin' => $is_admin,
421 'url' => get_option( 'home' ) . '/',
422 'blogid' => (string) $blog_id,
423 'blogName' => get_option( 'blogname' ),
424 'xmlrpc' => site_url( 'xmlrpc.php' )
427 restore_current_blog( );
438 * @param array $args Method parameters.
441 function wp_getPage($args) {
442 $this->escape($args);
444 $blog_id = (int) $args[0];
445 $page_id = (int) $args[1];
446 $username = $args[2];
447 $password = $args[3];
449 if ( !$user = $this->login($username, $password) ) {
453 if ( !current_user_can( 'edit_page', $page_id ) )
454 return new IXR_Error( 401, __( 'Sorry, you cannot edit this page.' ) );
456 do_action('xmlrpc_call', 'wp.getPage');
459 $page = get_page($page_id);
461 // If we found the page then format the data.
462 if ( $page->ID && ($page->post_type == "page") ) {
463 // Get all of the page content and link.
464 $full_page = get_extended($page->post_content);
465 $link = post_permalink($page->ID);
467 // Get info the page parent if there is one.
469 if ( !empty($page->post_parent) ) {
470 $parent = get_page($page->post_parent);
471 $parent_title = $parent->post_title;
474 // Determine comment and ping settings.
475 $allow_comments = comments_open($page->ID) ? 1 : 0;
476 $allow_pings = pings_open($page->ID) ? 1 : 0;
479 $page_date = mysql2date("Ymd\TH:i:s", $page->post_date, false);
480 $page_date_gmt = mysql2date("Ymd\TH:i:s", $page->post_date_gmt, false);
482 // For drafts use the GMT version of the date
483 if ( $page->post_status == 'draft' )
484 $page_date_gmt = get_gmt_from_date( mysql2date( 'Y-m-d H:i:s', $page->post_date ), 'Ymd\TH:i:s' );
486 // Pull the categories info together.
487 $categories = array();
488 foreach ( wp_get_post_categories($page->ID) as $cat_id ) {
489 $categories[] = get_cat_name($cat_id);
492 // Get the author info.
493 $author = get_userdata($page->post_author);
495 $page_template = get_post_meta( $page->ID, '_wp_page_template', true );
496 if ( empty( $page_template ) )
497 $page_template = 'default';
499 $page_struct = array(
500 "dateCreated" => new IXR_Date($page_date),
501 "userid" => $page->post_author,
502 "page_id" => $page->ID,
503 "page_status" => $page->post_status,
504 "description" => $full_page["main"],
505 "title" => $page->post_title,
507 "permaLink" => $link,
508 "categories" => $categories,
509 "excerpt" => $page->post_excerpt,
510 "text_more" => $full_page["extended"],
511 "mt_allow_comments" => $allow_comments,
512 "mt_allow_pings" => $allow_pings,
513 "wp_slug" => $page->post_name,
514 "wp_password" => $page->post_password,
515 "wp_author" => $author->display_name,
516 "wp_page_parent_id" => $page->post_parent,
517 "wp_page_parent_title" => $parent_title,
518 "wp_page_order" => $page->menu_order,
519 "wp_author_id" => $author->ID,
520 "wp_author_display_name" => $author->display_name,
521 "date_created_gmt" => new IXR_Date($page_date_gmt),
522 "custom_fields" => $this->get_custom_fields($page_id),
523 "wp_page_template" => $page_template
526 return($page_struct);
528 // If the page doesn't exist indicate that.
530 return(new IXR_Error(404, __("Sorry, no such page.")));
539 * @param array $args Method parameters.
542 function wp_getPages($args) {
543 $this->escape($args);
545 $blog_id = (int) $args[0];
546 $username = $args[1];
547 $password = $args[2];
548 $num_pages = isset($args[3]) ? (int) $args[3] : 10;
550 if ( !$user = $this->login($username, $password) )
553 if ( !current_user_can( 'edit_pages' ) )
554 return new IXR_Error( 401, __( 'Sorry, you cannot edit pages.' ) );
556 do_action('xmlrpc_call', 'wp.getPages');
558 $pages = get_posts( array('post_type' => 'page', 'post_status' => 'any', 'numberposts' => $num_pages) );
559 $num_pages = count($pages);
561 // If we have pages, put together their info.
562 if ( $num_pages >= 1 ) {
563 $pages_struct = array();
565 for ( $i = 0; $i < $num_pages; $i++ ) {
566 $page = wp_xmlrpc_server::wp_getPage(array(
567 $blog_id, $pages[$i]->ID, $username, $password
569 $pages_struct[] = $page;
572 return($pages_struct);
574 // If no pages were found return an error.
585 * @param array $args Method parameters.
588 function wp_newPage($args) {
589 // Items not escaped here will be escaped in newPost.
590 $username = $this->escape($args[1]);
591 $password = $this->escape($args[2]);
595 if ( !$user = $this->login($username, $password) )
598 do_action('xmlrpc_call', 'wp.newPage');
600 // Make sure the user is allowed to add new pages.
601 if ( !current_user_can("publish_pages") )
602 return(new IXR_Error(401, __("Sorry, you cannot add new pages.")));
604 // Mark this as content for a page.
605 $args[3]["post_type"] = "page";
607 // Let mw_newPost do all of the heavy lifting.
608 return($this->mw_newPost($args));
616 * @param array $args Method parameters.
617 * @return bool True, if success.
619 function wp_deletePage($args) {
620 $this->escape($args);
622 $blog_id = (int) $args[0];
623 $username = $args[1];
624 $password = $args[2];
625 $page_id = (int) $args[3];
627 if ( !$user = $this->login($username, $password) )
630 do_action('xmlrpc_call', 'wp.deletePage');
632 // Get the current page based on the page_id and
633 // make sure it is a page and not a post.
634 $actual_page = wp_get_single_post($page_id, ARRAY_A);
635 if ( !$actual_page || ($actual_page["post_type"] != "page") )
636 return(new IXR_Error(404, __("Sorry, no such page.")));
638 // Make sure the user can delete pages.
639 if ( !current_user_can("delete_page", $page_id) )
640 return(new IXR_Error(401, __("Sorry, you do not have the right to delete this page.")));
642 // Attempt to delete the page.
643 $result = wp_delete_post($page_id);
645 return(new IXR_Error(500, __("Failed to delete the page.")));
655 * @param array $args Method parameters.
658 function wp_editPage($args) {
659 // Items not escaped here will be escaped in editPost.
660 $blog_id = (int) $args[0];
661 $page_id = (int) $this->escape($args[1]);
662 $username = $this->escape($args[2]);
663 $password = $this->escape($args[3]);
667 if ( !$user = $this->login($username, $password) )
670 do_action('xmlrpc_call', 'wp.editPage');
672 // Get the page data and make sure it is a page.
673 $actual_page = wp_get_single_post($page_id, ARRAY_A);
674 if ( !$actual_page || ($actual_page["post_type"] != "page") )
675 return(new IXR_Error(404, __("Sorry, no such page.")));
677 // Make sure the user is allowed to edit pages.
678 if ( !current_user_can("edit_page", $page_id) )
679 return(new IXR_Error(401, __("Sorry, you do not have the right to edit this page.")));
681 // Mark this as content for a page.
682 $content["post_type"] = "page";
684 // Arrange args in the way mw_editPost understands.
693 // Let mw_editPost do all of the heavy lifting.
694 return($this->mw_editPost($args));
698 * Retrieve page list.
702 * @param array $args Method parameters.
705 function wp_getPageList($args) {
708 $this->escape($args);
710 $blog_id = (int) $args[0];
711 $username = $args[1];
712 $password = $args[2];
714 if ( !$user = $this->login($username, $password) )
717 if ( !current_user_can( 'edit_pages' ) )
718 return new IXR_Error( 401, __( 'Sorry, you cannot edit pages.' ) );
720 do_action('xmlrpc_call', 'wp.getPageList');
722 // Get list of pages ids and titles
723 $page_list = $wpdb->get_results("
725 post_title page_title,
726 post_parent page_parent_id,
731 WHERE post_type = 'page'
735 // The date needs to be formated properly.
736 $num_pages = count($page_list);
737 for ( $i = 0; $i < $num_pages; $i++ ) {
738 $post_date = mysql2date("Ymd\TH:i:s", $page_list[$i]->post_date, false);
739 $post_date_gmt = mysql2date("Ymd\TH:i:s", $page_list[$i]->post_date_gmt, false);
741 $page_list[$i]->dateCreated = new IXR_Date($post_date);
742 $page_list[$i]->date_created_gmt = new IXR_Date($post_date_gmt);
744 // For drafts use the GMT version of the date
745 if ( $page_list[$i]->post_status == 'draft' ) {
746 $page_list[$i]->date_created_gmt = get_gmt_from_date( mysql2date( 'Y-m-d H:i:s', $page_list[$i]->post_date ), 'Ymd\TH:i:s' );
747 $page_list[$i]->date_created_gmt = new IXR_Date( $page_list[$i]->date_created_gmt );
750 unset($page_list[$i]->post_date_gmt);
751 unset($page_list[$i]->post_date);
752 unset($page_list[$i]->post_status);
759 * Retrieve authors list.
763 * @param array $args Method parameters.
766 function wp_getAuthors($args) {
768 $this->escape($args);
770 $blog_id = (int) $args[0];
771 $username = $args[1];
772 $password = $args[2];
774 if ( !$user = $this->login($username, $password) )
777 if ( !current_user_can("edit_posts") )
778 return(new IXR_Error(401, __("Sorry, you cannot edit posts on this site.")));
780 do_action('xmlrpc_call', 'wp.getAuthors');
783 foreach ( get_users( array( 'fields' => array('ID','user_login','display_name') ) ) as $user ) {
785 "user_id" => $user->ID,
786 "user_login" => $user->user_login,
787 "display_name" => $user->display_name
795 * Get list of all tags
799 * @param array $args Method parameters.
802 function wp_getTags( $args ) {
803 $this->escape( $args );
805 $blog_id = (int) $args[0];
806 $username = $args[1];
807 $password = $args[2];
809 if ( !$user = $this->login($username, $password) )
812 if ( !current_user_can( 'edit_posts' ) )
813 return new IXR_Error( 401, __( 'Sorry, you must be able to edit posts on this site in order to view tags.' ) );
815 do_action( 'xmlrpc_call', 'wp.getKeywords' );
819 if ( $all_tags = get_tags() ) {
820 foreach( (array) $all_tags as $tag ) {
821 $struct['tag_id'] = $tag->term_id;
822 $struct['name'] = $tag->name;
823 $struct['count'] = $tag->count;
824 $struct['slug'] = $tag->slug;
825 $struct['html_url'] = esc_html( get_tag_link( $tag->term_id ) );
826 $struct['rss_url'] = esc_html( get_tag_feed_link( $tag->term_id ) );
836 * Create new category.
840 * @param array $args Method parameters.
841 * @return int Category ID.
843 function wp_newCategory($args) {
844 $this->escape($args);
846 $blog_id = (int) $args[0];
847 $username = $args[1];
848 $password = $args[2];
849 $category = $args[3];
851 if ( !$user = $this->login($username, $password) )
854 do_action('xmlrpc_call', 'wp.newCategory');
856 // Make sure the user is allowed to add a category.
857 if ( !current_user_can("manage_categories") )
858 return(new IXR_Error(401, __("Sorry, you do not have the right to add a category.")));
860 // If no slug was provided make it empty so that
861 // WordPress will generate one.
862 if ( empty($category["slug"]) )
863 $category["slug"] = "";
865 // If no parent_id was provided make it empty
866 // so that it will be a top level page (no parent).
867 if ( !isset($category["parent_id"]) )
868 $category["parent_id"] = "";
870 // If no description was provided make it empty.
871 if ( empty($category["description"]) )
872 $category["description"] = "";
874 $new_category = array(
875 "cat_name" => $category["name"],
876 "category_nicename" => $category["slug"],
877 "category_parent" => $category["parent_id"],
878 "category_description" => $category["description"]
881 $cat_id = wp_insert_category($new_category, true);
882 if ( is_wp_error( $cat_id ) ) {
883 if ( 'term_exists' == $cat_id->get_error_code() )
884 return (int) $cat_id->get_error_data();
886 return(new IXR_Error(500, __("Sorry, the new category failed.")));
887 } elseif ( ! $cat_id ) {
888 return(new IXR_Error(500, __("Sorry, the new category failed.")));
899 * @param array $args Method parameters.
900 * @return mixed See {@link wp_delete_term()} for return info.
902 function wp_deleteCategory($args) {
903 $this->escape($args);
905 $blog_id = (int) $args[0];
906 $username = $args[1];
907 $password = $args[2];
908 $category_id = (int) $args[3];
910 if ( !$user = $this->login($username, $password) )
913 do_action('xmlrpc_call', 'wp.deleteCategory');
915 if ( !current_user_can("manage_categories") )
916 return new IXR_Error( 401, __( "Sorry, you do not have the right to delete a category." ) );
918 return wp_delete_term( $category_id, 'category' );
922 * Retrieve category list.
926 * @param array $args Method parameters.
929 function wp_suggestCategories($args) {
930 $this->escape($args);
932 $blog_id = (int) $args[0];
933 $username = $args[1];
934 $password = $args[2];
935 $category = $args[3];
936 $max_results = (int) $args[4];
938 if ( !$user = $this->login($username, $password) )
941 if ( !current_user_can( 'edit_posts' ) )
942 return new IXR_Error( 401, __( 'Sorry, you must be able to edit posts to this site in order to view categories.' ) );
944 do_action('xmlrpc_call', 'wp.suggestCategories');
946 $category_suggestions = array();
947 $args = array('get' => 'all', 'number' => $max_results, 'name__like' => $category);
948 foreach ( (array) get_categories($args) as $cat ) {
949 $category_suggestions[] = array(
950 "category_id" => $cat->term_id,
951 "category_name" => $cat->name
955 return($category_suggestions);
963 * @param array $args Method parameters.
966 function wp_getComment($args) {
967 $this->escape($args);
969 $blog_id = (int) $args[0];
970 $username = $args[1];
971 $password = $args[2];
972 $comment_id = (int) $args[3];
974 if ( !$user = $this->login($username, $password) )
977 if ( !current_user_can( 'moderate_comments' ) )
978 return new IXR_Error( 403, __( 'You are not allowed to moderate comments on this site.' ) );
980 do_action('xmlrpc_call', 'wp.getComment');
982 if ( ! $comment = get_comment($comment_id) )
983 return new IXR_Error( 404, __( 'Invalid comment ID.' ) );
986 $comment_date = mysql2date("Ymd\TH:i:s", $comment->comment_date, false);
987 $comment_date_gmt = mysql2date("Ymd\TH:i:s", $comment->comment_date_gmt, false);
989 if ( '0' == $comment->comment_approved )
990 $comment_status = 'hold';
991 else if ( 'spam' == $comment->comment_approved )
992 $comment_status = 'spam';
993 else if ( '1' == $comment->comment_approved )
994 $comment_status = 'approve';
996 $comment_status = $comment->comment_approved;
998 $link = get_comment_link($comment);
1000 $comment_struct = array(
1001 "date_created_gmt" => new IXR_Date($comment_date_gmt),
1002 "user_id" => $comment->user_id,
1003 "comment_id" => $comment->comment_ID,
1004 "parent" => $comment->comment_parent,
1005 "status" => $comment_status,
1006 "content" => $comment->comment_content,
1008 "post_id" => $comment->comment_post_ID,
1009 "post_title" => get_the_title($comment->comment_post_ID),
1010 "author" => $comment->comment_author,
1011 "author_url" => $comment->comment_author_url,
1012 "author_email" => $comment->comment_author_email,
1013 "author_ip" => $comment->comment_author_IP,
1014 "type" => $comment->comment_type,
1017 return $comment_struct;
1021 * Retrieve comments.
1025 * @param array $args Method parameters.
1028 function wp_getComments($args) {
1030 $this->escape($args);
1032 $blog_id = (int) $args[0];
1033 $username = $args[1];
1034 $password = $args[2];
1037 if ( !$user = $this->login($username, $password) )
1038 return $this->error;
1040 if ( !current_user_can( 'moderate_comments' ) )
1041 return new IXR_Error( 401, __( 'Sorry, you cannot edit comments.' ) );
1043 do_action('xmlrpc_call', 'wp.getComments');
1045 if ( isset($struct['status']) )
1046 $status = $struct['status'];
1051 if ( isset($struct['post_id']) )
1052 $post_id = absint($struct['post_id']);
1055 if ( isset($struct['offset']) )
1056 $offset = absint($struct['offset']);
1059 if ( isset($struct['number']) )
1060 $number = absint($struct['number']);
1062 $comments = get_comments( array('status' => $status, 'post_id' => $post_id, 'offset' => $offset, 'number' => $number ) );
1063 $num_comments = count($comments);
1065 if ( ! $num_comments )
1068 $comments_struct = array();
1070 for ( $i = 0; $i < $num_comments; $i++ ) {
1071 $comment = wp_xmlrpc_server::wp_getComment(array(
1072 $raw_args[0], $raw_args[1], $raw_args[2], $comments[$i]->comment_ID,
1074 $comments_struct[] = $comment;
1077 return $comments_struct;
1085 * @param array $args Method parameters.
1086 * @return mixed {@link wp_delete_comment()}
1088 function wp_deleteComment($args) {
1089 $this->escape($args);
1091 $blog_id = (int) $args[0];
1092 $username = $args[1];
1093 $password = $args[2];
1094 $comment_ID = (int) $args[3];
1096 if ( !$user = $this->login($username, $password) )
1097 return $this->error;
1099 if ( !current_user_can( 'moderate_comments' ) )
1100 return new IXR_Error( 403, __( 'You are not allowed to moderate comments on this site.' ) );
1102 if ( !current_user_can( 'edit_comment', $comment_ID ) )
1103 return new IXR_Error( 403, __( 'You are not allowed to moderate comments on this site.' ) );
1105 do_action('xmlrpc_call', 'wp.deleteComment');
1107 if ( ! get_comment($comment_ID) )
1108 return new IXR_Error( 404, __( 'Invalid comment ID.' ) );
1110 return wp_delete_comment($comment_ID);
1118 * @param array $args Method parameters.
1119 * @return bool True, on success.
1121 function wp_editComment($args) {
1122 $this->escape($args);
1124 $blog_id = (int) $args[0];
1125 $username = $args[1];
1126 $password = $args[2];
1127 $comment_ID = (int) $args[3];
1128 $content_struct = $args[4];
1130 if ( !$user = $this->login($username, $password) )
1131 return $this->error;
1133 if ( !current_user_can( 'moderate_comments' ) )
1134 return new IXR_Error( 403, __( 'You are not allowed to moderate comments on this site.' ) );
1136 if ( !current_user_can( 'edit_comment', $comment_ID ) )
1137 return new IXR_Error( 403, __( 'You are not allowed to moderate comments on this site.' ) );
1139 do_action('xmlrpc_call', 'wp.editComment');
1141 if ( ! get_comment($comment_ID) )
1142 return new IXR_Error( 404, __( 'Invalid comment ID.' ) );
1144 if ( isset($content_struct['status']) ) {
1145 $statuses = get_comment_statuses();
1146 $statuses = array_keys($statuses);
1148 if ( ! in_array($content_struct['status'], $statuses) )
1149 return new IXR_Error( 401, __( 'Invalid comment status.' ) );
1150 $comment_approved = $content_struct['status'];
1153 // Do some timestamp voodoo
1154 if ( !empty( $content_struct['date_created_gmt'] ) ) {
1155 $dateCreated = str_replace( 'Z', '', $content_struct['date_created_gmt']->getIso() ) . 'Z'; // We know this is supposed to be GMT, so we're going to slap that Z on there by force
1156 $comment_date = get_date_from_gmt(iso8601_to_datetime($dateCreated));
1157 $comment_date_gmt = iso8601_to_datetime($dateCreated, 'GMT');
1160 if ( isset($content_struct['content']) )
1161 $comment_content = $content_struct['content'];
1163 if ( isset($content_struct['author']) )
1164 $comment_author = $content_struct['author'];
1166 if ( isset($content_struct['author_url']) )
1167 $comment_author_url = $content_struct['author_url'];
1169 if ( isset($content_struct['author_email']) )
1170 $comment_author_email = $content_struct['author_email'];
1172 // We've got all the data -- post it:
1173 $comment = compact('comment_ID', 'comment_content', 'comment_approved', 'comment_date', 'comment_date_gmt', 'comment_author', 'comment_author_email', 'comment_author_url');
1175 $result = wp_update_comment($comment);
1176 if ( is_wp_error( $result ) )
1177 return new IXR_Error(500, $result->get_error_message());
1180 return new IXR_Error(500, __('Sorry, the comment could not be edited. Something wrong happened.'));
1186 * Create new comment.
1190 * @param array $args Method parameters.
1191 * @return mixed {@link wp_new_comment()}
1193 function wp_newComment($args) {
1196 $this->escape($args);
1198 $blog_id = (int) $args[0];
1199 $username = $args[1];
1200 $password = $args[2];
1202 $content_struct = $args[4];
1204 $allow_anon = apply_filters('xmlrpc_allow_anonymous_comments', false);
1206 $user = $this->login($username, $password);
1210 if ( $allow_anon && get_option('comment_registration') )
1211 return new IXR_Error( 403, __( 'You must be registered to comment' ) );
1212 else if ( !$allow_anon )
1213 return $this->error;
1218 if ( is_numeric($post) )
1219 $post_id = absint($post);
1221 $post_id = url_to_postid($post);
1224 return new IXR_Error( 404, __( 'Invalid post ID.' ) );
1226 if ( ! get_post($post_id) )
1227 return new IXR_Error( 404, __( 'Invalid post ID.' ) );
1229 $comment['comment_post_ID'] = $post_id;
1232 $comment['comment_author'] = $wpdb->escape( $user->display_name );
1233 $comment['comment_author_email'] = $wpdb->escape( $user->user_email );
1234 $comment['comment_author_url'] = $wpdb->escape( $user->user_url );
1235 $comment['user_ID'] = $user->ID;
1237 $comment['comment_author'] = '';
1238 if ( isset($content_struct['author']) )
1239 $comment['comment_author'] = $content_struct['author'];
1241 $comment['comment_author_email'] = '';
1242 if ( isset($content_struct['author_email']) )
1243 $comment['comment_author_email'] = $content_struct['author_email'];
1245 $comment['comment_author_url'] = '';
1246 if ( isset($content_struct['author_url']) )
1247 $comment['comment_author_url'] = $content_struct['author_url'];
1249 $comment['user_ID'] = 0;
1251 if ( get_option('require_name_email') ) {
1252 if ( 6 > strlen($comment['comment_author_email']) || '' == $comment['comment_author'] )
1253 return new IXR_Error( 403, __( 'Comment author name and email are required' ) );
1254 elseif ( !is_email($comment['comment_author_email']) )
1255 return new IXR_Error( 403, __( 'A valid email address is required' ) );
1259 $comment['comment_parent'] = isset($content_struct['comment_parent']) ? absint($content_struct['comment_parent']) : 0;
1261 $comment['comment_content'] = isset($content_struct['content']) ? $content_struct['content'] : null;
1263 do_action('xmlrpc_call', 'wp.newComment');
1265 return wp_new_comment($comment);
1269 * Retrieve all of the comment status.
1273 * @param array $args Method parameters.
1276 function wp_getCommentStatusList($args) {
1277 $this->escape( $args );
1279 $blog_id = (int) $args[0];
1280 $username = $args[1];
1281 $password = $args[2];
1283 if ( !$user = $this->login($username, $password) )
1284 return $this->error;
1286 if ( !current_user_can( 'moderate_comments' ) )
1287 return new IXR_Error( 403, __( 'You are not allowed access to details about this site.' ) );
1289 do_action('xmlrpc_call', 'wp.getCommentStatusList');
1291 return get_comment_statuses( );
1295 * Retrieve comment count.
1299 * @param array $args Method parameters.
1302 function wp_getCommentCount( $args ) {
1303 $this->escape($args);
1305 $blog_id = (int) $args[0];
1306 $username = $args[1];
1307 $password = $args[2];
1308 $post_id = (int) $args[3];
1310 if ( !$user = $this->login($username, $password) )
1311 return $this->error;
1313 if ( !current_user_can( 'edit_posts' ) )
1314 return new IXR_Error( 403, __( 'You are not allowed access to details about comments.' ) );
1316 do_action('xmlrpc_call', 'wp.getCommentCount');
1318 $count = wp_count_comments( $post_id );
1320 "approved" => $count->approved,
1321 "awaiting_moderation" => $count->moderated,
1322 "spam" => $count->spam,
1323 "total_comments" => $count->total_comments
1328 * Retrieve post statuses.
1332 * @param array $args Method parameters.
1335 function wp_getPostStatusList( $args ) {
1336 $this->escape( $args );
1338 $blog_id = (int) $args[0];
1339 $username = $args[1];
1340 $password = $args[2];
1342 if ( !$user = $this->login($username, $password) )
1343 return $this->error;
1345 if ( !current_user_can( 'edit_posts' ) )
1346 return new IXR_Error( 403, __( 'You are not allowed access to details about this site.' ) );
1348 do_action('xmlrpc_call', 'wp.getPostStatusList');
1350 return get_post_statuses( );
1354 * Retrieve page statuses.
1358 * @param array $args Method parameters.
1361 function wp_getPageStatusList( $args ) {
1362 $this->escape( $args );
1364 $blog_id = (int) $args[0];
1365 $username = $args[1];
1366 $password = $args[2];
1368 if ( !$user = $this->login($username, $password) )
1369 return $this->error;
1371 if ( !current_user_can( 'edit_pages' ) )
1372 return new IXR_Error( 403, __( 'You are not allowed access to details about this site.' ) );
1374 do_action('xmlrpc_call', 'wp.getPageStatusList');
1376 return get_page_statuses( );
1380 * Retrieve page templates.
1384 * @param array $args Method parameters.
1387 function wp_getPageTemplates( $args ) {
1388 $this->escape( $args );
1390 $blog_id = (int) $args[0];
1391 $username = $args[1];
1392 $password = $args[2];
1394 if ( !$user = $this->login($username, $password) )
1395 return $this->error;
1397 if ( !current_user_can( 'edit_pages' ) )
1398 return new IXR_Error( 403, __( 'You are not allowed access to details about this site.' ) );
1400 $templates = get_page_templates( );
1401 $templates['Default'] = 'default';
1407 * Retrieve blog options.
1411 * @param array $args Method parameters.
1414 function wp_getOptions( $args ) {
1415 $this->escape( $args );
1417 $blog_id = (int) $args[0];
1418 $username = $args[1];
1419 $password = $args[2];
1420 $options = isset( $args[3] ) ? (array) $args[3] : array();
1422 if ( !$user = $this->login($username, $password) )
1423 return $this->error;
1425 // If no specific options where asked for, return all of them
1426 if ( count( $options ) == 0 )
1427 $options = array_keys($this->blog_options);
1429 return $this->_getOptions($options);
1433 * Retrieve blog options value from list.
1437 * @param array $options Options to retrieve.
1440 function _getOptions($options) {
1442 foreach ( $options as $option ) {
1443 if ( array_key_exists( $option, $this->blog_options ) ) {
1444 $data[$option] = $this->blog_options[$option];
1445 //Is the value static or dynamic?
1446 if ( isset( $data[$option]['option'] ) ) {
1447 $data[$option]['value'] = get_option( $data[$option]['option'] );
1448 unset($data[$option]['option']);
1457 * Update blog options.
1461 * @param array $args Method parameters.
1464 function wp_setOptions( $args ) {
1465 $this->escape( $args );
1467 $blog_id = (int) $args[0];
1468 $username = $args[1];
1469 $password = $args[2];
1470 $options = (array) $args[3];
1472 if ( !$user = $this->login($username, $password) )
1473 return $this->error;
1475 if ( !current_user_can( 'manage_options' ) )
1476 return new IXR_Error( 403, __( 'You are not allowed to update options.' ) );
1478 foreach ( $options as $o_name => $o_value ) {
1479 $option_names[] = $o_name;
1480 if ( !array_key_exists( $o_name, $this->blog_options ) )
1483 if ( $this->blog_options[$o_name]['readonly'] == true )
1486 update_option( $this->blog_options[$o_name]['option'], $o_value );
1489 //Now return the updated values
1490 return $this->_getOptions($option_names);
1494 * Retrieve a media item by ID
1498 * @param array $args Method parameters. Contains:
1503 * @return array. Assocciative array containing:
1504 * - 'date_created_gmt'
1513 function wp_getMediaItem($args) {
1514 $this->escape($args);
1516 $blog_id = (int) $args[0];
1517 $username = $args[1];
1518 $password = $args[2];
1519 $attachment_id = (int) $args[3];
1521 if ( !$user = $this->login($username, $password) )
1522 return $this->error;
1524 if ( !current_user_can( 'upload_files' ) )
1525 return new IXR_Error( 403, __( 'You are not allowed to upload files on this site.' ) );
1527 do_action('xmlrpc_call', 'wp.getMediaItem');
1529 if ( ! $attachment = get_post($attachment_id) )
1530 return new IXR_Error( 404, __( 'Invalid attachment ID.' ) );
1532 // Format page date.
1533 $attachment_date = mysql2date("Ymd\TH:i:s", $attachment->post_date, false);
1534 $attachment_date_gmt = mysql2date("Ymd\TH:i:s", $attachment->post_date_gmt, false);
1536 $link = wp_get_attachment_url($attachment->ID);
1537 $thumbnail_link = wp_get_attachment_thumb_url($attachment->ID);
1539 $attachment_struct = array(
1540 "date_created_gmt" => new IXR_Date($attachment_date_gmt),
1541 "parent" => $attachment->post_parent,
1543 "thumbnail" => $thumbnail_link,
1544 "title" => $attachment->post_title,
1545 "caption" => $attachment->post_excerpt,
1546 "description" => $attachment->post_content,
1547 "metadata" => wp_get_attachment_metadata($attachment->ID),
1550 return $attachment_struct;
1554 * Retrieves a collection of media library items (or attachments)
1556 * Besides the common blog_id, username, and password arguments, it takes a filter
1557 * array as last argument.
1559 * Accepted 'filter' keys are 'parent_id', 'mime_type', 'offset', and 'number'.
1561 * The defaults are as follows:
1562 * - 'number' - Default is 5. Total number of media items to retrieve.
1563 * - 'offset' - Default is 0. See {@link WP_Query::query()} for more.
1564 * - 'parent_id' - Default is ''. The post where the media item is attached. Empty string shows all media items. 0 shows unattached media items.
1565 * - 'mime_type' - Default is ''. Filter by mime type (e.g., 'image/jpeg', 'application/pdf')
1569 * @param array $args Method parameters. Contains:
1574 * @return array. Contains a collection of media items. See {@link wp_xmlrpc_server::wp_getMediaItem()} for a description of each item contents
1576 function wp_getMediaLibrary($args) {
1578 $this->escape($args);
1580 $blog_id = (int) $args[0];
1581 $username = $args[1];
1582 $password = $args[2];
1583 $struct = isset( $args[3] ) ? $args[3] : array() ;
1585 if ( !$user = $this->login($username, $password) )
1586 return $this->error;
1588 if ( !current_user_can( 'upload_files' ) )
1589 return new IXR_Error( 401, __( 'Sorry, you cannot upload files.' ) );
1591 do_action('xmlrpc_call', 'wp.getMediaLibrary');
1593 $parent_id = ( isset($struct['parent_id']) ) ? absint($struct['parent_id']) : '' ;
1594 $mime_type = ( isset($struct['mime_type']) ) ? $struct['mime_type'] : '' ;
1595 $offset = ( isset($struct['offset']) ) ? absint($struct['offset']) : 0 ;
1596 $number = ( isset($struct['number']) ) ? absint($struct['number']) : -1 ;
1598 $attachments = get_posts( array('post_type' => 'attachment', 'post_parent' => $parent_id, 'offset' => $offset, 'numberposts' => $number, 'post_mime_type' => $mime_type ) );
1599 $num_attachments = count($attachments);
1601 if ( ! $num_attachments )
1604 $attachments_struct = array();
1606 foreach ($attachments as $attachment )
1607 $attachments_struct[] = $this->wp_getMediaItem( array( $raw_args[0], $raw_args[1], $raw_args[2], $attachment->ID ) );
1609 return $attachments_struct;
1613 * Retrives a list of post formats used by the site
1617 * @param array $args Method parameters. Contains:
1623 function wp_getPostFormats( $args ) {
1624 $this->escape( $args );
1626 $blog_id = (int) $args[0];
1627 $username = $args[1];
1628 $password = $args[2];
1630 if ( !$user = $this->login( $username, $password ) )
1631 return $this->error;
1633 do_action( 'xmlrpc_call', 'wp.getPostFormats' );
1634 return get_post_format_strings();
1637 /* Blogger API functions.
1638 * specs on http://plant.blogger.com/api and http://groups.yahoo.com/group/bloggerDev/
1642 * Retrieve blogs that user owns.
1644 * Will make more sense once we support multiple blogs.
1648 * @param array $args Method parameters.
1651 function blogger_getUsersBlogs($args) {
1652 if ( is_multisite() )
1653 return $this->_multisite_getUsersBlogs($args);
1655 $this->escape($args);
1657 $username = $args[1];
1658 $password = $args[2];
1660 if ( !$user = $this->login($username, $password) )
1661 return $this->error;
1663 do_action('xmlrpc_call', 'blogger.getUsersBlogs');
1665 $is_admin = current_user_can('manage_options');
1668 'isAdmin' => $is_admin,
1669 'url' => get_option('home') . '/',
1671 'blogName' => get_option('blogname'),
1672 'xmlrpc' => site_url( 'xmlrpc.php' )
1675 return array($struct);
1679 * Private function for retrieving a users blogs for multisite setups
1683 function _multisite_getUsersBlogs($args) {
1684 global $current_blog;
1685 $domain = $current_blog->domain;
1686 $path = $current_blog->path . 'xmlrpc.php';
1687 $protocol = is_ssl() ? 'https' : 'http';
1689 $rpc = new IXR_Client("$protocol://{$domain}{$path}");
1690 $rpc->query('wp.getUsersBlogs', $args[1], $args[2]);
1691 $blogs = $rpc->getResponse();
1693 if ( isset($blogs['faultCode']) )
1694 return new IXR_Error($blogs['faultCode'], $blogs['faultString']);
1696 if ( $_SERVER['HTTP_HOST'] == $domain && $_SERVER['REQUEST_URI'] == $path ) {
1699 foreach ( (array) $blogs as $blog ) {
1700 if ( strpos($blog['url'], $_SERVER['HTTP_HOST']) )
1701 return array($blog);
1708 * Retrieve user's data.
1710 * Gives your client some info about you, so you don't have to.
1714 * @param array $args Method parameters.
1717 function blogger_getUserInfo($args) {
1719 $this->escape($args);
1721 $username = $args[1];
1722 $password = $args[2];
1724 if ( !$user = $this->login($username, $password) )
1725 return $this->error;
1727 if ( !current_user_can( 'edit_posts' ) )
1728 return new IXR_Error( 401, __( 'Sorry, you do not have access to user data on this site.' ) );
1730 do_action('xmlrpc_call', 'blogger.getUserInfo');
1733 'nickname' => $user->nickname,
1734 'userid' => $user->ID,
1735 'url' => $user->user_url,
1736 'lastname' => $user->last_name,
1737 'firstname' => $user->first_name
1748 * @param array $args Method parameters.
1751 function blogger_getPost($args) {
1753 $this->escape($args);
1755 $post_ID = (int) $args[1];
1756 $username = $args[2];
1757 $password = $args[3];
1759 if ( !$user = $this->login($username, $password) )
1760 return $this->error;
1762 if ( !current_user_can( 'edit_post', $post_ID ) )
1763 return new IXR_Error( 401, __( 'Sorry, you cannot edit this post.' ) );
1765 do_action('xmlrpc_call', 'blogger.getPost');
1767 $post_data = wp_get_single_post($post_ID, ARRAY_A);
1769 $categories = implode(',', wp_get_post_categories($post_ID));
1771 $content = '<title>'.stripslashes($post_data['post_title']).'</title>';
1772 $content .= '<category>'.$categories.'</category>';
1773 $content .= stripslashes($post_data['post_content']);
1776 'userid' => $post_data['post_author'],
1777 'dateCreated' => new IXR_Date(mysql2date('Ymd\TH:i:s', $post_data['post_date'], false)),
1778 'content' => $content,
1779 'postid' => (string) $post_data['ID']
1786 * Retrieve list of recent posts.
1790 * @param array $args Method parameters.
1793 function blogger_getRecentPosts($args) {
1795 $this->escape($args);
1797 // $args[0] = appkey - ignored
1798 $blog_ID = (int) $args[1]; /* though we don't use it yet */
1799 $username = $args[2];
1800 $password = $args[3];
1801 if ( isset( $args[4] ) )
1802 $query = array( 'numberposts' => absint( $args[4] ) );
1806 if ( !$user = $this->login($username, $password) )
1807 return $this->error;
1809 do_action('xmlrpc_call', 'blogger.getRecentPosts');
1811 $posts_list = wp_get_recent_posts( $query );
1813 if ( !$posts_list ) {
1814 $this->error = new IXR_Error(500, __('Either there are no posts, or something went wrong.'));
1815 return $this->error;
1818 foreach ($posts_list as $entry) {
1819 if ( !current_user_can( 'edit_post', $entry['ID'] ) )
1822 $post_date = mysql2date('Ymd\TH:i:s', $entry['post_date'], false);
1823 $categories = implode(',', wp_get_post_categories($entry['ID']));
1825 $content = '<title>'.stripslashes($entry['post_title']).'</title>';
1826 $content .= '<category>'.$categories.'</category>';
1827 $content .= stripslashes($entry['post_content']);
1830 'userid' => $entry['post_author'],
1831 'dateCreated' => new IXR_Date($post_date),
1832 'content' => $content,
1833 'postid' => (string) $entry['ID'],
1838 $recent_posts = array();
1839 for ( $j=0; $j<count($struct); $j++ ) {
1840 array_push($recent_posts, $struct[$j]);
1843 return $recent_posts;
1847 * Retrieve blog_filename content.
1851 * @param array $args Method parameters.
1854 function blogger_getTemplate($args) {
1856 $this->escape($args);
1858 $blog_ID = (int) $args[1];
1859 $username = $args[2];
1860 $password = $args[3];
1861 $template = $args[4]; /* could be 'main' or 'archiveIndex', but we don't use it */
1863 if ( !$user = $this->login($username, $password) )
1864 return $this->error;
1866 do_action('xmlrpc_call', 'blogger.getTemplate');
1868 if ( !current_user_can('edit_themes') )
1869 return new IXR_Error(401, __('Sorry, this user can not edit the template.'));
1871 /* warning: here we make the assumption that the blog's URL is on the same server */
1872 $filename = get_option('home') . '/';
1873 $filename = preg_replace('#https?://.+?/#', $_SERVER['DOCUMENT_ROOT'].'/', $filename);
1875 $f = fopen($filename, 'r');
1876 $content = fread($f, filesize($filename));
1879 /* so it is actually editable with a windows/mac client */
1880 // FIXME: (or delete me) do we really want to cater to bad clients at the expense of good ones by BEEPing up their line breaks? commented. $content = str_replace("\n", "\r\n", $content);
1886 * Updates the content of blog_filename.
1890 * @param array $args Method parameters.
1891 * @return bool True when done.
1893 function blogger_setTemplate($args) {
1895 $this->escape($args);
1897 $blog_ID = (int) $args[1];
1898 $username = $args[2];
1899 $password = $args[3];
1900 $content = $args[4];
1901 $template = $args[5]; /* could be 'main' or 'archiveIndex', but we don't use it */
1903 if ( !$user = $this->login($username, $password) )
1904 return $this->error;
1906 do_action('xmlrpc_call', 'blogger.setTemplate');
1908 if ( !current_user_can('edit_themes') )
1909 return new IXR_Error(401, __('Sorry, this user cannot edit the template.'));
1911 /* warning: here we make the assumption that the blog's URL is on the same server */
1912 $filename = get_option('home') . '/';
1913 $filename = preg_replace('#https?://.+?/#', $_SERVER['DOCUMENT_ROOT'].'/', $filename);
1915 if ($f = fopen($filename, 'w+')) {
1916 fwrite($f, $content);
1919 return new IXR_Error(500, __('Either the file is not writable, or something wrong happened. The file has not been updated.'));
1930 * @param array $args Method parameters.
1933 function blogger_newPost($args) {
1935 $this->escape($args);
1937 $blog_ID = (int) $args[1]; /* though we don't use it yet */
1938 $username = $args[2];
1939 $password = $args[3];
1940 $content = $args[4];
1941 $publish = $args[5];
1943 if ( !$user = $this->login($username, $password) )
1944 return $this->error;
1946 do_action('xmlrpc_call', 'blogger.newPost');
1948 $cap = ($publish) ? 'publish_posts' : 'edit_posts';
1949 if ( !current_user_can($cap) )
1950 return new IXR_Error(401, __('Sorry, you are not allowed to post on this site.'));
1952 $post_status = ($publish) ? 'publish' : 'draft';
1954 $post_author = $user->ID;
1956 $post_title = xmlrpc_getposttitle($content);
1957 $post_category = xmlrpc_getpostcategory($content);
1958 $post_content = xmlrpc_removepostdata($content);
1960 $post_date = current_time('mysql');
1961 $post_date_gmt = current_time('mysql', 1);
1963 $post_data = compact('blog_ID', 'post_author', 'post_date', 'post_date_gmt', 'post_content', 'post_title', 'post_category', 'post_status');
1965 $post_ID = wp_insert_post($post_data);
1966 if ( is_wp_error( $post_ID ) )
1967 return new IXR_Error(500, $post_ID->get_error_message());
1970 return new IXR_Error(500, __('Sorry, your entry could not be posted. Something wrong happened.'));
1972 $this->attach_uploads( $post_ID, $post_content );
1974 logIO('O', "Posted ! ID: $post_ID");
1984 * @param array $args Method parameters.
1985 * @return bool true when done.
1987 function blogger_editPost($args) {
1989 $this->escape($args);
1991 $post_ID = (int) $args[1];
1992 $username = $args[2];
1993 $password = $args[3];
1994 $content = $args[4];
1995 $publish = $args[5];
1997 if ( !$user = $this->login($username, $password) )
1998 return $this->error;
2000 do_action('xmlrpc_call', 'blogger.editPost');
2002 $actual_post = wp_get_single_post($post_ID,ARRAY_A);
2004 if ( !$actual_post || $actual_post['post_type'] != 'post' )
2005 return new IXR_Error(404, __('Sorry, no such post.'));
2007 $this->escape($actual_post);
2009 if ( !current_user_can('edit_post', $post_ID) )
2010 return new IXR_Error(401, __('Sorry, you do not have the right to edit this post.'));
2012 extract($actual_post, EXTR_SKIP);
2014 if ( ('publish' == $post_status) && !current_user_can('publish_posts') )
2015 return new IXR_Error(401, __('Sorry, you do not have the right to publish this post.'));
2017 $post_title = xmlrpc_getposttitle($content);
2018 $post_category = xmlrpc_getpostcategory($content);
2019 $post_content = xmlrpc_removepostdata($content);
2021 $postdata = compact('ID', 'post_content', 'post_title', 'post_category', 'post_status', 'post_excerpt');
2023 $result = wp_update_post($postdata);
2026 return new IXR_Error(500, __('For some strange yet very annoying reason, this post could not be edited.'));
2028 $this->attach_uploads( $ID, $post_content );
2038 * @param array $args Method parameters.
2039 * @return bool True when post is deleted.
2041 function blogger_deletePost($args) {
2042 $this->escape($args);
2044 $post_ID = (int) $args[1];
2045 $username = $args[2];
2046 $password = $args[3];
2047 $publish = $args[4];
2049 if ( !$user = $this->login($username, $password) )
2050 return $this->error;
2052 do_action('xmlrpc_call', 'blogger.deletePost');
2054 $actual_post = wp_get_single_post($post_ID,ARRAY_A);
2056 if ( !$actual_post || $actual_post['post_type'] != 'post' )
2057 return new IXR_Error(404, __('Sorry, no such post.'));
2059 if ( !current_user_can('delete_post', $post_ID) )
2060 return new IXR_Error(401, __('Sorry, you do not have the right to delete this post.'));
2062 $result = wp_delete_post($post_ID);
2065 return new IXR_Error(500, __('For some strange yet very annoying reason, this post could not be deleted.'));
2070 /* MetaWeblog API functions
2071 * specs on wherever Dave Winer wants them to be
2075 * Create a new post.
2079 * @param array $args Method parameters.
2082 function mw_newPost($args) {
2083 $this->escape($args);
2085 $blog_ID = (int) $args[0]; // we will support this in the near future
2086 $username = $args[1];
2087 $password = $args[2];
2088 $content_struct = $args[3];
2089 $publish = isset( $args[4] ) ? $args[4] : 0;
2091 if ( !$user = $this->login($username, $password) )
2092 return $this->error;
2094 do_action('xmlrpc_call', 'metaWeblog.newPost');
2096 $page_template = '';
2097 if ( !empty( $content_struct['post_type'] ) ) {
2098 if ( $content_struct['post_type'] == 'page' ) {
2100 $cap = 'publish_pages';
2101 elseif ('publish' == $content_struct['page_status'])
2102 $cap = 'publish_pages';
2104 $cap = 'edit_pages';
2105 $error_message = __( 'Sorry, you are not allowed to publish pages on this site.' );
2106 $post_type = 'page';
2107 if ( !empty( $content_struct['wp_page_template'] ) )
2108 $page_template = $content_struct['wp_page_template'];
2109 } elseif ( $content_struct['post_type'] == 'post' ) {
2111 $cap = 'publish_posts';
2112 elseif ('publish' == $content_struct['post_status'])
2113 $cap = 'publish_posts';
2115 $cap = 'edit_posts';
2116 $error_message = __( 'Sorry, you are not allowed to publish posts on this site.' );
2117 $post_type = 'post';
2119 // No other post_type values are allowed here
2120 return new IXR_Error( 401, __( 'Invalid post type.' ) );
2124 $cap = 'publish_posts';
2125 elseif ('publish' == $content_struct['post_status'])
2126 $cap = 'publish_posts';
2128 $cap = 'edit_posts';
2129 $error_message = __( 'Sorry, you are not allowed to publish posts on this site.' );
2130 $post_type = 'post';
2133 if ( !current_user_can( $cap ) )
2134 return new IXR_Error( 401, $error_message );
2136 // Check for a valid post format if one was given
2137 if ( isset( $content_struct['wp_post_format'] ) ) {
2138 $content_struct['wp_post_format'] = sanitize_key( $content_struct['wp_post_format'] );
2139 if ( !array_key_exists( $content_struct['wp_post_format'], get_post_format_strings() ) ) {
2140 return new IXR_Error( 404, __( 'Invalid post format' ) );
2144 // Let WordPress generate the post_name (slug) unless
2145 // one has been provided.
2147 if ( isset($content_struct["wp_slug"]) )
2148 $post_name = $content_struct["wp_slug"];
2150 // Only use a password if one was given.
2151 if ( isset($content_struct["wp_password"]) )
2152 $post_password = $content_struct["wp_password"];
2154 // Only set a post parent if one was provided.
2155 if ( isset($content_struct["wp_page_parent_id"]) )
2156 $post_parent = $content_struct["wp_page_parent_id"];
2158 // Only set the menu_order if it was provided.
2159 if ( isset($content_struct["wp_page_order"]) )
2160 $menu_order = $content_struct["wp_page_order"];
2162 $post_author = $user->ID;
2164 // If an author id was provided then use it instead.
2165 if ( isset($content_struct["wp_author_id"]) && ($user->ID != $content_struct["wp_author_id"]) ) {
2166 switch ( $post_type ) {
2168 if ( !current_user_can("edit_others_posts") )
2169 return(new IXR_Error(401, __("You are not allowed to post as this user")));
2172 if ( !current_user_can("edit_others_pages") )
2173 return(new IXR_Error(401, __("You are not allowed to create pages as this user")));
2176 return(new IXR_Error(401, __("Invalid post type.")));
2179 $post_author = $content_struct["wp_author_id"];
2182 $post_title = isset( $content_struct['title'] ) ? $content_struct['title'] : null;
2183 $post_content = isset( $content_struct['description'] ) ? $content_struct['description'] : null;
2185 $post_status = $publish ? 'publish' : 'draft';
2187 if ( isset( $content_struct["{$post_type}_status"] ) ) {
2188 switch ( $content_struct["{$post_type}_status"] ) {
2192 $post_status = $content_struct["{$post_type}_status"];
2195 // Pending is only valid for posts, not pages.
2196 if ( $post_type === 'post' )
2197 $post_status = $content_struct["{$post_type}_status"];
2200 $post_status = $publish ? 'publish' : 'draft';
2205 $post_excerpt = isset($content_struct['mt_excerpt']) ? $content_struct['mt_excerpt'] : null;
2206 $post_more = isset($content_struct['mt_text_more']) ? $content_struct['mt_text_more'] : null;
2208 $tags_input = isset($content_struct['mt_keywords']) ? $content_struct['mt_keywords'] : null;
2210 if ( isset($content_struct["mt_allow_comments"]) ) {
2211 if ( !is_numeric($content_struct["mt_allow_comments"]) ) {
2212 switch ( $content_struct["mt_allow_comments"] ) {
2214 $comment_status = "closed";
2217 $comment_status = "open";
2220 $comment_status = get_option("default_comment_status");
2224 switch ( (int) $content_struct["mt_allow_comments"] ) {
2227 $comment_status = "closed";
2230 $comment_status = "open";
2233 $comment_status = get_option("default_comment_status");
2238 $comment_status = get_option("default_comment_status");
2241 if ( isset($content_struct["mt_allow_pings"]) ) {
2242 if ( !is_numeric($content_struct["mt_allow_pings"]) ) {
2243 switch ( $content_struct['mt_allow_pings'] ) {
2245 $ping_status = "closed";
2248 $ping_status = "open";
2251 $ping_status = get_option("default_ping_status");
2255 switch ( (int) $content_struct["mt_allow_pings"] ) {
2257 $ping_status = "closed";
2260 $ping_status = "open";
2263 $ping_status = get_option("default_ping_status");
2268 $ping_status = get_option("default_ping_status");
2272 $post_content = $post_content . "<!--more-->" . $post_more;
2275 if ( isset( $content_struct['mt_tb_ping_urls'] ) ) {
2276 $to_ping = $content_struct['mt_tb_ping_urls'];
2277 if ( is_array($to_ping) )
2278 $to_ping = implode(' ', $to_ping);
2281 // Do some timestamp voodoo
2282 if ( !empty( $content_struct['date_created_gmt'] ) )
2283 $dateCreated = str_replace( 'Z', '', $content_struct['date_created_gmt']->getIso() ) . 'Z'; // We know this is supposed to be GMT, so we're going to slap that Z on there by force
2284 elseif ( !empty( $content_struct['dateCreated']) )
2285 $dateCreated = $content_struct['dateCreated']->getIso();
2287 if ( !empty( $dateCreated ) ) {
2288 $post_date = get_date_from_gmt(iso8601_to_datetime($dateCreated));
2289 $post_date_gmt = iso8601_to_datetime($dateCreated, 'GMT');
2291 $post_date = current_time('mysql');
2292 $post_date_gmt = current_time('mysql', 1);
2295 $post_category = array();
2296 if ( isset( $content_struct['categories'] ) ) {
2297 $catnames = $content_struct['categories'];
2298 logIO('O', 'Post cats: ' . var_export($catnames,true));
2300 if ( is_array($catnames) ) {
2301 foreach ($catnames as $cat) {
2302 $post_category[] = get_cat_ID($cat);
2307 // We've got all the data -- post it:
2308 $postdata = compact('post_author', 'post_date', 'post_date_gmt', 'post_content', 'post_title', 'post_category', 'post_status', 'post_excerpt', 'comment_status', 'ping_status', 'to_ping', 'post_type', 'post_name', 'post_password', 'post_parent', 'menu_order', 'tags_input', 'page_template');
2310 $post_ID = wp_insert_post($postdata, true);
2311 if ( is_wp_error( $post_ID ) )
2312 return new IXR_Error(500, $post_ID->get_error_message());
2315 return new IXR_Error(500, __('Sorry, your entry could not be posted. Something wrong happened.'));
2317 // Only posts can be sticky
2318 if ( $post_type == 'post' && isset( $content_struct['sticky'] ) ) {
2319 if ( $content_struct['sticky'] == true )
2320 stick_post( $post_ID );
2321 elseif ( $content_struct['sticky'] == false )
2322 unstick_post( $post_ID );
2325 if ( isset($content_struct['custom_fields']) )
2326 $this->set_custom_fields($post_ID, $content_struct['custom_fields']);
2328 // Handle enclosures
2329 $thisEnclosure = isset($content_struct['enclosure']) ? $content_struct['enclosure'] : null;
2330 $this->add_enclosure_if_new($post_ID, $thisEnclosure);
2332 $this->attach_uploads( $post_ID, $post_content );
2334 // Handle post formats if assigned, value is validated earlier
2336 if ( isset( $content_struct['wp_post_format'] ) )
2337 wp_set_post_terms( $post_ID, array( 'post-format-' . $content_struct['wp_post_format'] ), 'post_format' );
2339 logIO('O', "Posted ! ID: $post_ID");
2341 return strval($post_ID);
2344 function add_enclosure_if_new($post_ID, $enclosure) {
2345 if ( is_array( $enclosure ) && isset( $enclosure['url'] ) && isset( $enclosure['length'] ) && isset( $enclosure['type'] ) ) {
2347 $encstring = $enclosure['url'] . "\n" . $enclosure['length'] . "\n" . $enclosure['type'];
2349 foreach ( (array) get_post_custom($post_ID) as $key => $val) {
2350 if ($key == 'enclosure') {
2351 foreach ( (array) $val as $enc ) {
2352 if ($enc == $encstring) {
2360 add_post_meta( $post_ID, 'enclosure', $encstring );
2365 * Attach upload to a post.
2369 * @param int $post_ID Post ID.
2370 * @param string $post_content Post Content for attachment.
2372 function attach_uploads( $post_ID, $post_content ) {
2375 // find any unattached files
2376 $attachments = $wpdb->get_results( "SELECT ID, guid FROM {$wpdb->posts} WHERE post_parent = '0' AND post_type = 'attachment'" );
2377 if ( is_array( $attachments ) ) {
2378 foreach ( $attachments as $file ) {
2379 if ( strpos( $post_content, $file->guid ) !== false )
2380 $wpdb->update($wpdb->posts, array('post_parent' => $post_ID), array('ID' => $file->ID) );
2390 * @param array $args Method parameters.
2391 * @return bool True on success.
2393 function mw_editPost($args) {
2395 $this->escape($args);
2397 $post_ID = (int) $args[0];
2398 $username = $args[1];
2399 $password = $args[2];
2400 $content_struct = $args[3];
2401 $publish = $args[4];
2403 if ( !$user = $this->login($username, $password) )
2404 return $this->error;
2406 do_action('xmlrpc_call', 'metaWeblog.editPost');
2408 $cap = ( $publish ) ? 'publish_posts' : 'edit_posts';
2409 $error_message = __( 'Sorry, you are not allowed to publish posts on this site.' );
2410 $post_type = 'post';
2411 $page_template = '';
2412 if ( !empty( $content_struct['post_type'] ) ) {
2413 if ( $content_struct['post_type'] == 'page' ) {
2414 if ( $publish || 'publish' == $content_struct['page_status'] )
2415 $cap = 'publish_pages';
2417 $cap = 'edit_pages';
2418 $error_message = __( 'Sorry, you are not allowed to publish pages on this site.' );
2419 $post_type = 'page';
2420 if ( !empty( $content_struct['wp_page_template'] ) )
2421 $page_template = $content_struct['wp_page_template'];
2422 } elseif ( $content_struct['post_type'] == 'post' ) {
2423 if ( $publish || 'publish' == $content_struct['post_status'] )
2424 $cap = 'publish_posts';
2426 $cap = 'edit_posts';
2427 $error_message = __( 'Sorry, you are not allowed to publish posts on this site.' );
2428 $post_type = 'post';
2430 // No other post_type values are allowed here
2431 return new IXR_Error( 401, __( 'Invalid post type.' ) );
2434 if ( $publish || 'publish' == $content_struct['post_status'] )
2435 $cap = 'publish_posts';
2437 $cap = 'edit_posts';
2438 $error_message = __( 'Sorry, you are not allowed to publish posts on this site.' );
2439 $post_type = 'post';
2442 if ( !current_user_can( $cap ) )
2443 return new IXR_Error( 401, $error_message );
2445 // Check for a valid post format if one was given
2446 if ( isset( $content_struct['wp_post_format'] ) ) {
2447 $content_struct['wp_post_format'] = sanitize_key( $content_struct['wp_post_format'] );
2448 if ( !array_key_exists( $content_struct['wp_post_format'], get_post_format_strings() ) ) {
2449 return new IXR_Error( 404, __( 'Invalid post format' ) );
2453 $postdata = wp_get_single_post($post_ID, ARRAY_A);
2455 // If there is no post data for the give post id, stop
2456 // now and return an error. Other wise a new post will be
2457 // created (which was the old behavior).
2458 if ( empty($postdata["ID"]) )
2459 return(new IXR_Error(404, __("Invalid post ID.")));
2461 $this->escape($postdata);
2462 extract($postdata, EXTR_SKIP);
2464 // Let WordPress manage slug if none was provided.
2466 if ( isset($content_struct["wp_slug"]) )
2467 $post_name = $content_struct["wp_slug"];
2469 // Only use a password if one was given.
2470 if ( isset($content_struct["wp_password"]) )
2471 $post_password = $content_struct["wp_password"];
2473 // Only set a post parent if one was given.
2474 if ( isset($content_struct["wp_page_parent_id"]) )
2475 $post_parent = $content_struct["wp_page_parent_id"];
2477 // Only set the menu_order if it was given.
2478 if ( isset($content_struct["wp_page_order"]) )
2479 $menu_order = $content_struct["wp_page_order"];
2481 $post_author = $postdata["post_author"];
2483 // Only set the post_author if one is set.
2484 if ( isset($content_struct["wp_author_id"]) && ($user->ID != $content_struct["wp_author_id"]) ) {
2485 switch ( $post_type ) {
2487 if ( !current_user_can("edit_others_posts") )
2488 return(new IXR_Error(401, __("You are not allowed to change the post author as this user.")));
2491 if ( !current_user_can("edit_others_pages") )
2492 return(new IXR_Error(401, __("You are not allowed to change the page author as this user.")));
2495 return(new IXR_Error(401, __("Invalid post type.")));
2498 $post_author = $content_struct["wp_author_id"];
2501 if ( isset($content_struct["mt_allow_comments"]) ) {
2502 if ( !is_numeric($content_struct["mt_allow_comments"]) ) {
2503 switch ( $content_struct["mt_allow_comments"] ) {
2505 $comment_status = "closed";
2508 $comment_status = "open";
2511 $comment_status = get_option("default_comment_status");
2515 switch ( (int) $content_struct["mt_allow_comments"] ) {
2518 $comment_status = "closed";
2521 $comment_status = "open";
2524 $comment_status = get_option("default_comment_status");
2530 if ( isset($content_struct["mt_allow_pings"]) ) {
2531 if ( !is_numeric($content_struct["mt_allow_pings"]) ) {
2532 switch ( $content_struct["mt_allow_pings"] ) {
2534 $ping_status = "closed";
2537 $ping_status = "open";
2540 $ping_status = get_option("default_ping_status");
2544 switch ( (int) $content_struct["mt_allow_pings"] ) {
2546 $ping_status = "closed";
2549 $ping_status = "open";
2552 $ping_status = get_option("default_ping_status");
2558 $post_title = isset( $content_struct['title'] ) ? $content_struct['title'] : null;
2559 $post_content = isset( $content_struct['description'] ) ? $content_struct['description'] : null;
2561 $post_category = array();
2562 if ( isset( $content_struct['categories'] ) ) {
2563 $catnames = $content_struct['categories'];
2564 if ( is_array($catnames) ) {
2565 foreach ($catnames as $cat) {
2566 $post_category[] = get_cat_ID($cat);
2571 $post_excerpt = isset( $content_struct['mt_excerpt'] ) ? $content_struct['mt_excerpt'] : null;
2572 $post_more = isset( $content_struct['mt_text_more'] ) ? $content_struct['mt_text_more'] : null;
2574 $post_status = $publish ? 'publish' : 'draft';
2575 if ( isset( $content_struct["{$post_type}_status"] ) ) {
2576 switch( $content_struct["{$post_type}_status"] ) {
2580 $post_status = $content_struct["{$post_type}_status"];
2583 // Pending is only valid for posts, not pages.
2584 if ( $post_type === 'post' )
2585 $post_status = $content_struct["{$post_type}_status"];
2588 $post_status = $publish ? 'publish' : 'draft';
2593 $tags_input = isset( $content_struct['mt_keywords'] ) ? $content_struct['mt_keywords'] : null;
2595 if ( ('publish' == $post_status) ) {
2596 if ( ( 'page' == $post_type ) && !current_user_can('publish_pages') )
2597 return new IXR_Error(401, __('Sorry, you do not have the right to publish this page.'));
2598 else if ( !current_user_can('publish_posts') )
2599 return new IXR_Error(401, __('Sorry, you do not have the right to publish this post.'));
2603 $post_content = $post_content . "<!--more-->" . $post_more;
2606 if ( isset( $content_struct['mt_tb_ping_urls'] ) ) {
2607 $to_ping = $content_struct['mt_tb_ping_urls'];
2608 if ( is_array($to_ping) )
2609 $to_ping = implode(' ', $to_ping);
2612 // Do some timestamp voodoo
2613 if ( !empty( $content_struct['date_created_gmt'] ) )
2614 $dateCreated = str_replace( 'Z', '', $content_struct['date_created_gmt']->getIso() ) . 'Z'; // We know this is supposed to be GMT, so we're going to slap that Z on there by force
2615 elseif ( !empty( $content_struct['dateCreated']) )
2616 $dateCreated = $content_struct['dateCreated']->getIso();
2618 if ( !empty( $dateCreated ) ) {
2619 $post_date = get_date_from_gmt(iso8601_to_datetime($dateCreated));
2620 $post_date_gmt = iso8601_to_datetime($dateCreated, 'GMT');
2622 $post_date = $postdata['post_date'];
2623 $post_date_gmt = $postdata['post_date_gmt'];
2626 // We've got all the data -- post it:
2627 $newpost = compact('ID', 'post_content', 'post_title', 'post_category', 'post_status', 'post_excerpt', 'comment_status', 'ping_status', 'post_date', 'post_date_gmt', 'to_ping', 'post_name', 'post_password', 'post_parent', 'menu_order', 'post_author', 'tags_input', 'page_template');
2629 $result = wp_update_post($newpost, true);
2630 if ( is_wp_error( $result ) )
2631 return new IXR_Error(500, $result->get_error_message());
2634 return new IXR_Error(500, __('Sorry, your entry could not be edited. Something wrong happened.'));
2636 // Only posts can be sticky
2637 if ( $post_type == 'post' && isset( $content_struct['sticky'] ) ) {
2638 if ( $content_struct['sticky'] == true )
2639 stick_post( $post_ID );
2640 elseif ( $content_struct['sticky'] == false )
2641 unstick_post( $post_ID );
2644 if ( isset($content_struct['custom_fields']) )
2645 $this->set_custom_fields($post_ID, $content_struct['custom_fields']);
2647 // Handle enclosures
2648 $thisEnclosure = isset($content_struct['enclosure']) ? $content_struct['enclosure'] : null;
2649 $this->add_enclosure_if_new($post_ID, $thisEnclosure);
2651 $this->attach_uploads( $ID, $post_content );
2653 // Handle post formats if assigned, validation is handled
2654 // earlier in this function
2655 if ( isset( $content_struct['wp_post_format'] ) )
2656 wp_set_post_terms( $post_ID, array( 'post-format-' . $content_struct['wp_post_format'] ), 'post_format' );
2658 logIO('O',"(MW) Edited ! ID: $post_ID");
2668 * @param array $args Method parameters.
2671 function mw_getPost($args) {
2673 $this->escape($args);
2675 $post_ID = (int) $args[0];
2676 $username = $args[1];
2677 $password = $args[2];
2679 if ( !$user = $this->login($username, $password) )
2680 return $this->error;
2682 if ( !current_user_can( 'edit_post', $post_ID ) )
2683 return new IXR_Error( 401, __( 'Sorry, you cannot edit this post.' ) );
2685 do_action('xmlrpc_call', 'metaWeblog.getPost');
2687 $postdata = wp_get_single_post($post_ID, ARRAY_A);
2689 if ($postdata['post_date'] != '') {
2690 $post_date = mysql2date('Ymd\TH:i:s', $postdata['post_date'], false);
2691 $post_date_gmt = mysql2date('Ymd\TH:i:s', $postdata['post_date_gmt'], false);
2693 // For drafts use the GMT version of the post date
2694 if ( $postdata['post_status'] == 'draft' )
2695 $post_date_gmt = get_gmt_from_date( mysql2date( 'Y-m-d H:i:s', $postdata['post_date'] ), 'Ymd\TH:i:s' );
2697 $categories = array();
2698 $catids = wp_get_post_categories($post_ID);
2699 foreach($catids as $catid)
2700 $categories[] = get_cat_name($catid);
2702 $tagnames = array();
2703 $tags = wp_get_post_tags( $post_ID );
2704 if ( !empty( $tags ) ) {
2705 foreach ( $tags as $tag )
2706 $tagnames[] = $tag->name;
2707 $tagnames = implode( ', ', $tagnames );
2712 $post = get_extended($postdata['post_content']);
2713 $link = post_permalink($postdata['ID']);
2715 // Get the author info.
2716 $author = get_userdata($postdata['post_author']);
2718 $allow_comments = ('open' == $postdata['comment_status']) ? 1 : 0;
2719 $allow_pings = ('open' == $postdata['ping_status']) ? 1 : 0;
2721 // Consider future posts as published
2722 if ( $postdata['post_status'] === 'future' )
2723 $postdata['post_status'] = 'publish';
2726 $post_format = get_post_format( $post_ID );
2727 if ( empty( $post_format ) )
2728 $post_format = 'standard';
2731 if ( is_sticky( $post_ID ) )
2734 $enclosure = array();
2735 foreach ( (array) get_post_custom($post_ID) as $key => $val) {
2736 if ($key == 'enclosure') {
2737 foreach ( (array) $val as $enc ) {
2738 $encdata = split("\n", $enc);
2739 $enclosure['url'] = trim(htmlspecialchars($encdata[0]));
2740 $enclosure['length'] = (int) trim($encdata[1]);
2741 $enclosure['type'] = trim($encdata[2]);
2748 'dateCreated' => new IXR_Date($post_date),
2749 'userid' => $postdata['post_author'],
2750 'postid' => $postdata['ID'],
2751 'description' => $post['main'],
2752 'title' => $postdata['post_title'],
2754 'permaLink' => $link,
2755 // commented out because no other tool seems to use this
2756 // 'content' => $entry['post_content'],
2757 'categories' => $categories,
2758 'mt_excerpt' => $postdata['post_excerpt'],
2759 'mt_text_more' => $post['extended'],
2760 'mt_allow_comments' => $allow_comments,
2761 'mt_allow_pings' => $allow_pings,
2762 'mt_keywords' => $tagnames,
2763 'wp_slug' => $postdata['post_name'],
2764 'wp_password' => $postdata['post_password'],
2765 'wp_author_id' => $author->ID,
2766 'wp_author_display_name' => $author->display_name,
2767 'date_created_gmt' => new IXR_Date($post_date_gmt),
2768 'post_status' => $postdata['post_status'],
2769 'custom_fields' => $this->get_custom_fields($post_ID),
2770 'wp_post_format' => $post_format,
2774 if ( !empty($enclosure) ) $resp['enclosure'] = $enclosure;
2778 return new IXR_Error(404, __('Sorry, no such post.'));
2783 * Retrieve list of recent posts.
2787 * @param array $args Method parameters.
2790 function mw_getRecentPosts($args) {
2792 $this->escape($args);
2794 $blog_ID = (int) $args[0];
2795 $username = $args[1];
2796 $password = $args[2];
2797 if ( isset( $args[3] ) )
2798 $query = array( 'numberposts' => absint( $args[3] ) );
2802 if ( !$user = $this->login($username, $password) )
2803 return $this->error;
2805 do_action('xmlrpc_call', 'metaWeblog.getRecentPosts');
2807 $posts_list = wp_get_recent_posts( $query );
2812 foreach ($posts_list as $entry) {
2813 if ( !current_user_can( 'edit_post', $entry['ID'] ) )
2816 $post_date = mysql2date('Ymd\TH:i:s', $entry['post_date'], false);
2817 $post_date_gmt = mysql2date('Ymd\TH:i:s', $entry['post_date_gmt'], false);
2819 // For drafts use the GMT version of the date
2820 if ( $entry['post_status'] == 'draft' )
2821 $post_date_gmt = get_gmt_from_date( mysql2date( 'Y-m-d H:i:s', $entry['post_date'] ), 'Ymd\TH:i:s' );
2823 $categories = array();
2824 $catids = wp_get_post_categories($entry['ID']);
2825 foreach( $catids as $catid )
2826 $categories[] = get_cat_name($catid);
2828 $tagnames = array();
2829 $tags = wp_get_post_tags( $entry['ID'] );
2830 if ( !empty( $tags ) ) {
2831 foreach ( $tags as $tag ) {
2832 $tagnames[] = $tag->name;
2834 $tagnames = implode( ', ', $tagnames );
2839 $post = get_extended($entry['post_content']);
2840 $link = post_permalink($entry['ID']);
2842 // Get the post author info.
2843 $author = get_userdata($entry['post_author']);
2845 $allow_comments = ('open' == $entry['comment_status']) ? 1 : 0;
2846 $allow_pings = ('open' == $entry['ping_status']) ? 1 : 0;
2848 // Consider future posts as published
2849 if ( $entry['post_status'] === 'future' )
2850 $entry['post_status'] = 'publish';
2853 $post_format = get_post_format( $entry['ID'] );
2854 if ( empty( $post_format ) )
2855 $post_format = 'standard';
2858 'dateCreated' => new IXR_Date($post_date),
2859 'userid' => $entry['post_author'],
2860 'postid' => (string) $entry['ID'],
2861 'description' => $post['main'],
2862 'title' => $entry['post_title'],
2864 'permaLink' => $link,
2865 // commented out because no other tool seems to use this
2866 // 'content' => $entry['post_content'],
2867 'categories' => $categories,
2868 'mt_excerpt' => $entry['post_excerpt'],
2869 'mt_text_more' => $post['extended'],
2870 'mt_allow_comments' => $allow_comments,
2871 'mt_allow_pings' => $allow_pings,
2872 'mt_keywords' => $tagnames,
2873 'wp_slug' => $entry['post_name'],
2874 'wp_password' => $entry['post_password'],
2875 'wp_author_id' => $author->ID,
2876 'wp_author_display_name' => $author->display_name,
2877 'date_created_gmt' => new IXR_Date($post_date_gmt),
2878 'post_status' => $entry['post_status'],
2879 'custom_fields' => $this->get_custom_fields($entry['ID']),
2880 'wp_post_format' => $post_format
2885 $recent_posts = array();
2886 for ( $j=0; $j<count($struct); $j++ ) {
2887 array_push($recent_posts, $struct[$j]);
2890 return $recent_posts;
2894 * Retrieve the list of categories on a given blog.
2898 * @param array $args Method parameters.
2901 function mw_getCategories($args) {
2903 $this->escape($args);
2905 $blog_ID = (int) $args[0];
2906 $username = $args[1];
2907 $password = $args[2];
2909 if ( !$user = $this->login($username, $password) )
2910 return $this->error;
2912 if ( !current_user_can( 'edit_posts' ) )
2913 return new IXR_Error( 401, __( 'Sorry, you must be able to edit posts on this site in order to view categories.' ) );
2915 do_action('xmlrpc_call', 'metaWeblog.getCategories');
2917 $categories_struct = array();
2919 if ( $cats = get_categories(array('get' => 'all')) ) {
2920 foreach ( $cats as $cat ) {
2921 $struct['categoryId'] = $cat->term_id;
2922 $struct['parentId'] = $cat->parent;
2923 $struct['description'] = $cat->name;
2924 $struct['categoryDescription'] = $cat->description;
2925 $struct['categoryName'] = $cat->name;
2926 $struct['htmlUrl'] = esc_html(get_category_link($cat->term_id));
2927 $struct['rssUrl'] = esc_html(get_category_feed_link($cat->term_id, 'rss2'));
2929 $categories_struct[] = $struct;
2933 return $categories_struct;
2937 * Uploads a file, following your settings.
2939 * Adapted from a patch by Johann Richard.
2941 * @link http://mycvs.org/archives/2004/06/30/file-upload-to-wordpress-in-ecto/
2945 * @param array $args Method parameters.
2948 function mw_newMediaObject($args) {
2951 $blog_ID = (int) $args[0];
2952 $username = $wpdb->escape($args[1]);
2953 $password = $wpdb->escape($args[2]);
2956 $name = sanitize_file_name( $data['name'] );
2957 $type = $data['type'];
2958 $bits = $data['bits'];
2960 logIO('O', '(MW) Received '.strlen($bits).' bytes');
2962 if ( !$user = $this->login($username, $password) )
2963 return $this->error;
2965 do_action('xmlrpc_call', 'metaWeblog.newMediaObject');
2967 if ( !current_user_can('upload_files') ) {
2968 logIO('O', '(MW) User does not have upload_files capability');
2969 $this->error = new IXR_Error(401, __('You are not allowed to upload files to this site.'));
2970 return $this->error;
2973 if ( $upload_err = apply_filters( "pre_upload_error", false ) )
2974 return new IXR_Error(500, $upload_err);
2976 if ( !empty($data["overwrite"]) && ($data["overwrite"] == true) ) {
2977 // Get postmeta info on the object.
2978 $old_file = $wpdb->get_row("
2981 WHERE post_title = '{$name}'
2982 AND post_type = 'attachment'
2985 // Delete previous file.
2986 wp_delete_attachment($old_file->ID);
2988 // Make sure the new name is different by pre-pending the
2989 // previous post id.
2990 $filename = preg_replace("/^wpid\d+-/", "", $name);
2991 $name = "wpid{$old_file->ID}-{$filename}";
2994 $upload = wp_upload_bits($name, NULL, $bits);
2995 if ( ! empty($upload['error']) ) {
2996 $errorString = sprintf(__('Could not write file %1$s (%2$s)'), $name, $upload['error']);
2997 logIO('O', '(MW) ' . $errorString);
2998 return new IXR_Error(500, $errorString);
3000 // Construct the attachment array
3001 // attach to post_id 0
3003 $attachment = array(
3004 'post_title' => $name,
3005 'post_content' => '',
3006 'post_type' => 'attachment',
3007 'post_parent' => $post_id,
3008 'post_mime_type' => $type,
3009 'guid' => $upload[ 'url' ]
3013 $id = wp_insert_attachment( $attachment, $upload[ 'file' ], $post_id );
3014 wp_update_attachment_metadata( $id, wp_generate_attachment_metadata( $id, $upload['file'] ) );
3016 return apply_filters( 'wp_handle_upload', array( 'file' => $name, 'url' => $upload[ 'url' ], 'type' => $type ), 'upload' );
3019 /* MovableType API functions
3020 * specs on http://www.movabletype.org/docs/mtmanual_programmatic.html
3024 * Retrieve the post titles of recent posts.
3028 * @param array $args Method parameters.
3031 function mt_getRecentPostTitles($args) {
3033 $this->escape($args);
3035 $blog_ID = (int) $args[0];
3036 $username = $args[1];
3037 $password = $args[2];
3038 if ( isset( $args[3] ) )
3039 $query = array( 'numberposts' => absint( $args[3] ) );
3043 if ( !$user = $this->login($username, $password) )
3044 return $this->error;
3046 do_action('xmlrpc_call', 'mt.getRecentPostTitles');
3048 $posts_list = wp_get_recent_posts( $query );
3050 if ( !$posts_list ) {
3051 $this->error = new IXR_Error(500, __('Either there are no posts, or something went wrong.'));
3052 return $this->error;
3055 foreach ($posts_list as $entry) {
3056 if ( !current_user_can( 'edit_post', $entry['ID'] ) )
3059 $post_date = mysql2date('Ymd\TH:i:s', $entry['post_date'], false);
3060 $post_date_gmt = mysql2date('Ymd\TH:i:s', $entry['post_date_gmt'], false);
3062 // For drafts use the GMT version of the date
3063 if ( $entry['post_status'] == 'draft' )
3064 $post_date_gmt = get_gmt_from_date( mysql2date( 'Y-m-d H:i:s', $entry['post_date'] ), 'Ymd\TH:i:s' );
3067 'dateCreated' => new IXR_Date($post_date),
3068 'userid' => $entry['post_author'],
3069 'postid' => (string) $entry['ID'],
3070 'title' => $entry['post_title'],
3071 'date_created_gmt' => new IXR_Date($post_date_gmt)
3076 $recent_posts = array();
3077 for ( $j=0; $j<count($struct); $j++ ) {
3078 array_push($recent_posts, $struct[$j]);
3081 return $recent_posts;
3085 * Retrieve list of all categories on blog.
3089 * @param array $args Method parameters.
3092 function mt_getCategoryList($args) {
3094 $this->escape($args);
3096 $blog_ID = (int) $args[0];
3097 $username = $args[1];
3098 $password = $args[2];
3100 if ( !$user = $this->login($username, $password) )
3101 return $this->error;
3103 if ( !current_user_can( 'edit_posts' ) )
3104 return new IXR_Error( 401, __( 'Sorry, you must be able to edit posts on this site in order to view categories.' ) );
3106 do_action('xmlrpc_call', 'mt.getCategoryList');
3108 $categories_struct = array();
3110 if ( $cats = get_categories(array('hide_empty' => 0, 'hierarchical' => 0)) ) {
3111 foreach ( $cats as $cat ) {
3112 $struct['categoryId'] = $cat->term_id;
3113 $struct['categoryName'] = $cat->name;
3115 $categories_struct[] = $struct;
3119 return $categories_struct;
3123 * Retrieve post categories.
3127 * @param array $args Method parameters.
3130 function mt_getPostCategories($args) {
3132 $this->escape($args);
3134 $post_ID = (int) $args[0];
3135 $username = $args[1];
3136 $password = $args[2];
3138 if ( !$user = $this->login($username, $password) )
3139 return $this->error;
3141 if ( !current_user_can( 'edit_post', $post_ID ) )
3142 return new IXR_Error( 401, __( 'Sorry, you can not edit this post.' ) );
3144 do_action('xmlrpc_call', 'mt.getPostCategories');
3146 $categories = array();
3147 $catids = wp_get_post_categories(intval($post_ID));
3148 // first listed category will be the primary category
3150 foreach ( $catids as $catid ) {
3151 $categories[] = array(
3152 'categoryName' => get_cat_name($catid),
3153 'categoryId' => (string) $catid,
3154 'isPrimary' => $isPrimary
3163 * Sets categories for a post.
3167 * @param array $args Method parameters.
3168 * @return bool True on success.
3170 function mt_setPostCategories($args) {
3172 $this->escape($args);
3174 $post_ID = (int) $args[0];
3175 $username = $args[1];
3176 $password = $args[2];
3177 $categories = $args[3];
3179 if ( !$user = $this->login($username, $password) )
3180 return $this->error;
3182 do_action('xmlrpc_call', 'mt.setPostCategories');
3184 if ( !current_user_can('edit_post', $post_ID) )
3185 return new IXR_Error(401, __('Sorry, you cannot edit this post.'));
3187 foreach ( $categories as $cat ) {
3188 $catids[] = $cat['categoryId'];
3191 wp_set_post_categories($post_ID, $catids);
3197 * Retrieve an array of methods supported by this server.
3201 * @param array $args Method parameters.
3204 function mt_supportedMethods($args) {
3206 do_action('xmlrpc_call', 'mt.supportedMethods');
3208 $supported_methods = array();
3209 foreach ( $this->methods as $key => $value ) {
3210 $supported_methods[] = $key;
3213 return $supported_methods;
3217 * Retrieve an empty array because we don't support per-post text filters.
3221 * @param array $args Method parameters.
3223 function mt_supportedTextFilters($args) {
3224 do_action('xmlrpc_call', 'mt.supportedTextFilters');
3225 return apply_filters('xmlrpc_text_filters', array());
3229 * Retrieve trackbacks sent to a given post.
3233 * @param array $args Method parameters.
3236 function mt_getTrackbackPings($args) {
3240 $post_ID = intval($args);
3242 do_action('xmlrpc_call', 'mt.getTrackbackPings');
3244 $actual_post = wp_get_single_post($post_ID, ARRAY_A);
3246 if ( !$actual_post )
3247 return new IXR_Error(404, __('Sorry, no such post.'));
3249 $comments = $wpdb->get_results( $wpdb->prepare("SELECT comment_author_url, comment_content, comment_author_IP, comment_type FROM $wpdb->comments WHERE comment_post_ID = %d", $post_ID) );
3254 $trackback_pings = array();
3255 foreach ( $comments as $comment ) {
3256 if ( 'trackback' == $comment->comment_type ) {
3257 $content = $comment->comment_content;
3258 $title = substr($content, 8, (strpos($content, '</strong>') - 8));
3259 $trackback_pings[] = array(
3260 'pingTitle' => $title,
3261 'pingURL' => $comment->comment_author_url,
3262 'pingIP' => $comment->comment_author_IP
3267 return $trackback_pings;
3271 * Sets a post's publish status to 'publish'.
3275 * @param array $args Method parameters.
3278 function mt_publishPost($args) {
3280 $this->escape($args);
3282 $post_ID = (int) $args[0];
3283 $username = $args[1];
3284 $password = $args[2];
3286 if ( !$user = $this->login($username, $password) )
3287 return $this->error;
3289 do_action('xmlrpc_call', 'mt.publishPost');
3291 if ( !current_user_can('publish_posts') || !current_user_can('edit_post', $post_ID) )
3292 return new IXR_Error(401, __('Sorry, you cannot publish this post.'));
3294 $postdata = wp_get_single_post($post_ID,ARRAY_A);
3296 $postdata['post_status'] = 'publish';
3299 $cats = wp_get_post_categories($post_ID);
3300 $postdata['post_category'] = $cats;
3301 $this->escape($postdata);
3303 $result = wp_update_post($postdata);
3308 /* PingBack functions
3309 * specs on www.hixie.ch/specs/pingback/pingback
3313 * Retrieves a pingback and registers it.
3317 * @param array $args Method parameters.
3320 function pingback_ping($args) {
3323 do_action('xmlrpc_call', 'pingback.ping');
3325 $this->escape($args);
3327 $pagelinkedfrom = $args[0];
3328 $pagelinkedto = $args[1];
3332 $pagelinkedfrom = str_replace('&', '&', $pagelinkedfrom);
3333 $pagelinkedto = str_replace('&', '&', $pagelinkedto);
3334 $pagelinkedto = str_replace('&', '&', $pagelinkedto);
3336 // Check if the page linked to is in our site
3337 $pos1 = strpos($pagelinkedto, str_replace(array('http://www.','http://','https://www.','https://'), '', get_option('home')));
3339 return new IXR_Error(0, __('Is there no link to us?'));
3341 // let's find which post is linked to
3342 // FIXME: does url_to_postid() cover all these cases already?
3343 // if so, then let's use it and drop the old code.
3344 $urltest = parse_url($pagelinkedto);
3345 if ( $post_ID = url_to_postid($pagelinkedto) ) {
3346 $way = 'url_to_postid()';
3347 } elseif ( preg_match('#p/[0-9]{1,}#', $urltest['path'], $match) ) {
3348 // the path defines the post_ID (archives/p/XXXX)
3349 $blah = explode('/', $match[0]);
3350 $post_ID = (int) $blah[1];
3351 $way = 'from the path';
3352 } elseif ( preg_match('#p=[0-9]{1,}#', $urltest['query'], $match) ) {
3353 // the querystring defines the post_ID (?p=XXXX)
3354 $blah = explode('=', $match[0]);
3355 $post_ID = (int) $blah[1];
3356 $way = 'from the querystring';
3357 } elseif ( isset($urltest['fragment']) ) {
3358 // an #anchor is there, it's either...
3359 if ( intval($urltest['fragment']) ) {
3360 // ...an integer #XXXX (simpliest case)
3361 $post_ID = (int) $urltest['fragment'];
3362 $way = 'from the fragment (numeric)';
3363 } elseif ( preg_match('/post-[0-9]+/',$urltest['fragment']) ) {
3364 // ...a post id in the form 'post-###'
3365 $post_ID = preg_replace('/[^0-9]+/', '', $urltest['fragment']);
3366 $way = 'from the fragment (post-###)';
3367 } elseif ( is_string($urltest['fragment']) ) {
3368 // ...or a string #title, a little more complicated
3369 $title = preg_replace('/[^a-z0-9]/i', '.', $urltest['fragment']);
3370 $sql = $wpdb->prepare("SELECT ID FROM $wpdb->posts WHERE post_title RLIKE %s", like_escape( $title ) );
3371 if (! ($post_ID = $wpdb->get_var($sql)) ) {
3372 // returning unknown error '0' is better than die()ing
3373 return new IXR_Error(0, '');
3375 $way = 'from the fragment (title)';
3378 // TODO: Attempt to extract a post ID from the given URL
3379 return new IXR_Error(33, __('The specified target URL cannot be used as a target. It either doesn’t exist, or it is not a pingback-enabled resource.'));
3381 $post_ID = (int) $post_ID;
3384 logIO("O","(PB) URL='$pagelinkedto' ID='$post_ID' Found='$way'");
3386 $post = get_post($post_ID);
3388 if ( !$post ) // Post_ID not found
3389 return new IXR_Error(33, __('The specified target URL cannot be used as a target. It either doesn’t exist, or it is not a pingback-enabled resource.'));
3391 if ( $post_ID == url_to_postid($pagelinkedfrom) )
3392 return new IXR_Error(0, __('The source URL and the target URL cannot both point to the same resource.'));
3394 // Check if pings are on
3395 if ( !pings_open($post) )
3396 return new IXR_Error(33, __('The specified target URL cannot be used as a target. It either doesn’t exist, or it is not a pingback-enabled resource.'));
3398 // Let's check that the remote site didn't already pingback this entry
3399 if ( $wpdb->get_results( $wpdb->prepare("SELECT * FROM $wpdb->comments WHERE comment_post_ID = %d AND comment_author_url = %s", $post_ID, $pagelinkedfrom) ) )
3400 return new IXR_Error( 48, __( 'The pingback has already been registered.' ) );
3402 // very stupid, but gives time to the 'from' server to publish !
3405 // Let's check the remote site
3406 $linea = wp_remote_fopen( $pagelinkedfrom );
3408 return new IXR_Error(16, __('The source URL does not exist.'));
3410 $linea = apply_filters('pre_remote_source', $linea, $pagelinkedto);
3412 // Work around bug in strip_tags():
3413 $linea = str_replace('<!DOC', '<DOC', $linea);
3414 $linea = preg_replace( '/[\s\r\n\t]+/', ' ', $linea ); // normalize spaces
3415 $linea = preg_replace( "/ <(h1|h2|h3|h4|h5|h6|p|th|td|li|dt|dd|pre|caption|input|textarea|button|body)[^>]*>/", "\n\n", $linea );
3417 preg_match('|<title>([^<]*?)</title>|is', $linea, $matchtitle);
3418 $title = $matchtitle[1];
3419 if ( empty( $title ) )
3420 return new IXR_Error(32, __('We cannot find a title on that page.'));
3422 $linea = strip_tags( $linea, '<a>' ); // just keep the tag we need
3424 $p = explode( "\n\n", $linea );
3426 $preg_target = preg_quote($pagelinkedto, '|');
3428 foreach ( $p as $para ) {
3429 if ( strpos($para, $pagelinkedto) !== false ) { // it exists, but is it a link?
3430 preg_match("|<a[^>]+?".$preg_target."[^>]*>([^>]+?)</a>|", $para, $context);
3432 // If the URL isn't in a link context, keep looking
3433 if ( empty($context) )
3436 // We're going to use this fake tag to mark the context in a bit
3437 // the marker is needed in case the link text appears more than once in the paragraph
3438 $excerpt = preg_replace('|\</?wpcontext\>|', '', $para);
3440 // prevent really long link text
3441 if ( strlen($context[1]) > 100 )
3442 $context[1] = substr($context[1], 0, 100) . '...';
3444 $marker = '<wpcontext>'.$context[1].'</wpcontext>'; // set up our marker
3445 $excerpt= str_replace($context[0], $marker, $excerpt); // swap out the link for our marker
3446 $excerpt = strip_tags($excerpt, '<wpcontext>'); // strip all tags but our context marker
3447 $excerpt = trim($excerpt);
3448 $preg_marker = preg_quote($marker, '|');
3449 $excerpt = preg_replace("|.*?\s(.{0,100}$preg_marker.{0,100})\s.*|s", '$1', $excerpt);
3450 $excerpt = strip_tags($excerpt); // YES, again, to remove the marker wrapper
3455 if ( empty($context) ) // Link to target not found
3456 return new IXR_Error(17, __('The source URL does not contain a link to the target URL, and so cannot be used as a source.'));
3458 $pagelinkedfrom = str_replace('&', '&', $pagelinkedfrom);
3460 $context = '[...] ' . esc_html( $excerpt ) . ' [...]';
3461 $pagelinkedfrom = $wpdb->escape( $pagelinkedfrom );
3463 $comment_post_ID = (int) $post_ID;
3464 $comment_author = $title;
3465 $comment_author_email = '';
3466 $this->escape($comment_author);
3467 $comment_author_url = $pagelinkedfrom;
3468 $comment_content = $context;
3469 $this->escape($comment_content);
3470 $comment_type = 'pingback';
3472 $commentdata = compact('comment_post_ID', 'comment_author', 'comment_author_url', 'comment_author_email', 'comment_content', 'comment_type');
3474 $comment_ID = wp_new_comment($commentdata);
3475 do_action('pingback_post', $comment_ID);
3477 return sprintf(__('Pingback from %1$s to %2$s registered. Keep the web talking! :-)'), $pagelinkedfrom, $pagelinkedto);
3481 * Retrieve array of URLs that pingbacked the given URL.
3483 * Specs on http://www.aquarionics.com/misc/archives/blogite/0198.html
3487 * @param array $args Method parameters.
3490 function pingback_extensions_getPingbacks($args) {
3494 do_action('xmlrpc_call', 'pingback.extensions.getPingbacks');
3496 $this->escape($args);
3500 $post_ID = url_to_postid($url);
3502 // We aren't sure that the resource is available and/or pingback enabled
3503 return new IXR_Error(33, __('The specified target URL cannot be used as a target. It either doesn’t exist, or it is not a pingback-enabled resource.'));
3506 $actual_post = wp_get_single_post($post_ID, ARRAY_A);
3508 if ( !$actual_post ) {
3509 // No such post = resource not found
3510 return new IXR_Error(32, __('The specified target URL does not exist.'));
3513 $comments = $wpdb->get_results( $wpdb->prepare("SELECT comment_author_url, comment_content, comment_author_IP, comment_type FROM $wpdb->comments WHERE comment_post_ID = %d", $post_ID) );
3518 $pingbacks = array();
3519 foreach ( $comments as $comment ) {
3520 if ( 'pingback' == $comment->comment_type )
3521 $pingbacks[] = $comment->comment_author_url;