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