[ Index ]

PHP Cross Reference of WordPress

title

Body

[close]

/wp-includes/ -> class-wp-xmlrpc-server.php (source)

   1  <?php
   2  /**
   3   * XML-RPC protocol support for WordPress
   4   *
   5   * @package WordPress
   6   */
   7  
   8  /**
   9   * WordPress XMLRPC server implementation.
  10   *
  11   * Implements compatibility for Blogger API, MetaWeblog API, MovableType, and
  12   * pingback. Additional WordPress API for managing comments, pages, posts,
  13   * options, etc.
  14   *
  15   * Since WordPress 2.6.0, WordPress XMLRPC server can be disabled in the
  16   * administration panels.
  17   *
  18   * @package WordPress
  19   * @subpackage Publishing
  20   * @since 1.5.0
  21   */
  22  class wp_xmlrpc_server extends IXR_Server {
  23  
  24      /**
  25       * Register all of the XMLRPC methods that XMLRPC server understands.
  26       *
  27       * Sets up server and method property. Passes XMLRPC
  28       * methods through the 'xmlrpc_methods' filter to allow plugins to extend
  29       * or replace XMLRPC methods.
  30       *
  31       * @since 1.5.0
  32       *
  33       * @return wp_xmlrpc_server
  34       */
  35  	function __construct() {
  36          $this->methods = array(
  37              // WordPress API
  38              'wp.getUsersBlogs'        => 'this:wp_getUsersBlogs',
  39              'wp.newPost'            => 'this:wp_newPost',
  40              'wp.editPost'            => 'this:wp_editPost',
  41              'wp.deletePost'            => 'this:wp_deletePost',
  42              'wp.getPost'            => 'this:wp_getPost',
  43              'wp.getPosts'            => 'this:wp_getPosts',
  44              'wp.newTerm'            => 'this:wp_newTerm',
  45              'wp.editTerm'            => 'this:wp_editTerm',
  46              'wp.deleteTerm'            => 'this:wp_deleteTerm',
  47              'wp.getTerm'            => 'this:wp_getTerm',
  48              'wp.getTerms'            => 'this:wp_getTerms',
  49              'wp.getTaxonomy'        => 'this:wp_getTaxonomy',
  50              'wp.getTaxonomies'        => 'this:wp_getTaxonomies',
  51              'wp.getPage'            => 'this:wp_getPage',
  52              'wp.getPages'            => 'this:wp_getPages',
  53              'wp.newPage'            => 'this:wp_newPage',
  54              'wp.deletePage'            => 'this:wp_deletePage',
  55              'wp.editPage'            => 'this:wp_editPage',
  56              'wp.getPageList'        => 'this:wp_getPageList',
  57              'wp.getAuthors'            => 'this:wp_getAuthors',
  58              'wp.getCategories'        => 'this:mw_getCategories',        // Alias
  59              'wp.getTags'            => 'this:wp_getTags',
  60              'wp.newCategory'        => 'this:wp_newCategory',
  61              'wp.deleteCategory'        => 'this:wp_deleteCategory',
  62              'wp.suggestCategories'    => 'this:wp_suggestCategories',
  63              'wp.uploadFile'            => 'this:mw_newMediaObject',    // Alias
  64              'wp.getCommentCount'    => 'this:wp_getCommentCount',
  65              'wp.getPostStatusList'    => 'this:wp_getPostStatusList',
  66              'wp.getPageStatusList'    => 'this:wp_getPageStatusList',
  67              'wp.getPageTemplates'    => 'this:wp_getPageTemplates',
  68              'wp.getOptions'            => 'this:wp_getOptions',
  69              'wp.setOptions'            => 'this:wp_setOptions',
  70              'wp.getComment'            => 'this:wp_getComment',
  71              'wp.getComments'        => 'this:wp_getComments',
  72              'wp.deleteComment'        => 'this:wp_deleteComment',
  73              'wp.editComment'        => 'this:wp_editComment',
  74              'wp.newComment'            => 'this:wp_newComment',
  75              'wp.getCommentStatusList' => 'this:wp_getCommentStatusList',
  76              'wp.getMediaItem'        => 'this:wp_getMediaItem',
  77              'wp.getMediaLibrary'    => 'this:wp_getMediaLibrary',
  78              'wp.getPostFormats'     => 'this:wp_getPostFormats',
  79              'wp.getPostType'        => 'this:wp_getPostType',
  80              'wp.getPostTypes'        => 'this:wp_getPostTypes',
  81  
  82              // Blogger API
  83              'blogger.getUsersBlogs' => 'this:blogger_getUsersBlogs',
  84              'blogger.getUserInfo' => 'this:blogger_getUserInfo',
  85              'blogger.getPost' => 'this:blogger_getPost',
  86              'blogger.getRecentPosts' => 'this:blogger_getRecentPosts',
  87              'blogger.getTemplate' => 'this:blogger_getTemplate',
  88              'blogger.setTemplate' => 'this:blogger_setTemplate',
  89              'blogger.newPost' => 'this:blogger_newPost',
  90              'blogger.editPost' => 'this:blogger_editPost',
  91              'blogger.deletePost' => 'this:blogger_deletePost',
  92  
  93              // MetaWeblog API (with MT extensions to structs)
  94              'metaWeblog.newPost' => 'this:mw_newPost',
  95              'metaWeblog.editPost' => 'this:mw_editPost',
  96              'metaWeblog.getPost' => 'this:mw_getPost',
  97              'metaWeblog.getRecentPosts' => 'this:mw_getRecentPosts',
  98              'metaWeblog.getCategories' => 'this:mw_getCategories',
  99              'metaWeblog.newMediaObject' => 'this:mw_newMediaObject',
 100  
 101              // MetaWeblog API aliases for Blogger API
 102              // see http://www.xmlrpc.com/stories/storyReader$2460
 103              'metaWeblog.deletePost' => 'this:blogger_deletePost',
 104              'metaWeblog.getTemplate' => 'this:blogger_getTemplate',
 105              'metaWeblog.setTemplate' => 'this:blogger_setTemplate',
 106              'metaWeblog.getUsersBlogs' => 'this:blogger_getUsersBlogs',
 107  
 108              // MovableType API
 109              'mt.getCategoryList' => 'this:mt_getCategoryList',
 110              'mt.getRecentPostTitles' => 'this:mt_getRecentPostTitles',
 111              'mt.getPostCategories' => 'this:mt_getPostCategories',
 112              'mt.setPostCategories' => 'this:mt_setPostCategories',
 113              'mt.supportedMethods' => 'this:mt_supportedMethods',
 114              'mt.supportedTextFilters' => 'this:mt_supportedTextFilters',
 115              'mt.getTrackbackPings' => 'this:mt_getTrackbackPings',
 116              'mt.publishPost' => 'this:mt_publishPost',
 117  
 118              // PingBack
 119              'pingback.ping' => 'this:pingback_ping',
 120              'pingback.extensions.getPingbacks' => 'this:pingback_extensions_getPingbacks',
 121  
 122              'demo.sayHello' => 'this:sayHello',
 123              'demo.addTwoNumbers' => 'this:addTwoNumbers'
 124          );
 125  
 126          $this->initialise_blog_option_info();
 127          $this->methods = apply_filters('xmlrpc_methods', $this->methods);
 128      }
 129  
 130  	function serve_request() {
 131          $this->IXR_Server($this->methods);
 132      }
 133  
 134      /**
 135       * Test XMLRPC API by saying, "Hello!" to client.
 136       *
 137       * @since 1.5.0
 138       *
 139       * @param array $args Method Parameters.
 140       * @return string
 141       */
 142  	function sayHello($args) {
 143          return 'Hello!';
 144      }
 145  
 146      /**
 147       * Test XMLRPC API by adding two numbers for client.
 148       *
 149       * @since 1.5.0
 150       *
 151       * @param array $args Method Parameters.
 152       * @return int
 153       */
 154  	function addTwoNumbers($args) {
 155          $number1 = $args[0];
 156          $number2 = $args[1];
 157          return $number1 + $number2;
 158      }
 159  
 160      /**
 161       * Check user's credentials.
 162       *
 163       * @since 1.5.0
 164       *
 165       * @param string $user_login User's username.
 166       * @param string $user_pass User's password.
 167       * @return bool Whether authentication passed.
 168       * @deprecated use wp_xmlrpc_server::login
 169       * @see wp_xmlrpc_server::login
 170       */
 171  	function login_pass_ok($user_login, $user_pass) {
 172          if ( !get_option( 'enable_xmlrpc' ) ) {
 173              $this->error = new IXR_Error( 405, sprintf( __( 'XML-RPC services are disabled on this site. An admin user can enable them at %s'),  admin_url('options-writing.php') ) );
 174              return false;
 175          }
 176  
 177          if (!user_pass_ok($user_login, $user_pass)) {
 178              $this->error = new IXR_Error(403, __('Bad login/pass combination.'));
 179              return false;
 180          }
 181          return true;
 182      }
 183  
 184      /**
 185       * Log user in.
 186       *
 187       * @since 2.8
 188       *
 189       * @param string $username User's username.
 190       * @param string $password User's password.
 191       * @return mixed WP_User object if authentication passed, false otherwise
 192       */
 193  	function login($username, $password) {
 194          if ( !get_option( 'enable_xmlrpc' ) ) {
 195              $this->error = new IXR_Error( 405, sprintf( __( 'XML-RPC services are disabled on this site. An admin user can enable them at %s'),  admin_url('options-writing.php') ) );
 196              return false;
 197          }
 198  
 199          $user = wp_authenticate($username, $password);
 200  
 201          if (is_wp_error($user)) {
 202              $this->error = new IXR_Error(403, __('Bad login/pass combination.'));
 203              return false;
 204          }
 205  
 206          wp_set_current_user( $user->ID );
 207          return $user;
 208      }
 209  
 210      /**
 211       * Sanitize string or array of strings for database.
 212       *
 213       * @since 1.5.2
 214       *
 215       * @param string|array $array Sanitize single string or array of strings.
 216       * @return string|array Type matches $array and sanitized for the database.
 217       */
 218  	function escape(&$array) {
 219          global $wpdb;
 220  
 221          if (!is_array($array)) {
 222              return($wpdb->escape($array));
 223          } else {
 224              foreach ( (array) $array as $k => $v ) {
 225                  if ( is_array($v) ) {
 226                      $this->escape($array[$k]);
 227                  } else if ( is_object($v) ) {
 228                      //skip
 229                  } else {
 230                      $array[$k] = $wpdb->escape($v);
 231                  }
 232              }
 233          }
 234      }
 235  
 236      /**
 237       * Retrieve custom fields for post.
 238       *
 239       * @since 2.5.0
 240       *
 241       * @param int $post_id Post ID.
 242       * @return array Custom fields, if exist.
 243       */
 244  	function get_custom_fields($post_id) {
 245          $post_id = (int) $post_id;
 246  
 247          $custom_fields = array();
 248  
 249          foreach ( (array) has_meta($post_id) as $meta ) {
 250              // Don't expose protected fields.
 251              if ( ! current_user_can( 'edit_post_meta', $post_id , $meta['meta_key'] ) )
 252                  continue;
 253  
 254              $custom_fields[] = array(
 255                  "id"    => $meta['meta_id'],
 256                  "key"   => $meta['meta_key'],
 257                  "value" => $meta['meta_value']
 258              );
 259          }
 260  
 261          return $custom_fields;
 262      }
 263  
 264      /**
 265       * Set custom fields for post.
 266       *
 267       * @since 2.5.0
 268       *
 269       * @param int $post_id Post ID.
 270       * @param array $fields Custom fields.
 271       */
 272  	function set_custom_fields($post_id, $fields) {
 273          $post_id = (int) $post_id;
 274  
 275          foreach ( (array) $fields as $meta ) {
 276              if ( isset($meta['id']) ) {
 277                  $meta['id'] = (int) $meta['id'];
 278                  $pmeta = get_metadata_by_mid( 'post', $meta['id'] );
 279                  if ( isset($meta['key']) ) {
 280                      $meta['key'] = stripslashes( $meta['key'] );
 281                      if ( $meta['key'] != $pmeta->meta_key )
 282                          continue;
 283                      $meta['value'] = stripslashes_deep( $meta['value'] );
 284                      if ( current_user_can( 'edit_post_meta', $post_id, $meta['key'] ) )
 285                          update_metadata_by_mid( 'post', $meta['id'], $meta['value'] );
 286                  } elseif ( current_user_can( 'delete_post_meta', $post_id, $pmeta->meta_key ) ) {
 287                      delete_metadata_by_mid( 'post', $meta['id'] );
 288                  }
 289              } elseif ( current_user_can( 'add_post_meta', $post_id, stripslashes( $meta['key'] ) ) ) {
 290                  add_post_meta( $post_id, $meta['key'], $meta['value'] );
 291              }
 292          }
 293      }
 294  
 295      /**
 296       * Set up blog options property.
 297       *
 298       * Passes property through 'xmlrpc_blog_options' filter.
 299       *
 300       * @since 2.6.0
 301       */
 302  	function initialise_blog_option_info() {
 303          global $wp_version;
 304  
 305          $this->blog_options = array(
 306              // Read only options
 307              'software_name'     => array(
 308                  'desc'          => __( 'Software Name' ),
 309                  'readonly'      => true,
 310                  'value'         => 'WordPress'
 311              ),
 312              'software_version'  => array(
 313                  'desc'          => __( 'Software Version' ),
 314                  'readonly'      => true,
 315                  'value'         => $wp_version
 316              ),
 317              'blog_url'          => array(
 318                  'desc'          => __( 'Site URL' ),
 319                  'readonly'      => true,
 320                  'option'        => 'siteurl'
 321              ),
 322              'image_default_link_type' => array(
 323                  'desc'          => __( 'Image default link type' ),
 324                  'readonly'      => true,
 325                  'option'        => 'image_default_link_type'
 326              ),
 327              'image_default_size' => array(
 328                  'desc'          => __( 'Image default size' ),
 329                  'readonly'      => true,
 330                  'option'        => 'image_default_size'
 331              ),
 332              'image_default_align' => array(
 333                  'desc'          => __( 'Image default align' ),
 334                  'readonly'      => true,
 335                  'option'        => 'image_default_align'
 336              ),
 337              'template'          => array(
 338                  'desc'          => __( 'Template' ),
 339                  'readonly'      => true,
 340                  'option'        => 'template'
 341              ),
 342              'stylesheet'        => array(
 343                  'desc'          => __( 'Stylesheet' ),
 344                  'readonly'      => true,
 345                  'option'        => 'stylesheet'
 346              ),
 347              'post_thumbnail'    => array(
 348                  'desc'          => __('Post Thumbnail'),
 349                  'readonly'      => true,
 350                  'value'         => current_theme_supports( 'post-thumbnails' )
 351              ),
 352  
 353              // Updatable options
 354              'time_zone'         => array(
 355                  'desc'          => __( 'Time Zone' ),
 356                  'readonly'      => false,
 357                  'option'        => 'gmt_offset'
 358              ),
 359              'blog_title'        => array(
 360                  'desc'          => __( 'Site Title' ),
 361                  'readonly'      => false,
 362                  'option'        => 'blogname'
 363              ),
 364              'blog_tagline'      => array(
 365                  'desc'          => __( 'Site Tagline' ),
 366                  'readonly'      => false,
 367                  'option'        => 'blogdescription'
 368              ),
 369              'date_format'       => array(
 370                  'desc'          => __( 'Date Format' ),
 371                  'readonly'      => false,
 372                  'option'        => 'date_format'
 373              ),
 374              'time_format'       => array(
 375                  'desc'          => __( 'Time Format' ),
 376                  'readonly'      => false,
 377                  'option'        => 'time_format'
 378              ),
 379              'users_can_register' => array(
 380                  'desc'          => __( 'Allow new users to sign up' ),
 381                  'readonly'      => false,
 382                  'option'        => 'users_can_register'
 383              ),
 384              'thumbnail_size_w'  => array(
 385                  'desc'          => __( 'Thumbnail Width' ),
 386                  'readonly'      => false,
 387                  'option'        => 'thumbnail_size_w'
 388              ),
 389              'thumbnail_size_h'  => array(
 390                  'desc'          => __( 'Thumbnail Height' ),
 391                  'readonly'      => false,
 392                  'option'        => 'thumbnail_size_h'
 393              ),
 394              'thumbnail_crop'    => array(
 395                  'desc'          => __( 'Crop thumbnail to exact dimensions' ),
 396                  'readonly'      => false,
 397                  'option'        => 'thumbnail_crop'
 398              ),
 399              'medium_size_w'     => array(
 400                  'desc'          => __( 'Medium size image width' ),
 401                  'readonly'      => false,
 402                  'option'        => 'medium_size_w'
 403              ),
 404              'medium_size_h'     => array(
 405                  'desc'          => __( 'Medium size image height' ),
 406                  'readonly'      => false,
 407                  'option'        => 'medium_size_h'
 408              ),
 409              'large_size_w'      => array(
 410                  'desc'          => __( 'Large size image width' ),
 411                  'readonly'      => false,
 412                  'option'        => 'large_size_w'
 413              ),
 414              'large_size_h'      => array(
 415                  'desc'          => __( 'Large size image height' ),
 416                  'readonly'      => false,
 417                  'option'        => 'large_size_h'
 418              ),
 419              'default_comment_status' => array(
 420                  'desc'          => __( 'Allow people to post comments on new articles' ),
 421                  'readonly'      => false,
 422                  'option'        => 'default_comment_status'
 423              ),
 424              'default_ping_status' => array(
 425                  'desc'          => __( 'Allow link notifications from other blogs (pingbacks and trackbacks)' ),
 426                  'readonly'      => false,
 427                  'option'        => 'default_ping_status'
 428              )
 429          );
 430  
 431          $this->blog_options = apply_filters( 'xmlrpc_blog_options', $this->blog_options );
 432      }
 433  
 434      /**
 435       * Retrieve the blogs of the user.
 436       *
 437       * @since 2.6.0
 438       *
 439       * @param array $args Method parameters. Contains:
 440       *  - username
 441       *  - password
 442       * @return array. Contains:
 443       *  - 'isAdmin'
 444       *  - 'url'
 445       *  - 'blogid'
 446       *  - 'blogName'
 447       *  - 'xmlrpc' - url of xmlrpc endpoint
 448       */
 449  	function wp_getUsersBlogs( $args ) {
 450          global $current_site;
 451          // If this isn't on WPMU then just use blogger_getUsersBlogs
 452          if ( !is_multisite() ) {
 453              array_unshift( $args, 1 );
 454              return $this->blogger_getUsersBlogs( $args );
 455          }
 456  
 457          $this->escape( $args );
 458  
 459          $username = $args[0];
 460          $password = $args[1];
 461  
 462          if ( !$user = $this->login($username, $password) )
 463              return $this->error;
 464  
 465          do_action( 'xmlrpc_call', 'wp.getUsersBlogs' );
 466  
 467          $blogs = (array) get_blogs_of_user( $user->ID );
 468          $struct = array();
 469  
 470          foreach ( $blogs as $blog ) {
 471              // Don't include blogs that aren't hosted at this site
 472              if ( $blog->site_id != $current_site->id )
 473                  continue;
 474  
 475              $blog_id = $blog->userblog_id;
 476              switch_to_blog($blog_id);
 477              $is_admin = current_user_can('manage_options');
 478  
 479              $struct[] = array(
 480                  'isAdmin'        => $is_admin,
 481                  'url'            => get_option( 'home' ) . '/',
 482                  'blogid'        => (string) $blog_id,
 483                  'blogName'        => get_option( 'blogname' ),
 484                  'xmlrpc'        => site_url( 'xmlrpc.php' )
 485              );
 486  
 487              restore_current_blog();
 488          }
 489  
 490          return $struct;
 491      }
 492  
 493      /**
 494       * Checks if the method received at least the minimum number of arguments.
 495       *
 496       * @since 3.4.0
 497       *
 498       * @param string|array $args Sanitize single string or array of strings.
 499       * @param int $count Minimum number of arguments.
 500       * @return boolean if $args contains at least $count arguments.
 501       */
 502  	protected function minimum_args( $args, $count ) {
 503          if ( count( $args ) < $count ) {
 504              $this->error = new IXR_Error( 400, __( 'Insufficient arguments passed to this XML-RPC method.' ) );
 505              return false;
 506          }
 507  
 508          return true;
 509      }
 510  
 511      /**
 512       * Prepares taxonomy data for return in an XML-RPC object.
 513       *
 514       * @access protected
 515       *
 516       * @param object $taxonomy The unprepared taxonomy data
 517       * @param array $fields The subset of taxonomy fields to return
 518       * @return array The prepared taxonomy data
 519       */
 520  	protected function _prepare_taxonomy( $taxonomy, $fields ) {
 521          $_taxonomy = array(
 522              'name' => $taxonomy->name,
 523              'label' => $taxonomy->label,
 524              'hierarchical' => (bool) $taxonomy->hierarchical,
 525              'public' => (bool) $taxonomy->public,
 526              'show_ui' => (bool) $taxonomy->show_ui,
 527              '_builtin' => (bool) $taxonomy->_builtin,
 528          );
 529  
 530          if ( in_array( 'labels', $fields ) )
 531              $_taxonomy['labels'] = (array) $taxonomy->labels;
 532  
 533          if ( in_array( 'cap', $fields ) )
 534              $_taxonomy['cap'] = (array) $taxonomy->cap;
 535  
 536          if ( in_array( 'object_type', $fields ) )
 537              $_taxonomy['object_type'] = array_unique( (array) $taxonomy->object_type );
 538  
 539          return apply_filters( 'xmlrpc_prepare_taxonomy', $_taxonomy, $taxonomy, $fields );
 540      }
 541  
 542      /**
 543       * Prepares term data for return in an XML-RPC object.
 544       *
 545       * @access protected
 546       *
 547       * @param array|object $term The unprepared term data
 548       * @return array The prepared term data
 549       */
 550  	protected function _prepare_term( $term ) {
 551          $_term = $term;
 552          if ( ! is_array( $_term) )
 553              $_term = get_object_vars( $_term );
 554  
 555          // For Intergers which may be largeer than XMLRPC supports ensure we return strings.
 556          $_term['term_id'] = strval( $_term['term_id'] );
 557          $_term['term_group'] = strval( $_term['term_group'] );
 558          $_term['term_taxonomy_id'] = strval( $_term['term_taxonomy_id'] );
 559          $_term['parent'] = strval( $_term['parent'] );
 560  
 561          // Count we are happy to return as an Integer because people really shouldn't use Terms that much.
 562          $_term['count'] = intval( $_term['count'] );
 563  
 564          return apply_filters( 'xmlrpc_prepare_term', $_term, $term );
 565      }
 566  
 567      /**
 568       * Convert a WordPress date string to an IXR_Date object.
 569       *
 570       * @access protected
 571       *
 572       * @param string $date
 573       * @return IXR_Date
 574       */
 575  	protected function _convert_date( $date ) {
 576          if ( $date === '0000-00-00 00:00:00' ) {
 577              return new IXR_Date( '00000000T00:00:00Z' );
 578          }
 579          return new IXR_Date( mysql2date( 'Ymd\TH:i:s', $date, false ) );
 580      }
 581  
 582      /**
 583       * Convert a WordPress GMT date string to an IXR_Date object.
 584       *
 585       * @access protected
 586       *
 587       * @param string $date_gmt
 588       * @param string $date
 589       * @return IXR_Date
 590       */
 591  	protected function _convert_date_gmt( $date_gmt, $date ) {
 592          if ( $date !== '0000-00-00 00:00:00' && $date_gmt === '0000-00-00 00:00:00' ) {
 593              return new IXR_Date( get_gmt_from_date( mysql2date( 'Y-m-d H:i:s', $date, false ), 'Ymd\TH:i:s' ) );
 594          }
 595          return $this->_convert_date( $date_gmt );
 596      }
 597  
 598      /**
 599       * Prepares post data for return in an XML-RPC object.
 600       *
 601       * @access protected
 602       *
 603       * @param array $post The unprepared post data
 604       * @param array $fields The subset of post type fields to return
 605       * @return array The prepared post data
 606       */
 607  	protected function _prepare_post( $post, $fields ) {
 608          // holds the data for this post. built up based on $fields
 609          $_post = array( 'post_id' => strval( $post['ID'] ) );
 610  
 611          // prepare common post fields
 612          $post_fields = array(
 613              'post_title'        => $post['post_title'],
 614              'post_date'         => $this->_convert_date( $post['post_date'] ),
 615              'post_date_gmt'     => $this->_convert_date_gmt( $post['post_date_gmt'], $post['post_date'] ),
 616              'post_modified'     => $this->_convert_date( $post['post_modified'] ),
 617              'post_modified_gmt' => $this->_convert_date_gmt( $post['post_modified_gmt'], $post['post_modified'] ),
 618              'post_status'       => $post['post_status'],
 619              'post_type'         => $post['post_type'],
 620              'post_name'         => $post['post_name'],
 621              'post_author'       => $post['post_author'],
 622              'post_password'     => $post['post_password'],
 623              'post_excerpt'      => $post['post_excerpt'],
 624              'post_content'      => $post['post_content'],
 625              'link'              => post_permalink( $post['ID'] ),
 626              'comment_status'    => $post['comment_status'],
 627              'ping_status'       => $post['ping_status'],
 628              'sticky'            => ( $post['post_type'] === 'post' && is_sticky( $post['ID'] ) ),
 629          );
 630  
 631          // Thumbnail
 632          $post_fields['post_thumbnail'] = array();
 633          $thumbnail_id = get_post_thumbnail_id( $post['ID'] );
 634          if ( $thumbnail_id ) {
 635              $thumbnail_size = current_theme_supports('post-thumbnail') ? 'post-thumbnail' : 'thumbnail';
 636              $post_fields['post_thumbnail'] = $this->_prepare_media_item( get_post( $thumbnail_id ), $thumbnail_size );
 637          }
 638  
 639          // Consider future posts as published
 640          if ( $post_fields['post_status'] === 'future' )
 641              $post_fields['post_status'] = 'publish';
 642  
 643          // Fill in blank post format
 644          $post_fields['post_format'] = get_post_format( $post['ID'] );
 645          if ( empty( $post_fields['post_format'] ) )
 646              $post_fields['post_format'] = 'standard';
 647  
 648          // Merge requested $post_fields fields into $_post
 649          if ( in_array( 'post', $fields ) ) {
 650              $_post = array_merge( $_post, $post_fields );
 651          } else {
 652              $requested_fields = array_intersect_key( $post_fields, array_flip( $fields ) );
 653              $_post = array_merge( $_post, $requested_fields );
 654          }
 655  
 656          $all_taxonomy_fields = in_array( 'taxonomies', $fields );
 657  
 658          if ( $all_taxonomy_fields || in_array( 'terms', $fields ) ) {
 659              $post_type_taxonomies = get_object_taxonomies( $post['post_type'], 'names' );
 660              $terms = wp_get_object_terms( $post['ID'], $post_type_taxonomies );
 661              $_post['terms'] = array();
 662              foreach ( $terms as $term ) {
 663                  $_post['terms'][] = $this->_prepare_term( $term );
 664              }
 665          }
 666  
 667          if ( in_array( 'custom_fields', $fields ) )
 668              $_post['custom_fields'] = $this->get_custom_fields( $post['ID'] );
 669  
 670          if ( in_array( 'enclosure', $fields ) ) {
 671              $_post['enclosure'] = array();
 672              $enclosures = (array) get_post_meta( $post['ID'], 'enclosure' );
 673              if ( ! empty( $enclosures ) ) {
 674                  $encdata = explode( "\n", $enclosures[0] );
 675                  $_post['enclosure']['url'] = trim( htmlspecialchars( $encdata[0] ) );
 676                  $_post['enclosure']['length'] = (int) trim( $encdata[1] );
 677                  $_post['enclosure']['type'] = trim( $encdata[2] );
 678              }
 679          }
 680  
 681          return apply_filters( 'xmlrpc_prepare_post', $_post, $post, $fields );
 682      }
 683  
 684      /**
 685       * Prepares post data for return in an XML-RPC object.
 686       *
 687       * @access protected
 688       *
 689       * @param object $post_type Post type object
 690       * @param array $fields The subset of post fields to return
 691       * @return array The prepared post type data
 692       */
 693  	protected function _prepare_post_type( $post_type, $fields ) {
 694          $_post_type = array(
 695              'name' => $post_type->name,
 696              'label' => $post_type->label,
 697              'hierarchical' => (bool) $post_type->hierarchical,
 698              'public' => (bool) $post_type->public,
 699              'show_ui' => (bool) $post_type->show_ui,
 700              '_builtin' => (bool) $post_type->_builtin,
 701              'has_archive' => (bool) $post_type->has_archive,
 702              'supports' => get_all_post_type_supports( $post_type->name ),
 703          );
 704  
 705          if ( in_array( 'labels', $fields ) ) {
 706              $_post_type['labels'] = (array) $post_type->labels;
 707          }
 708  
 709          if ( in_array( 'cap', $fields ) ) {
 710              $_post_type['cap'] = (array) $post_type->cap;
 711              $_post_type['map_meta_cap'] = (bool) $post_type->map_meta_cap;
 712          }
 713  
 714          if ( in_array( 'menu', $fields ) ) {
 715              $_post_type['menu_position'] = (int) $post_type->menu_position;
 716              $_post_type['menu_icon'] = $post_type->menu_icon;
 717              $_post_type['show_in_menu'] = (bool) $post_type->show_in_menu;
 718          }
 719  
 720          if ( in_array( 'taxonomies', $fields ) )
 721              $_post_type['taxonomies'] = get_object_taxonomies( $post_type->name, 'names' );
 722  
 723          return apply_filters( 'xmlrpc_prepare_post_type', $_post_type, $post_type );
 724      }
 725  
 726      /**
 727       * Prepares media item data for return in an XML-RPC object.
 728       *
 729       * @access protected
 730       *
 731       * @param object $media_item The unprepared media item data
 732       * @param string $thumbnail_size The image size to use for the thumbnail URL
 733       * @return array The prepared media item data
 734       */
 735  	protected function _prepare_media_item( $media_item, $thumbnail_size = 'thumbnail' ) {
 736          $_media_item = array(
 737              'attachment_id'    => strval( $media_item->ID ),
 738              'date_created_gmt' => $this->_convert_date_gmt( $media_item->post_date_gmt, $media_item->post_date ),
 739              'parent'           => $media_item->post_parent,
 740              'link'             => wp_get_attachment_url( $media_item->ID ),
 741              'title'            => $media_item->post_title,
 742              'caption'          => $media_item->post_excerpt,
 743              'description'      => $media_item->post_content,
 744              'metadata'         => wp_get_attachment_metadata( $media_item->ID ),
 745          );
 746  
 747          $thumbnail_src = image_downsize( $media_item->ID, $thumbnail_size );
 748          if ( $thumbnail_src )
 749              $_media_item['thumbnail'] = $thumbnail_src[0];
 750          else
 751              $_media_item['thumbnail'] = $_media_item['link'];
 752  
 753          return apply_filters( 'xmlrpc_prepare_media_item', $_media_item, $media_item, $thumbnail_size );
 754      }
 755  
 756      /**
 757       * Prepares page data for return in an XML-RPC object.
 758       *
 759       * @access protected
 760       *
 761       * @param object $page The unprepared page data
 762       * @return array The prepared page data
 763       */
 764  	protected function _prepare_page( $page ) {
 765          // Get all of the page content and link.
 766          $full_page = get_extended( $page->post_content );
 767          $link = post_permalink( $page->ID );
 768  
 769          // Get info the page parent if there is one.
 770          $parent_title = "";
 771          if ( ! empty( $page->post_parent ) ) {
 772              $parent = get_page( $page->post_parent );
 773              $parent_title = $parent->post_title;
 774          }
 775  
 776          // Determine comment and ping settings.
 777          $allow_comments = comments_open( $page->ID ) ? 1 : 0;
 778          $allow_pings = pings_open( $page->ID ) ? 1 : 0;
 779  
 780          // Format page date.
 781          $page_date = $this->_convert_date( $page->post_date );
 782          $page_date_gmt = $this->_convert_date_gmt( $page->post_date_gmt, $page->post_date );
 783  
 784          // Pull the categories info together.
 785          $categories = array();
 786          foreach ( wp_get_post_categories( $page->ID ) as $cat_id ) {
 787              $categories[] = get_cat_name( $cat_id );
 788          }
 789  
 790          // Get the author info.
 791          $author = get_userdata( $page->post_author );
 792  
 793          $page_template = get_page_template_slug( $page->ID );
 794          if ( empty( $page_template ) )
 795              $page_template = 'default';
 796  
 797          $_page = array(
 798              'dateCreated'            => $page_date,
 799              'userid'                 => $page->post_author,
 800              'page_id'                => $page->ID,
 801              'page_status'            => $page->post_status,
 802              'description'            => $full_page['main'],
 803              'title'                  => $page->post_title,
 804              'link'                   => $link,
 805              'permaLink'              => $link,
 806              'categories'             => $categories,
 807              'excerpt'                => $page->post_excerpt,
 808              'text_more'              => $full_page['extended'],
 809              'mt_allow_comments'      => $allow_comments,
 810              'mt_allow_pings'         => $allow_pings,
 811              'wp_slug'                => $page->post_name,
 812              'wp_password'            => $page->post_password,
 813              'wp_author'              => $author->display_name,
 814              'wp_page_parent_id'      => $page->post_parent,
 815              'wp_page_parent_title'   => $parent_title,
 816              'wp_page_order'          => $page->menu_order,
 817              'wp_author_id'           => (string) $author->ID,
 818              'wp_author_display_name' => $author->display_name,
 819              'date_created_gmt'       => $page_date_gmt,
 820              'custom_fields'          => $this->get_custom_fields( $page->ID ),
 821              'wp_page_template'       => $page_template
 822          );
 823  
 824          return apply_filters( 'xmlrpc_prepare_page', $_page, $page );
 825      }
 826  
 827      /**
 828       * Prepares comment data for return in an XML-RPC object.
 829       *
 830       * @access protected
 831       *
 832       * @param object $comment The unprepared comment data
 833       * @return array The prepared comment data
 834       */
 835  	protected function _prepare_comment( $comment ) {
 836          // Format page date.
 837          $comment_date = $this->_convert_date( $comment->comment_date );
 838          $comment_date_gmt = $this->_convert_date_gmt( $comment->comment_date_gmt, $comment->comment_date );
 839  
 840          if ( '0' == $comment->comment_approved )
 841              $comment_status = 'hold';
 842          else if ( 'spam' == $comment->comment_approved )
 843              $comment_status = 'spam';
 844          else if ( '1' == $comment->comment_approved )
 845              $comment_status = 'approve';
 846          else
 847              $comment_status = $comment->comment_approved;
 848  
 849          $_comment = array(
 850              'date_created_gmt' => $comment_date_gmt,
 851              'user_id'          => $comment->user_id,
 852              'comment_id'       => $comment->comment_ID,
 853              'parent'           => $comment->comment_parent,
 854              'status'           => $comment_status,
 855              'content'          => $comment->comment_content,
 856              'link'             => get_comment_link($comment),
 857              'post_id'          => $comment->comment_post_ID,
 858              'post_title'       => get_the_title($comment->comment_post_ID),
 859              'author'           => $comment->comment_author,
 860              'author_url'       => $comment->comment_author_url,
 861              'author_email'     => $comment->comment_author_email,
 862              'author_ip'        => $comment->comment_author_IP,
 863              'type'             => $comment->comment_type,
 864          );
 865  
 866          return apply_filters( 'xmlrpc_prepare_comment', $_comment, $comment );
 867      }
 868  
 869      /**
 870       * Create a new post for any registered post type.
 871       *
 872       * @since 3.4.0
 873       *
 874       * @param array $args Method parameters. Contains:
 875       *  - int     $blog_id
 876       *  - string  $username
 877       *  - string  $password
 878       *  - array   $content_struct
 879       *      $content_struct can contain:
 880       *      - post_type (default: 'post')
 881       *      - post_status (default: 'draft')
 882       *      - post_title
 883       *      - post_author
 884       *      - post_exerpt
 885       *      - post_content
 886       *      - post_date_gmt | post_date
 887       *      - post_format
 888       *      - post_password
 889       *      - comment_status - can be 'open' | 'closed'
 890       *      - ping_status - can be 'open' | 'closed'
 891       *      - sticky
 892       *      - post_thumbnail - ID of a media item to use as the post thumbnail/featured image
 893       *      - custom_fields - array, with each element containing 'key' and 'value'
 894       *      - terms - array, with taxonomy names as keys and arrays of term IDs as values
 895       *      - terms_names - array, with taxonomy names as keys and arrays of term names as values
 896       *      - enclosure
 897       *      - any other fields supported by wp_insert_post()
 898       * @return string post_id
 899       */
 900  	function wp_newPost( $args ) {
 901          if ( ! $this->minimum_args( $args, 4 ) )
 902              return $this->error;
 903  
 904          $this->escape( $args );
 905  
 906          $blog_id        = (int) $args[0];
 907          $username       = $args[1];
 908          $password       = $args[2];
 909          $content_struct = $args[3];
 910  
 911          if ( ! $user = $this->login( $username, $password ) )
 912              return $this->error;
 913  
 914          do_action( 'xmlrpc_call', 'wp.newPost' );
 915  
 916          unset( $content_struct['ID'] );
 917  
 918          return $this->_insert_post( $user, $content_struct );
 919      }
 920  
 921      /**
 922       * Helper method for filtering out elements from an array.
 923       *
 924       * @since 3.4.0
 925       *
 926       * @param int $count Number to compare to one.
 927       */
 928  	private function _is_greater_than_one( $count ) {
 929          return $count > 1;
 930      }
 931  
 932      /**
 933       * Helper method for wp_newPost and wp_editPost, containing shared logic.
 934       *
 935       * @since 3.4.0
 936       * @uses wp_insert_post()
 937       *
 938       * @param WP_User $user The post author if post_author isn't set in $content_struct.
 939       * @param array $content_struct Post data to insert.
 940       */
 941  	protected function _insert_post( $user, $content_struct ) {
 942          $defaults = array( 'post_status' => 'draft', 'post_type' => 'post', 'post_author' => 0,
 943              'post_password' => '', 'post_excerpt' => '', 'post_content' => '', 'post_title' => '' );
 944  
 945          $post_data = wp_parse_args( $content_struct, $defaults );
 946  
 947          $post_type = get_post_type_object( $post_data['post_type'] );
 948          if ( ! $post_type )
 949              return new IXR_Error( 403, __( 'Invalid post type' ) );
 950  
 951          $update = ! empty( $post_data['ID'] );
 952  
 953          if ( $update ) {
 954              if ( ! get_post( $post_data['ID'] ) )
 955                  return new IXR_Error( 401, __( 'Invalid post ID.' ) );
 956              if ( ! current_user_can( $post_type->cap->edit_post, $post_data['ID'] ) )
 957                  return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit this post.' ) );
 958              if ( $post_data['post_type'] != get_post_type( $post_data['ID'] ) )
 959                  return new IXR_Error( 401, __( 'The post type may not be changed.' ) );
 960          } else {
 961              if ( ! current_user_can( $post_type->cap->edit_posts ) )
 962                  return new IXR_Error( 401, __( 'Sorry, you are not allowed to post on this site.' ) );
 963          }
 964  
 965          switch ( $post_data['post_status'] ) {
 966              case 'draft':
 967              case 'pending':
 968                  break;
 969              case 'private':
 970                  if ( ! current_user_can( $post_type->cap->publish_posts ) )
 971                      return new IXR_Error( 401, __( 'Sorry, you are not allowed to create private posts in this post type' ) );
 972                  break;
 973              case 'publish':
 974              case 'future':
 975                  if ( ! current_user_can( $post_type->cap->publish_posts ) )
 976                      return new IXR_Error( 401, __( 'Sorry, you are not allowed to publish posts in this post type' ) );
 977                  break;
 978              default:
 979                  $post_data['post_status'] = 'draft';
 980              break;
 981          }
 982  
 983          if ( ! empty( $post_data['post_password'] ) && ! current_user_can( $post_type->cap->publish_posts ) )
 984              return new IXR_Error( 401, __( 'Sorry, you are not allowed to create password protected posts in this post type' ) );
 985  
 986          $post_data['post_author'] = absint( $post_data['post_author'] );
 987          if ( ! empty( $post_data['post_author'] ) && $post_data['post_author'] != $user->ID ) {
 988              if ( ! current_user_can( $post_type->cap->edit_others_posts ) )
 989                  return new IXR_Error( 401, __( 'You are not allowed to create posts as this user.' ) );
 990  
 991              $author = get_userdata( $post_data['post_author'] );
 992  
 993              if ( ! $author )
 994                  return new IXR_Error( 404, __( 'Invalid author ID.' ) );
 995          } else {
 996              $post_data['post_author'] = $user->ID;
 997          }
 998  
 999          if ( isset( $post_data['comment_status'] ) && $post_data['comment_status'] != 'open' && $post_data['comment_status'] != 'closed' )
1000              unset( $post_data['comment_status'] );
1001  
1002          if ( isset( $post_data['ping_status'] ) && $post_data['ping_status'] != 'open' && $post_data['ping_status'] != 'closed' )
1003              unset( $post_data['ping_status'] );
1004  
1005          // Do some timestamp voodoo
1006          if ( ! empty( $post_data['post_date_gmt'] ) ) {
1007              // We know this is supposed to be GMT, so we're going to slap that Z on there by force
1008              $dateCreated = rtrim( $post_data['post_date_gmt']->getIso(), 'Z' ) . 'Z';
1009          } elseif ( ! empty( $post_data['post_date'] ) ) {
1010              $dateCreated = $post_data['post_date']->getIso();
1011          }
1012  
1013          if ( ! empty( $dateCreated ) ) {
1014              $post_data['post_date'] = get_date_from_gmt( iso8601_to_datetime( $dateCreated ) );
1015              $post_data['post_date_gmt'] = iso8601_to_datetime( $dateCreated, 'GMT' );
1016          }
1017  
1018          if ( ! isset( $post_data['ID'] ) )
1019              $post_data['ID'] = get_default_post_to_edit( $post_data['post_type'], true )->ID;
1020          $post_ID = $post_data['ID'];
1021  
1022          if ( $post_data['post_type'] == 'post' ) {
1023              // Private and password-protected posts cannot be stickied.
1024              if ( $post_data['post_status'] == 'private' || ! empty( $post_data['post_password'] ) ) {
1025                  // Error if the client tried to stick the post, otherwise, silently unstick.
1026                  if ( ! empty( $post_data['sticky'] ) )
1027                      return new IXR_Error( 401, __( 'Sorry, you cannot stick a private post.' ) );
1028                  if ( $update )
1029                      unstick_post( $post_ID );
1030              } elseif ( isset( $post_data['sticky'] ) )  {
1031                  if ( ! current_user_can( $post_type->cap->edit_others_posts ) )
1032                      return new IXR_Error( 401, __( 'Sorry, you are not allowed to stick this post.' ) );
1033                  if ( $post_data['sticky'] )
1034                      stick_post( $post_ID );
1035                  else
1036                      unstick_post( $post_ID );
1037              }
1038          }
1039  
1040          if ( isset( $post_data['post_thumbnail'] ) ) {
1041              // empty value deletes, non-empty value adds/updates
1042              if ( ! $post_data['post_thumbnail'] )
1043                  delete_post_thumbnail( $post_ID );
1044              elseif ( ! set_post_thumbnail( $post_ID, $post_data['post_thumbnail'] ) )
1045                      return new IXR_Error( 404, __( 'Invalid attachment ID.' ) );
1046              unset( $content_struct['post_thumbnail'] );
1047          }
1048  
1049          if ( isset( $post_data['custom_fields'] ) )
1050              $this->set_custom_fields( $post_ID, $post_data['custom_fields'] );
1051  
1052          if ( isset( $post_data['terms'] ) || isset( $post_data['terms_names'] ) ) {
1053              $post_type_taxonomies = get_object_taxonomies( $post_data['post_type'], 'objects' );
1054  
1055              // accumulate term IDs from terms and terms_names
1056              $terms = array();
1057  
1058              // first validate the terms specified by ID
1059              if ( isset( $post_data['terms'] ) && is_array( $post_data['terms'] ) ) {
1060                  $taxonomies = array_keys( $post_data['terms'] );
1061  
1062                  // validating term ids
1063                  foreach ( $taxonomies as $taxonomy ) {
1064                      if ( ! array_key_exists( $taxonomy , $post_type_taxonomies ) )
1065                          return new IXR_Error( 401, __( 'Sorry, one of the given taxonomies is not supported by the post type.' ) );
1066  
1067                      if ( ! current_user_can( $post_type_taxonomies[$taxonomy]->cap->assign_terms ) )
1068                          return new IXR_Error( 401, __( 'Sorry, you are not allowed to assign a term to one of the given taxonomies.' ) );
1069  
1070                      $term_ids = $post_data['terms'][$taxonomy];
1071                      foreach ( $term_ids as $term_id ) {
1072                          $term = get_term_by( 'id', $term_id, $taxonomy );
1073  
1074                          if ( ! $term )
1075                              return new IXR_Error( 403, __( 'Invalid term ID' ) );
1076  
1077                          $terms[$taxonomy][] = (int) $term_id;
1078                      }
1079                  }
1080              }
1081  
1082              // now validate terms specified by name
1083              if ( isset( $post_data['terms_names'] ) && is_array( $post_data['terms_names'] ) ) {
1084                  $taxonomies = array_keys( $post_data['terms_names'] );
1085  
1086                  foreach ( $taxonomies as $taxonomy ) {
1087                      if ( ! array_key_exists( $taxonomy , $post_type_taxonomies ) )
1088                          return new IXR_Error( 401, __( 'Sorry, one of the given taxonomies is not supported by the post type.' ) );
1089  
1090                      if ( ! current_user_can( $post_type_taxonomies[$taxonomy]->cap->assign_terms ) )
1091                          return new IXR_Error( 401, __( 'Sorry, you are not allowed to assign a term to one of the given taxonomies.' ) );
1092  
1093                      // for hierarchical taxonomies, we can't assign a term when multiple terms in the hierarchy share the same name
1094                      $ambiguous_terms = array();
1095                      if ( is_taxonomy_hierarchical( $taxonomy ) ) {
1096                          $tax_term_names = get_terms( $taxonomy, array( 'fields' => 'names', 'hide_empty' => false ) );
1097  
1098                          // count the number of terms with the same name
1099                          $tax_term_names_count = array_count_values( $tax_term_names );
1100  
1101                          // filter out non-ambiguous term names
1102                          $ambiguous_tax_term_counts = array_filter( $tax_term_names_count, array( $this, '_is_greater_than_one') );
1103  
1104                          $ambiguous_terms = array_keys( $ambiguous_tax_term_counts );
1105                      }
1106  
1107                      $term_names = $post_data['terms_names'][$taxonomy];
1108                      foreach ( $term_names as $term_name ) {
1109                          if ( in_array( $term_name, $ambiguous_terms ) )
1110                              return new IXR_Error( 401, __( 'Ambiguous term name used in a hierarchical taxonomy. Please use term ID instead.' ) );
1111  
1112                          $term = get_term_by( 'name', $term_name, $taxonomy );
1113  
1114                          if ( ! $term ) {
1115                              // term doesn't exist, so check that the user is allowed to create new terms
1116                              if ( ! current_user_can( $post_type_taxonomies[$taxonomy]->cap->edit_terms ) )
1117                                  return new IXR_Error( 401, __( 'Sorry, you are not allowed to add a term to one of the given taxonomies.' ) );
1118  
1119                              // create the new term
1120                              $term_info = wp_insert_term( $term_name, $taxonomy );
1121                              if ( is_wp_error( $term_info ) )
1122                                  return new IXR_Error( 500, $term_info->get_error_message() );
1123  
1124                              $terms[$taxonomy][] = (int) $term_info['term_id'];
1125                          } else {
1126                              $terms[$taxonomy][] = (int) $term->term_id;
1127                          }
1128                      }
1129                  }
1130              }
1131  
1132              $post_data['tax_input'] = $terms;
1133              unset( $post_data['terms'], $post_data['terms_names'] );
1134          } else {
1135              // do not allow direct submission of 'tax_input', clients must use 'terms' and/or 'terms_names'
1136              unset( $post_data['tax_input'], $post_data['post_category'], $post_data['tags_input'] );
1137          }
1138  
1139          if ( isset( $post_data['post_format'] ) ) {
1140              $format = set_post_format( $post_ID, $post_data['post_format'] );
1141  
1142              if ( is_wp_error( $format ) )
1143                  return new IXR_Error( 500, $format->get_error_message() );
1144  
1145              unset( $post_data['post_format'] );
1146          }
1147  
1148          // Handle enclosures
1149          $enclosure = isset( $post_data['enclosure'] ) ? $post_data['enclosure'] : null;
1150          $this->add_enclosure_if_new( $post_ID, $enclosure );
1151  
1152          $this->attach_uploads( $post_ID, $post_data['post_content'] );
1153  
1154          $post_data = apply_filters( 'xmlrpc_wp_insert_post_data', $post_data, $content_struct );
1155  
1156          $post_ID = wp_insert_post( $post_data, true );
1157          if ( is_wp_error( $post_ID ) )
1158              return new IXR_Error( 500, $post_ID->get_error_message() );
1159  
1160          if ( ! $post_ID )
1161              return new IXR_Error( 401, __( 'Sorry, your entry could not be posted. Something wrong happened.' ) );
1162  
1163          return strval( $post_ID );
1164      }
1165  
1166      /**
1167       * Edit a post for any registered post type.
1168       *
1169       * The $content_struct parameter only needs to contain fields that
1170       * should be changed. All other fields will retain their existing values.
1171       *
1172       * @since 3.4.0
1173       *
1174       * @param array $args Method parameters. Contains:
1175       *  - int     $blog_id
1176       *  - string  $username
1177       *  - string  $password
1178       *  - int     $post_id
1179       *  - array   $content_struct
1180       * @return true on success
1181       */
1182  	function wp_editPost( $args ) {
1183          if ( ! $this->minimum_args( $args, 5 ) )
1184              return $this->error;
1185  
1186          $this->escape( $args );
1187  
1188          $blog_id        = (int) $args[0];
1189          $username       = $args[1];
1190          $password       = $args[2];
1191          $post_id        = (int) $args[3];
1192          $content_struct = $args[4];
1193  
1194          if ( ! $user = $this->login( $username, $password ) )
1195              return $this->error;
1196  
1197          do_action( 'xmlrpc_call', 'wp.editPost' );
1198  
1199          $post = get_post( $post_id, ARRAY_A );
1200  
1201          if ( empty( $post['ID'] ) )
1202              return new IXR_Error( 404, __( 'Invalid post ID.' ) );
1203  
1204          // convert the date field back to IXR form
1205          $post['post_date'] = $this->_convert_date( $post['post_date'] );
1206  
1207          // ignore the existing GMT date if it is empty or a non-GMT date was supplied in $content_struct,
1208          // since _insert_post will ignore the non-GMT date if the GMT date is set
1209          if ( $post['post_date_gmt'] == '0000-00-00 00:00:00' || isset( $content_struct['post_date'] ) )
1210              unset( $post['post_date_gmt'] );
1211          else
1212              $post['post_date_gmt'] = $this->_convert_date( $post['post_date_gmt'] );
1213  
1214          $this->escape( $post );
1215          $merged_content_struct = array_merge( $post, $content_struct );
1216  
1217          $retval = $this->_insert_post( $user, $merged_content_struct );
1218          if ( $retval instanceof IXR_Error )
1219              return $retval;
1220  
1221          return true;
1222      }
1223  
1224      /**
1225       * Delete a post for any registered post type.
1226       *
1227       * @since 3.4.0
1228       *
1229       * @uses wp_delete_post()
1230       * @param array $args Method parameters. Contains:
1231       *  - int     $blog_id
1232       *  - string  $username
1233       *  - string  $password
1234       *  - int     $post_id
1235       * @return true on success
1236       */
1237  	function wp_deletePost( $args ) {
1238          if ( ! $this->minimum_args( $args, 4 ) )
1239              return $this->error;
1240  
1241          $this->escape( $args );
1242  
1243          $blog_id    = (int) $args[0];
1244          $username   = $args[1];
1245          $password   = $args[2];
1246          $post_id    = (int) $args[3];
1247  
1248          if ( ! $user = $this->login( $username, $password ) )
1249              return $this->error;
1250  
1251          do_action( 'xmlrpc_call', 'wp.deletePost' );
1252  
1253          $post = wp_get_single_post( $post_id, ARRAY_A );
1254          if ( empty( $post['ID'] ) )
1255              return new IXR_Error( 404, __( 'Invalid post ID.' ) );
1256  
1257          $post_type = get_post_type_object( $post['post_type'] );
1258          if ( ! current_user_can( $post_type->cap->delete_post, $post_id ) )
1259              return new IXR_Error( 401, __( 'Sorry, you are not allowed to delete this post.' ) );
1260  
1261          $result = wp_delete_post( $post_id );
1262  
1263          if ( ! $result )
1264              return new IXR_Error( 500, __( 'The post cannot be deleted.' ) );
1265  
1266          return true;
1267      }
1268  
1269      /**
1270       * Retrieve a post.
1271       *
1272       * @since 3.4.0
1273       *
1274       * The optional $fields parameter specifies what fields will be included
1275       * in the response array. This should be a list of field names. 'post_id' will
1276       * always be included in the response regardless of the value of $fields.
1277       *
1278       * Instead of, or in addition to, individual field names, conceptual group
1279       * names can be used to specify multiple fields. The available conceptual
1280       * groups are 'post' (all basic fields), 'taxonomies', 'custom_fields',
1281       * and 'enclosure'.
1282       *
1283       * @uses wp_get_single_post()
1284       * @param array $args Method parameters. Contains:
1285       *  - int     $post_id
1286       *  - string  $username
1287       *  - string  $password
1288       *  - array   $fields optional
1289       * @return array contains (based on $fields parameter):
1290       *  - 'post_id'
1291       *  - 'post_title'
1292       *  - 'post_date'
1293       *  - 'post_date_gmt'
1294       *  - 'post_modified'
1295       *  - 'post_modified_gmt'
1296       *  - 'post_status'
1297       *  - 'post_type'
1298       *  - 'post_name'
1299       *  - 'post_author'
1300       *  - 'post_password'
1301       *  - 'post_excerpt'
1302       *  - 'post_content'
1303       *  - 'link'
1304       *  - 'comment_status'
1305       *  - 'ping_status'
1306       *  - 'sticky'
1307       *  - 'custom_fields'
1308       *  - 'terms'
1309       *  - 'categories'
1310       *  - 'tags'
1311       *  - 'enclosure'
1312       */
1313  	function wp_getPost( $args ) {
1314          if ( ! $this->minimum_args( $args, 4 ) )
1315              return $this->error;
1316  
1317          $this->escape( $args );
1318  
1319          $blog_id            = (int) $args[0];
1320          $username           = $args[1];
1321          $password           = $args[2];
1322          $post_id            = (int) $args[3];
1323  
1324          if ( isset( $args[4] ) )
1325              $fields = $args[4];
1326          else
1327              $fields = apply_filters( 'xmlrpc_default_post_fields', array( 'post', 'terms', 'custom_fields' ), 'wp.getPost' );
1328  
1329          if ( ! $user = $this->login( $username, $password ) )
1330              return $this->error;
1331  
1332          do_action( 'xmlrpc_call', 'wp.getPost' );
1333  
1334          $post = wp_get_single_post( $post_id, ARRAY_A );
1335  
1336          if ( empty( $post['ID'] ) )
1337              return new IXR_Error( 404, __( 'Invalid post ID.' ) );
1338  
1339          $post_type = get_post_type_object( $post['post_type'] );
1340          if ( ! current_user_can( $post_type->cap->edit_posts, $post_id ) )
1341              return new IXR_Error( 401, __( 'Sorry, you cannot edit this post.' ) );
1342  
1343          return $this->_prepare_post( $post, $fields );
1344      }
1345  
1346      /**
1347       * Retrieve posts.
1348       *
1349       * @since 3.4.0
1350       *
1351       * The optional $filter parameter modifies the query used to retrieve posts.
1352       * Accepted keys are 'post_type', 'post_status', 'number', 'offset',
1353       * 'orderby', and 'order'.
1354       *
1355       * The optional $fields parameter specifies what fields will be included
1356       * in the response array.
1357       *
1358       * @uses wp_get_recent_posts()
1359       * @see wp_getPost() for more on $fields
1360       * @see get_posts() for more on $filter values
1361       *
1362       * @param array $args Method parameters. Contains:
1363       *  - int     $blog_id
1364       *  - string  $username
1365       *  - string  $password
1366       *  - array   $filter optional
1367       *  - array   $fields optional
1368       * @return array contains a collection of posts.
1369       */
1370  	function wp_getPosts( $args ) {
1371          if ( ! $this->minimum_args( $args, 3 ) )
1372              return $this->error;
1373  
1374          $this->escape( $args );
1375  
1376          $blog_id    = (int) $args[0];
1377          $username   = $args[1];
1378          $password   = $args[2];
1379          $filter     = isset( $args[3] ) ? $args[3] : array();
1380  
1381          if ( isset( $args[4] ) )
1382              $fields = $args[4];
1383          else
1384              $fields = apply_filters( 'xmlrpc_default_post_fields', array( 'post', 'terms', 'custom_fields' ), 'wp.getPosts' );
1385  
1386          if ( ! $user = $this->login( $username, $password ) )
1387              return $this->error;
1388  
1389          do_action( 'xmlrpc_call', 'wp.getPosts' );
1390  
1391          $query = array();
1392  
1393          if ( isset( $filter['post_type'] ) ) {
1394              $post_type = get_post_type_object( $filter['post_type'] );
1395              if ( ! ( (bool) $post_type ) )
1396                  return new IXR_Error( 403, __( 'The post type specified is not valid' ) );
1397  
1398              if ( ! current_user_can( $post_type->cap->edit_posts ) )
1399                  return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit posts in this post type' ));
1400  
1401              $query['post_type'] = $filter['post_type'];
1402          }
1403  
1404          if ( isset( $filter['post_status'] ) )
1405              $query['post_status'] = $filter['post_status'];
1406  
1407          if ( isset( $filter['number'] ) )
1408              $query['numberposts'] = absint( $filter['number'] );
1409  
1410          if ( isset( $filter['offset'] ) )
1411              $query['offset'] = absint( $filter['offset'] );
1412  
1413          if ( isset( $filter['orderby'] ) ) {
1414              $query['orderby'] = $filter['orderby'];
1415  
1416              if ( isset( $filter['order'] ) )
1417                  $query['order'] = $filter['order'];
1418          }
1419  
1420          $posts_list = wp_get_recent_posts( $query );
1421  
1422          if ( ! $posts_list )
1423              return array();
1424  
1425          // holds all the posts data
1426          $struct = array();
1427  
1428          foreach ( $posts_list as $post ) {
1429              $post_type = get_post_type_object( $post['post_type'] );
1430              if ( ! current_user_can( $post_type->cap->edit_posts, $post['ID'] ) )
1431                  continue;
1432  
1433              $struct[] = $this->_prepare_post( $post, $fields );
1434          }
1435  
1436          return $struct;
1437      }
1438  
1439      /**
1440       * Create a new term.
1441       *
1442       * @since 3.4.0
1443       *
1444       * @uses wp_insert_term()
1445       * @param array $args Method parameters. Contains:
1446       *  - int     $blog_id
1447       *  - string  $username
1448       *  - string  $password
1449       *  - array   $content_struct
1450       *      The $content_struct must contain:
1451       *      - 'name'
1452       *      - 'taxonomy'
1453       *      Also, it can optionally contain:
1454       *      - 'parent'
1455       *      - 'description'
1456       *      - 'slug'
1457       * @return string term_id
1458       */
1459  	function wp_newTerm( $args ) {
1460          if ( ! $this->minimum_args( $args, 4 ) )
1461              return $this->error;
1462  
1463          $this->escape( $args );
1464  
1465          $blog_id            = (int) $args[0];
1466          $username           = $args[1];
1467          $password           = $args[2];
1468          $content_struct     = $args[3];
1469  
1470          if ( ! $user = $this->login( $username, $password ) )
1471              return $this->error;
1472  
1473          do_action( 'xmlrpc_call', 'wp.newTerm' );
1474  
1475          if ( ! taxonomy_exists( $content_struct['taxonomy'] ) )
1476              return new IXR_Error( 403, __( 'Invalid taxonomy.' ) );
1477  
1478          $taxonomy = get_taxonomy( $content_struct['taxonomy'] );
1479  
1480          if ( ! current_user_can( $taxonomy->cap->manage_terms ) )
1481              return new IXR_Error( 401, __( 'You are not allowed to create terms in this taxonomy.' ) );
1482  
1483          $taxonomy = (array) $taxonomy;
1484  
1485          // hold the data of the term
1486          $term_data = array();
1487  
1488          $term_data['name'] = trim( $content_struct['name'] );
1489          if ( empty( $term_data['name'] ) )
1490              return new IXR_Error( 403, __( 'The term name cannot be empty.' ) );
1491  
1492          if ( isset( $content_struct['parent'] ) ) {
1493              if ( ! $taxonomy['hierarchical'] )
1494                  return new IXR_Error( 403, __( 'This taxonomy is not hierarchical.' ) );
1495  
1496              $parent_term_id = (int) $content_struct['parent'];
1497              $parent_term = get_term( $parent_term_id , $taxonomy['name'] );
1498  
1499              if ( is_wp_error( $parent_term ) )
1500                  return new IXR_Error( 500, $parent_term->get_error_message() );
1501  
1502              if ( ! $parent_term )
1503                  return new IXR_Error( 403, __( 'Parent term does not exist.' ) );
1504  
1505              $term_data['parent'] = $content_struct['parent'];
1506          }
1507  
1508          if ( isset( $content_struct['description'] ) )
1509              $term_data['description'] = $content_struct['description'];
1510  
1511          if ( isset( $content_struct['slug'] ) )
1512              $term_data['slug'] = $content_struct['slug'];
1513  
1514          $term = wp_insert_term( $term_data['name'] , $taxonomy['name'] , $term_data );
1515  
1516          if ( is_wp_error( $term ) )
1517              return new IXR_Error( 500, $term->get_error_message() );
1518  
1519          if ( ! $term )
1520              return new IXR_Error( 500, __( 'Sorry, your term could not be created. Something wrong happened.' ) );
1521  
1522          return strval( $term['term_id'] );
1523      }
1524  
1525      /**
1526       * Edit a term.
1527       *
1528       * @since 3.4.0
1529       *
1530       * @uses wp_update_term()
1531       * @param array $args Method parameters. Contains:
1532       *  - int     $blog_id
1533       *  - string  $username
1534       *  - string  $password
1535       *  - string  $term_id
1536       *  - array   $content_struct
1537       *      The $content_struct must contain:
1538       *      - 'taxonomy'
1539       *      Also, it can optionally contain:
1540       *      - 'name'
1541       *      - 'parent'
1542       *      - 'description'
1543       *      - 'slug'
1544       * @return bool True, on success.
1545       */
1546  	function wp_editTerm( $args ) {
1547          if ( ! $this->minimum_args( $args, 5 ) )
1548              return $this->error;
1549  
1550          $this->escape( $args );
1551  
1552          $blog_id            = (int) $args[0];
1553          $username           = $args[1];
1554          $password           = $args[2];
1555          $term_id            = (int) $args[3];
1556          $content_struct     = $args[4];
1557  
1558          if ( ! $user = $this->login( $username, $password ) )
1559              return $this->error;
1560  
1561          do_action( 'xmlrpc_call', 'wp.editTerm' );
1562  
1563          if ( ! taxonomy_exists( $content_struct['taxonomy'] ) )
1564              return new IXR_Error( 403, __( 'Invalid taxonomy.' ) );
1565  
1566          $taxonomy = get_taxonomy( $content_struct['taxonomy'] );
1567  
1568          if ( ! current_user_can( $taxonomy->cap->edit_terms ) )
1569              return new IXR_Error( 401, __( 'You are not allowed to edit terms in this taxonomy.' ) );
1570  
1571          $taxonomy = (array) $taxonomy;
1572  
1573          // hold the data of the term
1574          $term_data = array();
1575  
1576          $term = get_term( $term_id , $content_struct['taxonomy'] );
1577  
1578          if ( is_wp_error( $term ) )
1579              return new IXR_Error( 500, $term->get_error_message() );
1580  
1581          if ( ! $term )
1582              return new IXR_Error( 404, __( 'Invalid term ID.' ) );
1583  
1584          if ( isset( $content_struct['name'] ) ) {
1585              $term_data['name'] = trim( $content_struct['name'] );
1586  
1587              if ( empty( $term_data['name'] ) )
1588                  return new IXR_Error( 403, __( 'The term name cannot be empty.' ) );
1589          }
1590  
1591          if ( isset( $content_struct['parent'] ) ) {
1592              if ( ! $taxonomy['hierarchical'] )
1593                  return new IXR_Error( 403, __( "This taxonomy is not hierarchical so you can't set a parent." ) );
1594  
1595              $parent_term_id = (int) $content_struct['parent'];
1596              $parent_term = get_term( $parent_term_id , $taxonomy['name'] );
1597  
1598              if ( is_wp_error( $parent_term ) )
1599                  return new IXR_Error( 500, $parent_term->get_error_message() );
1600  
1601              if ( ! $parent_term )
1602                  return new IXR_Error( 403, __( 'Parent term does not exist.' ) );
1603  
1604              $term_data['parent'] = $content_struct['parent'];
1605          }
1606  
1607          if ( isset( $content_struct['description'] ) )
1608              $term_data['description'] = $content_struct['description'];
1609  
1610          if ( isset( $content_struct['slug'] ) )
1611              $term_data['slug'] = $content_struct['slug'];
1612  
1613          $term = wp_update_term( $term_id , $taxonomy['name'] , $term_data );
1614  
1615          if ( is_wp_error( $term ) )
1616              return new IXR_Error( 500, $term->get_error_message() );
1617  
1618          if ( ! $term )
1619              return new IXR_Error( 500, __( 'Sorry, editing the term failed.' ) );
1620  
1621          return true;
1622      }
1623  
1624      /**
1625       * Delete a term.
1626       *
1627       * @since 3.4.0
1628       *
1629       * @uses wp_delete_term()
1630       * @param array $args Method parameters. Contains:
1631       *  - int     $blog_id
1632       *  - string  $username
1633       *  - string  $password
1634       *  - string  $taxnomy_name
1635       *  - string     $term_id
1636       * @return boolean|IXR_Error If it suceeded true else a reason why not
1637       */
1638  	function wp_deleteTerm( $args ) {
1639          if ( ! $this->minimum_args( $args, 5 ) )
1640              return $this->error;
1641  
1642          $this->escape( $args );
1643  
1644          $blog_id            = (int) $args[0];
1645          $username           = $args[1];
1646          $password           = $args[2];
1647          $taxonomy           = $args[3];
1648          $term_id            = (int) $args[4];
1649  
1650          if ( ! $user = $this->login( $username, $password ) )
1651              return $this->error;
1652  
1653          do_action( 'xmlrpc_call', 'wp.deleteTerm' );
1654  
1655          if ( ! taxonomy_exists( $taxonomy ) )
1656              return new IXR_Error( 403, __( 'Invalid taxonomy.' ) );
1657  
1658          $taxonomy = get_taxonomy( $taxonomy );
1659  
1660          if ( ! current_user_can( $taxonomy->cap->delete_terms ) )
1661              return new IXR_Error( 401, __( 'You are not allowed to delete terms in this taxonomy.' ) );
1662  
1663          $term = get_term( $term_id, $taxonomy->name );
1664  
1665          if ( is_wp_error( $term ) )
1666              return new IXR_Error( 500, $term->get_error_message() );
1667  
1668          if ( ! $term )
1669              return new IXR_Error( 404, __( 'Invalid term ID.' ) );
1670  
1671          $result = wp_delete_term( $term_id, $taxonomy->name );
1672  
1673          if ( is_wp_error( $result ) )
1674              return new IXR_Error( 500, $term->get_error_message() );
1675  
1676          if ( ! $result )
1677              return new IXR_Error( 500, __( 'Sorry, deleting the term failed.' ) );
1678  
1679          return $result;
1680      }
1681  
1682      /**
1683       * Retrieve a term.
1684       *
1685       * @since 3.4.0
1686       *
1687       * @uses get_term()
1688       * @param array $args Method parameters. Contains:
1689       *  - int     $blog_id
1690       *  - string  $username
1691       *  - string  $password
1692       *  - string  $taxonomy
1693       *  - string  $term_id
1694       * @return array contains:
1695       *  - 'term_id'
1696       *  - 'name'
1697       *  - 'slug'
1698       *  - 'term_group'
1699       *  - 'term_taxonomy_id'
1700       *  - 'taxonomy'
1701       *  - 'description'
1702       *  - 'parent'
1703       *  - 'count'
1704       */
1705  	function wp_getTerm( $args ) {
1706          if ( ! $this->minimum_args( $args, 5 ) )
1707              return $this->error;
1708  
1709          $this->escape( $args );
1710  
1711          $blog_id            = (int) $args[0];
1712          $username           = $args[1];
1713          $password           = $args[2];
1714          $taxonomy           = $args[3];
1715          $term_id            = (int) $args[4];
1716  
1717          if ( ! $user = $this->login( $username, $password ) )
1718              return $this->error;
1719  
1720          do_action( 'xmlrpc_call', 'wp.getTerm' );
1721  
1722          if ( ! taxonomy_exists( $taxonomy ) )
1723              return new IXR_Error( 403, __( 'Invalid taxonomy.' ) );
1724  
1725          $taxonomy = get_taxonomy( $taxonomy );
1726  
1727          if ( ! current_user_can( $taxonomy->cap->assign_terms ) )
1728              return new IXR_Error( 401, __( 'You are not allowed to assign terms in this taxonomy.' ) );
1729  
1730          $term = get_term( $term_id , $taxonomy->name, ARRAY_A );
1731  
1732          if ( is_wp_error( $term ) )
1733              return new IXR_Error( 500, $term->get_error_message() );
1734  
1735          if ( ! $term )
1736              return new IXR_Error( 404, __( 'Invalid term ID.' ) );
1737  
1738          return $this->_prepare_term( $term );
1739      }
1740  
1741      /**
1742       * Retrieve all terms for a taxonomy.
1743       *
1744       * @since 3.4.0
1745       *
1746       * The optional $filter parameter modifies the query used to retrieve terms.
1747       * Accepted keys are 'number', 'offset', 'orderby', 'order', 'hide_empty', and 'search'.
1748       *
1749       * @uses get_terms()
1750       * @param array $args Method parameters. Contains:
1751       *  - int     $blog_id
1752       *  - string  $username
1753       *  - string  $password
1754       *  - string  $taxonomy
1755       *  - array   $filter optional
1756       * @return array terms
1757       */
1758  	function wp_getTerms( $args ) {
1759          if ( ! $this->minimum_args( $args, 4 ) )
1760              return $this->error;
1761  
1762          $this->escape( $args );
1763  
1764          $blog_id        = (int) $args[0];
1765          $username       = $args[1];
1766          $password       = $args[2];
1767          $taxonomy       = $args[3];
1768          $filter         = isset( $args[4] ) ? $args[4] : array();
1769  
1770          if ( ! $user = $this->login( $username, $password ) )
1771              return $this->error;
1772  
1773          do_action( 'xmlrpc_call', 'wp.getTerms' );
1774  
1775          if ( ! taxonomy_exists( $taxonomy ) )
1776              return new IXR_Error( 403, __( 'Invalid taxonomy.' ) );
1777  
1778          $taxonomy = get_taxonomy( $taxonomy );
1779  
1780          if ( ! current_user_can( $taxonomy->cap->assign_terms ) )
1781              return new IXR_Error( 401, __( 'You are not allowed to assign terms in this taxonomy.' ) );
1782  
1783          $query = array();
1784  
1785          if ( isset( $filter['number'] ) )
1786              $query['number'] = absint( $filter['number'] );
1787  
1788          if ( isset( $filter['offset'] ) )
1789              $query['offset'] = absint( $filter['offset'] );
1790  
1791          if ( isset( $filter['orderby'] ) ) {
1792              $query['orderby'] = $filter['orderby'];
1793  
1794              if ( isset( $filter['order'] ) )
1795                  $query['order'] = $filter['order'];
1796          }
1797  
1798          if ( isset( $filter['hide_empty'] ) )
1799              $query['hide_empty'] = $filter['hide_empty'];
1800          else
1801              $query['get'] = 'all';
1802  
1803          if ( isset( $filter['search'] ) )
1804              $query['search'] = $filter['search'];
1805  
1806          $terms = get_terms( $taxonomy->name, $query );
1807  
1808          if ( is_wp_error( $terms ) )
1809              return new IXR_Error( 500, $terms->get_error_message() );
1810  
1811          $struct = array();
1812  
1813          foreach ( $terms as $term ) {
1814              $struct[] = $this->_prepare_term( $term );
1815          }
1816  
1817          return $struct;
1818      }
1819  
1820      /**
1821       * Retrieve a taxonomy.
1822       *
1823       * @since 3.4.0
1824       *
1825       * @uses get_taxonomy()
1826       * @param array $args Method parameters. Contains:
1827       *  - int     $blog_id
1828       *  - string  $username
1829       *  - string  $password
1830       *  - string  $taxonomy
1831       * @return array (@see get_taxonomy())
1832       */
1833  	function wp_getTaxonomy( $args ) {
1834          if ( ! $this->minimum_args( $args, 4 ) )
1835              return $this->error;
1836  
1837          $this->escape( $args );
1838  
1839          $blog_id        = (int) $args[0];
1840          $username       = $args[1];
1841          $password       = $args[2];
1842          $taxonomy       = $args[3];
1843  
1844          if ( isset( $args[4] ) )
1845              $fields = $args[4];
1846          else
1847              $fields = apply_filters( 'xmlrpc_default_taxonomy_fields', array( 'labels', 'cap', 'object_type' ), 'wp.getTaxonomy' );
1848  
1849          if ( ! $user = $this->login( $username, $password ) )
1850              return $this->error;
1851  
1852          do_action( 'xmlrpc_call', 'wp.getTaxonomy' );
1853  
1854          if ( ! taxonomy_exists( $taxonomy ) )
1855              return new IXR_Error( 403, __( 'Invalid taxonomy.' ) );
1856  
1857          $taxonomy = get_taxonomy( $taxonomy );
1858  
1859          if ( ! current_user_can( $taxonomy->cap->assign_terms ) )
1860              return new IXR_Error( 401, __( 'You are not allowed to assign terms in this taxonomy.' ) );
1861  
1862          return $this->_prepare_taxonomy( $taxonomy, $fields );
1863      }
1864  
1865      /**
1866       * Retrieve all taxonomies.
1867       *
1868       * @since 3.4.0
1869       *
1870       * @uses get_taxonomies()
1871       * @param array $args Method parameters. Contains:
1872       *  - int     $blog_id
1873       *  - string  $username
1874       *  - string  $password
1875       * @return array taxonomies
1876       */
1877  	function wp_getTaxonomies( $args ) {
1878          if ( ! $this->minimum_args( $args, 3 ) )
1879              return $this->error;
1880  
1881          $this->escape( $args );
1882  
1883          $blog_id            = (int) $args[0];
1884          $username           = $args[1];
1885          $password           = $args[2];
1886          $filter             = isset( $args[3] ) ? $args[3] : array( 'public' => true );
1887  
1888          if ( isset( $args[4] ) )
1889              $fields = $args[4];
1890          else
1891              $fields = apply_filters( 'xmlrpc_default_taxonomy_fields', array( 'labels', 'cap', 'object_type' ), 'wp.getTaxonomies' );
1892  
1893          if ( ! $user = $this->login( $username, $password ) )
1894              return $this->error;
1895  
1896          do_action( 'xmlrpc_call', 'wp.getTaxonomies' );
1897  
1898          $taxonomies = get_taxonomies( $filter, 'objects' );
1899  
1900          // holds all the taxonomy data
1901          $struct = array();
1902  
1903          foreach ( $taxonomies as $taxonomy ) {
1904              // capability check for post_types
1905              if ( ! current_user_can( $taxonomy->cap->assign_terms ) )
1906                  continue;
1907  
1908              $struct[] = $this->_prepare_taxonomy( $taxonomy, $fields );
1909          }
1910  
1911          return $struct;
1912      }
1913  
1914      /**
1915       * Retrieve page.
1916       *
1917       * @since 2.2.0
1918       *
1919       * @param array $args Method parameters. Contains:
1920       *  - blog_id
1921       *  - page_id
1922       *  - username
1923       *  - password
1924       * @return array
1925       */
1926  	function wp_getPage($args) {
1927          $this->escape($args);
1928  
1929          $blog_id    = (int) $args[0];
1930          $page_id    = (int) $args[1];
1931          $username    = $args[2];
1932          $password    = $args[3];
1933  
1934          if ( !$user = $this->login($username, $password) ) {
1935              return $this->error;
1936          }
1937  
1938          $page = get_page($page_id);
1939          if ( ! $page )
1940              return new IXR_Error( 404, __( 'Invalid post ID.' ) );
1941  
1942          if ( !current_user_can( 'edit_page', $page_id ) )
1943              return new IXR_Error( 401, __( 'Sorry, you cannot edit this page.' ) );
1944  
1945          do_action('xmlrpc_call', 'wp.getPage');
1946  
1947          // If we found the page then format the data.
1948          if ( $page->ID && ($page->post_type == 'page') ) {
1949              return $this->_prepare_page( $page );
1950          }
1951          // If the page doesn't exist indicate that.
1952          else {
1953              return(new IXR_Error(404, __('Sorry, no such page.')));
1954          }
1955      }
1956  
1957      /**
1958       * Retrieve Pages.
1959       *
1960       * @since 2.2.0
1961       *
1962       * @param array $args Method parameters. Contains:
1963       *  - blog_id
1964       *  - username
1965       *  - password
1966       *  - num_pages
1967       * @return array
1968       */
1969  	function wp_getPages($args) {
1970          $this->escape($args);
1971  
1972          $blog_id    = (int) $args[0];
1973          $username    = $args[1];
1974          $password    = $args[2];
1975          $num_pages    = isset($args[3]) ? (int) $args[3] : 10;
1976  
1977          if ( !$user = $this->login($username, $password) )
1978              return $this->error;
1979  
1980          if ( !current_user_can( 'edit_pages' ) )
1981              return new IXR_Error( 401, __( 'Sorry, you cannot edit pages.' ) );
1982  
1983          do_action('xmlrpc_call', 'wp.getPages');
1984  
1985          $pages = get_posts( array('post_type' => 'page', 'post_status' => 'any', 'numberposts' => $num_pages) );
1986          $num_pages = count($pages);
1987  
1988          // If we have pages, put together their info.
1989          if ( $num_pages >= 1 ) {
1990              $pages_struct = array();
1991  
1992              foreach ($pages as $page) {
1993                  if ( current_user_can( 'edit_page', $page->ID ) )
1994                      $pages_struct[] = $this->_prepare_page( $page );
1995              }
1996  
1997              return($pages_struct);
1998          }
1999          // If no pages were found return an error.
2000          else {
2001              return(array());
2002          }
2003      }
2004  
2005      /**
2006       * Create new page.
2007       *
2008       * @since 2.2.0
2009       *
2010       * @param array $args Method parameters. See {@link wp_xmlrpc_server::mw_newPost()}
2011       * @return unknown
2012       */
2013  	function wp_newPage($args) {
2014          // Items not escaped here will be escaped in newPost.
2015          $username    = $this->escape($args[1]);
2016          $password    = $this->escape($args[2]);
2017          $page        = $args[3];
2018          $publish    = $args[4];
2019  
2020          if ( !$user = $this->login($username, $password) )
2021              return $this->error;
2022  
2023          do_action('xmlrpc_call', 'wp.newPage');
2024  
2025          // Mark this as content for a page.
2026          $args[3]["post_type"] = 'page';
2027  
2028          // Let mw_newPost do all of the heavy lifting.
2029          return($this->mw_newPost($args));
2030      }
2031  
2032      /**
2033       * Delete page.
2034       *
2035       * @since 2.2.0
2036       *
2037       * @param array $args Method parameters.
2038       * @return bool True, if success.
2039       */
2040  	function wp_deletePage($args) {
2041          $this->escape($args);
2042  
2043          $blog_id    = (int) $args[0];
2044          $username    = $args[1];
2045          $password    = $args[2];
2046          $page_id    = (int) $args[3];
2047  
2048          if ( !$user = $this->login($username, $password) )
2049              return $this->error;
2050  
2051          do_action('xmlrpc_call', 'wp.deletePage');
2052  
2053          // Get the current page based on the page_id and
2054          // make sure it is a page and not a post.
2055          $actual_page = wp_get_single_post($page_id, ARRAY_A);
2056          if ( !$actual_page || ($actual_page['post_type'] != 'page') )
2057              return(new IXR_Error(404, __('Sorry, no such page.')));
2058  
2059          // Make sure the user can delete pages.
2060          if ( !current_user_can('delete_page', $page_id) )
2061              return(new IXR_Error(401, __('Sorry, you do not have the right to delete this page.')));
2062  
2063          // Attempt to delete the page.
2064          $result = wp_delete_post($page_id);
2065          if ( !$result )
2066              return(new IXR_Error(500, __('Failed to delete the page.')));
2067  
2068          do_action( 'xmlrpc_call_success_wp_deletePage', $page_id, $args );
2069  
2070          return(true);
2071      }
2072  
2073      /**
2074       * Edit page.
2075       *
2076       * @since 2.2.0
2077       *
2078       * @param array $args Method parameters.
2079       * @return unknown
2080       */
2081  	function wp_editPage($args) {
2082          // Items not escaped here will be escaped in editPost.
2083          $blog_id    = (int) $args[0];
2084          $page_id    = (int) $this->escape($args[1]);
2085          $username    = $this->escape($args[2]);
2086          $password    = $this->escape($args[3]);
2087          $content    = $args[4];
2088          $publish    = $args[5];
2089  
2090          if ( !$user = $this->login($username, $password) )
2091              return $this->error;
2092  
2093          do_action('xmlrpc_call', 'wp.editPage');
2094  
2095          // Get the page data and make sure it is a page.
2096          $actual_page = wp_get_single_post($page_id, ARRAY_A);
2097          if ( !$actual_page || ($actual_page['post_type'] != 'page') )
2098              return(new IXR_Error(404, __('Sorry, no such page.')));
2099  
2100          // Make sure the user is allowed to edit pages.
2101          if ( !current_user_can('edit_page', $page_id) )
2102              return(new IXR_Error(401, __('Sorry, you do not have the right to edit this page.')));
2103  
2104          // Mark this as content for a page.
2105          $content['post_type'] = 'page';
2106  
2107          // Arrange args in the way mw_editPost understands.
2108          $args = array(
2109              $page_id,
2110              $username,
2111              $password,
2112              $content,
2113              $publish
2114          );
2115  
2116          // Let mw_editPost do all of the heavy lifting.
2117          return($this->mw_editPost($args));
2118      }
2119  
2120      /**
2121       * Retrieve page list.
2122       *
2123       * @since 2.2.0
2124       *
2125       * @param array $args Method parameters.
2126       * @return unknown
2127       */
2128  	function wp_getPageList($args) {
2129          global $wpdb;
2130  
2131          $this->escape($args);
2132  
2133          $blog_id                = (int) $args[0];
2134          $username                = $args[1];
2135          $password                = $args[2];
2136  
2137          if ( !$user = $this->login($username, $password) )
2138              return $this->error;
2139  
2140          if ( !current_user_can( 'edit_pages' ) )
2141              return new IXR_Error( 401, __( 'Sorry, you cannot edit pages.' ) );
2142  
2143          do_action('xmlrpc_call', 'wp.getPageList');
2144  
2145          // Get list of pages ids and titles
2146          $page_list = $wpdb->get_results("
2147              SELECT ID page_id,
2148                  post_title page_title,
2149                  post_parent page_parent_id,
2150                  post_date_gmt,
2151                  post_date,
2152                  post_status
2153              FROM {$wpdb->posts}
2154              WHERE post_type = 'page'
2155              ORDER BY ID
2156          ");
2157  
2158          // The date needs to be formatted properly.
2159          $num_pages = count($page_list);
2160          for ( $i = 0; $i < $num_pages; $i++ ) {
2161              $page_list[$i]->dateCreated = $this->_convert_date(  $page_list[$i]->post_date );
2162              $page_list[$i]->date_created_gmt = $this->_convert_date_gmt( $page_list[$i]->post_date_gmt, $page_list[$i]->post_date );
2163  
2164              unset($page_list[$i]->post_date_gmt);
2165              unset($page_list[$i]->post_date);
2166              unset($page_list[$i]->post_status);
2167          }
2168  
2169          return($page_list);
2170      }
2171  
2172      /**
2173       * Retrieve authors list.
2174       *
2175       * @since 2.2.0
2176       *
2177       * @param array $args Method parameters.
2178       * @return array
2179       */
2180  	function wp_getAuthors($args) {
2181  
2182          $this->escape($args);
2183  
2184          $blog_id    = (int) $args[0];
2185          $username    = $args[1];
2186          $password    = $args[2];
2187  
2188          if ( !$user = $this->login($username, $password) )
2189              return $this->error;
2190  
2191          if ( !current_user_can('edit_posts') )
2192              return(new IXR_Error(401, __('Sorry, you cannot edit posts on this site.')));
2193  
2194          do_action('xmlrpc_call', 'wp.getAuthors');
2195  
2196          $authors = array();
2197          foreach ( get_users( array( 'fields' => array('ID','user_login','display_name') ) ) as $user ) {
2198              $authors[] = array(
2199                  'user_id'       => $user->ID,
2200                  'user_login'    => $user->user_login,
2201                  'display_name'  => $user->display_name
2202              );
2203          }
2204  
2205          return $authors;
2206      }
2207  
2208      /**
2209       * Get list of all tags
2210       *
2211       * @since 2.7.0
2212       *
2213       * @param array $args Method parameters.
2214       * @return array
2215       */
2216  	function wp_getTags( $args ) {
2217          $this->escape( $args );
2218  
2219          $blog_id        = (int) $args[0];
2220          $username        = $args[1];
2221          $password        = $args[2];
2222  
2223          if ( !$user = $this->login($username, $password) )
2224              return $this->error;
2225  
2226          if ( !current_user_can( 'edit_posts' ) )
2227              return new IXR_Error( 401, __( 'Sorry, you must be able to edit posts on this site in order to view tags.' ) );
2228  
2229          do_action( 'xmlrpc_call', 'wp.getKeywords' );
2230  
2231          $tags = array();
2232  
2233          if ( $all_tags = get_tags() ) {
2234              foreach( (array) $all_tags as $tag ) {
2235                  $struct['tag_id']            = $tag->term_id;
2236                  $struct['name']                = $tag->name;
2237                  $struct['count']            = $tag->count;
2238                  $struct['slug']                = $tag->slug;
2239                  $struct['html_url']            = esc_html( get_tag_link( $tag->term_id ) );
2240                  $struct['rss_url']            = esc_html( get_tag_feed_link( $tag->term_id ) );
2241  
2242                  $tags[] = $struct;
2243              }
2244          }
2245  
2246          return $tags;
2247      }
2248  
2249      /**
2250       * Create new category.
2251       *
2252       * @since 2.2.0
2253       *
2254       * @param array $args Method parameters.
2255       * @return int Category ID.
2256       */
2257  	function wp_newCategory($args) {
2258          $this->escape($args);
2259  
2260          $blog_id                = (int) $args[0];
2261          $username                = $args[1];
2262          $password                = $args[2];
2263          $category                = $args[3];
2264  
2265          if ( !$user = $this->login($username, $password) )
2266              return $this->error;
2267  
2268          do_action('xmlrpc_call', 'wp.newCategory');
2269  
2270          // Make sure the user is allowed to add a category.
2271          if ( !current_user_can('manage_categories') )
2272              return(new IXR_Error(401, __('Sorry, you do not have the right to add a category.')));
2273  
2274          // If no slug was provided make it empty so that
2275          // WordPress will generate one.
2276          if ( empty($category['slug']) )
2277              $category['slug'] = '';
2278  
2279          // If no parent_id was provided make it empty
2280          // so that it will be a top level page (no parent).
2281          if ( !isset($category['parent_id']) )
2282              $category['parent_id'] = '';
2283  
2284          // If no description was provided make it empty.
2285          if ( empty($category["description"]) )
2286              $category["description"] = "";
2287  
2288          $new_category = array(
2289              'cat_name'                => $category['name'],
2290              'category_nicename'        => $category['slug'],
2291              'category_parent'        => $category['parent_id'],
2292              'category_description'    => $category['description']
2293          );
2294  
2295          $cat_id = wp_insert_category($new_category, true);
2296          if ( is_wp_error( $cat_id ) ) {
2297              if ( 'term_exists' == $cat_id->get_error_code() )
2298                  return (int) $cat_id->get_error_data();
2299              else
2300                  return(new IXR_Error(500, __('Sorry, the new category failed.')));
2301          } elseif ( ! $cat_id ) {
2302              return(new IXR_Error(500, __('Sorry, the new category failed.')));
2303          }
2304  
2305          do_action( 'xmlrpc_call_success_wp_newCategory', $cat_id, $args );
2306  
2307          return $cat_id;
2308      }
2309  
2310      /**
2311       * Remove category.
2312       *
2313       * @since 2.5.0
2314       *
2315       * @param array $args Method parameters.
2316       * @return mixed See {@link wp_delete_term()} for return info.
2317       */
2318  	function wp_deleteCategory($args) {
2319          $this->escape($args);
2320  
2321          $blog_id        = (int) $args[0];
2322          $username        = $args[1];
2323          $password        = $args[2];
2324          $category_id    = (int) $args[3];
2325  
2326          if ( !$user = $this->login($username, $password) )
2327              return $this->error;
2328  
2329          do_action('xmlrpc_call', 'wp.deleteCategory');
2330  
2331          if ( !current_user_can('manage_categories') )
2332              return new IXR_Error( 401, __( 'Sorry, you do not have the right to delete a category.' ) );
2333  
2334          $status = wp_delete_term( $category_id, 'category' );
2335  
2336          if( true == $status )
2337              do_action( 'xmlrpc_call_success_wp_deleteCategory', $category_id, $args );
2338  
2339          return $status;
2340      }
2341  
2342      /**
2343       * Retrieve category list.
2344       *
2345       * @since 2.2.0
2346       *
2347       * @param array $args Method parameters.
2348       * @return array
2349       */
2350  	function wp_suggestCategories($args) {
2351          $this->escape($args);
2352  
2353          $blog_id                = (int) $args[0];
2354          $username                = $args[1];
2355          $password                = $args[2];
2356          $category                = $args[3];
2357          $max_results            = (int) $args[4];
2358  
2359          if ( !$user = $this->login($username, $password) )
2360              return $this->error;
2361  
2362          if ( !current_user_can( 'edit_posts' ) )
2363              return new IXR_Error( 401, __( 'Sorry, you must be able to edit posts to this site in order to view categories.' ) );
2364  
2365          do_action('xmlrpc_call', 'wp.suggestCategories');
2366  
2367          $category_suggestions = array();
2368          $args = array('get' => 'all', 'number' => $max_results, 'name__like' => $category);
2369          foreach ( (array) get_categories($args) as $cat ) {
2370              $category_suggestions[] = array(
2371                  'category_id'    => $cat->term_id,
2372                  'category_name'    => $cat->name
2373              );
2374          }
2375  
2376          return($category_suggestions);
2377      }
2378  
2379      /**
2380       * Retrieve comment.
2381       *
2382       * @since 2.7.0
2383       *
2384       * @param array $args Method parameters.
2385       * @return array
2386       */
2387  	function wp_getComment($args) {
2388          $this->escape($args);
2389  
2390          $blog_id    = (int) $args[0];
2391          $username    = $args[1];
2392          $password    = $args[2];
2393          $comment_id    = (int) $args[3];
2394  
2395          if ( !$user = $this->login($username, $password) )
2396              return $this->error;
2397  
2398          if ( !current_user_can( 'moderate_comments' ) )
2399              return new IXR_Error( 403, __( 'You are not allowed to moderate comments on this site.' ) );
2400  
2401          do_action('xmlrpc_call', 'wp.getComment');
2402  
2403          if ( ! $comment = get_comment($comment_id) )
2404              return new IXR_Error( 404, __( 'Invalid comment ID.' ) );
2405  
2406          return $this->_prepare_comment( $comment );
2407      }
2408  
2409      /**
2410       * Retrieve comments.
2411       *
2412       * Besides the common blog_id, username, and password arguments, it takes a filter
2413       * array as last argument.
2414       *
2415       * Accepted 'filter' keys are 'status', 'post_id', 'offset', and 'number'.
2416       *
2417       * The defaults are as follows:
2418       * - 'status' - Default is ''. Filter by status (e.g., 'approve', 'hold')
2419       * - 'post_id' - Default is ''. The post where the comment is posted. Empty string shows all comments.
2420       * - 'number' - Default is 10. Total number of media items to retrieve.
2421       * - 'offset' - Default is 0. See {@link WP_Query::query()} for more.
2422       *
2423       * @since 2.7.0
2424       *
2425       * @param array $args Method parameters.
2426       * @return array. Contains a collection of comments. See {@link wp_xmlrpc_server::wp_getComment()} for a description of each item contents
2427       */
2428  	function wp_getComments($args) {
2429          $this->escape($args);
2430  
2431          $blog_id    = (int) $args[0];
2432          $username    = $args[1];
2433          $password    = $args[2];
2434          $struct        = isset( $args[3] ) ? $args[3] : array();
2435  
2436          if ( !$user = $this->login($username, $password) )
2437              return $this->error;
2438  
2439          if ( !current_user_can( 'moderate_comments' ) )
2440              return new IXR_Error( 401, __( 'Sorry, you cannot edit comments.' ) );
2441  
2442          do_action('xmlrpc_call', 'wp.getComments');
2443  
2444          if ( isset($struct['status']) )
2445              $status = $struct['status'];
2446          else
2447              $status = '';
2448  
2449          $post_id = '';
2450          if ( isset($struct['post_id']) )
2451              $post_id = absint($struct['post_id']);
2452  
2453          $offset = 0;
2454          if ( isset($struct['offset']) )
2455              $offset = absint($struct['offset']);
2456  
2457          $number = 10;
2458          if ( isset($struct['number']) )
2459              $number = absint($struct['number']);
2460  
2461          $comments = get_comments( array('status' => $status, 'post_id' => $post_id, 'offset' => $offset, 'number' => $number ) );
2462  
2463          $comments_struct = array();
2464  
2465          foreach ( $comments as $comment ) {
2466              $comments_struct[] = $this->_prepare_comment( $comment );
2467          }
2468  
2469          return $comments_struct;
2470      }
2471  
2472      /**
2473       * Delete a comment.
2474       *
2475       * By default, the comment will be moved to the trash instead of deleted.
2476       * See {@link wp_delete_comment()} for more information on
2477       * this behavior.
2478       *
2479       * @since 2.7.0
2480       *
2481       * @param array $args Method parameters. Contains:
2482       *  - blog_id
2483       *  - username
2484       *  - password
2485       *  - comment_id
2486       * @return mixed {@link wp_delete_comment()}
2487       */
2488  	function wp_deleteComment($args) {
2489          $this->escape($args);
2490  
2491          $blog_id    = (int) $args[0];
2492          $username    = $args[1];
2493          $password    = $args[2];
2494          $comment_ID    = (int) $args[3];
2495  
2496          if ( !$user = $this->login($username, $password) )
2497              return $this->error;
2498  
2499          if ( !current_user_can( 'moderate_comments' ) )
2500              return new IXR_Error( 403, __( 'You are not allowed to moderate comments on this site.' ) );
2501  
2502          if ( ! get_comment($comment_ID) )
2503              return new IXR_Error( 404, __( 'Invalid comment ID.' ) );
2504  
2505          if ( !current_user_can( 'edit_comment', $comment_ID ) )
2506              return new IXR_Error( 403, __( 'You are not allowed to moderate comments on this site.' ) );
2507  
2508          do_action('xmlrpc_call', 'wp.deleteComment');
2509  
2510          $status = wp_delete_comment( $comment_ID );
2511  
2512          if( true == $status )
2513              do_action( 'xmlrpc_call_success_wp_deleteComment', $comment_ID, $args );
2514  
2515          return $status;
2516      }
2517  
2518      /**
2519       * Edit comment.
2520       *
2521       * Besides the common blog_id, username, and password arguments, it takes a
2522       * comment_id integer and a content_struct array as last argument.
2523       *
2524       * The allowed keys in the content_struct array are:
2525       *  - 'author'
2526       *  - 'author_url'
2527       *  - 'author_email'
2528       *  - 'content'
2529       *  - 'date_created_gmt'
2530       *  - 'status'. Common statuses are 'approve', 'hold', 'spam'. See {@link get_comment_statuses()} for more details
2531       *
2532       * @since 2.7.0
2533       *
2534       * @param array $args. Contains:
2535       *  - blog_id
2536       *  - username
2537       *  - password
2538       *  - comment_id
2539       *  - content_struct
2540       * @return bool True, on success.
2541       */
2542  	function wp_editComment($args) {
2543          $this->escape($args);
2544  
2545          $blog_id    = (int) $args[0];
2546          $username    = $args[1];
2547          $password    = $args[2];
2548          $comment_ID    = (int) $args[3];
2549          $content_struct = $args[4];
2550  
2551          if ( !$user = $this->login($username, $password) )
2552              return $this->error;
2553  
2554          if ( !current_user_can( 'moderate_comments' ) )
2555              return new IXR_Error( 403, __( 'You are not allowed to moderate comments on this site.' ) );
2556  
2557          if ( ! get_comment($comment_ID) )
2558              return new IXR_Error( 404, __( 'Invalid comment ID.' ) );
2559  
2560          if ( !current_user_can( 'edit_comment', $comment_ID ) )
2561              return new IXR_Error( 403, __( 'You are not allowed to moderate comments on this site.' ) );
2562  
2563          do_action('xmlrpc_call', 'wp.editComment');
2564  
2565          if ( isset($content_struct['status']) ) {
2566              $statuses = get_comment_statuses();
2567              $statuses = array_keys($statuses);
2568  
2569              if ( ! in_array($content_struct['status'], $statuses) )
2570                  return new IXR_Error( 401, __( 'Invalid comment status.' ) );
2571              $comment_approved = $content_struct['status'];
2572          }
2573  
2574          // Do some timestamp voodoo
2575          if ( !empty( $content_struct['date_created_gmt'] ) ) {
2576              // We know this is supposed to be GMT, so we're going to slap that Z on there by force
2577              $dateCreated = rtrim( $content_struct['date_created_gmt']->getIso(), 'Z' ) . 'Z';
2578              $comment_date = get_date_from_gmt(iso8601_to_datetime($dateCreated));
2579              $comment_date_gmt = iso8601_to_datetime($dateCreated, 'GMT');
2580          }
2581  
2582          if ( isset($content_struct['content']) )
2583              $comment_content = $content_struct['content'];
2584  
2585          if ( isset($content_struct['author']) )
2586              $comment_author = $content_struct['author'];
2587  
2588          if ( isset($content_struct['author_url']) )
2589              $comment_author_url = $content_struct['author_url'];
2590  
2591          if ( isset($content_struct['author_email']) )
2592              $comment_author_email = $content_struct['author_email'];
2593  
2594          // We've got all the data -- post it:
2595          $comment = compact('comment_ID', 'comment_content', 'comment_approved', 'comment_date', 'comment_date_gmt', 'comment_author', 'comment_author_email', 'comment_author_url');
2596  
2597          $result = wp_update_comment($comment);
2598          if ( is_wp_error( $result ) )
2599              return new IXR_Error(500, $result->get_error_message());
2600  
2601          if ( !$result )
2602              return new IXR_Error(500, __('Sorry, the comment could not be edited. Something wrong happened.'));
2603  
2604          do_action( 'xmlrpc_call_success_wp_editComment', $comment_ID, $args );
2605  
2606          return true;
2607      }
2608  
2609      /**
2610       * Create new comment.
2611       *
2612       * @since 2.7.0
2613       *
2614       * @param array $args Method parameters.
2615       * @return mixed {@link wp_new_comment()}
2616       */
2617  	function wp_newComment($args) {
2618          global $wpdb;
2619  
2620          $this->escape($args);
2621  
2622          $blog_id    = (int) $args[0];
2623          $username    = $args[1];
2624          $password    = $args[2];
2625          $post        = $args[3];
2626          $content_struct = $args[4];
2627  
2628          $allow_anon = apply_filters('xmlrpc_allow_anonymous_comments', false);
2629  
2630          $user = $this->login($username, $password);
2631  
2632          if ( !$user ) {
2633              $logged_in = false;
2634              if ( $allow_anon && get_option('comment_registration') )
2635                  return new IXR_Error( 403, __( 'You must be registered to comment' ) );
2636              else if ( !$allow_anon )
2637                  return $this->error;
2638          } else {
2639              $logged_in = true;
2640          }
2641  
2642          if ( is_numeric($post) )
2643              $post_id = absint($post);
2644          else
2645              $post_id = url_to_postid($post);
2646  
2647          if ( ! $post_id )
2648              return new IXR_Error( 404, __( 'Invalid post ID.' ) );
2649  
2650          if ( ! get_post($post_id) )
2651              return new IXR_Error( 404, __( 'Invalid post ID.' ) );
2652  
2653          $comment['comment_post_ID'] = $post_id;
2654  
2655          if ( $logged_in ) {
2656              $comment['comment_author'] = $wpdb->escape( $user->display_name );
2657              $comment['comment_author_email'] = $wpdb->escape( $user->user_email );
2658              $comment['comment_author_url'] = $wpdb->escape( $user->user_url );
2659              $comment['user_ID'] = $user->ID;
2660          } else {
2661              $comment['comment_author'] = '';
2662              if ( isset($content_struct['author']) )
2663                  $comment['comment_author'] = $content_struct['author'];
2664  
2665              $comment['comment_author_email'] = '';
2666              if ( isset($content_struct['author_email']) )
2667                  $comment['comment_author_email'] = $content_struct['author_email'];
2668  
2669              $comment['comment_author_url'] = '';
2670              if ( isset($content_struct['author_url']) )
2671                  $comment['comment_author_url'] = $content_struct['author_url'];
2672  
2673              $comment['user_ID'] = 0;
2674  
2675              if ( get_option('require_name_email') ) {
2676                  if ( 6 > strlen($comment['comment_author_email']) || '' == $comment['comment_author'] )
2677                      return new IXR_Error( 403, __( 'Comment author name and email are required' ) );
2678                  elseif ( !is_email($comment['comment_author_email']) )
2679                      return new IXR_Error( 403, __( 'A valid email address is required' ) );
2680              }
2681          }
2682  
2683          $comment['comment_parent'] = isset($content_struct['comment_parent']) ? absint($content_struct['comment_parent']) : 0;
2684  
2685          $comment['comment_content'] =  isset($content_struct['content']) ? $content_struct['content'] : null;
2686  
2687          do_action('xmlrpc_call', 'wp.newComment');
2688  
2689          $comment_ID = wp_new_comment( $comment );
2690  
2691          do_action( 'xmlrpc_call_success_wp_newComment', $comment_ID, $args );
2692  
2693          return $comment_ID;
2694      }
2695  
2696      /**
2697       * Retrieve all of the comment status.
2698       *
2699       * @since 2.7.0
2700       *
2701       * @param array $args Method parameters.
2702       * @return array
2703       */
2704  	function wp_getCommentStatusList($args) {
2705          $this->escape( $args );
2706  
2707          $blog_id    = (int) $args[0];
2708          $username    = $args[1];
2709          $password    = $args[2];
2710  
2711          if ( !$user = $this->login($username, $password) )
2712              return $this->error;
2713  
2714          if ( !current_user_can( 'moderate_comments' ) )
2715              return new IXR_Error( 403, __( 'You are not allowed access to details about this site.' ) );
2716  
2717          do_action('xmlrpc_call', 'wp.getCommentStatusList');
2718  
2719          return get_comment_statuses();
2720      }
2721  
2722      /**
2723       * Retrieve comment count.
2724       *
2725       * @since 2.5.0
2726       *
2727       * @param array $args Method parameters.
2728       * @return array
2729       */
2730  	function wp_getCommentCount( $args ) {
2731          $this->escape($args);
2732  
2733          $blog_id    = (int) $args[0];
2734          $username    = $args[1];
2735          $password    = $args[2];
2736          $post_id    = (int) $args[3];
2737  
2738          if ( !$user = $this->login($username, $password) )
2739              return $this->error;
2740  
2741          if ( !current_user_can( 'edit_posts' ) )
2742              return new IXR_Error( 403, __( 'You are not allowed access to details about comments.' ) );
2743  
2744          do_action('xmlrpc_call', 'wp.getCommentCount');
2745  
2746          $count = wp_count_comments( $post_id );
2747          return array(
2748              'approved' => $count->approved,
2749              'awaiting_moderation' => $count->moderated,
2750              'spam' => $count->spam,
2751              'total_comments' => $count->total_comments
2752          );
2753      }
2754  
2755      /**
2756       * Retrieve post statuses.
2757       *
2758       * @since 2.5.0
2759       *
2760       * @param array $args Method parameters.
2761       * @return array
2762       */
2763  	function wp_getPostStatusList( $args ) {
2764          $this->escape( $args );
2765  
2766          $blog_id    = (int) $args[0];
2767          $username    = $args[1];
2768          $password    = $args[2];
2769  
2770          if ( !$user = $this->login($username, $password) )
2771              return $this->error;
2772  
2773          if ( !current_user_can( 'edit_posts' ) )
2774              return new IXR_Error( 403, __( 'You are not allowed access to details about this site.' ) );
2775  
2776          do_action('xmlrpc_call', 'wp.getPostStatusList');
2777  
2778          return get_post_statuses();
2779      }
2780  
2781      /**
2782       * Retrieve page statuses.
2783       *
2784       * @since 2.5.0
2785       *
2786       * @param array $args Method parameters.
2787       * @return array
2788       */
2789  	function wp_getPageStatusList( $args ) {
2790          $this->escape( $args );
2791  
2792          $blog_id    = (int) $args[0];
2793          $username    = $args[1];
2794          $password    = $args[2];
2795  
2796          if ( !$user = $this->login($username, $password) )
2797              return $this->error;
2798  
2799          if ( !current_user_can( 'edit_pages' ) )
2800              return new IXR_Error( 403, __( 'You are not allowed access to details about this site.' ) );
2801  
2802          do_action('xmlrpc_call', 'wp.getPageStatusList');
2803  
2804          return get_page_statuses();
2805      }
2806  
2807      /**
2808       * Retrieve page templates.
2809       *
2810       * @since 2.6.0
2811       *
2812       * @param array $args Method parameters.
2813       * @return array
2814       */
2815  	function wp_getPageTemplates( $args ) {
2816          $this->escape( $args );
2817  
2818          $blog_id    = (int) $args[0];
2819          $username    = $args[1];
2820          $password    = $args[2];
2821  
2822          if ( !$user = $this->login($username, $password) )
2823              return $this->error;
2824  
2825          if ( !current_user_can( 'edit_pages' ) )
2826              return new IXR_Error( 403, __( 'You are not allowed access to details about this site.' ) );
2827  
2828          $templates = get_page_templates();
2829          $templates['Default'] = 'default';
2830  
2831          return $templates;
2832      }
2833  
2834      /**
2835       * Retrieve blog options.
2836       *
2837       * @since 2.6.0
2838       *
2839       * @param array $args Method parameters.
2840       * @return array
2841       */
2842  	function wp_getOptions( $args ) {
2843          $this->escape( $args );
2844  
2845          $blog_id    = (int) $args[0];
2846          $username    = $args[1];
2847          $password    = $args[2];
2848          $options    = isset( $args[3] ) ? (array) $args[3] : array();
2849  
2850          if ( !$user = $this->login($username, $password) )
2851              return $this->error;
2852  
2853          // If no specific options where asked for, return all of them
2854          if ( count( $options ) == 0 )
2855              $options = array_keys($this->blog_options);
2856  
2857          return $this->_getOptions($options);
2858      }
2859  
2860      /**
2861       * Retrieve blog options value from list.
2862       *
2863       * @since 2.6.0
2864       *
2865       * @param array $options Options to retrieve.
2866       * @return array
2867       */
2868  	function _getOptions($options) {
2869          $data = array();
2870          foreach ( $options as $option ) {
2871              if ( array_key_exists( $option, $this->blog_options ) ) {
2872                  $data[$option] = $this->blog_options[$option];
2873                  //Is the value static or dynamic?
2874                  if ( isset( $data[$option]['option'] ) ) {
2875                      $data[$option]['value'] = get_option( $data[$option]['option'] );
2876                      unset($data[$option]['option']);
2877                  }
2878              }
2879          }
2880  
2881          return $data;
2882      }
2883  
2884      /**
2885       * Update blog options.
2886       *
2887       * @since 2.6.0
2888       *
2889       * @param array $args Method parameters.
2890       * @return unknown
2891       */
2892  	function wp_setOptions( $args ) {
2893          $this->escape( $args );
2894  
2895          $blog_id    = (int) $args[0];
2896          $username    = $args[1];
2897          $password    = $args[2];
2898          $options    = (array) $args[3];
2899  
2900          if ( !$user = $this->login($username, $password) )
2901              return $this->error;
2902  
2903          if ( !current_user_can( 'manage_options' ) )
2904              return new IXR_Error( 403, __( 'You are not allowed to update options.' ) );
2905  
2906          foreach ( $options as $o_name => $o_value ) {
2907              $option_names[] = $o_name;
2908              if ( !array_key_exists( $o_name, $this->blog_options ) )
2909                  continue;
2910  
2911              if ( $this->blog_options[$o_name]['readonly'] == true )
2912                  continue;
2913  
2914              update_option( $this->blog_options[$o_name]['option'], $o_value );
2915          }
2916  
2917          //Now return the updated values
2918          return $this->_getOptions($option_names);
2919      }
2920  
2921      /**
2922       * Retrieve a media item by ID
2923       *
2924       * @since 3.1.0
2925       *
2926       * @param array $args Method parameters. Contains:
2927       *  - blog_id
2928       *  - username
2929       *  - password
2930       *  - attachment_id
2931       * @return array. Associative array containing:
2932       *  - 'date_created_gmt'
2933       *  - 'parent'
2934       *  - 'link'
2935       *  - 'thumbnail'
2936       *  - 'title'
2937       *  - 'caption'
2938       *  - 'description'
2939       *  - 'metadata'
2940       */
2941  	function wp_getMediaItem($args) {
2942          $this->escape($args);
2943  
2944          $blog_id        = (int) $args[0];
2945          $username        = $args[1];
2946          $password        = $args[2];
2947          $attachment_id    = (int) $args[3];
2948  
2949          if ( !$user = $this->login($username, $password) )
2950              return $this->error;
2951  
2952          if ( !current_user_can( 'upload_files' ) )
2953              return new IXR_Error( 403, __( 'You are not allowed to upload files to this site.' ) );
2954  
2955          do_action('xmlrpc_call', 'wp.getMediaItem');
2956  
2957          if ( ! $attachment = get_post($attachment_id) )
2958              return new IXR_Error( 404, __( 'Invalid attachment ID.' ) );
2959  
2960          return $this->_prepare_media_item( $attachment );
2961      }
2962  
2963      /**
2964       * Retrieves a collection of media library items (or attachments)
2965       *
2966       * Besides the common blog_id, username, and password arguments, it takes a filter
2967       * array as last argument.
2968       *
2969       * Accepted 'filter' keys are 'parent_id', 'mime_type', 'offset', and 'number'.
2970       *
2971       * The defaults are as follows:
2972       * - 'number' - Default is 5. Total number of media items to retrieve.
2973       * - 'offset' - Default is 0. See {@link WP_Query::query()} for more.
2974       * - 'parent_id' - Default is ''. The post where the media item is attached. Empty string shows all media items. 0 shows unattached media items.
2975       * - 'mime_type' - Default is ''. Filter by mime type (e.g., 'image/jpeg', 'application/pdf')
2976       *
2977       * @since 3.1.0
2978       *
2979       * @param array $args Method parameters. Contains:
2980       *  - blog_id
2981       *  - username
2982       *  - password
2983       *  - filter
2984       * @return array. Contains a collection of media items. See {@link wp_xmlrpc_server::wp_getMediaItem()} for a description of each item contents
2985       */
2986  	function wp_getMediaLibrary($args) {
2987          $this->escape($args);
2988  
2989          $blog_id    = (int) $args[0];
2990          $username    = $args[1];
2991          $password    = $args[2];
2992          $struct        = isset( $args[3] ) ? $args[3] : array() ;
2993  
2994          if ( !$user = $this->login($username, $password) )
2995              return $this->error;
2996  
2997          if ( !current_user_can( 'upload_files' ) )
2998              return new IXR_Error( 401, __( 'Sorry, you cannot upload files.' ) );
2999  
3000          do_action('xmlrpc_call', 'wp.getMediaLibrary');
3001  
3002          $parent_id = ( isset($struct['parent_id']) ) ? absint($struct['parent_id']) : '' ;
3003          $mime_type = ( isset($struct['mime_type']) ) ? $struct['mime_type'] : '' ;
3004          $offset = ( isset($struct['offset']) ) ? absint($struct['offset']) : 0 ;
3005          $number = ( isset($struct['number']) ) ? absint($struct['number']) : -1 ;
3006  
3007          $attachments = get_posts( array('post_type' => 'attachment', 'post_parent' => $parent_id, 'offset' => $offset, 'numberposts' => $number, 'post_mime_type' => $mime_type ) );
3008  
3009          $attachments_struct = array();
3010  
3011          foreach ($attachments as $attachment )
3012              $attachments_struct[] = $this->_prepare_media_item( $attachment );
3013  
3014          return $attachments_struct;
3015      }
3016  
3017      /**
3018        * Retrieves a list of post formats used by the site
3019        *
3020        * @since 3.1
3021        *
3022        * @param array $args Method parameters. Contains:
3023        *  - blog_id
3024        *  - username
3025        *  - password
3026        * @return array
3027        */
3028  	function wp_getPostFormats( $args ) {
3029          $this->escape( $args );
3030  
3031          $blog_id = (int) $args[0];
3032          $username = $args[1];
3033          $password = $args[2];
3034  
3035          if ( !$user = $this->login( $username, $password ) )
3036              return $this->error;
3037  
3038          if ( !current_user_can( 'edit_posts' ) )
3039              return new IXR_Error( 403, __( 'You are not allowed access to details about this site.' ) );
3040  
3041          do_action( 'xmlrpc_call', 'wp.getPostFormats' );
3042  
3043          $formats = get_post_format_strings();
3044  
3045          # find out if they want a list of currently supports formats
3046          if ( isset( $args[3] ) && is_array( $args[3] ) ) {
3047              if ( $args[3]['show-supported'] ) {
3048                  if ( current_theme_supports( 'post-formats' ) ) {
3049                      $supported = get_theme_support( 'post-formats' );
3050  
3051                      $data['all'] = $formats;
3052                      $data['supported'] = $supported[0];
3053  
3054                      $formats = $data;
3055                  }
3056              }
3057          }
3058  
3059          return $formats;
3060      }
3061  
3062      /**
3063       * Retrieves a post type
3064       *
3065       * @since 3.4.0
3066       *
3067       * @uses get_post_type_object()
3068       * @param array $args Method parameters. Contains:
3069       *  - int     $blog_id
3070       *  - string  $username
3071       *  - string  $password
3072       *  - string  $post_type_name
3073       *  - array   $fields
3074       * @return array contains:
3075       *  - 'labels'
3076       *  - 'description'
3077       *  - 'capability_type'
3078       *  - 'cap'
3079       *  - 'map_meta_cap'
3080       *  - 'hierarchical'
3081       *  - 'menu_position'
3082       *  - 'taxonomies'
3083       *  - 'supports'
3084       */
3085  	function wp_getPostType( $args ) {
3086          if ( ! $this->minimum_args( $args, 4 ) )
3087              return $this->error;
3088  
3089          $this->escape( $args );
3090  
3091          $blog_id        = (int) $args[0];
3092          $username       = $args[1];
3093          $password       = $args[2];
3094          $post_type_name = $args[3];
3095  
3096          if ( isset( $args[4] ) )
3097              $fields = $args[4];
3098          else
3099              $fields = apply_filters( 'xmlrpc_default_posttype_fields', array( 'labels', 'cap', 'taxonomies' ), 'wp.getPostType' );
3100  
3101          if ( !$user = $this->login( $username, $password ) )
3102              return $this->error;
3103  
3104          do_action( 'xmlrpc_call', 'wp.getPostType' );
3105  
3106          if( ! post_type_exists( $post_type_name ) )
3107              return new IXR_Error( 403, __( 'Invalid post type.' ) );
3108  
3109          $post_type = get_post_type_object( $post_type_name );
3110  
3111          if( ! current_user_can( $post_type->cap->edit_posts ) )
3112              return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit this post type.' ) );
3113  
3114          return $this->_prepare_post_type( $post_type, $fields );
3115      }
3116  
3117      /**
3118       * Retrieves a post types
3119       *
3120       * @since 3.4.0
3121       *
3122       * @uses get_post_types()
3123       * @param array $args Method parameters. Contains:
3124       *  - int     $blog_id
3125       *  - string  $username
3126       *  - string  $password
3127       *  - array   $filter
3128       *  - array   $fields
3129       * @return array
3130       */
3131  	function wp_getPostTypes( $args ) {
3132          if ( ! $this->minimum_args( $args, 3 ) )
3133              return $this->error;
3134  
3135          $this->escape( $args );
3136  
3137          $blog_id            = (int) $args[0];
3138          $username           = $args[1];
3139          $password           = $args[2];
3140          $filter             = isset( $args[3] ) ? $args[3] : array( 'public' => true );
3141  
3142          if ( isset( $args[4] ) )
3143              $fields = $args[4];
3144          else
3145              $fields = apply_filters( 'xmlrpc_default_posttype_fields', array( 'labels', 'cap', 'taxonomies' ), 'wp.getPostTypes' );
3146  
3147          if ( ! $user = $this->login( $username, $password ) )
3148              return $this->error;
3149  
3150          do_action( 'xmlrpc_call', 'wp.getPostTypes' );
3151  
3152          $post_types = get_post_types( $filter, 'objects' );
3153  
3154          $struct = array();
3155  
3156          foreach( $post_types as $post_type ) {
3157              if( ! current_user_can( $post_type->cap->edit_posts ) )
3158                  continue;
3159  
3160              $struct[$post_type->name] = $this->_prepare_post_type( $post_type, $fields );
3161          }
3162  
3163          return $struct;
3164      }
3165  
3166      /* Blogger API functions.
3167       * specs on http://plant.blogger.com/api and http://groups.yahoo.com/group/bloggerDev/
3168       */
3169  
3170      /**
3171       * Retrieve blogs that user owns.
3172       *
3173       * Will make more sense once we support multiple blogs.
3174       *
3175       * @since 1.5.0
3176       *
3177       * @param array $args Method parameters.
3178       * @return array
3179       */
3180  	function blogger_getUsersBlogs($args) {
3181          if ( is_multisite() )
3182              return $this->_multisite_getUsersBlogs($args);
3183  
3184          $this->escape($args);
3185  
3186          $username = $args[1];
3187          $password  = $args[2];
3188  
3189          if ( !$user = $this->login($username, $password) )
3190              return $this->error;
3191  
3192          do_action('xmlrpc_call', 'blogger.getUsersBlogs');
3193  
3194          $is_admin = current_user_can('manage_options');
3195  
3196          $struct = array(
3197              'isAdmin'  => $is_admin,
3198              'url'      => get_option('home') . '/',
3199              'blogid'   => '1',
3200              'blogName' => get_option('blogname'),
3201              'xmlrpc'   => site_url( 'xmlrpc.php' )
3202          );
3203  
3204          return array($struct);
3205      }
3206  
3207      /**
3208       * Private function for retrieving a users blogs for multisite setups
3209       *
3210       * @access protected
3211       */
3212  	function _multisite_getUsersBlogs($args) {
3213          global $current_blog;
3214          $domain = $current_blog->domain;
3215          $path = $current_blog->path . 'xmlrpc.php';
3216          $protocol = is_ssl() ? 'https' : 'http';
3217  
3218          $rpc = new IXR_Client("$protocol://{$domain}{$path}");
3219          $rpc->query('wp.getUsersBlogs', $args[1], $args[2]);
3220          $blogs = $rpc->getResponse();
3221  
3222          if ( isset($blogs['faultCode']) )
3223              return new IXR_Error($blogs['faultCode'], $blogs['faultString']);
3224  
3225          if ( $_SERVER['HTTP_HOST'] == $domain && $_SERVER['REQUEST_URI'] == $path ) {
3226              return $blogs;
3227          } else {
3228              foreach ( (array) $blogs as $blog ) {
3229                  if ( strpos($blog['url'], $_SERVER['HTTP_HOST']) )
3230                      return array($blog);
3231              }
3232              return array();
3233          }
3234      }
3235  
3236      /**
3237       * Retrieve user's data.
3238       *
3239       * Gives your client some info about you, so you don't have to.
3240       *
3241       * @since 1.5.0
3242       *
3243       * @param array $args Method parameters.
3244       * @return array
3245       */
3246  	function blogger_getUserInfo($args) {
3247  
3248          $this->escape($args);
3249  
3250          $username = $args[1];
3251          $password  = $args[2];
3252  
3253          if ( !$user = $this->login($username, $password) )
3254              return $this->error;
3255  
3256          if ( !current_user_can( 'edit_posts' ) )
3257              return new IXR_Error( 401, __( 'Sorry, you do not have access to user data on this site.' ) );
3258  
3259          do_action('xmlrpc_call', 'blogger.getUserInfo');
3260  
3261          $struct = array(
3262              'nickname'  => $user->nickname,
3263              'userid'    => $user->ID,
3264              'url'       => $user->user_url,
3265              'lastname'  => $user->last_name,
3266              'firstname' => $user->first_name
3267          );
3268  
3269          return $struct;
3270      }
3271  
3272      /**
3273       * Retrieve post.
3274       *
3275       * @since 1.5.0
3276       *
3277       * @param array $args Method parameters.
3278       * @return array
3279       */
3280  	function blogger_getPost($args) {
3281  
3282          $this->escape($args);
3283  
3284          $post_ID    = (int) $args[1];
3285          $username = $args[2];
3286          $password  = $args[3];
3287  
3288          if ( !$user = $this->login($username, $password) )
3289              return $this->error;
3290  
3291          $post_data = wp_get_single_post($post_ID, ARRAY_A);
3292          if ( ! $post_data )
3293              return new IXR_Error( 404, __( 'Invalid post ID.' ) );
3294  
3295          if ( !current_user_can( 'edit_post', $post_ID ) )
3296              return new IXR_Error( 401, __( 'Sorry, you cannot edit this post.' ) );
3297  
3298          do_action('xmlrpc_call', 'blogger.getPost');
3299  
3300          $categories = implode(',', wp_get_post_categories($post_ID));
3301  
3302          $content  = '<title>'.stripslashes($post_data['post_title']).'</title>';
3303          $content .= '<category>'.$categories.'</category>';
3304          $content .= stripslashes($post_data['post_content']);
3305  
3306          $struct = array(
3307              'userid'    => $post_data['post_author'],
3308              'dateCreated' => $this->_convert_date( $post_data['post_date'] ),
3309              'content'     => $content,
3310              'postid'  => (string) $post_data['ID']
3311          );
3312  
3313          return $struct;
3314      }
3315  
3316      /**
3317       * Retrieve list of recent posts.
3318       *
3319       * @since 1.5.0
3320       *
3321       * @param array $args Method parameters.
3322       * @return array
3323       */
3324  	function blogger_getRecentPosts($args) {
3325  
3326          $this->escape($args);
3327  
3328          // $args[0] = appkey - ignored
3329          $blog_ID    = (int) $args[1]; /* though we don't use it yet */
3330          $username = $args[2];
3331          $password  = $args[3];
3332          if ( isset( $args[4] ) )
3333              $query = array( 'numberposts' => absint( $args[4] ) );
3334          else
3335              $query = array();
3336  
3337          if ( !$user = $this->login($username, $password) )
3338              return $this->error;
3339  
3340          do_action('xmlrpc_call', 'blogger.getRecentPosts');
3341  
3342          $posts_list = wp_get_recent_posts( $query );
3343  
3344          if ( !$posts_list ) {
3345              $this->error = new IXR_Error(500, __('Either there are no posts, or something went wrong.'));
3346              return $this->error;
3347          }
3348  
3349          foreach ($posts_list as $entry) {
3350              if ( !current_user_can( 'edit_post', $entry['ID'] ) )
3351                  continue;
3352  
3353              $post_date  = $this->_convert_date( $entry['post_date'] );
3354              $categories = implode(',', wp_get_post_categories($entry['ID']));
3355  
3356              $content  = '<title>'.stripslashes($entry['post_title']).'</title>';
3357              $content .= '<category>'.$categories.'</category>';
3358              $content .= stripslashes($entry['post_content']);
3359  
3360              $struct[] = array(
3361                  'userid' => $entry['post_author'],
3362                  'dateCreated' => $post_date,
3363                  'content' => $content,
3364                  'postid' => (string) $entry['ID'],
3365              );
3366  
3367          }
3368  
3369          $recent_posts = array();
3370          for ( $j=0; $j<count($struct); $j++ ) {
3371              array_push($recent_posts, $struct[$j]);
3372          }
3373  
3374          return $recent_posts;
3375      }
3376  
3377      /**
3378       * Retrieve blog_filename content.
3379       *
3380       * @since 1.5.0
3381       *
3382       * @param array $args Method parameters.
3383       * @return string
3384       */
3385  	function blogger_getTemplate($args) {
3386  
3387          $this->escape($args);
3388  
3389          $blog_ID    = (int) $args[1];
3390          $username = $args[2];
3391          $password  = $args[3];
3392          $template   = $args[4]; /* could be 'main' or 'archiveIndex', but we don't use it */
3393  
3394          if ( !$user = $this->login($username, $password) )
3395              return $this->error;
3396  
3397          do_action('xmlrpc_call', 'blogger.getTemplate');
3398  
3399          if ( !current_user_can('edit_themes') )
3400              return new IXR_Error(401, __('Sorry, this user cannot edit the template.'));
3401  
3402          /* warning: here we make the assumption that the blog's URL is on the same server */
3403          $filename = get_option('home') . '/';
3404          $filename = preg_replace('#https?://.+?/#', $_SERVER['DOCUMENT_ROOT'].'/', $filename);
3405  
3406          $f = fopen($filename, 'r');
3407          $content = fread($f, filesize($filename));
3408          fclose($f);
3409  
3410          /* so it is actually editable with a windows/mac client */
3411          // 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);
3412  
3413          return $content;
3414      }
3415  
3416      /**
3417       * Updates the content of blog_filename.
3418       *
3419       * @since 1.5.0
3420       *
3421       * @param array $args Method parameters.
3422       * @return bool True when done.
3423       */
3424  	function blogger_setTemplate($args) {
3425  
3426          $this->escape($args);
3427  
3428          $blog_ID    = (int) $args[1];
3429          $username = $args[2];
3430          $password  = $args[3];
3431          $content    = $args[4];
3432          $template   = $args[5]; /* could be 'main' or 'archiveIndex', but we don't use it */
3433  
3434          if ( !$user = $this->login($username, $password) )
3435              return $this->error;
3436  
3437          do_action('xmlrpc_call', 'blogger.setTemplate');
3438  
3439          if ( !current_user_can('edit_themes') )
3440              return new IXR_Error(401, __('Sorry, this user cannot edit the template.'));
3441  
3442          /* warning: here we make the assumption that the blog's URL is on the same server */
3443          $filename = get_option('home') . '/';
3444          $filename = preg_replace('#https?://.+?/#', $_SERVER['DOCUMENT_ROOT'].'/', $filename);
3445  
3446          if ($f = fopen($filename, 'w+')) {
3447              fwrite($f, $content);
3448              fclose($f);
3449          } else {
3450              return new IXR_Error(500, __('Either the file is not writable, or something wrong happened. The file has not been updated.'));
3451          }
3452  
3453          return true;
3454      }
3455  
3456      /**
3457       * Create new post.
3458       *
3459       * @since 1.5.0
3460       *
3461       * @param array $args Method parameters.
3462       * @return int
3463       */
3464  	function blogger_newPost($args) {
3465  
3466          $this->escape($args);
3467  
3468          $blog_ID    = (int) $args[1]; /* though we don't use it yet */
3469          $username = $args[2];
3470          $password  = $args[3];
3471          $content    = $args[4];
3472          $publish    = $args[5];
3473  
3474          if ( !$user = $this->login($username, $password) )
3475              return $this->error;
3476  
3477          do_action('xmlrpc_call', 'blogger.newPost');
3478  
3479          $cap = ($publish) ? 'publish_posts' : 'edit_posts';
3480          if ( !current_user_can($cap) )
3481              return new IXR_Error(401, __('Sorry, you are not allowed to post on this site.'));
3482  
3483          $post_status = ($publish) ? 'publish' : 'draft';
3484  
3485          $post_author = $user->ID;
3486  
3487          $post_title = xmlrpc_getposttitle($content);
3488          $post_category = xmlrpc_getpostcategory($content);
3489          $post_content = xmlrpc_removepostdata($content);
3490  
3491          $post_date = current_time('mysql');
3492          $post_date_gmt = current_time('mysql', 1);
3493  
3494          $post_data = compact('blog_ID', 'post_author', 'post_date', 'post_date_gmt', 'post_content', 'post_title', 'post_category', 'post_status');
3495  
3496          $post_ID = wp_insert_post($post_data);
3497          if ( is_wp_error( $post_ID ) )
3498              return new IXR_Error(500, $post_ID->get_error_message());
3499  
3500          if ( !$post_ID )
3501              return new IXR_Error(500, __('Sorry, your entry could not be posted. Something wrong happened.'));
3502  
3503          $this->attach_uploads( $post_ID, $post_content );
3504  
3505          do_action( 'xmlrpc_call_success_blogger_newPost', $post_ID, $args );
3506  
3507          return $post_ID;
3508      }
3509  
3510      /**
3511       * Edit a post.
3512       *
3513       * @since 1.5.0
3514       *
3515       * @param array $args Method parameters.
3516       * @return bool true when done.
3517       */
3518  	function blogger_editPost($args) {
3519  
3520          $this->escape($args);
3521  
3522          $post_ID     = (int) $args[1];
3523          $username  = $args[2];
3524          $password   = $args[3];
3525          $content     = $args[4];
3526          $publish     = $args[5];
3527  
3528          if ( !$user = $this->login($username, $password) )
3529              return $this->error;
3530  
3531          do_action('xmlrpc_call', 'blogger.editPost');
3532  
3533          $actual_post = wp_get_single_post($post_ID,ARRAY_A);
3534  
3535          if ( !$actual_post || $actual_post['post_type'] != 'post' )
3536              return new IXR_Error(404, __('Sorry, no such post.'));
3537  
3538          $this->escape($actual_post);
3539  
3540          if ( !current_user_can('edit_post', $post_ID) )
3541              return new IXR_Error(401, __('Sorry, you do not have the right to edit this post.'));
3542  
3543          extract($actual_post, EXTR_SKIP);
3544  
3545          if ( ('publish' == $post_status) && !current_user_can('publish_posts') )
3546              return new IXR_Error(401, __('Sorry, you do not have the right to publish this post.'));
3547  
3548          $post_title = xmlrpc_getposttitle($content);
3549          $post_category = xmlrpc_getpostcategory($content);
3550          $post_content = xmlrpc_removepostdata($content);
3551  
3552          $postdata = compact('ID', 'post_content', 'post_title', 'post_category', 'post_status', 'post_excerpt');
3553  
3554          $result = wp_update_post($postdata);
3555  
3556          if ( !$result )
3557              return new IXR_Error(500, __('For some strange yet very annoying reason, this post could not be edited.'));
3558  
3559          $this->attach_uploads( $ID, $post_content );
3560  
3561          do_action( 'xmlrpc_call_success_blogger_editPost', $post_ID, $args );
3562  
3563          return true;
3564      }
3565  
3566      /**
3567       * Remove a post.
3568       *
3569       * @since 1.5.0
3570       *
3571       * @param array $args Method parameters.
3572       * @return bool True when post is deleted.
3573       */
3574  	function blogger_deletePost($args) {
3575          $this->escape($args);
3576  
3577          $post_ID     = (int) $args[1];
3578          $username  = $args[2];
3579          $password   = $args[3];
3580          $publish     = $args[4];
3581  
3582          if ( !$user = $this->login($username, $password) )
3583              return $this->error;
3584  
3585          do_action('xmlrpc_call', 'blogger.deletePost');
3586  
3587          $actual_post = wp_get_single_post($post_ID,ARRAY_A);
3588  
3589          if ( !$actual_post || $actual_post['post_type'] != 'post' )
3590              return new IXR_Error(404, __('Sorry, no such post.'));
3591  
3592          if ( !current_user_can('delete_post', $post_ID) )
3593              return new IXR_Error(401, __('Sorry, you do not have the right to delete this post.'));
3594  
3595          $result = wp_delete_post($post_ID);
3596  
3597          if ( !$result )
3598              return new IXR_Error(500, __('For some strange yet very annoying reason, this post could not be deleted.'));
3599  
3600          do_action( 'xmlrpc_call_success_blogger_deletePost', $post_ID, $args );
3601  
3602          return true;
3603      }
3604  
3605      /* MetaWeblog API functions
3606       * specs on wherever Dave Winer wants them to be
3607       */
3608  
3609      /**
3610       * Create a new post.
3611       *
3612       * The 'content_struct' argument must contain:
3613       *  - title
3614       *  - description
3615       *  - mt_excerpt
3616       *  - mt_text_more
3617       *  - mt_keywords
3618       *  - mt_tb_ping_urls
3619       *  - categories
3620       *
3621       * Also, it can optionally contain:
3622       *  - wp_slug
3623       *  - wp_password
3624       *  - wp_page_parent_id
3625       *  - wp_page_order
3626       *  - wp_author_id
3627       *  - post_status | page_status - can be 'draft', 'private', 'publish', or 'pending'
3628       *  - mt_allow_comments - can be 'open' or 'closed'
3629       *  - mt_allow_pings - can be 'open' or 'closed'
3630       *  - date_created_gmt
3631       *  - dateCreated
3632       *  - wp_post_thumbnail
3633       *
3634       * @since 1.5.0
3635       *
3636       * @param array $args Method parameters. Contains:
3637       *  - blog_id
3638       *  - username
3639       *  - password
3640       *  - content_struct
3641       *  - publish
3642       * @return int
3643       */
3644  	function mw_newPost($args) {
3645          $this->escape($args);
3646  
3647          $blog_ID     = (int) $args[0];
3648          $username  = $args[1];
3649          $password   = $args[2];
3650          $content_struct = $args[3];
3651          $publish     = isset( $args[4] ) ? $args[4] : 0;
3652  
3653          if ( !$user = $this->login($username, $password) )
3654              return $this->error;
3655  
3656          do_action('xmlrpc_call', 'metaWeblog.newPost');
3657  
3658          $page_template = '';
3659          if ( !empty( $content_struct['post_type'] ) ) {
3660              if ( $content_struct['post_type'] == 'page' ) {
3661                  if ( $publish )
3662                      $cap  = 'publish_pages';
3663                  elseif ( isset( $content_struct['page_status'] ) && 'publish' == $content_struct['page_status'] )
3664                      $cap  = 'publish_pages';
3665                  else
3666                      $cap = 'edit_pages';
3667                  $error_message = __( 'Sorry, you are not allowed to publish pages on this site.' );
3668                  $post_type = 'page';
3669                  if ( !empty( $content_struct['wp_page_template'] ) )
3670                      $page_template = $content_struct['wp_page_template'];
3671              } elseif ( $content_struct['post_type'] == 'post' ) {
3672                  if ( $publish )
3673                      $cap  = 'publish_posts';
3674                  elseif ( isset( $content_struct['post_status'] ) && 'publish' == $content_struct['post_status'] )
3675                      $cap  = 'publish_posts';
3676                  else
3677                      $cap = 'edit_posts';
3678                  $error_message = __( 'Sorry, you are not allowed to publish posts on this site.' );
3679                  $post_type = 'post';
3680              } else {
3681                  // No other post_type values are allowed here
3682                  return new IXR_Error( 401, __( 'Invalid post type.' ) );
3683              }
3684          } else {
3685              if ( $publish )
3686                  $cap  = 'publish_posts';
3687              elseif ( isset( $content_struct['post_status'] ) && 'publish' == $content_struct['post_status'])
3688                  $cap  = 'publish_posts';
3689              else
3690                  $cap = 'edit_posts';
3691              $error_message = __( 'Sorry, you are not allowed to publish posts on this site.' );
3692              $post_type = 'post';
3693          }
3694  
3695          if ( !current_user_can( $cap ) )
3696              return new IXR_Error( 401, $error_message );
3697  
3698          // Check for a valid post format if one was given
3699          if ( isset( $content_struct['wp_post_format'] ) ) {
3700              $content_struct['wp_post_format'] = sanitize_key( $content_struct['wp_post_format'] );
3701              if ( !array_key_exists( $content_struct['wp_post_format'], get_post_format_strings() ) ) {
3702                  return new IXR_Error( 404, __( 'Invalid post format' ) );
3703              }
3704          }
3705  
3706          // Let WordPress generate the post_name (slug) unless
3707          // one has been provided.
3708          $post_name = "";
3709          if ( isset($content_struct['wp_slug']) )
3710              $post_name = $content_struct['wp_slug'];
3711  
3712          // Only use a password if one was given.
3713          if ( isset($content_struct['wp_password']) )
3714              $post_password = $content_struct['wp_password'];
3715  
3716          // Only set a post parent if one was provided.
3717          if ( isset($content_struct['wp_page_parent_id']) )
3718              $post_parent = $content_struct['wp_page_parent_id'];
3719  
3720          // Only set the menu_order if it was provided.
3721          if ( isset($content_struct['wp_page_order']) )
3722              $menu_order = $content_struct['wp_page_order'];
3723  
3724          $post_author = $user->ID;
3725  
3726          // If an author id was provided then use it instead.
3727          if ( isset($content_struct['wp_author_id']) && ($user->ID != $content_struct['wp_author_id']) ) {
3728              switch ( $post_type ) {
3729                  case "post":
3730                      if ( !current_user_can('edit_others_posts') )
3731                          return(new IXR_Error(401, __('You are not allowed to post as this user')));
3732                      break;
3733                  case "page":
3734                      if ( !current_user_can('edit_others_pages') )
3735                          return(new IXR_Error(401, __('You are not allowed to create pages as this user')));
3736                      break;
3737                  default:
3738                      return(new IXR_Error(401, __('Invalid post type.')));
3739                      break;
3740              }
3741              $author = get_userdata( $content_struct['wp_author_id'] );
3742              if ( ! $author )
3743                  return new IXR_Error( 404, __( 'Invalid author ID.' ) );
3744              $post_author = $content_struct['wp_author_id'];
3745          }
3746  
3747          $post_title = isset( $content_struct['title'] ) ? $content_struct['title'] : null;
3748          $post_content = isset( $content_struct['description'] ) ? $content_struct['description'] : null;
3749  
3750          $post_status = $publish ? 'publish' : 'draft';
3751  
3752          if ( isset( $content_struct["{$post_type}_status"] ) ) {
3753              switch ( $content_struct["{$post_type}_status"] ) {
3754                  case 'draft':
3755                  case 'pending':
3756                  case 'private':
3757                  case 'publish':
3758                      $post_status = $content_struct["{$post_type}_status"];
3759                      break;
3760                  default:
3761                      $post_status = $publish ? 'publish' : 'draft';
3762                      break;
3763              }
3764          }
3765  
3766          $post_excerpt = isset($content_struct['mt_excerpt']) ? $content_struct['mt_excerpt'] : null;
3767          $post_more = isset($content_struct['mt_text_more']) ? $content_struct['mt_text_more'] : null;
3768  
3769          $tags_input = isset($content_struct['mt_keywords']) ? $content_struct['mt_keywords'] : null;
3770  
3771          if ( isset($content_struct['mt_allow_comments']) ) {
3772              if ( !is_numeric($content_struct['mt_allow_comments']) ) {
3773                  switch ( $content_struct['mt_allow_comments'] ) {
3774                      case 'closed':
3775                          $comment_status = 'closed';
3776                          break;
3777                      case 'open':
3778                          $comment_status = 'open';
3779                          break;
3780                      default:
3781                          $comment_status = get_option('default_comment_status');
3782                          break;
3783                  }
3784              } else {
3785                  switch ( (int) $content_struct['mt_allow_comments'] ) {
3786                      case 0:
3787                      case 2:
3788                          $comment_status = 'closed';
3789                          break;
3790                      case 1:
3791                          $comment_status = 'open';
3792                          break;
3793                      default:
3794                          $comment_status = get_option('default_comment_status');
3795                          break;
3796                  }
3797              }
3798          } else {
3799              $comment_status = get_option('default_comment_status');
3800          }
3801  
3802          if ( isset($content_struct['mt_allow_pings']) ) {
3803              if ( !is_numeric($content_struct['mt_allow_pings']) ) {
3804                  switch ( $content_struct['mt_allow_pings'] ) {
3805                      case 'closed':
3806                          $ping_status = 'closed';
3807                          break;
3808                      case 'open':
3809                          $ping_status = 'open';
3810                          break;
3811                      default:
3812                          $ping_status = get_option('default_ping_status');
3813                          break;
3814                  }
3815              } else {
3816                  switch ( (int) $content_struct['mt_allow_pings'] ) {
3817                      case 0:
3818                          $ping_status = 'closed';
3819                          break;
3820                      case 1:
3821                          $ping_status = 'open';
3822                          break;
3823                      default:
3824                          $ping_status = get_option('default_ping_status');
3825                          break;
3826                  }
3827              }
3828          } else {
3829              $ping_status = get_option('default_ping_status');
3830          }
3831  
3832          if ( $post_more )
3833              $post_content = $post_content . '<!--more-->' . $post_more;
3834  
3835          $to_ping = null;
3836          if ( isset( $content_struct['mt_tb_ping_urls'] ) ) {
3837              $to_ping = $content_struct['mt_tb_ping_urls'];
3838              if ( is_array($to_ping) )
3839                  $to_ping = implode(' ', $to_ping);
3840          }
3841  
3842          // Do some timestamp voodoo
3843          if ( !empty( $content_struct['date_created_gmt'] ) )
3844              // We know this is supposed to be GMT, so we're going to slap that Z on there by force
3845              $dateCreated = rtrim( $content_struct['date_created_gmt']->getIso(), 'Z' ) . 'Z';
3846          elseif ( !empty( $content_struct['dateCreated']) )
3847              $dateCreated = $content_struct['dateCreated']->getIso();
3848  
3849          if ( !empty( $dateCreated ) ) {
3850              $post_date = get_date_from_gmt(iso8601_to_datetime($dateCreated));
3851              $post_date_gmt = iso8601_to_datetime($dateCreated, 'GMT');
3852          } else {
3853              $post_date = current_time('mysql');
3854              $post_date_gmt = current_time('mysql', 1);
3855          }
3856  
3857          $post_category = array();
3858          if ( isset( $content_struct['categories'] ) ) {
3859              $catnames = $content_struct['categories'];
3860  
3861              if ( is_array($catnames) ) {
3862                  foreach ($catnames as $cat) {
3863                      $post_category[] = get_cat_ID($cat);
3864                  }
3865              }
3866          }
3867  
3868          $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');
3869  
3870          $post_ID = $postdata['ID'] = get_default_post_to_edit( $post_type, true )->ID;
3871  
3872          // Only posts can be sticky
3873          if ( $post_type == 'post' && isset( $content_struct['sticky'] ) ) {
3874              if ( $content_struct['sticky'] == true )
3875                  stick_post( $post_ID );
3876              elseif ( $content_struct['sticky'] == false )
3877                  unstick_post( $post_ID );
3878          }
3879  
3880          if ( isset($content_struct['custom_fields']) )
3881              $this->set_custom_fields($post_ID, $content_struct['custom_fields']);
3882  
3883          if ( isset ( $content_struct['wp_post_thumbnail'] ) ) {
3884              if ( set_post_thumbnail( $post_ID, $content_struct['wp_post_thumbnail'] ) === false )
3885                  return new IXR_Error( 404, __( 'Invalid attachment ID.' ) );
3886  
3887              unset( $content_struct['wp_post_thumbnail'] );
3888          }
3889  
3890          // Handle enclosures
3891          $thisEnclosure = isset($content_struct['enclosure']) ? $content_struct['enclosure'] : null;
3892          $this->add_enclosure_if_new($post_ID, $thisEnclosure);
3893  
3894          $this->attach_uploads( $post_ID, $post_content );
3895  
3896          // Handle post formats if assigned, value is validated earlier
3897          // in this function
3898          if ( isset( $content_struct['wp_post_format'] ) )
3899              wp_set_post_terms( $post_ID, array( 'post-format-' . $content_struct['wp_post_format'] ), 'post_format' );
3900  
3901          $post_ID = wp_insert_post( $postdata, true );
3902          if ( is_wp_error( $post_ID ) )
3903              return new IXR_Error(500, $post_ID->get_error_message());
3904  
3905          if ( !$post_ID )
3906              return new IXR_Error(500, __('Sorry, your entry could not be posted. Something wrong happened.'));
3907  
3908          do_action( 'xmlrpc_call_success_mw_newPost', $post_ID, $args );
3909  
3910          return strval($post_ID);
3911      }
3912  
3913  	function add_enclosure_if_new($post_ID, $enclosure) {
3914          if ( is_array( $enclosure ) && isset( $enclosure['url'] ) && isset( $enclosure['length'] ) && isset( $enclosure['type'] ) ) {
3915  
3916              $encstring = $enclosure['url'] . "\n" . $enclosure['length'] . "\n" . $enclosure['type'];
3917              $found = false;
3918              foreach ( (array) get_post_custom($post_ID) as $key => $val) {
3919                  if ($key == 'enclosure') {
3920                      foreach ( (array) $val as $enc ) {
3921                          if ($enc == $encstring) {
3922                              $found = true;
3923                              break 2;
3924                          }
3925                      }
3926                  }
3927              }
3928              if (!$found)
3929                  add_post_meta( $post_ID, 'enclosure', $encstring );
3930          }
3931      }
3932  
3933      /**
3934       * Attach upload to a post.
3935       *
3936       * @since 2.1.0
3937       *
3938       * @param int $post_ID Post ID.
3939       * @param string $post_content Post Content for attachment.
3940       */
3941  	function attach_uploads( $post_ID, $post_content ) {
3942          global $wpdb;
3943  
3944          // find any unattached files
3945          $attachments = $wpdb->get_results( "SELECT ID, guid FROM {$wpdb->posts} WHERE post_parent = '0' AND post_type = 'attachment'" );
3946          if ( is_array( $attachments ) ) {
3947              foreach ( $attachments as $file ) {
3948                  if ( strpos( $post_content, $file->guid ) !== false )
3949                      $wpdb->update($wpdb->posts, array('post_parent' => $post_ID), array('ID' => $file->ID) );
3950              }
3951          }
3952      }
3953  
3954      /**
3955       * Edit a post.
3956       *
3957       * @since 1.5.0
3958       *
3959       * @param array $args Method parameters.
3960       * @return bool True on success.
3961       */
3962  	function mw_editPost($args) {
3963  
3964          $this->escape($args);
3965  
3966          $post_ID        = (int) $args[0];
3967          $username       = $args[1];
3968          $password       = $args[2];
3969          $content_struct = $args[3];
3970          $publish        = isset( $args[4] ) ? $args[4] : 0;
3971  
3972          if ( ! $user = $this->login($username, $password) )
3973              return $this->error;
3974  
3975          do_action('xmlrpc_call', 'metaWeblog.editPost');
3976  
3977          $postdata = wp_get_single_post( $post_ID, ARRAY_A );
3978  
3979          // If there is no post data for the give post id, stop
3980          // now and return an error. Other wise a new post will be
3981          // created (which was the old behavior).
3982          if ( ! $postdata || empty( $postdata[ 'ID' ] ) )
3983              return new IXR_Error( 404, __( 'Invalid post ID.' ) );
3984  
3985          if ( ! current_user_can( 'edit_post', $post_ID ) )
3986              return new IXR_Error( 401, __( 'Sorry, you do not have the right to edit this post.' ) );
3987  
3988          // Use wp.editPost to edit post types other than post and page.
3989          if ( ! in_array( $postdata[ 'post_type' ], array( 'post', 'page' ) ) )
3990              return new IXR_Error( 401, __( 'Invalid post type.' ) );
3991  
3992          // Thwart attempt to change the post type.
3993          if ( ! empty( $content_struct[ 'post_type' ] ) && ( $content_struct['post_type'] != $postdata[ 'post_type' ] ) )
3994              return new IXR_Error( 401, __( 'The post type may not be changed.' ) );
3995  
3996          // Check for a valid post format if one was given
3997          if ( isset( $content_struct['wp_post_format'] ) ) {
3998              $content_struct['wp_post_format'] = sanitize_key( $content_struct['wp_post_format'] );
3999              if ( !array_key_exists( $content_struct['wp_post_format'], get_post_format_strings() ) ) {
4000                  return new IXR_Error( 404, __( 'Invalid post format' ) );
4001              }
4002          }
4003  
4004          $this->escape($postdata);
4005          extract($postdata, EXTR_SKIP);
4006  
4007          // Let WordPress manage slug if none was provided.
4008          $post_name = "";
4009          $post_name = $postdata['post_name'];
4010          if ( isset($content_struct['wp_slug']) )
4011              $post_name = $content_struct['wp_slug'];
4012  
4013          // Only use a password if one was given.
4014          if ( isset($content_struct['wp_password']) )
4015              $post_password = $content_struct['wp_password'];
4016  
4017          // Only set a post parent if one was given.
4018          if ( isset($content_struct['wp_page_parent_id']) )
4019              $post_parent = $content_struct['wp_page_parent_id'];
4020  
4021          // Only set the menu_order if it was given.
4022          if ( isset($content_struct['wp_page_order']) )
4023              $menu_order = $content_struct['wp_page_order'];
4024  
4025          if ( ! empty( $content_struct['wp_page_template'] ) && 'page' == $post_type )
4026              $page_template = $content_struct['wp_page_template'];
4027  
4028          $post_author = $postdata['post_author'];
4029  
4030          // Only set the post_author if one is set.
4031          if ( isset($content_struct['wp_author_id']) && ($user->ID != $content_struct['wp_author_id']) ) {
4032              switch ( $post_type ) {
4033                  case 'post':
4034                      if ( !current_user_can('edit_others_posts') )
4035                          return(new IXR_Error(401, __('You are not allowed to change the post author as this user.')));
4036                      break;
4037                  case 'page':
4038                      if ( !current_user_can('edit_others_pages') )
4039                          return(new IXR_Error(401, __('You are not allowed to change the page author as this user.')));
4040                      break;
4041                  default:
4042                      return(new IXR_Error(401, __('Invalid post type.')));
4043                      break;
4044              }
4045              $post_author = $content_struct['wp_author_id'];
4046          }
4047  
4048          if ( isset($content_struct['mt_allow_comments']) ) {
4049              if ( !is_numeric($content_struct['mt_allow_comments']) ) {
4050                  switch ( $content_struct['mt_allow_comments'] ) {
4051                      case 'closed':
4052                          $comment_status = 'closed';
4053                          break;
4054                      case 'open':
4055                          $comment_status = 'open';
4056                          break;
4057                      default:
4058                          $comment_status = get_option('default_comment_status');
4059                          break;
4060                  }
4061              } else {
4062                  switch ( (int) $content_struct['mt_allow_comments'] ) {
4063                      case 0:
4064                      case 2:
4065                          $comment_status = 'closed';
4066                          break;
4067                      case 1:
4068                          $comment_status = 'open';
4069                          break;
4070                      default:
4071                          $comment_status = get_option('default_comment_status');
4072                          break;
4073                  }
4074              }
4075          }
4076  
4077          if ( isset($content_struct['mt_allow_pings']) ) {
4078              if ( !is_numeric($content_struct['mt_allow_pings']) ) {
4079                  switch ( $content_struct['mt_allow_pings'] ) {
4080                      case 'closed':
4081                          $ping_status = 'closed';
4082                          break;
4083                      case 'open':
4084                          $ping_status = 'open';
4085                          break;
4086                      default:
4087                          $ping_status = get_option('default_ping_status');
4088                          break;
4089                  }
4090              } else {
4091                  switch ( (int) $content_struct["mt_allow_pings"] ) {
4092                      case 0:
4093                          $ping_status = 'closed';
4094                          break;
4095                      case 1:
4096                          $ping_status = 'open';
4097                          break;
4098                      default:
4099                          $ping_status = get_option('default_ping_status');
4100                          break;
4101                  }
4102              }
4103          }
4104  
4105          if ( isset( $content_struct['title'] ) )
4106              $post_title =  $content_struct['title'];
4107  
4108          if ( isset( $content_struct['description'] ) )
4109              $post_content = $content_struct['description'];
4110  
4111          $post_category = array();
4112          if ( isset( $content_struct['categories'] ) ) {
4113              $catnames = $content_struct['categories'];
4114              if ( is_array($catnames) ) {
4115                  foreach ($catnames as $cat) {
4116                      $post_category[] = get_cat_ID($cat);
4117                  }
4118              }
4119          }
4120  
4121          if ( isset( $content_struct['mt_excerpt'] ) )
4122              $post_excerpt =  $content_struct['mt_excerpt'];
4123  
4124          $post_more = isset( $content_struct['mt_text_more'] ) ? $content_struct['mt_text_more'] : null;
4125  
4126          $post_status = $publish ? 'publish' : 'draft';
4127          if ( isset( $content_struct["{$post_type}_status"] ) ) {
4128              switch( $content_struct["{$post_type}_status"] ) {
4129                  case 'draft':
4130                  case 'pending':
4131                  case 'private':
4132                  case 'publish':
4133                      $post_status = $content_struct["{$post_type}_status"];
4134                      break;
4135                  default:
4136                      $post_status = $publish ? 'publish' : 'draft';
4137                      break;
4138              }
4139          }
4140  
4141          $tags_input = isset( $content_struct['mt_keywords'] ) ? $content_struct['mt_keywords'] : null;
4142  
4143          if ( ('publish' == $post_status) ) {
4144              if ( ( 'page' == $post_type ) && !current_user_can('publish_pages') )
4145                  return new IXR_Error(401, __('Sorry, you do not have the right to publish this page.'));
4146              else if ( !current_user_can('publish_posts') )
4147                  return new IXR_Error(401, __('Sorry, you do not have the right to publish this post.'));
4148          }
4149  
4150          if ( $post_more )
4151              $post_content = $post_content . "<!--more-->" . $post_more;
4152  
4153          $to_ping = null;
4154          if ( isset( $content_struct['mt_tb_ping_urls'] ) ) {
4155              $to_ping = $content_struct['mt_tb_ping_urls'];
4156              if ( is_array($to_ping) )
4157                  $to_ping = implode(' ', $to_ping);
4158          }
4159  
4160          // Do some timestamp voodoo
4161          if ( !empty( $content_struct['date_created_gmt'] ) )
4162              // We know this is supposed to be GMT, so we're going to slap that Z on there by force
4163              $dateCreated = rtrim( $content_struct['date_created_gmt']->getIso(), 'Z' ) . 'Z';
4164          elseif ( !empty( $content_struct['dateCreated']) )
4165              $dateCreated = $content_struct['dateCreated']->getIso();
4166  
4167          if ( !empty( $dateCreated ) ) {
4168              $post_date = get_date_from_gmt(iso8601_to_datetime($dateCreated));
4169              $post_date_gmt = iso8601_to_datetime($dateCreated, 'GMT');
4170          } else {
4171              $post_date     = $postdata['post_date'];
4172              $post_date_gmt = $postdata['post_date_gmt'];
4173          }
4174  
4175          // We've got all the data -- post it:
4176          $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');
4177  
4178          $result = wp_update_post($newpost, true);
4179          if ( is_wp_error( $result ) )
4180              return new IXR_Error(500, $result->get_error_message());
4181  
4182          if ( !$result )
4183              return new IXR_Error(500, __('Sorry, your entry could not be edited. Something wrong happened.'));
4184  
4185          // Only posts can be sticky
4186          if ( $post_type == 'post' && isset( $content_struct['sticky'] ) ) {
4187              if ( $content_struct['sticky'] == true )
4188                  stick_post( $post_ID );
4189              elseif ( $content_struct['sticky'] == false )
4190                  unstick_post( $post_ID );
4191          }
4192  
4193          if ( isset($content_struct['custom_fields']) )
4194              $this->set_custom_fields($post_ID, $content_struct['custom_fields']);
4195  
4196          if ( isset ( $content_struct['wp_post_thumbnail'] ) ) {
4197              // empty value deletes, non-empty value adds/updates
4198              if ( empty( $content_struct['wp_post_thumbnail'] ) ) {
4199                  delete_post_thumbnail( $post_ID );
4200              } else {
4201                  if ( set_post_thumbnail( $post_ID, $content_struct['wp_post_thumbnail'] ) === false )
4202                      return new IXR_Error( 404, __( 'Invalid attachment ID.' ) );
4203              }
4204              unset( $content_struct['wp_post_thumbnail'] );
4205          }
4206  
4207          // Handle enclosures
4208          $thisEnclosure = isset($content_struct['enclosure']) ? $content_struct['enclosure'] : null;
4209          $this->add_enclosure_if_new($post_ID, $thisEnclosure);
4210  
4211          $this->attach_uploads( $ID, $post_content );
4212  
4213          // Handle post formats if assigned, validation is handled
4214          // earlier in this function
4215          if ( isset( $content_struct['wp_post_format'] ) )
4216              wp_set_post_terms( $post_ID, array( 'post-format-' . $content_struct['wp_post_format'] ), 'post_format' );
4217  
4218          do_action( 'xmlrpc_call_success_mw_editPost', $post_ID, $args );
4219  
4220          return true;
4221      }
4222  
4223      /**
4224       * Retrieve post.
4225       *
4226       * @since 1.5.0
4227       *
4228       * @param array $args Method parameters.
4229       * @return array
4230       */
4231  	function mw_getPost($args) {
4232  
4233          $this->escape($args);
4234  
4235          $post_ID     = (int) $args[0];
4236          $username  = $args[1];
4237          $password   = $args[2];
4238  
4239          if ( !$user = $this->login($username, $password) )
4240              return $this->error;
4241  
4242          $postdata = wp_get_single_post($post_ID, ARRAY_A);
4243          if ( ! $postdata )
4244              return new IXR_Error( 404, __( 'Invalid post ID.' ) );
4245  
4246          if ( !current_user_can( 'edit_post', $post_ID ) )
4247              return new IXR_Error( 401, __( 'Sorry, you cannot edit this post.' ) );
4248  
4249          do_action('xmlrpc_call', 'metaWeblog.getPost');
4250  
4251          if ($postdata['post_date'] != '') {
4252              $post_date = $this->_convert_date( $postdata['post_date'] );
4253              $post_date_gmt = $this->_convert_date_gmt( $postdata['post_date_gmt'],  $postdata['post_date'] );
4254              $post_modified = $this->_convert_date( $postdata['post_modified'] );
4255              $post_modified_gmt = $this->_convert_date_gmt( $postdata['post_modified_gmt'], $postdata['post_modified'] );
4256  
4257              $categories = array();
4258              $catids = wp_get_post_categories($post_ID);
4259              foreach($catids as $catid)
4260                  $categories[] = get_cat_name($catid);
4261  
4262              $tagnames = array();
4263              $tags = wp_get_post_tags( $post_ID );
4264              if ( !empty( $tags ) ) {
4265                  foreach ( $tags as $tag )
4266                      $tagnames[] = $tag->name;
4267                  $tagnames = implode( ', ', $tagnames );
4268              } else {
4269                  $tagnames = '';
4270              }
4271  
4272              $post = get_extended($postdata['post_content']);
4273              $link = post_permalink($postdata['ID']);
4274  
4275              // Get the author info.
4276              $author = get_userdata($postdata['post_author']);
4277  
4278              $allow_comments = ('open' == $postdata['comment_status']) ? 1 : 0;
4279              $allow_pings = ('open' == $postdata['ping_status']) ? 1 : 0;
4280  
4281              // Consider future posts as published
4282              if ( $postdata['post_status'] === 'future' )
4283                  $postdata['post_status'] = 'publish';
4284  
4285              // Get post format
4286              $post_format = get_post_format( $post_ID );
4287              if ( empty( $post_format ) )
4288                  $post_format = 'standard';
4289  
4290              $sticky = false;
4291              if ( is_sticky( $post_ID ) )
4292                  $sticky = true;
4293  
4294              $enclosure = array();
4295              foreach ( (array) get_post_custom($post_ID) as $key => $val) {
4296                  if ($key == 'enclosure') {
4297                      foreach ( (array) $val as $enc ) {
4298                          $encdata = explode("\n", $enc);
4299                          $enclosure['url'] = trim(htmlspecialchars($encdata[0]));
4300                          $enclosure['length'] = (int) trim($encdata[1]);
4301                          $enclosure['type'] = trim($encdata[2]);
4302                          break 2;
4303                      }
4304                  }
4305              }
4306  
4307              $resp = array(
4308                  'dateCreated' => $post_date,
4309                  'userid' => $postdata['post_author'],
4310                  'postid' => $postdata['ID'],
4311                  'description' => $post['main'],
4312                  'title' => $postdata['post_title'],
4313                  'link' => $link,
4314                  'permaLink' => $link,
4315                  // commented out because no other tool seems to use this
4316                  //          'content' => $entry['post_content'],
4317                  'categories' => $categories,
4318                  'mt_excerpt' => $postdata['post_excerpt'],
4319                  'mt_text_more' => $post['extended'],
4320                  'wp_more_text' => $post['more_text'],
4321                  'mt_allow_comments' => $allow_comments,
4322                  'mt_allow_pings' => $allow_pings,
4323                  'mt_keywords' => $tagnames,
4324                  'wp_slug' => $postdata['post_name'],
4325                  'wp_password' => $postdata['post_password'],
4326                  'wp_author_id' => (string) $author->ID,
4327                  'wp_author_display_name' => $author->display_name,
4328                  'date_created_gmt' => $post_date_gmt,
4329                  'post_status' => $postdata['post_status'],
4330                  'custom_fields' => $this->get_custom_fields($post_ID),
4331                  'wp_post_format' => $post_format,
4332                  'sticky' => $sticky,
4333                  'date_modified' => $post_modified,
4334                  'date_modified_gmt' => $post_modified_gmt
4335              );
4336  
4337              if ( !empty($enclosure) ) $resp['enclosure'] = $enclosure;
4338  
4339              $resp['wp_post_thumbnail'] = get_post_thumbnail_id( $postdata['ID'] );
4340  
4341              return $resp;
4342          } else {
4343              return new IXR_Error(404, __('Sorry, no such post.'));
4344          }
4345      }
4346  
4347      /**
4348       * Retrieve list of recent posts.
4349       *
4350       * @since 1.5.0
4351       *
4352       * @param array $args Method parameters.
4353       * @return array
4354       */
4355  	function mw_getRecentPosts($args) {
4356  
4357          $this->escape($args);
4358  
4359          $blog_ID     = (int) $args[0];
4360          $username  = $args[1];
4361          $password   = $args[2];
4362          if ( isset( $args[3] ) )
4363              $query = array( 'numberposts' => absint( $args[3] ) );
4364          else
4365              $query = array();
4366  
4367          if ( !$user = $this->login($username, $password) )
4368              return $this->error;
4369  
4370          do_action('xmlrpc_call', 'metaWeblog.getRecentPosts');
4371  
4372          $posts_list = wp_get_recent_posts( $query );
4373  
4374          if ( !$posts_list )
4375              return array();
4376  
4377          foreach ($posts_list as $entry) {
4378              if ( !current_user_can( 'edit_post', $entry['ID'] ) )
4379                  continue;
4380  
4381              $post_date = $this->_convert_date( $entry['post_date'] );
4382              $post_date_gmt = $this->_convert_date_gmt( $entry['post_date_gmt'], $entry['post_date'] );
4383              $post_modified = $this->_convert_date( $entry['post_modified'] );
4384              $post_modified_gmt = $this->_convert_date_gmt( $entry['post_modified_gmt'], $entry['post_modified'] );
4385  
4386              $categories = array();
4387              $catids = wp_get_post_categories($entry['ID']);
4388              foreach( $catids as $catid )
4389                  $categories[] = get_cat_name($catid);
4390  
4391              $tagnames = array();
4392              $tags = wp_get_post_tags( $entry['ID'] );
4393              if ( !empty( $tags ) ) {
4394                  foreach ( $tags as $tag ) {
4395                      $tagnames[] = $tag->name;
4396                  }
4397                  $tagnames = implode( ', ', $tagnames );
4398              } else {
4399                  $tagnames = '';
4400              }
4401  
4402              $post = get_extended($entry['post_content']);
4403              $link = post_permalink($entry['ID']);
4404  
4405              // Get the post author info.
4406              $author = get_userdata($entry['post_author']);
4407  
4408              $allow_comments = ('open' == $entry['comment_status']) ? 1 : 0;
4409              $allow_pings = ('open' == $entry['ping_status']) ? 1 : 0;
4410  
4411              // Consider future posts as published
4412              if ( $entry['post_status'] === 'future' )
4413                  $entry['post_status'] = 'publish';
4414  
4415              // Get post format
4416              $post_format = get_post_format( $entry['ID'] );
4417              if ( empty( $post_format ) )
4418                  $post_format = 'standard';
4419  
4420              $struct[] = array(
4421                  'dateCreated' => $post_date,
4422                  'userid' => $entry['post_author'],
4423                  'postid' => (string) $entry['ID'],
4424                  'description' => $post['main'],
4425                  'title' => $entry['post_title'],
4426                  'link' => $link,
4427                  'permaLink' => $link,
4428                  // commented out because no other tool seems to use this
4429                  // 'content' => $entry['post_content'],
4430                  'categories' => $categories,
4431                  'mt_excerpt' => $entry['post_excerpt'],
4432                  'mt_text_more' => $post['extended'],
4433                  'wp_more_text' => $post['more_text'],
4434                  'mt_allow_comments' => $allow_comments,
4435                  'mt_allow_pings' => $allow_pings,
4436                  'mt_keywords' => $tagnames,
4437                  'wp_slug' => $entry['post_name'],
4438                  'wp_password' => $entry['post_password'],
4439                  'wp_author_id' => (string) $author->ID,
4440                  'wp_author_display_name' => $author->display_name,
4441                  'date_created_gmt' => $post_date_gmt,
4442                  'post_status' => $entry['post_status'],
4443                  'custom_fields' => $this->get_custom_fields($entry['ID']),
4444                  'wp_post_format' => $post_format,
4445                  'date_modified' => $post_modified,
4446                  'date_modified_gmt' => $post_modified_gmt
4447              );
4448  
4449              $entry_index = count( $struct ) - 1;
4450              $struct[ $entry_index ][ 'wp_post_thumbnail' ] = get_post_thumbnail_id( $entry['ID'] );
4451          }
4452  
4453          $recent_posts = array();
4454          for ( $j=0; $j<count($struct); $j++ ) {
4455              array_push($recent_posts, $struct[$j]);
4456          }
4457  
4458          return $recent_posts;
4459      }
4460  
4461      /**
4462       * Retrieve the list of categories on a given blog.
4463       *
4464       * @since 1.5.0
4465       *
4466       * @param array $args Method parameters.
4467       * @return array
4468       */
4469  	function mw_getCategories($args) {
4470  
4471          $this->escape($args);
4472  
4473          $blog_ID     = (int) $args[0];
4474          $username  = $args[1];
4475          $password   = $args[2];
4476  
4477          if ( !$user = $this->login($username, $password) )
4478              return $this->error;
4479  
4480          if ( !current_user_can( 'edit_posts' ) )
4481              return new IXR_Error( 401, __( 'Sorry, you must be able to edit posts on this site in order to view categories.' ) );
4482  
4483          do_action('xmlrpc_call', 'metaWeblog.getCategories');
4484  
4485          $categories_struct = array();
4486  
4487          if ( $cats = get_categories(array('get' => 'all')) ) {
4488              foreach ( $cats as $cat ) {
4489                  $struct['categoryId'] = $cat->term_id;
4490                  $struct['parentId'] = $cat->parent;
4491                  $struct['description'] = $cat->name;
4492                  $struct['categoryDescription'] = $cat->description;
4493                  $struct['categoryName'] = $cat->name;
4494                  $struct['htmlUrl'] = esc_html(get_category_link($cat->term_id));
4495                  $struct['rssUrl'] = esc_html(get_category_feed_link($cat->term_id, 'rss2'));
4496  
4497                  $categories_struct[] = $struct;
4498              }
4499          }
4500  
4501          return $categories_struct;
4502      }
4503  
4504      /**
4505       * Uploads a file, following your settings.
4506       *
4507       * Adapted from a patch by Johann Richard.
4508       *
4509       * @link http://mycvs.org/archives/2004/06/30/file-upload-to-wordpress-in-ecto/
4510       *
4511       * @since 1.5.0
4512       *
4513       * @param array $args Method parameters.
4514       * @return array
4515       */
4516  	function mw_newMediaObject($args) {
4517          global $wpdb;
4518  
4519          $blog_ID     = (int) $args[0];
4520          $username  = $wpdb->escape($args[1]);
4521          $password   = $wpdb->escape($args[2]);
4522          $data        = $args[3];
4523  
4524          $name = sanitize_file_name( $data['name'] );
4525          $type = $data['type'];
4526          $bits = $data['bits'];
4527  
4528          if ( !$user = $this->login($username, $password) )
4529              return $this->error;
4530  
4531          do_action('xmlrpc_call', 'metaWeblog.newMediaObject');
4532  
4533          if ( !current_user_can('upload_files') ) {
4534              $this->error = new IXR_Error(401, __('You are not allowed to upload files to this site.'));
4535              return $this->error;
4536          }
4537  
4538          if ( $upload_err = apply_filters( 'pre_upload_error', false ) )
4539              return new IXR_Error(500, $upload_err);
4540  
4541          if ( !empty($data['overwrite']) && ($data['overwrite'] == true) ) {
4542              // Get postmeta info on the object.
4543              $old_file = $wpdb->get_row("
4544                  SELECT ID
4545                  FROM {$wpdb->posts}
4546                  WHERE post_title = '{$name}'
4547                      AND post_type = 'attachment'
4548              ");
4549  
4550              // Delete previous file.
4551              wp_delete_attachment($old_file->ID);
4552  
4553              // Make sure the new name is different by pre-pending the
4554              // previous post id.
4555              $filename = preg_replace('/^wpid\d+-/', '', $name);
4556              $name = "wpid{$old_file->ID}-{$filename}";
4557          }
4558  
4559          $upload = wp_upload_bits($name, null, $bits);
4560          if ( ! empty($upload['error']) ) {
4561              $errorString = sprintf(__('Could not write file %1$s (%2$s)'), $name, $upload['error']);
4562              return new IXR_Error(500, $errorString);
4563          }
4564          // Construct the attachment array
4565          // attach to post_id 0
4566          $post_id = 0;
4567          $attachment = array(
4568              'post_title' => $name,
4569              'post_content' => '',
4570              'post_type' => 'attachment',
4571              'post_parent' => $post_id,
4572              'post_mime_type' => $type,
4573              'guid' => $upload[ 'url' ]
4574          );
4575  
4576          // Save the data
4577          $id = wp_insert_attachment( $attachment, $upload[ 'file' ], $post_id );
4578          wp_update_attachment_metadata( $id, wp_generate_attachment_metadata( $id, $upload['file'] ) );
4579  
4580          do_action( 'xmlrpc_call_success_mw_newMediaObject', $id, $args );
4581  
4582          $struct = array(
4583              'id'   => strval( $id ),
4584              'file' => $name,
4585              'url'  => $upload[ 'url' ],
4586              'type' => $type
4587          );
4588          return apply_filters( 'wp_handle_upload', $struct, 'upload' );
4589      }
4590  
4591      /* MovableType API functions
4592       * specs on http://www.movabletype.org/docs/mtmanual_programmatic.html
4593       */
4594  
4595      /**
4596       * Retrieve the post titles of recent posts.
4597       *
4598       * @since 1.5.0
4599       *
4600       * @param array $args Method parameters.
4601       * @return array
4602       */
4603  	function mt_getRecentPostTitles($args) {
4604  
4605          $this->escape($args);
4606  
4607          $blog_ID     = (int) $args[0];
4608          $username  = $args[1];
4609          $password   = $args[2];
4610          if ( isset( $args[3] ) )
4611              $query = array( 'numberposts' => absint( $args[3] ) );
4612          else
4613              $query = array();
4614  
4615          if ( !$user = $this->login($username, $password) )
4616              return $this->error;
4617  
4618          do_action('xmlrpc_call', 'mt.getRecentPostTitles');
4619  
4620          $posts_list = wp_get_recent_posts( $query );
4621  
4622          if ( !$posts_list ) {
4623              $this->error = new IXR_Error(500, __('Either there are no posts, or something went wrong.'));
4624              return $this->error;
4625          }
4626  
4627          $struct = array();
4628  
4629          foreach ($posts_list as $entry) {
4630              if ( !current_user_can( 'edit_post', $entry['ID'] ) )
4631                  continue;
4632  
4633              $post_date = $this->_convert_date( $entry['post_date'] );
4634              $post_date_gmt = $this->_convert_date_gmt( $entry['post_date_gmt'], $entry['post_date'] );
4635  
4636              $struct[] = array(
4637                  'dateCreated' => $post_date,
4638                  'userid' => $entry['post_author'],
4639                  'postid' => (string) $entry['ID'],
4640                  'title' => $entry['post_title'],
4641                  'post_status' => $entry['post_status'],
4642                  'date_created_gmt' => $post_date_gmt
4643              );
4644  
4645          }
4646  
4647          $recent_posts = array();
4648          for ( $j=0; $j<count($struct); $j++ ) {
4649              array_push($recent_posts, $struct[$j]);
4650          }
4651  
4652          return $recent_posts;
4653      }
4654  
4655      /**
4656       * Retrieve list of all categories on blog.
4657       *
4658       * @since 1.5.0
4659       *
4660       * @param array $args Method parameters.
4661       * @return array
4662       */
4663  	function mt_getCategoryList($args) {
4664  
4665          $this->escape($args);
4666  
4667          $blog_ID     = (int) $args[0];
4668          $username  = $args[1];
4669          $password   = $args[2];
4670  
4671          if ( !$user = $this->login($username, $password) )
4672              return $this->error;
4673  
4674          if ( !current_user_can( 'edit_posts' ) )
4675              return new IXR_Error( 401, __( 'Sorry, you must be able to edit posts on this site in order to view categories.' ) );
4676  
4677          do_action('xmlrpc_call', 'mt.getCategoryList');
4678  
4679          $categories_struct = array();
4680  
4681          if ( $cats = get_categories(array('hide_empty' => 0, 'hierarchical' => 0)) ) {
4682              foreach ( $cats as $cat ) {
4683                  $struct['categoryId'] = $cat->term_id;
4684                  $struct['categoryName'] = $cat->name;
4685  
4686                  $categories_struct[] = $struct;
4687              }
4688          }
4689  
4690          return $categories_struct;
4691      }
4692  
4693      /**
4694       * Retrieve post categories.
4695       *
4696       * @since 1.5.0
4697       *
4698       * @param array $args Method parameters.
4699       * @return array
4700       */
4701  	function mt_getPostCategories($args) {
4702  
4703          $this->escape($args);
4704  
4705          $post_ID     = (int) $args[0];
4706          $username  = $args[1];
4707          $password   = $args[2];
4708  
4709          if ( !$user = $this->login($username, $password) )
4710              return $this->error;
4711  
4712          if ( ! get_post( $post_ID ) )
4713              return new IXR_Error( 404, __( 'Invalid post ID.' ) );
4714  
4715          if ( !current_user_can( 'edit_post', $post_ID ) )
4716              return new IXR_Error( 401, __( 'Sorry, you can not edit this post.' ) );
4717  
4718          do_action('xmlrpc_call', 'mt.getPostCategories');
4719  
4720          $categories = array();
4721          $catids = wp_get_post_categories(intval($post_ID));
4722          // first listed category will be the primary category
4723          $isPrimary = true;
4724          foreach ( $catids as $catid ) {
4725              $categories[] = array(
4726                  'categoryName' => get_cat_name($catid),
4727                  'categoryId' => (string) $catid,
4728                  'isPrimary' => $isPrimary
4729              );
4730              $isPrimary = false;
4731          }
4732  
4733          return $categories;
4734      }
4735  
4736      /**
4737       * Sets categories for a post.
4738       *
4739       * @since 1.5.0
4740       *
4741       * @param array $args Method parameters.
4742       * @return bool True on success.
4743       */
4744  	function mt_setPostCategories($args) {
4745  
4746          $this->escape($args);
4747  
4748          $post_ID     = (int) $args[0];
4749          $username  = $args[1];
4750          $password   = $args[2];
4751          $categories  = $args[3];
4752  
4753          if ( !$user = $this->login($username, $password) )
4754              return $this->error;
4755  
4756          do_action('xmlrpc_call', 'mt.setPostCategories');
4757  
4758          if ( ! get_post( $post_ID ) )
4759              return new IXR_Error( 404, __( 'Invalid post ID.' ) );
4760  
4761          if ( !current_user_can('edit_post', $post_ID) )
4762              return new IXR_Error(401, __('Sorry, you cannot edit this post.'));
4763  
4764          foreach ( $categories as $cat ) {
4765              $catids[] = $cat['categoryId'];
4766          }
4767  
4768          wp_set_post_categories($post_ID, $catids);
4769  
4770          return true;
4771      }
4772  
4773      /**
4774       * Retrieve an array of methods supported by this server.
4775       *
4776       * @since 1.5.0
4777       *
4778       * @param array $args Method parameters.
4779       * @return array
4780       */
4781  	function mt_supportedMethods($args) {
4782  
4783          do_action('xmlrpc_call', 'mt.supportedMethods');
4784  
4785          $supported_methods = array();
4786          foreach ( $this->methods as $key => $value ) {
4787              $supported_methods[] = $key;
4788          }
4789  
4790          return $supported_methods;
4791      }
4792  
4793      /**
4794       * Retrieve an empty array because we don't support per-post text filters.
4795       *
4796       * @since 1.5.0
4797       *
4798       * @param array $args Method parameters.
4799       */
4800  	function mt_supportedTextFilters($args) {
4801          do_action('xmlrpc_call', 'mt.supportedTextFilters');
4802          return apply_filters('xmlrpc_text_filters', array());
4803      }
4804  
4805      /**
4806       * Retrieve trackbacks sent to a given post.
4807       *
4808       * @since 1.5.0
4809       *
4810       * @param array $args Method parameters.
4811       * @return mixed
4812       */
4813  	function mt_getTrackbackPings($args) {
4814  
4815          global $wpdb;
4816  
4817          $post_ID = intval($args);
4818  
4819          do_action('xmlrpc_call', 'mt.getTrackbackPings');
4820  
4821          $actual_post = wp_get_single_post($post_ID, ARRAY_A);
4822  
4823          if ( !$actual_post )
4824              return new IXR_Error(404, __('Sorry, no such post.'));
4825  
4826          $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) );
4827  
4828          if ( !$comments )
4829              return array();
4830  
4831          $trackback_pings = array();
4832          foreach ( $comments as $comment ) {
4833              if ( 'trackback' == $comment->comment_type ) {
4834                  $content = $comment->comment_content;
4835                  $title = substr($content, 8, (strpos($content, '</strong>') - 8));
4836                  $trackback_pings[] = array(
4837                      'pingTitle' => $title,
4838                      'pingURL'   => $comment->comment_author_url,
4839                      'pingIP'    => $comment->comment_author_IP
4840                  );
4841              }
4842          }
4843  
4844          return $trackback_pings;
4845      }
4846  
4847      /**
4848       * Sets a post's publish status to 'publish'.
4849       *
4850       * @since 1.5.0
4851       *
4852       * @param array $args Method parameters.
4853       * @return int
4854       */
4855  	function mt_publishPost($args) {
4856  
4857          $this->escape($args);
4858  
4859          $post_ID     = (int) $args[0];
4860          $username  = $args[1];
4861          $password   = $args[2];
4862  
4863          if ( !$user = $this->login($username, $password) )
4864              return $this->error;
4865  
4866          do_action('xmlrpc_call', 'mt.publishPost');
4867  
4868          $postdata = wp_get_single_post($post_ID, ARRAY_A);
4869          if ( ! $postdata )
4870              return new IXR_Error( 404, __( 'Invalid post ID.' ) );
4871  
4872          if ( !current_user_can('publish_posts') || !current_user_can('edit_post', $post_ID) )
4873              return new IXR_Error(401, __('Sorry, you cannot publish this post.'));
4874  
4875          $postdata['post_status'] = 'publish';
4876  
4877          // retain old cats
4878          $cats = wp_get_post_categories($post_ID);
4879          $postdata['post_category'] = $cats;
4880          $this->escape($postdata);
4881  
4882          $result = wp_update_post($postdata);
4883  
4884          return $result;
4885      }
4886  
4887      /* PingBack functions
4888       * specs on www.hixie.ch/specs/pingback/pingback
4889       */
4890  
4891      /**
4892       * Retrieves a pingback and registers it.
4893       *
4894       * @since 1.5.0
4895       *
4896       * @param array $args Method parameters.
4897       * @return array
4898       */
4899  	function pingback_ping($args) {
4900          global $wpdb;
4901  
4902          do_action('xmlrpc_call', 'pingback.ping');
4903  
4904          $this->escape($args);
4905  
4906          $pagelinkedfrom = $args[0];
4907          $pagelinkedto   = $args[1];
4908  
4909          $title = '';
4910  
4911          $pagelinkedfrom = str_replace('&amp;', '&', $pagelinkedfrom);
4912          $pagelinkedto = str_replace('&amp;', '&', $pagelinkedto);
4913          $pagelinkedto = str_replace('&', '&amp;', $pagelinkedto);
4914  
4915          // Check if the page linked to is in our site
4916          $pos1 = strpos($pagelinkedto, str_replace(array('http://www.','http://','https://www.','https://'), '', get_option('home')));
4917          if ( !$pos1 )
4918              return new IXR_Error(0, __('Is there no link to us?'));
4919  
4920          // let's find which post is linked to
4921          // FIXME: does url_to_postid() cover all these cases already?
4922          //        if so, then let's use it and drop the old code.
4923          $urltest = parse_url($pagelinkedto);
4924          if ( $post_ID = url_to_postid($pagelinkedto) ) {
4925              $way = 'url_to_postid()';
4926          } elseif ( preg_match('#p/[0-9]{1,}#', $urltest['path'], $match) ) {
4927              // the path defines the post_ID (archives/p/XXXX)
4928              $blah = explode('/', $match[0]);
4929              $post_ID = (int) $blah[1];
4930              $way = 'from the path';
4931          } elseif ( preg_match('#p=[0-9]{1,}#', $urltest['query'], $match) ) {
4932              // the querystring defines the post_ID (?p=XXXX)
4933              $blah = explode('=', $match[0]);
4934              $post_ID = (int) $blah[1];
4935              $way = 'from the querystring';
4936          } elseif ( isset($urltest['fragment']) ) {
4937              // an #anchor is there, it's either...
4938              if ( intval($urltest['fragment']) ) {
4939                  // ...an integer #XXXX (simplest case)
4940                  $post_ID = (int) $urltest['fragment'];
4941                  $way = 'from the fragment (numeric)';
4942              } elseif ( preg_match('/post-[0-9]+/',$urltest['fragment']) ) {
4943                  // ...a post id in the form 'post-###'
4944                  $post_ID = preg_replace('/[^0-9]+/', '', $urltest['fragment']);
4945                  $way = 'from the fragment (post-###)';
4946              } elseif ( is_string($urltest['fragment']) ) {
4947                  // ...or a string #title, a little more complicated
4948                  $title = preg_replace('/[^a-z0-9]/i', '.', $urltest['fragment']);
4949                  $sql = $wpdb->prepare("SELECT ID FROM $wpdb->posts WHERE post_title RLIKE %s", like_escape( $title ) );
4950                  if (! ($post_ID = $wpdb->get_var($sql)) ) {
4951                      // returning unknown error '0' is better than die()ing
4952                        return new IXR_Error(0, '');
4953                  }
4954                  $way = 'from the fragment (title)';
4955              }
4956          } else {
4957              // TODO: Attempt to extract a post ID from the given URL
4958                return new IXR_Error(33, __('The specified target URL cannot be used as a target. It either doesn&#8217;t exist, or it is not a pingback-enabled resource.'));
4959          }
4960          $post_ID = (int) $post_ID;
4961  
4962          $post = get_post($post_ID);
4963  
4964          if ( !$post ) // Post_ID not found
4965                return new IXR_Error(33, __('The specified target URL cannot be used as a target. It either doesn&#8217;t exist, or it is not a pingback-enabled resource.'));
4966  
4967          if ( $post_ID == url_to_postid($pagelinkedfrom) )
4968              return new IXR_Error(0, __('The source URL and the target URL cannot both point to the same resource.'));
4969  
4970          // Check if pings are on
4971          if ( !pings_open($post) )
4972                return new IXR_Error(33, __('The specified target URL cannot be used as a target. It either doesn&#8217;t exist, or it is not a pingback-enabled resource.'));
4973  
4974          // Let's check that the remote site didn't already pingback this entry
4975          if ( $wpdb->get_results( $wpdb->prepare("SELECT * FROM $wpdb->comments WHERE comment_post_ID = %d AND comment_author_url = %s", $post_ID, $pagelinkedfrom) ) )
4976              return new IXR_Error( 48, __( 'The pingback has already been registered.' ) );
4977  
4978          // very stupid, but gives time to the 'from' server to publish !
4979          sleep(1);
4980  
4981          // Let's check the remote site
4982          $linea = wp_remote_fopen( $pagelinkedfrom );
4983          if ( !$linea )
4984                return new IXR_Error(16, __('The source URL does not exist.'));
4985  
4986          $linea = apply_filters('pre_remote_source', $linea, $pagelinkedto);
4987  
4988          // Work around bug in strip_tags():
4989          $linea = str_replace('<!DOC', '<DOC', $linea);
4990          $linea = preg_replace( '/[\s\r\n\t]+/', ' ', $linea ); // normalize spaces
4991          $linea = preg_replace( "/ <(h1|h2|h3|h4|h5|h6|p|th|td|li|dt|dd|pre|caption|input|textarea|button|body)[^>]*>/", "\n\n", $linea );
4992  
4993          preg_match('|<title>([^<]*?)</title>|is', $linea, $matchtitle);
4994          $title = $matchtitle[1];
4995          if ( empty( $title ) )
4996              return new IXR_Error(32, __('We cannot find a title on that page.'));
4997  
4998          $linea = strip_tags( $linea, '<a>' ); // just keep the tag we need
4999  
5000          $p = explode( "\n\n", $linea );
5001  
5002          $preg_target = preg_quote($pagelinkedto, '|');
5003  
5004          foreach ( $p as $para ) {
5005              if ( strpos($para, $pagelinkedto) !== false ) { // it exists, but is it a link?
5006                  preg_match("|<a[^>]+?".$preg_target."[^>]*>([^>]+?)</a>|", $para, $context);
5007  
5008                  // If the URL isn't in a link context, keep looking
5009                  if ( empty($context) )
5010                      continue;
5011  
5012                  // We're going to use this fake tag to mark the context in a bit
5013                  // the marker is needed in case the link text appears more than once in the paragraph
5014                  $excerpt = preg_replace('|\</?wpcontext\>|', '', $para);
5015  
5016                  // prevent really long link text
5017                  if ( strlen($context[1]) > 100 )
5018                      $context[1] = substr($context[1], 0, 100) . '...';
5019  
5020                  $marker = '<wpcontext>'.$context[1].'</wpcontext>';    // set up our marker
5021                  $excerpt= str_replace($context[0], $marker, $excerpt); // swap out the link for our marker
5022                  $excerpt = strip_tags($excerpt, '<wpcontext>');        // strip all tags but our context marker
5023                  $excerpt = trim($excerpt);
5024                  $preg_marker = preg_quote($marker, '|');
5025                  $excerpt = preg_replace("|.*?\s(.{0,100}$preg_marker.{0,100})\s.*|s", '$1', $excerpt);
5026                  $excerpt = strip_tags($excerpt); // YES, again, to remove the marker wrapper
5027                  break;
5028              }
5029          }
5030  
5031          if ( empty($context) ) // Link to target not found
5032              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.'));
5033  
5034          $pagelinkedfrom = str_replace('&', '&amp;', $pagelinkedfrom);
5035  
5036          $context = '[...] ' . esc_html( $excerpt ) . ' [...]';
5037          $pagelinkedfrom = $wpdb->escape( $pagelinkedfrom );
5038  
5039          $comment_post_ID = (int) $post_ID;
5040          $comment_author = $title;
5041          $comment_author_email = '';
5042          $this->escape($comment_author);
5043          $comment_author_url = $pagelinkedfrom;
5044          $comment_content = $context;
5045          $this->escape($comment_content);
5046          $comment_type = 'pingback';
5047  
5048          $commentdata = compact('comment_post_ID', 'comment_author', 'comment_author_url', 'comment_author_email', 'comment_content', 'comment_type');
5049  
5050          $comment_ID = wp_new_comment($commentdata);
5051          do_action('pingback_post', $comment_ID);
5052  
5053          return sprintf(__('Pingback from %1$s to %2$s registered. Keep the web talking! :-)'), $pagelinkedfrom, $pagelinkedto);
5054      }
5055  
5056      /**
5057       * Retrieve array of URLs that pingbacked the given URL.
5058       *
5059       * Specs on http://www.aquarionics.com/misc/archives/blogite/0198.html
5060       *
5061       * @since 1.5.0
5062       *
5063       * @param array $args Method parameters.
5064       * @return array
5065       */
5066  	function pingback_extensions_getPingbacks($args) {
5067  
5068          global $wpdb;
5069  
5070          do_action('xmlrpc_call', 'pingback.extensions.getPingbacks');
5071  
5072          $this->escape($args);
5073  
5074          $url = $args;
5075  
5076          $post_ID = url_to_postid($url);
5077          if ( !$post_ID ) {
5078              // We aren't sure that the resource is available and/or pingback enabled
5079                return new IXR_Error(33, __('The specified target URL cannot be used as a target. It either doesn&#8217;t exist, or it is not a pingback-enabled resource.'));
5080          }
5081  
5082          $actual_post = wp_get_single_post($post_ID, ARRAY_A);
5083  
5084          if ( !$actual_post ) {
5085              // No such post = resource not found
5086                return new IXR_Error(32, __('The specified target URL does not exist.'));
5087          }
5088  
5089          $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) );
5090  
5091          if ( !$comments )
5092              return array();
5093  
5094          $pingbacks = array();
5095          foreach ( $comments as $comment ) {
5096              if ( 'pingback' == $comment->comment_type )
5097                  $pingbacks[] = $comment->comment_author_url;
5098          }
5099  
5100          return $pingbacks;
5101      }
5102  }


Generated: Fri May 25 03:56:23 2012 Hosted by follow the white rabbit.