Ticket #14820: move-wp-xmlrpc-server-class.14820.diff
File move-wp-xmlrpc-server-class.14820.diff, 186.4 KB (added by , 14 years ago) |
---|
-
wp-includes/class-xmlrpc-server.php
1 <?php 2 3 /** 4 * logIO() - Writes logging info to a file. 5 * 6 * @uses $xmlrpc_logging 7 * @package WordPress 8 * @subpackage Logging 9 * 10 * @param string $io Whether input or output 11 * @param string $msg Information describing logging reason. 12 * @return bool Always return true 13 */ 14 function logIO($io,$msg) { 15 global $xmlrpc_logging; 16 if ($xmlrpc_logging) { 17 $fp = fopen("../xmlrpc.log","a+"); 18 $date = gmdate("Y-m-d H:i:s "); 19 $iot = ($io == "I") ? " Input: " : " Output: "; 20 fwrite($fp, "\n\n".$date.$iot.$msg); 21 fclose($fp); 22 } 23 return true; 24 } 25 26 /** 27 * WordPress XMLRPC server implementation. 28 * 29 * Implements compatability for Blogger API, MetaWeblog API, MovableType, and 30 * pingback. Additional WordPress API for managing comments, pages, posts, 31 * options, etc. 32 * 33 * Since WordPress 2.6.0, WordPress XMLRPC server can be disabled in the 34 * administration panels. 35 * 36 * @package WordPress 37 * @subpackage Publishing 38 * @since 1.5.0 39 */ 40 class wp_xmlrpc_server extends IXR_Server { 41 42 /** 43 * Register all of the XMLRPC methods that XMLRPC server understands. 44 * 45 * PHP4 constructor and sets up server and method property. Passes XMLRPC 46 * methods through the 'xmlrpc_methods' filter to allow plugins to extend 47 * or replace XMLRPC methods. 48 * 49 * @since 1.5.0 50 * 51 * @return wp_xmlrpc_server 52 */ 53 function wp_xmlrpc_server() { 54 $this->methods = array( 55 // WordPress API 56 'wp.getUsersBlogs' => 'this:wp_getUsersBlogs', 57 'wp.getPage' => 'this:wp_getPage', 58 'wp.getPages' => 'this:wp_getPages', 59 'wp.newPage' => 'this:wp_newPage', 60 'wp.deletePage' => 'this:wp_deletePage', 61 'wp.editPage' => 'this:wp_editPage', 62 'wp.getPageList' => 'this:wp_getPageList', 63 'wp.getAuthors' => 'this:wp_getAuthors', 64 'wp.getCategories' => 'this:mw_getCategories', // Alias 65 'wp.getTags' => 'this:wp_getTags', 66 'wp.newCategory' => 'this:wp_newCategory', 67 'wp.deleteCategory' => 'this:wp_deleteCategory', 68 'wp.suggestCategories' => 'this:wp_suggestCategories', 69 'wp.uploadFile' => 'this:mw_newMediaObject', // Alias 70 'wp.getCommentCount' => 'this:wp_getCommentCount', 71 'wp.getPostStatusList' => 'this:wp_getPostStatusList', 72 'wp.getPageStatusList' => 'this:wp_getPageStatusList', 73 'wp.getPageTemplates' => 'this:wp_getPageTemplates', 74 'wp.getOptions' => 'this:wp_getOptions', 75 'wp.setOptions' => 'this:wp_setOptions', 76 'wp.getComment' => 'this:wp_getComment', 77 'wp.getComments' => 'this:wp_getComments', 78 'wp.deleteComment' => 'this:wp_deleteComment', 79 'wp.editComment' => 'this:wp_editComment', 80 'wp.newComment' => 'this:wp_newComment', 81 'wp.getCommentStatusList' => 'this:wp_getCommentStatusList', 82 83 // Blogger API 84 'blogger.getUsersBlogs' => 'this:blogger_getUsersBlogs', 85 'blogger.getUserInfo' => 'this:blogger_getUserInfo', 86 'blogger.getPost' => 'this:blogger_getPost', 87 'blogger.getRecentPosts' => 'this:blogger_getRecentPosts', 88 'blogger.getTemplate' => 'this:blogger_getTemplate', 89 'blogger.setTemplate' => 'this:blogger_setTemplate', 90 'blogger.newPost' => 'this:blogger_newPost', 91 'blogger.editPost' => 'this:blogger_editPost', 92 'blogger.deletePost' => 'this:blogger_deletePost', 93 94 // MetaWeblog API (with MT extensions to structs) 95 'metaWeblog.newPost' => 'this:mw_newPost', 96 'metaWeblog.editPost' => 'this:mw_editPost', 97 'metaWeblog.getPost' => 'this:mw_getPost', 98 'metaWeblog.getRecentPosts' => 'this:mw_getRecentPosts', 99 'metaWeblog.getCategories' => 'this:mw_getCategories', 100 'metaWeblog.newMediaObject' => 'this:mw_newMediaObject', 101 102 // MetaWeblog API aliases for Blogger API 103 // see http://www.xmlrpc.com/stories/storyReader$2460 104 'metaWeblog.deletePost' => 'this:blogger_deletePost', 105 'metaWeblog.getTemplate' => 'this:blogger_getTemplate', 106 'metaWeblog.setTemplate' => 'this:blogger_setTemplate', 107 'metaWeblog.getUsersBlogs' => 'this:blogger_getUsersBlogs', 108 109 // MovableType API 110 'mt.getCategoryList' => 'this:mt_getCategoryList', 111 'mt.getRecentPostTitles' => 'this:mt_getRecentPostTitles', 112 'mt.getPostCategories' => 'this:mt_getPostCategories', 113 'mt.setPostCategories' => 'this:mt_setPostCategories', 114 'mt.supportedMethods' => 'this:mt_supportedMethods', 115 'mt.supportedTextFilters' => 'this:mt_supportedTextFilters', 116 'mt.getTrackbackPings' => 'this:mt_getTrackbackPings', 117 'mt.publishPost' => 'this:mt_publishPost', 118 119 // PingBack 120 'pingback.ping' => 'this:pingback_ping', 121 'pingback.extensions.getPingbacks' => 'this:pingback_extensions_getPingbacks', 122 123 'demo.sayHello' => 'this:sayHello', 124 'demo.addTwoNumbers' => 'this:addTwoNumbers' 125 ); 126 127 $this->initialise_blog_option_info( ); 128 $this->methods = apply_filters('xmlrpc_methods', $this->methods); 129 } 130 131 function serve_request() { 132 $this->IXR_Server($this->methods); 133 } 134 135 /** 136 * Test XMLRPC API by saying, "Hello!" to client. 137 * 138 * @since 1.5.0 139 * 140 * @param array $args Method Parameters. 141 * @return string 142 */ 143 function sayHello($args) { 144 return 'Hello!'; 145 } 146 147 /** 148 * Test XMLRPC API by adding two numbers for client. 149 * 150 * @since 1.5.0 151 * 152 * @param array $args Method Parameters. 153 * @return int 154 */ 155 function addTwoNumbers($args) { 156 $number1 = $args[0]; 157 $number2 = $args[1]; 158 return $number1 + $number2; 159 } 160 161 /** 162 * Check user's credentials. 163 * 164 * @since 1.5.0 165 * 166 * @param string $user_login User's username. 167 * @param string $user_pass User's password. 168 * @return bool Whether authentication passed. 169 * @deprecated use wp_xmlrpc_server::login 170 * @see wp_xmlrpc_server::login 171 */ 172 function login_pass_ok($user_login, $user_pass) { 173 if ( !get_option( 'enable_xmlrpc' ) ) { 174 $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') ) ); 175 return false; 176 } 177 178 if (!user_pass_ok($user_login, $user_pass)) { 179 $this->error = new IXR_Error(403, __('Bad login/pass combination.')); 180 return false; 181 } 182 return true; 183 } 184 185 /** 186 * Log user in. 187 * 188 * @since 2.8 189 * 190 * @param string $username User's username. 191 * @param string $password User's password. 192 * @return mixed WP_User object if authentication passed, false otherwise 193 */ 194 function login($username, $password) { 195 if ( !get_option( 'enable_xmlrpc' ) ) { 196 $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') ) ); 197 return false; 198 } 199 200 $user = wp_authenticate($username, $password); 201 202 if (is_wp_error($user)) { 203 $this->error = new IXR_Error(403, __('Bad login/pass combination.')); 204 return false; 205 } 206 207 wp_set_current_user( $user->ID ); 208 return $user; 209 } 210 211 /** 212 * Sanitize string or array of strings for database. 213 * 214 * @since 1.5.2 215 * 216 * @param string|array $array Sanitize single string or array of strings. 217 * @return string|array Type matches $array and sanitized for the database. 218 */ 219 function escape(&$array) { 220 global $wpdb; 221 222 if (!is_array($array)) { 223 return($wpdb->escape($array)); 224 } else { 225 foreach ( (array) $array as $k => $v ) { 226 if ( is_array($v) ) { 227 $this->escape($array[$k]); 228 } else if ( is_object($v) ) { 229 //skip 230 } else { 231 $array[$k] = $wpdb->escape($v); 232 } 233 } 234 } 235 } 236 237 /** 238 * Retrieve custom fields for post. 239 * 240 * @since 2.5.0 241 * 242 * @param int $post_id Post ID. 243 * @return array Custom fields, if exist. 244 */ 245 function get_custom_fields($post_id) { 246 $post_id = (int) $post_id; 247 248 $custom_fields = array(); 249 250 foreach ( (array) has_meta($post_id) as $meta ) { 251 // Don't expose protected fields. 252 if ( strpos($meta['meta_key'], '_wp_') === 0 ) { 253 continue; 254 } 255 256 $custom_fields[] = array( 257 "id" => $meta['meta_id'], 258 "key" => $meta['meta_key'], 259 "value" => $meta['meta_value'] 260 ); 261 } 262 263 return $custom_fields; 264 } 265 266 /** 267 * Set custom fields for post. 268 * 269 * @since 2.5.0 270 * 271 * @param int $post_id Post ID. 272 * @param array $fields Custom fields. 273 */ 274 function set_custom_fields($post_id, $fields) { 275 $post_id = (int) $post_id; 276 277 foreach ( (array) $fields as $meta ) { 278 if ( isset($meta['id']) ) { 279 $meta['id'] = (int) $meta['id']; 280 281 if ( isset($meta['key']) ) { 282 update_meta($meta['id'], $meta['key'], $meta['value']); 283 } 284 else { 285 delete_meta($meta['id']); 286 } 287 } 288 else { 289 $_POST['metakeyinput'] = $meta['key']; 290 $_POST['metavalue'] = $meta['value']; 291 add_meta($post_id); 292 } 293 } 294 } 295 296 /** 297 * Set up blog options property. 298 * 299 * Passes property through 'xmlrpc_blog_options' filter. 300 * 301 * @since 2.6.0 302 */ 303 function initialise_blog_option_info( ) { 304 global $wp_version; 305 306 $this->blog_options = array( 307 // Read only options 308 'software_name' => array( 309 'desc' => __( 'Software Name' ), 310 'readonly' => true, 311 'value' => 'WordPress' 312 ), 313 'software_version' => array( 314 'desc' => __( 'Software Version' ), 315 'readonly' => true, 316 'value' => $wp_version 317 ), 318 'blog_url' => array( 319 'desc' => __( 'Site URL' ), 320 'readonly' => true, 321 'option' => 'siteurl' 322 ), 323 324 // Updatable options 325 'time_zone' => array( 326 'desc' => __( 'Time Zone' ), 327 'readonly' => false, 328 'option' => 'gmt_offset' 329 ), 330 'blog_title' => array( 331 'desc' => __( 'Site Title' ), 332 'readonly' => false, 333 'option' => 'blogname' 334 ), 335 'blog_tagline' => array( 336 'desc' => __( 'Site Tagline' ), 337 'readonly' => false, 338 'option' => 'blogdescription' 339 ), 340 'date_format' => array( 341 'desc' => __( 'Date Format' ), 342 'readonly' => false, 343 'option' => 'date_format' 344 ), 345 'time_format' => array( 346 'desc' => __( 'Time Format' ), 347 'readonly' => false, 348 'option' => 'time_format' 349 ), 350 'users_can_register' => array( 351 'desc' => __( 'Allow new users to sign up' ), 352 'readonly' => false, 353 'option' => 'users_can_register' 354 ) 355 ); 356 357 $this->blog_options = apply_filters( 'xmlrpc_blog_options', $this->blog_options ); 358 } 359 360 /** 361 * Retrieve the blogs of the user. 362 * 363 * @since 2.6.0 364 * 365 * @param array $args Method parameters. 366 * @return array 367 */ 368 function wp_getUsersBlogs( $args ) { 369 global $current_site; 370 // If this isn't on WPMU then just use blogger_getUsersBlogs 371 if ( !is_multisite() ) { 372 array_unshift( $args, 1 ); 373 return $this->blogger_getUsersBlogs( $args ); 374 } 375 376 $this->escape( $args ); 377 378 $username = $args[0]; 379 $password = $args[1]; 380 381 if ( !$user = $this->login($username, $password) ) 382 return $this->error; 383 384 385 do_action( 'xmlrpc_call', 'wp.getUsersBlogs' ); 386 387 $blogs = (array) get_blogs_of_user( $user->ID ); 388 $struct = array( ); 389 390 foreach ( $blogs as $blog ) { 391 // Don't include blogs that aren't hosted at this site 392 if ( $blog->site_id != $current_site->id ) 393 continue; 394 395 $blog_id = $blog->userblog_id; 396 switch_to_blog($blog_id); 397 $is_admin = current_user_can('manage_options'); 398 399 $struct[] = array( 400 'isAdmin' => $is_admin, 401 'url' => get_option( 'home' ) . '/', 402 'blogid' => $blog_id, 403 'blogName' => get_option( 'blogname' ), 404 'xmlrpc' => site_url( 'xmlrpc.php' ) 405 ); 406 407 restore_current_blog( ); 408 } 409 410 return $struct; 411 } 412 413 /** 414 * Retrieve page. 415 * 416 * @since 2.2.0 417 * 418 * @param array $args Method parameters. 419 * @return array 420 */ 421 function wp_getPage($args) { 422 $this->escape($args); 423 424 $blog_id = (int) $args[0]; 425 $page_id = (int) $args[1]; 426 $username = $args[2]; 427 $password = $args[3]; 428 429 if ( !$user = $this->login($username, $password) ) { 430 return $this->error; 431 } 432 433 if ( !current_user_can( 'edit_page', $page_id ) ) 434 return new IXR_Error( 401, __( 'Sorry, you cannot edit this page.' ) ); 435 436 do_action('xmlrpc_call', 'wp.getPage'); 437 438 // Lookup page info. 439 $page = get_page($page_id); 440 441 // If we found the page then format the data. 442 if ( $page->ID && ($page->post_type == "page") ) { 443 // Get all of the page content and link. 444 $full_page = get_extended($page->post_content); 445 $link = post_permalink($page->ID); 446 447 // Get info the page parent if there is one. 448 $parent_title = ""; 449 if ( !empty($page->post_parent) ) { 450 $parent = get_page($page->post_parent); 451 $parent_title = $parent->post_title; 452 } 453 454 // Determine comment and ping settings. 455 $allow_comments = comments_open($page->ID) ? 1 : 0; 456 $allow_pings = pings_open($page->ID) ? 1 : 0; 457 458 // Format page date. 459 $page_date = mysql2date("Ymd\TH:i:s", $page->post_date, false); 460 $page_date_gmt = mysql2date("Ymd\TH:i:s", $page->post_date_gmt, false); 461 462 // For drafts use the GMT version of the date 463 if ( $page->post_status == 'draft' ) 464 $page_date_gmt = get_gmt_from_date( mysql2date( 'Y-m-d H:i:s', $page->post_date ), 'Ymd\TH:i:s' ); 465 466 // Pull the categories info together. 467 $categories = array(); 468 foreach ( wp_get_post_categories($page->ID) as $cat_id ) { 469 $categories[] = get_cat_name($cat_id); 470 } 471 472 // Get the author info. 473 $author = get_userdata($page->post_author); 474 475 $page_template = get_post_meta( $page->ID, '_wp_page_template', true ); 476 if ( empty( $page_template ) ) 477 $page_template = 'default'; 478 479 $page_struct = array( 480 "dateCreated" => new IXR_Date($page_date), 481 "userid" => $page->post_author, 482 "page_id" => $page->ID, 483 "page_status" => $page->post_status, 484 "description" => $full_page["main"], 485 "title" => $page->post_title, 486 "link" => $link, 487 "permaLink" => $link, 488 "categories" => $categories, 489 "excerpt" => $page->post_excerpt, 490 "text_more" => $full_page["extended"], 491 "mt_allow_comments" => $allow_comments, 492 "mt_allow_pings" => $allow_pings, 493 "wp_slug" => $page->post_name, 494 "wp_password" => $page->post_password, 495 "wp_author" => $author->display_name, 496 "wp_page_parent_id" => $page->post_parent, 497 "wp_page_parent_title" => $parent_title, 498 "wp_page_order" => $page->menu_order, 499 "wp_author_id" => $author->ID, 500 "wp_author_display_name" => $author->display_name, 501 "date_created_gmt" => new IXR_Date($page_date_gmt), 502 "custom_fields" => $this->get_custom_fields($page_id), 503 "wp_page_template" => $page_template 504 ); 505 506 return($page_struct); 507 } 508 // If the page doesn't exist indicate that. 509 else { 510 return(new IXR_Error(404, __("Sorry, no such page."))); 511 } 512 } 513 514 /** 515 * Retrieve Pages. 516 * 517 * @since 2.2.0 518 * 519 * @param array $args Method parameters. 520 * @return array 521 */ 522 function wp_getPages($args) { 523 $this->escape($args); 524 525 $blog_id = (int) $args[0]; 526 $username = $args[1]; 527 $password = $args[2]; 528 $num_pages = isset($args[3]) ? (int) $args[3] : 10; 529 530 if ( !$user = $this->login($username, $password) ) 531 return $this->error; 532 533 if ( !current_user_can( 'edit_pages' ) ) 534 return new IXR_Error( 401, __( 'Sorry, you cannot edit pages.' ) ); 535 536 do_action('xmlrpc_call', 'wp.getPages'); 537 538 $pages = get_posts( array('post_type' => 'page', 'post_status' => 'any', 'numberposts' => $num_pages) ); 539 $num_pages = count($pages); 540 541 // If we have pages, put together their info. 542 if ( $num_pages >= 1 ) { 543 $pages_struct = array(); 544 545 for ( $i = 0; $i < $num_pages; $i++ ) { 546 $page = wp_xmlrpc_server::wp_getPage(array( 547 $blog_id, $pages[$i]->ID, $username, $password 548 )); 549 $pages_struct[] = $page; 550 } 551 552 return($pages_struct); 553 } 554 // If no pages were found return an error. 555 else { 556 return(array()); 557 } 558 } 559 560 /** 561 * Create new page. 562 * 563 * @since 2.2.0 564 * 565 * @param array $args Method parameters. 566 * @return unknown 567 */ 568 function wp_newPage($args) { 569 // Items not escaped here will be escaped in newPost. 570 $username = $this->escape($args[1]); 571 $password = $this->escape($args[2]); 572 $page = $args[3]; 573 $publish = $args[4]; 574 575 if ( !$user = $this->login($username, $password) ) 576 return $this->error; 577 578 do_action('xmlrpc_call', 'wp.newPage'); 579 580 // Make sure the user is allowed to add new pages. 581 if ( !current_user_can("publish_pages") ) 582 return(new IXR_Error(401, __("Sorry, you cannot add new pages."))); 583 584 // Mark this as content for a page. 585 $args[3]["post_type"] = "page"; 586 587 // Let mw_newPost do all of the heavy lifting. 588 return($this->mw_newPost($args)); 589 } 590 591 /** 592 * Delete page. 593 * 594 * @since 2.2.0 595 * 596 * @param array $args Method parameters. 597 * @return bool True, if success. 598 */ 599 function wp_deletePage($args) { 600 $this->escape($args); 601 602 $blog_id = (int) $args[0]; 603 $username = $args[1]; 604 $password = $args[2]; 605 $page_id = (int) $args[3]; 606 607 if ( !$user = $this->login($username, $password) ) 608 return $this->error; 609 610 do_action('xmlrpc_call', 'wp.deletePage'); 611 612 // Get the current page based on the page_id and 613 // make sure it is a page and not a post. 614 $actual_page = wp_get_single_post($page_id, ARRAY_A); 615 if ( !$actual_page || ($actual_page["post_type"] != "page") ) 616 return(new IXR_Error(404, __("Sorry, no such page."))); 617 618 // Make sure the user can delete pages. 619 if ( !current_user_can("delete_page", $page_id) ) 620 return(new IXR_Error(401, __("Sorry, you do not have the right to delete this page."))); 621 622 // Attempt to delete the page. 623 $result = wp_delete_post($page_id); 624 if ( !$result ) 625 return(new IXR_Error(500, __("Failed to delete the page."))); 626 627 return(true); 628 } 629 630 /** 631 * Edit page. 632 * 633 * @since 2.2.0 634 * 635 * @param array $args Method parameters. 636 * @return unknown 637 */ 638 function wp_editPage($args) { 639 // Items not escaped here will be escaped in editPost. 640 $blog_id = (int) $args[0]; 641 $page_id = (int) $this->escape($args[1]); 642 $username = $this->escape($args[2]); 643 $password = $this->escape($args[3]); 644 $content = $args[4]; 645 $publish = $args[5]; 646 647 if ( !$user = $this->login($username, $password) ) 648 return $this->error; 649 650 do_action('xmlrpc_call', 'wp.editPage'); 651 652 // Get the page data and make sure it is a page. 653 $actual_page = wp_get_single_post($page_id, ARRAY_A); 654 if ( !$actual_page || ($actual_page["post_type"] != "page") ) 655 return(new IXR_Error(404, __("Sorry, no such page."))); 656 657 // Make sure the user is allowed to edit pages. 658 if ( !current_user_can("edit_page", $page_id) ) 659 return(new IXR_Error(401, __("Sorry, you do not have the right to edit this page."))); 660 661 // Mark this as content for a page. 662 $content["post_type"] = "page"; 663 664 // Arrange args in the way mw_editPost understands. 665 $args = array( 666 $page_id, 667 $username, 668 $password, 669 $content, 670 $publish 671 ); 672 673 // Let mw_editPost do all of the heavy lifting. 674 return($this->mw_editPost($args)); 675 } 676 677 /** 678 * Retrieve page list. 679 * 680 * @since 2.2.0 681 * 682 * @param array $args Method parameters. 683 * @return unknown 684 */ 685 function wp_getPageList($args) { 686 global $wpdb; 687 688 $this->escape($args); 689 690 $blog_id = (int) $args[0]; 691 $username = $args[1]; 692 $password = $args[2]; 693 694 if ( !$user = $this->login($username, $password) ) 695 return $this->error; 696 697 if ( !current_user_can( 'edit_pages' ) ) 698 return new IXR_Error( 401, __( 'Sorry, you cannot edit pages.' ) ); 699 700 do_action('xmlrpc_call', 'wp.getPageList'); 701 702 // Get list of pages ids and titles 703 $page_list = $wpdb->get_results(" 704 SELECT ID page_id, 705 post_title page_title, 706 post_parent page_parent_id, 707 post_date_gmt, 708 post_date, 709 post_status 710 FROM {$wpdb->posts} 711 WHERE post_type = 'page' 712 ORDER BY ID 713 "); 714 715 // The date needs to be formated properly. 716 $num_pages = count($page_list); 717 for ( $i = 0; $i < $num_pages; $i++ ) { 718 $post_date = mysql2date("Ymd\TH:i:s", $page_list[$i]->post_date, false); 719 $post_date_gmt = mysql2date("Ymd\TH:i:s", $page_list[$i]->post_date_gmt, false); 720 721 $page_list[$i]->dateCreated = new IXR_Date($post_date); 722 $page_list[$i]->date_created_gmt = new IXR_Date($post_date_gmt); 723 724 // For drafts use the GMT version of the date 725 if ( $page_list[$i]->post_status == 'draft' ) { 726 $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' ); 727 $page_list[$i]->date_created_gmt = new IXR_Date( $page_list[$i]->date_created_gmt ); 728 } 729 730 unset($page_list[$i]->post_date_gmt); 731 unset($page_list[$i]->post_date); 732 unset($page_list[$i]->post_status); 733 } 734 735 return($page_list); 736 } 737 738 /** 739 * Retrieve authors list. 740 * 741 * @since 2.2.0 742 * 743 * @param array $args Method parameters. 744 * @return array 745 */ 746 function wp_getAuthors($args) { 747 748 $this->escape($args); 749 750 $blog_id = (int) $args[0]; 751 $username = $args[1]; 752 $password = $args[2]; 753 754 if ( !$user = $this->login($username, $password) ) 755 return $this->error; 756 757 if ( !current_user_can("edit_posts") ) 758 return(new IXR_Error(401, __("Sorry, you cannot edit posts on this site."))); 759 760 do_action('xmlrpc_call', 'wp.getAuthors'); 761 762 $authors = array(); 763 foreach ( get_users() as $user_id => $user_object ) { 764 $authors[] = array( 765 "user_id" => $user_id, 766 "user_login" => $user_object->user_login, 767 "display_name" => $user_object->display_name 768 ); 769 } 770 771 return $authors; 772 } 773 774 /** 775 * Get list of all tags 776 * 777 * @since 2.7 778 * 779 * @param array $args Method parameters. 780 * @return array 781 */ 782 function wp_getTags( $args ) { 783 $this->escape( $args ); 784 785 $blog_id = (int) $args[0]; 786 $username = $args[1]; 787 $password = $args[2]; 788 789 if ( !$user = $this->login($username, $password) ) 790 return $this->error; 791 792 if ( !current_user_can( 'edit_posts' ) ) 793 return new IXR_Error( 401, __( 'Sorry, you must be able to edit posts on this site in order to view tags.' ) ); 794 795 do_action( 'xmlrpc_call', 'wp.getKeywords' ); 796 797 $tags = array( ); 798 799 if ( $all_tags = get_tags() ) { 800 foreach( (array) $all_tags as $tag ) { 801 $struct['tag_id'] = $tag->term_id; 802 $struct['name'] = $tag->name; 803 $struct['count'] = $tag->count; 804 $struct['slug'] = $tag->slug; 805 $struct['html_url'] = esc_html( get_tag_link( $tag->term_id ) ); 806 $struct['rss_url'] = esc_html( get_tag_feed_link( $tag->term_id ) ); 807 808 $tags[] = $struct; 809 } 810 } 811 812 return $tags; 813 } 814 815 /** 816 * Create new category. 817 * 818 * @since 2.2.0 819 * 820 * @param array $args Method parameters. 821 * @return int Category ID. 822 */ 823 function wp_newCategory($args) { 824 $this->escape($args); 825 826 $blog_id = (int) $args[0]; 827 $username = $args[1]; 828 $password = $args[2]; 829 $category = $args[3]; 830 831 if ( !$user = $this->login($username, $password) ) 832 return $this->error; 833 834 do_action('xmlrpc_call', 'wp.newCategory'); 835 836 // Make sure the user is allowed to add a category. 837 if ( !current_user_can("manage_categories") ) 838 return(new IXR_Error(401, __("Sorry, you do not have the right to add a category."))); 839 840 // If no slug was provided make it empty so that 841 // WordPress will generate one. 842 if ( empty($category["slug"]) ) 843 $category["slug"] = ""; 844 845 // If no parent_id was provided make it empty 846 // so that it will be a top level page (no parent). 847 if ( !isset($category["parent_id"]) ) 848 $category["parent_id"] = ""; 849 850 // If no description was provided make it empty. 851 if ( empty($category["description"]) ) 852 $category["description"] = ""; 853 854 $new_category = array( 855 "cat_name" => $category["name"], 856 "category_nicename" => $category["slug"], 857 "category_parent" => $category["parent_id"], 858 "category_description" => $category["description"] 859 ); 860 861 $cat_id = wp_insert_category($new_category, true); 862 if ( is_wp_error( $cat_id ) ) { 863 if ( 'term_exists' == $cat_id->get_error_code() ) 864 return (int) $cat_id->get_error_data(); 865 else 866 return(new IXR_Error(500, __("Sorry, the new category failed."))); 867 } elseif ( ! $cat_id ) { 868 return(new IXR_Error(500, __("Sorry, the new category failed."))); 869 } 870 871 return($cat_id); 872 } 873 874 /** 875 * Remove category. 876 * 877 * @since 2.5.0 878 * 879 * @param array $args Method parameters. 880 * @return mixed See {@link wp_delete_category()} for return info. 881 */ 882 function wp_deleteCategory($args) { 883 $this->escape($args); 884 885 $blog_id = (int) $args[0]; 886 $username = $args[1]; 887 $password = $args[2]; 888 $category_id = (int) $args[3]; 889 890 if ( !$user = $this->login($username, $password) ) 891 return $this->error; 892 893 do_action('xmlrpc_call', 'wp.deleteCategory'); 894 895 if ( !current_user_can("manage_categories") ) 896 return new IXR_Error( 401, __( "Sorry, you do not have the right to delete a category." ) ); 897 898 return wp_delete_category( $category_id ); 899 } 900 901 /** 902 * Retrieve category list. 903 * 904 * @since 2.2.0 905 * 906 * @param array $args Method parameters. 907 * @return array 908 */ 909 function wp_suggestCategories($args) { 910 $this->escape($args); 911 912 $blog_id = (int) $args[0]; 913 $username = $args[1]; 914 $password = $args[2]; 915 $category = $args[3]; 916 $max_results = (int) $args[4]; 917 918 if ( !$user = $this->login($username, $password) ) 919 return $this->error; 920 921 if ( !current_user_can( 'edit_posts' ) ) 922 return new IXR_Error( 401, __( 'Sorry, you must be able to edit posts to this site in order to view categories.' ) ); 923 924 do_action('xmlrpc_call', 'wp.suggestCategories'); 925 926 $category_suggestions = array(); 927 $args = array('get' => 'all', 'number' => $max_results, 'name__like' => $category); 928 foreach ( (array) get_categories($args) as $cat ) { 929 $category_suggestions[] = array( 930 "category_id" => $cat->cat_ID, 931 "category_name" => $cat->cat_name 932 ); 933 } 934 935 return($category_suggestions); 936 } 937 938 /** 939 * Retrieve comment. 940 * 941 * @since 2.7.0 942 * 943 * @param array $args Method parameters. 944 * @return array 945 */ 946 function wp_getComment($args) { 947 $this->escape($args); 948 949 $blog_id = (int) $args[0]; 950 $username = $args[1]; 951 $password = $args[2]; 952 $comment_id = (int) $args[3]; 953 954 if ( !$user = $this->login($username, $password) ) 955 return $this->error; 956 957 if ( !current_user_can( 'moderate_comments' ) ) 958 return new IXR_Error( 403, __( 'You are not allowed to moderate comments on this site.' ) ); 959 960 do_action('xmlrpc_call', 'wp.getComment'); 961 962 if ( ! $comment = get_comment($comment_id) ) 963 return new IXR_Error( 404, __( 'Invalid comment ID.' ) ); 964 965 // Format page date. 966 $comment_date = mysql2date("Ymd\TH:i:s", $comment->comment_date, false); 967 $comment_date_gmt = mysql2date("Ymd\TH:i:s", $comment->comment_date_gmt, false); 968 969 if ( '0' == $comment->comment_approved ) 970 $comment_status = 'hold'; 971 else if ( 'spam' == $comment->comment_approved ) 972 $comment_status = 'spam'; 973 else if ( '1' == $comment->comment_approved ) 974 $comment_status = 'approve'; 975 else 976 $comment_status = $comment->comment_approved; 977 978 $link = get_comment_link($comment); 979 980 $comment_struct = array( 981 "date_created_gmt" => new IXR_Date($comment_date_gmt), 982 "user_id" => $comment->user_id, 983 "comment_id" => $comment->comment_ID, 984 "parent" => $comment->comment_parent, 985 "status" => $comment_status, 986 "content" => $comment->comment_content, 987 "link" => $link, 988 "post_id" => $comment->comment_post_ID, 989 "post_title" => get_the_title($comment->comment_post_ID), 990 "author" => $comment->comment_author, 991 "author_url" => $comment->comment_author_url, 992 "author_email" => $comment->comment_author_email, 993 "author_ip" => $comment->comment_author_IP, 994 "type" => $comment->comment_type, 995 ); 996 997 return $comment_struct; 998 } 999 1000 /** 1001 * Retrieve comments. 1002 * 1003 * @since 2.7.0 1004 * 1005 * @param array $args Method parameters. 1006 * @return array 1007 */ 1008 function wp_getComments($args) { 1009 $raw_args = $args; 1010 $this->escape($args); 1011 1012 $blog_id = (int) $args[0]; 1013 $username = $args[1]; 1014 $password = $args[2]; 1015 $struct = $args[3]; 1016 1017 if ( !$user = $this->login($username, $password) ) 1018 return $this->error; 1019 1020 if ( !current_user_can( 'moderate_comments' ) ) 1021 return new IXR_Error( 401, __( 'Sorry, you cannot edit comments.' ) ); 1022 1023 do_action('xmlrpc_call', 'wp.getComments'); 1024 1025 if ( isset($struct['status']) ) 1026 $status = $struct['status']; 1027 else 1028 $status = ''; 1029 1030 $post_id = ''; 1031 if ( isset($struct['post_id']) ) 1032 $post_id = absint($struct['post_id']); 1033 1034 $offset = 0; 1035 if ( isset($struct['offset']) ) 1036 $offset = absint($struct['offset']); 1037 1038 $number = 10; 1039 if ( isset($struct['number']) ) 1040 $number = absint($struct['number']); 1041 1042 $comments = get_comments( array('status' => $status, 'post_id' => $post_id, 'offset' => $offset, 'number' => $number ) ); 1043 $num_comments = count($comments); 1044 1045 if ( ! $num_comments ) 1046 return array(); 1047 1048 $comments_struct = array(); 1049 1050 for ( $i = 0; $i < $num_comments; $i++ ) { 1051 $comment = wp_xmlrpc_server::wp_getComment(array( 1052 $raw_args[0], $raw_args[1], $raw_args[2], $comments[$i]->comment_ID, 1053 )); 1054 $comments_struct[] = $comment; 1055 } 1056 1057 return $comments_struct; 1058 } 1059 1060 /** 1061 * Remove comment. 1062 * 1063 * @since 2.7.0 1064 * 1065 * @param array $args Method parameters. 1066 * @return mixed {@link wp_delete_comment()} 1067 */ 1068 function wp_deleteComment($args) { 1069 $this->escape($args); 1070 1071 $blog_id = (int) $args[0]; 1072 $username = $args[1]; 1073 $password = $args[2]; 1074 $comment_ID = (int) $args[3]; 1075 1076 if ( !$user = $this->login($username, $password) ) 1077 return $this->error; 1078 1079 if ( !current_user_can( 'moderate_comments' ) ) 1080 return new IXR_Error( 403, __( 'You are not allowed to moderate comments on this site.' ) ); 1081 1082 do_action('xmlrpc_call', 'wp.deleteComment'); 1083 1084 if ( ! get_comment($comment_ID) ) 1085 return new IXR_Error( 404, __( 'Invalid comment ID.' ) ); 1086 1087 return wp_delete_comment($comment_ID); 1088 } 1089 1090 /** 1091 * Edit comment. 1092 * 1093 * @since 2.7.0 1094 * 1095 * @param array $args Method parameters. 1096 * @return bool True, on success. 1097 */ 1098 function wp_editComment($args) { 1099 $this->escape($args); 1100 1101 $blog_id = (int) $args[0]; 1102 $username = $args[1]; 1103 $password = $args[2]; 1104 $comment_ID = (int) $args[3]; 1105 $content_struct = $args[4]; 1106 1107 if ( !$user = $this->login($username, $password) ) 1108 return $this->error; 1109 1110 if ( !current_user_can( 'moderate_comments' ) ) 1111 return new IXR_Error( 403, __( 'You are not allowed to moderate comments on this site.' ) ); 1112 1113 do_action('xmlrpc_call', 'wp.editComment'); 1114 1115 if ( ! get_comment($comment_ID) ) 1116 return new IXR_Error( 404, __( 'Invalid comment ID.' ) ); 1117 1118 if ( isset($content_struct['status']) ) { 1119 $statuses = get_comment_statuses(); 1120 $statuses = array_keys($statuses); 1121 1122 if ( ! in_array($content_struct['status'], $statuses) ) 1123 return new IXR_Error( 401, __( 'Invalid comment status.' ) ); 1124 $comment_approved = $content_struct['status']; 1125 } 1126 1127 // Do some timestamp voodoo 1128 if ( !empty( $content_struct['date_created_gmt'] ) ) { 1129 $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 1130 $comment_date = get_date_from_gmt(iso8601_to_datetime($dateCreated)); 1131 $comment_date_gmt = iso8601_to_datetime($dateCreated, GMT); 1132 } 1133 1134 if ( isset($content_struct['content']) ) 1135 $comment_content = $content_struct['content']; 1136 1137 if ( isset($content_struct['author']) ) 1138 $comment_author = $content_struct['author']; 1139 1140 if ( isset($content_struct['author_url']) ) 1141 $comment_author_url = $content_struct['author_url']; 1142 1143 if ( isset($content_struct['author_email']) ) 1144 $comment_author_email = $content_struct['author_email']; 1145 1146 // We've got all the data -- post it: 1147 $comment = compact('comment_ID', 'comment_content', 'comment_approved', 'comment_date', 'comment_date_gmt', 'comment_author', 'comment_author_email', 'comment_author_url'); 1148 1149 $result = wp_update_comment($comment); 1150 if ( is_wp_error( $result ) ) 1151 return new IXR_Error(500, $result->get_error_message()); 1152 1153 if ( !$result ) 1154 return new IXR_Error(500, __('Sorry, the comment could not be edited. Something wrong happened.')); 1155 1156 return true; 1157 } 1158 1159 /** 1160 * Create new comment. 1161 * 1162 * @since 2.7.0 1163 * 1164 * @param array $args Method parameters. 1165 * @return mixed {@link wp_new_comment()} 1166 */ 1167 function wp_newComment($args) { 1168 global $wpdb; 1169 1170 $this->escape($args); 1171 1172 $blog_id = (int) $args[0]; 1173 $username = $args[1]; 1174 $password = $args[2]; 1175 $post = $args[3]; 1176 $content_struct = $args[4]; 1177 1178 $allow_anon = apply_filters('xmlrpc_allow_anonymous_comments', false); 1179 1180 $user = $this->login($username, $password); 1181 1182 if ( !$user ) { 1183 $logged_in = false; 1184 if ( $allow_anon && get_option('comment_registration') ) 1185 return new IXR_Error( 403, __( 'You must be registered to comment' ) ); 1186 else if ( !$allow_anon ) 1187 return $this->error; 1188 } else { 1189 $logged_in = true; 1190 } 1191 1192 if ( is_numeric($post) ) 1193 $post_id = absint($post); 1194 else 1195 $post_id = url_to_postid($post); 1196 1197 if ( ! $post_id ) 1198 return new IXR_Error( 404, __( 'Invalid post ID.' ) ); 1199 1200 if ( ! get_post($post_id) ) 1201 return new IXR_Error( 404, __( 'Invalid post ID.' ) ); 1202 1203 $comment['comment_post_ID'] = $post_id; 1204 1205 if ( $logged_in ) { 1206 $comment['comment_author'] = $wpdb->escape( $user->display_name ); 1207 $comment['comment_author_email'] = $wpdb->escape( $user->user_email ); 1208 $comment['comment_author_url'] = $wpdb->escape( $user->user_url ); 1209 $comment['user_ID'] = $user->ID; 1210 } else { 1211 $comment['comment_author'] = ''; 1212 if ( isset($content_struct['author']) ) 1213 $comment['comment_author'] = $content_struct['author']; 1214 1215 $comment['comment_author_email'] = ''; 1216 if ( isset($content_struct['author_email']) ) 1217 $comment['comment_author_email'] = $content_struct['author_email']; 1218 1219 $comment['comment_author_url'] = ''; 1220 if ( isset($content_struct['author_url']) ) 1221 $comment['comment_author_url'] = $content_struct['author_url']; 1222 1223 $comment['user_ID'] = 0; 1224 1225 if ( get_option('require_name_email') ) { 1226 if ( 6 > strlen($comment['comment_author_email']) || '' == $comment['comment_author'] ) 1227 return new IXR_Error( 403, __( 'Comment author name and email are required' ) ); 1228 elseif ( !is_email($comment['comment_author_email']) ) 1229 return new IXR_Error( 403, __( 'A valid email address is required' ) ); 1230 } 1231 } 1232 1233 $comment['comment_parent'] = isset($content_struct['comment_parent']) ? absint($content_struct['comment_parent']) : 0; 1234 1235 $comment['comment_content'] = $content_struct['content']; 1236 1237 do_action('xmlrpc_call', 'wp.newComment'); 1238 1239 return wp_new_comment($comment); 1240 } 1241 1242 /** 1243 * Retrieve all of the comment status. 1244 * 1245 * @since 2.7.0 1246 * 1247 * @param array $args Method parameters. 1248 * @return array 1249 */ 1250 function wp_getCommentStatusList($args) { 1251 $this->escape( $args ); 1252 1253 $blog_id = (int) $args[0]; 1254 $username = $args[1]; 1255 $password = $args[2]; 1256 1257 if ( !$user = $this->login($username, $password) ) 1258 return $this->error; 1259 1260 if ( !current_user_can( 'moderate_comments' ) ) 1261 return new IXR_Error( 403, __( 'You are not allowed access to details about this site.' ) ); 1262 1263 do_action('xmlrpc_call', 'wp.getCommentStatusList'); 1264 1265 return get_comment_statuses( ); 1266 } 1267 1268 /** 1269 * Retrieve comment count. 1270 * 1271 * @since 2.5.0 1272 * 1273 * @param array $args Method parameters. 1274 * @return array 1275 */ 1276 function wp_getCommentCount( $args ) { 1277 $this->escape($args); 1278 1279 $blog_id = (int) $args[0]; 1280 $username = $args[1]; 1281 $password = $args[2]; 1282 $post_id = (int) $args[3]; 1283 1284 if ( !$user = $this->login($username, $password) ) 1285 return $this->error; 1286 1287 if ( !current_user_can( 'edit_posts' ) ) 1288 return new IXR_Error( 403, __( 'You are not allowed access to details about comments.' ) ); 1289 1290 do_action('xmlrpc_call', 'wp.getCommentCount'); 1291 1292 $count = wp_count_comments( $post_id ); 1293 return array( 1294 "approved" => $count->approved, 1295 "awaiting_moderation" => $count->moderated, 1296 "spam" => $count->spam, 1297 "total_comments" => $count->total_comments 1298 ); 1299 } 1300 1301 /** 1302 * Retrieve post statuses. 1303 * 1304 * @since 2.5.0 1305 * 1306 * @param array $args Method parameters. 1307 * @return array 1308 */ 1309 function wp_getPostStatusList( $args ) { 1310 $this->escape( $args ); 1311 1312 $blog_id = (int) $args[0]; 1313 $username = $args[1]; 1314 $password = $args[2]; 1315 1316 if ( !$user = $this->login($username, $password) ) 1317 return $this->error; 1318 1319 if ( !current_user_can( 'edit_posts' ) ) 1320 return new IXR_Error( 403, __( 'You are not allowed access to details about this site.' ) ); 1321 1322 do_action('xmlrpc_call', 'wp.getPostStatusList'); 1323 1324 return get_post_statuses( ); 1325 } 1326 1327 /** 1328 * Retrieve page statuses. 1329 * 1330 * @since 2.5.0 1331 * 1332 * @param array $args Method parameters. 1333 * @return array 1334 */ 1335 function wp_getPageStatusList( $args ) { 1336 $this->escape( $args ); 1337 1338 $blog_id = (int) $args[0]; 1339 $username = $args[1]; 1340 $password = $args[2]; 1341 1342 if ( !$user = $this->login($username, $password) ) 1343 return $this->error; 1344 1345 if ( !current_user_can( 'edit_posts' ) ) 1346 return new IXR_Error( 403, __( 'You are not allowed access to details about this site.' ) ); 1347 1348 do_action('xmlrpc_call', 'wp.getPageStatusList'); 1349 1350 return get_page_statuses( ); 1351 } 1352 1353 /** 1354 * Retrieve page templates. 1355 * 1356 * @since 2.6.0 1357 * 1358 * @param array $args Method parameters. 1359 * @return array 1360 */ 1361 function wp_getPageTemplates( $args ) { 1362 $this->escape( $args ); 1363 1364 $blog_id = (int) $args[0]; 1365 $username = $args[1]; 1366 $password = $args[2]; 1367 1368 if ( !$user = $this->login($username, $password) ) 1369 return $this->error; 1370 1371 if ( !current_user_can( 'edit_pages' ) ) 1372 return new IXR_Error( 403, __( 'You are not allowed access to details about this site.' ) ); 1373 1374 $templates = get_page_templates( ); 1375 $templates['Default'] = 'default'; 1376 1377 return $templates; 1378 } 1379 1380 /** 1381 * Retrieve blog options. 1382 * 1383 * @since 2.6.0 1384 * 1385 * @param array $args Method parameters. 1386 * @return array 1387 */ 1388 function wp_getOptions( $args ) { 1389 $this->escape( $args ); 1390 1391 $blog_id = (int) $args[0]; 1392 $username = $args[1]; 1393 $password = $args[2]; 1394 $options = (array) $args[3]; 1395 1396 if ( !$user = $this->login($username, $password) ) 1397 return $this->error; 1398 1399 // If no specific options where asked for, return all of them 1400 if ( count( $options ) == 0 ) 1401 $options = array_keys($this->blog_options); 1402 1403 return $this->_getOptions($options); 1404 } 1405 1406 /** 1407 * Retrieve blog options value from list. 1408 * 1409 * @since 2.6.0 1410 * 1411 * @param array $options Options to retrieve. 1412 * @return array 1413 */ 1414 function _getOptions($options) { 1415 $data = array( ); 1416 foreach ( $options as $option ) { 1417 if ( array_key_exists( $option, $this->blog_options ) ) { 1418 $data[$option] = $this->blog_options[$option]; 1419 //Is the value static or dynamic? 1420 if ( isset( $data[$option]['option'] ) ) { 1421 $data[$option]['value'] = get_option( $data[$option]['option'] ); 1422 unset($data[$option]['option']); 1423 } 1424 } 1425 } 1426 1427 return $data; 1428 } 1429 1430 /** 1431 * Update blog options. 1432 * 1433 * @since 2.6.0 1434 * 1435 * @param array $args Method parameters. 1436 * @return unknown 1437 */ 1438 function wp_setOptions( $args ) { 1439 $this->escape( $args ); 1440 1441 $blog_id = (int) $args[0]; 1442 $username = $args[1]; 1443 $password = $args[2]; 1444 $options = (array) $args[3]; 1445 1446 if ( !$user = $this->login($username, $password) ) 1447 return $this->error; 1448 1449 if ( !current_user_can( 'manage_options' ) ) 1450 return new IXR_Error( 403, __( 'You are not allowed to update options.' ) ); 1451 1452 foreach ( $options as $o_name => $o_value ) { 1453 $option_names[] = $o_name; 1454 if ( !array_key_exists( $o_name, $this->blog_options ) ) 1455 continue; 1456 1457 if ( $this->blog_options[$o_name]['readonly'] == true ) 1458 continue; 1459 1460 update_option( $this->blog_options[$o_name]['option'], $o_value ); 1461 } 1462 1463 //Now return the updated values 1464 return $this->_getOptions($option_names); 1465 } 1466 1467 /* Blogger API functions. 1468 * specs on http://plant.blogger.com/api and http://groups.yahoo.com/group/bloggerDev/ 1469 */ 1470 1471 /** 1472 * Retrieve blogs that user owns. 1473 * 1474 * Will make more sense once we support multiple blogs. 1475 * 1476 * @since 1.5.0 1477 * 1478 * @param array $args Method parameters. 1479 * @return array 1480 */ 1481 function blogger_getUsersBlogs($args) { 1482 if ( is_multisite() ) 1483 return $this->_multisite_getUsersBlogs($args); 1484 1485 $this->escape($args); 1486 1487 $username = $args[1]; 1488 $password = $args[2]; 1489 1490 if ( !$user = $this->login($username, $password) ) 1491 return $this->error; 1492 1493 do_action('xmlrpc_call', 'blogger.getUsersBlogs'); 1494 1495 $is_admin = current_user_can('manage_options'); 1496 1497 $struct = array( 1498 'isAdmin' => $is_admin, 1499 'url' => get_option('home') . '/', 1500 'blogid' => '1', 1501 'blogName' => get_option('blogname'), 1502 'xmlrpc' => site_url( 'xmlrpc.php' ) 1503 ); 1504 1505 return array($struct); 1506 } 1507 1508 /** 1509 * Private function for retrieving a users blogs for multisite setups 1510 * 1511 * @access protected 1512 */ 1513 function _multisite_getUsersBlogs($args) { 1514 global $current_blog; 1515 $domain = $current_blog->domain; 1516 $path = $current_blog->path . 'xmlrpc.php'; 1517 $protocol = is_ssl() ? 'https' : 'http'; 1518 1519 $rpc = new IXR_Client("$protocol://{$domain}{$path}"); 1520 $rpc->query('wp.getUsersBlogs', $args[1], $args[2]); 1521 $blogs = $rpc->getResponse(); 1522 1523 if ( isset($blogs['faultCode']) ) 1524 return new IXR_Error($blogs['faultCode'], $blogs['faultString']); 1525 1526 if ( $_SERVER['HTTP_HOST'] == $domain && $_SERVER['REQUEST_URI'] == $path ) { 1527 return $blogs; 1528 } else { 1529 foreach ( (array) $blogs as $blog ) { 1530 if ( strpos($blog['url'], $_SERVER['HTTP_HOST']) ) 1531 return array($blog); 1532 } 1533 return array(); 1534 } 1535 } 1536 1537 /** 1538 * Retrieve user's data. 1539 * 1540 * Gives your client some info about you, so you don't have to. 1541 * 1542 * @since 1.5.0 1543 * 1544 * @param array $args Method parameters. 1545 * @return array 1546 */ 1547 function blogger_getUserInfo($args) { 1548 1549 $this->escape($args); 1550 1551 $username = $args[1]; 1552 $password = $args[2]; 1553 1554 if ( !$user = $this->login($username, $password) ) 1555 return $this->error; 1556 1557 if ( !current_user_can( 'edit_posts' ) ) 1558 return new IXR_Error( 401, __( 'Sorry, you do not have access to user data on this site.' ) ); 1559 1560 do_action('xmlrpc_call', 'blogger.getUserInfo'); 1561 1562 $struct = array( 1563 'nickname' => $user->nickname, 1564 'userid' => $user->ID, 1565 'url' => $user->user_url, 1566 'lastname' => $user->last_name, 1567 'firstname' => $user->first_name 1568 ); 1569 1570 return $struct; 1571 } 1572 1573 /** 1574 * Retrieve post. 1575 * 1576 * @since 1.5.0 1577 * 1578 * @param array $args Method parameters. 1579 * @return array 1580 */ 1581 function blogger_getPost($args) { 1582 1583 $this->escape($args); 1584 1585 $post_ID = (int) $args[1]; 1586 $username = $args[2]; 1587 $password = $args[3]; 1588 1589 if ( !$user = $this->login($username, $password) ) 1590 return $this->error; 1591 1592 if ( !current_user_can( 'edit_post', $post_ID ) ) 1593 return new IXR_Error( 401, __( 'Sorry, you cannot edit this post.' ) ); 1594 1595 do_action('xmlrpc_call', 'blogger.getPost'); 1596 1597 $post_data = wp_get_single_post($post_ID, ARRAY_A); 1598 1599 $categories = implode(',', wp_get_post_categories($post_ID)); 1600 1601 $content = '<title>'.stripslashes($post_data['post_title']).'</title>'; 1602 $content .= '<category>'.$categories.'</category>'; 1603 $content .= stripslashes($post_data['post_content']); 1604 1605 $struct = array( 1606 'userid' => $post_data['post_author'], 1607 'dateCreated' => new IXR_Date(mysql2date('Ymd\TH:i:s', $post_data['post_date'], false)), 1608 'content' => $content, 1609 'postid' => $post_data['ID'] 1610 ); 1611 1612 return $struct; 1613 } 1614 1615 /** 1616 * Retrieve list of recent posts. 1617 * 1618 * @since 1.5.0 1619 * 1620 * @param array $args Method parameters. 1621 * @return array 1622 */ 1623 function blogger_getRecentPosts($args) { 1624 1625 $this->escape($args); 1626 1627 $blog_ID = (int) $args[1]; /* though we don't use it yet */ 1628 $username = $args[2]; 1629 $password = $args[3]; 1630 $num_posts = $args[4]; 1631 1632 if ( !$user = $this->login($username, $password) ) 1633 return $this->error; 1634 1635 do_action('xmlrpc_call', 'blogger.getRecentPosts'); 1636 1637 $posts_list = wp_get_recent_posts($num_posts); 1638 1639 if ( !$posts_list ) { 1640 $this->error = new IXR_Error(500, __('Either there are no posts, or something went wrong.')); 1641 return $this->error; 1642 } 1643 1644 foreach ($posts_list as $entry) { 1645 if ( !current_user_can( 'edit_post', $entry['ID'] ) ) 1646 continue; 1647 1648 $post_date = mysql2date('Ymd\TH:i:s', $entry['post_date'], false); 1649 $categories = implode(',', wp_get_post_categories($entry['ID'])); 1650 1651 $content = '<title>'.stripslashes($entry['post_title']).'</title>'; 1652 $content .= '<category>'.$categories.'</category>'; 1653 $content .= stripslashes($entry['post_content']); 1654 1655 $struct[] = array( 1656 'userid' => $entry['post_author'], 1657 'dateCreated' => new IXR_Date($post_date), 1658 'content' => $content, 1659 'postid' => $entry['ID'], 1660 ); 1661 1662 } 1663 1664 $recent_posts = array(); 1665 for ( $j=0; $j<count($struct); $j++ ) { 1666 array_push($recent_posts, $struct[$j]); 1667 } 1668 1669 return $recent_posts; 1670 } 1671 1672 /** 1673 * Retrieve blog_filename content. 1674 * 1675 * @since 1.5.0 1676 * 1677 * @param array $args Method parameters. 1678 * @return string 1679 */ 1680 function blogger_getTemplate($args) { 1681 1682 $this->escape($args); 1683 1684 $blog_ID = (int) $args[1]; 1685 $username = $args[2]; 1686 $password = $args[3]; 1687 $template = $args[4]; /* could be 'main' or 'archiveIndex', but we don't use it */ 1688 1689 if ( !$user = $this->login($username, $password) ) 1690 return $this->error; 1691 1692 do_action('xmlrpc_call', 'blogger.getTemplate'); 1693 1694 if ( !current_user_can('edit_themes') ) 1695 return new IXR_Error(401, __('Sorry, this user can not edit the template.')); 1696 1697 /* warning: here we make the assumption that the blog's URL is on the same server */ 1698 $filename = get_option('home') . '/'; 1699 $filename = preg_replace('#https?://.+?/#', $_SERVER['DOCUMENT_ROOT'].'/', $filename); 1700 1701 $f = fopen($filename, 'r'); 1702 $content = fread($f, filesize($filename)); 1703 fclose($f); 1704 1705 /* so it is actually editable with a windows/mac client */ 1706 // 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); 1707 1708 return $content; 1709 } 1710 1711 /** 1712 * Updates the content of blog_filename. 1713 * 1714 * @since 1.5.0 1715 * 1716 * @param array $args Method parameters. 1717 * @return bool True when done. 1718 */ 1719 function blogger_setTemplate($args) { 1720 1721 $this->escape($args); 1722 1723 $blog_ID = (int) $args[1]; 1724 $username = $args[2]; 1725 $password = $args[3]; 1726 $content = $args[4]; 1727 $template = $args[5]; /* could be 'main' or 'archiveIndex', but we don't use it */ 1728 1729 if ( !$user = $this->login($username, $password) ) 1730 return $this->error; 1731 1732 do_action('xmlrpc_call', 'blogger.setTemplate'); 1733 1734 if ( !current_user_can('edit_themes') ) 1735 return new IXR_Error(401, __('Sorry, this user cannot edit the template.')); 1736 1737 /* warning: here we make the assumption that the blog's URL is on the same server */ 1738 $filename = get_option('home') . '/'; 1739 $filename = preg_replace('#https?://.+?/#', $_SERVER['DOCUMENT_ROOT'].'/', $filename); 1740 1741 if ($f = fopen($filename, 'w+')) { 1742 fwrite($f, $content); 1743 fclose($f); 1744 } else { 1745 return new IXR_Error(500, __('Either the file is not writable, or something wrong happened. The file has not been updated.')); 1746 } 1747 1748 return true; 1749 } 1750 1751 /** 1752 * Create new post. 1753 * 1754 * @since 1.5.0 1755 * 1756 * @param array $args Method parameters. 1757 * @return int 1758 */ 1759 function blogger_newPost($args) { 1760 1761 $this->escape($args); 1762 1763 $blog_ID = (int) $args[1]; /* though we don't use it yet */ 1764 $username = $args[2]; 1765 $password = $args[3]; 1766 $content = $args[4]; 1767 $publish = $args[5]; 1768 1769 if ( !$user = $this->login($username, $password) ) 1770 return $this->error; 1771 1772 do_action('xmlrpc_call', 'blogger.newPost'); 1773 1774 $cap = ($publish) ? 'publish_posts' : 'edit_posts'; 1775 if ( !current_user_can($cap) ) 1776 return new IXR_Error(401, __('Sorry, you are not allowed to post on this site.')); 1777 1778 $post_status = ($publish) ? 'publish' : 'draft'; 1779 1780 $post_author = $user->ID; 1781 1782 $post_title = xmlrpc_getposttitle($content); 1783 $post_category = xmlrpc_getpostcategory($content); 1784 $post_content = xmlrpc_removepostdata($content); 1785 1786 $post_date = current_time('mysql'); 1787 $post_date_gmt = current_time('mysql', 1); 1788 1789 $post_data = compact('blog_ID', 'post_author', 'post_date', 'post_date_gmt', 'post_content', 'post_title', 'post_category', 'post_status'); 1790 1791 $post_ID = wp_insert_post($post_data); 1792 if ( is_wp_error( $post_ID ) ) 1793 return new IXR_Error(500, $post_ID->get_error_message()); 1794 1795 if ( !$post_ID ) 1796 return new IXR_Error(500, __('Sorry, your entry could not be posted. Something wrong happened.')); 1797 1798 $this->attach_uploads( $post_ID, $post_content ); 1799 1800 logIO('O', "Posted ! ID: $post_ID"); 1801 1802 return $post_ID; 1803 } 1804 1805 /** 1806 * Edit a post. 1807 * 1808 * @since 1.5.0 1809 * 1810 * @param array $args Method parameters. 1811 * @return bool true when done. 1812 */ 1813 function blogger_editPost($args) { 1814 1815 $this->escape($args); 1816 1817 $post_ID = (int) $args[1]; 1818 $username = $args[2]; 1819 $password = $args[3]; 1820 $content = $args[4]; 1821 $publish = $args[5]; 1822 1823 if ( !$user = $this->login($username, $password) ) 1824 return $this->error; 1825 1826 do_action('xmlrpc_call', 'blogger.editPost'); 1827 1828 $actual_post = wp_get_single_post($post_ID,ARRAY_A); 1829 1830 if ( !$actual_post || $actual_post['post_type'] != 'post' ) 1831 return new IXR_Error(404, __('Sorry, no such post.')); 1832 1833 $this->escape($actual_post); 1834 1835 if ( !current_user_can('edit_post', $post_ID) ) 1836 return new IXR_Error(401, __('Sorry, you do not have the right to edit this post.')); 1837 1838 extract($actual_post, EXTR_SKIP); 1839 1840 if ( ('publish' == $post_status) && !current_user_can('publish_posts') ) 1841 return new IXR_Error(401, __('Sorry, you do not have the right to publish this post.')); 1842 1843 $post_title = xmlrpc_getposttitle($content); 1844 $post_category = xmlrpc_getpostcategory($content); 1845 $post_content = xmlrpc_removepostdata($content); 1846 1847 $postdata = compact('ID', 'post_content', 'post_title', 'post_category', 'post_status', 'post_excerpt'); 1848 1849 $result = wp_update_post($postdata); 1850 1851 if ( !$result ) 1852 return new IXR_Error(500, __('For some strange yet very annoying reason, this post could not be edited.')); 1853 1854 $this->attach_uploads( $ID, $post_content ); 1855 1856 return true; 1857 } 1858 1859 /** 1860 * Remove a post. 1861 * 1862 * @since 1.5.0 1863 * 1864 * @param array $args Method parameters. 1865 * @return bool True when post is deleted. 1866 */ 1867 function blogger_deletePost($args) { 1868 $this->escape($args); 1869 1870 $post_ID = (int) $args[1]; 1871 $username = $args[2]; 1872 $password = $args[3]; 1873 $publish = $args[4]; 1874 1875 if ( !$user = $this->login($username, $password) ) 1876 return $this->error; 1877 1878 do_action('xmlrpc_call', 'blogger.deletePost'); 1879 1880 $actual_post = wp_get_single_post($post_ID,ARRAY_A); 1881 1882 if ( !$actual_post || $actual_post['post_type'] != 'post' ) 1883 return new IXR_Error(404, __('Sorry, no such post.')); 1884 1885 if ( !current_user_can('edit_post', $post_ID) ) 1886 return new IXR_Error(401, __('Sorry, you do not have the right to delete this post.')); 1887 1888 $result = wp_delete_post($post_ID); 1889 1890 if ( !$result ) 1891 return new IXR_Error(500, __('For some strange yet very annoying reason, this post could not be deleted.')); 1892 1893 return true; 1894 } 1895 1896 /* MetaWeblog API functions 1897 * specs on wherever Dave Winer wants them to be 1898 */ 1899 1900 /** 1901 * Create a new post. 1902 * 1903 * @since 1.5.0 1904 * 1905 * @param array $args Method parameters. 1906 * @return int 1907 */ 1908 function mw_newPost($args) { 1909 $this->escape($args); 1910 1911 $blog_ID = (int) $args[0]; // we will support this in the near future 1912 $username = $args[1]; 1913 $password = $args[2]; 1914 $content_struct = $args[3]; 1915 $publish = $args[4]; 1916 1917 if ( !$user = $this->login($username, $password) ) 1918 return $this->error; 1919 1920 do_action('xmlrpc_call', 'metaWeblog.newPost'); 1921 1922 $cap = ( $publish ) ? 'publish_posts' : 'edit_posts'; 1923 $error_message = __( 'Sorry, you are not allowed to publish posts on this site.' ); 1924 $post_type = 'post'; 1925 $page_template = ''; 1926 if ( !empty( $content_struct['post_type'] ) ) { 1927 if ( $content_struct['post_type'] == 'page' ) { 1928 $cap = ( $publish ) ? 'publish_pages' : 'edit_pages'; 1929 $error_message = __( 'Sorry, you are not allowed to publish pages on this site.' ); 1930 $post_type = 'page'; 1931 if ( !empty( $content_struct['wp_page_template'] ) ) 1932 $page_template = $content_struct['wp_page_template']; 1933 } elseif ( $content_struct['post_type'] == 'post' ) { 1934 // This is the default, no changes needed 1935 } else { 1936 // No other post_type values are allowed here 1937 return new IXR_Error( 401, __( 'Invalid post type.' ) ); 1938 } 1939 } 1940 1941 if ( !current_user_can( $cap ) ) 1942 return new IXR_Error( 401, $error_message ); 1943 1944 // Let WordPress generate the post_name (slug) unless 1945 // one has been provided. 1946 $post_name = ""; 1947 if ( isset($content_struct["wp_slug"]) ) 1948 $post_name = $content_struct["wp_slug"]; 1949 1950 // Only use a password if one was given. 1951 if ( isset($content_struct["wp_password"]) ) 1952 $post_password = $content_struct["wp_password"]; 1953 1954 // Only set a post parent if one was provided. 1955 if ( isset($content_struct["wp_page_parent_id"]) ) 1956 $post_parent = $content_struct["wp_page_parent_id"]; 1957 1958 // Only set the menu_order if it was provided. 1959 if ( isset($content_struct["wp_page_order"]) ) 1960 $menu_order = $content_struct["wp_page_order"]; 1961 1962 $post_author = $user->ID; 1963 1964 // If an author id was provided then use it instead. 1965 if ( isset($content_struct["wp_author_id"]) && ($user->ID != $content_struct["wp_author_id"]) ) { 1966 switch ( $post_type ) { 1967 case "post": 1968 if ( !current_user_can("edit_others_posts") ) 1969 return(new IXR_Error(401, __("You are not allowed to post as this user"))); 1970 break; 1971 case "page": 1972 if ( !current_user_can("edit_others_pages") ) 1973 return(new IXR_Error(401, __("You are not allowed to create pages as this user"))); 1974 break; 1975 default: 1976 return(new IXR_Error(401, __("Invalid post type."))); 1977 break; 1978 } 1979 $post_author = $content_struct["wp_author_id"]; 1980 } 1981 1982 $post_title = $content_struct['title']; 1983 $post_content = $content_struct['description']; 1984 1985 $post_status = $publish ? 'publish' : 'draft'; 1986 1987 if ( isset( $content_struct["{$post_type}_status"] ) ) { 1988 switch ( $content_struct["{$post_type}_status"] ) { 1989 case 'draft': 1990 case 'private': 1991 case 'publish': 1992 $post_status = $content_struct["{$post_type}_status"]; 1993 break; 1994 case 'pending': 1995 // Pending is only valid for posts, not pages. 1996 if ( $post_type === 'post' ) 1997 $post_status = $content_struct["{$post_type}_status"]; 1998 break; 1999 default: 2000 $post_status = $publish ? 'publish' : 'draft'; 2001 break; 2002 } 2003 } 2004 2005 $post_excerpt = $content_struct['mt_excerpt']; 2006 $post_more = $content_struct['mt_text_more']; 2007 2008 $tags_input = $content_struct['mt_keywords']; 2009 2010 if ( isset($content_struct["mt_allow_comments"]) ) { 2011 if ( !is_numeric($content_struct["mt_allow_comments"]) ) { 2012 switch ( $content_struct["mt_allow_comments"] ) { 2013 case "closed": 2014 $comment_status = "closed"; 2015 break; 2016 case "open": 2017 $comment_status = "open"; 2018 break; 2019 default: 2020 $comment_status = get_option("default_comment_status"); 2021 break; 2022 } 2023 } else { 2024 switch ( (int) $content_struct["mt_allow_comments"] ) { 2025 case 0: 2026 case 2: 2027 $comment_status = "closed"; 2028 break; 2029 case 1: 2030 $comment_status = "open"; 2031 break; 2032 default: 2033 $comment_status = get_option("default_comment_status"); 2034 break; 2035 } 2036 } 2037 } else { 2038 $comment_status = get_option("default_comment_status"); 2039 } 2040 2041 if ( isset($content_struct["mt_allow_pings"]) ) { 2042 if ( !is_numeric($content_struct["mt_allow_pings"]) ) { 2043 switch ( $content_struct['mt_allow_pings'] ) { 2044 case "closed": 2045 $ping_status = "closed"; 2046 break; 2047 case "open": 2048 $ping_status = "open"; 2049 break; 2050 default: 2051 $ping_status = get_option("default_ping_status"); 2052 break; 2053 } 2054 } else { 2055 switch ( (int) $content_struct["mt_allow_pings"] ) { 2056 case 0: 2057 $ping_status = "closed"; 2058 break; 2059 case 1: 2060 $ping_status = "open"; 2061 break; 2062 default: 2063 $ping_status = get_option("default_ping_status"); 2064 break; 2065 } 2066 } 2067 } else { 2068 $ping_status = get_option("default_ping_status"); 2069 } 2070 2071 if ( $post_more ) 2072 $post_content = $post_content . "<!--more-->" . $post_more; 2073 2074 $to_ping = $content_struct['mt_tb_ping_urls']; 2075 if ( is_array($to_ping) ) 2076 $to_ping = implode(' ', $to_ping); 2077 2078 // Do some timestamp voodoo 2079 if ( !empty( $content_struct['date_created_gmt'] ) ) 2080 $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 2081 elseif ( !empty( $content_struct['dateCreated']) ) 2082 $dateCreated = $content_struct['dateCreated']->getIso(); 2083 2084 if ( !empty( $dateCreated ) ) { 2085 $post_date = get_date_from_gmt(iso8601_to_datetime($dateCreated)); 2086 $post_date_gmt = iso8601_to_datetime($dateCreated, GMT); 2087 } else { 2088 $post_date = current_time('mysql'); 2089 $post_date_gmt = current_time('mysql', 1); 2090 } 2091 2092 $catnames = $content_struct['categories']; 2093 logIO('O', 'Post cats: ' . var_export($catnames,true)); 2094 $post_category = array(); 2095 2096 if ( is_array($catnames) ) { 2097 foreach ($catnames as $cat) { 2098 $post_category[] = get_cat_ID($cat); 2099 } 2100 } 2101 2102 // We've got all the data -- post it: 2103 $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'); 2104 2105 $post_ID = wp_insert_post($postdata, true); 2106 if ( is_wp_error( $post_ID ) ) 2107 return new IXR_Error(500, $post_ID->get_error_message()); 2108 2109 if ( !$post_ID ) 2110 return new IXR_Error(500, __('Sorry, your entry could not be posted. Something wrong happened.')); 2111 2112 // Only posts can be sticky 2113 if ( $post_type == 'post' && isset( $content_struct['sticky'] ) ) { 2114 if ( $content_struct['sticky'] == true ) 2115 stick_post( $post_ID ); 2116 elseif ( $content_struct['sticky'] == false ) 2117 unstick_post( $post_ID ); 2118 } 2119 2120 if ( isset($content_struct['custom_fields']) ) 2121 $this->set_custom_fields($post_ID, $content_struct['custom_fields']); 2122 2123 // Handle enclosures 2124 $this->add_enclosure_if_new($post_ID, $content_struct['enclosure']); 2125 2126 $this->attach_uploads( $post_ID, $post_content ); 2127 2128 logIO('O', "Posted ! ID: $post_ID"); 2129 2130 return strval($post_ID); 2131 } 2132 2133 function add_enclosure_if_new($post_ID, $enclosure) { 2134 if ( is_array( $enclosure ) && isset( $enclosure['url'] ) && isset( $enclosure['length'] ) && isset( $enclosure['type'] ) ) { 2135 2136 $encstring = $enclosure['url'] . "\n" . $enclosure['length'] . "\n" . $enclosure['type']; 2137 $found = false; 2138 foreach ( (array) get_post_custom($post_ID) as $key => $val) { 2139 if ($key == 'enclosure') { 2140 foreach ( (array) $val as $enc ) { 2141 if ($enc == $encstring) { 2142 $found = true; 2143 break 2; 2144 } 2145 } 2146 } 2147 } 2148 if (!$found) 2149 add_post_meta( $post_ID, 'enclosure', $encstring ); 2150 } 2151 } 2152 2153 /** 2154 * Attach upload to a post. 2155 * 2156 * @since 2.1.0 2157 * 2158 * @param int $post_ID Post ID. 2159 * @param string $post_content Post Content for attachment. 2160 */ 2161 function attach_uploads( $post_ID, $post_content ) { 2162 global $wpdb; 2163 2164 // find any unattached files 2165 $attachments = $wpdb->get_results( "SELECT ID, guid FROM {$wpdb->posts} WHERE post_parent = '0' AND post_type = 'attachment'" ); 2166 if ( is_array( $attachments ) ) { 2167 foreach ( $attachments as $file ) { 2168 if ( strpos( $post_content, $file->guid ) !== false ) 2169 $wpdb->update($wpdb->posts, array('post_parent' => $post_ID), array('ID' => $file->ID) ); 2170 } 2171 } 2172 } 2173 2174 /** 2175 * Edit a post. 2176 * 2177 * @since 1.5.0 2178 * 2179 * @param array $args Method parameters. 2180 * @return bool True on success. 2181 */ 2182 function mw_editPost($args) { 2183 2184 $this->escape($args); 2185 2186 $post_ID = (int) $args[0]; 2187 $username = $args[1]; 2188 $password = $args[2]; 2189 $content_struct = $args[3]; 2190 $publish = $args[4]; 2191 2192 if ( !$user = $this->login($username, $password) ) 2193 return $this->error; 2194 2195 do_action('xmlrpc_call', 'metaWeblog.editPost'); 2196 2197 $cap = ( $publish ) ? 'publish_posts' : 'edit_posts'; 2198 $error_message = __( 'Sorry, you are not allowed to publish posts on this site.' ); 2199 $post_type = 'post'; 2200 $page_template = ''; 2201 if ( !empty( $content_struct['post_type'] ) ) { 2202 if ( $content_struct['post_type'] == 'page' ) { 2203 $cap = ( $publish ) ? 'publish_pages' : 'edit_pages'; 2204 $error_message = __( 'Sorry, you are not allowed to publish pages on this site.' ); 2205 $post_type = 'page'; 2206 if ( !empty( $content_struct['wp_page_template'] ) ) 2207 $page_template = $content_struct['wp_page_template']; 2208 } elseif ( $content_struct['post_type'] == 'post' ) { 2209 // This is the default, no changes needed 2210 } else { 2211 // No other post_type values are allowed here 2212 return new IXR_Error( 401, __( 'Invalid post type.' ) ); 2213 } 2214 } 2215 2216 if ( !current_user_can( $cap ) ) 2217 return new IXR_Error( 401, $error_message ); 2218 2219 $postdata = wp_get_single_post($post_ID, ARRAY_A); 2220 2221 // If there is no post data for the give post id, stop 2222 // now and return an error. Other wise a new post will be 2223 // created (which was the old behavior). 2224 if ( empty($postdata["ID"]) ) 2225 return(new IXR_Error(404, __("Invalid post ID."))); 2226 2227 $this->escape($postdata); 2228 extract($postdata, EXTR_SKIP); 2229 2230 // Let WordPress manage slug if none was provided. 2231 $post_name = ""; 2232 if ( isset($content_struct["wp_slug"]) ) 2233 $post_name = $content_struct["wp_slug"]; 2234 2235 // Only use a password if one was given. 2236 if ( isset($content_struct["wp_password"]) ) 2237 $post_password = $content_struct["wp_password"]; 2238 2239 // Only set a post parent if one was given. 2240 if ( isset($content_struct["wp_page_parent_id"]) ) 2241 $post_parent = $content_struct["wp_page_parent_id"]; 2242 2243 // Only set the menu_order if it was given. 2244 if ( isset($content_struct["wp_page_order"]) ) 2245 $menu_order = $content_struct["wp_page_order"]; 2246 2247 $post_author = $postdata["post_author"]; 2248 2249 // Only set the post_author if one is set. 2250 if ( isset($content_struct["wp_author_id"]) && ($user->ID != $content_struct["wp_author_id"]) ) { 2251 switch ( $post_type ) { 2252 case "post": 2253 if ( !current_user_can("edit_others_posts") ) 2254 return(new IXR_Error(401, __("You are not allowed to change the post author as this user."))); 2255 break; 2256 case "page": 2257 if ( !current_user_can("edit_others_pages") ) 2258 return(new IXR_Error(401, __("You are not allowed to change the page author as this user."))); 2259 break; 2260 default: 2261 return(new IXR_Error(401, __("Invalid post type."))); 2262 break; 2263 } 2264 $post_author = $content_struct["wp_author_id"]; 2265 } 2266 2267 if ( isset($content_struct["mt_allow_comments"]) ) { 2268 if ( !is_numeric($content_struct["mt_allow_comments"]) ) { 2269 switch ( $content_struct["mt_allow_comments"] ) { 2270 case "closed": 2271 $comment_status = "closed"; 2272 break; 2273 case "open": 2274 $comment_status = "open"; 2275 break; 2276 default: 2277 $comment_status = get_option("default_comment_status"); 2278 break; 2279 } 2280 } else { 2281 switch ( (int) $content_struct["mt_allow_comments"] ) { 2282 case 0: 2283 case 2: 2284 $comment_status = "closed"; 2285 break; 2286 case 1: 2287 $comment_status = "open"; 2288 break; 2289 default: 2290 $comment_status = get_option("default_comment_status"); 2291 break; 2292 } 2293 } 2294 } 2295 2296 if ( isset($content_struct["mt_allow_pings"]) ) { 2297 if ( !is_numeric($content_struct["mt_allow_pings"]) ) { 2298 switch ( $content_struct["mt_allow_pings"] ) { 2299 case "closed": 2300 $ping_status = "closed"; 2301 break; 2302 case "open": 2303 $ping_status = "open"; 2304 break; 2305 default: 2306 $ping_status = get_option("default_ping_status"); 2307 break; 2308 } 2309 } else { 2310 switch ( (int) $content_struct["mt_allow_pings"] ) { 2311 case 0: 2312 $ping_status = "closed"; 2313 break; 2314 case 1: 2315 $ping_status = "open"; 2316 break; 2317 default: 2318 $ping_status = get_option("default_ping_status"); 2319 break; 2320 } 2321 } 2322 } 2323 2324 $post_title = $content_struct['title']; 2325 $post_content = $content_struct['description']; 2326 $catnames = $content_struct['categories']; 2327 2328 $post_category = array(); 2329 2330 if ( is_array($catnames) ) { 2331 foreach ($catnames as $cat) { 2332 $post_category[] = get_cat_ID($cat); 2333 } 2334 } 2335 2336 $post_excerpt = $content_struct['mt_excerpt']; 2337 $post_more = $content_struct['mt_text_more']; 2338 2339 $post_status = $publish ? 'publish' : 'draft'; 2340 if ( isset( $content_struct["{$post_type}_status"] ) ) { 2341 switch( $content_struct["{$post_type}_status"] ) { 2342 case 'draft': 2343 case 'private': 2344 case 'publish': 2345 $post_status = $content_struct["{$post_type}_status"]; 2346 break; 2347 case 'pending': 2348 // Pending is only valid for posts, not pages. 2349 if ( $post_type === 'post' ) 2350 $post_status = $content_struct["{$post_type}_status"]; 2351 break; 2352 default: 2353 $post_status = $publish ? 'publish' : 'draft'; 2354 break; 2355 } 2356 } 2357 2358 $tags_input = $content_struct['mt_keywords']; 2359 2360 if ( ('publish' == $post_status) ) { 2361 if ( ( 'page' == $post_type ) && !current_user_can('publish_pages') ) 2362 return new IXR_Error(401, __('Sorry, you do not have the right to publish this page.')); 2363 else if ( !current_user_can('publish_posts') ) 2364 return new IXR_Error(401, __('Sorry, you do not have the right to publish this post.')); 2365 } 2366 2367 if ( $post_more ) 2368 $post_content = $post_content . "<!--more-->" . $post_more; 2369 2370 $to_ping = $content_struct['mt_tb_ping_urls']; 2371 if ( is_array($to_ping) ) 2372 $to_ping = implode(' ', $to_ping); 2373 2374 // Do some timestamp voodoo 2375 if ( !empty( $content_struct['date_created_gmt'] ) ) 2376 $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 2377 elseif ( !empty( $content_struct['dateCreated']) ) 2378 $dateCreated = $content_struct['dateCreated']->getIso(); 2379 2380 if ( !empty( $dateCreated ) ) { 2381 $post_date = get_date_from_gmt(iso8601_to_datetime($dateCreated)); 2382 $post_date_gmt = iso8601_to_datetime($dateCreated, GMT); 2383 } else { 2384 $post_date = $postdata['post_date']; 2385 $post_date_gmt = $postdata['post_date_gmt']; 2386 } 2387 2388 // We've got all the data -- post it: 2389 $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'); 2390 2391 $result = wp_update_post($newpost, true); 2392 if ( is_wp_error( $result ) ) 2393 return new IXR_Error(500, $result->get_error_message()); 2394 2395 if ( !$result ) 2396 return new IXR_Error(500, __('Sorry, your entry could not be edited. Something wrong happened.')); 2397 2398 // Only posts can be sticky 2399 if ( $post_type == 'post' && isset( $content_struct['sticky'] ) ) { 2400 if ( $content_struct['sticky'] == true ) 2401 stick_post( $post_ID ); 2402 elseif ( $content_struct['sticky'] == false ) 2403 unstick_post( $post_ID ); 2404 } 2405 2406 if ( isset($content_struct['custom_fields']) ) 2407 $this->set_custom_fields($post_ID, $content_struct['custom_fields']); 2408 2409 // Handle enclosures 2410 $this->add_enclosure_if_new($post_ID, $content_struct['enclosure']); 2411 2412 $this->attach_uploads( $ID, $post_content ); 2413 2414 logIO('O',"(MW) Edited ! ID: $post_ID"); 2415 2416 return true; 2417 } 2418 2419 /** 2420 * Retrieve post. 2421 * 2422 * @since 1.5.0 2423 * 2424 * @param array $args Method parameters. 2425 * @return array 2426 */ 2427 function mw_getPost($args) { 2428 2429 $this->escape($args); 2430 2431 $post_ID = (int) $args[0]; 2432 $username = $args[1]; 2433 $password = $args[2]; 2434 2435 if ( !$user = $this->login($username, $password) ) 2436 return $this->error; 2437 2438 if ( !current_user_can( 'edit_post', $post_ID ) ) 2439 return new IXR_Error( 401, __( 'Sorry, you cannot edit this post.' ) ); 2440 2441 do_action('xmlrpc_call', 'metaWeblog.getPost'); 2442 2443 $postdata = wp_get_single_post($post_ID, ARRAY_A); 2444 2445 if ($postdata['post_date'] != '') { 2446 $post_date = mysql2date('Ymd\TH:i:s', $postdata['post_date'], false); 2447 $post_date_gmt = mysql2date('Ymd\TH:i:s', $postdata['post_date_gmt'], false); 2448 2449 // For drafts use the GMT version of the post date 2450 if ( $postdata['post_status'] == 'draft' ) 2451 $post_date_gmt = get_gmt_from_date( mysql2date( 'Y-m-d H:i:s', $postdata['post_date'] ), 'Ymd\TH:i:s' ); 2452 2453 $categories = array(); 2454 $catids = wp_get_post_categories($post_ID); 2455 foreach($catids as $catid) 2456 $categories[] = get_cat_name($catid); 2457 2458 $tagnames = array(); 2459 $tags = wp_get_post_tags( $post_ID ); 2460 if ( !empty( $tags ) ) { 2461 foreach ( $tags as $tag ) 2462 $tagnames[] = $tag->name; 2463 $tagnames = implode( ', ', $tagnames ); 2464 } else { 2465 $tagnames = ''; 2466 } 2467 2468 $post = get_extended($postdata['post_content']); 2469 $link = post_permalink($postdata['ID']); 2470 2471 // Get the author info. 2472 $author = get_userdata($postdata['post_author']); 2473 2474 $allow_comments = ('open' == $postdata['comment_status']) ? 1 : 0; 2475 $allow_pings = ('open' == $postdata['ping_status']) ? 1 : 0; 2476 2477 // Consider future posts as published 2478 if ( $postdata['post_status'] === 'future' ) 2479 $postdata['post_status'] = 'publish'; 2480 2481 $sticky = false; 2482 if ( is_sticky( $post_ID ) ) 2483 $sticky = true; 2484 2485 $enclosure = array(); 2486 foreach ( (array) get_post_custom($post_ID) as $key => $val) { 2487 if ($key == 'enclosure') { 2488 foreach ( (array) $val as $enc ) { 2489 $encdata = split("\n", $enc); 2490 $enclosure['url'] = trim(htmlspecialchars($encdata[0])); 2491 $enclosure['length'] = (int) trim($encdata[1]); 2492 $enclosure['type'] = trim($encdata[2]); 2493 break 2; 2494 } 2495 } 2496 } 2497 2498 $resp = array( 2499 'dateCreated' => new IXR_Date($post_date), 2500 'userid' => $postdata['post_author'], 2501 'postid' => $postdata['ID'], 2502 'description' => $post['main'], 2503 'title' => $postdata['post_title'], 2504 'link' => $link, 2505 'permaLink' => $link, 2506 // commented out because no other tool seems to use this 2507 // 'content' => $entry['post_content'], 2508 'categories' => $categories, 2509 'mt_excerpt' => $postdata['post_excerpt'], 2510 'mt_text_more' => $post['extended'], 2511 'mt_allow_comments' => $allow_comments, 2512 'mt_allow_pings' => $allow_pings, 2513 'mt_keywords' => $tagnames, 2514 'wp_slug' => $postdata['post_name'], 2515 'wp_password' => $postdata['post_password'], 2516 'wp_author_id' => $author->ID, 2517 'wp_author_display_name' => $author->display_name, 2518 'date_created_gmt' => new IXR_Date($post_date_gmt), 2519 'post_status' => $postdata['post_status'], 2520 'custom_fields' => $this->get_custom_fields($post_ID), 2521 'sticky' => $sticky 2522 ); 2523 2524 if ( !empty($enclosure) ) $resp['enclosure'] = $enclosure; 2525 2526 return $resp; 2527 } else { 2528 return new IXR_Error(404, __('Sorry, no such post.')); 2529 } 2530 } 2531 2532 /** 2533 * Retrieve list of recent posts. 2534 * 2535 * @since 1.5.0 2536 * 2537 * @param array $args Method parameters. 2538 * @return array 2539 */ 2540 function mw_getRecentPosts($args) { 2541 2542 $this->escape($args); 2543 2544 $blog_ID = (int) $args[0]; 2545 $username = $args[1]; 2546 $password = $args[2]; 2547 $num_posts = (int) $args[3]; 2548 2549 if ( !$user = $this->login($username, $password) ) 2550 return $this->error; 2551 2552 do_action('xmlrpc_call', 'metaWeblog.getRecentPosts'); 2553 2554 $posts_list = wp_get_recent_posts($num_posts); 2555 2556 if ( !$posts_list ) 2557 return array( ); 2558 2559 foreach ($posts_list as $entry) { 2560 if ( !current_user_can( 'edit_post', $entry['ID'] ) ) 2561 continue; 2562 2563 $post_date = mysql2date('Ymd\TH:i:s', $entry['post_date'], false); 2564 $post_date_gmt = mysql2date('Ymd\TH:i:s', $entry['post_date_gmt'], false); 2565 2566 // For drafts use the GMT version of the date 2567 if ( $entry['post_status'] == 'draft' ) 2568 $post_date_gmt = get_gmt_from_date( mysql2date( 'Y-m-d H:i:s', $entry['post_date'] ), 'Ymd\TH:i:s' ); 2569 2570 $categories = array(); 2571 $catids = wp_get_post_categories($entry['ID']); 2572 foreach( $catids as $catid ) 2573 $categories[] = get_cat_name($catid); 2574 2575 $tagnames = array(); 2576 $tags = wp_get_post_tags( $entry['ID'] ); 2577 if ( !empty( $tags ) ) { 2578 foreach ( $tags as $tag ) { 2579 $tagnames[] = $tag->name; 2580 } 2581 $tagnames = implode( ', ', $tagnames ); 2582 } else { 2583 $tagnames = ''; 2584 } 2585 2586 $post = get_extended($entry['post_content']); 2587 $link = post_permalink($entry['ID']); 2588 2589 // Get the post author info. 2590 $author = get_userdata($entry['post_author']); 2591 2592 $allow_comments = ('open' == $entry['comment_status']) ? 1 : 0; 2593 $allow_pings = ('open' == $entry['ping_status']) ? 1 : 0; 2594 2595 // Consider future posts as published 2596 if ( $entry['post_status'] === 'future' ) 2597 $entry['post_status'] = 'publish'; 2598 2599 $struct[] = array( 2600 'dateCreated' => new IXR_Date($post_date), 2601 'userid' => $entry['post_author'], 2602 'postid' => $entry['ID'], 2603 'description' => $post['main'], 2604 'title' => $entry['post_title'], 2605 'link' => $link, 2606 'permaLink' => $link, 2607 // commented out because no other tool seems to use this 2608 // 'content' => $entry['post_content'], 2609 'categories' => $categories, 2610 'mt_excerpt' => $entry['post_excerpt'], 2611 'mt_text_more' => $post['extended'], 2612 'mt_allow_comments' => $allow_comments, 2613 'mt_allow_pings' => $allow_pings, 2614 'mt_keywords' => $tagnames, 2615 'wp_slug' => $entry['post_name'], 2616 'wp_password' => $entry['post_password'], 2617 'wp_author_id' => $author->ID, 2618 'wp_author_display_name' => $author->display_name, 2619 'date_created_gmt' => new IXR_Date($post_date_gmt), 2620 'post_status' => $entry['post_status'], 2621 'custom_fields' => $this->get_custom_fields($entry['ID']) 2622 ); 2623 2624 } 2625 2626 $recent_posts = array(); 2627 for ( $j=0; $j<count($struct); $j++ ) { 2628 array_push($recent_posts, $struct[$j]); 2629 } 2630 2631 return $recent_posts; 2632 } 2633 2634 /** 2635 * Retrieve the list of categories on a given blog. 2636 * 2637 * @since 1.5.0 2638 * 2639 * @param array $args Method parameters. 2640 * @return array 2641 */ 2642 function mw_getCategories($args) { 2643 2644 $this->escape($args); 2645 2646 $blog_ID = (int) $args[0]; 2647 $username = $args[1]; 2648 $password = $args[2]; 2649 2650 if ( !$user = $this->login($username, $password) ) 2651 return $this->error; 2652 2653 if ( !current_user_can( 'edit_posts' ) ) 2654 return new IXR_Error( 401, __( 'Sorry, you must be able to edit posts on this site in order to view categories.' ) ); 2655 2656 do_action('xmlrpc_call', 'metaWeblog.getCategories'); 2657 2658 $categories_struct = array(); 2659 2660 if ( $cats = get_categories(array('get' => 'all')) ) { 2661 foreach ( $cats as $cat ) { 2662 $struct['categoryId'] = $cat->term_id; 2663 $struct['parentId'] = $cat->parent; 2664 $struct['description'] = $cat->name; 2665 $struct['categoryDescription'] = $cat->description; 2666 $struct['categoryName'] = $cat->name; 2667 $struct['htmlUrl'] = esc_html(get_category_link($cat->term_id)); 2668 $struct['rssUrl'] = esc_html(get_category_feed_link($cat->term_id, 'rss2')); 2669 2670 $categories_struct[] = $struct; 2671 } 2672 } 2673 2674 return $categories_struct; 2675 } 2676 2677 /** 2678 * Uploads a file, following your settings. 2679 * 2680 * Adapted from a patch by Johann Richard. 2681 * 2682 * @link http://mycvs.org/archives/2004/06/30/file-upload-to-wordpress-in-ecto/ 2683 * 2684 * @since 1.5.0 2685 * 2686 * @param array $args Method parameters. 2687 * @return array 2688 */ 2689 function mw_newMediaObject($args) { 2690 global $wpdb; 2691 2692 $blog_ID = (int) $args[0]; 2693 $username = $wpdb->escape($args[1]); 2694 $password = $wpdb->escape($args[2]); 2695 $data = $args[3]; 2696 2697 $name = sanitize_file_name( $data['name'] ); 2698 $type = $data['type']; 2699 $bits = $data['bits']; 2700 2701 logIO('O', '(MW) Received '.strlen($bits).' bytes'); 2702 2703 if ( !$user = $this->login($username, $password) ) 2704 return $this->error; 2705 2706 do_action('xmlrpc_call', 'metaWeblog.newMediaObject'); 2707 2708 if ( !current_user_can('upload_files') ) { 2709 logIO('O', '(MW) User does not have upload_files capability'); 2710 $this->error = new IXR_Error(401, __('You are not allowed to upload files to this site.')); 2711 return $this->error; 2712 } 2713 2714 if ( $upload_err = apply_filters( "pre_upload_error", false ) ) 2715 return new IXR_Error(500, $upload_err); 2716 2717 if ( !empty($data["overwrite"]) && ($data["overwrite"] == true) ) { 2718 // Get postmeta info on the object. 2719 $old_file = $wpdb->get_row(" 2720 SELECT ID 2721 FROM {$wpdb->posts} 2722 WHERE post_title = '{$name}' 2723 AND post_type = 'attachment' 2724 "); 2725 2726 // Delete previous file. 2727 wp_delete_attachment($old_file->ID); 2728 2729 // Make sure the new name is different by pre-pending the 2730 // previous post id. 2731 $filename = preg_replace("/^wpid\d+-/", "", $name); 2732 $name = "wpid{$old_file->ID}-{$filename}"; 2733 } 2734 2735 $upload = wp_upload_bits($name, $type, $bits); 2736 if ( ! empty($upload['error']) ) { 2737 $errorString = sprintf(__('Could not write file %1$s (%2$s)'), $name, $upload['error']); 2738 logIO('O', '(MW) ' . $errorString); 2739 return new IXR_Error(500, $errorString); 2740 } 2741 // Construct the attachment array 2742 // attach to post_id 0 2743 $post_id = 0; 2744 $attachment = array( 2745 'post_title' => $name, 2746 'post_content' => '', 2747 'post_type' => 'attachment', 2748 'post_parent' => $post_id, 2749 'post_mime_type' => $type, 2750 'guid' => $upload[ 'url' ] 2751 ); 2752 2753 // Save the data 2754 $id = wp_insert_attachment( $attachment, $upload[ 'file' ], $post_id ); 2755 wp_update_attachment_metadata( $id, wp_generate_attachment_metadata( $id, $upload['file'] ) ); 2756 2757 return apply_filters( 'wp_handle_upload', array( 'file' => $name, 'url' => $upload[ 'url' ], 'type' => $type ), 'upload' ); 2758 } 2759 2760 /* MovableType API functions 2761 * specs on http://www.movabletype.org/docs/mtmanual_programmatic.html 2762 */ 2763 2764 /** 2765 * Retrieve the post titles of recent posts. 2766 * 2767 * @since 1.5.0 2768 * 2769 * @param array $args Method parameters. 2770 * @return array 2771 */ 2772 function mt_getRecentPostTitles($args) { 2773 2774 $this->escape($args); 2775 2776 $blog_ID = (int) $args[0]; 2777 $username = $args[1]; 2778 $password = $args[2]; 2779 $num_posts = (int) $args[3]; 2780 2781 if ( !$user = $this->login($username, $password) ) 2782 return $this->error; 2783 2784 do_action('xmlrpc_call', 'mt.getRecentPostTitles'); 2785 2786 $posts_list = wp_get_recent_posts($num_posts); 2787 2788 if ( !$posts_list ) { 2789 $this->error = new IXR_Error(500, __('Either there are no posts, or something went wrong.')); 2790 return $this->error; 2791 } 2792 2793 foreach ($posts_list as $entry) { 2794 if ( !current_user_can( 'edit_post', $entry['ID'] ) ) 2795 continue; 2796 2797 $post_date = mysql2date('Ymd\TH:i:s', $entry['post_date'], false); 2798 $post_date_gmt = mysql2date('Ymd\TH:i:s', $entry['post_date_gmt'], false); 2799 2800 // For drafts use the GMT version of the date 2801 if ( $entry['post_status'] == 'draft' ) 2802 $post_date_gmt = get_gmt_from_date( mysql2date( 'Y-m-d H:i:s', $entry['post_date'] ), 'Ymd\TH:i:s' ); 2803 2804 $struct[] = array( 2805 'dateCreated' => new IXR_Date($post_date), 2806 'userid' => $entry['post_author'], 2807 'postid' => $entry['ID'], 2808 'title' => $entry['post_title'], 2809 'date_created_gmt' => new IXR_Date($post_date_gmt) 2810 ); 2811 2812 } 2813 2814 $recent_posts = array(); 2815 for ( $j=0; $j<count($struct); $j++ ) { 2816 array_push($recent_posts, $struct[$j]); 2817 } 2818 2819 return $recent_posts; 2820 } 2821 2822 /** 2823 * Retrieve list of all categories on blog. 2824 * 2825 * @since 1.5.0 2826 * 2827 * @param array $args Method parameters. 2828 * @return array 2829 */ 2830 function mt_getCategoryList($args) { 2831 2832 $this->escape($args); 2833 2834 $blog_ID = (int) $args[0]; 2835 $username = $args[1]; 2836 $password = $args[2]; 2837 2838 if ( !$user = $this->login($username, $password) ) 2839 return $this->error; 2840 2841 if ( !current_user_can( 'edit_posts' ) ) 2842 return new IXR_Error( 401, __( 'Sorry, you must be able to edit posts on this site in order to view categories.' ) ); 2843 2844 do_action('xmlrpc_call', 'mt.getCategoryList'); 2845 2846 $categories_struct = array(); 2847 2848 if ( $cats = get_categories(array('hide_empty' => 0, 'hierarchical' => 0)) ) { 2849 foreach ( $cats as $cat ) { 2850 $struct['categoryId'] = $cat->term_id; 2851 $struct['categoryName'] = $cat->name; 2852 2853 $categories_struct[] = $struct; 2854 } 2855 } 2856 2857 return $categories_struct; 2858 } 2859 2860 /** 2861 * Retrieve post categories. 2862 * 2863 * @since 1.5.0 2864 * 2865 * @param array $args Method parameters. 2866 * @return array 2867 */ 2868 function mt_getPostCategories($args) { 2869 2870 $this->escape($args); 2871 2872 $post_ID = (int) $args[0]; 2873 $username = $args[1]; 2874 $password = $args[2]; 2875 2876 if ( !$user = $this->login($username, $password) ) 2877 return $this->error; 2878 2879 if ( !current_user_can( 'edit_post', $post_ID ) ) 2880 return new IXR_Error( 401, __( 'Sorry, you can not edit this post.' ) ); 2881 2882 do_action('xmlrpc_call', 'mt.getPostCategories'); 2883 2884 $categories = array(); 2885 $catids = wp_get_post_categories(intval($post_ID)); 2886 // first listed category will be the primary category 2887 $isPrimary = true; 2888 foreach ( $catids as $catid ) { 2889 $categories[] = array( 2890 'categoryName' => get_cat_name($catid), 2891 'categoryId' => (string) $catid, 2892 'isPrimary' => $isPrimary 2893 ); 2894 $isPrimary = false; 2895 } 2896 2897 return $categories; 2898 } 2899 2900 /** 2901 * Sets categories for a post. 2902 * 2903 * @since 1.5.0 2904 * 2905 * @param array $args Method parameters. 2906 * @return bool True on success. 2907 */ 2908 function mt_setPostCategories($args) { 2909 2910 $this->escape($args); 2911 2912 $post_ID = (int) $args[0]; 2913 $username = $args[1]; 2914 $password = $args[2]; 2915 $categories = $args[3]; 2916 2917 if ( !$user = $this->login($username, $password) ) 2918 return $this->error; 2919 2920 do_action('xmlrpc_call', 'mt.setPostCategories'); 2921 2922 if ( !current_user_can('edit_post', $post_ID) ) 2923 return new IXR_Error(401, __('Sorry, you cannot edit this post.')); 2924 2925 foreach ( $categories as $cat ) { 2926 $catids[] = $cat['categoryId']; 2927 } 2928 2929 wp_set_post_categories($post_ID, $catids); 2930 2931 return true; 2932 } 2933 2934 /** 2935 * Retrieve an array of methods supported by this server. 2936 * 2937 * @since 1.5.0 2938 * 2939 * @param array $args Method parameters. 2940 * @return array 2941 */ 2942 function mt_supportedMethods($args) { 2943 2944 do_action('xmlrpc_call', 'mt.supportedMethods'); 2945 2946 $supported_methods = array(); 2947 foreach ( $this->methods as $key => $value ) { 2948 $supported_methods[] = $key; 2949 } 2950 2951 return $supported_methods; 2952 } 2953 2954 /** 2955 * Retrieve an empty array because we don't support per-post text filters. 2956 * 2957 * @since 1.5.0 2958 * 2959 * @param array $args Method parameters. 2960 */ 2961 function mt_supportedTextFilters($args) { 2962 do_action('xmlrpc_call', 'mt.supportedTextFilters'); 2963 return apply_filters('xmlrpc_text_filters', array()); 2964 } 2965 2966 /** 2967 * Retrieve trackbacks sent to a given post. 2968 * 2969 * @since 1.5.0 2970 * 2971 * @param array $args Method parameters. 2972 * @return mixed 2973 */ 2974 function mt_getTrackbackPings($args) { 2975 2976 global $wpdb; 2977 2978 $post_ID = intval($args); 2979 2980 do_action('xmlrpc_call', 'mt.getTrackbackPings'); 2981 2982 $actual_post = wp_get_single_post($post_ID, ARRAY_A); 2983 2984 if ( !$actual_post ) 2985 return new IXR_Error(404, __('Sorry, no such post.')); 2986 2987 $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) ); 2988 2989 if ( !$comments ) 2990 return array(); 2991 2992 $trackback_pings = array(); 2993 foreach ( $comments as $comment ) { 2994 if ( 'trackback' == $comment->comment_type ) { 2995 $content = $comment->comment_content; 2996 $title = substr($content, 8, (strpos($content, '</strong>') - 8)); 2997 $trackback_pings[] = array( 2998 'pingTitle' => $title, 2999 'pingURL' => $comment->comment_author_url, 3000 'pingIP' => $comment->comment_author_IP 3001 ); 3002 } 3003 } 3004 3005 return $trackback_pings; 3006 } 3007 3008 /** 3009 * Sets a post's publish status to 'publish'. 3010 * 3011 * @since 1.5.0 3012 * 3013 * @param array $args Method parameters. 3014 * @return int 3015 */ 3016 function mt_publishPost($args) { 3017 3018 $this->escape($args); 3019 3020 $post_ID = (int) $args[0]; 3021 $username = $args[1]; 3022 $password = $args[2]; 3023 3024 if ( !$user = $this->login($username, $password) ) 3025 return $this->error; 3026 3027 do_action('xmlrpc_call', 'mt.publishPost'); 3028 3029 if ( !current_user_can('edit_post', $post_ID) ) 3030 return new IXR_Error(401, __('Sorry, you cannot edit this post.')); 3031 3032 $postdata = wp_get_single_post($post_ID,ARRAY_A); 3033 3034 $postdata['post_status'] = 'publish'; 3035 3036 // retain old cats 3037 $cats = wp_get_post_categories($post_ID); 3038 $postdata['post_category'] = $cats; 3039 $this->escape($postdata); 3040 3041 $result = wp_update_post($postdata); 3042 3043 return $result; 3044 } 3045 3046 /* PingBack functions 3047 * specs on www.hixie.ch/specs/pingback/pingback 3048 */ 3049 3050 /** 3051 * Retrieves a pingback and registers it. 3052 * 3053 * @since 1.5.0 3054 * 3055 * @param array $args Method parameters. 3056 * @return array 3057 */ 3058 function pingback_ping($args) { 3059 global $wpdb; 3060 3061 do_action('xmlrpc_call', 'pingback.ping'); 3062 3063 $this->escape($args); 3064 3065 $pagelinkedfrom = $args[0]; 3066 $pagelinkedto = $args[1]; 3067 3068 $title = ''; 3069 3070 $pagelinkedfrom = str_replace('&', '&', $pagelinkedfrom); 3071 $pagelinkedto = str_replace('&', '&', $pagelinkedto); 3072 $pagelinkedto = str_replace('&', '&', $pagelinkedto); 3073 3074 // Check if the page linked to is in our site 3075 $pos1 = strpos($pagelinkedto, str_replace(array('http://www.','http://','https://www.','https://'), '', get_option('home'))); 3076 if ( !$pos1 ) 3077 return new IXR_Error(0, __('Is there no link to us?')); 3078 3079 // let's find which post is linked to 3080 // FIXME: does url_to_postid() cover all these cases already? 3081 // if so, then let's use it and drop the old code. 3082 $urltest = parse_url($pagelinkedto); 3083 if ( $post_ID = url_to_postid($pagelinkedto) ) { 3084 $way = 'url_to_postid()'; 3085 } elseif ( preg_match('#p/[0-9]{1,}#', $urltest['path'], $match) ) { 3086 // the path defines the post_ID (archives/p/XXXX) 3087 $blah = explode('/', $match[0]); 3088 $post_ID = (int) $blah[1]; 3089 $way = 'from the path'; 3090 } elseif ( preg_match('#p=[0-9]{1,}#', $urltest['query'], $match) ) { 3091 // the querystring defines the post_ID (?p=XXXX) 3092 $blah = explode('=', $match[0]); 3093 $post_ID = (int) $blah[1]; 3094 $way = 'from the querystring'; 3095 } elseif ( isset($urltest['fragment']) ) { 3096 // an #anchor is there, it's either... 3097 if ( intval($urltest['fragment']) ) { 3098 // ...an integer #XXXX (simpliest case) 3099 $post_ID = (int) $urltest['fragment']; 3100 $way = 'from the fragment (numeric)'; 3101 } elseif ( preg_match('/post-[0-9]+/',$urltest['fragment']) ) { 3102 // ...a post id in the form 'post-###' 3103 $post_ID = preg_replace('/[^0-9]+/', '', $urltest['fragment']); 3104 $way = 'from the fragment (post-###)'; 3105 } elseif ( is_string($urltest['fragment']) ) { 3106 // ...or a string #title, a little more complicated 3107 $title = preg_replace('/[^a-z0-9]/i', '.', $urltest['fragment']); 3108 $sql = $wpdb->prepare("SELECT ID FROM $wpdb->posts WHERE post_title RLIKE %s", $title); 3109 if (! ($post_ID = $wpdb->get_var($sql)) ) { 3110 // returning unknown error '0' is better than die()ing 3111 return new IXR_Error(0, ''); 3112 } 3113 $way = 'from the fragment (title)'; 3114 } 3115 } else { 3116 // TODO: Attempt to extract a post ID from the given URL 3117 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.')); 3118 } 3119 $post_ID = (int) $post_ID; 3120 3121 3122 logIO("O","(PB) URL='$pagelinkedto' ID='$post_ID' Found='$way'"); 3123 3124 $post = get_post($post_ID); 3125 3126 if ( !$post ) // Post_ID not found 3127 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.')); 3128 3129 if ( $post_ID == url_to_postid($pagelinkedfrom) ) 3130 return new IXR_Error(0, __('The source URL and the target URL cannot both point to the same resource.')); 3131 3132 // Check if pings are on 3133 if ( !pings_open($post) ) 3134 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.')); 3135 3136 // Let's check that the remote site didn't already pingback this entry 3137 if ( $wpdb->get_results( $wpdb->prepare("SELECT * FROM $wpdb->comments WHERE comment_post_ID = %d AND comment_author_url = %s", $post_ID, $pagelinkedfrom) ) ) 3138 return new IXR_Error( 48, __( 'The pingback has already been registered.' ) ); 3139 3140 // very stupid, but gives time to the 'from' server to publish ! 3141 sleep(1); 3142 3143 // Let's check the remote site 3144 $linea = wp_remote_fopen( $pagelinkedfrom ); 3145 if ( !$linea ) 3146 return new IXR_Error(16, __('The source URL does not exist.')); 3147 3148 $linea = apply_filters('pre_remote_source', $linea, $pagelinkedto); 3149 3150 // Work around bug in strip_tags(): 3151 $linea = str_replace('<!DOC', '<DOC', $linea); 3152 $linea = preg_replace( '/[\s\r\n\t]+/', ' ', $linea ); // normalize spaces 3153 $linea = preg_replace( "/ <(h1|h2|h3|h4|h5|h6|p|th|td|li|dt|dd|pre|caption|input|textarea|button|body)[^>]*>/", "\n\n", $linea ); 3154 3155 preg_match('|<title>([^<]*?)</title>|is', $linea, $matchtitle); 3156 $title = $matchtitle[1]; 3157 if ( empty( $title ) ) 3158 return new IXR_Error(32, __('We cannot find a title on that page.')); 3159 3160 $linea = strip_tags( $linea, '<a>' ); // just keep the tag we need 3161 3162 $p = explode( "\n\n", $linea ); 3163 3164 $preg_target = preg_quote($pagelinkedto, '|'); 3165 3166 foreach ( $p as $para ) { 3167 if ( strpos($para, $pagelinkedto) !== false ) { // it exists, but is it a link? 3168 preg_match("|<a[^>]+?".$preg_target."[^>]*>([^>]+?)</a>|", $para, $context); 3169 3170 // If the URL isn't in a link context, keep looking 3171 if ( empty($context) ) 3172 continue; 3173 3174 // We're going to use this fake tag to mark the context in a bit 3175 // the marker is needed in case the link text appears more than once in the paragraph 3176 $excerpt = preg_replace('|\</?wpcontext\>|', '', $para); 3177 3178 // prevent really long link text 3179 if ( strlen($context[1]) > 100 ) 3180 $context[1] = substr($context[1], 0, 100) . '...'; 3181 3182 $marker = '<wpcontext>'.$context[1].'</wpcontext>'; // set up our marker 3183 $excerpt= str_replace($context[0], $marker, $excerpt); // swap out the link for our marker 3184 $excerpt = strip_tags($excerpt, '<wpcontext>'); // strip all tags but our context marker 3185 $excerpt = trim($excerpt); 3186 $preg_marker = preg_quote($marker, '|'); 3187 $excerpt = preg_replace("|.*?\s(.{0,100}$preg_marker.{0,100})\s.*|s", '$1', $excerpt); 3188 $excerpt = strip_tags($excerpt); // YES, again, to remove the marker wrapper 3189 break; 3190 } 3191 } 3192 3193 if ( empty($context) ) // Link to target not found 3194 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.')); 3195 3196 $pagelinkedfrom = str_replace('&', '&', $pagelinkedfrom); 3197 3198 $context = '[...] ' . esc_html( $excerpt ) . ' [...]'; 3199 $pagelinkedfrom = $wpdb->escape( $pagelinkedfrom ); 3200 3201 $comment_post_ID = (int) $post_ID; 3202 $comment_author = $title; 3203 $this->escape($comment_author); 3204 $comment_author_url = $pagelinkedfrom; 3205 $comment_content = $context; 3206 $this->escape($comment_content); 3207 $comment_type = 'pingback'; 3208 3209 $commentdata = compact('comment_post_ID', 'comment_author', 'comment_author_url', 'comment_content', 'comment_type'); 3210 3211 $comment_ID = wp_new_comment($commentdata); 3212 do_action('pingback_post', $comment_ID); 3213 3214 return sprintf(__('Pingback from %1$s to %2$s registered. Keep the web talking! :-)'), $pagelinkedfrom, $pagelinkedto); 3215 } 3216 3217 /** 3218 * Retrieve array of URLs that pingbacked the given URL. 3219 * 3220 * Specs on http://www.aquarionics.com/misc/archives/blogite/0198.html 3221 * 3222 * @since 1.5.0 3223 * 3224 * @param array $args Method parameters. 3225 * @return array 3226 */ 3227 function pingback_extensions_getPingbacks($args) { 3228 3229 global $wpdb; 3230 3231 do_action('xmlrpc_call', 'pingback.extensions.getPingbacks'); 3232 3233 $this->escape($args); 3234 3235 $url = $args; 3236 3237 $post_ID = url_to_postid($url); 3238 if ( !$post_ID ) { 3239 // We aren't sure that the resource is available and/or pingback enabled 3240 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.')); 3241 } 3242 3243 $actual_post = wp_get_single_post($post_ID, ARRAY_A); 3244 3245 if ( !$actual_post ) { 3246 // No such post = resource not found 3247 return new IXR_Error(32, __('The specified target URL does not exist.')); 3248 } 3249 3250 $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) ); 3251 3252 if ( !$comments ) 3253 return array(); 3254 3255 $pingbacks = array(); 3256 foreach ( $comments as $comment ) { 3257 if ( 'pingback' == $comment->comment_type ) 3258 $pingbacks[] = $comment->comment_author_url; 3259 } 3260 3261 return $pingbacks; 3262 } 3263 } -
xmlrpc.php
52 52 53 53 include_once(ABSPATH . 'wp-admin/includes/admin.php'); 54 54 include_once(ABSPATH . WPINC . '/class-IXR.php'); 55 include_once(ABSPATH . WPINC . '/class-xmlrpc-server.php'); 55 56 56 57 // Turn off all warnings and errors. 57 58 // error_reporting(0); … … 71 72 */ 72 73 $xmlrpc_logging = 0; 73 74 74 /**75 * logIO() - Writes logging info to a file.76 *77 * @uses $xmlrpc_logging78 * @package WordPress79 * @subpackage Logging80 *81 * @param string $io Whether input or output82 * @param string $msg Information describing logging reason.83 * @return bool Always return true84 */85 function logIO($io,$msg) {86 global $xmlrpc_logging;87 if ($xmlrpc_logging) {88 $fp = fopen("../xmlrpc.log","a+");89 $date = gmdate("Y-m-d H:i:s ");90 $iot = ($io == "I") ? " Input: " : " Output: ";91 fwrite($fp, "\n\n".$date.$iot.$msg);92 fclose($fp);93 }94 return true;95 }96 97 75 if ( isset($HTTP_RAW_POST_DATA) ) 98 76 logIO("I", $HTTP_RAW_POST_DATA); 99 77 100 /**101 * WordPress XMLRPC server implementation.102 *103 * Implements compatability for Blogger API, MetaWeblog API, MovableType, and104 * pingback. Additional WordPress API for managing comments, pages, posts,105 * options, etc.106 *107 * Since WordPress 2.6.0, WordPress XMLRPC server can be disabled in the108 * administration panels.109 *110 * @package WordPress111 * @subpackage Publishing112 * @since 1.5.0113 */114 class wp_xmlrpc_server extends IXR_Server {115 116 /**117 * Register all of the XMLRPC methods that XMLRPC server understands.118 *119 * PHP4 constructor and sets up server and method property. Passes XMLRPC120 * methods through the 'xmlrpc_methods' filter to allow plugins to extend121 * or replace XMLRPC methods.122 *123 * @since 1.5.0124 *125 * @return wp_xmlrpc_server126 */127 function wp_xmlrpc_server() {128 $this->methods = array(129 // WordPress API130 'wp.getUsersBlogs' => 'this:wp_getUsersBlogs',131 'wp.getPage' => 'this:wp_getPage',132 'wp.getPages' => 'this:wp_getPages',133 'wp.newPage' => 'this:wp_newPage',134 'wp.deletePage' => 'this:wp_deletePage',135 'wp.editPage' => 'this:wp_editPage',136 'wp.getPageList' => 'this:wp_getPageList',137 'wp.getAuthors' => 'this:wp_getAuthors',138 'wp.getCategories' => 'this:mw_getCategories', // Alias139 'wp.getTags' => 'this:wp_getTags',140 'wp.newCategory' => 'this:wp_newCategory',141 'wp.deleteCategory' => 'this:wp_deleteCategory',142 'wp.suggestCategories' => 'this:wp_suggestCategories',143 'wp.uploadFile' => 'this:mw_newMediaObject', // Alias144 'wp.getCommentCount' => 'this:wp_getCommentCount',145 'wp.getPostStatusList' => 'this:wp_getPostStatusList',146 'wp.getPageStatusList' => 'this:wp_getPageStatusList',147 'wp.getPageTemplates' => 'this:wp_getPageTemplates',148 'wp.getOptions' => 'this:wp_getOptions',149 'wp.setOptions' => 'this:wp_setOptions',150 'wp.getComment' => 'this:wp_getComment',151 'wp.getComments' => 'this:wp_getComments',152 'wp.deleteComment' => 'this:wp_deleteComment',153 'wp.editComment' => 'this:wp_editComment',154 'wp.newComment' => 'this:wp_newComment',155 'wp.getCommentStatusList' => 'this:wp_getCommentStatusList',156 157 // Blogger API158 'blogger.getUsersBlogs' => 'this:blogger_getUsersBlogs',159 'blogger.getUserInfo' => 'this:blogger_getUserInfo',160 'blogger.getPost' => 'this:blogger_getPost',161 'blogger.getRecentPosts' => 'this:blogger_getRecentPosts',162 'blogger.getTemplate' => 'this:blogger_getTemplate',163 'blogger.setTemplate' => 'this:blogger_setTemplate',164 'blogger.newPost' => 'this:blogger_newPost',165 'blogger.editPost' => 'this:blogger_editPost',166 'blogger.deletePost' => 'this:blogger_deletePost',167 168 // MetaWeblog API (with MT extensions to structs)169 'metaWeblog.newPost' => 'this:mw_newPost',170 'metaWeblog.editPost' => 'this:mw_editPost',171 'metaWeblog.getPost' => 'this:mw_getPost',172 'metaWeblog.getRecentPosts' => 'this:mw_getRecentPosts',173 'metaWeblog.getCategories' => 'this:mw_getCategories',174 'metaWeblog.newMediaObject' => 'this:mw_newMediaObject',175 176 // MetaWeblog API aliases for Blogger API177 // see http://www.xmlrpc.com/stories/storyReader$2460178 'metaWeblog.deletePost' => 'this:blogger_deletePost',179 'metaWeblog.getTemplate' => 'this:blogger_getTemplate',180 'metaWeblog.setTemplate' => 'this:blogger_setTemplate',181 'metaWeblog.getUsersBlogs' => 'this:blogger_getUsersBlogs',182 183 // MovableType API184 'mt.getCategoryList' => 'this:mt_getCategoryList',185 'mt.getRecentPostTitles' => 'this:mt_getRecentPostTitles',186 'mt.getPostCategories' => 'this:mt_getPostCategories',187 'mt.setPostCategories' => 'this:mt_setPostCategories',188 'mt.supportedMethods' => 'this:mt_supportedMethods',189 'mt.supportedTextFilters' => 'this:mt_supportedTextFilters',190 'mt.getTrackbackPings' => 'this:mt_getTrackbackPings',191 'mt.publishPost' => 'this:mt_publishPost',192 193 // PingBack194 'pingback.ping' => 'this:pingback_ping',195 'pingback.extensions.getPingbacks' => 'this:pingback_extensions_getPingbacks',196 197 'demo.sayHello' => 'this:sayHello',198 'demo.addTwoNumbers' => 'this:addTwoNumbers'199 );200 201 $this->initialise_blog_option_info( );202 $this->methods = apply_filters('xmlrpc_methods', $this->methods);203 }204 205 function serve_request() {206 $this->IXR_Server($this->methods);207 }208 209 /**210 * Test XMLRPC API by saying, "Hello!" to client.211 *212 * @since 1.5.0213 *214 * @param array $args Method Parameters.215 * @return string216 */217 function sayHello($args) {218 return 'Hello!';219 }220 221 /**222 * Test XMLRPC API by adding two numbers for client.223 *224 * @since 1.5.0225 *226 * @param array $args Method Parameters.227 * @return int228 */229 function addTwoNumbers($args) {230 $number1 = $args[0];231 $number2 = $args[1];232 return $number1 + $number2;233 }234 235 /**236 * Check user's credentials.237 *238 * @since 1.5.0239 *240 * @param string $user_login User's username.241 * @param string $user_pass User's password.242 * @return bool Whether authentication passed.243 * @deprecated use wp_xmlrpc_server::login244 * @see wp_xmlrpc_server::login245 */246 function login_pass_ok($user_login, $user_pass) {247 if ( !get_option( 'enable_xmlrpc' ) ) {248 $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') ) );249 return false;250 }251 252 if (!user_pass_ok($user_login, $user_pass)) {253 $this->error = new IXR_Error(403, __('Bad login/pass combination.'));254 return false;255 }256 return true;257 }258 259 /**260 * Log user in.261 *262 * @since 2.8263 *264 * @param string $username User's username.265 * @param string $password User's password.266 * @return mixed WP_User object if authentication passed, false otherwise267 */268 function login($username, $password) {269 if ( !get_option( 'enable_xmlrpc' ) ) {270 $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') ) );271 return false;272 }273 274 $user = wp_authenticate($username, $password);275 276 if (is_wp_error($user)) {277 $this->error = new IXR_Error(403, __('Bad login/pass combination.'));278 return false;279 }280 281 wp_set_current_user( $user->ID );282 return $user;283 }284 285 /**286 * Sanitize string or array of strings for database.287 *288 * @since 1.5.2289 *290 * @param string|array $array Sanitize single string or array of strings.291 * @return string|array Type matches $array and sanitized for the database.292 */293 function escape(&$array) {294 global $wpdb;295 296 if (!is_array($array)) {297 return($wpdb->escape($array));298 } else {299 foreach ( (array) $array as $k => $v ) {300 if ( is_array($v) ) {301 $this->escape($array[$k]);302 } else if ( is_object($v) ) {303 //skip304 } else {305 $array[$k] = $wpdb->escape($v);306 }307 }308 }309 }310 311 /**312 * Retrieve custom fields for post.313 *314 * @since 2.5.0315 *316 * @param int $post_id Post ID.317 * @return array Custom fields, if exist.318 */319 function get_custom_fields($post_id) {320 $post_id = (int) $post_id;321 322 $custom_fields = array();323 324 foreach ( (array) has_meta($post_id) as $meta ) {325 // Don't expose protected fields.326 if ( strpos($meta['meta_key'], '_wp_') === 0 ) {327 continue;328 }329 330 $custom_fields[] = array(331 "id" => $meta['meta_id'],332 "key" => $meta['meta_key'],333 "value" => $meta['meta_value']334 );335 }336 337 return $custom_fields;338 }339 340 /**341 * Set custom fields for post.342 *343 * @since 2.5.0344 *345 * @param int $post_id Post ID.346 * @param array $fields Custom fields.347 */348 function set_custom_fields($post_id, $fields) {349 $post_id = (int) $post_id;350 351 foreach ( (array) $fields as $meta ) {352 if ( isset($meta['id']) ) {353 $meta['id'] = (int) $meta['id'];354 355 if ( isset($meta['key']) ) {356 update_meta($meta['id'], $meta['key'], $meta['value']);357 }358 else {359 delete_meta($meta['id']);360 }361 }362 else {363 $_POST['metakeyinput'] = $meta['key'];364 $_POST['metavalue'] = $meta['value'];365 add_meta($post_id);366 }367 }368 }369 370 /**371 * Set up blog options property.372 *373 * Passes property through 'xmlrpc_blog_options' filter.374 *375 * @since 2.6.0376 */377 function initialise_blog_option_info( ) {378 global $wp_version;379 380 $this->blog_options = array(381 // Read only options382 'software_name' => array(383 'desc' => __( 'Software Name' ),384 'readonly' => true,385 'value' => 'WordPress'386 ),387 'software_version' => array(388 'desc' => __( 'Software Version' ),389 'readonly' => true,390 'value' => $wp_version391 ),392 'blog_url' => array(393 'desc' => __( 'Site URL' ),394 'readonly' => true,395 'option' => 'siteurl'396 ),397 398 // Updatable options399 'time_zone' => array(400 'desc' => __( 'Time Zone' ),401 'readonly' => false,402 'option' => 'gmt_offset'403 ),404 'blog_title' => array(405 'desc' => __( 'Site Title' ),406 'readonly' => false,407 'option' => 'blogname'408 ),409 'blog_tagline' => array(410 'desc' => __( 'Site Tagline' ),411 'readonly' => false,412 'option' => 'blogdescription'413 ),414 'date_format' => array(415 'desc' => __( 'Date Format' ),416 'readonly' => false,417 'option' => 'date_format'418 ),419 'time_format' => array(420 'desc' => __( 'Time Format' ),421 'readonly' => false,422 'option' => 'time_format'423 ),424 'users_can_register' => array(425 'desc' => __( 'Allow new users to sign up' ),426 'readonly' => false,427 'option' => 'users_can_register'428 )429 );430 431 $this->blog_options = apply_filters( 'xmlrpc_blog_options', $this->blog_options );432 }433 434 /**435 * Retrieve the blogs of the user.436 *437 * @since 2.6.0438 *439 * @param array $args Method parameters.440 * @return array441 */442 function wp_getUsersBlogs( $args ) {443 global $current_site;444 // If this isn't on WPMU then just use blogger_getUsersBlogs445 if ( !is_multisite() ) {446 array_unshift( $args, 1 );447 return $this->blogger_getUsersBlogs( $args );448 }449 450 $this->escape( $args );451 452 $username = $args[0];453 $password = $args[1];454 455 if ( !$user = $this->login($username, $password) )456 return $this->error;457 458 459 do_action( 'xmlrpc_call', 'wp.getUsersBlogs' );460 461 $blogs = (array) get_blogs_of_user( $user->ID );462 $struct = array( );463 464 foreach ( $blogs as $blog ) {465 // Don't include blogs that aren't hosted at this site466 if ( $blog->site_id != $current_site->id )467 continue;468 469 $blog_id = $blog->userblog_id;470 switch_to_blog($blog_id);471 $is_admin = current_user_can('manage_options');472 473 $struct[] = array(474 'isAdmin' => $is_admin,475 'url' => get_option( 'home' ) . '/',476 'blogid' => $blog_id,477 'blogName' => get_option( 'blogname' ),478 'xmlrpc' => site_url( 'xmlrpc.php' )479 );480 481 restore_current_blog( );482 }483 484 return $struct;485 }486 487 /**488 * Retrieve page.489 *490 * @since 2.2.0491 *492 * @param array $args Method parameters.493 * @return array494 */495 function wp_getPage($args) {496 $this->escape($args);497 498 $blog_id = (int) $args[0];499 $page_id = (int) $args[1];500 $username = $args[2];501 $password = $args[3];502 503 if ( !$user = $this->login($username, $password) ) {504 return $this->error;505 }506 507 if ( !current_user_can( 'edit_page', $page_id ) )508 return new IXR_Error( 401, __( 'Sorry, you cannot edit this page.' ) );509 510 do_action('xmlrpc_call', 'wp.getPage');511 512 // Lookup page info.513 $page = get_page($page_id);514 515 // If we found the page then format the data.516 if ( $page->ID && ($page->post_type == "page") ) {517 // Get all of the page content and link.518 $full_page = get_extended($page->post_content);519 $link = post_permalink($page->ID);520 521 // Get info the page parent if there is one.522 $parent_title = "";523 if ( !empty($page->post_parent) ) {524 $parent = get_page($page->post_parent);525 $parent_title = $parent->post_title;526 }527 528 // Determine comment and ping settings.529 $allow_comments = comments_open($page->ID) ? 1 : 0;530 $allow_pings = pings_open($page->ID) ? 1 : 0;531 532 // Format page date.533 $page_date = mysql2date("Ymd\TH:i:s", $page->post_date, false);534 $page_date_gmt = mysql2date("Ymd\TH:i:s", $page->post_date_gmt, false);535 536 // For drafts use the GMT version of the date537 if ( $page->post_status == 'draft' )538 $page_date_gmt = get_gmt_from_date( mysql2date( 'Y-m-d H:i:s', $page->post_date ), 'Ymd\TH:i:s' );539 540 // Pull the categories info together.541 $categories = array();542 foreach ( wp_get_post_categories($page->ID) as $cat_id ) {543 $categories[] = get_cat_name($cat_id);544 }545 546 // Get the author info.547 $author = get_userdata($page->post_author);548 549 $page_template = get_post_meta( $page->ID, '_wp_page_template', true );550 if ( empty( $page_template ) )551 $page_template = 'default';552 553 $page_struct = array(554 "dateCreated" => new IXR_Date($page_date),555 "userid" => $page->post_author,556 "page_id" => $page->ID,557 "page_status" => $page->post_status,558 "description" => $full_page["main"],559 "title" => $page->post_title,560 "link" => $link,561 "permaLink" => $link,562 "categories" => $categories,563 "excerpt" => $page->post_excerpt,564 "text_more" => $full_page["extended"],565 "mt_allow_comments" => $allow_comments,566 "mt_allow_pings" => $allow_pings,567 "wp_slug" => $page->post_name,568 "wp_password" => $page->post_password,569 "wp_author" => $author->display_name,570 "wp_page_parent_id" => $page->post_parent,571 "wp_page_parent_title" => $parent_title,572 "wp_page_order" => $page->menu_order,573 "wp_author_id" => $author->ID,574 "wp_author_display_name" => $author->display_name,575 "date_created_gmt" => new IXR_Date($page_date_gmt),576 "custom_fields" => $this->get_custom_fields($page_id),577 "wp_page_template" => $page_template578 );579 580 return($page_struct);581 }582 // If the page doesn't exist indicate that.583 else {584 return(new IXR_Error(404, __("Sorry, no such page.")));585 }586 }587 588 /**589 * Retrieve Pages.590 *591 * @since 2.2.0592 *593 * @param array $args Method parameters.594 * @return array595 */596 function wp_getPages($args) {597 $this->escape($args);598 599 $blog_id = (int) $args[0];600 $username = $args[1];601 $password = $args[2];602 $num_pages = isset($args[3]) ? (int) $args[3] : 10;603 604 if ( !$user = $this->login($username, $password) )605 return $this->error;606 607 if ( !current_user_can( 'edit_pages' ) )608 return new IXR_Error( 401, __( 'Sorry, you cannot edit pages.' ) );609 610 do_action('xmlrpc_call', 'wp.getPages');611 612 $pages = get_posts( array('post_type' => 'page', 'post_status' => 'any', 'numberposts' => $num_pages) );613 $num_pages = count($pages);614 615 // If we have pages, put together their info.616 if ( $num_pages >= 1 ) {617 $pages_struct = array();618 619 for ( $i = 0; $i < $num_pages; $i++ ) {620 $page = wp_xmlrpc_server::wp_getPage(array(621 $blog_id, $pages[$i]->ID, $username, $password622 ));623 $pages_struct[] = $page;624 }625 626 return($pages_struct);627 }628 // If no pages were found return an error.629 else {630 return(array());631 }632 }633 634 /**635 * Create new page.636 *637 * @since 2.2.0638 *639 * @param array $args Method parameters.640 * @return unknown641 */642 function wp_newPage($args) {643 // Items not escaped here will be escaped in newPost.644 $username = $this->escape($args[1]);645 $password = $this->escape($args[2]);646 $page = $args[3];647 $publish = $args[4];648 649 if ( !$user = $this->login($username, $password) )650 return $this->error;651 652 do_action('xmlrpc_call', 'wp.newPage');653 654 // Make sure the user is allowed to add new pages.655 if ( !current_user_can("publish_pages") )656 return(new IXR_Error(401, __("Sorry, you cannot add new pages.")));657 658 // Mark this as content for a page.659 $args[3]["post_type"] = "page";660 661 // Let mw_newPost do all of the heavy lifting.662 return($this->mw_newPost($args));663 }664 665 /**666 * Delete page.667 *668 * @since 2.2.0669 *670 * @param array $args Method parameters.671 * @return bool True, if success.672 */673 function wp_deletePage($args) {674 $this->escape($args);675 676 $blog_id = (int) $args[0];677 $username = $args[1];678 $password = $args[2];679 $page_id = (int) $args[3];680 681 if ( !$user = $this->login($username, $password) )682 return $this->error;683 684 do_action('xmlrpc_call', 'wp.deletePage');685 686 // Get the current page based on the page_id and687 // make sure it is a page and not a post.688 $actual_page = wp_get_single_post($page_id, ARRAY_A);689 if ( !$actual_page || ($actual_page["post_type"] != "page") )690 return(new IXR_Error(404, __("Sorry, no such page.")));691 692 // Make sure the user can delete pages.693 if ( !current_user_can("delete_page", $page_id) )694 return(new IXR_Error(401, __("Sorry, you do not have the right to delete this page.")));695 696 // Attempt to delete the page.697 $result = wp_delete_post($page_id);698 if ( !$result )699 return(new IXR_Error(500, __("Failed to delete the page.")));700 701 return(true);702 }703 704 /**705 * Edit page.706 *707 * @since 2.2.0708 *709 * @param array $args Method parameters.710 * @return unknown711 */712 function wp_editPage($args) {713 // Items not escaped here will be escaped in editPost.714 $blog_id = (int) $args[0];715 $page_id = (int) $this->escape($args[1]);716 $username = $this->escape($args[2]);717 $password = $this->escape($args[3]);718 $content = $args[4];719 $publish = $args[5];720 721 if ( !$user = $this->login($username, $password) )722 return $this->error;723 724 do_action('xmlrpc_call', 'wp.editPage');725 726 // Get the page data and make sure it is a page.727 $actual_page = wp_get_single_post($page_id, ARRAY_A);728 if ( !$actual_page || ($actual_page["post_type"] != "page") )729 return(new IXR_Error(404, __("Sorry, no such page.")));730 731 // Make sure the user is allowed to edit pages.732 if ( !current_user_can("edit_page", $page_id) )733 return(new IXR_Error(401, __("Sorry, you do not have the right to edit this page.")));734 735 // Mark this as content for a page.736 $content["post_type"] = "page";737 738 // Arrange args in the way mw_editPost understands.739 $args = array(740 $page_id,741 $username,742 $password,743 $content,744 $publish745 );746 747 // Let mw_editPost do all of the heavy lifting.748 return($this->mw_editPost($args));749 }750 751 /**752 * Retrieve page list.753 *754 * @since 2.2.0755 *756 * @param array $args Method parameters.757 * @return unknown758 */759 function wp_getPageList($args) {760 global $wpdb;761 762 $this->escape($args);763 764 $blog_id = (int) $args[0];765 $username = $args[1];766 $password = $args[2];767 768 if ( !$user = $this->login($username, $password) )769 return $this->error;770 771 if ( !current_user_can( 'edit_pages' ) )772 return new IXR_Error( 401, __( 'Sorry, you cannot edit pages.' ) );773 774 do_action('xmlrpc_call', 'wp.getPageList');775 776 // Get list of pages ids and titles777 $page_list = $wpdb->get_results("778 SELECT ID page_id,779 post_title page_title,780 post_parent page_parent_id,781 post_date_gmt,782 post_date,783 post_status784 FROM {$wpdb->posts}785 WHERE post_type = 'page'786 ORDER BY ID787 ");788 789 // The date needs to be formated properly.790 $num_pages = count($page_list);791 for ( $i = 0; $i < $num_pages; $i++ ) {792 $post_date = mysql2date("Ymd\TH:i:s", $page_list[$i]->post_date, false);793 $post_date_gmt = mysql2date("Ymd\TH:i:s", $page_list[$i]->post_date_gmt, false);794 795 $page_list[$i]->dateCreated = new IXR_Date($post_date);796 $page_list[$i]->date_created_gmt = new IXR_Date($post_date_gmt);797 798 // For drafts use the GMT version of the date799 if ( $page_list[$i]->post_status == 'draft' ) {800 $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' );801 $page_list[$i]->date_created_gmt = new IXR_Date( $page_list[$i]->date_created_gmt );802 }803 804 unset($page_list[$i]->post_date_gmt);805 unset($page_list[$i]->post_date);806 unset($page_list[$i]->post_status);807 }808 809 return($page_list);810 }811 812 /**813 * Retrieve authors list.814 *815 * @since 2.2.0816 *817 * @param array $args Method parameters.818 * @return array819 */820 function wp_getAuthors($args) {821 822 $this->escape($args);823 824 $blog_id = (int) $args[0];825 $username = $args[1];826 $password = $args[2];827 828 if ( !$user = $this->login($username, $password) )829 return $this->error;830 831 if ( !current_user_can("edit_posts") )832 return(new IXR_Error(401, __("Sorry, you cannot edit posts on this site.")));833 834 do_action('xmlrpc_call', 'wp.getAuthors');835 836 $authors = array();837 foreach ( get_users() as $user_id => $user_object ) {838 $authors[] = array(839 "user_id" => $user_id,840 "user_login" => $user_object->user_login,841 "display_name" => $user_object->display_name842 );843 }844 845 return $authors;846 }847 848 /**849 * Get list of all tags850 *851 * @since 2.7852 *853 * @param array $args Method parameters.854 * @return array855 */856 function wp_getTags( $args ) {857 $this->escape( $args );858 859 $blog_id = (int) $args[0];860 $username = $args[1];861 $password = $args[2];862 863 if ( !$user = $this->login($username, $password) )864 return $this->error;865 866 if ( !current_user_can( 'edit_posts' ) )867 return new IXR_Error( 401, __( 'Sorry, you must be able to edit posts on this site in order to view tags.' ) );868 869 do_action( 'xmlrpc_call', 'wp.getKeywords' );870 871 $tags = array( );872 873 if ( $all_tags = get_tags() ) {874 foreach( (array) $all_tags as $tag ) {875 $struct['tag_id'] = $tag->term_id;876 $struct['name'] = $tag->name;877 $struct['count'] = $tag->count;878 $struct['slug'] = $tag->slug;879 $struct['html_url'] = esc_html( get_tag_link( $tag->term_id ) );880 $struct['rss_url'] = esc_html( get_tag_feed_link( $tag->term_id ) );881 882 $tags[] = $struct;883 }884 }885 886 return $tags;887 }888 889 /**890 * Create new category.891 *892 * @since 2.2.0893 *894 * @param array $args Method parameters.895 * @return int Category ID.896 */897 function wp_newCategory($args) {898 $this->escape($args);899 900 $blog_id = (int) $args[0];901 $username = $args[1];902 $password = $args[2];903 $category = $args[3];904 905 if ( !$user = $this->login($username, $password) )906 return $this->error;907 908 do_action('xmlrpc_call', 'wp.newCategory');909 910 // Make sure the user is allowed to add a category.911 if ( !current_user_can("manage_categories") )912 return(new IXR_Error(401, __("Sorry, you do not have the right to add a category.")));913 914 // If no slug was provided make it empty so that915 // WordPress will generate one.916 if ( empty($category["slug"]) )917 $category["slug"] = "";918 919 // If no parent_id was provided make it empty920 // so that it will be a top level page (no parent).921 if ( !isset($category["parent_id"]) )922 $category["parent_id"] = "";923 924 // If no description was provided make it empty.925 if ( empty($category["description"]) )926 $category["description"] = "";927 928 $new_category = array(929 "cat_name" => $category["name"],930 "category_nicename" => $category["slug"],931 "category_parent" => $category["parent_id"],932 "category_description" => $category["description"]933 );934 935 $cat_id = wp_insert_category($new_category, true);936 if ( is_wp_error( $cat_id ) ) {937 if ( 'term_exists' == $cat_id->get_error_code() )938 return (int) $cat_id->get_error_data();939 else940 return(new IXR_Error(500, __("Sorry, the new category failed.")));941 } elseif ( ! $cat_id ) {942 return(new IXR_Error(500, __("Sorry, the new category failed.")));943 }944 945 return($cat_id);946 }947 948 /**949 * Remove category.950 *951 * @since 2.5.0952 *953 * @param array $args Method parameters.954 * @return mixed See {@link wp_delete_category()} for return info.955 */956 function wp_deleteCategory($args) {957 $this->escape($args);958 959 $blog_id = (int) $args[0];960 $username = $args[1];961 $password = $args[2];962 $category_id = (int) $args[3];963 964 if ( !$user = $this->login($username, $password) )965 return $this->error;966 967 do_action('xmlrpc_call', 'wp.deleteCategory');968 969 if ( !current_user_can("manage_categories") )970 return new IXR_Error( 401, __( "Sorry, you do not have the right to delete a category." ) );971 972 return wp_delete_category( $category_id );973 }974 975 /**976 * Retrieve category list.977 *978 * @since 2.2.0979 *980 * @param array $args Method parameters.981 * @return array982 */983 function wp_suggestCategories($args) {984 $this->escape($args);985 986 $blog_id = (int) $args[0];987 $username = $args[1];988 $password = $args[2];989 $category = $args[3];990 $max_results = (int) $args[4];991 992 if ( !$user = $this->login($username, $password) )993 return $this->error;994 995 if ( !current_user_can( 'edit_posts' ) )996 return new IXR_Error( 401, __( 'Sorry, you must be able to edit posts to this site in order to view categories.' ) );997 998 do_action('xmlrpc_call', 'wp.suggestCategories');999 1000 $category_suggestions = array();1001 $args = array('get' => 'all', 'number' => $max_results, 'name__like' => $category);1002 foreach ( (array) get_categories($args) as $cat ) {1003 $category_suggestions[] = array(1004 "category_id" => $cat->cat_ID,1005 "category_name" => $cat->cat_name1006 );1007 }1008 1009 return($category_suggestions);1010 }1011 1012 /**1013 * Retrieve comment.1014 *1015 * @since 2.7.01016 *1017 * @param array $args Method parameters.1018 * @return array1019 */1020 function wp_getComment($args) {1021 $this->escape($args);1022 1023 $blog_id = (int) $args[0];1024 $username = $args[1];1025 $password = $args[2];1026 $comment_id = (int) $args[3];1027 1028 if ( !$user = $this->login($username, $password) )1029 return $this->error;1030 1031 if ( !current_user_can( 'moderate_comments' ) )1032 return new IXR_Error( 403, __( 'You are not allowed to moderate comments on this site.' ) );1033 1034 do_action('xmlrpc_call', 'wp.getComment');1035 1036 if ( ! $comment = get_comment($comment_id) )1037 return new IXR_Error( 404, __( 'Invalid comment ID.' ) );1038 1039 // Format page date.1040 $comment_date = mysql2date("Ymd\TH:i:s", $comment->comment_date, false);1041 $comment_date_gmt = mysql2date("Ymd\TH:i:s", $comment->comment_date_gmt, false);1042 1043 if ( '0' == $comment->comment_approved )1044 $comment_status = 'hold';1045 else if ( 'spam' == $comment->comment_approved )1046 $comment_status = 'spam';1047 else if ( '1' == $comment->comment_approved )1048 $comment_status = 'approve';1049 else1050 $comment_status = $comment->comment_approved;1051 1052 $link = get_comment_link($comment);1053 1054 $comment_struct = array(1055 "date_created_gmt" => new IXR_Date($comment_date_gmt),1056 "user_id" => $comment->user_id,1057 "comment_id" => $comment->comment_ID,1058 "parent" => $comment->comment_parent,1059 "status" => $comment_status,1060 "content" => $comment->comment_content,1061 "link" => $link,1062 "post_id" => $comment->comment_post_ID,1063 "post_title" => get_the_title($comment->comment_post_ID),1064 "author" => $comment->comment_author,1065 "author_url" => $comment->comment_author_url,1066 "author_email" => $comment->comment_author_email,1067 "author_ip" => $comment->comment_author_IP,1068 "type" => $comment->comment_type,1069 );1070 1071 return $comment_struct;1072 }1073 1074 /**1075 * Retrieve comments.1076 *1077 * @since 2.7.01078 *1079 * @param array $args Method parameters.1080 * @return array1081 */1082 function wp_getComments($args) {1083 $raw_args = $args;1084 $this->escape($args);1085 1086 $blog_id = (int) $args[0];1087 $username = $args[1];1088 $password = $args[2];1089 $struct = $args[3];1090 1091 if ( !$user = $this->login($username, $password) )1092 return $this->error;1093 1094 if ( !current_user_can( 'moderate_comments' ) )1095 return new IXR_Error( 401, __( 'Sorry, you cannot edit comments.' ) );1096 1097 do_action('xmlrpc_call', 'wp.getComments');1098 1099 if ( isset($struct['status']) )1100 $status = $struct['status'];1101 else1102 $status = '';1103 1104 $post_id = '';1105 if ( isset($struct['post_id']) )1106 $post_id = absint($struct['post_id']);1107 1108 $offset = 0;1109 if ( isset($struct['offset']) )1110 $offset = absint($struct['offset']);1111 1112 $number = 10;1113 if ( isset($struct['number']) )1114 $number = absint($struct['number']);1115 1116 $comments = get_comments( array('status' => $status, 'post_id' => $post_id, 'offset' => $offset, 'number' => $number ) );1117 $num_comments = count($comments);1118 1119 if ( ! $num_comments )1120 return array();1121 1122 $comments_struct = array();1123 1124 for ( $i = 0; $i < $num_comments; $i++ ) {1125 $comment = wp_xmlrpc_server::wp_getComment(array(1126 $raw_args[0], $raw_args[1], $raw_args[2], $comments[$i]->comment_ID,1127 ));1128 $comments_struct[] = $comment;1129 }1130 1131 return $comments_struct;1132 }1133 1134 /**1135 * Remove comment.1136 *1137 * @since 2.7.01138 *1139 * @param array $args Method parameters.1140 * @return mixed {@link wp_delete_comment()}1141 */1142 function wp_deleteComment($args) {1143 $this->escape($args);1144 1145 $blog_id = (int) $args[0];1146 $username = $args[1];1147 $password = $args[2];1148 $comment_ID = (int) $args[3];1149 1150 if ( !$user = $this->login($username, $password) )1151 return $this->error;1152 1153 if ( !current_user_can( 'moderate_comments' ) )1154 return new IXR_Error( 403, __( 'You are not allowed to moderate comments on this site.' ) );1155 1156 do_action('xmlrpc_call', 'wp.deleteComment');1157 1158 if ( ! get_comment($comment_ID) )1159 return new IXR_Error( 404, __( 'Invalid comment ID.' ) );1160 1161 return wp_delete_comment($comment_ID);1162 }1163 1164 /**1165 * Edit comment.1166 *1167 * @since 2.7.01168 *1169 * @param array $args Method parameters.1170 * @return bool True, on success.1171 */1172 function wp_editComment($args) {1173 $this->escape($args);1174 1175 $blog_id = (int) $args[0];1176 $username = $args[1];1177 $password = $args[2];1178 $comment_ID = (int) $args[3];1179 $content_struct = $args[4];1180 1181 if ( !$user = $this->login($username, $password) )1182 return $this->error;1183 1184 if ( !current_user_can( 'moderate_comments' ) )1185 return new IXR_Error( 403, __( 'You are not allowed to moderate comments on this site.' ) );1186 1187 do_action('xmlrpc_call', 'wp.editComment');1188 1189 if ( ! get_comment($comment_ID) )1190 return new IXR_Error( 404, __( 'Invalid comment ID.' ) );1191 1192 if ( isset($content_struct['status']) ) {1193 $statuses = get_comment_statuses();1194 $statuses = array_keys($statuses);1195 1196 if ( ! in_array($content_struct['status'], $statuses) )1197 return new IXR_Error( 401, __( 'Invalid comment status.' ) );1198 $comment_approved = $content_struct['status'];1199 }1200 1201 // Do some timestamp voodoo1202 if ( !empty( $content_struct['date_created_gmt'] ) ) {1203 $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 force1204 $comment_date = get_date_from_gmt(iso8601_to_datetime($dateCreated));1205 $comment_date_gmt = iso8601_to_datetime($dateCreated, GMT);1206 }1207 1208 if ( isset($content_struct['content']) )1209 $comment_content = $content_struct['content'];1210 1211 if ( isset($content_struct['author']) )1212 $comment_author = $content_struct['author'];1213 1214 if ( isset($content_struct['author_url']) )1215 $comment_author_url = $content_struct['author_url'];1216 1217 if ( isset($content_struct['author_email']) )1218 $comment_author_email = $content_struct['author_email'];1219 1220 // We've got all the data -- post it:1221 $comment = compact('comment_ID', 'comment_content', 'comment_approved', 'comment_date', 'comment_date_gmt', 'comment_author', 'comment_author_email', 'comment_author_url');1222 1223 $result = wp_update_comment($comment);1224 if ( is_wp_error( $result ) )1225 return new IXR_Error(500, $result->get_error_message());1226 1227 if ( !$result )1228 return new IXR_Error(500, __('Sorry, the comment could not be edited. Something wrong happened.'));1229 1230 return true;1231 }1232 1233 /**1234 * Create new comment.1235 *1236 * @since 2.7.01237 *1238 * @param array $args Method parameters.1239 * @return mixed {@link wp_new_comment()}1240 */1241 function wp_newComment($args) {1242 global $wpdb;1243 1244 $this->escape($args);1245 1246 $blog_id = (int) $args[0];1247 $username = $args[1];1248 $password = $args[2];1249 $post = $args[3];1250 $content_struct = $args[4];1251 1252 $allow_anon = apply_filters('xmlrpc_allow_anonymous_comments', false);1253 1254 $user = $this->login($username, $password);1255 1256 if ( !$user ) {1257 $logged_in = false;1258 if ( $allow_anon && get_option('comment_registration') )1259 return new IXR_Error( 403, __( 'You must be registered to comment' ) );1260 else if ( !$allow_anon )1261 return $this->error;1262 } else {1263 $logged_in = true;1264 }1265 1266 if ( is_numeric($post) )1267 $post_id = absint($post);1268 else1269 $post_id = url_to_postid($post);1270 1271 if ( ! $post_id )1272 return new IXR_Error( 404, __( 'Invalid post ID.' ) );1273 1274 if ( ! get_post($post_id) )1275 return new IXR_Error( 404, __( 'Invalid post ID.' ) );1276 1277 $comment['comment_post_ID'] = $post_id;1278 1279 if ( $logged_in ) {1280 $comment['comment_author'] = $wpdb->escape( $user->display_name );1281 $comment['comment_author_email'] = $wpdb->escape( $user->user_email );1282 $comment['comment_author_url'] = $wpdb->escape( $user->user_url );1283 $comment['user_ID'] = $user->ID;1284 } else {1285 $comment['comment_author'] = '';1286 if ( isset($content_struct['author']) )1287 $comment['comment_author'] = $content_struct['author'];1288 1289 $comment['comment_author_email'] = '';1290 if ( isset($content_struct['author_email']) )1291 $comment['comment_author_email'] = $content_struct['author_email'];1292 1293 $comment['comment_author_url'] = '';1294 if ( isset($content_struct['author_url']) )1295 $comment['comment_author_url'] = $content_struct['author_url'];1296 1297 $comment['user_ID'] = 0;1298 1299 if ( get_option('require_name_email') ) {1300 if ( 6 > strlen($comment['comment_author_email']) || '' == $comment['comment_author'] )1301 return new IXR_Error( 403, __( 'Comment author name and email are required' ) );1302 elseif ( !is_email($comment['comment_author_email']) )1303 return new IXR_Error( 403, __( 'A valid email address is required' ) );1304 }1305 }1306 1307 $comment['comment_parent'] = isset($content_struct['comment_parent']) ? absint($content_struct['comment_parent']) : 0;1308 1309 $comment['comment_content'] = $content_struct['content'];1310 1311 do_action('xmlrpc_call', 'wp.newComment');1312 1313 return wp_new_comment($comment);1314 }1315 1316 /**1317 * Retrieve all of the comment status.1318 *1319 * @since 2.7.01320 *1321 * @param array $args Method parameters.1322 * @return array1323 */1324 function wp_getCommentStatusList($args) {1325 $this->escape( $args );1326 1327 $blog_id = (int) $args[0];1328 $username = $args[1];1329 $password = $args[2];1330 1331 if ( !$user = $this->login($username, $password) )1332 return $this->error;1333 1334 if ( !current_user_can( 'moderate_comments' ) )1335 return new IXR_Error( 403, __( 'You are not allowed access to details about this site.' ) );1336 1337 do_action('xmlrpc_call', 'wp.getCommentStatusList');1338 1339 return get_comment_statuses( );1340 }1341 1342 /**1343 * Retrieve comment count.1344 *1345 * @since 2.5.01346 *1347 * @param array $args Method parameters.1348 * @return array1349 */1350 function wp_getCommentCount( $args ) {1351 $this->escape($args);1352 1353 $blog_id = (int) $args[0];1354 $username = $args[1];1355 $password = $args[2];1356 $post_id = (int) $args[3];1357 1358 if ( !$user = $this->login($username, $password) )1359 return $this->error;1360 1361 if ( !current_user_can( 'edit_posts' ) )1362 return new IXR_Error( 403, __( 'You are not allowed access to details about comments.' ) );1363 1364 do_action('xmlrpc_call', 'wp.getCommentCount');1365 1366 $count = wp_count_comments( $post_id );1367 return array(1368 "approved" => $count->approved,1369 "awaiting_moderation" => $count->moderated,1370 "spam" => $count->spam,1371 "total_comments" => $count->total_comments1372 );1373 }1374 1375 /**1376 * Retrieve post statuses.1377 *1378 * @since 2.5.01379 *1380 * @param array $args Method parameters.1381 * @return array1382 */1383 function wp_getPostStatusList( $args ) {1384 $this->escape( $args );1385 1386 $blog_id = (int) $args[0];1387 $username = $args[1];1388 $password = $args[2];1389 1390 if ( !$user = $this->login($username, $password) )1391 return $this->error;1392 1393 if ( !current_user_can( 'edit_posts' ) )1394 return new IXR_Error( 403, __( 'You are not allowed access to details about this site.' ) );1395 1396 do_action('xmlrpc_call', 'wp.getPostStatusList');1397 1398 return get_post_statuses( );1399 }1400 1401 /**1402 * Retrieve page statuses.1403 *1404 * @since 2.5.01405 *1406 * @param array $args Method parameters.1407 * @return array1408 */1409 function wp_getPageStatusList( $args ) {1410 $this->escape( $args );1411 1412 $blog_id = (int) $args[0];1413 $username = $args[1];1414 $password = $args[2];1415 1416 if ( !$user = $this->login($username, $password) )1417 return $this->error;1418 1419 if ( !current_user_can( 'edit_posts' ) )1420 return new IXR_Error( 403, __( 'You are not allowed access to details about this site.' ) );1421 1422 do_action('xmlrpc_call', 'wp.getPageStatusList');1423 1424 return get_page_statuses( );1425 }1426 1427 /**1428 * Retrieve page templates.1429 *1430 * @since 2.6.01431 *1432 * @param array $args Method parameters.1433 * @return array1434 */1435 function wp_getPageTemplates( $args ) {1436 $this->escape( $args );1437 1438 $blog_id = (int) $args[0];1439 $username = $args[1];1440 $password = $args[2];1441 1442 if ( !$user = $this->login($username, $password) )1443 return $this->error;1444 1445 if ( !current_user_can( 'edit_pages' ) )1446 return new IXR_Error( 403, __( 'You are not allowed access to details about this site.' ) );1447 1448 $templates = get_page_templates( );1449 $templates['Default'] = 'default';1450 1451 return $templates;1452 }1453 1454 /**1455 * Retrieve blog options.1456 *1457 * @since 2.6.01458 *1459 * @param array $args Method parameters.1460 * @return array1461 */1462 function wp_getOptions( $args ) {1463 $this->escape( $args );1464 1465 $blog_id = (int) $args[0];1466 $username = $args[1];1467 $password = $args[2];1468 $options = (array) $args[3];1469 1470 if ( !$user = $this->login($username, $password) )1471 return $this->error;1472 1473 // If no specific options where asked for, return all of them1474 if ( count( $options ) == 0 )1475 $options = array_keys($this->blog_options);1476 1477 return $this->_getOptions($options);1478 }1479 1480 /**1481 * Retrieve blog options value from list.1482 *1483 * @since 2.6.01484 *1485 * @param array $options Options to retrieve.1486 * @return array1487 */1488 function _getOptions($options) {1489 $data = array( );1490 foreach ( $options as $option ) {1491 if ( array_key_exists( $option, $this->blog_options ) ) {1492 $data[$option] = $this->blog_options[$option];1493 //Is the value static or dynamic?1494 if ( isset( $data[$option]['option'] ) ) {1495 $data[$option]['value'] = get_option( $data[$option]['option'] );1496 unset($data[$option]['option']);1497 }1498 }1499 }1500 1501 return $data;1502 }1503 1504 /**1505 * Update blog options.1506 *1507 * @since 2.6.01508 *1509 * @param array $args Method parameters.1510 * @return unknown1511 */1512 function wp_setOptions( $args ) {1513 $this->escape( $args );1514 1515 $blog_id = (int) $args[0];1516 $username = $args[1];1517 $password = $args[2];1518 $options = (array) $args[3];1519 1520 if ( !$user = $this->login($username, $password) )1521 return $this->error;1522 1523 if ( !current_user_can( 'manage_options' ) )1524 return new IXR_Error( 403, __( 'You are not allowed to update options.' ) );1525 1526 foreach ( $options as $o_name => $o_value ) {1527 $option_names[] = $o_name;1528 if ( !array_key_exists( $o_name, $this->blog_options ) )1529 continue;1530 1531 if ( $this->blog_options[$o_name]['readonly'] == true )1532 continue;1533 1534 update_option( $this->blog_options[$o_name]['option'], $o_value );1535 }1536 1537 //Now return the updated values1538 return $this->_getOptions($option_names);1539 }1540 1541 /* Blogger API functions.1542 * specs on http://plant.blogger.com/api and http://groups.yahoo.com/group/bloggerDev/1543 */1544 1545 /**1546 * Retrieve blogs that user owns.1547 *1548 * Will make more sense once we support multiple blogs.1549 *1550 * @since 1.5.01551 *1552 * @param array $args Method parameters.1553 * @return array1554 */1555 function blogger_getUsersBlogs($args) {1556 if ( is_multisite() )1557 return $this->_multisite_getUsersBlogs($args);1558 1559 $this->escape($args);1560 1561 $username = $args[1];1562 $password = $args[2];1563 1564 if ( !$user = $this->login($username, $password) )1565 return $this->error;1566 1567 do_action('xmlrpc_call', 'blogger.getUsersBlogs');1568 1569 $is_admin = current_user_can('manage_options');1570 1571 $struct = array(1572 'isAdmin' => $is_admin,1573 'url' => get_option('home') . '/',1574 'blogid' => '1',1575 'blogName' => get_option('blogname'),1576 'xmlrpc' => site_url( 'xmlrpc.php' )1577 );1578 1579 return array($struct);1580 }1581 1582 /**1583 * Private function for retrieving a users blogs for multisite setups1584 *1585 * @access protected1586 */1587 function _multisite_getUsersBlogs($args) {1588 global $current_blog;1589 $domain = $current_blog->domain;1590 $path = $current_blog->path . 'xmlrpc.php';1591 $protocol = is_ssl() ? 'https' : 'http';1592 1593 $rpc = new IXR_Client("$protocol://{$domain}{$path}");1594 $rpc->query('wp.getUsersBlogs', $args[1], $args[2]);1595 $blogs = $rpc->getResponse();1596 1597 if ( isset($blogs['faultCode']) )1598 return new IXR_Error($blogs['faultCode'], $blogs['faultString']);1599 1600 if ( $_SERVER['HTTP_HOST'] == $domain && $_SERVER['REQUEST_URI'] == $path ) {1601 return $blogs;1602 } else {1603 foreach ( (array) $blogs as $blog ) {1604 if ( strpos($blog['url'], $_SERVER['HTTP_HOST']) )1605 return array($blog);1606 }1607 return array();1608 }1609 }1610 1611 /**1612 * Retrieve user's data.1613 *1614 * Gives your client some info about you, so you don't have to.1615 *1616 * @since 1.5.01617 *1618 * @param array $args Method parameters.1619 * @return array1620 */1621 function blogger_getUserInfo($args) {1622 1623 $this->escape($args);1624 1625 $username = $args[1];1626 $password = $args[2];1627 1628 if ( !$user = $this->login($username, $password) )1629 return $this->error;1630 1631 if ( !current_user_can( 'edit_posts' ) )1632 return new IXR_Error( 401, __( 'Sorry, you do not have access to user data on this site.' ) );1633 1634 do_action('xmlrpc_call', 'blogger.getUserInfo');1635 1636 $struct = array(1637 'nickname' => $user->nickname,1638 'userid' => $user->ID,1639 'url' => $user->user_url,1640 'lastname' => $user->last_name,1641 'firstname' => $user->first_name1642 );1643 1644 return $struct;1645 }1646 1647 /**1648 * Retrieve post.1649 *1650 * @since 1.5.01651 *1652 * @param array $args Method parameters.1653 * @return array1654 */1655 function blogger_getPost($args) {1656 1657 $this->escape($args);1658 1659 $post_ID = (int) $args[1];1660 $username = $args[2];1661 $password = $args[3];1662 1663 if ( !$user = $this->login($username, $password) )1664 return $this->error;1665 1666 if ( !current_user_can( 'edit_post', $post_ID ) )1667 return new IXR_Error( 401, __( 'Sorry, you cannot edit this post.' ) );1668 1669 do_action('xmlrpc_call', 'blogger.getPost');1670 1671 $post_data = wp_get_single_post($post_ID, ARRAY_A);1672 1673 $categories = implode(',', wp_get_post_categories($post_ID));1674 1675 $content = '<title>'.stripslashes($post_data['post_title']).'</title>';1676 $content .= '<category>'.$categories.'</category>';1677 $content .= stripslashes($post_data['post_content']);1678 1679 $struct = array(1680 'userid' => $post_data['post_author'],1681 'dateCreated' => new IXR_Date(mysql2date('Ymd\TH:i:s', $post_data['post_date'], false)),1682 'content' => $content,1683 'postid' => $post_data['ID']1684 );1685 1686 return $struct;1687 }1688 1689 /**1690 * Retrieve list of recent posts.1691 *1692 * @since 1.5.01693 *1694 * @param array $args Method parameters.1695 * @return array1696 */1697 function blogger_getRecentPosts($args) {1698 1699 $this->escape($args);1700 1701 $blog_ID = (int) $args[1]; /* though we don't use it yet */1702 $username = $args[2];1703 $password = $args[3];1704 $num_posts = $args[4];1705 1706 if ( !$user = $this->login($username, $password) )1707 return $this->error;1708 1709 do_action('xmlrpc_call', 'blogger.getRecentPosts');1710 1711 $posts_list = wp_get_recent_posts($num_posts);1712 1713 if ( !$posts_list ) {1714 $this->error = new IXR_Error(500, __('Either there are no posts, or something went wrong.'));1715 return $this->error;1716 }1717 1718 foreach ($posts_list as $entry) {1719 if ( !current_user_can( 'edit_post', $entry['ID'] ) )1720 continue;1721 1722 $post_date = mysql2date('Ymd\TH:i:s', $entry['post_date'], false);1723 $categories = implode(',', wp_get_post_categories($entry['ID']));1724 1725 $content = '<title>'.stripslashes($entry['post_title']).'</title>';1726 $content .= '<category>'.$categories.'</category>';1727 $content .= stripslashes($entry['post_content']);1728 1729 $struct[] = array(1730 'userid' => $entry['post_author'],1731 'dateCreated' => new IXR_Date($post_date),1732 'content' => $content,1733 'postid' => $entry['ID'],1734 );1735 1736 }1737 1738 $recent_posts = array();1739 for ( $j=0; $j<count($struct); $j++ ) {1740 array_push($recent_posts, $struct[$j]);1741 }1742 1743 return $recent_posts;1744 }1745 1746 /**1747 * Retrieve blog_filename content.1748 *1749 * @since 1.5.01750 *1751 * @param array $args Method parameters.1752 * @return string1753 */1754 function blogger_getTemplate($args) {1755 1756 $this->escape($args);1757 1758 $blog_ID = (int) $args[1];1759 $username = $args[2];1760 $password = $args[3];1761 $template = $args[4]; /* could be 'main' or 'archiveIndex', but we don't use it */1762 1763 if ( !$user = $this->login($username, $password) )1764 return $this->error;1765 1766 do_action('xmlrpc_call', 'blogger.getTemplate');1767 1768 if ( !current_user_can('edit_themes') )1769 return new IXR_Error(401, __('Sorry, this user can not edit the template.'));1770 1771 /* warning: here we make the assumption that the blog's URL is on the same server */1772 $filename = get_option('home') . '/';1773 $filename = preg_replace('#https?://.+?/#', $_SERVER['DOCUMENT_ROOT'].'/', $filename);1774 1775 $f = fopen($filename, 'r');1776 $content = fread($f, filesize($filename));1777 fclose($f);1778 1779 /* so it is actually editable with a windows/mac client */1780 // 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);1781 1782 return $content;1783 }1784 1785 /**1786 * Updates the content of blog_filename.1787 *1788 * @since 1.5.01789 *1790 * @param array $args Method parameters.1791 * @return bool True when done.1792 */1793 function blogger_setTemplate($args) {1794 1795 $this->escape($args);1796 1797 $blog_ID = (int) $args[1];1798 $username = $args[2];1799 $password = $args[3];1800 $content = $args[4];1801 $template = $args[5]; /* could be 'main' or 'archiveIndex', but we don't use it */1802 1803 if ( !$user = $this->login($username, $password) )1804 return $this->error;1805 1806 do_action('xmlrpc_call', 'blogger.setTemplate');1807 1808 if ( !current_user_can('edit_themes') )1809 return new IXR_Error(401, __('Sorry, this user cannot edit the template.'));1810 1811 /* warning: here we make the assumption that the blog's URL is on the same server */1812 $filename = get_option('home') . '/';1813 $filename = preg_replace('#https?://.+?/#', $_SERVER['DOCUMENT_ROOT'].'/', $filename);1814 1815 if ($f = fopen($filename, 'w+')) {1816 fwrite($f, $content);1817 fclose($f);1818 } else {1819 return new IXR_Error(500, __('Either the file is not writable, or something wrong happened. The file has not been updated.'));1820 }1821 1822 return true;1823 }1824 1825 /**1826 * Create new post.1827 *1828 * @since 1.5.01829 *1830 * @param array $args Method parameters.1831 * @return int1832 */1833 function blogger_newPost($args) {1834 1835 $this->escape($args);1836 1837 $blog_ID = (int) $args[1]; /* though we don't use it yet */1838 $username = $args[2];1839 $password = $args[3];1840 $content = $args[4];1841 $publish = $args[5];1842 1843 if ( !$user = $this->login($username, $password) )1844 return $this->error;1845 1846 do_action('xmlrpc_call', 'blogger.newPost');1847 1848 $cap = ($publish) ? 'publish_posts' : 'edit_posts';1849 if ( !current_user_can($cap) )1850 return new IXR_Error(401, __('Sorry, you are not allowed to post on this site.'));1851 1852 $post_status = ($publish) ? 'publish' : 'draft';1853 1854 $post_author = $user->ID;1855 1856 $post_title = xmlrpc_getposttitle($content);1857 $post_category = xmlrpc_getpostcategory($content);1858 $post_content = xmlrpc_removepostdata($content);1859 1860 $post_date = current_time('mysql');1861 $post_date_gmt = current_time('mysql', 1);1862 1863 $post_data = compact('blog_ID', 'post_author', 'post_date', 'post_date_gmt', 'post_content', 'post_title', 'post_category', 'post_status');1864 1865 $post_ID = wp_insert_post($post_data);1866 if ( is_wp_error( $post_ID ) )1867 return new IXR_Error(500, $post_ID->get_error_message());1868 1869 if ( !$post_ID )1870 return new IXR_Error(500, __('Sorry, your entry could not be posted. Something wrong happened.'));1871 1872 $this->attach_uploads( $post_ID, $post_content );1873 1874 logIO('O', "Posted ! ID: $post_ID");1875 1876 return $post_ID;1877 }1878 1879 /**1880 * Edit a post.1881 *1882 * @since 1.5.01883 *1884 * @param array $args Method parameters.1885 * @return bool true when done.1886 */1887 function blogger_editPost($args) {1888 1889 $this->escape($args);1890 1891 $post_ID = (int) $args[1];1892 $username = $args[2];1893 $password = $args[3];1894 $content = $args[4];1895 $publish = $args[5];1896 1897 if ( !$user = $this->login($username, $password) )1898 return $this->error;1899 1900 do_action('xmlrpc_call', 'blogger.editPost');1901 1902 $actual_post = wp_get_single_post($post_ID,ARRAY_A);1903 1904 if ( !$actual_post || $actual_post['post_type'] != 'post' )1905 return new IXR_Error(404, __('Sorry, no such post.'));1906 1907 $this->escape($actual_post);1908 1909 if ( !current_user_can('edit_post', $post_ID) )1910 return new IXR_Error(401, __('Sorry, you do not have the right to edit this post.'));1911 1912 extract($actual_post, EXTR_SKIP);1913 1914 if ( ('publish' == $post_status) && !current_user_can('publish_posts') )1915 return new IXR_Error(401, __('Sorry, you do not have the right to publish this post.'));1916 1917 $post_title = xmlrpc_getposttitle($content);1918 $post_category = xmlrpc_getpostcategory($content);1919 $post_content = xmlrpc_removepostdata($content);1920 1921 $postdata = compact('ID', 'post_content', 'post_title', 'post_category', 'post_status', 'post_excerpt');1922 1923 $result = wp_update_post($postdata);1924 1925 if ( !$result )1926 return new IXR_Error(500, __('For some strange yet very annoying reason, this post could not be edited.'));1927 1928 $this->attach_uploads( $ID, $post_content );1929 1930 return true;1931 }1932 1933 /**1934 * Remove a post.1935 *1936 * @since 1.5.01937 *1938 * @param array $args Method parameters.1939 * @return bool True when post is deleted.1940 */1941 function blogger_deletePost($args) {1942 $this->escape($args);1943 1944 $post_ID = (int) $args[1];1945 $username = $args[2];1946 $password = $args[3];1947 $publish = $args[4];1948 1949 if ( !$user = $this->login($username, $password) )1950 return $this->error;1951 1952 do_action('xmlrpc_call', 'blogger.deletePost');1953 1954 $actual_post = wp_get_single_post($post_ID,ARRAY_A);1955 1956 if ( !$actual_post || $actual_post['post_type'] != 'post' )1957 return new IXR_Error(404, __('Sorry, no such post.'));1958 1959 if ( !current_user_can('edit_post', $post_ID) )1960 return new IXR_Error(401, __('Sorry, you do not have the right to delete this post.'));1961 1962 $result = wp_delete_post($post_ID);1963 1964 if ( !$result )1965 return new IXR_Error(500, __('For some strange yet very annoying reason, this post could not be deleted.'));1966 1967 return true;1968 }1969 1970 /* MetaWeblog API functions1971 * specs on wherever Dave Winer wants them to be1972 */1973 1974 /**1975 * Create a new post.1976 *1977 * @since 1.5.01978 *1979 * @param array $args Method parameters.1980 * @return int1981 */1982 function mw_newPost($args) {1983 $this->escape($args);1984 1985 $blog_ID = (int) $args[0]; // we will support this in the near future1986 $username = $args[1];1987 $password = $args[2];1988 $content_struct = $args[3];1989 $publish = $args[4];1990 1991 if ( !$user = $this->login($username, $password) )1992 return $this->error;1993 1994 do_action('xmlrpc_call', 'metaWeblog.newPost');1995 1996 $cap = ( $publish ) ? 'publish_posts' : 'edit_posts';1997 $error_message = __( 'Sorry, you are not allowed to publish posts on this site.' );1998 $post_type = 'post';1999 $page_template = '';2000 if ( !empty( $content_struct['post_type'] ) ) {2001 if ( $content_struct['post_type'] == 'page' ) {2002 $cap = ( $publish ) ? 'publish_pages' : 'edit_pages';2003 $error_message = __( 'Sorry, you are not allowed to publish pages on this site.' );2004 $post_type = 'page';2005 if ( !empty( $content_struct['wp_page_template'] ) )2006 $page_template = $content_struct['wp_page_template'];2007 } elseif ( $content_struct['post_type'] == 'post' ) {2008 // This is the default, no changes needed2009 } else {2010 // No other post_type values are allowed here2011 return new IXR_Error( 401, __( 'Invalid post type.' ) );2012 }2013 }2014 2015 if ( !current_user_can( $cap ) )2016 return new IXR_Error( 401, $error_message );2017 2018 // Let WordPress generate the post_name (slug) unless2019 // one has been provided.2020 $post_name = "";2021 if ( isset($content_struct["wp_slug"]) )2022 $post_name = $content_struct["wp_slug"];2023 2024 // Only use a password if one was given.2025 if ( isset($content_struct["wp_password"]) )2026 $post_password = $content_struct["wp_password"];2027 2028 // Only set a post parent if one was provided.2029 if ( isset($content_struct["wp_page_parent_id"]) )2030 $post_parent = $content_struct["wp_page_parent_id"];2031 2032 // Only set the menu_order if it was provided.2033 if ( isset($content_struct["wp_page_order"]) )2034 $menu_order = $content_struct["wp_page_order"];2035 2036 $post_author = $user->ID;2037 2038 // If an author id was provided then use it instead.2039 if ( isset($content_struct["wp_author_id"]) && ($user->ID != $content_struct["wp_author_id"]) ) {2040 switch ( $post_type ) {2041 case "post":2042 if ( !current_user_can("edit_others_posts") )2043 return(new IXR_Error(401, __("You are not allowed to post as this user")));2044 break;2045 case "page":2046 if ( !current_user_can("edit_others_pages") )2047 return(new IXR_Error(401, __("You are not allowed to create pages as this user")));2048 break;2049 default:2050 return(new IXR_Error(401, __("Invalid post type.")));2051 break;2052 }2053 $post_author = $content_struct["wp_author_id"];2054 }2055 2056 $post_title = $content_struct['title'];2057 $post_content = $content_struct['description'];2058 2059 $post_status = $publish ? 'publish' : 'draft';2060 2061 if ( isset( $content_struct["{$post_type}_status"] ) ) {2062 switch ( $content_struct["{$post_type}_status"] ) {2063 case 'draft':2064 case 'private':2065 case 'publish':2066 $post_status = $content_struct["{$post_type}_status"];2067 break;2068 case 'pending':2069 // Pending is only valid for posts, not pages.2070 if ( $post_type === 'post' )2071 $post_status = $content_struct["{$post_type}_status"];2072 break;2073 default:2074 $post_status = $publish ? 'publish' : 'draft';2075 break;2076 }2077 }2078 2079 $post_excerpt = $content_struct['mt_excerpt'];2080 $post_more = $content_struct['mt_text_more'];2081 2082 $tags_input = $content_struct['mt_keywords'];2083 2084 if ( isset($content_struct["mt_allow_comments"]) ) {2085 if ( !is_numeric($content_struct["mt_allow_comments"]) ) {2086 switch ( $content_struct["mt_allow_comments"] ) {2087 case "closed":2088 $comment_status = "closed";2089 break;2090 case "open":2091 $comment_status = "open";2092 break;2093 default:2094 $comment_status = get_option("default_comment_status");2095 break;2096 }2097 } else {2098 switch ( (int) $content_struct["mt_allow_comments"] ) {2099 case 0:2100 case 2:2101 $comment_status = "closed";2102 break;2103 case 1:2104 $comment_status = "open";2105 break;2106 default:2107 $comment_status = get_option("default_comment_status");2108 break;2109 }2110 }2111 } else {2112 $comment_status = get_option("default_comment_status");2113 }2114 2115 if ( isset($content_struct["mt_allow_pings"]) ) {2116 if ( !is_numeric($content_struct["mt_allow_pings"]) ) {2117 switch ( $content_struct['mt_allow_pings'] ) {2118 case "closed":2119 $ping_status = "closed";2120 break;2121 case "open":2122 $ping_status = "open";2123 break;2124 default:2125 $ping_status = get_option("default_ping_status");2126 break;2127 }2128 } else {2129 switch ( (int) $content_struct["mt_allow_pings"] ) {2130 case 0:2131 $ping_status = "closed";2132 break;2133 case 1:2134 $ping_status = "open";2135 break;2136 default:2137 $ping_status = get_option("default_ping_status");2138 break;2139 }2140 }2141 } else {2142 $ping_status = get_option("default_ping_status");2143 }2144 2145 if ( $post_more )2146 $post_content = $post_content . "<!--more-->" . $post_more;2147 2148 $to_ping = $content_struct['mt_tb_ping_urls'];2149 if ( is_array($to_ping) )2150 $to_ping = implode(' ', $to_ping);2151 2152 // Do some timestamp voodoo2153 if ( !empty( $content_struct['date_created_gmt'] ) )2154 $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 force2155 elseif ( !empty( $content_struct['dateCreated']) )2156 $dateCreated = $content_struct['dateCreated']->getIso();2157 2158 if ( !empty( $dateCreated ) ) {2159 $post_date = get_date_from_gmt(iso8601_to_datetime($dateCreated));2160 $post_date_gmt = iso8601_to_datetime($dateCreated, GMT);2161 } else {2162 $post_date = current_time('mysql');2163 $post_date_gmt = current_time('mysql', 1);2164 }2165 2166 $catnames = $content_struct['categories'];2167 logIO('O', 'Post cats: ' . var_export($catnames,true));2168 $post_category = array();2169 2170 if ( is_array($catnames) ) {2171 foreach ($catnames as $cat) {2172 $post_category[] = get_cat_ID($cat);2173 }2174 }2175 2176 // We've got all the data -- post it:2177 $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');2178 2179 $post_ID = wp_insert_post($postdata, true);2180 if ( is_wp_error( $post_ID ) )2181 return new IXR_Error(500, $post_ID->get_error_message());2182 2183 if ( !$post_ID )2184 return new IXR_Error(500, __('Sorry, your entry could not be posted. Something wrong happened.'));2185 2186 // Only posts can be sticky2187 if ( $post_type == 'post' && isset( $content_struct['sticky'] ) ) {2188 if ( $content_struct['sticky'] == true )2189 stick_post( $post_ID );2190 elseif ( $content_struct['sticky'] == false )2191 unstick_post( $post_ID );2192 }2193 2194 if ( isset($content_struct['custom_fields']) )2195 $this->set_custom_fields($post_ID, $content_struct['custom_fields']);2196 2197 // Handle enclosures2198 $this->add_enclosure_if_new($post_ID, $content_struct['enclosure']);2199 2200 $this->attach_uploads( $post_ID, $post_content );2201 2202 logIO('O', "Posted ! ID: $post_ID");2203 2204 return strval($post_ID);2205 }2206 2207 function add_enclosure_if_new($post_ID, $enclosure) {2208 if ( is_array( $enclosure ) && isset( $enclosure['url'] ) && isset( $enclosure['length'] ) && isset( $enclosure['type'] ) ) {2209 2210 $encstring = $enclosure['url'] . "\n" . $enclosure['length'] . "\n" . $enclosure['type'];2211 $found = false;2212 foreach ( (array) get_post_custom($post_ID) as $key => $val) {2213 if ($key == 'enclosure') {2214 foreach ( (array) $val as $enc ) {2215 if ($enc == $encstring) {2216 $found = true;2217 break 2;2218 }2219 }2220 }2221 }2222 if (!$found)2223 add_post_meta( $post_ID, 'enclosure', $encstring );2224 }2225 }2226 2227 /**2228 * Attach upload to a post.2229 *2230 * @since 2.1.02231 *2232 * @param int $post_ID Post ID.2233 * @param string $post_content Post Content for attachment.2234 */2235 function attach_uploads( $post_ID, $post_content ) {2236 global $wpdb;2237 2238 // find any unattached files2239 $attachments = $wpdb->get_results( "SELECT ID, guid FROM {$wpdb->posts} WHERE post_parent = '0' AND post_type = 'attachment'" );2240 if ( is_array( $attachments ) ) {2241 foreach ( $attachments as $file ) {2242 if ( strpos( $post_content, $file->guid ) !== false )2243 $wpdb->update($wpdb->posts, array('post_parent' => $post_ID), array('ID' => $file->ID) );2244 }2245 }2246 }2247 2248 /**2249 * Edit a post.2250 *2251 * @since 1.5.02252 *2253 * @param array $args Method parameters.2254 * @return bool True on success.2255 */2256 function mw_editPost($args) {2257 2258 $this->escape($args);2259 2260 $post_ID = (int) $args[0];2261 $username = $args[1];2262 $password = $args[2];2263 $content_struct = $args[3];2264 $publish = $args[4];2265 2266 if ( !$user = $this->login($username, $password) )2267 return $this->error;2268 2269 do_action('xmlrpc_call', 'metaWeblog.editPost');2270 2271 $cap = ( $publish ) ? 'publish_posts' : 'edit_posts';2272 $error_message = __( 'Sorry, you are not allowed to publish posts on this site.' );2273 $post_type = 'post';2274 $page_template = '';2275 if ( !empty( $content_struct['post_type'] ) ) {2276 if ( $content_struct['post_type'] == 'page' ) {2277 $cap = ( $publish ) ? 'publish_pages' : 'edit_pages';2278 $error_message = __( 'Sorry, you are not allowed to publish pages on this site.' );2279 $post_type = 'page';2280 if ( !empty( $content_struct['wp_page_template'] ) )2281 $page_template = $content_struct['wp_page_template'];2282 } elseif ( $content_struct['post_type'] == 'post' ) {2283 // This is the default, no changes needed2284 } else {2285 // No other post_type values are allowed here2286 return new IXR_Error( 401, __( 'Invalid post type.' ) );2287 }2288 }2289 2290 if ( !current_user_can( $cap ) )2291 return new IXR_Error( 401, $error_message );2292 2293 $postdata = wp_get_single_post($post_ID, ARRAY_A);2294 2295 // If there is no post data for the give post id, stop2296 // now and return an error. Other wise a new post will be2297 // created (which was the old behavior).2298 if ( empty($postdata["ID"]) )2299 return(new IXR_Error(404, __("Invalid post ID.")));2300 2301 $this->escape($postdata);2302 extract($postdata, EXTR_SKIP);2303 2304 // Let WordPress manage slug if none was provided.2305 $post_name = "";2306 if ( isset($content_struct["wp_slug"]) )2307 $post_name = $content_struct["wp_slug"];2308 2309 // Only use a password if one was given.2310 if ( isset($content_struct["wp_password"]) )2311 $post_password = $content_struct["wp_password"];2312 2313 // Only set a post parent if one was given.2314 if ( isset($content_struct["wp_page_parent_id"]) )2315 $post_parent = $content_struct["wp_page_parent_id"];2316 2317 // Only set the menu_order if it was given.2318 if ( isset($content_struct["wp_page_order"]) )2319 $menu_order = $content_struct["wp_page_order"];2320 2321 $post_author = $postdata["post_author"];2322 2323 // Only set the post_author if one is set.2324 if ( isset($content_struct["wp_author_id"]) && ($user->ID != $content_struct["wp_author_id"]) ) {2325 switch ( $post_type ) {2326 case "post":2327 if ( !current_user_can("edit_others_posts") )2328 return(new IXR_Error(401, __("You are not allowed to change the post author as this user.")));2329 break;2330 case "page":2331 if ( !current_user_can("edit_others_pages") )2332 return(new IXR_Error(401, __("You are not allowed to change the page author as this user.")));2333 break;2334 default:2335 return(new IXR_Error(401, __("Invalid post type.")));2336 break;2337 }2338 $post_author = $content_struct["wp_author_id"];2339 }2340 2341 if ( isset($content_struct["mt_allow_comments"]) ) {2342 if ( !is_numeric($content_struct["mt_allow_comments"]) ) {2343 switch ( $content_struct["mt_allow_comments"] ) {2344 case "closed":2345 $comment_status = "closed";2346 break;2347 case "open":2348 $comment_status = "open";2349 break;2350 default:2351 $comment_status = get_option("default_comment_status");2352 break;2353 }2354 } else {2355 switch ( (int) $content_struct["mt_allow_comments"] ) {2356 case 0:2357 case 2:2358 $comment_status = "closed";2359 break;2360 case 1:2361 $comment_status = "open";2362 break;2363 default:2364 $comment_status = get_option("default_comment_status");2365 break;2366 }2367 }2368 }2369 2370 if ( isset($content_struct["mt_allow_pings"]) ) {2371 if ( !is_numeric($content_struct["mt_allow_pings"]) ) {2372 switch ( $content_struct["mt_allow_pings"] ) {2373 case "closed":2374 $ping_status = "closed";2375 break;2376 case "open":2377 $ping_status = "open";2378 break;2379 default:2380 $ping_status = get_option("default_ping_status");2381 break;2382 }2383 } else {2384 switch ( (int) $content_struct["mt_allow_pings"] ) {2385 case 0:2386 $ping_status = "closed";2387 break;2388 case 1:2389 $ping_status = "open";2390 break;2391 default:2392 $ping_status = get_option("default_ping_status");2393 break;2394 }2395 }2396 }2397 2398 $post_title = $content_struct['title'];2399 $post_content = $content_struct['description'];2400 $catnames = $content_struct['categories'];2401 2402 $post_category = array();2403 2404 if ( is_array($catnames) ) {2405 foreach ($catnames as $cat) {2406 $post_category[] = get_cat_ID($cat);2407 }2408 }2409 2410 $post_excerpt = $content_struct['mt_excerpt'];2411 $post_more = $content_struct['mt_text_more'];2412 2413 $post_status = $publish ? 'publish' : 'draft';2414 if ( isset( $content_struct["{$post_type}_status"] ) ) {2415 switch( $content_struct["{$post_type}_status"] ) {2416 case 'draft':2417 case 'private':2418 case 'publish':2419 $post_status = $content_struct["{$post_type}_status"];2420 break;2421 case 'pending':2422 // Pending is only valid for posts, not pages.2423 if ( $post_type === 'post' )2424 $post_status = $content_struct["{$post_type}_status"];2425 break;2426 default:2427 $post_status = $publish ? 'publish' : 'draft';2428 break;2429 }2430 }2431 2432 $tags_input = $content_struct['mt_keywords'];2433 2434 if ( ('publish' == $post_status) ) {2435 if ( ( 'page' == $post_type ) && !current_user_can('publish_pages') )2436 return new IXR_Error(401, __('Sorry, you do not have the right to publish this page.'));2437 else if ( !current_user_can('publish_posts') )2438 return new IXR_Error(401, __('Sorry, you do not have the right to publish this post.'));2439 }2440 2441 if ( $post_more )2442 $post_content = $post_content . "<!--more-->" . $post_more;2443 2444 $to_ping = $content_struct['mt_tb_ping_urls'];2445 if ( is_array($to_ping) )2446 $to_ping = implode(' ', $to_ping);2447 2448 // Do some timestamp voodoo2449 if ( !empty( $content_struct['date_created_gmt'] ) )2450 $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 force2451 elseif ( !empty( $content_struct['dateCreated']) )2452 $dateCreated = $content_struct['dateCreated']->getIso();2453 2454 if ( !empty( $dateCreated ) ) {2455 $post_date = get_date_from_gmt(iso8601_to_datetime($dateCreated));2456 $post_date_gmt = iso8601_to_datetime($dateCreated, GMT);2457 } else {2458 $post_date = $postdata['post_date'];2459 $post_date_gmt = $postdata['post_date_gmt'];2460 }2461 2462 // We've got all the data -- post it:2463 $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');2464 2465 $result = wp_update_post($newpost, true);2466 if ( is_wp_error( $result ) )2467 return new IXR_Error(500, $result->get_error_message());2468 2469 if ( !$result )2470 return new IXR_Error(500, __('Sorry, your entry could not be edited. Something wrong happened.'));2471 2472 // Only posts can be sticky2473 if ( $post_type == 'post' && isset( $content_struct['sticky'] ) ) {2474 if ( $content_struct['sticky'] == true )2475 stick_post( $post_ID );2476 elseif ( $content_struct['sticky'] == false )2477 unstick_post( $post_ID );2478 }2479 2480 if ( isset($content_struct['custom_fields']) )2481 $this->set_custom_fields($post_ID, $content_struct['custom_fields']);2482 2483 // Handle enclosures2484 $this->add_enclosure_if_new($post_ID, $content_struct['enclosure']);2485 2486 $this->attach_uploads( $ID, $post_content );2487 2488 logIO('O',"(MW) Edited ! ID: $post_ID");2489 2490 return true;2491 }2492 2493 /**2494 * Retrieve post.2495 *2496 * @since 1.5.02497 *2498 * @param array $args Method parameters.2499 * @return array2500 */2501 function mw_getPost($args) {2502 2503 $this->escape($args);2504 2505 $post_ID = (int) $args[0];2506 $username = $args[1];2507 $password = $args[2];2508 2509 if ( !$user = $this->login($username, $password) )2510 return $this->error;2511 2512 if ( !current_user_can( 'edit_post', $post_ID ) )2513 return new IXR_Error( 401, __( 'Sorry, you cannot edit this post.' ) );2514 2515 do_action('xmlrpc_call', 'metaWeblog.getPost');2516 2517 $postdata = wp_get_single_post($post_ID, ARRAY_A);2518 2519 if ($postdata['post_date'] != '') {2520 $post_date = mysql2date('Ymd\TH:i:s', $postdata['post_date'], false);2521 $post_date_gmt = mysql2date('Ymd\TH:i:s', $postdata['post_date_gmt'], false);2522 2523 // For drafts use the GMT version of the post date2524 if ( $postdata['post_status'] == 'draft' )2525 $post_date_gmt = get_gmt_from_date( mysql2date( 'Y-m-d H:i:s', $postdata['post_date'] ), 'Ymd\TH:i:s' );2526 2527 $categories = array();2528 $catids = wp_get_post_categories($post_ID);2529 foreach($catids as $catid)2530 $categories[] = get_cat_name($catid);2531 2532 $tagnames = array();2533 $tags = wp_get_post_tags( $post_ID );2534 if ( !empty( $tags ) ) {2535 foreach ( $tags as $tag )2536 $tagnames[] = $tag->name;2537 $tagnames = implode( ', ', $tagnames );2538 } else {2539 $tagnames = '';2540 }2541 2542 $post = get_extended($postdata['post_content']);2543 $link = post_permalink($postdata['ID']);2544 2545 // Get the author info.2546 $author = get_userdata($postdata['post_author']);2547 2548 $allow_comments = ('open' == $postdata['comment_status']) ? 1 : 0;2549 $allow_pings = ('open' == $postdata['ping_status']) ? 1 : 0;2550 2551 // Consider future posts as published2552 if ( $postdata['post_status'] === 'future' )2553 $postdata['post_status'] = 'publish';2554 2555 $sticky = false;2556 if ( is_sticky( $post_ID ) )2557 $sticky = true;2558 2559 $enclosure = array();2560 foreach ( (array) get_post_custom($post_ID) as $key => $val) {2561 if ($key == 'enclosure') {2562 foreach ( (array) $val as $enc ) {2563 $encdata = split("\n", $enc);2564 $enclosure['url'] = trim(htmlspecialchars($encdata[0]));2565 $enclosure['length'] = (int) trim($encdata[1]);2566 $enclosure['type'] = trim($encdata[2]);2567 break 2;2568 }2569 }2570 }2571 2572 $resp = array(2573 'dateCreated' => new IXR_Date($post_date),2574 'userid' => $postdata['post_author'],2575 'postid' => $postdata['ID'],2576 'description' => $post['main'],2577 'title' => $postdata['post_title'],2578 'link' => $link,2579 'permaLink' => $link,2580 // commented out because no other tool seems to use this2581 // 'content' => $entry['post_content'],2582 'categories' => $categories,2583 'mt_excerpt' => $postdata['post_excerpt'],2584 'mt_text_more' => $post['extended'],2585 'mt_allow_comments' => $allow_comments,2586 'mt_allow_pings' => $allow_pings,2587 'mt_keywords' => $tagnames,2588 'wp_slug' => $postdata['post_name'],2589 'wp_password' => $postdata['post_password'],2590 'wp_author_id' => $author->ID,2591 'wp_author_display_name' => $author->display_name,2592 'date_created_gmt' => new IXR_Date($post_date_gmt),2593 'post_status' => $postdata['post_status'],2594 'custom_fields' => $this->get_custom_fields($post_ID),2595 'sticky' => $sticky2596 );2597 2598 if ( !empty($enclosure) ) $resp['enclosure'] = $enclosure;2599 2600 return $resp;2601 } else {2602 return new IXR_Error(404, __('Sorry, no such post.'));2603 }2604 }2605 2606 /**2607 * Retrieve list of recent posts.2608 *2609 * @since 1.5.02610 *2611 * @param array $args Method parameters.2612 * @return array2613 */2614 function mw_getRecentPosts($args) {2615 2616 $this->escape($args);2617 2618 $blog_ID = (int) $args[0];2619 $username = $args[1];2620 $password = $args[2];2621 $num_posts = (int) $args[3];2622 2623 if ( !$user = $this->login($username, $password) )2624 return $this->error;2625 2626 do_action('xmlrpc_call', 'metaWeblog.getRecentPosts');2627 2628 $posts_list = wp_get_recent_posts($num_posts);2629 2630 if ( !$posts_list )2631 return array( );2632 2633 foreach ($posts_list as $entry) {2634 if ( !current_user_can( 'edit_post', $entry['ID'] ) )2635 continue;2636 2637 $post_date = mysql2date('Ymd\TH:i:s', $entry['post_date'], false);2638 $post_date_gmt = mysql2date('Ymd\TH:i:s', $entry['post_date_gmt'], false);2639 2640 // For drafts use the GMT version of the date2641 if ( $entry['post_status'] == 'draft' )2642 $post_date_gmt = get_gmt_from_date( mysql2date( 'Y-m-d H:i:s', $entry['post_date'] ), 'Ymd\TH:i:s' );2643 2644 $categories = array();2645 $catids = wp_get_post_categories($entry['ID']);2646 foreach( $catids as $catid )2647 $categories[] = get_cat_name($catid);2648 2649 $tagnames = array();2650 $tags = wp_get_post_tags( $entry['ID'] );2651 if ( !empty( $tags ) ) {2652 foreach ( $tags as $tag ) {2653 $tagnames[] = $tag->name;2654 }2655 $tagnames = implode( ', ', $tagnames );2656 } else {2657 $tagnames = '';2658 }2659 2660 $post = get_extended($entry['post_content']);2661 $link = post_permalink($entry['ID']);2662 2663 // Get the post author info.2664 $author = get_userdata($entry['post_author']);2665 2666 $allow_comments = ('open' == $entry['comment_status']) ? 1 : 0;2667 $allow_pings = ('open' == $entry['ping_status']) ? 1 : 0;2668 2669 // Consider future posts as published2670 if ( $entry['post_status'] === 'future' )2671 $entry['post_status'] = 'publish';2672 2673 $struct[] = array(2674 'dateCreated' => new IXR_Date($post_date),2675 'userid' => $entry['post_author'],2676 'postid' => $entry['ID'],2677 'description' => $post['main'],2678 'title' => $entry['post_title'],2679 'link' => $link,2680 'permaLink' => $link,2681 // commented out because no other tool seems to use this2682 // 'content' => $entry['post_content'],2683 'categories' => $categories,2684 'mt_excerpt' => $entry['post_excerpt'],2685 'mt_text_more' => $post['extended'],2686 'mt_allow_comments' => $allow_comments,2687 'mt_allow_pings' => $allow_pings,2688 'mt_keywords' => $tagnames,2689 'wp_slug' => $entry['post_name'],2690 'wp_password' => $entry['post_password'],2691 'wp_author_id' => $author->ID,2692 'wp_author_display_name' => $author->display_name,2693 'date_created_gmt' => new IXR_Date($post_date_gmt),2694 'post_status' => $entry['post_status'],2695 'custom_fields' => $this->get_custom_fields($entry['ID'])2696 );2697 2698 }2699 2700 $recent_posts = array();2701 for ( $j=0; $j<count($struct); $j++ ) {2702 array_push($recent_posts, $struct[$j]);2703 }2704 2705 return $recent_posts;2706 }2707 2708 /**2709 * Retrieve the list of categories on a given blog.2710 *2711 * @since 1.5.02712 *2713 * @param array $args Method parameters.2714 * @return array2715 */2716 function mw_getCategories($args) {2717 2718 $this->escape($args);2719 2720 $blog_ID = (int) $args[0];2721 $username = $args[1];2722 $password = $args[2];2723 2724 if ( !$user = $this->login($username, $password) )2725 return $this->error;2726 2727 if ( !current_user_can( 'edit_posts' ) )2728 return new IXR_Error( 401, __( 'Sorry, you must be able to edit posts on this site in order to view categories.' ) );2729 2730 do_action('xmlrpc_call', 'metaWeblog.getCategories');2731 2732 $categories_struct = array();2733 2734 if ( $cats = get_categories(array('get' => 'all')) ) {2735 foreach ( $cats as $cat ) {2736 $struct['categoryId'] = $cat->term_id;2737 $struct['parentId'] = $cat->parent;2738 $struct['description'] = $cat->name;2739 $struct['categoryDescription'] = $cat->description;2740 $struct['categoryName'] = $cat->name;2741 $struct['htmlUrl'] = esc_html(get_category_link($cat->term_id));2742 $struct['rssUrl'] = esc_html(get_category_feed_link($cat->term_id, 'rss2'));2743 2744 $categories_struct[] = $struct;2745 }2746 }2747 2748 return $categories_struct;2749 }2750 2751 /**2752 * Uploads a file, following your settings.2753 *2754 * Adapted from a patch by Johann Richard.2755 *2756 * @link http://mycvs.org/archives/2004/06/30/file-upload-to-wordpress-in-ecto/2757 *2758 * @since 1.5.02759 *2760 * @param array $args Method parameters.2761 * @return array2762 */2763 function mw_newMediaObject($args) {2764 global $wpdb;2765 2766 $blog_ID = (int) $args[0];2767 $username = $wpdb->escape($args[1]);2768 $password = $wpdb->escape($args[2]);2769 $data = $args[3];2770 2771 $name = sanitize_file_name( $data['name'] );2772 $type = $data['type'];2773 $bits = $data['bits'];2774 2775 logIO('O', '(MW) Received '.strlen($bits).' bytes');2776 2777 if ( !$user = $this->login($username, $password) )2778 return $this->error;2779 2780 do_action('xmlrpc_call', 'metaWeblog.newMediaObject');2781 2782 if ( !current_user_can('upload_files') ) {2783 logIO('O', '(MW) User does not have upload_files capability');2784 $this->error = new IXR_Error(401, __('You are not allowed to upload files to this site.'));2785 return $this->error;2786 }2787 2788 if ( $upload_err = apply_filters( "pre_upload_error", false ) )2789 return new IXR_Error(500, $upload_err);2790 2791 if ( !empty($data["overwrite"]) && ($data["overwrite"] == true) ) {2792 // Get postmeta info on the object.2793 $old_file = $wpdb->get_row("2794 SELECT ID2795 FROM {$wpdb->posts}2796 WHERE post_title = '{$name}'2797 AND post_type = 'attachment'2798 ");2799 2800 // Delete previous file.2801 wp_delete_attachment($old_file->ID);2802 2803 // Make sure the new name is different by pre-pending the2804 // previous post id.2805 $filename = preg_replace("/^wpid\d+-/", "", $name);2806 $name = "wpid{$old_file->ID}-{$filename}";2807 }2808 2809 $upload = wp_upload_bits($name, $type, $bits);2810 if ( ! empty($upload['error']) ) {2811 $errorString = sprintf(__('Could not write file %1$s (%2$s)'), $name, $upload['error']);2812 logIO('O', '(MW) ' . $errorString);2813 return new IXR_Error(500, $errorString);2814 }2815 // Construct the attachment array2816 // attach to post_id 02817 $post_id = 0;2818 $attachment = array(2819 'post_title' => $name,2820 'post_content' => '',2821 'post_type' => 'attachment',2822 'post_parent' => $post_id,2823 'post_mime_type' => $type,2824 'guid' => $upload[ 'url' ]2825 );2826 2827 // Save the data2828 $id = wp_insert_attachment( $attachment, $upload[ 'file' ], $post_id );2829 wp_update_attachment_metadata( $id, wp_generate_attachment_metadata( $id, $upload['file'] ) );2830 2831 return apply_filters( 'wp_handle_upload', array( 'file' => $name, 'url' => $upload[ 'url' ], 'type' => $type ), 'upload' );2832 }2833 2834 /* MovableType API functions2835 * specs on http://www.movabletype.org/docs/mtmanual_programmatic.html2836 */2837 2838 /**2839 * Retrieve the post titles of recent posts.2840 *2841 * @since 1.5.02842 *2843 * @param array $args Method parameters.2844 * @return array2845 */2846 function mt_getRecentPostTitles($args) {2847 2848 $this->escape($args);2849 2850 $blog_ID = (int) $args[0];2851 $username = $args[1];2852 $password = $args[2];2853 $num_posts = (int) $args[3];2854 2855 if ( !$user = $this->login($username, $password) )2856 return $this->error;2857 2858 do_action('xmlrpc_call', 'mt.getRecentPostTitles');2859 2860 $posts_list = wp_get_recent_posts($num_posts);2861 2862 if ( !$posts_list ) {2863 $this->error = new IXR_Error(500, __('Either there are no posts, or something went wrong.'));2864 return $this->error;2865 }2866 2867 foreach ($posts_list as $entry) {2868 if ( !current_user_can( 'edit_post', $entry['ID'] ) )2869 continue;2870 2871 $post_date = mysql2date('Ymd\TH:i:s', $entry['post_date'], false);2872 $post_date_gmt = mysql2date('Ymd\TH:i:s', $entry['post_date_gmt'], false);2873 2874 // For drafts use the GMT version of the date2875 if ( $entry['post_status'] == 'draft' )2876 $post_date_gmt = get_gmt_from_date( mysql2date( 'Y-m-d H:i:s', $entry['post_date'] ), 'Ymd\TH:i:s' );2877 2878 $struct[] = array(2879 'dateCreated' => new IXR_Date($post_date),2880 'userid' => $entry['post_author'],2881 'postid' => $entry['ID'],2882 'title' => $entry['post_title'],2883 'date_created_gmt' => new IXR_Date($post_date_gmt)2884 );2885 2886 }2887 2888 $recent_posts = array();2889 for ( $j=0; $j<count($struct); $j++ ) {2890 array_push($recent_posts, $struct[$j]);2891 }2892 2893 return $recent_posts;2894 }2895 2896 /**2897 * Retrieve list of all categories on blog.2898 *2899 * @since 1.5.02900 *2901 * @param array $args Method parameters.2902 * @return array2903 */2904 function mt_getCategoryList($args) {2905 2906 $this->escape($args);2907 2908 $blog_ID = (int) $args[0];2909 $username = $args[1];2910 $password = $args[2];2911 2912 if ( !$user = $this->login($username, $password) )2913 return $this->error;2914 2915 if ( !current_user_can( 'edit_posts' ) )2916 return new IXR_Error( 401, __( 'Sorry, you must be able to edit posts on this site in order to view categories.' ) );2917 2918 do_action('xmlrpc_call', 'mt.getCategoryList');2919 2920 $categories_struct = array();2921 2922 if ( $cats = get_categories(array('hide_empty' => 0, 'hierarchical' => 0)) ) {2923 foreach ( $cats as $cat ) {2924 $struct['categoryId'] = $cat->term_id;2925 $struct['categoryName'] = $cat->name;2926 2927 $categories_struct[] = $struct;2928 }2929 }2930 2931 return $categories_struct;2932 }2933 2934 /**2935 * Retrieve post categories.2936 *2937 * @since 1.5.02938 *2939 * @param array $args Method parameters.2940 * @return array2941 */2942 function mt_getPostCategories($args) {2943 2944 $this->escape($args);2945 2946 $post_ID = (int) $args[0];2947 $username = $args[1];2948 $password = $args[2];2949 2950 if ( !$user = $this->login($username, $password) )2951 return $this->error;2952 2953 if ( !current_user_can( 'edit_post', $post_ID ) )2954 return new IXR_Error( 401, __( 'Sorry, you can not edit this post.' ) );2955 2956 do_action('xmlrpc_call', 'mt.getPostCategories');2957 2958 $categories = array();2959 $catids = wp_get_post_categories(intval($post_ID));2960 // first listed category will be the primary category2961 $isPrimary = true;2962 foreach ( $catids as $catid ) {2963 $categories[] = array(2964 'categoryName' => get_cat_name($catid),2965 'categoryId' => (string) $catid,2966 'isPrimary' => $isPrimary2967 );2968 $isPrimary = false;2969 }2970 2971 return $categories;2972 }2973 2974 /**2975 * Sets categories for a post.2976 *2977 * @since 1.5.02978 *2979 * @param array $args Method parameters.2980 * @return bool True on success.2981 */2982 function mt_setPostCategories($args) {2983 2984 $this->escape($args);2985 2986 $post_ID = (int) $args[0];2987 $username = $args[1];2988 $password = $args[2];2989 $categories = $args[3];2990 2991 if ( !$user = $this->login($username, $password) )2992 return $this->error;2993 2994 do_action('xmlrpc_call', 'mt.setPostCategories');2995 2996 if ( !current_user_can('edit_post', $post_ID) )2997 return new IXR_Error(401, __('Sorry, you cannot edit this post.'));2998 2999 foreach ( $categories as $cat ) {3000 $catids[] = $cat['categoryId'];3001 }3002 3003 wp_set_post_categories($post_ID, $catids);3004 3005 return true;3006 }3007 3008 /**3009 * Retrieve an array of methods supported by this server.3010 *3011 * @since 1.5.03012 *3013 * @param array $args Method parameters.3014 * @return array3015 */3016 function mt_supportedMethods($args) {3017 3018 do_action('xmlrpc_call', 'mt.supportedMethods');3019 3020 $supported_methods = array();3021 foreach ( $this->methods as $key => $value ) {3022 $supported_methods[] = $key;3023 }3024 3025 return $supported_methods;3026 }3027 3028 /**3029 * Retrieve an empty array because we don't support per-post text filters.3030 *3031 * @since 1.5.03032 *3033 * @param array $args Method parameters.3034 */3035 function mt_supportedTextFilters($args) {3036 do_action('xmlrpc_call', 'mt.supportedTextFilters');3037 return apply_filters('xmlrpc_text_filters', array());3038 }3039 3040 /**3041 * Retrieve trackbacks sent to a given post.3042 *3043 * @since 1.5.03044 *3045 * @param array $args Method parameters.3046 * @return mixed3047 */3048 function mt_getTrackbackPings($args) {3049 3050 global $wpdb;3051 3052 $post_ID = intval($args);3053 3054 do_action('xmlrpc_call', 'mt.getTrackbackPings');3055 3056 $actual_post = wp_get_single_post($post_ID, ARRAY_A);3057 3058 if ( !$actual_post )3059 return new IXR_Error(404, __('Sorry, no such post.'));3060 3061 $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) );3062 3063 if ( !$comments )3064 return array();3065 3066 $trackback_pings = array();3067 foreach ( $comments as $comment ) {3068 if ( 'trackback' == $comment->comment_type ) {3069 $content = $comment->comment_content;3070 $title = substr($content, 8, (strpos($content, '</strong>') - 8));3071 $trackback_pings[] = array(3072 'pingTitle' => $title,3073 'pingURL' => $comment->comment_author_url,3074 'pingIP' => $comment->comment_author_IP3075 );3076 }3077 }3078 3079 return $trackback_pings;3080 }3081 3082 /**3083 * Sets a post's publish status to 'publish'.3084 *3085 * @since 1.5.03086 *3087 * @param array $args Method parameters.3088 * @return int3089 */3090 function mt_publishPost($args) {3091 3092 $this->escape($args);3093 3094 $post_ID = (int) $args[0];3095 $username = $args[1];3096 $password = $args[2];3097 3098 if ( !$user = $this->login($username, $password) )3099 return $this->error;3100 3101 do_action('xmlrpc_call', 'mt.publishPost');3102 3103 if ( !current_user_can('edit_post', $post_ID) )3104 return new IXR_Error(401, __('Sorry, you cannot edit this post.'));3105 3106 $postdata = wp_get_single_post($post_ID,ARRAY_A);3107 3108 $postdata['post_status'] = 'publish';3109 3110 // retain old cats3111 $cats = wp_get_post_categories($post_ID);3112 $postdata['post_category'] = $cats;3113 $this->escape($postdata);3114 3115 $result = wp_update_post($postdata);3116 3117 return $result;3118 }3119 3120 /* PingBack functions3121 * specs on www.hixie.ch/specs/pingback/pingback3122 */3123 3124 /**3125 * Retrieves a pingback and registers it.3126 *3127 * @since 1.5.03128 *3129 * @param array $args Method parameters.3130 * @return array3131 */3132 function pingback_ping($args) {3133 global $wpdb;3134 3135 do_action('xmlrpc_call', 'pingback.ping');3136 3137 $this->escape($args);3138 3139 $pagelinkedfrom = $args[0];3140 $pagelinkedto = $args[1];3141 3142 $title = '';3143 3144 $pagelinkedfrom = str_replace('&', '&', $pagelinkedfrom);3145 $pagelinkedto = str_replace('&', '&', $pagelinkedto);3146 $pagelinkedto = str_replace('&', '&', $pagelinkedto);3147 3148 // Check if the page linked to is in our site3149 $pos1 = strpos($pagelinkedto, str_replace(array('http://www.','http://','https://www.','https://'), '', get_option('home')));3150 if ( !$pos1 )3151 return new IXR_Error(0, __('Is there no link to us?'));3152 3153 // let's find which post is linked to3154 // FIXME: does url_to_postid() cover all these cases already?3155 // if so, then let's use it and drop the old code.3156 $urltest = parse_url($pagelinkedto);3157 if ( $post_ID = url_to_postid($pagelinkedto) ) {3158 $way = 'url_to_postid()';3159 } elseif ( preg_match('#p/[0-9]{1,}#', $urltest['path'], $match) ) {3160 // the path defines the post_ID (archives/p/XXXX)3161 $blah = explode('/', $match[0]);3162 $post_ID = (int) $blah[1];3163 $way = 'from the path';3164 } elseif ( preg_match('#p=[0-9]{1,}#', $urltest['query'], $match) ) {3165 // the querystring defines the post_ID (?p=XXXX)3166 $blah = explode('=', $match[0]);3167 $post_ID = (int) $blah[1];3168 $way = 'from the querystring';3169 } elseif ( isset($urltest['fragment']) ) {3170 // an #anchor is there, it's either...3171 if ( intval($urltest['fragment']) ) {3172 // ...an integer #XXXX (simpliest case)3173 $post_ID = (int) $urltest['fragment'];3174 $way = 'from the fragment (numeric)';3175 } elseif ( preg_match('/post-[0-9]+/',$urltest['fragment']) ) {3176 // ...a post id in the form 'post-###'3177 $post_ID = preg_replace('/[^0-9]+/', '', $urltest['fragment']);3178 $way = 'from the fragment (post-###)';3179 } elseif ( is_string($urltest['fragment']) ) {3180 // ...or a string #title, a little more complicated3181 $title = preg_replace('/[^a-z0-9]/i', '.', $urltest['fragment']);3182 $sql = $wpdb->prepare("SELECT ID FROM $wpdb->posts WHERE post_title RLIKE %s", $title);3183 if (! ($post_ID = $wpdb->get_var($sql)) ) {3184 // returning unknown error '0' is better than die()ing3185 return new IXR_Error(0, '');3186 }3187 $way = 'from the fragment (title)';3188 }3189 } else {3190 // TODO: Attempt to extract a post ID from the given URL3191 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.'));3192 }3193 $post_ID = (int) $post_ID;3194 3195 3196 logIO("O","(PB) URL='$pagelinkedto' ID='$post_ID' Found='$way'");3197 3198 $post = get_post($post_ID);3199 3200 if ( !$post ) // Post_ID not found3201 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.'));3202 3203 if ( $post_ID == url_to_postid($pagelinkedfrom) )3204 return new IXR_Error(0, __('The source URL and the target URL cannot both point to the same resource.'));3205 3206 // Check if pings are on3207 if ( !pings_open($post) )3208 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.'));3209 3210 // Let's check that the remote site didn't already pingback this entry3211 if ( $wpdb->get_results( $wpdb->prepare("SELECT * FROM $wpdb->comments WHERE comment_post_ID = %d AND comment_author_url = %s", $post_ID, $pagelinkedfrom) ) )3212 return new IXR_Error( 48, __( 'The pingback has already been registered.' ) );3213 3214 // very stupid, but gives time to the 'from' server to publish !3215 sleep(1);3216 3217 // Let's check the remote site3218 $linea = wp_remote_fopen( $pagelinkedfrom );3219 if ( !$linea )3220 return new IXR_Error(16, __('The source URL does not exist.'));3221 3222 $linea = apply_filters('pre_remote_source', $linea, $pagelinkedto);3223 3224 // Work around bug in strip_tags():3225 $linea = str_replace('<!DOC', '<DOC', $linea);3226 $linea = preg_replace( '/[\s\r\n\t]+/', ' ', $linea ); // normalize spaces3227 $linea = preg_replace( "/ <(h1|h2|h3|h4|h5|h6|p|th|td|li|dt|dd|pre|caption|input|textarea|button|body)[^>]*>/", "\n\n", $linea );3228 3229 preg_match('|<title>([^<]*?)</title>|is', $linea, $matchtitle);3230 $title = $matchtitle[1];3231 if ( empty( $title ) )3232 return new IXR_Error(32, __('We cannot find a title on that page.'));3233 3234 $linea = strip_tags( $linea, '<a>' ); // just keep the tag we need3235 3236 $p = explode( "\n\n", $linea );3237 3238 $preg_target = preg_quote($pagelinkedto, '|');3239 3240 foreach ( $p as $para ) {3241 if ( strpos($para, $pagelinkedto) !== false ) { // it exists, but is it a link?3242 preg_match("|<a[^>]+?".$preg_target."[^>]*>([^>]+?)</a>|", $para, $context);3243 3244 // If the URL isn't in a link context, keep looking3245 if ( empty($context) )3246 continue;3247 3248 // We're going to use this fake tag to mark the context in a bit3249 // the marker is needed in case the link text appears more than once in the paragraph3250 $excerpt = preg_replace('|\</?wpcontext\>|', '', $para);3251 3252 // prevent really long link text3253 if ( strlen($context[1]) > 100 )3254 $context[1] = substr($context[1], 0, 100) . '...';3255 3256 $marker = '<wpcontext>'.$context[1].'</wpcontext>'; // set up our marker3257 $excerpt= str_replace($context[0], $marker, $excerpt); // swap out the link for our marker3258 $excerpt = strip_tags($excerpt, '<wpcontext>'); // strip all tags but our context marker3259 $excerpt = trim($excerpt);3260 $preg_marker = preg_quote($marker, '|');3261 $excerpt = preg_replace("|.*?\s(.{0,100}$preg_marker.{0,100})\s.*|s", '$1', $excerpt);3262 $excerpt = strip_tags($excerpt); // YES, again, to remove the marker wrapper3263 break;3264 }3265 }3266 3267 if ( empty($context) ) // Link to target not found3268 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.'));3269 3270 $pagelinkedfrom = str_replace('&', '&', $pagelinkedfrom);3271 3272 $context = '[...] ' . esc_html( $excerpt ) . ' [...]';3273 $pagelinkedfrom = $wpdb->escape( $pagelinkedfrom );3274 3275 $comment_post_ID = (int) $post_ID;3276 $comment_author = $title;3277 $this->escape($comment_author);3278 $comment_author_url = $pagelinkedfrom;3279 $comment_content = $context;3280 $this->escape($comment_content);3281 $comment_type = 'pingback';3282 3283 $commentdata = compact('comment_post_ID', 'comment_author', 'comment_author_url', 'comment_content', 'comment_type');3284 3285 $comment_ID = wp_new_comment($commentdata);3286 do_action('pingback_post', $comment_ID);3287 3288 return sprintf(__('Pingback from %1$s to %2$s registered. Keep the web talking! :-)'), $pagelinkedfrom, $pagelinkedto);3289 }3290 3291 /**3292 * Retrieve array of URLs that pingbacked the given URL.3293 *3294 * Specs on http://www.aquarionics.com/misc/archives/blogite/0198.html3295 *3296 * @since 1.5.03297 *3298 * @param array $args Method parameters.3299 * @return array3300 */3301 function pingback_extensions_getPingbacks($args) {3302 3303 global $wpdb;3304 3305 do_action('xmlrpc_call', 'pingback.extensions.getPingbacks');3306 3307 $this->escape($args);3308 3309 $url = $args;3310 3311 $post_ID = url_to_postid($url);3312 if ( !$post_ID ) {3313 // We aren't sure that the resource is available and/or pingback enabled3314 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.'));3315 }3316 3317 $actual_post = wp_get_single_post($post_ID, ARRAY_A);3318 3319 if ( !$actual_post ) {3320 // No such post = resource not found3321 return new IXR_Error(32, __('The specified target URL does not exist.'));3322 }3323 3324 $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) );3325 3326 if ( !$comments )3327 return array();3328 3329 $pingbacks = array();3330 foreach ( $comments as $comment ) {3331 if ( 'pingback' == $comment->comment_type )3332 $pingbacks[] = $comment->comment_author_url;3333 }3334 3335 return $pingbacks;3336 }3337 }3338 3339 78 $wp_xmlrpc_server = new wp_xmlrpc_server(); 3340 79 $wp_xmlrpc_server->serve_request(); 3341 80 ?>