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