[ Root ] [ Search ] [ Index ]

PHP Cross Reference of bbPress Trunk

Provided by Yoast

title

Body

[close]

/ -> xmlrpc.php (source)

   1  <?php
   2  /**
   3   * XML-RPC protocol support for bbPress
   4   *
   5   * @since 1.0
   6   * @package bbPress
   7   */
   8  
   9  
  10  
  11  /**
  12   * Whether this is an XML-RPC Request
  13   *
  14   * @since 1.0
  15   * @var bool
  16   */
  17  define( 'XMLRPC_REQUEST', true );
  18  
  19  // Get rid of cookies sent by some browser-embedded clients
  20  $_COOKIE = array();
  21  
  22  // A bug in PHP < 5.2.2 makes $HTTP_RAW_POST_DATA not set by default
  23  if ( !isset( $HTTP_RAW_POST_DATA ) ) {
  24      $HTTP_RAW_POST_DATA = file_get_contents( 'php://input' );
  25  }
  26  
  27  // Fix for mozBlog and other cases where '<?xml' isn't on the very first line
  28  if ( isset( $HTTP_RAW_POST_DATA ) ) {
  29      $HTTP_RAW_POST_DATA = trim( $HTTP_RAW_POST_DATA );
  30  }
  31  
  32  // Load bbPress
  33  require_once ( './bb-load.php' );
  34  
  35  
  36  
  37  // If the service discovery data is requested then return it and exit
  38  if ( isset( $_GET['rsd'] ) ) {
  39      header( 'Content-Type: text/xml; charset=UTF-8', true );
  40      echo '<?xml version="1.0" encoding="UTF-8"?'.'>' . "\n";
  41      echo '<rsd version="1.0" xmlns="http://archipelago.phrasewise.com/rsd">' . "\n";
  42      echo '    <service>' . "\n";
  43      echo '        <engineName>bbPress</engineName>' . "\n";
  44      echo '        <engineLink>http://bbpress.org/</engineLink>' . "\n";
  45      echo '        <homePageLink>' . bb_get_uri() . '</homePageLink>' . "\n";
  46      echo '        <apis>' . "\n";
  47      echo '            <api name="bbPress" blogID="1" preferred="true" apiLink="' . bb_get_uri( 'xmlrpc.php' ) . '" />' . "\n";
  48      echo '        </apis>' . "\n";
  49      echo '    </service>' . "\n";
  50      echo '</rsd>' . "\n";
  51      exit;
  52  }
  53  
  54  
  55  
  56  // Load the XML-RPC server/client classes
  57  require_once( BACKPRESS_PATH . '/class.ixr.php' );
  58  
  59  
  60  
  61  /**
  62   * XML-RPC server class to allow for remote publishing
  63   *
  64   * @since 1.0
  65   * @package bbPress
  66   * @subpackage Publishing
  67   * @uses class IXR_Server
  68   */
  69  class BB_XMLRPC_Server extends IXR_Server
  70  {
  71      /**
  72       * Stores the last error generated by the class
  73       *
  74       * @since 1.0
  75       * @var object|boolean An instance of the IXR_Error class or false if no error exists
  76       */
  77      var $error = false;
  78  
  79      /**
  80       * Site options which can be manipulated using XML-RPC
  81       *
  82       * @since 1.0
  83       * @var array
  84       */
  85      var $site_options = array();
  86  
  87      /**
  88       * Whether read-only methods require authentication
  89       *
  90       * @since 1.0
  91       * @var boolean
  92       **/
  93      var $auth_readonly = false;
  94  
  95      /**
  96       * Whether user switching is allowed
  97       *
  98       * @since 1.0
  99       * @var boolean
 100       **/
 101      var $allow_user_switching = false;
 102  
 103      /**
 104       * Initialises the XML-RPC server
 105       *
 106       * @since 1.0
 107       * @return void
 108       */
 109  	function BB_XMLRPC_Server()
 110      {
 111          // bbPress publishing API
 112          if ( bb_get_option( 'enable_xmlrpc' ) ) {
 113              $this->methods = array(
 114                  // - Demo
 115                  'demo.sayHello'         => 'this:sayHello',
 116                  'demo.addTwoNumbers'    => 'this:addTwoNumbers',
 117                  // - Forums
 118                  'bb.getForumCount'      => 'this:bb_getForumCount',
 119                  'bb.getForums'          => 'this:bb_getForums',
 120                  'bb.getForum'           => 'this:bb_getForum',
 121                  'bb.newForum'           => 'this:bb_newForum',
 122                  'bb.editForum'          => 'this:bb_editForum',
 123                  'bb.deleteForum'        => 'this:bb_deleteForum',
 124                  // - Topics
 125                  'bb.getTopicCount'      => 'this:bb_getTopicCount',
 126                  'bb.getTopics'          => 'this:bb_getTopics',
 127                  'bb.getTopic'           => 'this:bb_getTopic',
 128                  'bb.newTopic'           => 'this:bb_newTopic',
 129                  'bb.editTopic'          => 'this:bb_editTopic',
 130                  'bb.deleteTopic'        => 'this:bb_deleteTopic',        // Also undeletes
 131                  'bb.moveTopic'          => 'this:bb_moveTopic',
 132                  'bb.stickTopic'         => 'this:bb_stickTopic',         // Also unsticks
 133                  'bb.closeTopic'         => 'this:bb_closeTopic',         // Also opens
 134                  'bb.getTopicStatusList' => 'this:bb_getTopicStatusList',
 135                  // - Posts (replies)
 136                  'bb.getPostCount'      => 'this:bb_getPostCount',
 137                  'bb.getPosts'          => 'this:bb_getPosts',
 138                  'bb.getPost'           => 'this:bb_getPost',
 139                  'bb.newPost'           => 'this:bb_newPost',
 140                  'bb.editPost'          => 'this:bb_editPost',
 141                  'bb.deletePost'        => 'this:bb_deletePost',          // Also undeletes
 142                  'bb.getPostStatusList' => 'this:bb_getPostStatusList',
 143                  // - Topic Tags
 144                  'bb.getHotTopicTags'   => 'this:bb_getHotTopicTags',
 145                  'bb.getTopicTagCount'  => 'this:bb_getTopicTagCount',
 146                  'bb.getTopicTags'      => 'this:bb_getTopicTags',
 147                  'bb.getTopicTag'       => 'this:bb_getTopicTag',
 148                  'bb.addTopicTags'      => 'this:bb_addTopicTags',
 149                  'bb.removeTopicTags'   => 'this:bb_removeTopicTags',
 150                  'bb.renameTopicTag'    => 'this:bb_renameTopicTag',
 151                  'bb.mergeTopicTags'    => 'this:bb_mergeTopicTags',
 152                  'bb.destroyTopicTag'   => 'this:bb_destroyTopicTag',
 153                  // - Options
 154                  'bb.getOptions'        => 'this:bb_getOptions',
 155                  'bb.setOptions'        => 'this:bb_setOptions'
 156              );
 157          }
 158  
 159          // Pingback
 160          if ( bb_get_option( 'enable_pingback' ) ) {
 161              $this->methods = array_merge( (array)$this->methods, array(
 162                  'pingback.ping' => 'this:pingback_ping',
 163                  'pingback.extensions.getPingbacks' => 'this:pingback_extensions_getPingbacks'
 164              ) );
 165          }
 166  
 167          // Tells read-only methods whether they require authentication or not
 168          $this->auth_readonly = apply_filters( 'bb_xmlrpc_auth_readonly', $this->auth_readonly );
 169  
 170          // Whether or not to allow user switching
 171          $this->allow_user_switching = bb_get_option( 'bb_xmlrpc_allow_user_switching' );
 172  
 173          $this->initialise_site_option_info();
 174          $this->methods = apply_filters( 'bb_xmlrpc_methods', $this->methods );
 175          $this->IXR_Server( $this->methods );
 176      }
 177  
 178  
 179  
 180      /**
 181       * Utility methods
 182       */
 183  
 184      /**
 185       * Checks the user credentials supplied in the request to make sure they are valid
 186       *
 187       * @since 1.0
 188       * @return integer|boolean The user id if the user is valid, otherwise false
 189       * @param string $user_login The users login
 190       * @param string $user_pass The users password in plain text
 191       * @param string $capability The capability to check (optional)
 192       * @param string $message The message to pass back in the error if the capability check fails (optional)
 193       */
 194  	function authenticate( $user_login, $user_pass, $capability = 'read', $message = false )
 195      {
 196          if ( is_array( $user_login ) ) {
 197              $auth_user_login = (string) $user_login[0];
 198              $switch_user_login = (string) $user_login[1];
 199          } else {
 200              $auth_user_login = (string) $user_login;
 201              $switch_user_login = false;
 202          }
 203          
 204          // Check the login
 205          $user = bb_check_login( $auth_user_login, $user_pass );
 206          if ( !$user || is_wp_error( $user ) ) {
 207              $this->error = new IXR_Error( 403, __( 'Authentication failed.' ) );
 208              return false;
 209          }
 210  
 211          // Set the current user
 212          $user = bb_set_current_user( $user->ID );
 213  
 214          // Make sure they are allowed to do this
 215          if ( !bb_current_user_can( $capability ) ) {
 216              if ( !$message ) {
 217                  $message = __( 'You do not have permission to read this.' );
 218              }
 219              $this->error = new IXR_Error( 403, $message );
 220              return false;
 221          }
 222  
 223          // Switch the user if requested and allowed
 224          if ( $switch_user_login && $this->allow_user_switching && bb_current_user_can( 'edit_users' ) ) {
 225              $user = $this->switch_user( $switch_user_login, $capability, $message );
 226          }
 227  
 228          return $user;
 229      }
 230  
 231      /**
 232       * Switches the currently active user for incognito actions
 233       *
 234       * @since 1.0
 235       * @return integer|boolean The user id if the user is valid, otherwise false
 236       * @param string $user_login The users login
 237       * @param string $capability The capability to check (optional)
 238       * @param string $message The message to pass back in the error if the capability check fails (optional)
 239       */
 240  	function switch_user( $user_login, $capability = 'read', $message = false )
 241      {
 242          // Just get the user, authentication has already been established by the 
 243          $user = bb_get_user( $user_login, array( 'by' => 'login' ) );
 244          if ( !$user || is_wp_error( $user ) ) {
 245              $this->error = new IXR_Error( 400, __( 'User switching failed, the requested user does not exist.' ) );
 246              return false;
 247          }
 248  
 249          // Set the current user
 250          $user = bb_set_current_user( $user->ID );
 251  
 252          // Make sure they are allowed to do this
 253          if ( !bb_current_user_can( $capability ) ) {
 254              if ( !$message ) {
 255                  $message = __( 'You do not have permission to read this.' );
 256              }
 257              $this->error = new IXR_Error( 403, $message );
 258              return false;
 259          }
 260  
 261          return $user;
 262      }
 263  
 264      /**
 265       * Sanitises data from XML-RPC request parameters
 266       *
 267       * @since 1.0
 268       * @return mixed The sanitised variable, should come back with the same type
 269       * @param $array mixed The variable to be sanitised
 270       * @uses $bbdb BackPress database class instance
 271       */
 272  	function escape( &$array )
 273      {
 274          global $bbdb;
 275  
 276          if ( !is_array( $array ) ) {
 277              // Escape it
 278              $array = $bbdb->escape( $array );
 279          } elseif ( count( $array ) ) {
 280              foreach ( (array) $array as $k => $v ) {
 281                  if ( is_array( $v ) ) {
 282                      // Recursively sanitize arrays
 283                      $this->escape( $array[$k] );
 284                  } elseif ( is_object( $v ) ) {
 285                      // Don't sanitise objects - shouldn't happen anyway
 286                  } else {
 287                      // Escape it
 288                      $array[$k] = $bbdb->escape( $v );
 289                  }
 290              }
 291          }
 292  
 293          return $array;
 294      }
 295  
 296      /**
 297       * Prepares forum data for return in an XML-RPC object
 298       *
 299       * @since 1.0
 300       * @return array The prepared forum data
 301       * @param array|object The unprepared forum data
 302       **/
 303  	function prepare_forum( $forum )
 304      {
 305          // Cast to an array
 306          $_forum = (array) $forum;
 307          // Set the URI
 308          $_forum['forum_uri'] = get_forum_link( $_forum['forum_id'] );
 309          // Give this a definite value
 310          if ( !isset( $_forum['forum_is_category'] ) ) {
 311              $_forum['forum_is_category'] = 0;
 312          }
 313          // Allow plugins to modify the data
 314          return apply_filters( 'bb_xmlrpc_prepare_forum', $_forum, (array) $forum );
 315      }
 316  
 317      /**
 318       * Prepares topic data for return in an XML-RPC object
 319       *
 320       * @since 1.0
 321       * @return array The prepared topic data
 322       * @param array|object The unprepared topic data
 323       **/
 324  	function prepare_topic( $topic )
 325      {
 326          // Cast to an array
 327          $_topic = (array) $topic;
 328          // Set the URI
 329          $_topic['topic_uri'] = get_topic_link( $_topic['topic_id'] );
 330          // Set readable times
 331          $_topic['topic_start_time_since'] = bb_since( $_topic['topic_start_time'] );
 332          $_topic['topic_time_since'] = bb_since( $_topic['topic_time'] );
 333          // Set the display names
 334          $_topic['topic_poster_display_name'] = get_user_display_name( $_topic['topic_poster'] );
 335          $_topic['topic_last_poster_display_name'] = get_user_display_name( $_topic['topic_last_poster'] );
 336          // Remove some sensitive user ids
 337          unset(
 338              $_topic['topic_poster'],
 339              $_topic['topic_last_poster']
 340          );
 341          // Allow plugins to modify the data
 342          return apply_filters( 'bb_xmlrpc_prepare_topic', $_topic, (array) $topic );
 343      }
 344  
 345      /**
 346       * Prepares post data for return in an XML-RPC object
 347       *
 348       * @since 1.0
 349       * @return array The prepared post data
 350       * @param array|object The unprepared post data
 351       **/
 352  	function prepare_post( $post )
 353      {
 354          // Cast to an array
 355          $_post = (array) $post;
 356          // Set the URI
 357          $_post['post_uri'] = get_post_link( $_post['post_id'] );
 358          // Set readable times
 359          $_post['post_time_since'] = bb_since( $_post['post_time'] );
 360          // Set the display names
 361          $_post['poster_display_name'] = get_user_display_name( $_post['poster_id'] );
 362          // Remove some sensitive data
 363          unset(
 364              $_post['poster_id'],
 365              $_post['poster_ip'],
 366              $_post['pingback_queued']
 367          );
 368          // Allow plugins to modify the data
 369          return apply_filters( 'bb_xmlrpc_prepare_post', $_post, (array) $post );
 370      }
 371  
 372      /**
 373       * Prepares topic tag data for return in an XML-RPC object
 374       *
 375       * @since 1.0
 376       * @return array The prepared topic tag data
 377       * @param array|object The unprepared topic tag data
 378       **/
 379  	function prepare_topic_tag( $tag )
 380      {
 381          // Cast to an array
 382          $_tag = (array) $tag;
 383          // Set the URI
 384          $_tag['topic_tag_uri'] = bb_get_tag_link( $tag );
 385          // Consistent nomenclature
 386          $_tag['topic_tag_name'] = (string) $_tag['name'];
 387          $_tag['topic_tag_slug'] = (string) $_tag['slug'];
 388          $_tag['topic_tag_count'] = (int) $_tag['count'];
 389          // Remove some sensitive data
 390          unset(
 391              $_tag['object_id'],
 392              $_tag['name'],
 393              $_tag['slug'],
 394              $_tag['count'],
 395              $_tag['term_id'],
 396              $_tag['term_group'],
 397              $_tag['term_taxonomy_id'],
 398              $_tag['taxonomy'],
 399              $_tag['description'],
 400              $_tag['parent'],
 401              $_tag['count'],
 402              $_tag['user_id'],
 403              $_tag['tag_id'],
 404              $_tag['tag'],
 405              $_tag['raw_tag'],
 406              $_tag['tag_count']
 407          );
 408          // Allow plugins to modify the data
 409          return apply_filters( 'bb_xmlrpc_prepare_topic_tag', $_tag, (array) $tag );
 410      }
 411  
 412  
 413  
 414      /**
 415       * bbPress publishing API - Demo XML-RPC methods
 416       */
 417  
 418      /**
 419       * Hello world demo function for XML-RPC
 420       *
 421       * @since 1.0
 422       * @return string The phrase 'Hello!'
 423       * @param array $args Arguments passed by the XML-RPC call
 424       * @param string $args[0] The username for authentication
 425       * @param string $args[1] The password for authentication
 426       *
 427       * XML-RPC request to get a greeting
 428       * <methodCall>
 429       *     <methodName>demo.sayHello</methodName>
 430       *     <params>
 431       *         <param><value><string>joeblow</string></value></param>
 432       *         <param><value><string>123password</string></value></param>
 433       *     </params>
 434       * </methodCall>
 435       */
 436  	function sayHello( $args )
 437      {
 438          // Escape args
 439          $this->escape( $args );
 440  
 441          // Get the login credentials
 442          $username = $args[0];
 443          $password = (string) $args[1];
 444  
 445          // Check the user is valid
 446          if ( $this->auth_readonly && !$this->authenticate( $username, $password ) ) {
 447              return $this->error;
 448          }
 449  
 450          return 'Hello!';
 451      }
 452  
 453      /**
 454       * Adds two numbers together as a demo of XML-RPC
 455       *
 456       * @since 1.0
 457       * @return integer The sum of the two supplied numbers
 458       * @param array $args Arguments passed by the XML-RPC call
 459       * @param string $args[0] The username for authentication
 460       * @param string $args[1] The password for authentication
 461       * @param integer $args[2] The first number to be added
 462       * @param integer $args[3] The second number to be added
 463       *
 464       * XML-RPC request to get the sum of two numbers
 465       * <methodCall>
 466       *     <methodName>demo.addTwoNumbers</methodName>
 467       *     <params>
 468       *         <param><value><string>joeblow</string></value></param>
 469       *         <param><value><string>123password</string></value></param>
 470       *         <param><value><int>5</int></value></param>
 471       *         <param><value><int>102</int></value></param>
 472       *     </params>
 473       * </methodCall>
 474       */
 475  	function addTwoNumbers( $args )
 476      {
 477          // Escape args
 478          $this->escape( $args );
 479  
 480          // Get the login credentials
 481          $username = $args[0];
 482          $password = (string) $args[1];
 483  
 484          // Check the user is valid
 485          if ( $this->auth_readonly && !$this->authenticate( $username, $password ) ) {
 486              return $this->error;
 487          }
 488  
 489          $number1 = (int) $args[2];
 490          $number2 = (int) $args[3];
 491  
 492          return ( $number1 + $number2 );
 493      }
 494  
 495  
 496  
 497      /**
 498       * bbPress publishing API - Forum XML-RPC methods
 499       */
 500  
 501      /**
 502       * Returns a numerical count of forums
 503       *
 504       * @since 1.0
 505       * @return integer|object The number of forums when successfully executed or an IXR_Error object on failure
 506       * @param array $args Arguments passed by the XML-RPC call
 507       * @param string $args[0] The username for authentication
 508       * @param string $args[1] The password for authentication
 509       * @param integer|string $args[2] The parent forum's id or slug (optional)
 510       * @param integer $args[3] The depth of child forums to retrieve (optional)
 511       *
 512       * XML-RPC request to get a count of all forums in the bbPress instance
 513       * <methodCall>
 514       *     <methodName>bb.getForumCount</methodName>
 515       *     <params>
 516       *         <param><value><string>joeblow</string></value></param>
 517       *         <param><value><string>123password</string></value></param>
 518       *     </params>
 519       * </methodCall>
 520       *
 521       * XML-RPC request to get a count of all child forums in the forum with id number 34
 522       * <methodCall>
 523       *     <methodName>bb.getForumCount</methodName>
 524       *     <params>
 525       *         <param><value><string>joeblow</string></value></param>
 526       *         <param><value><string>123password</string></value></param>
 527       *         <param><value><int>34</int></value></param>
 528       *     </params>
 529       * </methodCall>
 530       *
 531       * XML-RPC request to get a count of all child forums in the forum with slug "first-forum"
 532       * <methodCall>
 533       *     <methodName>bb.getForumCount</methodName>
 534       *     <params>
 535       *         <param><value><string>joeblow</string></value></param>
 536       *         <param><value><string>123password</string></value></param>
 537       *         <param><value><string>first-forum</string></value></param>
 538       *     </params>
 539       * </methodCall>
 540       *
 541       * XML-RPC request to get a count of all child forums in the forum with id number 34 no more than 2 forums deep in the hierarchy
 542       * <methodCall>
 543       *     <methodName>bb.getForumCount</methodName>
 544       *     <params>
 545       *         <param><value><string>joeblow</string></value></param>
 546       *         <param><value><string>123password</string></value></param>
 547       *         <param><value><int>34</int></value></param>
 548       *         <param><value><int>2</int></value></param>
 549       *     </params>
 550       * </methodCall>
 551       */
 552  	function bb_getForumCount( $args )
 553      {
 554          do_action( 'bb_xmlrpc_call', 'bb.getForumCount' );
 555  
 556          // Escape args
 557          $this->escape( $args );
 558  
 559          // Get the login credentials
 560          $username = $args[0];
 561          $password = (string) $args[1];
 562  
 563          // Check the user is valid
 564          if ( $this->auth_readonly ) {
 565              $user = $this->authenticate( $username, $password );
 566          }
 567  
 568          do_action( 'bb_xmlrpc_call_authenticated', 'bb.getForumCount' );
 569  
 570          // If an error was raised by authentication or by an action then return it
 571          if ( $this->error ) {
 572              return $this->error;
 573          }
 574  
 575          // Setup an array to store arguments to pass to bb_get_forums() function
 576          $get_forums_args = array(
 577              'child_of' => 0,
 578              'hierarchical' => 0,
 579              'depth' => 0
 580          );
 581  
 582          // Can be numeric id or slug
 583          $forum_id = isset( $args[2] ) ? $args[2] : false;
 584  
 585          if ( $forum_id ) {
 586              // Check for bad data
 587              if ( !is_string( $forum_id ) && !is_integer( $forum_id ) ) {
 588                  $this->error = new IXR_Error( 400, __( 'The forum id is invalid.' ) );
 589                  return $this->error;
 590              }
 591              // Check the requested forum exists
 592              if ( !$forum = bb_get_forum( $forum_id ) ) {
 593                  $this->error = new IXR_Error( 400, __( 'The forum does not exist.' ) );
 594                  return $this->error;
 595              }
 596              // Add the specific forum to the arguments
 597              $get_forums_args['child_of'] = (int) $forum->forum_id;
 598          }
 599  
 600          // Can only be an integer
 601          $depth = (int) $args[3];
 602  
 603          if ( $depth > 0 ) {
 604              // Add the depth to traverse to the arguments
 605              $get_forums_args['depth'] = $depth;
 606              // Only make it hierarchical if the depth > 1
 607              if ( $depth > 1 ) {
 608                  $get_forums_args['hierarchical'] = 1;
 609              }
 610          }
 611  
 612          // Get the forums. Return 0 when no forums exist
 613          if ( !$forums = bb_get_forums( $get_forums_args ) ) {
 614              $count = 0;
 615          } else {
 616              $count = count( $forums );
 617          }
 618  
 619          do_action( 'bb_xmlrpc_call_return', 'bb.getForumCount' );
 620  
 621          // Return a count of the forums
 622          return $count;
 623      }
 624  
 625      /**
 626       * Returns details of multiple forums
 627       *
 628       * @since 1.0
 629       * @return array|object An array containing details of all returned forums when successfully executed or an IXR_Error object on failure
 630       * @param array $args Arguments passed by the XML-RPC call
 631       * @param string $args[0] The username for authentication
 632       * @param string $args[1] The password for authentication
 633       * @param integer|string $args[2] The parent forum's id or slug (optional)
 634       * @param integer $args[3] The depth of child forums to retrieve (optional)
 635       *
 636       * XML-RPC request to get all forums in the bbPress instance
 637       * <methodCall>
 638       *     <methodName>bb.getForums</methodName>
 639       *     <params>
 640       *         <param><value><string>joeblow</string></value></param>
 641       *         <param><value><string>123password</string></value></param>
 642       *     </params>
 643       * </methodCall>
 644       *
 645       * XML-RPC request to get all child forums in the forum with id number 34
 646       * <methodCall>
 647       *     <methodName>bb.getForums</methodName>
 648       *     <params>
 649       *         <param><value><string>joeblow</string></value></param>
 650       *         <param><value><string>123password</string></value></param>
 651       *         <param><value><int>34</int></value></param>
 652       *     </params>
 653       * </methodCall>
 654       *
 655       * XML-RPC request to get all child forums in the forum with slug "first-forum"
 656       * <methodCall>
 657       *     <methodName>bb.getForums</methodName>
 658       *     <params>
 659       *         <param><value><string>joeblow</string></value></param>
 660       *         <param><value><string>123password</string></value></param>
 661       *         <param><value><string>first-forum</string></value></param>
 662       *     </params>
 663       * </methodCall>
 664       *
 665       * XML-RPC request to get all child forums in the forum with id number 34 no more than 2 forums deep in the hierarchy
 666       * <methodCall>
 667       *     <methodName>bb.getForums</methodName>
 668       *     <params>
 669       *         <param><value><string>joeblow</string></value></param>
 670       *         <param><value><string>123password</string></value></param>
 671       *         <param><value><int>34</int></value></param>
 672       *         <param><value><int>2</int></value></param>
 673       *     </params>
 674       * </methodCall>
 675       */
 676  	function bb_getForums( $args )
 677      {
 678          do_action( 'bb_xmlrpc_call', 'bb.getForums' );
 679  
 680          // Escape args
 681          $this->escape( $args );
 682  
 683          // Get the login credentials
 684          $username = $args[0];
 685          $password = (string) $args[1];
 686  
 687          // Check the user is valid
 688          if ( $this->auth_readonly ) {
 689              $user = $this->authenticate( $username, $password );
 690          }
 691  
 692          do_action( 'bb_xmlrpc_call_authenticated', 'bb.getForums' );
 693  
 694          // If an error was raised by authentication or by an action then return it
 695          if ( $this->error ) {
 696              return $this->error;
 697          }
 698  
 699          // Setup an array to store arguments to pass to bb_get_forums() function
 700          $get_forums_args = array(
 701              'child_of' => 0,
 702              'hierarchical' => 0,
 703              'depth' => 0
 704          );
 705  
 706          // Can be numeric id or slug
 707          $forum_id = isset( $args[2] ) ? $args[2] : false;
 708  
 709          if ( $forum_id ) {
 710              // Check for bad data
 711              if ( !is_string( $forum_id ) && !is_integer( $forum_id ) ) {
 712                  $this->error = new IXR_Error( 400, __( 'The forum id is invalid.' ) );
 713                  return $this->error;
 714              }
 715              // First check the requested forum exists
 716              if ( !$forum = bb_get_forum( $forum_id ) ) {
 717                  $this->error = new IXR_Error( 400, __( 'The forum does not exist.' ) );
 718                  return $this->error;
 719              }
 720              // Add the specific forum to the arguments
 721              $get_forums_args['child_of'] = (int) $forum->forum_id;
 722          }
 723  
 724          // Can only be an integer
 725          $depth = (int) $args[3];
 726  
 727          if ( $depth > 0 ) {
 728              // Add the depth to traverse to to the arguments
 729              $get_forums_args['depth'] = $depth;
 730              // Only make it hierarchical if the depth > 1
 731              if ( $depth > 1 ) {
 732                  $get_forums_args['hierarchical'] = 1;
 733              }
 734          }
 735  
 736          // Get the forums. Return an error when no forums exist
 737          if ( !$forums = bb_get_forums( $get_forums_args ) ) {
 738              $this->error = new IXR_Error( 404, __( 'No forums found.' ) );
 739              return $this->error;
 740          }
 741  
 742          // Only include "safe" data in the array
 743          $_forums = array();
 744          foreach ( $forums as $forum ) {
 745              $_forums[] = $this->prepare_forum( $forum );
 746          }
 747  
 748          do_action( 'bb_xmlrpc_call_return', 'bb.getForums' );
 749  
 750          // Return the forums
 751          return $_forums;
 752      }
 753  
 754      /**
 755       * Returns details of a forum
 756       *
 757       * @since 1.0
 758       * @return array|object An array containing details of the returned forum when successfully executed or an IXR_Error object on failure
 759       * @param string $args[0] The username for authentication
 760       * @param string $args[1] The password for authentication
 761       * @param integer|string $args[2] The forum's id or slug
 762       *
 763       * XML-RPC request to get the forum with id number 34
 764       * <methodCall>
 765       *     <methodName>bb.getForum</methodName>
 766       *     <params>
 767       *         <param><value><string>joeblow</string></value></param>
 768       *         <param><value><string>123password</string></value></param>
 769       *         <param><value><int>34</int></value></param>
 770       *     </params>
 771       * </methodCall>
 772       *
 773       * XML-RPC request to get the forum with slug "first-forum"
 774       * <methodCall>
 775       *     <methodName>bb.getForum</methodName>
 776       *     <params>
 777       *         <param><value><string>joeblow</string></value></param>
 778       *         <param><value><string>123password</string></value></param>
 779       *         <param><value><string>first-forum</string></value></param>
 780       *     </params>
 781       * </methodCall>
 782       */
 783  	function bb_getForum( $args )
 784      {
 785          do_action( 'bb_xmlrpc_call', 'bb.getForum' );
 786  
 787          // Escape args
 788          $this->escape( $args );
 789  
 790          // Get the login credentials
 791          $username = $args[0];
 792          $password = (string) $args[1];
 793  
 794          // Check the user is valid
 795          if ( $this->auth_readonly ) {
 796              $user = $this->authenticate( $username, $password );
 797          }
 798  
 799          do_action( 'bb_xmlrpc_call_authenticated', 'bb.getForum' );
 800  
 801          // If an error was raised by authentication or by an action then return it
 802          if ( $this->error ) {
 803              return $this->error;
 804          }
 805  
 806          // Can be numeric id or slug
 807          $forum_id = isset( $args[2] ) ? $args[2] : false;
 808  
 809          // Check for bad data
 810          if ( !$forum_id || ( !is_string( $forum_id ) && !is_integer( $forum_id ) ) ) {
 811              $this->error = new IXR_Error( 400, __( 'The forum id is invalid.' ) );
 812              return $this->error;
 813          }
 814  
 815          // Check the requested forum exists
 816          if ( !$forum = bb_get_forum( $forum_id ) ) {
 817              $this->error = new IXR_Error( 404, __( 'No forum found.' ) );
 818              return $this->error;
 819          }
 820  
 821          // Only include "safe" data in the array
 822          $forum = $this->prepare_forum( $forum );
 823  
 824          do_action( 'bb_xmlrpc_call_return', 'bb.getForum' );
 825  
 826          // Return the forums
 827          return $forum;
 828      }
 829  
 830      /**
 831       * Creates a new forum
 832       *
 833       * @since 1.0
 834       * @return array|object The forum data when successfully created or an IXR_Error object on failure
 835       * @param array $args Arguments passed by the XML-RPC call
 836       * @param string $args[0] The username for authentication
 837       * @param string $args[1] The password for authentication
 838       * @param array $args[2] The values for the various settings in the new forum
 839       * @param string $args[2]['name'] The name of the forum
 840       * @param string $args[2]['description'] The description of the forum (optional)
 841       * @param integer|string $args[2]['parent_id'] The unique id of the parent forum for this forum (optional)
 842       * @param integer $args[2]['order'] The position of the forum in the forum list (optional)
 843       * @param integer $args[2]['is_category'] Whether the forum is simply a container category (optional)
 844       *
 845       * XML-RPC request to create a new sub-forum called "A new forum" inside the parent forum with id 2
 846       * <methodCall>
 847       *     <methodName>bb.newForum</methodName>
 848       *     <params>
 849       *         <param><value><string>joeblow</string></value></param>
 850       *         <param><value><string>123password</string></value></param>
 851       *         <param><value><struct>
 852       *             <member>
 853       *                 <name>name</name>
 854       *                 <value><string>A new forum</string></value>
 855       *             </member>
 856       *             <member>
 857       *                 <name>parent_id</name>
 858       *                 <value><integer>2</integer></value>
 859       *             </member>
 860       *         </struct></value></param>
 861       *     </params>
 862       * </methodCall>
 863       */
 864  	function bb_newForum( $args )
 865      {
 866          do_action( 'bb_xmlrpc_call', 'bb.newForum' );
 867  
 868          // Escape args
 869          $this->escape( $args );
 870  
 871          // Get the login credentials
 872          $username = $args[0];
 873          $password = (string) $args[1];
 874  
 875          // Check the user is valid
 876          $user = $this->authenticate( $username, $password, 'manage_forums', __( 'You do not have permission to manage forums.' ) );
 877  
 878          do_action( 'bb_xmlrpc_call_authenticated', 'bb.newForum' );
 879  
 880          // If an error was raised by authentication or by an action then return it
 881          if ( $this->error ) {
 882              return $this->error;
 883          }
 884  
 885          // Make sure there is something for us to do
 886          if ( !$args[2] || !is_array( $args[2] ) || !count( $args[2] ) ) {
 887              $this->error = new IXR_Error( 400, __( 'The forum data is invalid.' ) );
 888              return $this->error;
 889          }
 890  
 891          $structure = (array) $args[2];
 892  
 893          // Minimum requirement is a name for the new forum
 894          if ( !isset( $structure['name'] ) || !$structure['name'] ) {
 895              $this->error = new IXR_Error( 400, __( 'The forum name is invalid.' ) );
 896              return $this->error;
 897          }
 898  
 899          // Inject structure into an array suitable for bb_new_forum()
 900          $bb_new_forum_args = array(
 901              'forum_name' => (string) $structure['name'],
 902              'forum_desc' => (string) $structure['description'],
 903              'forum_parent' => (int) $structure['parent_id'],
 904              'forum_order' => (int) $structure['order'],
 905              'forum_is_category' => (int) $structure['is_category']
 906          );
 907  
 908          // Remove empty settings so that changes to the defaults in bb_new_forum() are honoured
 909          $bb_new_forum_args = array_filter( $bb_new_forum_args );
 910  
 911          // Leave the require until the very end
 912          require_once ( BB_PATH . 'bb-admin/includes/functions.bb-admin.php' );
 913  
 914          // Create the forum
 915          if ( !$forum_id = (int) bb_new_forum( $bb_new_forum_args ) ) {
 916              $this->error = new IXR_Error( 500, __( 'The forum could not be created.' ) );
 917              return $this->error;
 918          }
 919  
 920          // Only include "safe" data in the array
 921          $forum = $this->prepare_forum( bb_get_forum( $forum_id ) );
 922  
 923          do_action( 'bb_xmlrpc_call_return', 'bb.newForum' );
 924  
 925          return $forum;
 926      }
 927  
 928      /**
 929       * Edits an existing forum
 930       *
 931       * @since 1.0
 932       * @return array|object The forum data when successfully edited or an IXR_Error object on failure
 933       * @param array $args Arguments passed by the XML-RPC call
 934       * @param string $args[0] The username for authentication
 935       * @param string $args[1] The password for authentication
 936       * @param array $args[2] The values for the various settings in the new forum, at least one must be specified
 937       * @param integer|string $args[2]['forum_id'] The unique id of the forum to be edited
 938       * @param string $args[2]['name'] The name of the forum (optional)
 939       * @param string $args[2]['slug'] The slug for the forum (optional)
 940       * @param string $args[2]['description'] The description of the forum (optional)
 941       * @param integer $args[2]['parent_id'] The unique id of the parent forum for this forum (optional)
 942       * @param integer $args[2]['order'] The position of the forum in the forum list (optional)
 943       * @param integer $args[2]['is_category'] Whether the forum is simply a container category (optional)
 944       *
 945       * XML-RPC request to edit a forum with id 11, changing the description
 946       * <methodCall>
 947       *     <methodName>bb.editForum</methodName>
 948       *     <params>
 949       *         <param><value><string>joeblow</string></value></param>
 950       *         <param><value><string>123password</string></value></param>
 951       *         <param><value><struct>
 952       *             <member>
 953       *                 <name>forum_id</name>
 954       *                 <value><integer>11</integer></value>
 955       *             </member>
 956       *             <member>
 957       *                 <name>description</name>
 958       *                 <value><string>This is a great forum for all sorts of reasons.</string></value>
 959       *             </member>
 960       *         </struct></value></param>
 961       *     </params>
 962       * </methodCall>
 963       */
 964  	function bb_editForum( $args )
 965      {
 966          do_action( 'bb_xmlrpc_call', 'bb.editForum' );
 967  
 968          // Escape args
 969          $this->escape( $args );
 970  
 971          // Get the login credentials
 972          $username = $args[0];
 973          $password = (string) $args[1];
 974  
 975          // Check the user is valid
 976          $user = $this->authenticate( $username, $password, 'manage_forums', __( 'You do not have permission to manage forums.' ) );
 977  
 978          do_action( 'bb_xmlrpc_call_authenticated', 'bb.editForum' );
 979  
 980          // If an error was raised by authentication or by an action then return it
 981          if ( $this->error ) {
 982              return $this->error;
 983          }
 984  
 985          // Make sure there is something for us to do
 986          if ( !$args[2] || !is_array( $args[2] ) || !count( $args[2] ) ) {
 987              $this->error = new IXR_Error( 400, __( 'The forum data is invalid.' ) );
 988              return $this->error;
 989          }
 990  
 991          $structure = (array) $args[2];
 992  
 993          // Can be numeric id or slug
 994          $forum_id = isset( $structure['forum_id'] ) ? $structure['forum_id'] : false;
 995  
 996          // Check for bad data
 997          if ( !$forum_id || ( !is_string( $forum_id ) && !is_integer( $forum_id ) ) ) {
 998              $this->error = new IXR_Error( 400, __( 'The forum id is invalid.' ) );
 999              return $this->error;
1000          }
1001  
1002          // Check the requested forum exists
1003          if ( !$forum = bb_get_forum( $forum_id ) ) {
1004              $this->error = new IXR_Error( 400, __( 'No forum found.' ) );
1005              return $this->error;
1006          }
1007  
1008          // Cast the forum object as an array
1009          $forum = (array) $forum;
1010          // The forum id may have been a slug, so make sure it's an integer here
1011          $forum_id = (int) $forum['forum_id'];
1012  
1013          // Remove some unneeded indexes
1014          unset( $forum['topics'] );
1015          unset( $forum['posts'] );
1016  
1017          // Add one if it isn't there
1018          if ( !isset( $forum['forum_is_category'] ) ) {
1019              $forum['forum_is_category'] = 0;
1020          }
1021  
1022          // Validate the name for the forum
1023          if ( isset( $structure['name'] ) && !$structure['name'] ) {
1024              $this->error = new IXR_Error( 400, __( 'The forum name is invalid.' ) );
1025              return $this->error;
1026          }
1027  
1028          // Inject structure into an array suitable for bb_update_forum()
1029          $bb_update_forum_args = array(
1030              'forum_name' => $structure['name']
1031          );
1032  
1033          // Slug cannot be blank
1034          if ( isset( $structure['slug'] ) && $structure['slug'] !== '' ) {
1035              $bb_update_forum_args['forum_slug'] = $structure['slug'];
1036          }
1037  
1038          // Description can be nothing
1039          if ( isset( $structure['description'] ) ) {
1040              $bb_update_forum_args['forum_desc'] = $structure['description'];
1041          }
1042  
1043          // Parent forum ID must be an integer and it can be 0
1044          if ( isset( $structure['parent_id'] ) && is_integer( $structure['parent_id'] ) ) {
1045              $bb_update_forum_args['forum_parent'] = $structure['parent_id'];
1046          }
1047  
1048          // Order must be an integer and it can be 0
1049          if ( isset( $structure['order'] ) && is_integer( $structure['order'] ) ) {
1050              $bb_update_forum_args['forum_order'] = $structure['order'];
1051          }
1052  
1053          // Category flag must be an integer and it can be 0
1054          if ( isset( $structure['is_category'] ) && is_integer( $structure['is_category'] ) ) {
1055              $bb_update_forum_args['forum_is_category'] = $structure['is_category'];
1056          }
1057  
1058          // Merge the changes into the existing data for the forum
1059          $bb_update_forum_args = wp_parse_args( $bb_update_forum_args, $forum );
1060  
1061          // Leave the require until the very end
1062          require_once ( BB_PATH . 'bb-admin/includes/functions.bb-admin.php' );
1063  
1064          // Update the forum
1065          if ( !bb_update_forum( $bb_update_forum_args ) ) {
1066              $this->error = new IXR_Error( 500, __( 'The forum could not be edited.' ) );
1067              return $this->error;
1068          }
1069  
1070          // Only include "safe" data in the array
1071          $forum = $this->prepare_forum( bb_get_forum( $forum_id ) );
1072  
1073          do_action( 'bb_xmlrpc_call_return', 'bb.editForum' );
1074  
1075          return $forum;
1076      }
1077  
1078      /**
1079       * Deletes a forum
1080       *
1081       * @since 1.0
1082       * @return integer|object 1 when successfully deleted or an IXR_Error object on failure
1083       * @param array $args Arguments passed by the XML-RPC call
1084       * @param string $args[0] The username for authentication
1085       * @param string $args[1] The password for authentication
1086       * @param integer|string $args[2] The unique id of the forum to be deleted
1087       *
1088       * XML-RPC request to delete a forum with the slug "naughty-forum"
1089       * <methodCall>
1090       *     <methodName>bb.deleteForum</methodName>
1091       *     <params>
1092       *         <param><value><string>joeblow</string></value></param>
1093       *         <param><value><string>123password</string></value></param>
1094       *         <param><value><string>naughty-forum</string></value></param>
1095       *     </params>
1096       * </methodCall>
1097       */
1098  	function bb_deleteForum( $args )
1099      {
1100          do_action( 'bb_xmlrpc_call', 'bb.deleteForum' );
1101  
1102          // Escape args
1103          $this->escape( $args );
1104  
1105          // Get the login credentials
1106          $username = $args[0];
1107          $password = (string) $args[1];
1108  
1109          // Check the user is valid
1110          $user = $this->authenticate( $username, $password, 'delete_forums', __( 'You do not have permission to delete forums.' ) );
1111  
1112          do_action( 'bb_xmlrpc_call_authenticated', 'bb.deleteForum' );
1113  
1114          // If an error was raised by authentication or by an action then return it
1115          if ( $this->error ) {
1116              return $this->error;
1117          }
1118  
1119          // Can be numeric id or slug
1120          $forum_id = isset( $args[2] ) ? $args[2] : false;
1121  
1122          // Check for bad data
1123          if ( !$forum_id || ( !is_string( $forum_id ) && !is_integer( $forum_id ) ) ) {
1124              $this->error = new IXR_Error( 400, __( 'The forum id is invalid.' ) );
1125              return $this->error;
1126          }
1127  
1128          // Check the requested forum exists
1129          if ( !$forum = bb_get_forum( $forum_id ) ) {
1130              $this->error = new IXR_Error( 400, __( 'No forum found.' ) );
1131              return $this->error;
1132          }
1133  
1134          // Cast the forum object as an array
1135          $forum = (array) $forum;
1136          // The forum id may have been a slug, so make sure it's an integer here
1137          $forum_id = (int) $forum['forum_id'];
1138  
1139          // Make sure they are allowed to delete this forum specifically
1140          if ( !bb_current_user_can( 'delete_forum', $forum_id ) ) {
1141              $this->error = new IXR_Error( 403, __( 'You do not have permission to delete this forum.' ) );
1142              return $this->error;
1143          }
1144  
1145          // Leave the require until the very end
1146          require_once ( BB_PATH . 'bb-admin/includes/functions.bb-admin.php' );
1147  
1148          // Delete the forum
1149          if ( !bb_delete_forum( $forum_id ) ) {
1150              $this->error = new IXR_Error( 500, __( 'The forum could not be deleted.' ) );
1151              return $this->error;
1152          }
1153  
1154          $result = 1;
1155  
1156          do_action( 'bb_xmlrpc_call_return', 'bb.deleteForum' );
1157  
1158          return $result;
1159      }
1160  
1161  
1162  
1163      /**
1164       * bbPress publishing API - Topic XML-RPC methods
1165       */
1166  
1167      /**
1168       * Returns a numerical count of topics
1169       *
1170       * @since 1.0
1171       * @return integer|object The number of topics when successfully executed or an IXR_Error object on failure
1172       * @param array $args Arguments passed by the XML-RPC call
1173       * @param string $args[0] The username for authentication
1174       * @param string $args[1] The password for authentication
1175       * @param integer|string $args[2] The forum id or slug (optional)
1176       *
1177       * XML-RPC request to get a count of all topics in the bbPress instance
1178       * <methodCall>
1179       *     <methodName>bb.getTopicCount</methodName>
1180       *     <params>
1181       *         <param><value><string>joeblow</string></value></param>
1182       *         <param><value><string>123password</string></value></param>
1183       *     </params>
1184       * </methodCall>
1185       *
1186       * XML-RPC request to get a count of all topics in the forum with id number 34
1187       * <methodCall>
1188       *     <methodName>bb.getTopicCount</methodName>
1189       *     <params>
1190       *         <param><value><string>joeblow</string></value></param>
1191       *         <param><value><string>123password</string></value></param>
1192       *         <param><value><int>34</int></value></param>
1193       *     </params>
1194       * </methodCall>
1195       *
1196       * XML-RPC request to get a count of all topics in the forum with slug "first-forum"
1197       * <methodCall>
1198       *     <methodName>bb.getTopicCount</methodName>
1199       *     <params>
1200       *         <param><value><string>joeblow</string></value></param>
1201       *         <param><value><string>123password</string></value></param>
1202       *         <param><value><string>first-forum</string></value></param>
1203       *     </params>
1204       * </methodCall>
1205       */
1206  	function bb_getTopicCount( $args )
1207      {
1208          do_action( 'bb_xmlrpc_call', 'bb.getTopicCount' );
1209  
1210          // Escape args
1211          $this->escape( $args );
1212  
1213          // Get the login credentials
1214          $username = $args[0];
1215          $password = (string) $args[1];
1216  
1217          // Check the user is valid
1218          if ( $this->auth_readonly ) {
1219              $user = $this->authenticate( $username, $password );
1220          }
1221  
1222          do_action( 'bb_xmlrpc_call_authenticated', 'bb.getTopicCount' );
1223  
1224          // If an error was raised by authentication or by an action then return it
1225          if ( $this->error ) {
1226              return $this->error;
1227          }
1228  
1229          // Can be numeric id or slug
1230          if ( isset( $args[2] ) && $forum_id = $args[2] ) {
1231              // Check for bad data
1232              if ( !is_string( $forum_id ) && !is_integer( $forum_id ) ) {
1233                  $this->error = new IXR_Error( 400, __( 'The forum id is invalid.' ) );
1234                  return $this->error;
1235              }
1236              // Check the requested forum exists
1237              if ( !$forum = bb_get_forum( $forum_id ) ) {
1238                  $this->error = new IXR_Error( 400, __( 'The forum does not exist.' ) );
1239                  return $this->error;
1240              }
1241  
1242              // OK, let's trust the count in the forum table
1243              $count = (int) $forum->topics;
1244          } else {
1245              // Get all forums
1246              $forums = bb_get_forums();
1247      
1248              // Return an error when no forums exist
1249              if ( !$forums ) {
1250                  $this->error = new IXR_Error( 400, __( 'No forums found.' ) );
1251                  return $this->error;
1252              }
1253  
1254              // Count the topics
1255              $count = 0;
1256              foreach ( $forums as $forum ) {
1257                  $count += (int) $forum->topics;
1258              }
1259          }
1260  
1261          do_action( 'bb_xmlrpc_call_return', 'bb.getTopicCount' );
1262  
1263          // Return the count of topics
1264          return $count;
1265      }
1266  
1267      /**
1268       * Returns details of the latest topics
1269       *
1270       * @since 1.0
1271       * @return array|object The topics when successfully executed or an IXR_Error object on failure
1272       * @param array $args Arguments passed by the XML-RPC call
1273       * @param string $args[0] The username for authentication
1274       * @param string $args[1] The password for authentication
1275       * @param integer|string $args[2] The forum id or slug (optional)
1276       * @param integer $args[3] The number of topics to return (optional)
1277       * @param integer $args[4] The number of the page to return (optional)
1278       *
1279       * XML-RPC request to get all topics in the bbPress instance
1280       * <methodCall>
1281       *     <methodName>bb.getTopics</methodName>
1282       *     <params>
1283       *         <param><value><string>joeblow</string></value></param>
1284       *         <param><value><string>123password</string></value></param>
1285       *     </params>
1286       * </methodCall>
1287       *
1288       * XML-RPC request to get all topics in the forum with id number 34
1289       * <methodCall>
1290       *     <methodName>bb.getTopics</methodName>
1291       *     <params>
1292       *         <param><value><string>joeblow</string></value></param>
1293       *         <param><value><string>123password</string></value></param>
1294       *         <param><value><int>34</int></value></param>
1295       *     </params>
1296       * </methodCall>
1297       *
1298       * XML-RPC request to get topics 6 to 10 in the forum with slug "first-forum"
1299       * <methodCall>
1300       *     <methodName>bb.getTopics</methodName>
1301       *     <params>
1302       *         <param><value><string>joeblow</string></value></param>
1303       *         <param><value><string>123password</string></value></param>
1304       *         <param><value><string>first-forum</string></value></param>
1305       *         <param><value><int>5</int></value></param>
1306       *         <param><value><int>2</int></value></param>
1307       *     </params>
1308       * </methodCall>
1309       */
1310  	function bb_getTopics( $args )
1311      {
1312          do_action( 'bb_xmlrpc_call', 'bb.getTopics' );
1313  
1314          // Escape args
1315          $this->escape( $args );
1316  
1317          // Get the login credentials
1318          $username = $args[0];
1319          $password = (string) $args[1];
1320  
1321          // Check the user is valid
1322          if ( $this->auth_readonly ) {
1323              $user = $this->authenticate( $username, $password );
1324          }
1325  
1326          do_action( 'bb_xmlrpc_call_authenticated', 'bb.getTopics' );
1327  
1328          // If an error was raised by authentication or by an action then return it
1329          if ( $this->error ) {
1330              return $this->error;
1331          }
1332  
1333          // Setup an array to store arguments to pass to get_topics() function
1334          $get_topics_args = array(
1335              'forum' => false,
1336              'number' => false,
1337              'page' => false
1338          );
1339  
1340          // Can be numeric id or slug
1341          if ( isset( $args[2] ) && $forum_id = $args[2] ) {
1342              // Check for bad data
1343              if ( !is_string( $forum_id ) && !is_integer( $forum_id ) ) {
1344                  $this->error = new IXR_Error( 400, __( 'The forum id is invalid.' ) );
1345                  return $this->error;
1346              }
1347              // Check the requested forum exists
1348              if ( !$forum = bb_get_forum( $forum_id ) ) {
1349                  $this->error = new IXR_Error( 400, __( 'The forum does not exist.' ) );
1350                  return $this->error;
1351              }
1352  
1353              // The forum id may have been a slug, so make sure it's an integer here
1354              $get_topics_args['forum'] = (int) $forum->forum_id;
1355          }
1356  
1357          // Can only be an integer
1358          if ( isset( $args[3] ) && $number = (int) $args[3] ) {
1359              $get_topics_args['number'] = $number;
1360          }
1361  
1362          // Can only be an integer
1363          if ( isset( $args[4] ) && $page = (int) $args[4] ) {
1364              $get_topics_args['page'] = $page;
1365          }
1366  
1367          // Get the topics
1368          if ( !$topics = get_latest_topics( $get_topics_args ) ) {
1369              $this->error = new IXR_Error( 400, __( 'No topics found.' ) );
1370              return $this->error;
1371          }
1372  
1373          // Only include "safe" data in the array
1374          $_topics = array();
1375          foreach ( $topics as $topic ) {
1376              $_topics[] = $this->prepare_topic( $topic );
1377          }
1378  
1379          do_action( 'bb_xmlrpc_call_return', 'bb.getTopics' );
1380  
1381          // Return the topics
1382          return $_topics;
1383      }
1384  
1385      /**
1386       * Returns details of a topic
1387       *
1388       * @since 1.0
1389       * @return array|object An array containing details of the returned topic when successfully executed or an IXR_Error object on failure
1390       * @param string $args[0] The username for authentication
1391       * @param string $args[1] The password for authentication
1392       * @param integer|string $args[2] The topic's id or slug
1393       *
1394       * XML-RPC request to get the topic with id number 105
1395       * <methodCall>
1396       *     <methodName>bb.getTopic</methodName>
1397       *     <params>
1398       *         <param><value><string>joeblow</string></value></param>
1399       *         <param><value><string>123password</string></value></param>
1400       *         <param><value><int>105</int></value></param>
1401       *     </params>
1402       * </methodCall>
1403       *
1404       * XML-RPC request to get the topic with slug "cheesy-biscuits"
1405       * <methodCall>
1406       *     <methodName>bb.getTopic</methodName>
1407       *     <params>
1408       *         <param><value><string>joeblow</string></value></param>
1409       *         <param><value><string>123password</string></value></param>
1410       *         <param><value><string>cheesy-biscuits</string></value></param>
1411       *     </params>
1412       * </methodCall>
1413       */
1414  	function bb_getTopic( $args )
1415      {
1416          do_action( 'bb_xmlrpc_call', 'bb.getTopic' );
1417  
1418          // Escape args
1419          $this->escape( $args );
1420  
1421          // Get the login credentials
1422          $username = $args[0];
1423          $password = (string) $args[1];
1424  
1425          // Check the user is valid
1426          if ( $this->auth_readonly ) {
1427              $user = $this->authenticate( $username, $password );
1428          }
1429  
1430          do_action( 'bb_xmlrpc_call_authenticated', 'bb.getTopic' );
1431  
1432          // If an error was raised by authentication or by an action then return it
1433          if ( $this->error ) {
1434              return $this->error;
1435          }
1436  
1437          // Can be numeric id or slug
1438          $topic_id = isset( $args[2] ) ? $args[2] : false;
1439  
1440          // Check for bad data
1441          if ( !$topic_id || ( !is_string( $topic_id ) && !is_integer( $topic_id ) ) ) {
1442              $this->error = new IXR_Error( 400, __( 'The topic id is invalid.' ) );
1443              return $this->error;
1444          }
1445  
1446          // Check the requested topic exists
1447          if ( !$topic = get_topic( $topic_id ) ) {
1448              $this->error = new IXR_Error( 400, __( 'No topic found.' ) );
1449              return $this->error;
1450          }
1451  
1452          // Only include "safe" data in the array
1453          $topic = $this->prepare_topic( $topic );
1454  
1455          do_action( 'bb_xmlrpc_call_return', 'bb.getTopic' );
1456  
1457          // Return the topic
1458          return $topic;
1459      }
1460  
1461      /**
1462       * Creates a new topic
1463       *
1464       * @since 1.0
1465       * @return array|object The topic data when successfully created or an IXR_Error object on failure
1466       * @param array $args Arguments passed by the XML-RPC call
1467       * @param string $args[0] The username for authentication
1468       * @param string $args[1] The password for authentication
1469       * @param array $args[2] The values for the various parameters in the new topic
1470       * @param string $args[2]['title'] The title of the topic
1471       * @param string $args[2]['text'] The text of the topic
1472       * @param integer|string $args[2]['forum_id'] The unique id of the forum which will contain this topic, slugs are OK to use too
1473       * @param string|array $args[2]['tags'] A comma delimited string or an array of tags to add to the topic (optional)
1474       *
1475       * XML-RPC request to create a new topic called "Insane monkeys" inside the forum with id 2
1476       * <methodCall>
1477       *     <methodName>bb.newTopic</methodName>
1478       *     <params>
1479       *         <param><value><string>joeblow</string></value></param>
1480       *         <param><value><string>123password</string></value></param>
1481       *         <param><value><struct>
1482       *             <member>
1483       *                 <name>title</name>
1484       *                 <value><string>Insane monkeys</string></value>
1485       *             </member>
1486       *             <member>
1487       *                 <name>text</name>
1488       *                 <value><string>I just saw some insane monkeys eating bananas, did anyone else see that?</string></value>
1489       *             </member>
1490       *             <member>
1491       *                 <name>forum_id</name>
1492       *                 <value><integer>2</integer></value>
1493       *             </member>
1494       *             <member>
1495       *                 <name>tags</name>
1496       *                 <value><string>monkeys, bananas</string></value>
1497       *             </member>
1498       *         </struct></value></param>
1499       *     </params>
1500       * </methodCall>
1501       */
1502  	function bb_newTopic( $args )
1503      {
1504          do_action( 'bb_xmlrpc_call', 'bb.newTopic' );
1505  
1506          // Escape args
1507          $this->escape( $args );
1508  
1509          // Get the login credentials
1510          $username = $args[0];
1511          $password = (string) $args[1];
1512  
1513          // Check the user is valid
1514          $user = $this->authenticate( $username, $password, 'write_topics', __( 'You do not have permission to write topics.' ) );
1515  
1516          // Additionally they need to be able to write posts
1517          if ( !$this->error && !bb_current_user_can( 'write_posts' ) ) {
1518              $this->error = new IXR_Error( 403, __( 'You do not have permission to write posts.' ) );
1519          }
1520  
1521          do_action( 'bb_xmlrpc_call_authenticated', 'bb.newTopic' );
1522  
1523          // If an error was raised by authentication or by an action then return it
1524          if ( $this->error ) {
1525              return $this->error;
1526          }
1527  
1528          // Make sure there is something for us to do
1529          if ( !$args[2] || !is_array( $args[2] ) || !count( $args[2] ) ) {
1530              $this->error = new IXR_Error( 400, __( 'The topic data is invalid.' ) );
1531              return $this->error;
1532          }
1533  
1534          $structure = (array) $args[2];
1535  
1536          // Can be numeric id or slug
1537          $forum_id = isset( $structure['forum_id'] ) ? $structure['forum_id'] : false;
1538  
1539          // Check for bad data
1540          if ( !$forum_id || ( !is_string( $forum_id ) && !is_integer( $forum_id ) ) ) {
1541              $this->error = new IXR_Error( 400, __( 'The forum id is invalid.' ) );
1542              return $this->error;
1543          }
1544  
1545          // Check the requested forum exists
1546          if ( !$forum = bb_get_forum( $forum_id ) ) {
1547              $this->error = new IXR_Error( 400, __( 'No forum found.' ) );
1548              return $this->error;
1549          }
1550  
1551          // The forum id may have been a slug, so make sure it's an integer here
1552          $forum_id = (int) $forum->forum_id;
1553  
1554          // Make sure they are allowed to write topics to this forum
1555          if ( !bb_current_user_can( 'write_topic', $forum_id ) ) {
1556              $this->error = new IXR_Error( 403, __( 'You do not have permission to write topics to this forum.' ) );
1557              return $this->error;
1558          }
1559  
1560          // The topic requires a title
1561          if ( !isset( $structure['title'] ) || !$structure['title'] ) {
1562              $this->error = new IXR_Error( 400, __( 'The topic title is invalid.' ) );
1563              return $this->error;
1564          }
1565  
1566          // The topic requires text
1567          if ( !isset( $structure['text'] ) || !$structure['text'] ) {
1568              $this->error = new IXR_Error( 400, __( 'The topic text is invalid.' ) );
1569              return $this->error;
1570          }
1571  
1572          // Inject structure into an array suitable for bb_insert_topic()
1573          $bb_insert_topic_args = array(
1574              'topic_title' => (string) $structure['title'],
1575              'forum_id' => $forum_id,
1576              'tags' => (string) trim( $structure['tags'] )
1577          );
1578  
1579          // Remove empty settings so that changes to the defaults in bb_insert_topic() are honoured
1580          $bb_insert_topic_args = array_filter( $bb_insert_topic_args );
1581  
1582          // Create the topic
1583          if ( !$topic_id = bb_insert_topic( $bb_insert_topic_args ) ) {
1584              $this->error = new IXR_Error( 500, __( 'The topic could not be created.' ) );
1585              return $this->error;
1586          }
1587  
1588          // Inject structure into an array suitable for bb_insert_post()
1589          $bb_insert_post_args = array(
1590              'topic_id' => (int) $topic_id,
1591              'post_text' => (string) $structure['text']
1592          );
1593  
1594          // Create the post
1595          if ( !$post_id = bb_insert_post( $bb_insert_post_args ) ) {
1596              $this->error = new IXR_Error( 500, __( 'The post could not be created.' ) );
1597              return $this->error;
1598          }
1599  
1600          // Only include "safe" data in the array
1601          $topic = $this->prepare_topic( get_topic( $topic_id ) );
1602  
1603          do_action( 'bb_xmlrpc_call_return', 'bb.newTopic' );
1604  
1605          return $topic;
1606      }
1607  
1608      /**
1609       * Edits an existing topic
1610       *
1611       * @since 1.0
1612       * @return array|object The topic data when successfully edited or an IXR_Error object on failure
1613       * @param array $args Arguments passed by the XML-RPC call
1614       * @param string $args[0] The username for authentication
1615       * @param string $args[1] The password for authentication
1616       * @param array $args[2] The values for the various parameters in the edited topic
1617       * @param integer|string $args[2]['topic_id'] The topic's id or slug
1618       * @param string $args[2]['title'] The title of the topic
1619       * @param string $args[2]['text'] The text of the topic
1620       *
1621       * XML-RPC request to edit the title of a topic with the slug "insane-monkeys"
1622       * <methodCall>
1623       *     <methodName>bb.editTopic</methodName>
1624       *     <params>
1625       *         <param><value><string>joeblow</string></value></param>
1626       *         <param><value><string>123password</string></value></param>
1627       *         <param><value><struct>
1628       *             <member>
1629       *                 <name>topic_id</name>
1630       *                 <value><string>insane-monkeys</string></value>
1631       *             </member>
1632       *             <member>
1633       *                 <name>title</name>
1634       *                 <value><string>Very insane monkeys</string></value>
1635       *             </member>
1636       *         </struct></value></param>
1637       *     </params>
1638       * </methodCall>
1639       */
1640  	function bb_editTopic( $args )
1641      {
1642          do_action( 'bb_xmlrpc_call', 'bb.editTopic' );
1643  
1644          // Escape args
1645          $this->escape( $args );
1646  
1647          // Get the login credentials
1648          $username = $args[0];
1649          $password = (string) $args[1];
1650  
1651          // Check the user is valid
1652          $user = $this->authenticate( $username, $password, 'edit_topics', __( 'You do not have permission to edit topics.' ) );
1653  
1654          // Additionally they need to be able to edit posts
1655          if ( !$this->error && !bb_current_user_can( 'edit_posts' ) ) {
1656              $this->error = new IXR_Error( 403, __( 'You do not have permission to edit posts.' ) );
1657          }
1658  
1659          do_action( 'bb_xmlrpc_call_authenticated', 'bb.editTopic' );
1660  
1661          // If an error was raised by authentication or by an action then return it
1662          if ( $this->error ) {
1663              return $this->error;
1664          }
1665  
1666          // Make sure there is something for us to do
1667          if ( !$args[2] || !is_array( $args[2] ) || !count( $args[2] ) ) {
1668              $this->error = new IXR_Error( 400, __( 'The topic data is invalid.' ) );
1669              return $this->error;
1670          }
1671  
1672          $structure = (array) $args[2];
1673  
1674          // Can be numeric id or slug
1675          $topic_id = isset( $structure['topic_id'] ) ? $structure['topic_id'] : false;
1676  
1677          // Check for bad data
1678          if ( !$topic_id || ( !is_string( $topic_id ) && !is_integer( $topic_id ) ) ) {
1679              $this->error = new IXR_Error( 400, __( 'The topic id is invalid.' ) );
1680              return $this->error;
1681          }
1682  
1683          // Check the requested topic exists
1684          if ( !$topic = get_topic( $topic_id ) ) {
1685              $this->error = new IXR_Error( 400, __( 'No topic found.' ) );
1686              return $this->error;
1687          }
1688  
1689          // The topic id may have been a slug, so make sure it's an integer here
1690          $topic_id = (int) $topic->topic_id;
1691  
1692          // Make sure they are allowed to edit this topic
1693          if ( !bb_current_user_can( 'edit_topic', $topic_id ) ) {
1694              $this->error = new IXR_Error( 403, __( 'You do not have permission to edit this topic.' ) );
1695              return $this->error;
1696          }
1697  
1698          // Get the first post in the topic (that's where the content is)
1699          if ( !$post = bb_get_first_post( $topic_id ) ) {
1700              $this->error = new IXR_Error( 400, __( 'No posts found.' ) );
1701              return $this->error;
1702          }
1703  
1704          $post_id = (int) $post->post_id;
1705  
1706          // Make sure they are allowed to edit this post
1707          if ( !bb_current_user_can( 'edit_post', $post_id ) ) {
1708              $this->error = new IXR_Error( 403, __( 'You do not have permission to edit this post.' ) );
1709              return $this->error;
1710          }
1711  
1712          // The topic requires a title
1713          if ( isset( $structure['title'] ) && !$structure['title'] ) {
1714              $this->error = new IXR_Error( 400, __( 'The topic title is invalid.' ) );
1715              return $this->error;
1716          }
1717  
1718          // The topic requires text
1719          if ( isset( $structure['text'] ) && !$structure['text'] ) {
1720              $this->error = new IXR_Error( 400, __( 'The topic text is invalid.' ) );
1721              return $this->error;
1722          }
1723  
1724          if ( $structure['title'] ) {
1725              if ( !bb_insert_topic( array( 'topic_title' => (string) $structure['title'], 'topic_id' => $topic_id ) ) ) {
1726                  $this->error = new IXR_Error( 500, __( 'The topic could not be edited.' ) );
1727                  return $this->error;
1728              }
1729          }
1730  
1731          if ( $structure['text'] ) {
1732              if ( !bb_insert_post( array( 'post_text' => (string) $structure['text'], 'post_id' => $post_id, 'topic_id'=> $topic_id ) ) ) {
1733                  $this->error = new IXR_Error( 500, __( 'The post could not be edited.' ) );
1734                  return $this->error;
1735              }
1736          }
1737  
1738          // Only include "safe" data in the array
1739          $topic = $this->prepare_topic( get_topic( $topic_id ) );
1740  
1741          do_action( 'bb_xmlrpc_call_return', 'bb.editTopic' );
1742  
1743          return $topic;
1744      }
1745  
1746      /**
1747       * Deletes a topic
1748       *
1749       * @since 1.0
1750       * @return integer|object 0 if already changed, 1 when successfully changed or an IXR_Error object on failure
1751       * @param array $args Arguments passed by the XML-RPC call
1752       * @param string $args[0] The username for authentication
1753       * @param string $args[1] The password for authentication
1754       * @param integer|string $args[2] The unique id of the topic to be deleted
1755       * @param integer $args[3] 1 deletes the topic, 0 undeletes the topic
1756       *
1757       * XML-RPC request to delete a topic with id of 34
1758       * <methodCall>
1759       *     <methodName>bb.deleteTopic</methodName>
1760       *     <params>
1761       *         <param><value><string>joeblow</string></value></param>
1762       *         <param><value><string>123password</string></value></param>
1763       *         <param><value><integer>34</integer></value></param>
1764       *     </params>
1765       * </methodCall>
1766       */
1767  	function bb_deleteTopic( $args )
1768      {
1769          do_action( 'bb_xmlrpc_call', 'bb.deleteTopic' );
1770  
1771          // Escape args
1772          $this->escape( $args );
1773  
1774          // Get the login credentials
1775          $username = $args[0];
1776          $password = (string) $args[1];
1777  
1778          // Check the user is valid
1779          $user = $this->authenticate( $username, $password, 'delete_topics', __( 'You do not have permission to delete topics.' ) );
1780  
1781          do_action( 'bb_xmlrpc_call_authenticated', 'bb.deleteTopic' );
1782  
1783          // If an error was raised by authentication or by an action then return it
1784          if ( $this->error ) {
1785              return $this->error;
1786          }
1787  
1788          // Can be numeric id or slug
1789          $topic_id = isset( $args[2] ) ? $args[2] : false;
1790  
1791          // Check for bad data
1792          if ( !$topic_id || ( !is_string( $topic_id ) && !is_integer( $topic_id ) ) ) {
1793              $this->error = new IXR_Error( 400, __( 'The topic id is invalid.' ) );
1794              return $this->error;
1795          }
1796  
1797          // Check the requested topic exists
1798          if ( !$topic = get_topic( $topic_id ) ) {
1799              $this->error = new IXR_Error( 400, __( 'No topic found.' ) );
1800              return $this->error;
1801          }
1802  
1803          // The topic id may have been a slug, so make sure it's an integer here
1804          $topic_id = (int) $topic->topic_id;
1805  
1806          $delete = isset( $args[3] ) ? (int) $args[3] : 1;
1807  
1808          // Don't do anything if already set that way
1809          if ( $delete === (int) $topic->topic_status ) {
1810              return 0;
1811          }
1812  
1813          // Make sure they are allowed to delete this topic
1814          if ( !bb_current_user_can( 'delete_topic', $topic_id ) ) {
1815              $this->error = new IXR_Error( 403, __( 'You do not have permission to delete this topic.' ) );
1816              return $this->error;
1817          }
1818  
1819          // Delete the topic
1820          if ( !bb_delete_topic( $topic_id, $delete ) ) {
1821              $this->error = new IXR_Error( 500, __( 'The topic could not be deleted.' ) );
1822              return $this->error;
1823          }
1824  
1825          $result = 1;
1826  
1827          do_action( 'bb_xmlrpc_call_return', 'bb.deleteTopic' );
1828  
1829          return $result;
1830      }
1831  
1832      /**
1833       * Moves a topic to a different forum
1834       *
1835       * @since 1.0
1836       * @return integer|object the forum id where the topic lives after the method is called or an IXR_Error object on failure
1837       * @param array $args Arguments passed by the XML-RPC call
1838       * @param string $args[0] The username for authentication
1839       * @param string $args[1] The password for authentication
1840       * @param integer|string $args[2] The unique id of the topic to be moved
1841       * @param integer|string $args[3] The unique id of the forum to be moved to
1842       *
1843       * XML-RPC request to move the topic with id of 34 to forum with slug of "better-forum"
1844       * <methodCall>
1845       *     <methodName>bb.moveTopic</methodName>
1846       *     <params>
1847       *         <param><value><string>joeblow</string></value></param>
1848       *         <param><value><string>123password</string></value></param>
1849       *         <param><value><integer>34</integer></value></param>
1850       *         <param><value><string>better-forum</string></value></param>
1851       *     </params>
1852       * </methodCall>
1853       */
1854  	function bb_moveTopic( $args )
1855      {
1856          do_action( 'bb_xmlrpc_call', 'bb.moveTopic' );
1857  
1858          // Escape args
1859          $this->escape( $args );
1860  
1861          // Get the login credentials
1862          $username = $args[0];
1863          $password = (string) $args[1];
1864  
1865          // Check the user is valid
1866          $user = $this->authenticate( $username, $password, 'move_topics', __( 'You do not have permission to move topics.' ) );
1867  
1868          do_action( 'bb_xmlrpc_call_authenticated', 'bb.moveTopic' );
1869  
1870          // If an error was raised by authentication or by an action then return it
1871          if ( $this->error ) {
1872              return $this->error;
1873          }
1874  
1875          // Can be numeric id or slug
1876          $topic_id = isset( $args[2] ) ? $args[2] : false;
1877  
1878          // Check for bad data
1879          if ( !$topic_id || ( !is_string( $topic_id ) && !is_integer( $topic_id ) ) ) {
1880              $this->error = new IXR_Error( 400, __( 'The topic id is invalid.' ) );
1881              return $this->error;
1882          }
1883  
1884          // Check the requested topic exists
1885          if ( !$topic = get_topic( $topic_id ) ) {
1886              $this->error = new IXR_Error( 400, __( 'No topic found.' ) );
1887              return $this->error;
1888          }
1889  
1890          // The topic id may have been a slug, so make sure it's an integer here
1891          $topic_id = (int) $topic->topic_id;
1892  
1893          // Can be numeric id or slug
1894          $forum_id = isset( $args[3] ) ? $args[3] : false;
1895  
1896          // Check for bad data
1897          if ( !$forum_id || ( !is_string( $forum_id ) && !is_integer( $forum_id ) ) ) {
1898              $this->error = new IXR_Error( 400, __( 'The forum id is invalid.' ) );
1899              return $this->error;
1900          }
1901  
1902          // Check the requested topic exists
1903          if ( !$forum = bb_get_forum( $forum_id ) ) {
1904              $this->error = new IXR_Error( 400, __( 'No forum found.' ) );
1905              return $this->error;
1906          }
1907  
1908          // The forum id may have been a slug, so make sure it's an integer here
1909          $forum_id = (int) $forum->forum_id;
1910  
1911          // Only move it if it isn't already there
1912          if ( $forum_id !== (int) $topic->forum_id ) {
1913              // Make sure they are allowed to move this topic specifically to this forum
1914              if ( !bb_current_user_can( 'move_topic', $topic_id, $forum_id ) ) {
1915                  $this->error = new IXR_Error( 403, __( 'You are not allowed to move this topic to this forum.' ) );
1916                  return $this->error;
1917              }
1918  
1919              // Move the topic
1920              if ( !bb_move_topic( $topic_id, $forum_id ) ) {
1921                  $this->error = new IXR_Error( 500, __( 'The topic could not be moved.' ) );
1922                  return $this->error;
1923              }
1924          }
1925  
1926          do_action( 'bb_xmlrpc_call_return', 'bb.moveTopic' );
1927  
1928          return $forum_id;
1929      }
1930  
1931      /**
1932       * Sticks a topic to the top of a forum or the front page
1933       *
1934       * @since 1.0
1935       * @return integer|object 0 if it is already stuck to the desired location, 1 when successfully stuck or an IXR_Error object on failure
1936       * @param array $args Arguments passed by the XML-RPC call
1937       * @param string $args[0] The username for authentication
1938       * @param string $args[1] The password for authentication
1939       * @param integer|string $args[2] The unique id of the topic to be stuck
1940       * @param integer $args[3] 0 unsticks, 1 sticks, 2 sticks to front (optional)
1941       *
1942       * XML-RPC request to stick the topic with id of 34 to the front page
1943       * <methodCall>
1944       *     <methodName>bb.stickTopic</methodName>
1945       *     <params>
1946       *         <param><value><string>joeblow</string></value></param>
1947       *         <param><value><string>123password</string></value></param>
1948       *         <param><value><integer>34</integer></value></param>
1949       *         <param><value><integer>1</integer></value></param>
1950       *     </params>
1951       * </methodCall>
1952       */
1953  	function bb_stickTopic( $args )
1954      {
1955          do_action( 'bb_xmlrpc_call', 'bb.stickTopic' );
1956  
1957          // Escape args
1958          $this->escape( $args );
1959  
1960          // Get the login credentials
1961          $username = $args[0];
1962          $password = (string) $args[1];
1963  
1964          // Check the user is valid
1965          $user = $this->authenticate( $username, $password, 'stick_topics', __( 'You do not have permission to stick topics.' ) );
1966  
1967          do_action( 'bb_xmlrpc_call_authenticated', 'bb.stickTopic' );
1968  
1969          // If an error was raised by authentication or by an action then return it
1970          if ( $this->error ) {
1971              return $this->error;
1972          }
1973  
1974          // Can be numeric id or slug
1975          $topic_id = isset( $args[2] ) ? $args[2] : false;
1976  
1977          // Check for bad data
1978          if ( !$topic_id || ( !is_string( $topic_id ) && !is_integer( $topic_id ) ) ) {
1979              $this->error = new IXR_Error( 400, __( 'The topic id is invalid.' ) );
1980              return $this->error;
1981          }
1982  
1983          // Check the requested topic exists
1984          if ( !$topic = get_topic( $topic_id ) ) {
1985              $this->error = new IXR_Error( 400, __( 'No topic found.' ) );
1986              return $this->error;
1987          }
1988  
1989          // The topic id may have been a slug, so make sure it's an integer here
1990          $topic_id = (int) $topic->topic_id;
1991  
1992          // Make sure they are allowed to stick this topic
1993          if ( !bb_current_user_can( 'stick_topic', $topic_id ) ) {
1994              $this->error = new IXR_Error( 403, __( 'You do not have permission to stick this topic.' ) );
1995              return $this->error;
1996          }
1997  
1998          // Stick to where?
1999          $where = isset( $args[3] ) ? (int) $args[3] : 1;
2000  
2001          // Forget it if it's already there
2002          if ( $where === (int) $topic->topic_sticky ) {
2003              return 0;
2004          }
2005  
2006          // Stick the topic
2007          if ( !bb_stick_topic( $topic_id, $where ) ) {
2008              $this->error = new IXR_Error( 500, __( 'The topic could not be stuck.' ) );
2009              return $this->error;
2010          }
2011  
2012          $result = 1;
2013  
2014          do_action( 'bb_xmlrpc_call_return', 'bb.stickTopic' );
2015  
2016          return $result;
2017      }
2018  
2019  
2020  
2021      /**
2022       * Closes a topic
2023       *
2024       * @since 1.0
2025       * @return integer|object 0 when already changed, 1 when successfully changed or an IXR_Error object on failure
2026       * @param array $args Arguments passed by the XML-RPC call
2027       * @param string $args[0] The username for authentication
2028       * @param string $args[1] The password for authentication
2029       * @param integer|string $args[2] The unique id of the topic to be closed
2030       * @param integer $args[2] 0 closes, 1 opens (optional)
2031       *
2032       * XML-RPC request to close the topic with slug of "really-old-topic"
2033       * <methodCall>
2034       *     <methodName>bb.closeTopic</methodName>
2035       *     <params>
2036       *         <param><value><string>joeblow</string></value></param>
2037       *         <param><value><string>123password</string></value></param>
2038       *         <param><value><string>really-old-topic</string></value></param>
2039       *     </params>
2040       * </methodCall>
2041       *
2042       * XML-RPC request to open the topic with slug of "really-old-topic"
2043       * <methodCall>
2044       *     <methodName>bb.closeTopic</methodName>
2045       *     <params>
2046       *         <param><value><string>joeblow</string></value></param>
2047       *         <param><value><string>123password</string></value></param>
2048       *         <param><value><string>really-old-topic</string></value></param>
2049       *         <param><value><integer>1</integer></value></param>
2050       *     </params>
2051       * </methodCall>
2052       */
2053  	function bb_closeTopic( $args )
2054      {
2055          do_action( 'bb_xmlrpc_call', 'bb.closeTopic' );
2056  
2057          // Escape args
2058          $this->escape( $args );
2059  
2060          // Get the login credentials
2061          $username = $args[0];
2062          $password = (string) $args[1];
2063  
2064          // Check the user is valid
2065          $user = $this->authenticate( $username, $password, 'close_topics', __( 'You do not have permission to close topics.' ) );
2066  
2067          do_action( 'bb_xmlrpc_call_authenticated', 'bb.closeTopic' );
2068  
2069          // If an error was raised by authentication or by an action then return it
2070          if ( $this->error ) {
2071              return $this->error;
2072          }
2073  
2074          // Can be numeric id or slug
2075          $topic_id = isset( $args[2] ) ? $args[2] : false;
2076  
2077          // Check for bad data
2078          if ( !$topic_id || ( !is_string( $topic_id ) && !is_integer( $topic_id ) ) ) {
2079              $this->error = new IXR_Error( 400, __( 'The topic id is invalid.' ) );
2080              return $this->error;
2081          }
2082  
2083          // Check the requested topic exists
2084          if ( !$topic = get_topic( $topic_id ) ) {
2085              $this->error = new IXR_Error( 400, __( 'No topic found.' ) );
2086              return $this->error;
2087          }
2088  
2089          // The topic id may have been a slug, so make sure it's an integer here
2090          $topic_id = (int) $topic->topic_id;
2091  
2092          // Make sure they are allowed to close this topic
2093          if ( !bb_current_user_can( 'close_topic', $topic_id ) ) {
2094              $this->error = new IXR_Error( 403, __( 'You do not have permission to close this topic.' ) );
2095              return $this->error;
2096          }
2097  
2098          // Open or close?
2099          $close = isset( $args[3] ) ? (int) $args[3] : 0;
2100  
2101          // Forget it if it's already matching
2102          if ( $close === (int) $topic->topic_open ) {
2103              return 0;
2104          }
2105  
2106          // Close the topic
2107          if ( !$close && !bb_close_topic( $topic_id ) ) {
2108              $this->error = new IXR_Error( 500, __( 'The topic could not be closed.' ) );
2109              return $this->error;
2110          }
2111  
2112          // Open the topic
2113          if ( $close && !bb_open_topic( $topic_id ) ) {
2114              $this->error = new IXR_Error( 500, __( 'The topic could not be opened.' ) );
2115              return $this->error;
2116          }
2117  
2118          $result = 1;
2119  
2120          do_action( 'bb_xmlrpc_call_return', 'bb.closeTopic' );
2121  
2122          return $result;
2123      }
2124  
2125  
2126  
2127      /**
2128       * bbPress publishing API - Post XML-RPC methods
2129       */
2130  
2131      /**
2132       * Returns a numerical count of posts
2133       *
2134       * @since 1.0
2135       * @return integer|object The number of topics when successfully executed or an IXR_Error object on failure
2136       * @param array $args Arguments passed by the XML-RPC call
2137       * @param string $args[0] The username for authentication
2138       * @param string $args[1] The password for authentication
2139       * @param integer|string $args[2] The topic id or slug
2140       *
2141       * XML-RPC request to get a count of all posts in the topic with slug "countable-topic"
2142       * <methodCall>
2143       *     <methodName>bb.getPostCount</methodName>
2144       *     <params>
2145       *         <param><value><string>joeblow</string></value></param>
2146       *         <param><value><string>123password</string></value></param>
2147       *         <param><value><string>countable-topic</string></value></param>
2148       *     </params>
2149       * </methodCall>
2150       */
2151  	function bb_getPostCount( $args )
2152      {
2153          do_action( 'bb_xmlrpc_call', 'bb.getPostCount' );
2154  
2155          // Escape args
2156          $this->escape( $args );
2157  
2158          // Get the login credentials
2159          $username = $args[0];
2160          $password = (string) $args[1];
2161  
2162          // Check the user is valid
2163          if ( $this->auth_readonly ) {
2164              $user = $this->authenticate( $username, $password );
2165          }
2166  
2167          do_action( 'bb_xmlrpc_call_authenticated', 'bb.getPostCount' );
2168  
2169          // If an error was raised by authentication or by an action then return it
2170          if ( $this->error ) {
2171              return $this->error;
2172          }
2173  
2174          // Can be numeric id or slug
2175          $topic_id = isset( $args[2] ) ? $args[2] : false;
2176  
2177          // Check for bad data
2178          if ( !$topic_id || ( !is_string( $topic_id ) && !is_integer( $topic_id ) ) ) {
2179              $this->error = new IXR_Error( 400, __( 'The topic id is invalid.' ) );
2180              return $this->error;
2181          }
2182  
2183          // Check the requested topic exists
2184          if ( !$topic = get_topic( $topic_id ) ) {
2185              $this->error = new IXR_Error( 400, __( 'No topic found.' ) );
2186              return $this->error;
2187          }
2188  
2189          // OK, let's trust the count in the topic table
2190          $count = $topic->topic_posts;
2191  
2192          do_action( 'bb_xmlrpc_call_return', 'bb.getPostCount' );
2193  
2194          // Return the count of posts
2195          return $count;
2196      }
2197  
2198      /**
2199       * Returns details of the posts in a given topic
2200       *
2201       * @since 1.0
2202       * @return array|object The posts when successfully executed or an IXR_Error object on failure
2203       * @param array $args Arguments passed by the XML-RPC call
2204       * @param string $args[0] The username for authentication
2205       * @param string $args[1] The password for authentication
2206       * @param integer|string $args[2] The topic id or slug
2207       * @param integer $args[3] The number of posts to return (optional)
2208       * @param integer $args[4] The number of the page to return (optional)
2209       *
2210       * XML-RPC request to get all posts in the topic with id number 53
2211       * <methodCall>
2212       *     <methodName>bb.getPosts</methodName>
2213       *     <params>
2214       *         <param><value><string>joeblow</string></value></param>
2215       *         <param><value><string>123password</string></value></param>
2216       *         <param><value><int>53</int></value></param>
2217       *     </params>
2218       * </methodCall>
2219       *
2220       * XML-RPC request to get the latest 5 posts in the topic with id number 341
2221       * <methodCall>
2222       *     <methodName>bb.getPosts</methodName>
2223       *     <params>
2224       *         <param><value><string>joeblow</string></value></param>
2225       *         <param><value><string>123password</string></value></param>
2226       *         <param><value><int>341</int></value></param>
2227       *         <param><value><int>5</int></value></param>
2228       *     </params>
2229       * </methodCall>
2230       *
2231       * XML-RPC request to get posts 11 to 20 in the topic with slug "long-topic"
2232       * <methodCall>
2233       *     <methodName>bb.getPosts</methodName>
2234       *     <params>
2235       *         <param><value><string>joeblow</string></value></param>
2236       *         <param><value><string>123password</string></value></param>
2237       *         <param><value><string>long-topic</string></value></param>
2238       *         <param><value><int>10</int></value></param>
2239       *         <param><value><int>2</int></value></param>
2240       *     </params>
2241       * </methodCall>
2242       */
2243  	function bb_getPosts( $args )
2244      {
2245          do_action( 'bb_xmlrpc_call', 'bb.getPosts' );
2246  
2247          // Escape args
2248          $this->escape( $args );
2249  
2250          // Get the login credentials
2251          $username = $args[0];
2252          $password = (string) $args[1];
2253  
2254          // Check the user is valid
2255          if ( $this->auth_readonly ) {
2256              $user = $this->authenticate( $username, $password );
2257          }
2258  
2259          do_action( 'bb_xmlrpc_call_authenticated', 'bb.getPosts' );
2260  
2261          // If an error was raised by authentication or by an action then return it
2262          if ( $this->error ) {
2263              return $this->error;
2264          }
2265  
2266          // Can be numeric id or slug
2267          $topic_id = isset( $args[2] ) ? $args[2] : false;
2268  
2269          // Check for bad data
2270          if ( !$topic_id || ( !is_string( $topic_id ) && !is_integer( $topic_id ) ) ) {
2271              $this->error = new IXR_Error( 400, __( 'The topic id is invalid.' ) );
2272              return $this->error;
2273          }
2274  
2275          // Check the requested topic exists
2276          if ( !$topic = get_topic( $topic_id ) ) {
2277              $this->error = new IXR_Error( 400, __( 'No topic found.' ) );
2278              return $this->error;
2279          }
2280  
2281          // The topic id may have been a slug, so make sure it's an integer here
2282          $topic_id = (int) $topic->topic_id;
2283  
2284          // Setup an array to store arguments to pass to get_thread() function
2285          $get_thread_args = array();
2286  
2287          // Can only be an integer
2288          if ( isset( $args[3] ) && $per_page = (int) $args[3] ) {
2289              $get_thread_args['per_page'] = $per_page;
2290          }
2291  
2292          // Can only be an integer
2293          if ( isset( $args[4] ) && $page = (int) $args[4] ) {
2294              $get_thread_args['page'] = $page;
2295          }
2296  
2297          // Get the posts
2298          if ( !$posts = get_thread( $topic_id, $get_thread_args ) ) {
2299              $this->error = new IXR_Error( 500, __( 'No posts found.' ) );
2300              return $this->error;
2301          }
2302  
2303          // Only include "safe" data in the array
2304          $_posts = array();
2305          foreach ( $posts as $post ) {
2306              $_posts[] = $this->prepare_post( $post );
2307          }
2308  
2309          do_action( 'bb_xmlrpc_call_return', 'bb.getPosts' );
2310  
2311          // Return the posts
2312          return $_posts;
2313      }
2314  
2315      /**
2316       * Returns details of a post
2317       *
2318       * @since 1.0
2319       * @return array|object An array containing details of the returned post when successfully executed or an IXR_Error object on failure
2320       * @param array $args Arguments passed by the XML-RPC call
2321       * @param string $args[0] The username for authentication
2322       * @param string $args[1] The password for authentication
2323       * @param integer $args[2] The post's id
2324       *
2325       * XML-RPC request to get the post with id number 32
2326       * <methodCall>
2327       *     <methodName>bb.getPost</methodName>
2328       *     <params>
2329       *         <param><value><string>joeblow</string></value></param>
2330       *         <param><value><string>123password</string></value></param>
2331       *         <param><value><int>32</int></value></param>
2332       *     </params>
2333       * </methodCall>
2334       */
2335  	function bb_getPost( $args )
2336      {
2337          do_action( 'bb_xmlrpc_call', 'bb.getPost' );
2338  
2339          // Escape args
2340          $this->escape( $args );
2341  
2342          // Get the login credentials
2343          $username = $args[0];
2344          $password = (string) $args[1];
2345  
2346          // Check the user is valid
2347          if ( $this->auth_readonly ) {
2348              $user = $this->authenticate( $username, $password );
2349          }
2350  
2351          do_action( 'bb_xmlrpc_call_authenticated', 'bb.getPost' );
2352  
2353          // If an error was raised by authentication or by an action then return it
2354          if ( $this->error ) {
2355              return $this->error;
2356          }
2357  
2358          // Can be numeric id or slug
2359          $post_id = isset( $args[2] ) ? (int) $args[2] : false;
2360  
2361          // Check for bad data
2362          if ( !$post_id ) {
2363              $this->error = new IXR_Error( 400, __( 'The post id is invalid.' ) );
2364              return $this->error;
2365          }
2366  
2367          // Check the requested post exists
2368          if ( !$post = bb_get_post( $post_id ) ) {
2369              $this->error = new IXR_Error( 400, __( 'No post found.' ) );
2370              return $this->error;
2371          }
2372  
2373          // Only include "safe" data in the array
2374          $_post = $this->prepare_post( $post );
2375  
2376          do_action( 'bb_xmlrpc_call_return', 'bb.getPost' );
2377  
2378          // Return the post
2379          return $_post;
2380      }
2381  
2382      /**
2383       * Creates a new post in a given topic
2384       *
2385       * @since 1.0
2386       * @return array|object The post data when successfully created or an IXR_Error object on failure
2387       * @param array $args Arguments passed by the XML-RPC call
2388       * @param string $args[0] The username for authentication
2389       * @param string $args[1] The password for authentication
2390       * @param array $args[2] The values for the various parameters in the new topic
2391       * @param string $args[2]['text'] The text of the topic
2392       * @param integer|string $args[2]['topic_id'] The unique id of the topic which will contain this topic, slugs are OK to use too
2393       *
2394       * XML-RPC request to create a new post in the topic with slug "totally-worth-it"
2395       * <methodCall>
2396       *     <methodName>bb.newPost</methodName>
2397       *     <params>
2398       *         <param><value><string>joeblow</string></value></param>
2399       *         <param><value><string>123password</string></value></param>
2400       *         <param><value><struct>
2401       *             <member>
2402       *                 <name>text</name>
2403       *                 <value><string>I agree, it is totally worth it.</string></value>
2404       *             </member>
2405       *             <member>
2406       *                 <name>topic_id</name>
2407       *                 <value><string>totally-worth-it</string></value>
2408       *             </member>
2409       *         </struct></value></param>
2410       *     </params>
2411       * </methodCall>
2412       */
2413  	function bb_newPost( $args )
2414      {
2415          do_action( 'bb_xmlrpc_call', 'bb.newPost' );
2416  
2417          // Escape args
2418          $this->escape( $args );
2419  
2420          // Get the login credentials
2421          $username = $args[0];
2422          $password = (string) $args[1];
2423  
2424          // Check the user is valid
2425          $user = $this->authenticate( $username, $password, 'write_posts', __( 'You do not have permission to write posts.' ) );
2426  
2427          do_action( 'bb_xmlrpc_call_authenticated', 'bb.newPost' );
2428  
2429          // If an error was raised by authentication or by an action then return it
2430          if ( $this->error ) {
2431              return $this->error;
2432          }
2433  
2434          // Make sure there is something for us to do
2435          if ( !$args[2] || !is_array( $args[2] ) || !count( $args[2] ) ) {
2436              $this->error = new IXR_Error( 400, __( 'The post data is invalid.' ) );
2437              return $this->error;
2438          }
2439  
2440          $structure = (array) $args[2];
2441  
2442          // Can be numeric id or slug
2443          $topic_id = isset( $structure['topic_id'] ) ? $structure['topic_id'] : false;
2444  
2445          // Check for bad data
2446          if ( !$topic_id || ( !is_string( $topic_id ) && !is_integer( $topic_id ) ) ) {
2447              $this->error = new IXR_Error( 400, __( 'The topic id is invalid.' ) );
2448              return $this->error;
2449          }
2450  
2451          // Check the requested topic exists
2452          if ( !$topic = get_topic( $topic_id ) ) {
2453              $this->error = new IXR_Error( 400, __( 'No topic found.' ) );
2454              return $this->error;
2455          }
2456  
2457          // The topic id may have been a slug, so make sure it's an integer here
2458          $topic_id = (int) $topic->topic_id;
2459  
2460          // Make sure they are allowed to write posts to this topic
2461          if ( !bb_current_user_can( 'write_post', $topic_id ) ) {
2462              $this->error = new IXR_Error( 403, __( 'You do not have permission to write posts to this topic.' ) );
2463              return $this->error;
2464          }
2465  
2466          // The post requires text
2467          if ( !isset( $structure['text'] ) || !$structure['text'] ) {
2468              $this->error = new IXR_Error( 400, __( 'The post text is invalid.' ) );
2469              return $this->error;
2470          }
2471  
2472          // Inject structure into an array suitable for bb_insert_post()
2473          $bb_insert_post_args = array(
2474              'topic_id' => $topic_id,
2475              'post_text' => (string) $structure['text']
2476          );
2477  
2478          // Create the post
2479          if ( !$post_id = bb_insert_post( $bb_insert_post_args ) ) {
2480              $this->error = new IXR_Error( 500, __( 'The post could not be created.' ) );
2481              return $this->error;
2482          }
2483  
2484          // Only include "safe" data in the array
2485          $post = $this->prepare_forum( bb_get_post( $post_id ) );
2486  
2487          do_action( 'bb_xmlrpc_call_return', 'bb.newPost' );
2488  
2489          return $post;
2490      }
2491  
2492      /**
2493       * Edits an existing post
2494       *
2495       * @since 1.0
2496       * @return array|object The post data when successfully edited or an IXR_Error object on failure
2497       * @param array $args Arguments passed by the XML-RPC call
2498       * @param string $args[0] The username for authentication
2499       * @param string $args[1] The password for authentication
2500       * @param array $args[2] The values for the various parameters in the new topic
2501       * @param integer $args[2]['post_id'] The unique id of the post
2502       * @param string $args[2]['text'] The text of the topic
2503       *
2504       * XML-RPC request to edit the text of the post with an id of 452
2505       * <methodCall>
2506       *     <methodName>bb.editPost</methodName>
2507       *     <params>
2508       *         <param><value><string>joeblow</string></value></param>
2509       *         <param><value><string>123password</string></value></param>
2510       *         <param><value><struct>
2511       *             <member>
2512       *                 <name>post_id</name>
2513       *                 <value><int>452</int></value>
2514       *             </member>
2515       *             <member>
2516       *                 <name>text</name>
2517       *                 <value><string>For now I will withhold my opinion.</string></value>
2518       *             </member>
2519       *         </struct></value></param>
2520       *     </params>
2521       * </methodCall>
2522       */
2523  	function bb_editPost( $args )
2524      {
2525          do_action( 'bb_xmlrpc_call', 'bb.editPost' );
2526  
2527          // Escape args
2528          $this->escape( $args );
2529  
2530          // Get the login credentials
2531          $username = $args[0];
2532          $password = (string) $args[1];
2533  
2534          // Check the user is valid
2535          $user = $this->authenticate( $username, $password, 'edit_posts', __( 'You do not have permission to edit posts.' ) );
2536  
2537          do_action( 'bb_xmlrpc_call_authenticated', 'bb.editPost' );
2538  
2539          // If an error was raised by authentication or by an action then return it
2540          if ( $this->error ) {
2541              return $this->error;
2542          }
2543  
2544          // Make sure there is something for us to do
2545          if ( !$args[2] || !is_array( $args[2] ) || !count( $args[2] ) ) {
2546              $this->error = new IXR_Error( 400, __( 'The post data is invalid.' ) );
2547              return $this->error;
2548          }
2549  
2550          $structure = (array) $args[2];
2551  
2552          // Can be numeric id or slug
2553          $post_id = isset( $structure['post_id'] ) ? (int) $structure['post_id'] : false;
2554  
2555          // Check for bad data
2556          if ( !$post_id ) {
2557              $this->error = new IXR_Error( 400, __( 'The post id is invalid.' ) );
2558              return $this->error;
2559          }
2560  
2561          // Check the requested topic exists
2562          if ( !$post = bb_get_post( $post_id ) ) {
2563