[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

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