r67077 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r67076‎ | r67077 | r67078 >
Date:15:33, 30 May 2010
Author:tisane
Status:reverted
Tags:
Comment:
=Wikibot PHP bot framework; released into GPL by Cobi Carter and revised by Tisane
Modified paths:
  • /trunk/tools/wikibot (added) (history)
  • /trunk/tools/wikibot/wikibot.classes.php (added) (history)

Diff [purge]

Index: trunk/tools/wikibot/wikibot.classes.php
@@ -0,0 +1,1091 @@
 2+<?PHP
 3+ /**
 4+ * @author Cobi Carter, Tisane
 5+ *
 6+ * This script is free software that is available under the terms of the
 7+ * current version of the GNU General Public License.
 8+ *
 9+ * This is a configurable MediaWiki bot framework.
 10+ **/
 11+
 12+ require( 'wikibot.config.php' );
 13+
 14+ function getWikibotSetting( $setting, $bot, $wiki ) {
 15+ global $wikibotSetting;
 16+ $permutation = 0;
 17+ while ( !isset( $result ) && $permutation < 4 ) {
 18+
 19+ switch ( $permutation ) {
 20+ case 0:
 21+ if ( isset( $wikibotSetting[$setting][$bot][$wiki] ) ) {
 22+ $result = $wikibotSetting[$setting][$bot][$wiki];
 23+ }
 24+ break;
 25+ case 1:
 26+ if ( isset( $wikibotSetting[$setting][$bot]['*'] ) ) {
 27+ $result = $wikibotSetting[$setting][$bot]['*'];
 28+ }
 29+ case 2:
 30+ if ( isset( $wikibotSetting[$setting]['*'][$wiki] ) ) {
 31+ $result = $wikibotSetting[$setting]['*'][$wiki];
 32+ }
 33+ break;
 34+ case 3:
 35+ if ( isset( $wikibotSetting[$setting]['*']['*'] ) ) {
 36+ $result = $wikibotSetting[$setting]['*']['*'];
 37+ }
 38+ break;
 39+ }
 40+ $permutation++;
 41+ }
 42+ if ( isset( $result ) ) {
 43+ return $result;
 44+ } else {
 45+ return false;
 46+ }
 47+ }
 48+
 49+ /**
 50+ * This class is designed to provide a simplified interface to cURL which maintains cookies.
 51+ * @author Cobi
 52+ **/
 53+ class http {
 54+ private $ch;
 55+ private $uid;
 56+ public $postfollowredirs;
 57+ public $getfollowredirs;
 58+
 59+ /**
 60+ * Our constructor function. This just does basic cURL initialization.
 61+ * @return void
 62+ **/
 63+ function __construct () {
 64+ global $proxyhost, $proxyport;
 65+ $this->ch = curl_init();
 66+ $this->uid = dechex( rand( 0, 99999999 ) );
 67+ curl_setopt( $this->ch, CURLOPT_COOKIEJAR, '/tmp/cluewikibot.cookies.' . $this->uid . '.dat' );
 68+ curl_setopt( $this->ch, CURLOPT_COOKIEFILE, '/tmp/cluewikibot.cookies.' . $this->uid . '.dat' );
 69+ curl_setopt( $this->ch, CURLOPT_MAXCONNECTS, 100 );
 70+ curl_setopt( $this->ch, CURLOPT_CLOSEPOLICY, CURLCLOSEPOLICY_LEAST_RECENTLY_USED );
 71+ curl_setopt( $this->ch, CURLOPT_USERAGENT, 'ClueBot/1.1' );
 72+ if ( isset( $proxyhost ) and isset( $proxyport ) and ( $proxyport != null ) and ( $proxyhost != null ) ) {
 73+ curl_setopt( $this->ch, CURLOPT_PROXYTYPE, CURLPROXY_HTTP );
 74+ curl_setopt( $this->ch, CURLOPT_PROXY, $proxyhost );
 75+ curl_setopt( $this->ch, CURLOPT_PROXYPORT, $proxyport );
 76+ }
 77+ $this->postfollowredirs = 0;
 78+ $this->getfollowredirs = 1;
 79+ }
 80+
 81+ /**
 82+ * Post to a URL.
 83+ * @param $url The URL to post to.
 84+ * @param $data The post-data to post, should be an array of key => value pairs.
 85+ * @return Data retrieved from the POST request.
 86+ **/
 87+ function post ( $url, $data ) {
 88+ $time = microtime( 1 );
 89+ curl_setopt( $this->ch, CURLOPT_URL, $url );
 90+ curl_setopt( $this->ch, CURLOPT_FOLLOWLOCATION, $this->postfollowredirs );
 91+ curl_setopt( $this->ch, CURLOPT_MAXREDIRS, 10 );
 92+ curl_setopt( $this->ch, CURLOPT_HEADER, 0 );
 93+ curl_setopt( $this->ch, CURLOPT_RETURNTRANSFER, 1 );
 94+ curl_setopt( $this->ch, CURLOPT_TIMEOUT, 30 );
 95+ curl_setopt( $this->ch, CURLOPT_CONNECTTIMEOUT, 10 );
 96+ curl_setopt( $this->ch, CURLOPT_POST, 1 );
 97+ curl_setopt( $this->ch, CURLOPT_POSTFIELDS, $data );
 98+ curl_setopt( $this->ch, CURLOPT_HTTPHEADER, array( 'Expect:' ) );
 99+ $data = curl_exec( $this->ch );
 100+ # global $logfd; if (!is_resource($logfd)) $logfd = fopen('php://stderr','w'); fwrite($logfd,'POST: '.$url.' ('.(microtime(1) - $time).' s) ('.strlen($data)." b)\n");
 101+ return $data;
 102+ }
 103+
 104+ /**
 105+ * Get a URL.
 106+ * @param $url The URL to get.
 107+ * @return Data retrieved from the GET request.
 108+ **/
 109+ function get ( $url ) {
 110+ $time = microtime( 1 );
 111+ curl_setopt( $this->ch, CURLOPT_URL, $url );
 112+ curl_setopt( $this->ch, CURLOPT_FOLLOWLOCATION, $this->getfollowredirs );
 113+ curl_setopt( $this->ch, CURLOPT_MAXREDIRS, 10 );
 114+ curl_setopt( $this->ch, CURLOPT_HEADER, 0 );
 115+ curl_setopt( $this->ch, CURLOPT_RETURNTRANSFER, 1 );
 116+ curl_setopt( $this->ch, CURLOPT_TIMEOUT, 30 );
 117+ curl_setopt( $this->ch, CURLOPT_CONNECTTIMEOUT, 10 );
 118+ curl_setopt( $this->ch, CURLOPT_HTTPGET, 1 );
 119+ $data = curl_exec( $this->ch );
 120+ # $global $logfd; if (!is_resource($logfd)) $logfd = fopen('php://stderr','w'); fwrite($logfd,'GET: '.$url.' ('.(microtime(1) - $time).' s) ('.strlen($data)." b)\n");
 121+ return $data;
 122+ }
 123+
 124+ /**
 125+ * Our destructor. Cleans up cURL and unlinks temporary files.
 126+ **/
 127+ function __destruct () {
 128+ curl_close( $this->ch );
 129+ @unlink( '/tmp/cluewikibot.cookies.' . $this->uid . '.dat' );
 130+ }
 131+ }
 132+
 133+ /**
 134+ * This class is a deprecated wrapper class which allows legacy code written for Wikipedia's query.php API to still work with wikipediaapi::.
 135+ **/
 136+ class wikipediaquery {
 137+ private $http;
 138+ private $api;
 139+ public $apiurl;
 140+ public $queryurl;
 141+ public $indexurl;
 142+
 143+ /**
 144+ * This is our constructor.
 145+ * @param $myApiurl API url; used if $fromFile is false
 146+ * @param $myQueryurl Query url; used if $fromFile is false
 147+ * @param $myIndexurl Index url; used if $fromFile is false
 148+ * @param $bot Bot name; used if $fromFile is true
 149+ * @param $wiki Wiki name; used if $fromFile is true
 150+ * @param $fromFile If true, load settings from wikibot.config.php
 151+ * @return void
 152+ **/
 153+ function __construct ( $myApiurl, $myQueryurl, $myIndexurl, $bot = '', $wiki = '', $fromFile = false ) {
 154+ global $queryFunctionFile;
 155+ if ( $fromFile == true ) {
 156+ $this->apiurl = getWikibotSetting( 'apiurl', $bot, $wiki );
 157+ $this->queryurl = getWikibotSetting( 'queryurl', $bot, $wiki ); // Obsolete, but kept for compatibility purposes.
 158+ $this->indexurl = getWikibotSetting( 'indexurl', $bot, $wiki );
 159+ } else {
 160+ $this->apiurl = $myApiurl;
 161+ $this->queryurl = $myQueryurl; // Obsolete, but kept for compatibility purposes.
 162+ $this->indexurl = $myIndexurl;
 163+ }
 164+ if ( isset( $queryFunctionFile ) ) {
 165+ foreach ( $queryFunctionFile as $item ) {
 166+ require_once( $item );
 167+ }
 168+ }
 169+ global $__wp__http;
 170+ if ( !isset( $__wp__http ) ) {
 171+ $__wp__http = new http;
 172+ }
 173+ $this->http = &$__wp__http;
 174+ $this->api = new wikipediaapi ( $this->apiurl, $this->queryurl, $this->indexurl );
 175+ }
 176+
 177+ /**
 178+ * Reinitializes the queryurl.
 179+ * @private
 180+ * @return void
 181+ **/
 182+ private function checkurl() {
 183+ $this->api->apiurl = str_replace( 'query.php', 'api.php', $this->queryurl );
 184+ }
 185+
 186+ /**
 187+ * Gets the content of a page.
 188+ * @param $page The wikipedia page to fetch.
 189+ * @return The wikitext for the page.
 190+ **/
 191+ function getpage ( $page ) {
 192+ $this->checkurl();
 193+ $ret = $this->api->revisions( $page, 1, 'older', true, null, true, false, false, false );
 194+ if (isset($ret[0]['*'])){
 195+ return $ret[0]['*'];
 196+ } else{
 197+ return;
 198+ }
 199+ }
 200+
 201+ /**
 202+ * Gets the page id for a page.
 203+ * @param $page The wikipedia page to get the id for.
 204+ * @return The page id of the page.
 205+ **/
 206+ function getpageid ( $page ) {
 207+ $this->checkurl();
 208+ $ret = $this->api->revisions( $page, 1, 'older', false, null, true, false, false, false );
 209+ return $ret['pageid'];
 210+ }
 211+
 212+ /**
 213+ * Gets the number of contributions a user has.
 214+ * @param $user The username for which to get the edit count.
 215+ * @return The number of contributions the user has.
 216+ **/
 217+ function contribcount ( $user ) {
 218+ $this->checkurl();
 219+ $ret = $this->api->users( $user, 1, null, true );
 220+ if ( $ret !== false ) return $ret[0]['editcount'];
 221+ return false;
 222+ }
 223+ }
 224+
 225+ /**
 226+ * This class is for interacting with Wikipedia's api.php API.
 227+ **/
 228+ class wikipediaapi {
 229+ private $http;
 230+ private $edittoken;
 231+ private $tokencache;
 232+ public $apiurl;
 233+ public $queryurl;
 234+ public $indexurl;
 235+
 236+ /**
 237+ * This is our constructor.
 238+ * @param $myApiurl API url; used if $fromFile is false
 239+ * @param $myQueryurl Query url; used if $fromFile is false
 240+ * @param $myIndexurl Index url; used if $fromFile is false
 241+ * @param $bot Bot name; used if $fromFile is true
 242+ * @param $wiki Wiki name; used if $fromFile is true
 243+ * @param $fromFile If true, load settings from wikibot.config.php
 244+ * @return void
 245+ **/
 246+ function __construct ( $myApiurl, $myQueryurl, $myIndexurl, $bot = '', $wiki = '', $fromFile = false ) {
 247+ global $apiFunctionFile;
 248+ if ( $fromFile == true ) {
 249+ $this->apiurl = getWikibotSetting( 'apiurl', $bot, $wiki );
 250+ $this->queryurl = getWikibotSetting( 'queryurl', $bot, $wiki ); // Obsolete, but kept for compatibility purposes.
 251+ $this->indexurl = getWikibotSetting( 'indexurl', $bot, $wiki );
 252+ } else {
 253+ $this->apiurl = $myApiurl;
 254+ $this->queryurl = $myQueryurl; // Obsolete, but kept for compatibility purposes.
 255+ $this->indexurl = $myIndexurl;
 256+ }
 257+ if ( isset( $queryFunctionFile ) ) {
 258+ foreach ( $apiFunctionFile as $item ) {
 259+
 260+ require_once( $item );
 261+ }
 262+ }
 263+ global $__wp__http;
 264+ if ( !isset( $__wp__http ) ) {
 265+ $__wp__http = new http;
 266+ }
 267+ $this->http = &$__wp__http;
 268+ }
 269+
 270+ /**
 271+ * This function takes a username and password and logs you into wikipedia.
 272+ * @param $user Username to login as.
 273+ * @param $pass Password that corresponds to the username.
 274+ * @return void
 275+ **/
 276+ function login ( $user, $pass ) {
 277+ $x = unserialize( $this->http->post( $this->apiurl . '?action=login&format=php', array( 'lgname' => $user, 'lgpassword' => $pass ) ) );
 278+ if ( $x['login']['result'] == 'Success' )
 279+ return true;
 280+ if ( $x['login']['result'] == 'NeedToken' ) {
 281+ $x = unserialize( $this->http->post( $this->apiurl . '?action=login&format=php', array( 'lgname' => $user, 'lgpassword' => $pass, 'lgtoken' => $x['login']['token'] ) ) );
 282+ if ( $x['login']['result'] == 'Success' )
 283+ return true;
 284+ }
 285+ return false;
 286+ }
 287+
 288+ /**
 289+ * This function returns the edit token.
 290+ * @return Edit token.
 291+ **/
 292+ function getedittoken () {
 293+ $tokens = $this->gettokens( 'Main Page' );
 294+ if ( $tokens['edittoken'] == '' ) $tokens = $this->gettokens( 'Main Page', true );
 295+ $this->edittoken = $tokens['edittoken'];
 296+ return $tokens['edittoken'];
 297+ }
 298+
 299+ /**
 300+ * This function returns the various tokens for a certain page.
 301+ * @param $title Page to get the tokens for.
 302+ * @param $flush Optional - internal use only. Flushes the token cache.
 303+ * @return An associative array of tokens for the page.
 304+ **/
 305+ function gettokens ( $title, $flush = false ) {
 306+ if ( !is_array( $this->tokencache ) ) $this->tokencache = array();
 307+ foreach ( $this->tokencache as $t => $data ) if ( time() - $data['timestamp'] > 6 * 60 * 60 ) unset( $this->tokencache[$t] );
 308+ if ( isset( $this->tokencache[$title] ) && ( !$flush ) ) {
 309+ return $this->tokencache[$title]['tokens'];
 310+ } else {
 311+ $tokens = array();
 312+ $x = $this->http->get( $this->apiurl . '?action=query&format=php&prop=info&intoken=edit|delete|protect|move|block|unblock|email&titles=' . urlencode( $title ) );
 313+ $x = unserialize( $x );
 314+ foreach ( $x['query']['pages'] as $y ) {
 315+ $tokens['edittoken'] = $y['edittoken'];
 316+ $tokens['deletetoken'] = $y['deletetoken'];
 317+ $tokens['protecttoken'] = $y['protecttoken'];
 318+ $tokens['movetoken'] = $y['movetoken'];
 319+ $tokens['blocktoken'] = $y['blocktoken'];
 320+ $tokens['unblocktoken'] = $y['unblocktoken'];
 321+ # $tokens['emailtoken'] = $y['emailtoken'];
 322+ $this->tokencache[$title] = array(
 323+ 'timestamp' => time(),
 324+ 'tokens' => $tokens
 325+ );
 326+ return $tokens;
 327+ }
 328+ }
 329+ }
 330+
 331+ /**
 332+ * This function returns the recent changes for the wiki.
 333+ * @param $count The number of items to return. (Default 10)
 334+ * @param $namespace The namespace ID to filter items on. Null for no filtering. (Default null)
 335+ * @param $dir The direction to pull items. "older" or "newer". (Default 'older')
 336+ * @param $ts The timestamp to start at. Null for the beginning/end (depending on direction). (Default null)
 337+ * @return Associative array of recent changes metadata.
 338+ **/
 339+ function recentchanges ( $count = 10, $namespace = null, $dir = 'older', $ts = null ) {
 340+ $append = '';
 341+ if ( $ts !== null ) { $append .= '&rcstart=' . urlencode( $ts ); }
 342+ $append .= '&rcdir=' . urlencode( $dir );
 343+ if ( $namespace !== null ) { $append .= '&rcnamespace=' . urlencode( $namespace ); }
 344+ $x = $this->http->get( $this->apiurl . '?action=query&list=recentchanges&rcprop=user|comment|flags|timestamp|title|ids|sizes&format=php&rclimit=' . $count . $append );
 345+ $x = unserialize( $x );
 346+ return $x['query']['recentchanges'];
 347+ }
 348+
 349+ /**
 350+ * This function returns search results from Wikipedia's internal search engine.
 351+ * @param $search The query string to search for.
 352+ * @param $limit The number of results to return. (Default 10)
 353+ * @param $offset The number to start at. (Default 0)
 354+ * @param $namespace The namespace ID to filter by. Null means no filtering. (Default 0)
 355+ * @param $what What to search, 'text' or 'title'. (Default 'text')
 356+ * @param $redirs Whether or not to list redirects. (Default false)
 357+ * @return Associative array of search result metadata.
 358+ **/
 359+ function search ( $search, $limit = 10, $offset = 0, $namespace = 0, $what = 'text', $redirs = false ) {
 360+ $append = '';
 361+ if ( $limit != null ) $append .= '&srlimit=' . urlencode( $limit );
 362+ if ( $offset != null ) $append .= '&sroffset=' . urlencode( $offset );
 363+ if ( $namespace != null ) $append .= '&srnamespace=' . urlencode( $namespace );
 364+ if ( $what != null ) $append .= '&srwhat=' . urlencode( $what );
 365+ if ( $redirs == true ) $append .= '&srredirects=1';
 366+ else $append .= '&srredirects=0';
 367+ $x = $this->http->get( $this->apiurl . '?action=query&list=search&format=php&srsearch=' . urlencode( $search ) . $append );
 368+ $x = unserialize( $x );
 369+ return $x['query']['search'];
 370+ }
 371+
 372+ /**
 373+ * Retrieve entries from the WikiLog.
 374+ * @param $user Username who caused the entry. Null means anyone. (Default null)
 375+ * @param $title Object to which the entry refers. Null means anything. (Default null)
 376+ * @param $limit Number of entries to return. (Default 50)
 377+ * @param $type Type of logs. Null means any type. (Default null)
 378+ * @param $start Date to start enumerating logs. Null means beginning/end depending on $dir. (Default null)
 379+ * @param $end Where to stop enumerating logs. Null means whenever limit is satisfied or there are no more logs. (Default null)
 380+ * @param $dir Direction to enumerate logs. "older" or "newer". (Default 'older')
 381+ * @return Associative array of logs metadata.
 382+ **/
 383+ function logs ( $user = null, $title = null, $limit = 50, $type = null, $start = null, $end = null, $dir = 'older' ) {
 384+ $append = '';
 385+ if ( $user != null ) $append .= '&leuser=' . urlencode( $user );
 386+ if ( $title != null ) $append .= '&letitle=' . urlencode( $title );
 387+ if ( $limit != null ) $append .= '&lelimit=' . urlencode( $limit );
 388+ if ( $type != null ) $append .= '&letype=' . urlencode( $type );
 389+ if ( $start != null ) $append .= '&lestart=' . urlencode( $start );
 390+ if ( $end != null ) $append .= '&leend=' . urlencode( $end );
 391+ if ( $dir != null ) $append .= '&ledir=' . urlencode( $dir );
 392+ $x = $this->http->get( $this->apiurl . '?action=query&format=php&list=logevents&leprop=ids|title|type|user|timestamp|comment|details' . $append );
 393+ $x = unserialize( $x );
 394+ return $x['query']['logevents'];
 395+ }
 396+
 397+ /**
 398+ * Retrieves metadata about a user's contributions.
 399+ * @param $user Username whose contributions we want to retrieve.
 400+ * @param $count Number of entries to return. (Default 50)
 401+ * @param[in,out] $continue Where to continue enumerating if part of a larger, split request. This is filled with the next logical continuation value. (Default null)
 402+ * @param $dir Which direction to enumerate from, "older" or "newer". (Default 'older')
 403+ * @return Associative array of contributions metadata.
 404+ **/
 405+ function usercontribs ( $user, $count = 50, &$continue = null, $dir = 'older' ) {
 406+ if ( $continue != null ) {
 407+ $append = '&ucstart=' . urlencode( $continue );
 408+ } else {
 409+ $append = '';
 410+ }
 411+ $x = $this->http->get( $this->apiurl . '?action=query&format=php&list=usercontribs&ucuser=' . urlencode( $user ) . '&uclimit=' . urlencode( $count ) . '&ucdir=' . urlencode( $dir ) . $append );
 412+ $x = unserialize( $x );
 413+ $continue = $x['query-continue']['usercontribs']['ucstart'];
 414+ return $x['query']['usercontribs'];
 415+ }
 416+
 417+ /**
 418+ * Returns revision data (meta and/or actual).
 419+ * @param $page Page for which to return revision data for.
 420+ * @param $count Number of revisions to return. (Default 1)
 421+ * @param $dir Direction to start enumerating multiple revisions from, "older" or "newer". (Default 'older')
 422+ * @param $content Whether to return actual revision content, true or false. (Default false)
 423+ * @param $revid Revision ID to start at. (Default null)
 424+ * @param $wait Whether or not to wait a few seconds for the specific revision to become available. (Default true)
 425+ * @param $getrbtok Whether or not to retrieve a rollback token for the revision. (Default false)
 426+ * @param $dieonerror Whether or not to kill the process with an error if an error occurs. (Default false)
 427+ * @param $redirects Whether or not to follow redirects. (Default false)
 428+ * @return Associative array of revision data.
 429+ **/
 430+ function revisions ( $page, $count = 1, $dir = 'older', $content = false, $revid = null, $wait = true, $getrbtok = false, $dieonerror = true, $redirects = false ) {
 431+ $x = $this->http->get( $this->apiurl . '?action=query&prop=revisions&titles=' . urlencode( $page ) . '&rvlimit=' . urlencode( $count ) . '&rvprop=timestamp|ids|user|comment' . ( ( $content ) ? '|content':'' ) . '&format=php&meta=userinfo&rvdir=' . urlencode( $dir ) . ( ( $revid !== null ) ? '&rvstartid=' . urlencode( $revid ):'' ) . ( ( $getrbtok == true ) ? '&rvtoken=rollback':'' ) . ( ( $redirects == true ) ? '&redirects':'' ) );
 432+ $x = unserialize( $x );
 433+ if ( $revid !== null ) {
 434+ $found = false;
 435+ if ( !isset( $x['query']['pages'] ) or !is_array( $x['query']['pages'] ) ) {
 436+ if ( $dieonerror == true ) die( 'No such page.' . "\n" );
 437+ else return false;
 438+ }
 439+ foreach ( $x['query']['pages'] as $data ) {
 440+ if ( !isset( $data['revisions'] ) or !is_array( $data['revisions'] ) ) {
 441+ if ( $dieonerror == true ) die( 'No such page.' . "\n" );
 442+ else return false;
 443+ }
 444+ foreach ( $data['revisions'] as $data2 ) if ( $data2['revid'] == $revid ) $found = true;
 445+ unset( $data, $data2 );
 446+ break;
 447+ }
 448+
 449+ if ( $found == false ) {
 450+ if ( $wait == true ) {
 451+ sleep( 1 );
 452+ return $this->revisions( $page, $count, $dir, $content, $revid, false, $getrbtok, $dieonerror );
 453+ } else {
 454+ if ( $dieonerror == true ) die( 'Revision error.' . "\n" );
 455+ }
 456+ }
 457+ }
 458+ foreach ( $x['query']['pages'] as $key => $data ) {
 459+ $data['revisions']['ns'] = $data['ns'];
 460+ $data['revisions']['title'] = $data['title'];
 461+ $data['revisions']['currentuser'] = $x['query']['userinfo']['name'];
 462+// $data['revisions']['currentuser'] = $x['query']['userinfo']['currentuser']['name'];
 463+ if ( isset( $x['query-continue'] ) ) {
 464+ $data['revisions']['continue'] = $x['query-continue']['revisions']['rvstartid'];
 465+ }
 466+ $data['revisions']['pageid'] = $key;
 467+ return $data['revisions'];
 468+ }
 469+ }
 470+
 471+ /**
 472+ * Enumerates user metadata.
 473+ * @param $start The username to start enumerating from. Null means from the beginning. (Default null)
 474+ * @param $limit The number of users to enumerate. (Default 1)
 475+ * @param $group The usergroup to filter by. Null means no filtering. (Default null)
 476+ * @param $requirestart Whether or not to require that $start be a valid username. (Default false)
 477+ * @param[out] $continue This is filled with the name to continue from next query. (Default null)
 478+ * @return Associative array of user metadata.
 479+ **/
 480+ function users ( $start = null, $limit = 1, $group = null, $requirestart = false, &$continue = null ) {
 481+ $append = '';
 482+ if ( $start != null ) $append .= '&aufrom=' . urlencode( $start );
 483+ if ( $group != null ) $append .= '&augroup=' . urlencode( $group );
 484+ $x = $this->http->get( $this->apiurl . '?action=query&list=allusers&format=php&auprop=blockinfo|editcount|registration|groups&aulimit=' . urlencode( $limit ) . $append );
 485+ $x = unserialize( $x );
 486+ $continue = $x['query-continue']['allusers']['aufrom'];
 487+ if ( ( $requirestart == true ) and ( $x['query']['allusers'][0]['name'] != $start ) ) return false;
 488+ return $x['query']['allusers'];
 489+ }
 490+
 491+ /**
 492+ * Get members of a category.
 493+ * @param $category Category to enumerate from.
 494+ * @param $count Number of members to enumerate. (Default 500)
 495+ * @param[in,out] $continue Where to continue enumerating from. This is automatically filled in when run. (Default null)
 496+ * @return Associative array of category member metadata.
 497+ **/
 498+ function categorymembers ( $category, $count = 500, &$continue = null ) {
 499+ if ( $continue != null ) {
 500+ $append = '&cmcontinue=' . urlencode( $continue );
 501+ } else {
 502+ $append = '';
 503+ }
 504+ $category = 'Category:' . str_ireplace( 'category:', '', $category );
 505+ $x = $this->http->get( $this->apiurl . '?action=query&list=categorymembers&cmtitle=' . urlencode( $category ) . '&format=php&cmlimit=' . $count . $append );
 506+ $x = unserialize( $x );
 507+ $continue = $x['query-continue']['categorymembers']['cmcontinue'];
 508+ return $x['query']['categorymembers'];
 509+ }
 510+
 511+ /**
 512+ * Enumerate all categories.
 513+ * @param[in,out] $start Where to start enumerating. This is updated automatically with the value to continue from. (Default null)
 514+ * @param $limit Number of categories to enumerate. (Default 50)
 515+ * @param $dir Direction to enumerate in. 'ascending' or 'descending'. (Default 'ascending')
 516+ * @param $prefix Only enumerate categories with this prefix. (Default null)
 517+ * @return Associative array of category list metadata.
 518+ **/
 519+ function listcategories ( &$start = null, $limit = 50, $dir = 'ascending', $prefix = null ) {
 520+ $append = '';
 521+ if ( $start != null ) $append .= '&acfrom=' . urlencode( $start );
 522+ if ( $limit != null ) $append .= '&aclimit=' . urlencode( $limit );
 523+ if ( $dir != null ) $append .= '&acdir=' . urlencode( $dir );
 524+ if ( $prefix != null ) $append .= '&acprefix=' . urlencode( $prefix );
 525+
 526+ $x = $this->http->get( $this->apiurl . '?action=query&list=allcategories&acprop=size&format=php' . $append );
 527+ $x = unserialize( $x );
 528+
 529+ $start = $x['query-continue']['allcategories']['acfrom'];
 530+
 531+ return $x['query']['allcategories'];
 532+ }
 533+
 534+ /**
 535+ * Enumerate all backlinks to a page.
 536+ * @param $page Page to search for backlinks to.
 537+ * @param $count Number of backlinks to list. (Default 500)
 538+ * @param[in,out] $continue Where to start enumerating from. This is automatically filled in. (Default null)
 539+ * @param $filter Whether or not to include redirects. Acceptible values are 'all', 'redirects', and 'nonredirects'. (Default null)
 540+ * @return Associative array of backlink metadata.
 541+ **/
 542+ function backlinks ( $page, $count = 500, &$continue = null, $filter = null ) {
 543+ if ( $continue != null ) {
 544+ $append = '&blcontinue=' . urlencode( $continue );
 545+ } else {
 546+ $append = '';
 547+ }
 548+ if ( $filter != null ) {
 549+ $append .= '&blfilterredir=' . urlencode( $filter );
 550+ }
 551+
 552+ $x = $this->http->get( $this->apiurl . '?action=query&list=backlinks&bltitle=' . urlencode( $page ) . '&format=php&bllimit=' . $count . $append );
 553+ $x = unserialize( $x );
 554+ $continue = $x['query-continue']['backlinks']['blcontinue'];
 555+ return $x['query']['backlinks'];
 556+ }
 557+
 558+ /**
 559+ * Gets a list of transcludes embedded in a page.
 560+ * @param $page Page to look for transcludes in.
 561+ * @param $count Number of transcludes to list. (Default 500)
 562+ * @param[in,out] $continue Where to start enumerating from. This is automatically filled in. (Default null)
 563+ * @return Associative array of transclude metadata.
 564+ **/
 565+ function embeddedin ( $page, $count = 500, &$continue = null ) {
 566+ if ( $continue != null ) {
 567+ $append = '&eicontinue=' . urlencode( $continue );
 568+ } else {
 569+ $append = '';
 570+ }
 571+ $x = $this->http->get( $this->apiurl . '?action=query&list=embeddedin&eititle=' . urlencode( $page ) . '&format=php&eilimit=' . $count . $append );
 572+ $x = unserialize( $x );
 573+ $continue = $x['query-continue']['embeddedin']['eicontinue'];
 574+ return $x['query']['embeddedin'];
 575+ }
 576+
 577+ /**
 578+ * Gets a list of pages with a common prefix.
 579+ * @param $prefix Common prefix to search for.
 580+ * @param $namespace Numeric namespace to filter on. (Default 0)
 581+ * @param $count Number of pages to list. (Default 500)
 582+ * @param[in,out] $continue Where to start enumerating from. This is automatically filled in. (Default null)
 583+ * @return Associative array of page metadata.
 584+ **/
 585+ function listprefix ( $prefix, $namespace = 0, $count = 500, &$continue = null ) {
 586+ $append = '&apnamespace=' . urlencode( $namespace );
 587+ if ( $continue != null ) {
 588+ $append .= '&apfrom=' . urlencode( $continue );
 589+ }
 590+ $x = $this->http->get( $this->apiurl . '?action=query&list=allpages&apprefix=' . urlencode( $prefix ) . '&format=php&aplimit=' . $count . $append );
 591+ $x = unserialize( $x );
 592+ $continue = $x['query-continue']['allpages']['apfrom'];
 593+ return $x['query']['allpages'];
 594+ }
 595+
 596+ /**
 597+ * Edits a page.
 598+ * @param $page Page name to edit.
 599+ * @param $data Data to post to page.
 600+ * @param $summary Edit summary to use.
 601+ * @param $minor Whether or not to mark edit as minor. (Default false)
 602+ * @param $bot Whether or not to mark edit as a bot edit. (Default true)
 603+ * @param $wpStarttime Time in MW TS format of beginning of edit. (Default now)
 604+ * @param $wpEdittime Time in MW TS format of last edit to that page. (Default correct)
 605+ * @return boolean True on success, false on failure.
 606+ **/
 607+ function edit ( $page, $data, $summary = '', $minor = false, $bot = true, $wpStarttime = null, $wpEdittime = null, $checkrun = true ) {
 608+ global $run, $user;
 609+
 610+ $wpq = new wikipediaquery( $this->apiurl, $this->queryurl, $this->indexurl ); $wpq->queryurl = str_replace( 'api.php', 'query.php', $this->apiurl );
 611+
 612+ if ( $checkrun == true )
 613+ if ( !preg_match( '/(yes|enable|true)/iS', ( ( isset( $run ) ) ? $run:$wpq->getpage( 'User:' . $user . '/Run' ) ) ) )
 614+ return false; /* Check /Run page */
 615+
 616+ $params = Array(
 617+ 'action' => 'edit',
 618+ 'format' => 'php',
 619+ 'assert' => 'bot',
 620+ 'title' => $page,
 621+ 'text' => $data,
 622+ 'token' => $this->getedittoken(),
 623+ 'summary' => $summary,
 624+ ( $minor ? 'minor':'notminor' ) => '1',
 625+ ( $bot ? 'bot':'notbot' ) => '1'
 626+ );
 627+
 628+ if ( $wpStarttime !== null ) $params['starttimestamp'] = $wpStarttime;
 629+ if ( $wpEdittime !== null ) $params['basetimestamp'] = $wpEdittime;
 630+
 631+ $x = $this->http->post( $this->apiurl, $params );
 632+ $x = unserialize( $x );
 633+ var_export( $x );
 634+ if ( $x['edit']['result'] == 'Success' ) return true;
 635+ else return false;
 636+ }
 637+
 638+ /**
 639+ * Uploads a file
 640+ * @param $page Page name to edit.
 641+ * @param $data Data to post to page.
 642+ * @param $summary Edit summary to use.
 643+ * @param $minor Whether or not to mark edit as minor. (Default false)
 644+ * @param $bot Whether or not to mark edit as a bot edit. (Default true)
 645+ * @param $wpStarttime Time in MW TS format of beginning of edit. (Default now)
 646+ * @param $wpEdittime Time in MW TS format of last edit to that page. (Default correct)
 647+ * @return boolean True on success, false on failure.
 648+ **/
 649+ function uploadFromString ( $page, $data, $summary = '', $checkrun = true ) {
 650+ global $run, $user;
 651+
 652+ $wpq = new wikipediaquery( $this->apiurl, $this->queryurl, $this->indexurl ); $wpq->queryurl = str_replace( 'api.php', 'query.php', $this->apiurl );
 653+
 654+ /*if ($checkrun == true)
 655+ if (!preg_match('/(yes|enable|true)/iS',((isset($run))?$run:$wpq->getpage('User:'.$user.'/Run'))))
 656+ return false; /* Check /Run page */
 657+
 658+
 659+ /*$params = Array(
 660+ 'action' => 'upload',
 661+ 'filename' => $page,
 662+ 'file' => $data,
 663+ 'token' => $this->getedittoken(),
 664+ 'comment' => $summary
 665+ );*/
 666+
 667+ $params = Array(
 668+ 'action' => 'upload',
 669+ 'filename' => 'grokstar420',
 670+ 'file' => 'foo',
 671+ 'token' => $this->getedittoken(),
 672+ 'text' => 'yoyo',
 673+ 'comment' => 'yo'
 674+ );
 675+
 676+ $x = $this->http->post( $this->apiurl, $params );
 677+ # $x = unserialize($x);
 678+ # var_export($x);
 679+ # if ($x['edit']['result'] == 'Success') return true;
 680+ # else return false;
 681+ }
 682+
 683+ /**
 684+ * Moves a page.
 685+ * @param $old Name of page to move.
 686+ * @param $new New page title.
 687+ * @param $reason Move summary to use.
 688+ * @return void
 689+ **/
 690+ function move ( $old, $new, $reason ) {
 691+ $tokens = $this->gettokens( $old );
 692+ $params = array(
 693+ 'action' => 'move',
 694+ 'format' => 'php',
 695+ 'from' => $old,
 696+ 'to' => $new,
 697+ 'token' => $tokens['movetoken'],
 698+ 'reason' => $reason
 699+ );
 700+
 701+ $x = $this->http->post( $this->apiurl, $params );
 702+ $x = unserialize( $x );
 703+ var_export( $x );
 704+ }
 705+
 706+ /**
 707+ * Rollback an edit.
 708+ * @param $title Title of page to rollback.
 709+ * @param $user Username of last edit to the page to rollback.
 710+ * @param $reason Edit summary to use for rollback.
 711+ * @param $token Rollback token. If not given, it will be fetched. (Default null)
 712+ * @return void
 713+ **/
 714+ function rollback ( $title, $user, $reason, $token = null ) {
 715+ if ( ( $token == null ) or ( $token == '' ) ) {
 716+ $token = $this->revisions( $title, 1, 'older', false, null, true, true );
 717+ print_r( $token );
 718+ if ( $token[0]['user'] == $user ) {
 719+ $token = $token[0]['rollbacktoken'];
 720+ } else {
 721+ return false;
 722+ }
 723+ }
 724+ $params = array(
 725+ 'action' => 'rollback',
 726+ 'format' => 'php',
 727+ 'title' => $title,
 728+ 'user' => $user,
 729+ 'summary' => $reason,
 730+ 'token' => $token,
 731+ 'markbot' => 0
 732+ );
 733+
 734+ echo 'Posting to API: ';
 735+ var_export( $params );
 736+
 737+ $x = $this->http->post( $this->apiurl, $params );
 738+ $x = unserialize( $x );
 739+ var_export( $x );
 740+ return ( isset( $x['rollback']['summary'] ) ? true:false );
 741+ }
 742+
 743+ /**
 744+ * Inserts one or more page into the RPED table.
 745+ * @return True.
 746+ **/
 747+ function rpedInsert ( $page ) {
 748+ $params = Array(
 749+ 'action' => 'rped',
 750+ 'format' => 'php',
 751+ 'insert' => $page,
 752+ );
 753+
 754+ $x = $this->http->post( $this->apiurl, $params );
 755+ $x = unserialize( $x );
 756+ # var_export($x);
 757+ return true;
 758+ }
 759+
 760+ /**
 761+ * Deletes one or more pages from the RPED table.
 762+ * @return True.
 763+ **/
 764+ function rpedDelete ( $page ) {
 765+ $params = Array(
 766+ 'action' => 'rped',
 767+ 'format' => 'php',
 768+ 'delete' => $page,
 769+ );
 770+
 771+ $x = $this->http->post( $this->apiurl, $params );
 772+ $x = unserialize( $x );
 773+ # var_export($x);
 774+ return true;
 775+ }
 776+ }
 777+
 778+ /**
 779+ * This class is for interacting with Wikipedia's browser interface, index.php.
 780+ * Many of these functions are deprecated.
 781+ **/
 782+ class wikipediaindex {
 783+ private $http;
 784+ private $postinterval = 0;
 785+ private $lastpost;
 786+ private $edittoken;
 787+ public $apiurl;
 788+ public $queryurl;
 789+ public $indexurl;
 790+
 791+ /**
 792+ * This is our constructor.
 793+ * @param $myApiurl API url; used if $fromFile is false
 794+ * @param $myQueryurl Query url; used if $fromFile is false
 795+ * @param $myIndexurl Index url; used if $fromFile is false
 796+ * @param $bot Bot name; used if $fromFile is true
 797+ * @param $wiki Wiki name; used if $fromFile is true
 798+ * @param $fromFile If true, load settings from wikibot.config.php
 799+ * @return void
 800+ **/
 801+ function __construct ( $myApiurl, $myQueryurl, $myIndexurl, $bot = '', $wiki = '', $fromFile = false ) {
 802+ global $indexFunctionFile;
 803+ if ( $fromFile == true ) {
 804+ $this->apiurl = getWikibotSetting( 'apiurl', $bot, $wiki );
 805+ $this->queryurl = getWikibotSetting( 'queryurl', $bot, $wiki ); // Obsolete, but kept for compatibility purposes.
 806+ $this->indexurl = getWikibotSetting( 'indexurl', $bot, $wiki );
 807+ } else {
 808+ $this->apiurl = $myApiurl;
 809+ $this->queryurl = $myQueryurl; // Obsolete, but kept for compatibility purposes.
 810+ $this->indexurl = $myIndexurl;
 811+ }
 812+ if ( isset( $indexFunctionFile ) ) {
 813+ foreach ( $indexFunctionFile as $item ) {
 814+ require_once( $item );
 815+ }
 816+ }
 817+ global $__wp__http;
 818+ if ( !isset( $__wp__http ) ) {
 819+ $__wp__http = new http;
 820+ }
 821+ $this->http = &$__wp__http;
 822+ }
 823+
 824+ /**
 825+ * Post data to a page, nicely.
 826+ * @param $page Page title.
 827+ * @param $data Data to post to page.
 828+ * @param $summery Edit summary. (Default '')
 829+ * @param $minor Whether to mark edit as minor. (Default false)
 830+ * @param $rv Revision data. If not given, it will be fetched. (Default null)
 831+ * @param $bot Whether to mark edit as bot. (Default true)
 832+ * @return HTML data from the page.
 833+ * @deprecated
 834+ * @see wikipediaapi::edit
 835+ **/
 836+ function post ( $page, $data, $summery = '', $minor = false, $rv = null, $bot = true ) {
 837+ global $user;
 838+ global $maxlag;
 839+ global $irc;
 840+ global $irctechchannel;
 841+ global $run;
 842+ global $maxlagkeepgoing;
 843+
 844+ $wpq = new wikipediaquery ( $this->apiurl, $this->queryurl, $this->indexurl ); $wpq->queryurl = str_replace( 'index.php', 'query.php', $this->indexurl );
 845+ $wpapi = new wikipediaapi ( $this->apiurl, $this->queryurl, $this->indexurl ); $wpapi->apiurl = str_replace( 'index.php', 'api.php', $this->indexurl );
 846+
 847+ if ( ( !$this->edittoken ) or ( $this->edittoken == '' ) ) $this->edittoken = $wpapi->getedittoken();
 848+ if ( $rv == null ) $rv = $wpapi->revisions( $page, 1, 'older', true );
 849+ if ( !$rv[0]['*'] ) $rv[0]['*'] = $wpq->getpage( $page );
 850+
 851+ // Fake the edit form.
 852+ $now = gmdate( 'YmdHis', time() );
 853+ $token = htmlspecialchars( $this->edittoken );
 854+ $tmp = date_parse( $rv[0]['timestamp'] );
 855+ $edittime = gmdate( 'YmdHis', gmmktime( $tmp['hour'], $tmp['minute'], $tmp['second'], $tmp['month'], $tmp['day'], $tmp['year'] ) );
 856+ $html = "<input type='hidden' value=\"{$now}\" name=\"wpStarttime\" />\n";
 857+ $html .= "<input type='hidden' value=\"{$edittime}\" name=\"wpEdittime\" />\n";
 858+ $html .= "<input type='hidden' value=\"{$token}\" name=\"wpEditToken\" />\n";
 859+ $html .= '<input name="wpAutoSummary" type="hidden" value="' . md5( '' ) . '" />' . "\n";
 860+
 861+ if ( preg_match( '/' . preg_quote( '{{nobots}}', '/' ) . '/iS', $rv[0]['*'] ) ) { return false; } /* Honor the bots flags */
 862+ if ( preg_match( '/' . preg_quote( '{{bots|allow=none}}', '/' ) . '/iS', $rv[0]['*'] ) ) { return false; }
 863+ if ( preg_match( '/' . preg_quote( '{{bots|deny=all}}', '/' ) . '/iS', $rv[0]['*'] ) ) { return false; }
 864+ if ( preg_match( '/' . preg_quote( '{{bots|deny=', '/' ) . '(.*)' . preg_quote( '}}', '/' ) . '/iS', $rv[0]['*'], $m ) ) { if ( in_array( explode( ',', $m[1] ), $user ) ) { return false; } } /* /Honor the bots flags */
 865+ if ( !preg_match( '/' . preg_quote( $user, '/' ) . '/iS', $rv['currentuser'] ) ) { return false; } /* We need to be logged in */
 866+// if (preg_match('/'.preg_quote('You have new messages','/').'/iS',$rv[0]['*'])) { return false; } /* Check talk page */
 867+ if ( !preg_match( '/(yes|enable|true)/iS', ( ( isset( $run ) ) ? $run:$wpq->getpage( 'User:' . $user . '/Run' ) ) ) ) { return false; } /* Check /Run page */
 868+
 869+ $x = $this->forcepost( $page, $data, $summery, $minor, $html, $maxlag, $maxlagkeepgoing, $bot ); /* Go ahead and post. */
 870+ $this->lastpost = time();
 871+ return $x;
 872+ }
 873+
 874+ /**
 875+ * Post data to a page.
 876+ * @param $page Page title.
 877+ * @param $data Data to post to page.
 878+ * @param $summery Edit summary. (Default '')
 879+ * @param $minor Whether to mark edit as minor. (Default false)
 880+ * @param $edithtml HTML from the edit form. If not given, it will be fetched. (Default null)
 881+ * @param $maxlag Maxlag for posting. (Default null)
 882+ * @param $mlkg Whether to keep going after encountering a maxlag error and sleeping or not. (Default null)
 883+ * @param $bot Whether to mark edit as bot. (Default true)
 884+ * @return HTML data from the page.
 885+ * @deprecated
 886+ * @see wikipediaapi::edit
 887+ **/
 888+ function forcepost ( $page, $data, $summery = '', $minor = false, $edithtml = null, $maxlag = null, $mlkg = null, $bot = true ) {
 889+ $post['wpSection'] = '';
 890+ $post['wpScrolltop'] = '';
 891+ if ( $minor == true ) { $post['wpMinoredit'] = 1; }
 892+ $post['wpTextbox1'] = $data;
 893+ $post['wpSummary'] = $summery;
 894+ if ( $edithtml == null ) {
 895+ $html = $this->http->get( $this->indexurl . '?title=' . urlencode( $page ) . '&action=edit' );
 896+ } else {
 897+ $html = $edithtml;
 898+ }
 899+ preg_match( '|\<input type\=\\\'hidden\\\' value\=\"(.*)\" name\=\"wpStarttime\" /\>|U', $html, $m );
 900+ $post['wpStarttime'] = $m[1];
 901+ preg_match( '|\<input type\=\\\'hidden\\\' value\=\"(.*)\" name\=\"wpEdittime\" /\>|U', $html, $m );
 902+ $post['wpEdittime'] = $m[1];
 903+ preg_match( '|\<input type\=\\\'hidden\\\' value\=\"(.*)\" name\=\"wpEditToken\" /\>|U', $html, $m );
 904+ $post['wpEditToken'] = $m[1];
 905+ preg_match( '|\<input name\=\"wpAutoSummary\" type\=\"hidden\" value\=\"(.*)\" /\>|U', $html, $m );
 906+ $post['wpAutoSummary'] = $m[1];
 907+ if ( $maxlag != null ) {
 908+ $x = $this->http->post( $this->indexurl . '?title=' . urlencode( $page ) . '&action=submit&maxlag=' . urlencode( $maxlag ) . '&bot=' . ( ( $bot == true ) ? '1':'0' ), $post );
 909+ if ( preg_match( '/Waiting for ([^ ]*): ([0-9.-]+) seconds lagged/S', $x, $lagged ) ) {
 910+ global $irc;
 911+ if ( is_resource( $irc ) ) {
 912+ global $irctechchannel;
 913+ foreach ( explode( ',', $irctechchannel ) as $y ) {
 914+ # fwrite($irc,'PRIVMSG '.$y.' :'.$lagged[1].' is lagged out by '.$lagged[2].' seconds. ('.$lagged[0].')'."\n");
 915+ }
 916+ }
 917+ sleep( 10 );
 918+ if ( $mlkg != true ) { return false; }
 919+ else { $x = $this->http->post( $this->indexurl . '?title=' . urlencode( $page ) . '&action=submit&bot=' . ( ( $bot == true ) ? '1':'0' ), $post ); }
 920+ }
 921+ return $x;
 922+ } else {
 923+ return $this->http->post( $this->indexurl . '?title=' . urlencode( $page ) . '&action=submit&bot=' . ( ( $bot == true ) ? '1':'0' ), $post );
 924+ }
 925+ }
 926+
 927+ /**
 928+ * Get a diff.
 929+ * @param $title Page title to get the diff of.
 930+ * @param $oldid Old revision ID.
 931+ * @param $id New revision ID.
 932+ * @param $wait Whether or not to wait for the diff to become available. (Default true)
 933+ * @return Array of added data, removed data, and a rollback token if one was fetchable.
 934+ **/
 935+ function diff ( $title, $oldid, $id, $wait = true ) {
 936+ $deleted = '';
 937+ $added = '';
 938+
 939+ $html = $this->http->get( $this->indexurl . '?title=' . urlencode( $title ) . '&action=render&diff=' . urlencode( $id ) . '&oldid=' . urlencode( $oldid ) . '&diffonly=1' );
 940+
 941+ if ( preg_match_all( '/\&amp\;(oldid\=)(\d*)\\\'\>(Revision as of|Current revision as of)/USs', $html, $m, PREG_SET_ORDER ) ) {
 942+ // print_r($m);
 943+ if ( ( ( $oldid != $m[0][2] ) and ( is_numeric( $oldid ) ) ) or ( ( $id != $m[1][2] ) and ( is_numeric( $id ) ) ) ) {
 944+ if ( $wait == true ) {
 945+ sleep( 1 );
 946+ return $this->diff( $title, $oldid, $id, false );
 947+ } else {
 948+ echo 'OLDID as detected: ' . $m[0][2] . ' Wanted: ' . $oldid . "\n";
 949+ echo 'NEWID as detected: ' . $m[1][2] . ' Wanted: ' . $id . "\n";
 950+ echo $html;
 951+ die( 'Revision error.' . "\n" );
 952+ }
 953+ }
 954+ }
 955+
 956+ if ( preg_match_all( '/\<td class\=(\"|\\\')diff-addedline\1\>\<div\>(.*)\<\/div\>\<\/td\>/USs', $html, $m, PREG_SET_ORDER ) ) {
 957+ // print_r($m);
 958+ foreach ( $m as $x ) {
 959+ $added .= htmlspecialchars_decode( strip_tags( $x[2] ) ) . "\n";
 960+ }
 961+ }
 962+
 963+ if ( preg_match_all( '/\<td class\=(\"|\\\')diff-deletedline\1\>\<div\>(.*)\<\/div\>\<\/td\>/USs', $html, $m, PREG_SET_ORDER ) ) {
 964+ // print_r($m);
 965+ foreach ( $m as $x ) {
 966+ $deleted .= htmlspecialchars_decode( strip_tags( $x[2] ) ) . "\n";
 967+ }
 968+ }
 969+
 970+ // echo $added."\n".$deleted."\n";
 971+
 972+ if ( preg_match( '/action\=rollback\&amp\;from\=.*\&amp\;token\=(.*)\"/US', $html, $m ) ) {
 973+ $rbtoken = $m[1];
 974+ $rbtoken = urldecode( $rbtoken );
 975+// echo 'rbtoken: '.$rbtoken.' -- '; print_r($m); echo "\n\n";
 976+ return array( $added, $deleted, $rbtoken );
 977+ }
 978+
 979+ return array( $added, $deleted );
 980+ }
 981+
 982+ /**
 983+ * Rollback an edit.
 984+ * @param $title Page title to rollback.
 985+ * @param $user Username of last edit to the page to rollback.
 986+ * @param $reason Reason to rollback. If null, default is generated. (Default null)
 987+ * @param $token Rollback token to use. If null, it is fetched. (Default null)
 988+ * @param $bot Whether or not to mark as bot. (Default true)
 989+ * @return HTML or false if failure.
 990+ * @deprecated
 991+ * @see wikipediaapi::rollback
 992+ **/
 993+ function rollback ( $title, $user, $reason = null, $token = null, $bot = true ) {
 994+ if ( ( $token == null ) or ( !$token ) ) {
 995+ $wpapi = new wikipediaapi( $this->apiurl, $this->queryurl, $this->indexurl ); $wpapi->apiurl = str_replace( 'index.php', 'api.php', $this->indexurl );
 996+ $token = $wpapi->revisions( $title, 1, 'older', false, null, true, true );
 997+ if ( $token[0]['user'] == $user ) {
 998+// echo 'Token: '; print_r($token); echo "\n\n";
 999+ $token = $token[0]['rollbacktoken'];
 1000+ } else {
 1001+ return false;
 1002+ }
 1003+ }
 1004+ $x = $this->http->get( $this->indexurl . '?title=' . urlencode( $title ) . '&action=rollback&from=' . urlencode( $user ) . '&token=' . urlencode( $token ) . ( ( $reason != null ) ? '&summary=' . urlencode( $reason ):'' ) . '&bot=' . ( ( $bot == true ) ? '1':'0' ) );
 1005+ # global $logfd; if (!is_resource($logfd)) $logfd = fopen('php://stderr','w'); fwrite($logfd,'Rollback return: '.$x."\n");
 1006+ if ( !preg_match( '/action complete/iS', $x ) ) return false;
 1007+ return $x;
 1008+ }
 1009+
 1010+ /**
 1011+ * Move a page.
 1012+ * @param $old Page title to move.
 1013+ * @param $new New title to move to.
 1014+ * @param $reason Move page summary.
 1015+ * @return HTML page.
 1016+ * @deprecated
 1017+ * @see wikipediaapi::move
 1018+ **/
 1019+ function move ( $old, $new, $reason ) {
 1020+ $wpapi = new wikipediaapi( $this->apiurl, $this->queryurl, $this->indexurl ); $wpapi->apiurl = str_replace( 'index.php', 'api.php', $this->indexurl );
 1021+ if ( ( !$this->edittoken ) or ( $this->edittoken == '' ) ) $this->edittoken = $wpapi->getedittoken();
 1022+
 1023+ $token = htmlspecialchars( $this->edittoken );
 1024+
 1025+ $post = array
 1026+ (
 1027+ 'wpOldTitle' => $old,
 1028+ 'wpNewTitle' => $new,
 1029+ 'wpReason' => $reason,
 1030+ 'wpWatch' => '0',
 1031+ 'wpEditToken' => $token,
 1032+ 'wpMove' => 'Move page'
 1033+ );
 1034+ return $this->http->post( $this->indexurl . '?title=Special:Movepage&action=submit', $post );
 1035+ }
 1036+
 1037+ /**
 1038+ * Uploads a file.
 1039+ * @param $page Name of page on the wiki to upload as.
 1040+ * @param $file Name of local file to upload.
 1041+ * @param $desc Content of the file description page.
 1042+ * @return HTML content.
 1043+ **/
 1044+ function upload ( $page, $file, $desc ) {
 1045+ $post = array
 1046+ (
 1047+ 'wpUploadFile' => '@' . $file,
 1048+ 'wpSourceType' => 'file',
 1049+ 'wpDestFile' => $page,
 1050+ 'wpUploadDescription' => $desc,
 1051+ 'wpLicense' => '',
 1052+ 'wpWatchthis' => '0',
 1053+ 'wpIgnoreWarning' => '1',
 1054+ 'wpUpload' => 'Upload file'
 1055+ );
 1056+ return $this->http->post( $this->indexurl . '?title=Special:Upload&action=submit', $post );
 1057+ }
 1058+
 1059+ /**
 1060+ * Check if a user has email enabled.
 1061+ * @param $user Username to check whether or not the user has email enabled.
 1062+ * @return True or false depending on whether or not the user has email enabled.
 1063+ **/
 1064+ function hasemail ( $user ) {
 1065+ $tmp = $this->http->get( $this->indexurl . '?title=Special:EmailUser&target=' . urlencode( $user ) );
 1066+ if ( stripos( $tmp, "No e-mail address" ) !== false ) return false;
 1067+ return true;
 1068+ }
 1069+
 1070+ /**
 1071+ * Sends an email to a user.
 1072+ * @param $user Username to send email to.
 1073+ * @param $subject Subject of email to send.
 1074+ * @param $body Body of email to send.
 1075+ * @return HTML content.
 1076+ **/
 1077+ function email ( $user, $subject, $body ) {
 1078+ $wpapi = new wikipediaapi( $this->apiurl, $this->queryurl, $this->indexurl ); $wpapi->apiurl = str_replace( 'index.php', 'api.php', $this->indexurl );
 1079+ if ( ( !$this->edittoken ) or ( $this->edittoken == '' ) ) $this->edittoken = $wpapi->getedittoken();
 1080+
 1081+ $post = array
 1082+ (
 1083+ 'wpSubject' => $subject,
 1084+ 'wpText' => $body,
 1085+ 'wpCCMe' => 0,
 1086+ 'wpSend' => 'Send',
 1087+ 'wpEditToken' => $this->edittoken
 1088+ );
 1089+
 1090+ return $this->http->post( $this->indexurl . '?title=Special:EmailUser&target=' . urlencode( $user ) . '&action=submit', $post );
 1091+ }
 1092+ }
\ No newline at end of file

Status & tagging log