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