| [ Root ] [ Search ] [ Index ] |
PHP Cross Reference of WordPress 3.0Provided by Yoast |
[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); 937 if ( !$cat_id ) 938 return(new IXR_Error(500, __("Sorry, the new category failed."))); 939 940 return($cat_id); 941 } 942 943 /** 944 * Remove category. 945 * 946 * @since 2.5.0 947 * 948 * @param array $args Method parameters. 949 * @return mixed See {@link wp_delete_category()} for return info. 950 */ 951 function wp_deleteCategory($args) { 952 $this->escape($args); 953 954 $blog_id = (int) $args[0]; 955 $username = $args[1]; 956 $password = $args[2]; 957 $category_id = (int) $args[3]; 958 959 if ( !$user = $this->login($username, $password) ) 960 return $this->error; 961 962 do_action('xmlrpc_call', 'wp.deleteCategory'); 963 964 if ( !current_user_can("manage_categories") ) 965 return new IXR_Error( 401, __( "Sorry, you do not have the right to delete a category." ) ); 966 967 return wp_delete_category( $category_id ); 968 } 969 970 /** 971 * Retrieve category list. 972 * 973 * @since 2.2.0 974 * 975 * @param array $args Method parameters. 976 * @return array 977 */ 978 function wp_suggestCategories($args) { 979 $this->escape($args); 980 981 $blog_id = (int) $args[0]; 982 $username = $args[1]; 983 $password = $args[2]; 984 $category = $args[3]; 985 $max_results = (int) $args[4]; 986 987 if ( !$user = $this->login($username, $password) ) 988 return $this->error; 989 990 if ( !current_user_can( 'edit_posts' ) ) 991 return new IXR_Error( 401, __( 'Sorry, you must be able to edit posts to this site in order to view categories.' ) ); 992 993 do_action('xmlrpc_call', 'wp.suggestCategories'); 994 995 $category_suggestions = array(); 996 $args = array('get' => 'all', 'number' => $max_results, 'name__like' => $category); 997 foreach ( (array) get_categories($args) as $cat ) { 998 $category_suggestions[] = array( 999 "category_id" => $cat->cat_ID, 1000 "category_name" => $cat->cat_name 1001 ); 1002 } 1003 1004 return($category_suggestions); 1005 } 1006 1007 /** 1008 * Retrieve comment. 1009 * 1010 * @since 2.7.0 1011 * 1012 * @param array $args Method parameters. 1013 * @return array 1014 */ 1015 function wp_getComment($args) { 1016 $this->escape($args); 1017 1018 $blog_id = (int) $args[0]; 1019 $username = $args[1]; 1020 $password = $args[2]; 1021 $comment_id = (int) $args[3]; 1022 1023 if ( !$user = $this->login($username, $password) ) 1024 return $this->error; 1025 1026 if ( !current_user_can( 'moderate_comments' ) ) 1027 return new IXR_Error( 403, __( 'You are not allowed to moderate comments on this site.' ) ); 1028 1029 do_action('xmlrpc_call', 'wp.getComment'); 1030 1031 if ( ! $comment = get_comment($comment_id) ) 1032 return new IXR_Error( 404, __( 'Invalid comment ID.' ) ); 1033 1034 // Format page date. 1035 $comment_date = mysql2date("Ymd\TH:i:s", $comment->comment_date, false); 1036 $comment_date_gmt = mysql2date("Ymd\TH:i:s", $comment->comment_date_gmt, false); 1037 1038 if ( '0' == $comment->comment_approved ) 1039 $comment_status = 'hold'; 1040 else if ( 'spam' == $comment->comment_approved ) 1041 $comment_status = 'spam'; 1042 else if ( '1' == $comment->comment_approved ) 1043 $comment_status = 'approve'; 1044 else 1045 $comment_status = $comment->comment_approved; 1046 1047 $link = get_comment_link($comment); 1048 1049 $comment_struct = array( 1050 "date_created_gmt" => new IXR_Date($comment_date_gmt), 1051 "user_id" => $comment->user_id, 1052 "comment_id" => $comment->comment_ID, 1053 "parent" => $comment->comment_parent, 1054 "status" => $comment_status, 1055 "content" => $comment->comment_content, 1056 "link" => $link, 1057 "post_id" => $comment->comment_post_ID, 1058 "post_title" => get_the_title($comment->comment_post_ID), 1059 "author" => $comment->comment_author, 1060 "author_url" => $comment->comment_author_url, 1061 "author_email" => $comment->comment_author_email, 1062 "author_ip" => $comment->comment_author_IP, 1063 "type" => $comment->comment_type, 1064 ); 1065 1066 return $comment_struct; 1067 } 1068 1069 /** 1070 * Retrieve comments. 1071 * 1072 * @since 2.7.0 1073 * 1074 * @param array $args Method parameters. 1075 * @return array 1076 */ 1077 function wp_getComments($args) { 1078 $raw_args = $args; 1079 $this->escape($args); 1080 1081 $blog_id = (int) $args[0]; 1082 $username = $args[1]; 1083 $password = $args[2]; 1084 $struct = $args[3]; 1085 1086 if ( !$user = $this->login($username, $password) ) 1087 return $this->error; 1088 1089 if ( !current_user_can( 'moderate_comments' ) ) 1090 return new IXR_Error( 401, __( 'Sorry, you cannot edit comments.' ) ); 1091 1092 do_action('xmlrpc_call', 'wp.getComments'); 1093 1094 if ( isset($struct['status']) ) 1095 $status = $struct['status']; 1096 else 1097 $status = ''; 1098 1099 $post_id = ''; 1100 if ( isset($struct['post_id']) ) 1101 $post_id = absint($struct['post_id']); 1102 1103 $offset = 0; 1104 if ( isset($struct['offset']) ) 1105 $offset = absint($struct['offset']); 1106 1107 $number = 10; 1108 if ( isset($struct['number']) ) 1109 $number = absint($struct['number']); 1110 1111 $comments = get_comments( array('status' => $status, 'post_id' => $post_id, 'offset' => $offset, 'number' => $number ) ); 1112 $num_comments = count($comments); 1113 1114 if ( ! $num_comments ) 1115 return array(); 1116 1117 $comments_struct = array(); 1118 1119 for ( $i = 0; $i < $num_comments; $i++ ) { 1120 $comment = wp_xmlrpc_server::wp_getComment(array( 1121 $raw_args[0], $raw_args[1], $raw_args[2], $comments[$i]->comment_ID, 1122 )); 1123 $comments_struct[] = $comment; 1124 } 1125 1126 return $comments_struct; 1127 } 1128 1129 /** 1130 * Remove comment. 1131 * 1132 * @since 2.7.0 1133 * 1134 * @param array $args Method parameters. 1135 * @return mixed {@link wp_delete_comment()} 1136 */ 1137 function wp_deleteComment($args) { 1138 $this->escape($args); 1139 1140 $blog_id = (int) $args[0]; 1141 $username = $args[1]; 1142 $password = $args[2]; 1143 $comment_ID = (int) $args[3]; 1144 1145 if ( !$user = $this->login($username, $password) ) 1146 return $this->error; 1147 1148 if ( !current_user_can( 'moderate_comments' ) ) 1149 return new IXR_Error( 403, __( 'You are not allowed to moderate comments on this site.' ) ); 1150 1151 do_action('xmlrpc_call', 'wp.deleteComment'); 1152 1153 if ( ! get_comment($comment_ID) ) 1154 return new IXR_Error( 404, __( 'Invalid comment ID.' ) ); 1155 1156 return wp_delete_comment($comment_ID); 1157 } 1158 1159 /** 1160 * Edit comment. 1161 * 1162 * @since 2.7.0 1163 * 1164 * @param array $args Method parameters. 1165 * @return bool True, on success. 1166 */ 1167 function wp_editComment($args) { 1168 $this->escape($args); 1169 1170 $blog_id = (int) $args[0]; 1171 $username = $args[1]; 1172 $password = $args[2]; 1173 $comment_ID = (int) $args[3]; 1174 $content_struct = $args[4]; 1175 1176 if ( !$user = $this->login($username, $password) ) 1177 return $this->error; 1178 1179 if ( !current_user_can( 'moderate_comments' ) ) 1180 return new IXR_Error( 403, __( 'You are not allowed to moderate comments on this site.' ) ); 1181 1182 do_action('xmlrpc_call', 'wp.editComment'); 1183 1184 if ( ! get_comment($comment_ID) ) 1185 return new IXR_Error( 404, __( 'Invalid comment ID.' ) ); 1186 1187 if ( isset($content_struct['status']) ) { 1188 $statuses = get_comment_statuses(); 1189 $statuses = array_keys($statuses); 1190 1191 if ( ! in_array($content_struct['status'], $statuses) ) 1192 return new IXR_Error( 401, __( 'Invalid comment status.' ) ); 1193 $comment_approved = $content_struct['status']; 1194 } 1195 1196 // Do some timestamp voodoo 1197 if ( !empty( $content_struct['date_created_gmt'] ) ) { 1198 $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 1199 $comment_date = get_date_from_gmt(iso8601_to_datetime($dateCreated)); 1200 $comment_date_gmt = iso8601_to_datetime($dateCreated, GMT); 1201 } 1202 1203 if ( isset($content_struct['content']) ) 1204 $comment_content = $content_struct['content']; 1205 1206 if ( isset($content_struct['author']) ) 1207 $comment_author = $content_struct['author']; 1208 1209 if ( isset($content_struct['author_url']) ) 1210 $comment_author_url = $content_struct['author_url']; 1211 1212 if ( isset($content_struct['author_email']) ) 1213 $comment_author_email = $content_struct['author_email']; 1214 1215 // We've got all the data -- post it: 1216 $comment = compact('comment_ID', 'comment_content', 'comment_approved', 'comment_date', 'comment_date_gmt', 'comment_author', 'comment_author_email', 'comment_author_url'); 1217 1218 $result = wp_update_comment($comment); 1219 if ( is_wp_error( $result ) ) 1220 return new IXR_Error(500, $result->get_error_message()); 1221 1222 if ( !$result ) 1223 return new IXR_Error(500, __('Sorry, the comment could not be edited. Something wrong happened.')); 1224 1225 return true; 1226 } 1227 1228 /** 1229 * Create new comment. 1230 * 1231 * @since 2.7.0 1232 * 1233 * @param array $args Method parameters. 1234 * @return mixed {@link wp_new_comment()} 1235 */ 1236 function wp_newComment($args) { 1237 global $wpdb; 1238 1239 $this->escape($args); 1240 1241 $blog_id = (int) $args[0]; 1242 $username = $args[1]; 1243 $password = $args[2]; 1244 $post = $args[3]; 1245 $content_struct = $args[4]; 1246 1247 $allow_anon = apply_filters('xmlrpc_allow_anonymous_comments', false); 1248 1249 $user = $this->login($username, $password); 1250 1251 if ( !$user ) { 1252 $logged_in = false; 1253 if ( $allow_anon && get_option('comment_registration') ) 1254 return new IXR_Error( 403, __( 'You must be registered to comment' ) ); 1255 else if ( !$allow_anon ) 1256 return $this->error; 1257 } else { 1258 $logged_in = true; 1259 } 1260 1261 if ( is_numeric($post) ) 1262 $post_id = absint($post); 1263 else 1264 $post_id = url_to_postid($post); 1265 1266 if ( ! $post_id ) 1267 return new IXR_Error( 404, __( 'Invalid post ID.' ) ); 1268 1269 if ( ! get_post($post_id) ) 1270 return new IXR_Error( 404, __( 'Invalid post ID.' ) ); 1271 1272 $comment['comment_post_ID'] = $post_id; 1273 1274 if ( $logged_in ) { 1275 $comment['comment_author'] = $wpdb->escape( $user->display_name ); 1276 $comment['comment_author_email'] = $wpdb->escape( $user->user_email ); 1277 $comment['comment_author_url'] = $wpdb->escape( $user->user_url ); 1278 $comment['user_ID'] = $user->ID; 1279 } else { 1280 $comment['comment_author'] = ''; 1281 if ( isset($content_struct['author']) ) 1282 $comment['comment_author'] = $content_struct['author']; 1283 1284 $comment['comment_author_email'] = ''; 1285 if ( isset($content_struct['author_email']) ) 1286 $comment['comment_author_email'] = $content_struct['author_email']; 1287 1288 $comment['comment_author_url'] = ''; 1289 if ( isset($content_struct['author_url']) ) 1290 $comment['comment_author_url'] = $content_struct['author_url']; 1291 1292 $comment['user_ID'] = 0; 1293 1294 if ( get_option('require_name_email') ) { 1295 if ( 6 > strlen($comment['comment_author_email']) || '' == $comment['comment_author'] ) 1296 return new IXR_Error( 403, __( 'Comment author name and email are required' ) ); 1297 elseif ( !is_email($comment['comment_author_email']) ) 1298 return new IXR_Error( 403, __( 'A valid email address is required' ) ); 1299 } 1300 } 1301 1302 $comment['comment_parent'] = isset($content_struct['comment_parent']) ? absint($content_struct['comment_parent']) : 0; 1303 1304 $comment['comment_content'] = $content_struct['content']; 1305 1306 do_action('xmlrpc_call', 'wp.newComment'); 1307 1308 return wp_new_comment($comment); 1309 } 1310 1311 /** 1312 * Retrieve all of the comment status. 1313 * 1314 * @since 2.7.0 1315 * 1316 * @param array $args Method parameters. 1317 * @return array 1318 */ 1319 function wp_getCommentStatusList($args) { 1320 $this->escape( $args ); 1321 1322 $blog_id = (int) $args[0]; 1323 $username = $args[1]; 1324 $password = $args[2]; 1325 1326 if ( !$user = $this->login($username, $password) ) 1327 return $this->error; 1328 1329 if ( !current_user_can( 'moderate_comments' ) ) 1330 return new IXR_Error( 403, __( 'You are not allowed access to details about this site.' ) ); 1331 1332 do_action('xmlrpc_call', 'wp.getCommentStatusList'); 1333 1334 return get_comment_statuses( ); 1335 } 1336 1337 /** 1338 * Retrieve comment count. 1339 * 1340 * @since 2.5.0 1341 * 1342 * @param array $args Method parameters. 1343 * @return array 1344 */ 1345 function wp_getCommentCount( $args ) { 1346 $this->escape($args); 1347 1348 $blog_id = (int) $args[0]; 1349 $username = $args[1]; 1350 $password = $args[2]; 1351 $post_id = (int) $args[3]; 1352 1353 if ( !$user = $this->login($username, $password) ) 1354 return $this->error; 1355 1356 if ( !current_user_can( 'edit_posts' ) ) 1357 return new IXR_Error( 403, __( 'You are not allowed access to details about comments.' ) ); 1358 1359 do_action('xmlrpc_call', 'wp.getCommentCount'); 1360 1361 $count = wp_count_comments( $post_id ); 1362 return array( 1363 "approved" => $count->approved, 1364 "awaiting_moderation" => $count->moderated, 1365 "spam" => $count->spam, 1366 "total_comments" => $count->total_comments 1367 ); 1368 } 1369 1370 /** 1371 * Retrieve post statuses. 1372 * 1373 * @since 2.5.0 1374 * 1375 * @param array $args Method parameters. 1376 * @return array 1377 */ 1378 function wp_getPostStatusList( $args ) { 1379 $this->escape( $args ); 1380 1381 $blog_id = (int) $args[0]; 1382 $username = $args[1]; 1383 $password = $args[2]; 1384 1385 if ( !$user = $this->login($username, $password) ) 1386 return $this->error; 1387 1388 if ( !current_user_can( 'edit_posts' ) ) 1389 return new IXR_Error( 403, __( 'You are not allowed access to details about this site.' ) ); 1390 1391 do_action('xmlrpc_call', 'wp.getPostStatusList'); 1392 1393 return get_post_statuses( ); 1394 } 1395 1396 /** 1397 * Retrieve page statuses. 1398 * 1399 * @since 2.5.0 1400 * 1401 * @param array $args Method parameters. 1402 * @return array 1403 */ 1404 function wp_getPageStatusList( $args ) { 1405 $this->escape( $args ); 1406 1407 $blog_id = (int) $args[0]; 1408 $username = $args[1]; 1409 $password = $args[2]; 1410 1411 if ( !$user = $this->login($username, $password) ) 1412 return $this->error; 1413 1414 if ( !current_user_can( 'edit_posts' ) ) 1415 return new IXR_Error( 403, __( 'You are not allowed access to details about this site.' ) ); 1416 1417 do_action('xmlrpc_call', 'wp.getPageStatusList'); 1418 1419 return get_page_statuses( ); 1420 } 1421 1422 /** 1423 * Retrieve page templates. 1424 * 1425 * @since 2.6.0 1426 * 1427 * @param array $args Method parameters. 1428 * @return array 1429 */ 1430 function wp_getPageTemplates( $args ) { 1431 $this->escape( $args ); 1432 1433 $blog_id = (int) $args[0]; 1434 $username = $args[1]; 1435 $password = $args[2]; 1436 1437 if ( !$user = $this->login($username, $password) ) 1438 return $this->error; 1439 1440 if ( !current_user_can( 'edit_pages' ) ) 1441 return new IXR_Error( 403, __( 'You are not allowed access to details about this site.' ) ); 1442 1443 $templates = get_page_templates( ); 1444 $templates['Default'] = 'default'; 1445 1446 return $templates; 1447 } 1448 1449 /** 1450 * Retrieve blog options. 1451 * 1452 * @since 2.6.0 1453 * 1454 * @param array $args Method parameters. 1455 * @return array 1456 */ 1457 function wp_getOptions( $args ) { 1458 $this->escape( $args ); 1459 1460 $blog_id = (int) $args[0]; 1461 $username = $args[1]; 1462 $password = $args[2]; 1463 $options = (array) $args[3]; 1464 1465 if ( !$user = $this->login($username, $password) ) 1466 return $this->error; 1467 1468 // If no specific options where asked for, return all of them 1469 if ( count( $options ) == 0 ) 1470 $options = array_keys($this->blog_options); 1471 1472 return $this->_getOptions($options); 1473 } 1474 1475 /** 1476 * Retrieve blog options value from list. 1477 * 1478 * @since 2.6.0 1479 * 1480 * @param array $options Options to retrieve. 1481 * @return array 1482 */ 1483 function _getOptions($options) { 1484 $data = array( ); 1485 foreach ( $options as $option ) { 1486 if ( array_key_exists( $option, $this->blog_options ) ) { 1487 $data[$option] = $this->blog_options[$option]; 1488 //Is the value static or dynamic? 1489 if ( isset( $data[$option]['option'] ) ) { 1490 $data[$option]['value'] = get_option( $data[$option]['option'] ); 1491 unset($data[$option]['option']); 1492 } 1493 } 1494 } 1495 1496 return $data; 1497 } 1498 1499 /** 1500 * Update blog options. 1501 * 1502 * @since 2.6.0 1503 * 1504 * @param array $args Method parameters. 1505 * @return unknown 1506 */ 1507 function wp_setOptions( $args ) { 1508 $this->escape( $args ); 1509 1510 $blog_id = (int) $args[0]; 1511 $username = $args[1]; 1512 $password = $args[2]; 1513 $options = (array) $args[3]; 1514 1515 if ( !$user = $this->login($username, $password) ) 1516 return $this->error; 1517 1518 if ( !current_user_can( 'manage_options' ) ) 1519 return new IXR_Error( 403, __( 'You are not allowed to update options.' ) ); 1520 1521 foreach ( $options as $o_name => $o_value ) { 1522 $option_names[] = $o_name; 1523 if ( !array_key_exists( $o_name, $this->blog_options ) ) 1524 continue; 1525 1526 if ( $this->blog_options[$o_name]['readonly'] == true ) 1527 continue; 1528 1529 update_option( $this->blog_options[$o_name]['option'], $o_value ); 1530 } 1531 1532 //Now return the updated values 1533 return $this->_getOptions($option_names); 1534 } 1535 1536 /* Blogger API functions. 1537 * specs on http://plant.blogger.com/api and http://groups.yahoo.com/group/bloggerDev/ 1538 */ 1539 1540 /** 1541 * Retrieve blogs that user owns. 1542 * 1543 * Will make more sense once we support multiple blogs. 1544 * 1545 * @since 1.5.0 1546 * 1547 * @param array $args Method parameters. 1548 * @return array 1549 */ 1550 function blogger_getUsersBlogs($args) { 1551 if ( is_multisite() ) 1552 return $this->_multisite_getUsersBlogs($args); 1553 1554 $this->escape($args); 1555 1556 $username = $args[1]; 1557 $password = $args[2]; 1558 1559 if ( !$user = $this->login($username, $password) ) 1560 return $this->error; 1561 1562 do_action('xmlrpc_call', 'blogger.getUsersBlogs'); 1563 1564 $is_admin = current_user_can('manage_options'); 1565 1566 $struct = array( 1567 'isAdmin' => $is_admin, 1568 'url' => get_option('home') . '/', 1569 'blogid' => '1', 1570 'blogName' => get_option('blogname'), 1571 'xmlrpc' => site_url( 'xmlrpc.php' ) 1572 ); 1573 1574 return array($struct); 1575 } 1576 1577 /** 1578 * Private function for retrieving a users blogs for multisite setups 1579 * 1580 * @access protected 1581 */ 1582 function _multisite_getUsersBlogs($args) { 1583 global $current_blog; 1584 $domain = $current_blog->domain; 1585 $path = $current_blog->path . 'xmlrpc.php'; 1586 $protocol = is_ssl() ? 'https' : 'http'; 1587 1588 $rpc = new IXR_Client("$protocol://{$domain}{$path}"); 1589 $rpc->query('wp.getUsersBlogs', $args[1], $args[2]); 1590 $blogs = $rpc->getResponse(); 1591 1592 if ( isset($blogs['faultCode']) ) 1593 return new IXR_Error($blogs['faultCode'], $blogs['faultString']); 1594 1595 if ( $_SERVER['HTTP_HOST'] == $domain && $_SERVER['REQUEST_URI'] == $path ) { 1596 return $blogs; 1597 } else { 1598 foreach ( (array) $blogs as $blog ) { 1599 if ( strpos($blog['url'], $_SERVER['HTTP_HOST']) ) 1600 return array($blog); 1601 } 1602 return array(); 1603 } 1604 } 1605 1606 /** 1607 * Retrieve user's data. 1608 * 1609 * Gives your client some info about you, so you don't have to. 1610 * 1611 * @since 1.5.0 1612 * 1613 * @param array $args Method parameters. 1614 * @return array 1615 */ 1616 function blogger_getUserInfo($args) { 1617 1618 $this->escape($args); 1619 1620 $username = $args[1]; 1621 $password = $args[2]; 1622 1623 if ( !$user = $this->login($username, $password) ) 1624 return $this->error; 1625 1626 if ( !current_user_can( 'edit_posts' ) ) 1627 return new IXR_Error( 401, __( 'Sorry, you do not have access to user data on this site.' ) ); 1628 1629 do_action('xmlrpc_call', 'blogger.getUserInfo'); 1630 1631 $struct = array( 1632 'nickname' => $user->nickname, 1633 'userid' => $user->ID, 1634 'url' => $user->user_url, 1635 'lastname' => $user->last_name, 1636 'firstname' => $user->first_name 1637 ); 1638 1639 return $struct; 1640 } 1641 1642 /** 1643 * Retrieve post. 1644 * 1645 * @since 1.5.0 1646 * 1647 * @param array $args Method parameters. 1648 * @return array 1649 */ 1650 function blogger_getPost($args) { 1651 1652 $this->escape($args); 1653 1654 $post_ID = (int) $args[1]; 1655 $username = $args[2]; 1656 $password = $args[3]; 1657 1658 if ( !$user = $this->login($username, $password) ) 1659 return $this->error; 1660 1661 if ( !current_user_can( 'edit_post', $post_ID ) ) 1662 return new IXR_Error( 401, __( 'Sorry, you cannot edit this post.' ) ); 1663 1664 do_action('xmlrpc_call', 'blogger.getPost'); 1665 1666 $post_data = wp_get_single_post($post_ID, ARRAY_A); 1667 1668 $categories = implode(',', wp_get_post_categories($post_ID)); 1669 1670 $content = '<title>'.stripslashes($post_data['post_title']).'</title>'; 1671 $content .= '<category>'.$categories.'</category>'; 1672 $content .= stripslashes($post_data['post_content']); 1673 1674 $struct = array( 1675 'userid' => $post_data['post_author'], 1676 'dateCreated' => new IXR_Date(mysql2date('Ymd\TH:i:s', $post_data['post_date'], false)), 1677 'content' => $content, 1678 'postid' => $post_data['ID'] 1679 ); 1680 1681 return $struct; 1682 } 1683 1684 /** 1685 * Retrieve list of recent posts. 1686 * 1687 * @since 1.5.0 1688 * 1689 * @param array $args Method parameters. 1690 * @return array 1691 */ 1692 function blogger_getRecentPosts($args) { 1693 1694 $this->escape($args); 1695 1696 $blog_ID = (int) $args[1]; /* though we don't use it yet */ 1697 $username = $args[2]; 1698 $password = $args[3]; 1699 $num_posts = $args[4]; 1700 1701 if ( !$user = $this->login($username, $password) ) 1702 return $this->error; 1703 1704 do_action('xmlrpc_call', 'blogger.getRecentPosts'); 1705 1706 $posts_list = wp_get_recent_posts($num_posts); 1707 1708 if ( !$posts_list ) { 1709 $this->error = new IXR_Error(500, __('Either there are no posts, or something went wrong.')); 1710 return $this->error; 1711 } 1712 1713 foreach ($posts_list as $entry) { 1714 if ( !current_user_can( 'edit_post', $entry['ID'] ) ) 1715 continue; 1716 1717 $post_date = mysql2date('Ymd\TH:i:s', $entry['post_date'], false); 1718 $categories = implode(',', wp_get_post_categories($entry['ID'])); 1719 1720 $content = '<title>'.stripslashes($entry['post_title']).'</title>'; 1721 $content .= '<category>'.$categories.'</category>'; 1722 $content .= stripslashes($entry['post_content']); 1723 1724 $struct[] = array( 1725 'userid' => $entry['post_author'], 1726 'dateCreated' => new IXR_Date($post_date), 1727 'content' => $content, 1728 'postid' => $entry['ID'], 1729 ); 1730 1731 } 1732 1733 $recent_posts = array(); 1734 for ( $j=0; $j<count($struct); $j++ ) { 1735 array_push($recent_posts, $struct[$j]); 1736 } 1737 1738 return $recent_posts; 1739 } 1740 1741 /** 1742 * Retrieve blog_filename content. 1743 * 1744 * @since 1.5.0 1745 * 1746 * @param array $args Method parameters. 1747 * @return string 1748 */ 1749 function blogger_getTemplate($args) { 1750 1751 $this->escape($args); 1752 1753 $blog_ID = (int) $args[1]; 1754 $username = $args[2]; 1755 $password = $args[3]; 1756 $template = $args[4]; /* could be 'main' or 'archiveIndex', but we don't use it */ 1757 1758 if ( !$user = $this->login($username, $password) ) 1759 return $this->error; 1760 1761 do_action('xmlrpc_call', 'blogger.getTemplate'); 1762 1763 if ( !current_user_can('edit_themes') ) 1764 return new IXR_Error(401, __('Sorry, this user can not edit the template.')); 1765 1766 /* warning: here we make the assumption that the blog's URL is on the same server */ 1767 $filename = get_option('home') . '/'; 1768 $filename = preg_replace('#https?://.+?/#', $_SERVER['DOCUMENT_ROOT'].'/', $filename); 1769 1770 $f = fopen($filename, 'r'); 1771 $content = fread($f, filesize($filename)); 1772 fclose($f); 1773 1774 /* so it is actually editable with a windows/mac client */ 1775 // 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); 1776 1777 return $content; 1778 } 1779 1780 /** 1781 * Updates the content of blog_filename. 1782 * 1783 * @since 1.5.0 1784 * 1785 * @param array $args Method parameters. 1786 * @return bool True when done. 1787 */ 1788 function blogger_setTemplate($args) { 1789 1790 $this->escape($args); 1791 1792 $blog_ID = (int) $args[1]; 1793 $username = $args[2]; 1794 $password = $args[3]; 1795 $content = $args[4]; 1796 $template = $args[5]; /* could be 'main' or 'archiveIndex', but we don't use it */ 1797 1798 if ( !$user = $this->login($username, $password) ) 1799 return $this->error; 1800 1801 do_action('xmlrpc_call', 'blogger.setTemplate'); 1802 1803 if ( !current_user_can('edit_themes') ) 1804 return new IXR_Error(401, __('Sorry, this user cannot edit the template.')); 1805 1806 /* warning: here we make the assumption that the blog's URL is on the same server */ 1807 $filename = get_option('home') . '/'; 1808 $filename = preg_replace('#https?://.+?/#', $_SERVER['DOCUMENT_ROOT'].'/', $filename); 1809 1810 if ($f = fopen($filename, 'w+')) { 1811 fwrite($f, $content); 1812 fclose($f); 1813 } else { 1814 return new IXR_Error(500, __('Either the file is not writable, or something wrong happened. The file has not been updated.')); 1815 } 1816 1817 return true; 1818 } 1819 1820 /** 1821 * Create new post. 1822 * 1823 * @since 1.5.0 1824 * 1825 * @param array $args Method parameters. 1826 * @return int 1827 */ 1828 function blogger_newPost($args) { 1829 1830 $this->escape($args); 1831 1832 $blog_ID = (int) $args[1]; /* though we don't use it yet */ 1833 $username = $args[2]; 1834 $password = $args[3]; 1835 $content = $args[4]; 1836 $publish = $args[5]; 1837 1838 if ( !$user = $this->login($username, $password) ) 1839 return $this->error; 1840 1841 do_action('xmlrpc_call', 'blogger.newPost'); 1842 1843 $cap = ($publish) ? 'publish_posts' : 'edit_posts'; 1844 if ( !current_user_can($cap) ) 1845 return new IXR_Error(401, __('Sorry, you are not allowed to post on this site.')); 1846 1847 $post_status = ($publish) ? 'publish' : 'draft'; 1848 1849 $post_author = $user->ID; 1850 1851 $post_title = xmlrpc_getposttitle($content); 1852 $post_category = xmlrpc_getpostcategory($content); 1853 $post_content = xmlrpc_removepostdata($content); 1854 1855 $post_date = current_time('mysql'); 1856 $post_date_gmt = current_time('mysql', 1); 1857 1858 $post_data = compact('blog_ID', 'post_author', 'post_date', 'post_date_gmt', 'post_content', 'post_title', 'post_category', 'post_status'); 1859 1860 $post_ID = wp_insert_post($post_data); 1861 if ( is_wp_error( $post_ID ) ) 1862 return new IXR_Error(500, $post_ID->get_error_message()); 1863 1864 if ( !$post_ID ) 1865 return new IXR_Error(500, __('Sorry, your entry could not be posted. Something wrong happened.')); 1866 1867 $this->attach_uploads( $post_ID, $post_content ); 1868 1869 logIO('O', "Posted ! ID: $post_ID"); 1870 1871 return $post_ID; 1872 } 1873 1874 /** 1875 * Edit a post. 1876 * 1877 * @since 1.5.0 1878 * 1879 * @param array $args Method parameters. 1880 * @return bool true when done. 1881 */ 1882 function blogger_editPost($args) { 1883 1884 $this->escape($args); 1885 1886 $post_ID = (int) $args[1]; 1887 $username = $args[2]; 1888 $password = $args[3]; 1889 $content = $args[4]; 1890 $publish = $args[5]; 1891 1892 if ( !$user = $this->login($username, $password) ) 1893 return $this->error; 1894 1895 do_action('xmlrpc_call', 'blogger.editPost'); 1896 1897 $actual_post = wp_get_single_post($post_ID,ARRAY_A); 1898 1899 if ( !$actual_post || $actual_post['post_type'] != 'post' ) 1900 return new IXR_Error(404, __('Sorry, no such post.')); 1901 1902 $this->escape($actual_post); 1903 1904 if ( !current_user_can('edit_post', $post_ID) ) 1905 return new IXR_Error(401, __('Sorry, you do not have the right to edit this post.')); 1906 1907 extract($actual_post, EXTR_SKIP); 1908 1909 if ( ('publish' == $post_status) && !current_user_can('publish_posts') ) 1910 return new IXR_Error(401, __('Sorry, you do not have the right to publish this post.')); 1911 1912 $post_title = xmlrpc_getposttitle($content); 1913 $post_category = xmlrpc_getpostcategory($content); 1914 $post_content = xmlrpc_removepostdata($content); 1915 1916 $postdata = compact('ID', 'post_content', 'post_title', 'post_category', 'post_status', 'post_excerpt'); 1917 1918 $result = wp_update_post($postdata); 1919 1920 if ( !$result ) 1921 return new IXR_Error(500, __('For some strange yet very annoying reason, this post could not be edited.')); 1922 1923 $this->attach_uploads( $ID, $post_content ); 1924 1925 return true; 1926 } 1927 1928 /** 1929 * Remove a post. 1930 * 1931 * @since 1.5.0 1932 * 1933 * @param array $args Method parameters. 1934 * @return bool True when post is deleted. 1935 */ 1936 function blogger_deletePost($args) { 1937 $this->escape($args); 1938 1939 $post_ID = (int) $args[1]; 1940 $username = $args[2]; 1941 $password = $args[3]; 1942 $publish = $args[4]; 1943 1944 if ( !$user = $this->login($username, $password) ) 1945 return $this->error; 1946 1947 do_action('xmlrpc_call', 'blogger.deletePost'); 1948 1949 $actual_post = wp_get_single_post($post_ID,ARRAY_A); 1950 1951 if ( !$actual_post || $actual_post['post_type'] != 'post' ) 1952 return new IXR_Error(404, __('Sorry, no such post.')); 1953 1954 if ( !current_user_can('edit_post', $post_ID) ) 1955 return new IXR_Error(401, __('Sorry, you do not have the right to delete this post.')); 1956 1957 $result = wp_delete_post($post_ID); 1958 1959 if ( !$result ) 1960 return new IXR_Error(500, __('For some strange yet very annoying reason, this post could not be deleted.')); 1961 1962 return true; 1963 } 1964 1965 /* MetaWeblog API functions 1966 * specs on wherever Dave Winer wants them to be 1967 */ 1968 1969 /** 1970 * Create a new post. 1971 * 1972 * @since 1.5.0 1973 * 1974 * @param array $args Method parameters. 1975 * @return int 1976 */ 1977 function mw_newPost($args) { 1978 $this->escape($args); 1979 1980 $blog_ID = (int) $args[0]; // we will support this in the near future 1981 $username = $args[1]; 1982 $password = $args[2]; 1983 $content_struct = $args[3]; 1984 $publish = $args[4]; 1985 1986 if ( !$user = $this->login($username, $password) ) 1987 return $this->error; 1988 1989 do_action('xmlrpc_call', 'metaWeblog.newPost'); 1990 1991 $cap = ( $publish ) ? 'publish_posts' : 'edit_posts'; 1992 $error_message = __( 'Sorry, you are not allowed to publish posts on this site.' ); 1993 $post_type = 'post'; 1994 $page_template = ''; 1995 if ( !empty( $content_struct['post_type'] ) ) { 1996 if ( $content_struct['post_type'] == 'page' ) { 1997 $cap = ( $publish ) ? 'publish_pages' : 'edit_pages'; 1998 $error_message = __( 'Sorry, you are not allowed to publish pages on this site.' ); 1999 $post_type = 'page'; 2000 if ( !empty( $content_struct['wp_page_template'] ) ) 2001 $page_template = $content_struct['wp_page_template']; 2002 } elseif ( $content_struct['post_type'] == 'post' ) { 2003 // This is the default, no changes needed 2004 } else { 2005 // No other post_type values are allowed here 2006 return new IXR_Error( 401, __( 'Invalid post type.' ) ); 2007 } 2008 } 2009 2010 if ( !current_user_can( $cap ) ) 2011 return new IXR_Error( 401, $error_message ); 2012 2013 // Let WordPress generate the post_name (slug) unless 2014 // one has been provided. 2015 $post_name = ""; 2016 if ( isset($content_struct["wp_slug"]) ) 2017 $post_name = $content_struct["wp_slug"]; 2018 2019 // Only use a password if one was given. 2020 if ( isset($content_struct["wp_password"]) ) 2021 $post_password = $content_struct["wp_password"]; 2022 2023 // Only set a post parent if one was provided. 2024 if ( isset($content_struct["wp_page_parent_id"]) ) 2025 $post_parent = $content_struct["wp_page_parent_id"]; 2026 2027 // Only set the menu_order if it was provided. 2028 if ( isset($content_struct["wp_page_order"]) ) 2029 $menu_order = $content_struct["wp_page_order"]; 2030 2031 $post_author = $user->ID; 2032 2033 // If an author id was provided then use it instead. 2034 if ( isset($content_struct["wp_author_id"]) && ($user->ID != $content_struct["wp_author_id"]) ) { 2035 switch ( $post_type ) { 2036 case "post": 2037 if ( !current_user_can("edit_others_posts") ) 2038 return(new IXR_Error(401, __("You are not allowed to post as this user"))); 2039 break; 2040 case "page": 2041 if ( !current_user_can("edit_others_pages") ) 2042 return(new IXR_Error(401, __("You are not allowed to create pages as this user"))); 2043 break; 2044 default: 2045 return(new IXR_Error(401, __("Invalid post type."))); 2046 break; 2047 } 2048 $post_author = $content_struct["wp_author_id"]; 2049 } 2050 2051 $post_title = $content_struct['title']; 2052 $post_content = $content_struct['description']; 2053 2054 $post_status = $publish ? 'publish' : 'draft'; 2055 2056 if ( isset( $content_struct["{$post_type}_status"] ) ) { 2057 switch ( $content_struct["{$post_type}_status"] ) { 2058 case 'draft': 2059 case 'private': 2060 case 'publish': 2061 $post_status = $content_struct["{$post_type}_status"]; 2062 break; 2063 case 'pending': 2064 // Pending is only valid for posts, not pages. 2065 if ( $post_type === 'post' ) 2066 $post_status = $content_struct["{$post_type}_status"]; 2067 break; 2068 default: 2069 $post_status = $publish ? 'publish' : 'draft'; 2070 break; 2071 } 2072 } 2073 2074 $post_excerpt = $content_struct['mt_excerpt']; 2075 $post_more = $content_struct['mt_text_more']; 2076 2077 $tags_input = $content_struct['mt_keywords']; 2078 2079 if ( isset($content_struct["mt_allow_comments"]) ) { 2080 if ( !is_numeric($content_struct["mt_allow_comments"]) ) { 2081 switch ( $content_struct["mt_allow_comments"] ) { 2082 case "closed": 2083 $comment_status = "closed"; 2084 break; 2085 case "open": 2086 $comment_status = "open"; 2087 break; 2088 default: 2089 $comment_status = get_option("default_comment_status"); 2090 break; 2091 } 2092 } else { 2093 switch ( (int) $content_struct["mt_allow_comments"] ) { 2094 case 0: 2095 case 2: 2096 $comment_status = "closed"; 2097 break; 2098 case 1: 2099 $comment_status = "open"; 2100 break; 2101 default: 2102 $comment_status = get_option("default_comment_status"); 2103 break; 2104 } 2105 } 2106 } else { 2107 $comment_status = get_option("default_comment_status"); 2108 } 2109 2110 if ( isset($content_struct["mt_allow_pings"]) ) { 2111 if ( !is_numeric($content_struct["mt_allow_pings"]) ) { 2112 switch ( $content_struct['mt_allow_pings'] ) { 2113 case "closed": 2114 $ping_status = "closed"; 2115 break; 2116 case "open": 2117 $ping_status = "open"; 2118 break; 2119 default: 2120 $ping_status = get_option("default_ping_status"); 2121 break; 2122 } 2123 } else { 2124 switch ( (int) $content_struct["mt_allow_pings"] ) { 2125 case 0: 2126 $ping_status = "closed"; 2127 break; 2128 case 1: 2129 $ping_status = "open"; 2130 break; 2131 default: 2132 $ping_status = get_option("default_ping_status"); 2133 break; 2134 } 2135 } 2136 } else { 2137 $ping_status = get_option("default_ping_status"); 2138 } 2139 2140 if ( $post_more ) 2141 $post_content = $post_content . "<!--more-->" . $post_more; 2142 2143 $to_ping = $content_struct['mt_tb_ping_urls']; 2144 if ( is_array($to_ping) ) 2145 $to_ping = implode(' ', $to_ping); 2146 2147 // Do some timestamp voodoo 2148 if ( !empty( $content_struct['date_created_gmt'] ) ) 2149 $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 2150 elseif ( !empty( $content_struct['dateCreated']) ) 2151 $dateCreated = $content_struct['dateCreated']->getIso(); 2152 2153 if ( !empty( $dateCreated ) ) { 2154 $post_date = get_date_from_gmt(iso8601_to_datetime($dateCreated)); 2155 $post_date_gmt = iso8601_to_datetime($dateCreated, GMT); 2156 } else { 2157 $post_date = current_time('mysql'); 2158 $post_date_gmt = current_time('mysql', 1); 2159 } 2160 2161 $catnames = $content_struct['categories']; 2162 logIO('O', 'Post cats: ' . var_export($catnames,true)); 2163 $post_category = array(); 2164 2165 if ( is_array($catnames) ) { 2166 foreach ($catnames as $cat) { 2167 $post_category[] = get_cat_ID($cat); 2168 } 2169 } 2170 2171 // We've got all the data -- post it: 2172 $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'); 2173 2174 $post_ID = wp_insert_post($postdata, true); 2175 if ( is_wp_error( $post_ID ) ) 2176 return new IXR_Error(500, $post_ID->get_error_message()); 2177 2178 if ( !$post_ID ) 2179 return new IXR_Error(500, __('Sorry, your entry could not be posted. Something wrong happened.')); 2180 2181 // Only posts can be sticky 2182 if ( $post_type == 'post' && isset( $content_struct['sticky'] ) ) { 2183 if ( $content_struct['sticky'] == true ) 2184 stick_post( $post_ID ); 2185 elseif ( $content_struct['sticky'] == false ) 2186 unstick_post( $post_ID ); 2187 } 2188 2189 if ( isset($content_struct['custom_fields']) ) 2190 $this->set_custom_fields($post_ID, $content_struct['custom_fields']); 2191 2192 // Handle enclosures 2193 $this->add_enclosure_if_new($post_ID, $content_struct['enclosure']); 2194 2195 $this->attach_uploads( $post_ID, $post_content ); 2196 2197 logIO('O', "Posted ! ID: $post_ID"); 2198 2199 return strval($post_ID); 2200 } 2201 2202 function add_enclosure_if_new($post_ID, $enclosure) { 2203 if ( is_array( $enclosure ) && isset( $enclosure['url'] ) && isset( $enclosure['length'] ) && isset( $enclosure['type'] ) ) { 2204 2205 $encstring = $enclosure['url'] . "\n" . $enclosure['length'] . "\n" . $enclosure['type']; 2206 $found = false; 2207 foreach ( (array) get_post_custom($post_ID) as $key => $val) { 2208 if ($key == 'enclosure') { 2209 foreach ( (array) $val as $enc ) { 2210 if ($enc == $encstring) { 2211 $found = true; 2212 break 2; 2213 } 2214 } 2215 } 2216 } 2217 if (!$found) 2218 add_post_meta( $post_ID, 'enclosure', $encstring ); 2219 } 2220 } 2221 2222 /** 2223 * Attach upload to a post. 2224 * 2225 * @since 2.1.0 2226 * 2227 * @param int $post_ID Post ID. 2228 * @param string $post_content Post Content for attachment. 2229 */ 2230 function attach_uploads( $post_ID, $post_content ) { 2231 global $wpdb; 2232 2233 // find any unattached files 2234 $attachments = $wpdb->get_results( "SELECT ID, guid FROM {$wpdb->posts} WHERE post_parent = '0' AND post_type = 'attachment'" ); 2235 if ( is_array( $attachments ) ) { 2236 foreach ( $attachments as $file ) { 2237 if ( strpos( $post_content, $file->guid ) !== false ) 2238 $wpdb->update($wpdb->posts, array('post_parent' => $post_ID), array('ID' => $file->ID) ); 2239 } 2240 } 2241 } 2242 2243 /** 2244 * Edit a post. 2245 * 2246 * @since 1.5.0 2247 * 2248 * @param array $args Method parameters. 2249 * @return bool True on success. 2250 */ 2251 function mw_editPost($args) { 2252 2253 $this->escape($args); 2254 2255 $post_ID = (int) $args[0]; 2256 $username = $args[1]; 2257 $password = $args[2]; 2258 $content_struct = $args[3]; 2259 $publish = $args[4]; 2260 2261 if ( !$user = $this->login($username, $password) ) 2262 return $this->error; 2263 2264 do_action('xmlrpc_call', 'metaWeblog.editPost'); 2265 2266 $cap = ( $publish ) ? 'publish_posts' : 'edit_posts'; 2267 $error_message = __( 'Sorry, you are not allowed to publish posts on this site.' ); 2268 $post_type = 'post'; 2269 $page_template = ''; 2270 if ( !empty( $content_struct['post_type'] ) ) { 2271 if ( $content_struct['post_type'] == 'page' ) { 2272 $cap = ( $publish ) ? 'publish_pages' : 'edit_pages'; 2273 $error_message = __( 'Sorry, you are not allowed to publish pages on this site.' ); 2274 $post_type = 'page'; 2275 if ( !empty( $content_struct['wp_page_template'] ) ) 2276 $page_template = $content_struct['wp_page_template']; 2277 } elseif ( $content_struct['post_type'] == 'post' ) { 2278 // This is the default, no changes needed 2279 } else { 2280 // No other post_type values are allowed here 2281 return new IXR_Error( 401, __( 'Invalid post type.' ) ); 2282 } 2283 } 2284 2285 if ( !current_user_can( $cap ) ) 2286 return new IXR_Error( 401, $error_message ); 2287 2288 $postdata = wp_get_single_post($post_ID, ARRAY_A); 2289 2290 // If there is no post data for the give post id, stop 2291 // now and return an error. Other wise a new post will be 2292 // created (which was the old behavior). 2293 if ( empty($postdata["ID"]) ) 2294 return(new IXR_Error(404, __("Invalid post ID."))); 2295 2296 $this->escape($postdata); 2297 extract($postdata, EXTR_SKIP); 2298 2299 // Let WordPress manage slug if none was provided. 2300 $post_name = ""; 2301 if ( isset($content_struct["wp_slug"]) ) 2302 $post_name = $content_struct["wp_slug"]; 2303 2304 // Only use a password if one was given. 2305 if ( isset($content_struct["wp_password"]) ) 2306 $post_password = $content_struct["wp_password"]; 2307 2308 // Only set a post parent if one was given. 2309 if ( isset($content_struct["wp_page_parent_id"]) ) 2310 $post_parent = $content_struct["wp_page_parent_id"]; 2311 2312 // Only set the menu_order if it was given. 2313 if ( isset($content_struct["wp_page_order"]) ) 2314 $menu_order = $content_struct["wp_page_order"]; 2315 2316 $post_author = $postdata["post_author"]; 2317 2318 // Only set the post_author if one is set. 2319 if ( isset($content_struct["wp_author_id"]) && ($user->ID != $content_struct["wp_author_id"]) ) { 2320 switch ( $post_type ) { 2321 case "post": 2322 if ( !current_user_can("edit_others_posts") ) 2323 return(new IXR_Error(401, __("You are not allowed to change the post author as this user."))); 2324 break; 2325 case "page": 2326 if ( !current_user_can("edit_others_pages") ) 2327 return(new IXR_Error(401, __("You are not allowed to change the page author as this user."))); 2328 break; 2329 default: 2330 return(new IXR_Error(401, __("Invalid post type."))); 2331 break; 2332 } 2333 $post_author = $content_struct["wp_author_id"]; 2334 } 2335 2336 if ( isset($content_struct["mt_allow_comments"]) ) { 2337 if ( !is_numeric($content_struct["mt_allow_comments"]) ) { 2338 switch ( $content_struct["mt_allow_comments"] ) { 2339 case "closed": 2340 $comment_status = "closed"; 2341 break; 2342 case "open": 2343 $comment_status = "open"; 2344 break; 2345 default: 2346 $comment_status = get_option("default_comment_status"); 2347 break; 2348 } 2349 } else { 2350 switch ( (int) $content_struct["mt_allow_comments"] ) { 2351 case 0: 2352 case 2: 2353 $comment_status = "closed"; 2354 break; 2355 case 1: 2356 $comment_status = "open"; 2357 break; 2358 default: 2359 $comment_status = get_option("default_comment_status"); 2360 break; 2361 } 2362 } 2363 } 2364 2365 if ( isset($content_struct["mt_allow_pings"]) ) { 2366 if ( !is_numeric($content_struct["mt_allow_pings"]) ) { 2367 switch ( $content_struct["mt_allow_pings"] ) { 2368 case "closed": 2369 $ping_status = "closed"; 2370 break; 2371 case "open": 2372 $ping_status = "open"; 2373 break; 2374 default: 2375 $ping_status = get_option("default_ping_status"); 2376 break; 2377 } 2378 } else { 2379 switch ( (int) $content_struct["mt_allow_pings"] ) { 2380 case 0: 2381 $ping_status = "closed"; 2382 break; 2383 case 1: 2384 $ping_status = "open"; 2385 break; 2386 default: 2387 $ping_status = get_option("default_ping_status"); 2388 break; 2389 } 2390 } 2391 } 2392 2393 $post_title = $content_struct['title']; 2394 $post_content = $content_struct['description']; 2395 $catnames = $content_struct['categories']; 2396 2397 $post_category = array(); 2398 2399 if ( is_array($catnames) ) { 2400 foreach ($catnames as $cat) { 2401 $post_category[] = get_cat_ID($cat); 2402 } 2403 } 2404 2405 $post_excerpt = $content_struct['mt_excerpt']; 2406 $post_more = $content_struct['mt_text_more']; 2407 2408 $post_status = $publish ? 'publish' : 'draft'; 2409 if ( isset( $content_struct["{$post_type}_status"] ) ) { 2410 switch( $content_struct["{$post_type}_status"] ) { 2411 case 'draft': 2412 case 'private': 2413 case 'publish': 2414 $post_status = $content_struct["{$post_type}_status"]; 2415 break; 2416 case 'pending': 2417 // Pending is only valid for posts, not pages. 2418 if ( $post_type === 'post' ) 2419 $post_status = $content_struct["{$post_type}_status"]; 2420 break; 2421 default: 2422 $post_status = $publish ? 'publish' : 'draft'; 2423 break; 2424 } 2425 } 2426 2427 $tags_input = $content_struct['mt_keywords']; 2428 2429 if ( ('publish' == $post_status) ) { 2430 if ( ( 'page' == $post_type ) && !current_user_can('publish_pages') ) 2431 return new IXR_Error(401, __('Sorry, you do not have the right to publish this page.')); 2432 else if ( !current_user_can('publish_posts') ) 2433 return new IXR_Error(401, __('Sorry, you do not have the right to publish this post.')); 2434 } 2435 2436 if ( $post_more ) 2437 $post_content = $post_content . "<!--more-->" . $post_more; 2438 2439 $to_ping = $content_struct['mt_tb_ping_urls']; 2440 if ( is_array($to_ping) ) 2441 $to_ping = implode(' ', $to_ping); 2442 2443 // Do some timestamp voodoo 2444 if ( !empty( $content_struct['date_created_gmt'] ) ) 2445 $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 2446 elseif ( !empty( $content_struct['dateCreated']) ) 2447 $dateCreated = $content_struct['dateCreated']->getIso(); 2448 2449 if ( !empty( $dateCreated ) ) { 2450 $post_date = get_date_from_gmt(iso8601_to_datetime($dateCreated)); 2451 $post_date_gmt = iso8601_to_datetime($dateCreated, GMT); 2452 } else { 2453 $post_date = $postdata['post_date']; 2454 $post_date_gmt = $postdata['post_date_gmt']; 2455 } 2456 2457 // We've got all the data -- post it: 2458 $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'); 2459 2460 $result = wp_update_post($newpost, true); 2461 if ( is_wp_error( $result ) ) 2462 return new IXR_Error(500, $result->get_error_message()); 2463 2464 if ( !$result ) 2465 return new IXR_Error(500, __('Sorry, your entry could not be edited. Something wrong happened.')); 2466 2467 // Only posts can be sticky 2468 if ( $post_type == 'post' && isset( $content_struct['sticky'] ) ) { 2469 if ( $content_struct['sticky'] == true ) 2470 stick_post( $post_ID ); 2471 elseif ( $content_struct['sticky'] == false ) 2472 unstick_post( $post_ID ); 2473 } 2474 2475 if ( isset($content_struct['custom_fields']) ) 2476 $this->set_custom_fields($post_ID, $content_struct['custom_fields']); 2477 2478 // Handle enclosures 2479 $this->add_enclosure_if_new($post_ID, $content_struct['enclosure']); 2480 2481 $this->attach_uploads( $ID, $post_content ); 2482 2483 logIO('O',"(MW) Edited ! ID: $post_ID"); 2484 2485 return true; 2486 } 2487 2488 /** 2489 * Retrieve post. 2490 * 2491 * @since 1.5.0 2492 * 2493 * @param array $args Method parameters. 2494 * @return array 2495 */ 2496 function mw_getPost($args) { 2497 2498 $this->escape($args); 2499 2500 $post_ID = (int) $args[0]; 2501 $username = $args[1]; 2502 $password = $args[2]; 2503 2504 if ( !$user = $this->login($username, $password) ) 2505 return $this->error; 2506 2507 if ( !current_user_can( 'edit_post', $post_ID ) ) 2508 return new IXR_Error( 401, __( 'Sorry, you cannot edit this post.' ) ); 2509 2510 do_action('xmlrpc_call', 'metaWeblog.getPost'); 2511 2512 $postdata = wp_get_single_post($post_ID, ARRAY_A); 2513 2514 if ($postdata['post_date'] != '') { 2515 $post_date = mysql2date('Ymd\TH:i:s', $postdata['post_date'], false); 2516 $post_date_gmt = mysql2date('Ymd\TH:i:s', $postdata['post_date_gmt'], false); 2517 2518 // For drafts use the GMT version of the post date 2519 if ( $postdata['post_status'] == 'draft' ) 2520 $post_date_gmt = get_gmt_from_date( mysql2date( 'Y-m-d H:i:s', $postdata['post_date'] ), 'Ymd\TH:i:s' ); 2521 2522 $categories = array(); 2523 $catids = wp_get_post_categories($post_ID); 2524 foreach($catids as $catid) 2525 $categories[] = get_cat_name($catid); 2526 2527 $tagnames = array(); 2528 $tags = wp_get_post_tags( $post_ID ); 2529 if ( !empty( $tags ) ) { 2530 foreach ( $tags as $tag ) 2531 $tagnames[] = $tag->name; 2532 $tagnames = implode( ', ', $tagnames ); 2533 } else { 2534 $tagnames = ''; 2535 } 2536 2537 $post = get_extended($postdata['post_content']); 2538 $link = post_permalink($postdata['ID']); 2539 2540 // Get the author info. 2541 $author = get_userdata($postdata['post_author']); 2542 2543 $allow_comments = ('open' == $postdata['comment_status']) ? 1 : 0; 2544 $allow_pings = ('open' == $postdata['ping_status']) ? 1 : 0; 2545 2546 // Consider future posts as published 2547 if ( $postdata['post_status'] === 'future' ) 2548 $postdata['post_status'] = 'publish'; 2549 2550 $sticky = false; 2551 if ( is_sticky( $post_ID ) ) 2552 $sticky = true; 2553 2554 $enclosure = array(); 2555 foreach ( (array) get_post_custom($post_ID) as $key => $val) { 2556 if ($key == 'enclosure') { 2557 foreach ( (array) $val as $enc ) { 2558 $encdata = split("\n", $enc); 2559 $enclosure['url'] = trim(htmlspecialchars($encdata[0])); 2560 $enclosure['length'] = (int) trim($encdata[1]); 2561 $enclosure['type'] = trim($encdata[2]); 2562 break 2; 2563 } 2564 } 2565 } 2566 2567 $resp = array( 2568 'dateCreated' => new IXR_Date($post_date), 2569 'userid' => $postdata['post_author'], 2570 'postid' => $postdata['ID'], 2571 'description' => $post['main'], 2572 'title' => $postdata['post_title'], 2573 'link' => $link, 2574 'permaLink' => $link, 2575 // commented out because no other tool seems to use this 2576 // 'content' => $entry['post_content'], 2577 'categories' => $categories, 2578 'mt_excerpt' => $postdata['post_excerpt'], 2579 'mt_text_more' => $post['extended'], 2580 'mt_allow_comments' => $allow_comments, 2581 'mt_allow_pings' => $allow_pings, 2582 'mt_keywords' => $tagnames, 2583 'wp_slug' => $postdata['post_name'], 2584 'wp_password' => $postdata['post_password'], 2585 'wp_author_id' => $author->ID, 2586 'wp_author_display_name' => $author->display_name, 2587 'date_created_gmt' => new IXR_Date($post_date_gmt), 2588 'post_status' => $postdata['post_status'], 2589 'custom_fields' => $this->get_custom_fields($post_ID), 2590 'sticky' => $sticky 2591 ); 2592 2593 if ( !empty($enclosure) ) $resp['enclosure'] = $enclosure; 2594 2595 return $resp; 2596 } else { 2597 return new IXR_Error(404, __('Sorry, no such post.')); 2598 } 2599 } 2600 2601 /** 2602 * Retrieve list of recent posts. 2603 * 2604 * @since 1.5.0 2605 * 2606 * @param array $args Method parameters. 2607 * @return array 2608 */ 2609 function mw_getRecentPosts($args) { 2610 2611 $this->escape($args); 2612 2613 $blog_ID = (int) $args[0]; 2614 $username = $args[1]; 2615 $password = $args[2]; 2616 $num_posts = (int) $args[3]; 2617 2618 if ( !$user = $this->login($username, $password) ) 2619 return $this->error; 2620 2621 do_action('xmlrpc_call', 'metaWeblog.getRecentPosts'); 2622 2623 $posts_list = wp_get_recent_posts($num_posts); 2624 2625 if ( !$posts_list ) 2626 return array( ); 2627 2628 foreach ($posts_list as $entry) { 2629 if ( !current_user_can( 'edit_post', $entry['ID'] ) ) 2630 continue; 2631 2632 $post_date = mysql2date('Ymd\TH:i:s', $entry['post_date'], false); 2633 $post_date_gmt = mysql2date('Ymd\TH:i:s', $entry['post_date_gmt'], false); 2634 2635 // For drafts use the GMT version of the date 2636 if ( $entry['post_status'] == 'draft' ) 2637 $post_date_gmt = get_gmt_from_date( mysql2date( 'Y-m-d H:i:s', $entry['post_date'] ), 'Ymd\TH:i:s' ); 2638 2639 $categories = array(); 2640 $catids = wp_get_post_categories($entry['ID']); 2641 foreach( $catids as $catid ) 2642 $categories[] = get_cat_name($catid); 2643 2644 $tagnames = array(); 2645 $tags = wp_get_post_tags( $entry['ID'] ); 2646 if ( !empty( $tags ) ) { 2647 foreach ( $tags as $tag ) { 2648 $tagnames[] = $tag->name; 2649 } 2650 $tagnames = implode( ', ', $tagnames ); 2651 } else { 2652 $tagnames = ''; 2653 } 2654 2655 $post = get_extended($entry['post_content']); 2656 $link = post_permalink($entry['ID']); 2657 2658 // Get the post author info. 2659 $author = get_userdata($entry['post_author']); 2660 2661 $allow_comments = ('open' == $entry['comment_status']) ? 1 : 0; 2662 $allow_pings = ('open' == $entry['ping_status']) ? 1 : 0; 2663 2664 // Consider future posts as published 2665 if ( $entry['post_status'] === 'future' ) 2666 $entry['post_status'] = 'publish'; 2667 2668 $struct[] = array( 2669 'dateCreated' => new IXR_Date($post_date), 2670 'userid' => $entry['post_author'], 2671 'postid' => $entry['ID'], 2672 'description' => $post['main'], 2673 'title' => $entry['post_title'], 2674 'link' => $link, 2675 'permaLink' => $link, 2676 // commented out because no other tool seems to use this 2677 // 'content' => $entry['post_content'], 2678 'categories' => $categories, 2679 'mt_excerpt' => $entry['post_excerpt'], 2680 'mt_text_more' => $post['extended'], 2681 'mt_allow_comments' => $allow_comments, 2682 'mt_allow_pings' => $allow_pings, 2683 'mt_keywords' => $tagnames, 2684 'wp_slug' => $entry['post_name'], 2685 'wp_password' => $entry['post_password'], 2686 'wp_author_id' => $author->ID, 2687 'wp_author_display_name' => $author->display_name, 2688 'date_created_gmt' => new IXR_Date($post_date_gmt), 2689 'post_status' => $entry['post_status'], 2690 'custom_fields' => $this->get_custom_fields($entry['ID']) 2691 ); 2692 2693 } 2694 2695 $recent_posts = array(); 2696 for ( $j=0; $j<count($struct); $j++ ) { 2697 array_push($recent_posts, $struct[$j]); 2698 } 2699 2700 return $recent_posts; 2701 } 2702 2703 /** 2704 * Retrieve the list of categories on a given blog. 2705 * 2706 * @since 1.5.0 2707 * 2708 * @param array $args Method parameters. 2709 * @return array 2710 */ 2711 function mw_getCategories($args) { 2712 2713 $this->escape($args); 2714 2715 $blog_ID = (int) $args[0]; 2716 $username = $args[1]; 2717 $password = $args[2]; 2718 2719 if ( !$user = $this->login($username, $password) ) 2720 return $this->error; 2721 2722 if ( !current_user_can( 'edit_posts' ) ) 2723 return new IXR_Error( 401, __( 'Sorry, you must be able to edit posts on this site in order to view categories.' ) ); 2724 2725 do_action('xmlrpc_call', 'metaWeblog.getCategories'); 2726 2727 $categories_struct = array(); 2728 2729 if ( $cats = get_categories(array('get' => 'all')) ) { 2730 foreach ( $cats as $cat ) { 2731 $struct['categoryId'] = $cat->term_id; 2732 $struct['parentId'] = $cat->parent; 2733 $struct['description'] = $cat->name; 2734 $struct['categoryDescription'] = $cat->description; 2735 $struct['categoryName'] = $cat->name; 2736 $struct['htmlUrl'] = esc_html(get_category_link($cat->term_id)); 2737 $struct['rssUrl'] = esc_html(get_category_feed_link($cat->term_id, 'rss2')); 2738 2739 $categories_struct[] = $struct; 2740 } 2741 } 2742 2743 return $categories_struct; 2744 } 2745 2746 /** 2747 * Uploads a file, following your settings. 2748 * 2749 * Adapted from a patch by Johann Richard. 2750 * 2751 * @link http://mycvs.org/archives/2004/06/30/file-upload-to-wordpress-in-ecto/ 2752 * 2753 * @since 1.5.0 2754 * 2755 * @param array $args Method parameters. 2756 * @return array 2757 */ 2758 function mw_newMediaObject($args) { 2759 global $wpdb; 2760 2761 $blog_ID = (int) $args[0]; 2762 $username = $wpdb->escape($args[1]); 2763 $password = $wpdb->escape($args[2]); 2764 $data = $args[3]; 2765 2766 $name = sanitize_file_name( $data['name'] ); 2767 $type = $data['type']; 2768 $bits = $data['bits']; 2769 2770 logIO('O', '(MW) Received '.strlen($bits).' bytes'); 2771 2772 if ( !$user = $this->login($username, $password) ) 2773 return $this->error; 2774 2775 do_action('xmlrpc_call', 'metaWeblog.newMediaObject'); 2776 2777 if ( !current_user_can('upload_files') ) { 2778 logIO('O', '(MW) User does not have upload_files capability'); 2779 $this->error = new IXR_Error(401, __('You are not allowed to upload files to this site.')); 2780 return $this->error; 2781 } 2782 2783 if ( $upload_err = apply_filters( "pre_upload_error", false ) ) 2784 return new IXR_Error(500, $upload_err); 2785 2786 if ( !empty($data["overwrite"]) && ($data["overwrite"] == true) ) { 2787 // Get postmeta info on the object. 2788 $old_file = $wpdb->get_row(" 2789 SELECT ID 2790 FROM {$wpdb->posts} 2791 WHERE post_title = '{$name}' 2792 AND post_type = 'attachment' 2793 "); 2794 2795 // Delete previous file. 2796 wp_delete_attachment($old_file->ID); 2797 2798 // Make sure the new name is different by pre-pending the 2799 // previous post id. 2800 $filename = preg_replace("/^wpid\d+-/", "", $name); 2801 $name = "wpid{$old_file->ID}-{$filename}"; 2802 } 2803 2804 $upload = wp_upload_bits($name, $type, $bits); 2805 if ( ! empty($upload['error']) ) { 2806 $errorString = sprintf(__('Could not write file %1$s (%2$s)'), $name, $upload['error']); 2807 logIO('O', '(MW) ' . $errorString); 2808 return new IXR_Error(500, $errorString); 2809 } 2810 // Construct the attachment array 2811 // attach to post_id 0 2812 $post_id = 0; 2813 $attachment = array( 2814 'post_title' => $name, 2815 'post_content' => '', 2816 'post_type' => 'attachment', 2817 'post_parent' => $post_id, 2818 'post_mime_type' => $type, 2819 'guid' => $upload[ 'url' ] 2820 ); 2821 2822 // Save the data 2823 $id = wp_insert_attachment( $attachment, $upload[ 'file' ], $post_id ); 2824 wp_update_attachment_metadata( $id, wp_generate_attachment_metadata( $id, $upload['file'] ) ); 2825 2826 return apply_filters( 'wp_handle_upload', array( 'file' => $name, 'url' => $upload[ 'url' ], 'type' => $type ), 'upload' ); 2827 } 2828 2829 /* MovableType API functions 2830 * specs on http://www.movabletype.org/docs/mtmanual_programmatic.html 2831 */ 2832 2833 /** 2834 * Retrieve the post titles of recent posts. 2835 * 2836 * @since 1.5.0 2837 * 2838 * @param array $args Method parameters. 2839 * @return array 2840 */ 2841 function mt_getRecentPostTitles($args) { 2842 2843 $this->escape($args); 2844 2845 $blog_ID = (int) $args[0]; 2846 $username = $args[1]; 2847 $password = $args[2]; 2848 $num_posts = (int) $args[3]; 2849 2850 if ( !$user = $this->login($username, $password) ) 2851 return $this->error; 2852 2853 do_action('xmlrpc_call', 'mt.getRecentPostTitles'); 2854 2855 $posts_list = wp_get_recent_posts($num_posts); 2856 2857 if ( !$posts_list ) { 2858 $this->error = new IXR_Error(500, __('Either there are no posts, or something went wrong.')); 2859 return $this->error; 2860 } 2861 2862 foreach ($posts_list as $entry) { 2863 if ( !current_user_can( 'edit_post', $entry['ID'] ) ) 2864 continue; 2865 2866 $post_date = mysql2date('Ymd\TH:i:s', $entry['post_date'], false); 2867 $post_date_gmt = mysql2date('Ymd\TH:i:s', $entry['post_date_gmt'], false); 2868 2869 // For drafts use the GMT version of the date 2870 if ( $entry['post_status'] == 'draft' ) 2871 $post_date_gmt = get_gmt_from_date( mysql2date( 'Y-m-d H:i:s', $entry['post_date'] ), 'Ymd\TH:i:s' ); 2872 2873 $struct[] = array( 2874 'dateCreated' => new IXR_Date($post_date), 2875 'userid' => $entry['post_author'], 2876 'postid' => $entry['ID'], 2877 'title' => $entry['post_title'], 2878 'date_created_gmt' => new IXR_Date($post_date_gmt) 2879 ); 2880 2881 } 2882 2883 $recent_posts = array(); 2884 for ( $j=0; $j<count($struct); $j++ ) { 2885 array_push($recent_posts, $struct[$j]); 2886 } 2887 2888 return $recent_posts; 2889 } 2890 2891 /** 2892 * Retrieve list of all categories on blog. 2893 * 2894 * @since 1.5.0 2895 * 2896 * @param array $args Method parameters. 2897 * @return array 2898 */ 2899 function mt_getCategoryList($args) { 2900 2901 $this->escape($args); 2902 2903 $blog_ID = (int) $args[0]; 2904 $username = $args[1]; 2905 $password = $args[2]; 2906 2907 if ( !$user = $this->login($username, $password) ) 2908 return $this->error; 2909 2910 if ( !current_user_can( 'edit_posts' ) ) 2911 return new IXR_Error( 401, __( 'Sorry, you must be able to edit posts on this site in order to view categories.' ) ); 2912 2913 do_action('xmlrpc_call', 'mt.getCategoryList'); 2914 2915 $categories_struct = array(); 2916 2917 if ( $cats = get_categories(array('hide_empty' => 0, 'hierarchical' => 0)) ) { 2918 foreach ( $cats as $cat ) { 2919 $struct['categoryId'] = $cat->term_id; 2920 $struct['categoryName'] = $cat->name; 2921 2922 $categories_struct[] = $struct; 2923 } 2924 } 2925 2926 return $categories_struct; 2927 } 2928 2929 /** 2930 * Retrieve post categories. 2931 * 2932 * @since 1.5.0 2933 * 2934 * @param array $args Method parameters. 2935 * @return array 2936 */ 2937 function mt_getPostCategories($args) { 2938 2939 $this->escape($args); 2940 2941 $post_ID = (int) $args[0]; 2942 $username = $args[1]; 2943 $password = $args[2]; 2944 2945 if ( !$user = $this->login($username, $password) ) 2946 return $this->error; 2947 2948 if ( !current_user_can( 'edit_post', $post_ID ) ) 2949 return new IXR_Error( 401, __( 'Sorry, you can not edit this post.' ) ); 2950 2951 do_action('xmlrpc_call', 'mt.getPostCategories'); 2952 2953 $categories = array(); 2954 $catids = wp_get_post_categories(intval($post_ID)); 2955 // first listed category will be the primary category 2956 $isPrimary = true; 2957 foreach ( $catids as $catid ) { 2958 $categories[] = array( 2959 'categoryName' => get_cat_name($catid), 2960 'categoryId' => (string) $catid, 2961 'isPrimary' => $isPrimary 2962 ); 2963 $isPrimary = false; 2964 } 2965 2966 return $categories; 2967 } 2968 2969 /** 2970 * Sets categories for a post. 2971 * 2972 * @since 1.5.0 2973 * 2974 * @param array $args Method parameters. 2975 * @return bool True on success. 2976 */ 2977 function mt_setPostCategories($args) { 2978 2979 $this->escape($args); 2980 2981 $post_ID = (int) $args[0]; 2982 $username = $args[1]; 2983 $password = $args[2]; 2984 $categories = $args[3]; 2985 2986 if ( !$user = $this->login($username, $password) ) 2987 return $this->error; 2988 2989 do_action('xmlrpc_call', 'mt.setPostCategories'); 2990 2991 if ( !current_user_can('edit_post', $post_ID) ) 2992 return new IXR_Error(401, __('Sorry, you cannot edit this post.')); 2993 2994 foreach ( $categories as $cat ) { 2995 $catids[] = $cat['categoryId']; 2996 } 2997 2998 wp_set_post_categories($post_ID, $catids); 2999 3000 return true; 3001 } 3002 3003 /** 3004 * Retrieve an array of methods supported by this server. 3005 * 3006 * @since 1.5.0 3007 * 3008 * @param array $args Method parameters. 3009 * @return array 3010 */ 3011 function mt_supportedMethods($args) { 3012 3013 do_action('xmlrpc_call', 'mt.supportedMethods'); 3014 3015 $supported_methods = array(); 3016 foreach ( $this->methods as $key => $value ) { 3017 $supported_methods[] = $key; 3018 } 3019 3020 return $supported_methods; 3021 } 3022 3023 /** 3024 * Retrieve an empty array because we don't support per-post text filters. 3025 * 3026 * @since 1.5.0 3027 * 3028 * @param array $args Method parameters. 3029 */ 3030 function mt_supportedTextFilters($args) { 3031 do_action('xmlrpc_call', 'mt.supportedTextFilters'); 3032 return apply_filters('xmlrpc_text_filters', array()); 3033 } 3034 3035 /** 3036 * Retrieve trackbacks sent to a given post. 3037 * 3038 * @since 1.5.0 3039 * 3040 * @param array $args Method parameters. 3041 * @return mixed 3042 */ 3043 function mt_getTrackbackPings($args) { 3044 3045 global $wpdb; 3046 3047 $post_ID = intval($args); 3048 3049 do_action('xmlrpc_call', 'mt.getTrackbackPings'); 3050 3051 $actual_post = wp_get_single_post($post_ID, ARRAY_A); 3052 3053 if ( !$actual_post ) 3054 return new IXR_Error(404, __('Sorry, no such post.')); 3055 3056 $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) ); 3057 3058 if ( !$comments ) 3059 return array(); 3060 3061 $trackback_pings = array(); 3062 foreach ( $comments as $comment ) { 3063 if ( 'trackback' == $comment->comment_type ) { 3064 $content = $comment->comment_content; 3065 $title = substr($content, 8, (strpos($content, '</strong>') - 8)); 3066 $trackback_pings[] = array( 3067 'pingTitle' => $title, 3068 'pingURL' => $comment->comment_author_url, 3069 'pingIP' => $comment->comment_author_IP 3070 ); 3071 } 3072 } 3073 3074 return $trackback_pings; 3075 } 3076 3077 /** 3078 * Sets a post's publish status to 'publish'. 3079 * 3080 * @since 1.5.0 3081 * 3082 * @param array $args Method parameters. 3083 * @return int 3084 */ 3085 function mt_publishPost($args) { 3086 3087 $this->escape($args); 3088 3089 $post_ID = (int) $args[0]; 3090 $username = $args[1]; 3091 $password = $args[2]; 3092 3093 if ( !$user = $this->login($username, $password) ) 3094 return $this->error; 3095 3096 do_action('xmlrpc_call', 'mt.publishPost'); 3097 3098 if ( !current_user_can('edit_post', $post_ID) ) 3099 return new IXR_Error(401, __('Sorry, you cannot edit this post.')); 3100 3101 $postdata = wp_get_single_post($post_ID,ARRAY_A); 3102 3103 $postdata['post_status'] = 'publish'; 3104 3105 // retain old cats 3106 $cats = wp_get_post_categories($post_ID); 3107 $postdata['post_category'] = $cats; 3108 $this->escape($postdata); 3109 3110 $result = wp_update_post($postdata); 3111 3112 return $result; 3113 } 3114 3115 /* PingBack functions 3116 * specs on www.hixie.ch/specs/pingback/pingback 3117 */ 3118 3119 /** 3120 * Retrieves a pingback and registers it. 3121 * 3122 * @since 1.5.0 3123 * 3124 * @param array $args Method parameters. 3125 * @return array 3126 */ 3127 function pingback_ping($args) { 3128 global $wpdb; 3129 3130 do_action('xmlrpc_call', 'pingback.ping'); 3131 3132 $this->escape($args); 3133 3134 $pagelinkedfrom = $args[0]; 3135 $pagelinkedto = $args[1]; 3136 3137 $title = ''; 3138 3139 $pagelinkedfrom = str_replace('&', '&', $pagelinkedfrom); 3140 $pagelinkedto = str_replace('&', '&', $pagelinkedto); 3141 $pagelinkedto = str_replace('&', '&', $pagelinkedto); 3142 3143 // Check if the page linked to is in our site 3144 $pos1 = strpos($pagelinkedto, str_replace(array('http://www.','http://','https://www.','https://'), '', get_option('home'))); 3145 if ( !$pos1 ) 3146 return new IXR_Error(0, __('Is there no link to us?')); 3147 3148 // let's find which post is linked to 3149 // FIXME: does url_to_postid() cover all these cases already? 3150 // if so, then let's use it and drop the old code. 3151 $urltest = parse_url($pagelinkedto); 3152 if ( $post_ID = url_to_postid($pagelinkedto) ) { 3153 $way = 'url_to_postid()'; 3154 } elseif ( preg_match('#p/[0-9]{1,}#', $urltest['path'], $match) ) { 3155 // the path defines the post_ID (archives/p/XXXX) 3156 $blah = explode('/', $match[0]); 3157 $post_ID = (int) $blah[1]; 3158 $way = 'from the path'; 3159 } elseif ( preg_match('#p=[0-9]{1,}#', $urltest['query'], $match) ) { 3160 // the querystring defines the post_ID (?p=XXXX) 3161 $blah = explode('=', $match[0]); 3162 $post_ID = (int) $blah[1]; 3163 $way = 'from the querystring'; 3164 } elseif ( isset($urltest['fragment']) ) { 3165 // an #anchor is there, it's either... 3166 if ( intval($urltest['fragment']) ) { 3167 // ...an integer #XXXX (simpliest case) 3168 $post_ID = (int) $urltest['fragment']; 3169 $way = 'from the fragment (numeric)'; 3170 } elseif ( preg_match('/post-[0-9]+/',$urltest['fragment']) ) { 3171 // ...a post id in the form 'post-###' 3172 $post_ID = preg_replace('/[^0-9]+/', '', $urltest['fragment']); 3173 $way = 'from the fragment (post-###)'; 3174 } elseif ( is_string($urltest['fragment']) ) { 3175 // ...or a string #title, a little more complicated 3176 $title = preg_replace('/[^a-z0-9]/i', '.', $urltest['fragment']); 3177 $sql = $wpdb->prepare("SELECT ID FROM $wpdb->posts WHERE post_title RLIKE %s", $title); 3178 if (! ($post_ID = $wpdb->get_var($sql)) ) { 3179 // returning unknown error '0' is better than die()ing 3180 return new IXR_Error(0, ''); 3181 } 3182 $way = 'from the fragment (title)'; 3183 } 3184 } else { 3185 // TODO: Attempt to extract a post ID from the given URL 3186 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.')); 3187 } 3188 $post_ID = (int) $post_ID; 3189 3190 3191 logIO("O","(PB) URL='$pagelinkedto' ID='$post_ID' Found='$way'"); 3192 3193 $post = get_post($post_ID); 3194 3195 if ( !$post ) // Post_ID not found 3196 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.')); 3197 3198 if ( $post_ID == url_to_postid($pagelinkedfrom) ) 3199 return new IXR_Error(0, __('The source URL and the target URL cannot both point to the same resource.')); 3200 3201 // Check if pings are on 3202 if ( !pings_open($post) ) 3203 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.')); 3204 3205 // Let's check that the remote site didn't already pingback this entry 3206 if ( $wpdb->get_results( $wpdb->prepare("SELECT * FROM $wpdb->comments WHERE comment_post_ID = %d AND comment_author_url = %s", $post_ID, $pagelinkedfrom) ) ) 3207 return new IXR_Error( 48, __( 'The pingback has already been registered.' ) ); 3208 3209 // very stupid, but gives time to the 'from' server to publish ! 3210 sleep(1); 3211 3212 // Let's check the remote site 3213 $linea = wp_remote_fopen( $pagelinkedfrom ); 3214 if ( !$linea ) 3215 return new IXR_Error(16, __('The source URL does not exist.')); 3216 3217 $linea = apply_filters('pre_remote_source', $linea, $pagelinkedto); 3218 3219 // Work around bug in strip_tags(): 3220 $linea = str_replace('<!DOC', '<DOC', $linea); 3221 $linea = preg_replace( '/[\s\r\n\t]+/', ' ', $linea ); // normalize spaces 3222 $linea = preg_replace( "/ <(h1|h2|h3|h4|h5|h6|p|th|td|li|dt|dd|pre|caption|input|textarea|button|body)[^>]*>/", "\n\n", $linea ); 3223 3224 preg_match('|<title>([^<]*?)</title>|is', $linea, $matchtitle); 3225 $title = $matchtitle[1]; 3226 if ( empty( $title ) ) 3227 return new IXR_Error(32, __('We cannot find a title on that page.')); 3228 3229 $linea = strip_tags( $linea, '<a>' ); // just keep the tag we need 3230 3231 $p = explode( "\n\n", $linea ); 3232 3233 $preg_target = preg_quote($pagelinkedto, '|'); 3234 3235 foreach ( $p as $para ) { 3236 if ( strpos($para, $pagelinkedto) !== false ) { // it exists, but is it a link? 3237 preg_match("|<a[^>]+?".$preg_target."[^>]*>([^>]+?)</a>|", $para, $context); 3238 3239 // If the URL isn't in a link context, keep looking 3240 if ( empty($context) ) 3241 continue; 3242 3243 // We're going to use this fake tag to mark the context in a bit 3244 // the marker is needed in case the link text appears more than once in the paragraph 3245 $excerpt = preg_replace('|\</?wpcontext\>|', '', $para); 3246 3247 // prevent really long link text 3248 if ( strlen($context[1]) > 100 ) 3249 $context[1] = substr($context[1], 0, 100) . '...'; 3250 3251 $marker = '<wpcontext>'.$context[1].'</wpcontext>'; // set up our marker 3252 $excerpt= str_replace($context[0], $marker, $excerpt); // swap out the link for our marker 3253 $excerpt = strip_tags($excerpt, '<wpcontext>'); // strip all tags but our context marker 3254 $excerpt = trim($excerpt); 3255 $preg_marker = preg_quote($marker, '|'); 3256 $excerpt = preg_replace("|.*?\s(.{0,100}$preg_marker.{0,100})\s.*|s", '$1', $excerpt); 3257 $excerpt = strip_tags($excerpt); // YES, again, to remove the marker wrapper 3258 break; 3259 } 3260 } 3261 3262 if ( empty($context) ) // Link to target not found 3263 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.')); 3264 3265 $pagelinkedfrom = str_replace('&', '&', $pagelinkedfrom); 3266 3267 $context = '[...] ' . esc_html( $excerpt ) . ' [...]'; 3268 $pagelinkedfrom = $wpdb->escape( $pagelinkedfrom ); 3269 3270 $comment_post_ID = (int) $post_ID; 3271 $comment_author = $title; 3272 $this->escape($comment_author); 3273 $comment_author_url = $pagelinkedfrom; 3274 $comment_content = $context; 3275 $this->escape($comment_content); 3276 $comment_type = 'pingback'; 3277 3278 $commentdata = compact('comment_post_ID', 'comment_author', 'comment_author_url', 'comment_content', 'comment_type'); 3279 3280 $comment_ID = wp_new_comment($commentdata); 3281 do_action('pingback_post', $comment_ID); 3282 3283 return sprintf(__('Pingback from %1$s to %2$s registered. Keep the web talking! :-)'), $pagelinkedfrom, $pagelinkedto); 3284 } 3285 3286 /** 3287 * Retrieve array of URLs that pingbacked the given URL. 3288 * 3289 * Specs on http://www.aquarionics.com/misc/archives/blogite/0198.html 3290 * 3291 * @since 1.5.0 3292 * 3293 * @param array $args Method parameters. 3294 * @return array 3295 */ 3296 function pingback_extensions_getPingbacks($args) { 3297 3298 global $wpdb; 3299 3300 do_action('xmlrpc_call', 'pingback.extensions.getPingbacks'); 3301 3302 $this->escape($args); 3303 3304 $url = $args; 3305 3306 $post_ID = url_to_postid($url); 3307 if ( !$post_ID ) { 3308 // We aren't sure that the resource is available and/or pingback enabled 3309 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.')); 3310 } 3311 3312 $actual_post = wp_get_single_post($post_ID, ARRAY_A); 3313 3314 if ( !$actual_post ) { 3315 // No such post = resource not found 3316 return new IXR_Error(32, __('The specified target URL does not exist.')); 3317 } 3318 3319 $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) ); 3320 3321 if ( !$comments ) 3322 return array(); 3323 3324 $pingbacks = array(); 3325 foreach ( $comments as $comment ) { 3326 if ( 'pingback' == $comment->comment_type ) 3327 $pingbacks[] = $comment->comment_author_url; 3328 } 3329 3330 return $pingbacks; 3331 } 3332 } 3333 3334 $wp_xmlrpc_server = new wp_xmlrpc_server(); 3335 $wp_xmlrpc_server->serve_request(); 3336 ?>
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated: Thu Oct 14 05:11:12 2010 | Cross-referenced by PHPXref 0.7 |