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