[ Index ]

PHP Cross Reference of bbPress

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              $this->error = new IXR_Error( 400, __( 'No post found.' ) );
2564              return $this->error;
2565          }
2566  
2567          // Re-assign the post id
2568          $post_id = (int) $post->post_id;
2569  
2570          // Make sure they are allowed to edit this post
2571          if ( !bb_current_user_can( 'edit_post', $post_id ) ) {
2572              $this->error = new IXR_Error( 403, __( 'You do not have permission to edit this post.' ) );
2573              return $this->error;
2574          }
2575  
2576          // The post requires text
2577          if ( !isset( $structure['text'] ) || !$structure['text'] ) {
2578              $this->error = new IXR_Error( 400, __( 'The post text is invalid.' ) );
2579              return $this->error;
2580          }
2581  
2582          // Inject structure into an array suitable for bb_insert_post()
2583          $bb_insert_post_args = array(
2584              'post_id' => $post_id,
2585              'post_text' => (string) $structure['text']
2586          );
2587  
2588          // Create the post
2589          if ( !$post_id = bb_insert_post( $bb_insert_post_args ) ) {
2590              $this->error = new IXR_Error( 500, __( 'The post could not be edited.' ) );
2591              return $this->error;
2592          }
2593  
2594          // Only include "safe" data in the array
2595          $post = $this->prepare_forum( bb_get_post( $post_id ) );
2596  
2597          do_action( 'bb_xmlrpc_call_return', 'bb.editPost' );
2598  
2599          return $post;
2600      }
2601  
2602      /**
2603       * Deletes an existing post
2604       *
2605       * @since 1.0
2606       * @return integer|object 1 when successfully deleted, 0 when already deleted or an IXR_Error object on failure
2607       * @param array $args Arguments passed by the XML-RPC call
2608       * @param string $args[0] The username for authentication
2609       * @param string $args[1] The password for authentication
2610       * @param array $args[2] The unique id of the post
2611       * @param array $args[3] 1 deletes the post, 0 undeletes the post (optional)
2612       *
2613       * XML-RPC request to delete the post with an id of 4301
2614       * <methodCall>
2615       *     <methodName>bb.editPost</methodName>
2616       *     <params>
2617       *         <param><value><string>joeblow</string></value></param>
2618       *         <param><value><string>123password</string></value></param>
2619       *         <param><value><int>4301</int></value></param>
2620       *     </params>
2621       * </methodCall>
2622       */
2623  	function bb_deletePost( $args )
2624      {
2625          do_action( 'bb_xmlrpc_call', 'bb.deletePost' );
2626  
2627          // Escape args
2628          $this->escape( $args );
2629  
2630          // Get the login credentials
2631          $username = $args[0];
2632          $password = (string) $args[1];
2633  
2634          // Check the user is valid
2635          $user = $this->authenticate( $username, $password, 'delete_posts', __( 'You do not have permission to delete posts.' ) );
2636  
2637          do_action( 'bb_xmlrpc_call_authenticated', 'bb.deletePost' );
2638  
2639          // If an error was raised by authentication or by an action then return it
2640          if ( $this->error ) {
2641              return $this->error;
2642          }
2643  
2644          // Can be numeric id or slug
2645          $post_id = isset( $args[2] ) ? (int) $args[2] : false;
2646  
2647          // Check for bad data
2648          if ( !$post_id ) {
2649              $this->error = new IXR_Error( 400, __( 'The post id is invalid.' ) );
2650              return $this->error;
2651          }
2652  
2653          // Check the requested topic exists
2654          if ( !$post = bb_get_post( $post_id ) ) {
2655              $this->error = new IXR_Error( 400, __( 'No post found.' ) );
2656              return $this->error;
2657          }
2658  
2659          // Re-assign the post id
2660          $post_id = (int) $post->post_id;
2661  
2662          // Make sure they are allowed to delete this post
2663          if ( !bb_current_user_can( 'delete_post', $post_id ) ) {
2664              $this->error = new IXR_Error( 403, __( 'You do not have permission to delete this post.' ) );
2665              return $this->error;
2666          }
2667  
2668          $status = isset( $args[3] ) ? (int) $args[3] : 1;
2669  
2670          if ( $status === (int) $post->post_status ) {
2671              return 0;
2672          }
2673  
2674          // Delete the post
2675          if ( !$post_id = bb_delete_post( $post_id, $status ) ) {
2676              $this->error = new IXR_Error( 500, __( 'The post could not be edited.' ) );
2677              return $this->error;
2678          }
2679  
2680          $result = 1;
2681  
2682          do_action( 'bb_xmlrpc_call_return', 'bb.deletePost' );
2683  
2684          return $result;
2685      }
2686  
2687  
2688  
2689      /**
2690       * bbPress publishing API - Topic Tag XML-RPC methods
2691       */
2692  
2693      /**
2694       * Returns the hot tags in order of hotness in a given forum or all hot tags
2695       *
2696       * @since 1.0
2697       * @return integer|object The tag data when successfully executed or an IXR_Error object on failure
2698       * @param array $args Arguments passed by the XML-RPC call
2699       * @param string $args[0] The username for authentication
2700       * @param string $args[1] The password for authentication
2701       * @param integer $args[2] The number of tags to return (optional)
2702       * @param integer|string $args[3] The forum id or slug (optional)
2703       *
2704       * XML-RPC request to get the 20 hottest tags in the forum with slug "hawtness"
2705       * <methodCall>
2706       *     <methodName>bb.getTopicTags</methodName>
2707       *     <params>
2708       *         <param><value><string>joeblow</string></value></param>
2709       *         <param><value><string>123password</string></value></param>
2710       *         <param><value><int>20</int></value></param>
2711       *         <param><value><string>hawtness</string></value></param>
2712       *     </params>
2713       * </methodCall>
2714       */
2715  	function bb_getHotTopicTags( $args )
2716      {
2717          do_action( 'bb_xmlrpc_call', 'bb.getHotTopicTags' );
2718  
2719          // Escape args
2720          $this->escape( $args );
2721  
2722          // Get the login credentials
2723          $username = $args[0];
2724          $password = (string) $args[1];
2725  
2726          // Check the user is valid
2727          if ( $this->auth_readonly ) {
2728              $user = $this->authenticate( $username, $password );
2729          }
2730  
2731          do_action( 'bb_xmlrpc_call_authenticated', 'bb.getHotTopicTags' );
2732  
2733          // If an error was raised by authentication or by an action then return it
2734          if ( $this->error ) {
2735              return $this->error;
2736          }
2737  
2738          // Must be a number
2739          $per_page = isset( $args[2] ) ? (integer) $args[2] : false;
2740  
2741          // Can be numeric id or slug
2742          $forum_id = isset( $args[3] ) ? $args[3] : false;
2743  
2744          if ( $forum_id ) {
2745              // Check for bad data
2746              if ( !is_string( $forum_id ) && !is_integer( $forum_id ) ) {
2747                  $this->error = new IXR_Error( 400, __( 'The forum id is invalid.' ) );
2748                  return $this->error;
2749              }
2750  
2751              // Check the requested forum exists
2752              if ( !$forum = bb_get_forum( $forum_id ) ) {
2753                  $this->error = new IXR_Error( 404, __( 'No forum found.' ) );
2754                  return $this->error;
2755              }
2756  
2757              global $bbdb;
2758              $topic_ids = $bbdb->get_col( $bbdb->prepare( "SELECT topic_id FROM `" . $bbdb->topics . "` WHERE `topic_status` = 0 AND `topic_open` = 1 AND `tag_count` > 0 AND `forum_id` = %s;", $forum_id ) );
2759  
2760              if ( !count( $topic_ids ) ) {
2761                  $this->error = new IXR_Error( 400, __( 'No topics found.' ) );
2762                  return $this->error;
2763              }
2764  
2765              global $wp_taxonomy_object;
2766              $tags = $wp_taxonomy_object->get_object_terms( $topic_ids, 'bb_topic_tag', array( 'fields' => 'all_with_object_id', 'orderby' => 'count', 'order' => 'DESC' ) );
2767  
2768              if ( !$tags || is_wp_error( $tags ) ) {
2769                  $this->error = new IXR_Error( 500, __( 'Could not retrieve hot topic tags.' ) );
2770                  return $this->error;
2771              }
2772              if ( !count( $tags ) ) {
2773                  $this->error = new IXR_Error( 500, __( 'No hot topic tags found.' ) );
2774                  return $this->error;
2775              }
2776              global $bb_log;
2777              $bb_log->debug($tags);
2778  
2779              for ( $i = 0; isset( $tags[$i] ); $i++ ) {
2780                  _bb_make_tag_compat( $tags[$i] );
2781              }
2782              $bb_log->debug($tags);
2783  
2784              // Only include "safe" data in the array
2785              $_tags = array();
2786              foreach ( $tags as $tag ) {
2787                  $_tag = $this->prepare_topic_tag( $tag );
2788                  if ( !in_array( $_tag, $_tags ) ) {
2789                      $_tags[] = $_tag;
2790                  }
2791              }
2792  
2793              if ( $per_page ) {
2794                  $_tags = array_slice( $_tags, 0, $per_page );
2795              }
2796          } else {
2797              if ( !$tags = bb_get_top_tags( array( 'get' => 'all', 'number' => $per_page ) ) ) {
2798                  $this->error = new IXR_Error( 500, __( 'No hot topic tags found.' ) );
2799                  return $this->error;
2800              }
2801  
2802              // Only include "safe" data in the array
2803              $_tags = array();
2804              foreach ( $tags as $tag ) {
2805                  $_tags[] = $this->prepare_topic_tag( $tag );
2806              }
2807          }
2808  
2809          do_action( 'bb_xmlrpc_call', 'bb.getHotTopicTags' );
2810  
2811          return $_tags;
2812      }
2813  
2814      /**
2815       * Returns a numerical count of tags in a given topic or all tags
2816       *
2817       * @since 1.0
2818       * @return integer|object The number of topics when successfully executed or an IXR_Error object on failure
2819       * @param array $args Arguments passed by the XML-RPC call
2820       * @param string $args[0] The username for authentication
2821       * @param string $args[1] The password for authentication
2822       * @param integer|string $args[2] The topic id or slug (optional)
2823       *
2824       * XML-RPC request to get a count of all tags in the topic with slug "woot-frist-topic"
2825       * <methodCall>
2826       *     <methodName>bb.getTopicTagCount</methodName>
2827       *     <params>
2828       *         <param><value><string>joeblow</string></value></param>
2829       *         <param><value><string>123password</string></value></param>
2830       *         <param><value><string>woot-frist-topic</string></value></param>
2831       *     </params>
2832       * </methodCall>
2833       */
2834  	function bb_getTopicTagCount( $args )
2835      {
2836          do_action( 'bb_xmlrpc_call', 'bb.getTopicTagCount' );
2837  
2838          // Escape args
2839          $this->escape( $args );
2840  
2841          // Get the login credentials
2842          $username = $args[0];
2843          $password = (string) $args[1];
2844  
2845          // Check the user is valid
2846          if ( $this->auth_readonly ) {
2847              $user = $this->authenticate( $username, $password );
2848          }
2849  
2850          do_action( 'bb_xmlrpc_call_authenticated', 'bb.getTopicTagCount' );
2851  
2852          // If an error was raised by authentication or by an action then return it
2853          if ( $this->error ) {
2854              return $this->error;
2855          }
2856  
2857          // Can be numeric id or slug
2858          $topic_id = isset( $args[2] ) ? $args[2] : false;
2859  
2860          // Check for bad data
2861          if ( $topic_id ) {
2862              if ( !is_string( $topic_id ) && !is_integer( $topic_id ) ) {
2863                  $this->error = new IXR_Error( 400, __( 'The topic id is invalid.' ) );
2864                  return $this->error;
2865              }
2866  
2867              // Check the requested topic exists
2868              if ( !$topic = get_topic( $topic_id ) ) {
2869                  $this->error = new IXR_Error( 400, __( 'No topic found.' ) );
2870                  return $this->error;
2871              }
2872  
2873              // The topic id may have been a slug, so make sure it's an integer here
2874              $topic_id = (int) $topic->topic_id;
2875  
2876              // Now get the tags
2877              if ( !$tags = bb_get_topic_tags( $topic_id ) ) {
2878                  $tags = array();
2879              }
2880  
2881              // Count the tags
2882              $count = count( $tags );
2883          } else {
2884              global $wp_taxonomy_object;
2885              $count = $wp_taxonomy_object->count_terms( 'bb_topic_tag' );
2886              if ( is_wp_error( $count ) ) {
2887                  $this->error = new IXR_Error( 500, __( 'Could not get a count of all topic tags.' ) );
2888                  return $this->error;
2889              }
2890          }
2891  
2892          do_action( 'bb_xmlrpc_call_return', 'bb.getTopicTagCount' );
2893  
2894          // Return the count of tags
2895          return $count;
2896      }
2897  
2898      /**
2899       * Returns the tags in a given topic or all tags
2900       *
2901       * @since 1.0
2902       * @return integer|object The tag data when successfully executed or an IXR_Error object on failure
2903       * @param array $args Arguments passed by the XML-RPC call
2904       * @param string $args[0] The username for authentication
2905       * @param string $args[1] The password for authentication
2906       * @param integer|string $args[2] The topic id or slug (optional)
2907       *
2908       * XML-RPC request to get all tags in the topic with slug "woot-frist-topic"
2909       * <methodCall>
2910       *     <methodName>bb.getTopicTags</methodName>
2911       *     <params>
2912       *         <param><value><string>joeblow</string></value></param>
2913       *         <param><value><string>123password</string></value></param>
2914       *         <param><value><string>woot-frist-topic</string></value></param>
2915       *     </params>
2916       * </methodCall>
2917       */
2918  	function bb_getTopicTags( $args )
2919      {
2920          do_action( 'bb_xmlrpc_call', 'bb.getTopicTags' );
2921  
2922          // Escape args
2923          $this->escape( $args );
2924  
2925          // Get the login credentials
2926          $username = $args[0];
2927          $password = (string) $args[1];
2928  
2929          // Check the user is valid
2930          if ( $this->auth_readonly ) {
2931              $user = $this->authenticate( $username, $password );
2932          }
2933  
2934          do_action( 'bb_xmlrpc_call_authenticated', 'bb.getTopicTags' );
2935  
2936          // If an error was raised by authentication or by an action then return it
2937          if ( $this->error ) {
2938              return $this->error;
2939          }
2940  
2941          // Can be numeric id or slug
2942          $topic_id = isset( $args[2] ) ? $args[2] : false;
2943  
2944          // Check for bad data
2945          if ( $topic_id ) {
2946              if ( !is_string( $topic_id ) && !is_integer( $topic_id ) ) {
2947                  $this->error = new IXR_Error( 400, __( 'The topic id is invalid.' ) );
2948                  return $this->error;
2949              }
2950  
2951              // Check the requested topic exists
2952              if ( !$topic = get_topic( $topic_id ) ) {
2953                  $this->error = new IXR_Error( 400, __( 'No topic found.' ) );
2954                  return $this->error;
2955              }
2956  
2957              // The topic id may have been a slug, so make sure it's an integer here
2958              $topic_id = (int) $topic->topic_id;
2959  
2960              // Now get the tags
2961              if ( !$tags = bb_get_topic_tags( $topic_id ) ) {
2962                  $this->error = new IXR_Error( 500, __( 'No topic tags found.' ) );
2963                  return $this->error;
2964              }
2965          } else {
2966              global $wp_taxonomy_object;
2967              $tags = $wp_taxonomy_object->get_terms( 'bb_topic_tag', array( 'get' => 'all' ) );
2968              if ( is_wp_error( $tags ) ) {
2969                  $this->error = new IXR_Error( 500, __( 'Could not retrieve all topic tags.' ) );
2970                  return $this->error;
2971              }
2972              for ( $i = 0; isset( $tags[$i] ); $i++ ) {
2973                  _bb_make_tag_compat( $tags[$i] );
2974              }
2975          }
2976  
2977          // Only include "safe" data in the array
2978          $_tags = array();
2979          foreach ( $tags as $tag ) {
2980              $_tags[] = $this->prepare_topic_tag( $tag );
2981          }
2982  
2983          do_action( 'bb_xmlrpc_call_return', 'bb.getTopicTags' );
2984  
2985          // Return the tags
2986          return $_tags;
2987      }
2988  
2989      /**
2990       * Returns the topics which are tagged with the given tag
2991       *
2992       * @since 1.0
2993       * @return integer|object The topic data when successfully executed or an IXR_Error object on failure
2994       * @param array $args Arguments passed by the XML-RPC call
2995       * @param string $args[0] The username for authentication
2996       * @param string $args[1] The password for authentication
2997       * @param string $args[2] The tag name or slug
2998       * @param integer $args[3] The number of topics to return (optional)
2999       * @param integer $args[4] The number of the page to return (optional)
3000       *
3001       * XML-RPC request to get the latest 10 topics tagged with the tag "apples"
3002       * <methodCall>
3003       *     <methodName>bb.getTopicTag</methodName>
3004       *     <params>
3005       *         <param><value><string>joeblow</string></value></param>
3006       *         <param><value><string>123password</string></value></param>
3007       *         <param><value><string>apples</string></value></param>
3008       *         <param><value><string>10</string></value></param>
3009       *     </params>
3010       * </methodCall>
3011       */
3012  	function bb_getTopicTag( $args )
3013      {
3014          do_action( 'bb_xmlrpc_call', 'bb.getTopicTag' );
3015  
3016          // Escape args
3017          $this->escape( $args );
3018  
3019          // Get the login credentials
3020          $username = $args[0];
3021          $password = (string) $args[1];
3022  
3023          // Check the user is valid
3024          if ( $this->auth_readonly ) {
3025              $user = $this->authenticate( $username, $password );
3026          }
3027  
3028          do_action( 'bb_xmlrpc_call_authenticated', 'bb.getTopicTag' );
3029  
3030          // If an error was raised by authentication or by an action then return it
3031          if ( $this->error ) {
3032              return $this->error;
3033          }
3034  
3035          // Can only be a string
3036          $tag_id = isset( $args[2] ) ? (string) $args[2] : false;
3037  
3038          // Check for bad data
3039          if ( !$tag_id ) {
3040              $this->error = new IXR_Error( 400, __( 'The tag id is invalid.' ) );
3041              return $this->error;
3042          }
3043  
3044          // Check the requested topic exists
3045          if ( !$tag = bb_get_tag( $tag_id ) ) {
3046              $this->error = new IXR_Error( 400, __( 'No tag found.' ) );
3047              return $this->error;
3048          }
3049  
3050          // Get the numeric tag id
3051          $tag_id = (int) $tag->tag_id;
3052  
3053          // Setup an array to store arguments to pass to get_tagged_topics() function
3054          $get_topics_args = array(
3055              'tag_id' => false,
3056              'number' => false,
3057              'page' => false
3058          );
3059  
3060          // Can only be an integer
3061          if ( isset( $args[3] ) && $number = (int) $args[3] ) {
3062              $get_topics_args['number'] = $number;
3063          }
3064  
3065          // Can only be an integer
3066          if ( isset( $args[4] ) && $page = (int) $args[4] ) {
3067              $get_topics_args['page'] = $page;
3068          }
3069  
3070          // Now get the topics
3071          if ( !$topics = get_tagged_topics( $tag_id ) ) {
3072              $this->error = new IXR_Error( 500, __( 'No topics found.' ) );
3073              return $this->error;
3074          }
3075  
3076          // Only include "safe" data in the array
3077          $_topics = array();
3078          foreach ( $topics as $topic ) {
3079              $_topics[] = $this->prepare_topic( $topic );
3080          }
3081  
3082          do_action( 'bb_xmlrpc_call_return', 'bb.getTopicTag' );
3083  
3084          // Return the topics
3085          return $_topics;
3086      }
3087  
3088      /**
3089       * Adds the specified tags to the specified topic
3090       *
3091       * @since 1.0
3092       * @return array|object The tags which were added when successfully executed or an IXR_Error object on failure
3093       * @param array $args Arguments passed by the XML-RPC call
3094       * @param string $args[0] The username for authentication
3095       * @param string $args[1] The password for authentication
3096       * @param string|integer $args[2] The topic id or slug
3097       * @param string|array $args[3] The tags to add to the topic
3098       *
3099       * XML-RPC request to add the tag "banana" to the topic with id 219
3100       * <methodCall>
3101       *     <methodName>bb.addTopicTags</methodName>
3102       *     <params>
3103       *         <param><value><string>joeblow</string></value></param>
3104       *         <param><value><string>123password</string></value></param>
3105       *         <param><value><int>219</int></value></param>
3106       *         <param><value><string>banana</string></value></param>
3107       *     </params>
3108       * </methodCall>
3109       *
3110       * XML-RPC request to add the tags "banana" and "man" to the topic with id 219
3111       * <methodCall>
3112       *     <methodName>bb.addTopicTags</methodName>
3113       *     <params>
3114       *         <param><value><string>joeblow</string></value></param>
3115       *         <param><value><string>123password</string></value></param>
3116       *         <param><value><int>219</int></value></param>
3117       *         <param><value><string>banana, man</string></value></param>
3118       *     </params>
3119       * </methodCall>
3120       *
3121       * XML-RPC request to add the tags "banana" and "man" to the topic with id 219 using an array
3122       * <methodCall>
3123       *     <methodName>bb.addTopicTags</methodName>
3124       *     <params>
3125       *         <param><value><string>joeblow</string></value></param>
3126       *         <param><value><string>123password</string></value></param>
3127       *         <param><value><int>219</int></value></param>
3128       *         <param><value><array>
3129       *             <data><value><string>banana</string></value></data>
3130       *             <data><value><string>man</string></value></data>
3131       *         </array></value></param>
3132       *     </params>
3133       * </methodCall>
3134       */
3135  	function bb_addTopicTags( $args )
3136      {
3137          do_action( 'bb_xmlrpc_call', 'bb.addTopicTags' );
3138  
3139          // Escape args
3140          $this->escape( $args );
3141  
3142          // Get the login credentials
3143          $username = $args[0];
3144          $password = (string) $args[1];
3145  
3146          // Check the user is valid
3147          $user = $this->authenticate( $username, $password, 'edit_tags', __( 'You do not have permission to edit tags.' ) );
3148  
3149          do_action( 'bb_xmlrpc_call_authenticated', 'bb.addTopicTags' );
3150  
3151          // If an error was raised by authentication or by an action then return it
3152          if ( $this->error ) {
3153              return $this->error;
3154          }
3155  
3156          // Can be numeric id or slug
3157          $topic_id = isset( $args[2] ) ? $args[2] : false;
3158  
3159          // Check for bad data
3160          if ( !$topic_id || ( !is_string( $topic_id ) && !is_integer( $topic_id ) ) ) {
3161              $this->error = new IXR_Error( 400, __( 'The topic id is invalid.' ) );
3162              return $this->error;
3163          }
3164  
3165          // Check the requested topic exists
3166          if ( !$topic = get_topic( $topic_id ) ) {
3167              $this->error = new IXR_Error( 400, __( 'No topic found.' ) );
3168              return $this->error;
3169          }
3170  
3171          // The topic id may have been a slug, so make sure it's an integer here
3172          $topic_id = (int) $topic->topic_id;
3173  
3174          // Make sure they are allowed to add tags to this topic
3175          if ( !bb_current_user_can( 'add_tag_to', $topic_id ) ) {
3176              $this->error = new IXR_Error( 403, __( 'You do not have permission to add tags to this topic.' ) );
3177              return $this->error;
3178          }
3179  
3180          $tags = isset( $args[3] ) ? $args[3] : false;
3181  
3182          // Check for bad data
3183          if ( !$tags || ( !is_string( $tags ) && !is_array( $tags ) ) ) {
3184              $this->error = new IXR_Error( 400, __( 'The tag data is invalid.' ) );
3185              return $this->error;
3186          }
3187  
3188          // Add the tags
3189          if ( !$tag_ids = bb_add_topic_tags( $topic_id, $tags ) ) {
3190              $this->error = new IXR_Error( 500, __( 'The tags could not be added.' ) );
3191              return $this->error;
3192          }
3193  
3194          // Only include "safe" data in the array
3195          $_tags = array();
3196          foreach ( $tag_ids as $tag_id ) {
3197              $_tags[] = $this->prepare_topic_tag( bb_get_tag( $tag_id ) );
3198          }
3199  
3200          do_action( 'bb_xmlrpc_call_return', 'bb.addTopicTags' );
3201  
3202          // Return the tags which were added as an array
3203          return $_tags;
3204      }
3205  
3206      /**
3207       * Removes the specified tags from the specified topic
3208       *
3209       * @since 1.0
3210       * @return integer|object 1 when successfully executed or an IXR_Error object on failure
3211       * @param array $args Arguments passed by the XML-RPC call
3212       * @param string $args[0] The username for authentication
3213       * @param string $args[1] The password for authentication
3214       * @param string|integer $args[2] The topic id or slug
3215       * @param string|array $args[3] The tags to remove from the topic
3216       *
3217       * XML-RPC request to remove the tag "banana" to the topic with id 219
3218       * <methodCall>
3219       *     <methodName>bb.removeTopicTags</methodName>
3220       *     <params>
3221       *         <param><value><string>joeblow</string></value></param>
3222       *         <param><value><string>123password</string></value></param>
3223       *         <param><value><int>219</int></value></param>
3224       *         <param><value><string>banana</string></value></param>
3225       *     </params>
3226       * </methodCall>
3227       *
3228       * XML-RPC request to remove the tags "banana" and "man" to the topic with id 219
3229       * <methodCall>
3230       *     <methodName>bb.removeTopicTags</methodName>
3231       *     <params>
3232       *         <param><value><string>joeblow</string></value></param>
3233       *         <param><value><string>123password</string></value></param>
3234       *         <param><value><int>219</int></value></param>
3235       *         <param><value><string>banana, man</string></value></param>
3236       *     </params>
3237       * </methodCall>
3238       *
3239       * XML-RPC request to remove the tags "banana" and "man" to the topic with id 219 using an array
3240       * <methodCall>
3241       *     <methodName>bb.removeTopicTags</methodName>
3242       *     <params>
3243       *         <param><value><string>joeblow</string></value></param>
3244       *         <param><value><string>123password</string></value></param>
3245       *         <param><value><int>219</int></value></param>
3246       *         <param><value><array>
3247       *             <data><value><string>banana</string></value></data>
3248       *             <data><value><string>man</string></value></data>
3249       *         </array></value></param>
3250       *     </params>
3251       * </methodCall>
3252       */
3253  	function bb_removeTopicTags( $args )
3254      {
3255          do_action( 'bb_xmlrpc_call', 'bb.removeTopicTags' );
3256  
3257          // Escape args
3258          $this->escape( $args );
3259  
3260          // Get the login credentials
3261          $username = $args[0];
3262          $password = (string) $args[1];
3263  
3264          // Check the user is valid
3265          $user = $this->authenticate( $username, $password, 'edit_tags', __( 'You do not have permission to edit tags.' ) );
3266  
3267          do_action( 'bb_xmlrpc_call_authenticated', 'bb.removeTopicTags' );
3268  
3269          // If an error was raised by authentication or by an action then return it
3270          if ( $this->error ) {
3271              return $this->error;
3272          }
3273  
3274          // Can be numeric id or slug
3275          $topic_id = isset( $args[2] ) ? $args[2] : false;
3276  
3277          // Check for bad data
3278          if ( !$topic_id || ( !is_string( $topic_id ) && !is_integer( $topic_id ) ) ) {
3279              $this->error = new IXR_Error( 400, __( 'The topic id is invalid.' ) );
3280              return $this->error;
3281          }
3282  
3283          // Check the requested topic exists
3284          if ( !$topic = get_topic( $topic_id ) ) {
3285              $this->error = new IXR_Error( 400, __( 'No topic found.' ) );
3286              return $this->error;
3287          }
3288  
3289          // The topic id may have been a slug, so make sure it's an integer here
3290          $topic_id = (int) $topic->topic_id;
3291  
3292          // Make sure they are allowed to add tags to this topic
3293          if ( !bb_current_user_can( 'add_tag_to', $topic_id ) ) {
3294              $this->error = new IXR_Error( 403, __( 'You do not have permission to remove tags from this topic.' ) );
3295              return $this->error;
3296          }
3297  
3298          $tags = isset( $args[3] ) ? $args[3] : false;
3299  
3300          // Check for bad data
3301          if ( !$tags || ( !is_string( $tags ) && !is_array( $tags ) ) ) {
3302              $this->error = new IXR_Error( 400, __( 'The tag data is invalid.' ) );
3303              return $this->error;
3304          }
3305  
3306          // Add the tags
3307          if ( !bb_remove_topic_tags( $topic_id, $tags ) ) {
3308              $this->error = new IXR_Error( 500, __( 'The tags could not be removed.' ) );
3309              return $this->error;
3310          }
3311  
3312          $result = 1;
3313  
3314          do_action( 'bb_xmlrpc_call_return', 'bb.removeTopicTags' );
3315  
3316          // Return the result
3317          return $result;
3318      }
3319  
3320      /**
3321       * Renames the specified tag to a new tag name
3322       *
3323       * @since 1.0
3324       * @return array|object The tag data when successfully renamed or an IXR_Error object on failure
3325       * @param array $args Arguments passed by the XML-RPC call
3326       * @param string $args[0] The username for authentication
3327       * @param string $args[1] The password for authentication
3328       * @param string $args[2] The tag name or slug
3329       * @param string $args[3] The new tag name (slug is auto-generated)
3330       *
3331       * XML-RPC request to rename the tag "banana" to "bananas"
3332       * <methodCall>
3333       *     <methodName>bb.renameTopicTag</methodName>
3334       *     <params>
3335       *         <param><value><string>joeblow</string></value></param>
3336       *         <param><value><string>123password</string></value></param>
3337       *         <param><value><string>banana</string></value></param>
3338       *         <param><value><string>bananas</string></value></param>
3339       *     </params>
3340       * </methodCall>
3341       */
3342  	function bb_renameTopicTag( $args )
3343      {
3344          do_action( 'bb_xmlrpc_call', 'bb.renameTopicTag' );
3345  
3346          // Escape args
3347          $this->escape( $args );
3348  
3349          // Get the login credentials
3350          $username = $args[0];
3351          $password = (string) $args[1];
3352  
3353          // Check the user is valid
3354          $user = $this->authenticate( $username, $password, 'manage_tags', __( 'You do not have permission to manage tags.' ) );
3355  
3356          do_action( 'bb_xmlrpc_call_authenticated', 'bb.renameTopicTag' );
3357  
3358          // If an error was raised by authentication or by an action then return it
3359          if ( $this->error ) {
3360              return $this->error;
3361          }
3362  
3363          // Can only be a string
3364          $tag_id = isset( $args[2] ) ? (string) $args[2] : false;
3365  
3366          // Check for bad data
3367          if ( !$tag_id ) {
3368              $this->error = new IXR_Error( 400, __( 'The tag id is invalid.' ) );
3369              return $this->error;
3370          }
3371  
3372          // Check the requested tag exists
3373          if ( !$tag = bb_get_tag( $tag_id ) ) {
3374              $this->error = new IXR_Error( 400, __( 'No tag found.' ) );
3375              return $this->error;
3376          }
3377  
3378          // Get the numeric tag id
3379          $tag_id = (int) $tag->tag_id;
3380  
3381          // Can only be a string
3382          $tag_name = isset( $args[3] ) ? (string) $args[3] : false;
3383  
3384          // Check for bad data
3385          if ( !$tag_name || $tag_name == $tag->tag_name ) {
3386              $this->error = new IXR_Error( 400, __( 'The tag name is invalid.' ) );
3387              return $this->error;
3388          }
3389  
3390          // Rename the tag
3391          if ( !$new_tag = bb_rename_tag( $tag_id, $tag_name ) ) {
3392              $this->error = new IXR_Error( 500, __( 'The tag could not be renamed.' ) );
3393              return $this->error;
3394          }
3395  
3396          // Only include "safe" data in the array
3397          $new_tag = $this->prepare_topic_tag( $new_tag );
3398  
3399          do_action( 'bb_xmlrpc_call_return', 'bb.renameTopicTag' );
3400  
3401          // Return the tag
3402          return $new_tag;
3403      }
3404  
3405      /**
3406       * Merges the specified tags
3407       *
3408       * @since 1.0
3409       * @return array|object The tag data when successfully merged or an IXR_Error object on failure
3410       * @param array $args Arguments passed by the XML-RPC call
3411       * @param string $args[0] The username for authentication
3412       * @param string $args[1] The password for authentication
3413       * @param string $args[2] The old tag name or slug to be destroyed
3414       * @param string $args[3] The new tag name or slug where the old tag will be merged to
3415       *
3416       * XML-RPC request to merge the tag "banana" into the tag "apple"
3417       * <methodCall>
3418       *     <methodName>bb.mergeTopicTags</methodName>
3419       *     <params>
3420       *         <param><value><string>joeblow</string></value></param>
3421       *         <param><value><string>123password</string></value></param>
3422       *         <param><value><string>banana</string></value></param>
3423       *         <param><value><string>apple</string></value></param>
3424       *     </params>
3425       * </methodCall>
3426       */
3427  	function bb_mergeTopicTags( $args )
3428      {
3429          do_action( 'bb_xmlrpc_call', 'bb.mergeTopicTags' );
3430  
3431          // Escape args
3432          $this->escape( $args );
3433  
3434          // Get the login credentials
3435          $username = $args[0];
3436          $password = (string) $args[1];
3437  
3438          // Check the user is valid
3439          $user = $this->authenticate( $username, $password, 'manage_tags', __( 'You do not have permission to manage tags.' ) );
3440  
3441          do_action( 'bb_xmlrpc_call_authenticated', 'bb.mergeTopicTags' );
3442  
3443          // If an error was raised by authentication or by an action then return it
3444          if ( $this->error ) {
3445              return $this->error;
3446          }
3447  
3448          // Can only be strings
3449          $old_tag_id = isset( $args[2] ) ? (string) $args[2] : false;
3450          $new_tag_id = isset( $args[3] ) ? (string) $args[3] : false;
3451  
3452          // Check for bad data
3453          if ( !$old_tag_id ) {
3454              $this->error = new IXR_Error( 400, __( 'The old tag id is invalid.' ) );
3455              return $this->error;
3456          }
3457          if ( !$new_tag_id ) {
3458              $this->error = new IXR_Error( 400, __( 'The new tag id is invalid.' ) );
3459              return $this->error;
3460          }
3461  
3462          // Check the requested tags exist
3463          if ( !$old_tag = bb_get_tag( $old_tag_id ) ) {
3464              $this->error = new IXR_Error( 400, __( 'No old tag found.' ) );
3465              return $this->error;
3466          }
3467          if ( !$new_tag = bb_get_tag( $new_tag_id ) ) {
3468              $this->error = new IXR_Error( 400, __( 'No new tag found.' ) );
3469              return $this->error;
3470          }
3471  
3472          // Get the numeric tag ids
3473          $old_tag_id = (int) $old_tag->tag_id;
3474          $new_tag_id = (int) $new_tag->tag_id;
3475  
3476          // Rename the tag
3477          if ( !$result = bb_rename_tag( $old_tag_id, $new_tag_id ) ) {
3478              $this->error = new IXR_Error( 500, __( 'The tags could not be merged.' ) );
3479              return $this->error;
3480          }
3481  
3482          // Get the merged tag
3483          $new_tag = bb_get_tag( $new_tag_id );
3484  
3485          // Only include "safe" data in the array
3486          $new_tag = $this->prepare_topic_tag( $new_tag );
3487  
3488          do_action( 'bb_xmlrpc_call_return', 'bb.mergeTopicTags' );
3489  
3490          // Return the tag
3491          return $new_tag;
3492      }
3493  
3494      /**
3495       * Destroys the specified tag
3496       *
3497       * @since 1.0
3498       * @return integer|object 1 when successfully deleted or an IXR_Error object on failure
3499       * @param array $args Arguments passed by the XML-RPC call
3500       * @param string $args[0] The username for authentication
3501       * @param string $args[1] The password for authentication
3502       * @param string $args[2] The tag name or slug to be destroyed
3503       *
3504       * XML-RPC request to destroy the tag "banana"
3505       * <methodCall>
3506       *     <methodName>bb.destroyTopicTag</methodName>
3507       *     <params>
3508       *         <param><value><string>joeblow</string></value></param>
3509       *         <param><value><string>123password</string></value></param>
3510       *         <param><value><string>banana</string></value></param>
3511       *     </params>
3512       * </methodCall>
3513       */
3514  	function bb_destroyTopicTag( $args )
3515      {
3516          do_action( 'bb_xmlrpc_call', 'bb.destroyTopicTag' );
3517  
3518          // Escape args
3519          $this->escape( $args );
3520  
3521          // Get the login credentials
3522          $username = $args[0];
3523          $password = (string) $args[1];
3524  
3525          // Check the user is valid
3526          $user = $this->authenticate( $username, $password, 'manage_tags', __( 'You do not have permission to manage tags.' ) );
3527  
3528          do_action( 'bb_xmlrpc_call_authenticated', 'bb.destroyTopicTag' );
3529  
3530          // If an error was raised by authentication or by an action then return it
3531          if ( $this->error ) {
3532              return $this->error;
3533          }
3534  
3535          // Can only be a string
3536          $tag_id = isset( $args[2] ) ? (string) $args[2] : false;
3537  
3538          // Check for bad data
3539          if ( !$tag_id ) {
3540              $this->error = new IXR_Error( 400, __( 'The tag id is invalid.' ) );
3541              return $this->error;
3542          }
3543  
3544          // Check the requested tag exists
3545          if ( !$tag = bb_get_tag( $tag_id ) ) {
3546              $this->error = new IXR_Error( 400, __( 'No tag found.' ) );
3547              return $this->error;
3548          }
3549  
3550          // Get the numeric tag id
3551          $tag_id = (int) $tag->tag_id;
3552  
3553          // Destroy the tag
3554          if ( !$result = bb_destroy_tag( $tag_id ) ) {
3555              $this->error = new IXR_Error( 500, __( 'The tag could not be destroyed.' ) );
3556              return $this->error;
3557          }
3558  
3559          $result = 1;
3560  
3561          do_action( 'bb_xmlrpc_call_return', 'bb.destroyTopicTag' );
3562  
3563          // Return the tag
3564          return $result;
3565      }
3566  
3567  
3568  
3569      /**
3570       * bbPress publishing API - Options XML-RPC methods
3571       */
3572  
3573      /**
3574       * Initialises site options which can be manipulated using XML-RPC
3575       *
3576       * @since 1.0
3577       * @return void
3578       */
3579  	function initialise_site_option_info()
3580      {
3581          $this->site_options = array(
3582              // Read only options
3583              'software_name'        => array(
3584                  'desc'            => __( 'Software Name' ),
3585                  'readonly'        => true,
3586                  'value'            => 'bbPress'
3587              ),
3588              'software_version'    => array(
3589                  'desc'            => __( 'Software Version' ),
3590                  'readonly'        => true,
3591                  'option'        => 'version'
3592              ),
3593              'site_url'            => array(
3594                  'desc'            => __( 'Site URL' ),
3595                  'readonly'        => true,
3596                  'option'        => 'uri'
3597              ),
3598  
3599              // Updatable options
3600              'site_name'        => array(
3601                  'desc'            => __( 'Site Name' ),
3602                  'readonly'        => false,
3603                  'option'        => 'name'
3604              ),
3605              'site_description'    => array(
3606                  'desc'            => __( 'Site Description' ),
3607                  'readonly'        => false,
3608                  'option'        => 'description'
3609              ),
3610              'time_zone'            => array(
3611                  'desc'            => __( 'Time Zone' ),
3612                  'readonly'        => false,
3613                  'option'        => 'gmt_offset'
3614              ),
3615              'datetime_format'    => array(
3616                  'desc'            => __( 'Date/Time Format' ),
3617                  'readonly'        => false,
3618                  'option'        => 'datetime_format'
3619              ),
3620              'date_format'        => array(
3621                  'desc'            => __( 'Date Format' ),
3622                  'readonly'        => false,
3623                  'option'        => 'date_format'
3624              )
3625          );
3626  
3627          $this->site_options = apply_filters( 'xmlrpc_site_options', $this->site_options );
3628      }
3629  
3630      /**
3631       * Compiles site options into an array suitable to be passed back through the XML-RPC server
3632       *
3633       * @since 1.0
3634       * @return array The site options in an array
3635       * @param array $options An array of options to fetch and return
3636       */
3637  	function _getOptions( $options )
3638      {
3639          $data = array();
3640          foreach ( $options as $option ) {
3641              if ( array_key_exists( $option, $this->site_options ) ) {
3642                  $data[$option] = $this->site_options[$option];
3643  
3644                  // Is the value static or dynamic?
3645                  if ( isset( $data[$option]['option'] ) ) {
3646                      $data[$option]['value'] = bb_get_option( $data[$option]['option'] );
3647                      unset( $data[$option]['option'] );
3648                  }
3649              }
3650          }
3651  
3652          return $data;
3653      }
3654  
3655      /**
3656       * Gets the specified site options
3657       *
3658       * @since 1.0
3659       * @return array|object An array containing the specified options when successfully executed or an IXR_Error object on failure
3660       * @param array $args Arguments passed by the XML-RPC call
3661       * @param string $args[0] The username for authentication
3662       * @param string $args[1] The password for authentication
3663       * @param array $args[2] The options to be retrieved, when omitted the method returns all options (optional)
3664       *
3665       * XML-RPC request to get all site options
3666       * <methodCall>
3667       *     <methodName>bb.getOptions</methodName>
3668       *     <params>
3669       *         <param><value><string>joeblow</string></value></param>
3670       *         <param><value><string>123password</string></value></param>
3671       *     </params>
3672       * </methodCall>
3673       *
3674       * XML-RPC request to get the site name and site description
3675       * <methodCall>
3676       *     <methodName>bb.getOptions</methodName>
3677       *     <params>
3678       *         <param><value><string>joeblow</string></value></param>
3679       *         <param><value><string>123password</string></value></param>
3680       *         <param><value><array>
3681       *             <data><value><string>site_name</string></value></data>
3682       *             <data><value><string>site_description</string></value></data>
3683       *         </array></value></param>
3684       *     </params>
3685       * </methodCall>
3686       */
3687  	function bb_getOptions( $args )
3688      {
3689          do_action( 'bb_xmlrpc_call', 'bb.getOptions' );
3690  
3691          // Escape args
3692          $this->escape( $args );
3693  
3694          // Get the login credentials
3695          $username = $args[0];
3696          $password = (string) $args[1];
3697  
3698          // Check the user is valid
3699          if ( $this->auth_readonly ) {
3700              $user = $this->authenticate( $username, $password );
3701          }
3702  
3703          do_action( 'bb_xmlrpc_call_authenticated', 'bb.getOptions' );
3704  
3705          // If an error was raised by authentication or by an action then return it
3706          if ( $this->error ) {
3707              return $this->error;
3708          }
3709  
3710          // If there are parameters supplied then make sure they are in an array
3711          $options = isset( $args[2] ) ? (array) $args[2] : false;
3712  
3713          // If no specific options where asked for, return all of them
3714          if ( !$options || !count( $options ) ) {
3715              $options = array_keys( $this->site_options );
3716          }
3717  
3718          do_action( 'bb_xmlrpc_call_return', 'bb.getOptions' );
3719  
3720          return $this->_getOptions( $options );
3721      }
3722  
3723      /**
3724       * Sets the specified site options to the specified values
3725       *
3726       * @since 1.0
3727       * @return array|object An array containing the specified options when successfully executed or an IXR_Error object on failure
3728       * @param array $args Arguments passed by the XML-RPC call
3729       * @param string $args[0] The username for authentication
3730       * @param string $args[1] The password for authentication
3731       * @param array $args[2] The options to be updated along with the new value of the option
3732       *
3733       * XML-RPC request to set the site name and site description
3734       * <methodCall>
3735       *     <methodName>bb.setOptions</methodName>
3736       *     <params>
3737       *         <param><value><string>joeblow</string></value></param>
3738       *         <param><value><string>123password</string></value></param>
3739       *         <param><value><struct>
3740       *             <member>
3741       *                 <name>site_name</name>
3742       *                 <value><string>Awesome forums</string></value>
3743       *             </member>
3744       *             <member>
3745       *                 <name>site_description</name>
3746       *                 <value><string>My totally awesome forums will kick your butt</string></value>
3747       *             </member>
3748       *         </struct></value></param>
3749       *     </params>
3750       * </methodCall>
3751       */
3752  	function bb_setOptions( $args )
3753      {
3754          do_action( 'bb_xmlrpc_call', 'bb.setOptions' );
3755  
3756          // Escape args
3757          $this->escape( $args );
3758  
3759          // Get the login credentials
3760          $username = $args[0];
3761          $password = (string) $args[1];
3762  
3763          // Check the user is valid
3764          $user = $this->authenticate( $username, $password, 'manage_options', __( 'You are not allowed to manage options.' ) );
3765  
3766          do_action( 'bb_xmlrpc_call_authenticated', 'bb.setOptions' );
3767  
3768          // If an error was raised by authentication or by an action then return it
3769          if ( $this->error ) {
3770              return $this->error;
3771          }
3772  
3773          // Make sure there is something for us to do
3774          if ( !$args[2] || !is_array( $args[2] ) || !count( $args[2] ) ) {
3775              $this->error = new IXR_Error( 400, __( 'The options data is invalid.' ) );
3776              return $this->error;
3777          }
3778  
3779          $options = (array) $args[2];
3780  
3781          // Update the requested options
3782          foreach( $options as $o_name => $o_value ) {
3783              $option_names[] = $o_name;
3784  
3785              // If there is no value set skip it
3786              if ( empty( $o_value ) ) {
3787                  continue;
3788              }
3789  
3790              // If the option doesn't exist skip it
3791              if ( !array_key_exists( $o_name, $this->site_options ) ) {
3792                  continue;
3793              }
3794  
3795              // If the option is readonly skip it
3796              if ( $this->site_options[$o_name]['readonly'] == true ) {
3797                  continue;
3798              }
3799  
3800              // Everything is good, update the option
3801              bb_update_option( $this->site_options[$o_name]['option'], $o_value );
3802          }
3803  
3804          $_options = $this->_getOptions( $option_names );
3805  
3806          do_action( 'bb_xmlrpc_call_return', 'bb.setOptions' );
3807  
3808          // Now return the updated values
3809          return $_options;
3810      }
3811  
3812  
3813  
3814      /**
3815       * Pingback XML-RPC methods
3816       */
3817  
3818      /**
3819       * Processes pingback requests
3820       *
3821       * @since 1.0
3822       * @link http://www.hixie.ch/specs/pingback/pingback
3823       * @return string|object A message of success or an IXR_Error object on failure
3824       * @param array $args Arguments passed by the XML-RPC call
3825       * @param string $args[0] The full URI of the post where the pingback is being sent from
3826       * @param string $args[1] The full URI of the post where the pingback is being sent to
3827       *
3828       * XML-RPC request to register a pingback
3829       * <methodCall>
3830       *     <methodName>pingback.ping</methodName>
3831       *     <params>
3832       *         <param><value><string>http://example.org/2008/09/post-containing-a-link/</string></value></param>
3833       *         <param><value><string>http://example.com/2008/08/post-being-linked-to/</string></value></param>
3834       *     </params>
3835       * </methodCall>
3836       */
3837  	function pingback_ping( $args )
3838      {
3839          do_action( 'bb_xmlrpc_call', 'pingback.ping' );
3840  
3841          $this->escape( $args );
3842  
3843          // No particular need to sanitise
3844          $link_from = (string) $args[0];
3845          $link_to   = (string) $args[1];
3846  
3847          // Tidy up ampersands in the URLs
3848          $link_from = str_replace( '&amp;', '&', $link_from );
3849          $link_to   = str_replace( '&amp;', '&', $link_to );
3850          $link_to   = str_replace( '&', '&amp;', $link_to );
3851  
3852          // Check if the topic linked to is in our site - a little more strict than WordPress, doesn't pull out the www if added
3853          if ( !bb_match_domains( $link_to, bb_get_uri() ) ) {
3854              // These are not the droids you are looking for
3855              $this->error = new IXR_Error( 0, __( 'This is not the site you are trying to pingback.' ) );
3856              return $this->error;
3857          }
3858  
3859          // Get the topic
3860          if ( $topic_to = bb_get_topic_from_uri( $link_to ) ) {
3861              // Topics shouldn't ping themselves
3862              if ( $topic_from = bb_get_topic_from_uri( $link_from ) ) {
3863                  if ( $topic_from->topic_id === $topic_to->topic_id ) {
3864                      $this->error = new IXR_Error( 0, __( 'The source URL and the target URL cannot both point to the same resource.' ) );
3865                      return $this->error;
3866                  }
3867              }
3868          } else {
3869              $this->error = new IXR_Error ( 33, __( 'The specified target URL cannot be used as a target. It either doesn\'t exist, or it is not a pingback-enabled resource.' ) );
3870              return $this->error;
3871          }
3872  
3873          // Let's check that the remote site didn't already pingback this entry
3874          $query = new BB_Query( 'post', array( 'topic_id' => $topic_to->topic_id, 'append_meta' => true ), 'get_thread' );
3875          $posts_to = $query->results;
3876          unset( $query );
3877  
3878          // Make sure we have some posts in the topic, this error should never happen really
3879          if ( !$posts_to || !is_array( $posts_to ) || !count( $posts_to ) ) {
3880              $this->error = new IXR_Error( 0, __( 'The specified target topic does not contain any posts.' ) );
3881              return $this->error;
3882          }
3883  
3884          // Check if we already have a pingback from this URL
3885          foreach ( $posts_to as $post ) {
3886              if ( isset( $post->pingback_uri ) && trim( $post->pingback_uri ) === trim( $link_from ) ) {
3887                  $this->error = new IXR_Error( 48, __( 'The pingback has already been registered.' ) );
3888                  return $this->error;
3889              }
3890          }
3891          unset( $posts_to, $post );
3892  
3893          // Give time for the server sending the pingback to finish publishing it's post
3894          sleep(1);
3895  
3896          // Let's check the remote site for valid URL and content
3897          $link_from_source = wp_remote_fopen( $link_from );
3898          if ( !$link_from_source ) {
3899              $this->error = new IXR_Error( 16, __( 'The source URL does not exist.' ) );
3900              return $this->error;
3901          }
3902  
3903          // Allow plugins to filter here
3904          $link_from_source = apply_filters( 'bb_pre_remote_source', $link_from_source, $link_to );
3905  
3906          // Work around bug in strip_tags()
3907          $link_from_source = str_replace( '<!DOC', '<DOC', $link_from_source );
3908  
3909          // Normalize spaces
3910          $link_from_source = preg_replace( '/[\s\r\n\t]+/', ' ', $link_from_source );
3911  
3912          // Turn certain elements to double line returns
3913          $link_from_source = preg_replace( "/ <(h1|h2|h3|h4|h5|h6|p|th|td|li|dt|dd|pre|caption|input|textarea|button|body)[^>]*>/", "\n\n", $link_from_source );
3914  
3915          // Find the title of the page
3916          preg_match( '|<title>([^<]*?)</title>|is', $link_from_source, $link_from_title );
3917          $link_from_title = $link_from_title[1];
3918          if ( empty( $link_from_title ) ) {
3919              $this->error = new IXR_Error( 32, __( 'We cannot find a title on that page.' ) );
3920              return $this->error;
3921          }
3922  
3923          // Strip out all tags except anchors
3924          $link_from_source = strip_tags( $link_from_source, '<a>' ); // just keep the tag we need
3925  
3926          // Split the source into paragraphs
3927          $link_from_paragraphs = explode( "\n\n", $link_from_source );
3928  
3929          // Prepare the link to search for in preg_match() once here
3930          $preg_target = preg_quote( $link_to );
3931  
3932          // Loop through the paragraphs looking for the context for the url
3933          foreach ( $link_from_paragraphs as $link_from_paragraph ) {
3934              // The url exists
3935              if ( strpos( $link_from_paragraph, $link_to ) !== false ) {
3936                  // But is it in an anchor tag
3937                  preg_match(
3938                      "|<a[^>]+?" . $preg_target . "[^>]*>([^>]+?)</a>|",
3939                      $link_from_paragraph,
3940                      $context
3941                  );
3942                  // If the URL isn't in an anchor tag, keep looking
3943                  if ( empty( $context ) ) {
3944                      continue;
3945                  }
3946  
3947                  // We're going to use this fake tag to mark the context in a bit
3948                  // the marker is needed in case the link text appears more than once in the paragraph
3949                  $excerpt = preg_replace( '|\</?wpcontext\>|', '', $link_from_paragraph );
3950  
3951                  // Prevent really long link text
3952                  if ( strlen( $context[1] ) > 100 ) {
3953                      $context[1] = substr( $context[1], 0, 100 ) . '...';
3954                  }
3955  
3956                  // Set up the marker around the context
3957                  $marker = '<wpcontext>' . $context[1] . '</wpcontext>';
3958                  // Swap out the link for our marker
3959                  $excerpt = str_replace( $context[0], $marker, $excerpt );
3960                  // Strip all tags except for our context marker
3961                  $excerpt = trim( strip_tags( $excerpt, '<wpcontext>' ) );
3962                  // Make the marker safe for use in regexp
3963                  $preg_marker = preg_quote( $marker );
3964                  // Reduce the excerpt to only include 100 characters on either side of the link
3965                  $excerpt = preg_replace( "|.*?\s(.{0,100}" . $preg_marker . "{0,100})\s.*|s", '$1', $excerpt );
3966                  // Strip tags again, to remove the marker wrapper
3967                  $excerpt = strip_tags( $excerpt );
3968                  break;
3969              }
3970          }
3971  
3972           // Make sure the link to the target was found in the excerpt
3973          if ( empty( $context ) ) {
3974              $this->error = new IXR_Error( 17, __( 'The source URL does not contain a link to the target URL, and so cannot be used as a source.' ) );
3975              return $this->error;
3976          }
3977  
3978          // Add whacky prefix and suffix to the excerpt and sanitize
3979          $excerpt = '[...] ' . esc_html( $excerpt ) . ' [...]';
3980          $this->escape( $excerpt );
3981  
3982          // Build an array of post data to insert then insert a new post
3983          $postdata = array(
3984              'topic_id' => $topic_to->topic_id,
3985              'post_text' => $excerpt,
3986              'poster_id' => 0,
3987          );
3988          if ( !$post_ID = bb_insert_post( $postdata ) ) {
3989              $this->error = new IXR_Error( 0, __( 'The pingback could not be added.' ) );
3990              return $this->error;
3991          }
3992  
3993          // Add meta to let us know where the pingback came from
3994          $link_from = str_replace( '&', '&amp;', $link_from );
3995          $this->escape( $link_from );
3996          bb_update_postmeta( $post_ID, 'pingback_uri', $link_from );
3997  
3998          // Add the title to meta
3999          $this->escape( $link_from_title );
4000          bb_update_postmeta( $post_ID, 'pingback_title', $link_from_title );
4001  
4002          // Action for plugins and what not
4003          do_action( 'bb_pingback_post', $post_ID );
4004  
4005          // Return success message, complete with emoticon
4006          return sprintf( __( 'Pingback from %1$s to %2$s registered. Keep the web talking! :-)' ), $link_from, $link_to );
4007      }
4008  
4009  
4010  
4011      /**
4012       * Returns an array of URLs that pingbacked the given URL
4013       *
4014       * @since 1.0
4015       * @link http://www.aquarionics.com/misc/archives/blogite/0198.html
4016       * @return array The array of URLs that pingbacked the given topic
4017       * @param array $args Arguments passed by the XML-RPC call
4018       * @param string $args[0] The full URI of the post where the pingback is being sent from
4019       * @param string $args[1] The full URI of the post where the pingback is being sent to
4020       *
4021       * XML-RPC request to get all pingbacks on a topic
4022       * <methodCall>
4023       *     <methodName>pingback.ping</methodName>
4024       *     <params>
4025       *         <param><value><string>http://example.com/2008/08/post-tobe-queried/</string></value></param>
4026       *     </params>
4027       * </methodCall>
4028       */
4029  	function pingback_extensions_getPingbacks( $args )
4030      {
4031          do_action( 'bb_xmlrpc_call', 'pingback.extensions.getPingbacks' );
4032  
4033          $this->escape( $args );
4034  
4035          // Don't accept arrays of arguments
4036          if ( is_array( $args ) ) {
4037              $this->error = new IXR_Error( 404, __( 'The requested method only accepts one parameter.' ) );
4038              return $this->error;
4039          } else {
4040              $url = (string) $args;
4041          }
4042  
4043          // Tidy up ampersands in the URI
4044          $url = str_replace( '&amp;', '&', $url );
4045          $url = str_replace( '&', '&amp;', $url );
4046  
4047          // Check if the URI is in our site
4048          if ( !bb_match_domains( $url, bb_get_uri() ) ) {
4049              // These are not the droids you are looking for
4050              $this->error = new IXR_Error( 0, __( 'The specified target URL is not on this domain.' ) );
4051              return $this->error;
4052          }
4053  
4054          // Make sure the specified URI is in fact associated with a topic
4055          if ( !$topic = bb_get_topic_from_uri( $url ) ) {
4056              $this->error = new IXR_Error( 33, __( 'The specified target URL cannot be used as a target. It either doesn\'t exist, or it is not a pingback-enabled resource.' ) );
4057              return $this->error;
4058          }
4059  
4060          // Grab the posts from the topic
4061          $query = new BB_Query( 'post', array( 'topic_id' => $topic_to->topic_id, 'append_meta' => true ), 'get_thread' );
4062          $posts_to = $query->results;
4063          unset( $query );
4064  
4065          // Check for pingbacks in the post meta data
4066          $pingbacks = array();
4067          foreach ( $posts_to as $post ) {
4068              if ( isset( $post->pingback_uri ) ) {
4069                  $pingbacks[] = $post->pingback_uri;
4070              }
4071          }
4072          unset( $post );
4073  
4074          // This will return an empty array on failure
4075          return $pingbacks;
4076      }
4077  }
4078  
4079  
4080  
4081  /**
4082   * Initialises the XML-RPC server
4083   *
4084   * @since 1.0
4085   * @var object The instance of the XML-RPC server class
4086   */
4087  $bb_xmlrpc_server = new BB_XMLRPC_Server();


Generated: Wed May 22 03:58:36 2013 Hosted by follow the white rabbit.