Merge "escape HTML elements in docblock with double quotes"
authorAaron Schulz <aschulz@wikimedia.org>
Wed, 18 Jul 2012 19:17:46 +0000 (19:17 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Wed, 18 Jul 2012 19:17:46 +0000 (19:17 +0000)
28 files changed:
1  2 
includes/api/ApiBase.php
includes/api/ApiBlock.php
includes/api/ApiDelete.php
includes/api/ApiMain.php
includes/api/ApiMove.php
includes/api/ApiPageSet.php
includes/api/ApiProtect.php
includes/api/ApiQuery.php
includes/api/ApiQueryAllCategories.php
includes/api/ApiQueryAllLinks.php
includes/api/ApiQueryAllPages.php
includes/api/ApiQueryBase.php
includes/api/ApiQueryCategories.php
includes/api/ApiQueryDeletedrevs.php
includes/api/ApiQueryDuplicateFiles.php
includes/api/ApiQueryIWBacklinks.php
includes/api/ApiQueryIWLinks.php
includes/api/ApiQueryImageInfo.php
includes/api/ApiQueryImages.php
includes/api/ApiQueryInfo.php
includes/api/ApiQueryLangBacklinks.php
includes/api/ApiQueryLinks.php
includes/api/ApiQuerySiteinfo.php
includes/api/ApiQueryUserContributions.php
includes/api/ApiQueryWatchlistRaw.php
includes/api/ApiRollback.php
includes/api/ApiUnblock.php
includes/api/ApiUndelete.php

diff --combined includes/api/ApiBase.php
@@@ -4,7 -4,7 +4,7 @@@
   *
   * Created on Sep 5, 2006
   *
-  * Copyright © 2006, 2010 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+  * Copyright © 2006, 2010 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License as published by
@@@ -134,7 -134,7 +134,7 @@@ abstract class ApiBase extends ContextS
        /**
         * Get the name of the module as shown in the profiler log
         *
 -       * @param $db DatabaseBase
 +       * @param $db DatabaseBase|bool
         *
         * @return string
         */
         *   automated identification of the error, e.g., 'unknown_action'
         * @param $httpRespCode int HTTP response code
         * @param $extradata array Data to add to the "<error>" element; array in ApiResult format
 +       * @throws UsageException
         */
        public function dieUsage( $description, $errorCode, $httpRespCode = 0, $extradata = null ) {
                Profiler::instance()->close();
@@@ -4,7 -4,7 +4,7 @@@
   *
   * Created on Sep 4, 2007
   *
-  * Copyright © 2007 Roan Kattouw <Firstname>.<Lastname>@gmail.com
+  * Copyright © 2007 Roan Kattouw "<Firstname>.<Lastname>@gmail.com"
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License as published by
@@@ -72,9 -72,9 +72,9 @@@ class ApiBlock extends ApiBase 
                $data = array(
                        'Target' => $params['user'],
                        'Reason' => array(
 -                              is_null( $params['reason'] ) ? '' : $params['reason'],
 +                              $params['reason'],
                                'other',
 -                              is_null( $params['reason'] ) ? '' : $params['reason']
 +                              $params['reason']
                        ),
                        'Expiry' => $params['expiry'] == 'never' ? 'infinite' : $params['expiry'],
                        'HardBlock' => !$params['anononly'],
                                ApiBase::PARAM_DEPRECATED => true,
                        ),
                        'expiry' => 'never',
 -                      'reason' => null,
 +                      'reason' => '',
                        'anononly' => false,
                        'nocreate' => false,
                        'autoblock' => false,
                        'token' => 'A block token previously obtained through prop=info',
                        'gettoken' => 'If set, a block token will be returned, and no other action will be taken',
                        'expiry' => 'Relative expiry time, e.g. \'5 months\' or \'2 weeks\'. If set to \'infinite\', \'indefinite\' or \'never\', the block will never expire.',
 -                      'reason' => 'Reason for block (optional)',
 +                      'reason' => 'Reason for block',
                        'anononly' => 'Block anonymous users only (i.e. disable anonymous edits for this IP)',
                        'nocreate' => 'Prevent account creation',
                        'autoblock' => 'Automatically block the last used IP address, and any subsequent IP addresses they try to login from',
@@@ -4,7 -4,7 +4,7 @@@
   *
   * Created on Jun 30, 2007
   *
-  * Copyright © 2007 Roan Kattouw <Firstname>.<Lastname>@gmail.com
+  * Copyright © 2007 Roan Kattouw "<Firstname>.<Lastname>@gmail.com"
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License as published by
@@@ -52,7 -52,7 +52,7 @@@ class ApiDelete extends ApiBase 
                }
  
                $titleObj = $pageObj->getTitle();
 -              $reason = ( isset( $params['reason'] ) ? $params['reason'] : null );
 +              $reason = $params['reason'];
                $user = $this->getUser();
  
                if ( $titleObj->getNamespace() == NS_FILE ) {
diff --combined includes/api/ApiMain.php
@@@ -4,7 -4,7 +4,7 @@@
   *
   * Created on Sep 4, 2006
   *
-  * Copyright © 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+  * Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License as published by
@@@ -967,11 -967,11 +967,11 @@@ class ApiMain extends ApiBase 
        protected function getCredits() {
                return array(
                        'API developers:',
-                       '    Roan Kattouw <Firstname>.<Lastname>@gmail.com (lead developer Sep 2007-present)',
+                       '    Roan Kattouw "<Firstname>.<Lastname>@gmail.com" (lead developer Sep 2007-present)',
                        '    Victor Vasiliev - vasilvv at gee mail dot com',
                        '    Bryan Tong Minh - bryan . tongminh @ gmail . com',
                        '    Sam Reed - sam @ reedyboy . net',
-                       '    Yuri Astrakhan <Firstname><Lastname>@gmail.com (creator, lead developer Sep 2006-Sep 2007)',
+                       '    Yuri Astrakhan "<Firstname><Lastname>@gmail.com" (creator, lead developer Sep 2006-Sep 2007)',
                        '',
                        'Please send your comments, suggestions and questions to mediawiki-api@lists.wikimedia.org',
                        'or file a bug report at https://bugzilla.wikimedia.org/'
  class UsageException extends MWException {
  
        private $mCodestr;
 +
 +      /**
 +       * @var null|array
 +       */
        private $mExtraData;
  
 +      /**
 +       * @param $message string
 +       * @param $codestr string
 +       * @param $code int
 +       * @param $extradata array|null
 +       */
        public function __construct( $message, $codestr, $code = 0, $extradata = null ) {
                parent::__construct( $message, $code );
                $this->mCodestr = $codestr;
diff --combined includes/api/ApiMove.php
@@@ -4,7 -4,7 +4,7 @@@
   *
   * Created on Oct 31, 2007
   *
-  * Copyright © 2007 Roan Kattouw <Firstname>.<Lastname>@gmail.com
+  * Copyright © 2007 Roan Kattouw "<Firstname>.<Lastname>@gmail.com"
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License as published by
@@@ -37,6 -37,9 +37,6 @@@ class ApiMove extends ApiBase 
        public function execute() {
                $user = $this->getUser();
                $params = $this->extractRequestParams();
 -              if ( is_null( $params['reason'] ) ) {
 -                      $params['reason'] = '';
 -              }
  
                $this->requireOnlyOneParameter( $params, 'from', 'fromid' );
  
                                ApiBase::PARAM_REQUIRED => true
                        ),
                        'token' => null,
 -                      'reason' => null,
 +                      'reason' => '',
                        'movetalk' => false,
                        'movesubpages' => false,
                        'noredirect' => false,
                        'fromid' => "Page ID of the page you want to move. Cannot be used together with {$p}from",
                        'to' => 'Title you want to rename the page to',
                        'token' => 'A move token previously retrieved through prop=info',
 -                      'reason' => 'Reason for the move (optional)',
 +                      'reason' => 'Reason for the move',
                        'movetalk' => 'Move the talk page, if it exists',
                        'movesubpages' => 'Move subpages, if applicable',
                        'noredirect' => 'Don\'t create a redirect',
@@@ -4,7 -4,7 +4,7 @@@
   *
   * Created on Sep 24, 2006
   *
-  * Copyright © 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+  * Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License as published by
@@@ -52,7 -52,7 +52,7 @@@ class ApiPageSet extends ApiQueryBase 
  
        /**
         * Constructor
 -       * @param $query ApiQueryBase
 +       * @param $query ApiBase
         * @param $resolveRedirects bool Whether redirects should be resolved
         * @param $convertTitles bool
         */
@@@ -4,7 -4,7 +4,7 @@@
   *
   * Created on Sep 1, 2007
   *
-  * Copyright © 2007 Roan Kattouw <Firstname>.<Lastname>@gmail.com
+  * Copyright © 2007 Roan Kattouw "<Firstname>.<Lastname>@gmail.com"
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License as published by
@@@ -176,7 -176,7 +176,7 @@@ class ApiProtect extends ApiBase 
                        'protections' => 'Pipe-separated list of protection levels, formatted action=group (e.g. edit=sysop)',
                        'expiry' => array( 'Expiry timestamps. If only one timestamp is set, it\'ll be used for all protections.',
                                        'Use \'infinite\', \'indefinite\' or \'never\', for a neverexpiring protection.' ),
 -                      'reason' => 'Reason for (un)protecting (optional)',
 +                      'reason' => 'Reason for (un)protecting',
                        'cascade' => array( 'Enable cascading protection (i.e. protect pages included in this page)',
                                        'Ignored if not all protection levels are \'sysop\' or \'protect\'' ),
                        'watch' => 'If set, add the page being (un)protected to your watchlist',
@@@ -4,7 -4,7 +4,7 @@@
   *
   * Created on Sep 7, 2006
   *
-  * Copyright © 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+  * Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License as published by
@@@ -103,10 -103,6 +103,10 @@@ class ApiQuery extends ApiBase 
  
        protected $mAllowedGenerators = array();
  
 +      /**
 +       * @param $main ApiMain
 +       * @param $action string
 +       */
        public function __construct( $main, $action ) {
                parent::__construct( $main, $action );
  
                return null;
        }
  
 +      /**
 +       * @return ApiFormatRaw|null
 +       */
        public function getCustomPrinter() {
                // If &exportnowrap is set, use the raw formatter
                if ( $this->getParameter( 'export' ) &&
                $this->outputGeneralPageInfo();
  
                // Execute all requested modules.
 +              /**
 +               * @var $module ApiQueryBase
 +               */
                foreach ( $modules as $module ) {
                        $params = $module->extractRequestParams();
                        $cacheMode = $this->mergeCacheMode(
         */
        private function addCustomFldsToPageSet( $modules, $pageSet ) {
                // Query all requested modules.
 +              /**
 +               * @var $module ApiQueryBase
 +               */
                foreach ( $modules as $module ) {
                        $module->requestExtraData( $pageSet );
                }
  
                // Show redirect information
                $redirValues = array();
 +              /**
 +               * @var $titleTo Title
 +               */
                foreach ( $pageSet->getRedirectTitles() as $titleStrFrom => $titleTo ) {
                        $r = array(
                                'from' => strval( $titleStrFrom ),
@@@ -4,7 -4,7 +4,7 @@@
   *
   * Created on December 12, 2007
   *
-  * Copyright © 2007 Roan Kattouw <Firstname>.<Lastname>@gmail.com
+  * Copyright © 2007 Roan Kattouw "<Firstname>.<Lastname>@gmail.com"
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License as published by
@@@ -59,17 -59,6 +59,17 @@@ class ApiQueryAllCategories extends Api
                $this->addFields( 'cat_title' );
                $this->addWhere( 'cat_pages > 0' );
  
 +              if ( !is_null( $params['continue'] ) ) {
 +                      $cont = explode( '|', $params['continue'] );
 +                      if ( count( $cont ) != 1 ) {
 +                              $this->dieUsage( "Invalid continue param. You should pass the " .
 +                                      "original value returned by the previous query", "_badcontinue" );
 +                      }
 +                      $op = $params['dir'] == 'descending' ? '<' : '>';
 +                      $cont_from = $db->addQuotes( $cont[0] );
 +                      $this->addWhere( "cat_title $op= $cont_from" );
 +              }
 +
                $dir = ( $params['dir'] == 'descending' ? 'older' : 'newer' );
                $from = ( is_null( $params['from'] ) ? null : $this->titlePartToKey( $params['from'] ) );
                $to = ( is_null( $params['to'] ) ? null : $this->titlePartToKey( $params['to'] ) );
                foreach ( $res as $row ) {
                        if ( ++ $count > $params['limit'] ) {
                                // We've reached the one extra which shows that there are additional cats to be had. Stop here...
 -                              // TODO: Security issue - if the user has no right to view next title, it will still be shown
 -                              $this->setContinueEnumParameter( 'from', $this->keyToTitle( $row->cat_title ) );
 +                              $this->setContinueEnumParameter( 'continue', $row->cat_title );
                                break;
                        }
  
                                }
                                $fit = $result->addValue( array( 'query', $this->getModuleName() ), null, $item );
                                if ( !$fit ) {
 -                                      $this->setContinueEnumParameter( 'from', $this->keyToTitle( $row->cat_title ) );
 +                                      $this->setContinueEnumParameter( 'continue', $row->cat_title );
                                        break;
                                }
                        }
        public function getAllowedParams() {
                return array(
                        'from' => null,
 +                      'continue' => null,
                        'to' => null,
                        'prefix' => null,
                        'dir' => array(
        public function getParamDescription() {
                return array(
                        'from' => 'The category to start enumerating from',
 +                      'continue' => 'When more results are available, use this to continue',
                        'to' => 'The category to stop enumerating at',
                        'prefix' => 'Search for all category titles that begin with this value',
                        'dir' => 'Direction to sort in',
                return 'Enumerate all categories';
        }
  
 +      public function getPossibleErrors() {
 +              return array_merge( parent::getPossibleErrors(), array(
 +                      array( 'code' => '_badcontinue', 'info' => 'Invalid continue param. You should pass the original value returned by the previous query' ),
 +              ) );
 +      }
 +
        public function getExamples() {
                return array(
                        'api.php?action=query&list=allcategories&acprop=size',
@@@ -4,7 -4,7 +4,7 @@@
   *
   * Created on July 7, 2007
   *
-  * Copyright © 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+  * Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License as published by
@@@ -77,25 -77,17 +77,25 @@@ class ApiQueryAllLinks extends ApiQuery
                }
                if ( !is_null( $params['continue'] ) ) {
                        $continueArr = explode( '|', $params['continue'] );
 -                      if ( count( $continueArr ) != 2 ) {
 -                              $this->dieUsage( 'Invalid continue parameter', 'badcontinue' );
 -                      }
                        $op = $params['dir'] == 'descending' ? '<' : '>';
 -                      $continueTitle = $db->addQuotes( $this->titleToKey( $continueArr[0] ) );
 -                      $continueFrom = intval( $continueArr[1] );
 -                      $this->addWhere(
 -                              "pl_title $op $continueTitle OR " .
 -                              "(pl_title = $continueTitle AND " .
 -                              "pl_from $op= $continueFrom)"
 -                      );
 +                      if ( $params['unique'] ) {
 +                              if ( count( $continueArr ) != 1 ) {
 +                                      $this->dieUsage( 'Invalid continue parameter', 'badcontinue' );
 +                              }
 +                              $continueTitle = $db->addQuotes( $continueArr[0] );
 +                              $this->addWhere( "pl_title $op= $continueTitle" );
 +                      } else {
 +                              if ( count( $continueArr ) != 2 ) {
 +                                      $this->dieUsage( 'Invalid continue parameter', 'badcontinue' );
 +                              }
 +                              $continueTitle = $db->addQuotes( $continueArr[0] );
 +                              $continueFrom = intval( $continueArr[1] );
 +                              $this->addWhere(
 +                                      "pl_title $op $continueTitle OR " .
 +                                      "(pl_title = $continueTitle AND " .
 +                                      "pl_from $op= $continueFrom)"
 +                              );
 +                      }
                }
  
                $from = ( is_null( $params['from'] ) ? null : $this->titlePartToKey( $params['from'] ) );
                foreach ( $res as $row ) {
                        if ( ++ $count > $limit ) {
                                // We've reached the one extra which shows that there are additional pages to be had. Stop here...
 -                              // TODO: Security issue - if the user has no right to view next title, it will still be shown
                                if ( $params['unique'] ) {
 -                                      $this->setContinueEnumParameter( 'from', $this->keyToTitle( $row->pl_title ) );
 +                                      $this->setContinueEnumParameter( 'continue', $row->pl_title );
                                } else {
 -                                      $this->setContinueEnumParameter( 'continue', $this->keyToTitle( $row->pl_title ) . "|" . $row->pl_from );
 +                                      $this->setContinueEnumParameter( 'continue', $row->pl_title . "|" . $row->pl_from );
                                }
                                break;
                        }
                                $fit = $result->addValue( array( 'query', $this->getModuleName() ), null, $vals );
                                if ( !$fit ) {
                                        if ( $params['unique'] ) {
 -                                              $this->setContinueEnumParameter( 'from', $this->keyToTitle( $row->pl_title ) );
 +                                              $this->setContinueEnumParameter( 'continue', $row->pl_title );
                                        } else {
 -                                              $this->setContinueEnumParameter( 'continue', $this->keyToTitle( $row->pl_title ) . "|" . $row->pl_from );
 +                                              $this->setContinueEnumParameter( 'continue', $row->pl_title . "|" . $row->pl_from );
                                        }
                                        break;
                                }
@@@ -4,7 -4,7 +4,7 @@@
   *
   * Created on Sep 25, 2006
   *
-  * Copyright © 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+  * Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License as published by
@@@ -67,17 -67,6 +67,17 @@@ class ApiQueryAllPages extends ApiQuery
                // Page filters
                $this->addTables( 'page' );
  
 +              if ( !is_null( $params['continue'] ) ) {
 +                      $cont = explode( '|', $params['continue'] );
 +                      if ( count( $cont ) != 1 ) {
 +                              $this->dieUsage( "Invalid continue param. You should pass the " .
 +                                      "original value returned by the previous query", "_badcontinue" );
 +                      }
 +                      $op = $params['dir'] == 'descending' ? '<' : '>';
 +                      $cont_from = $db->addQuotes( $cont[0] );
 +                      $this->addWhere( "page_title $op= $cont_from" );
 +              }
 +
                if ( $params['filterredir'] == 'redirects' ) {
                        $this->addWhereFld( 'page_is_redirect', 1 );
                } elseif ( $params['filterredir'] == 'nonredirects' ) {
                foreach ( $res as $row ) {
                        if ( ++ $count > $limit ) {
                                // We've reached the one extra which shows that there are additional pages to be had. Stop here...
 -                              // TODO: Security issue - if the user has no right to view next title, it will still be shown
 -                              $this->setContinueEnumParameter( 'from', $this->keyToTitle( $row->page_title ) );
 +                              $this->setContinueEnumParameter( 'continue', $row->page_title );
                                break;
                        }
  
                                );
                                $fit = $result->addValue( array( 'query', $this->getModuleName() ), null, $vals );
                                if ( !$fit ) {
 -                                      $this->setContinueEnumParameter( 'from', $this->keyToTitle( $row->page_title ) );
 +                                      $this->setContinueEnumParameter( 'continue', $row->page_title );
                                        break;
                                }
                        } else {
  
                return array(
                        'from' => null,
 +                      'continue' => null,
                        'to' => null,
                        'prefix' => null,
                        'namespace' => array(
                $p = $this->getModulePrefix();
                return array(
                        'from' => 'The page title to start enumerating from',
 +                      'continue' => 'When more results are available, use this to continue',
                        'to' => 'The page title to stop enumerating at',
                        'prefix' => 'Search for all page titles that begin with this value',
                        'namespace' => 'The namespace to enumerate',
                return array_merge( parent::getPossibleErrors(), array(
                        array( 'code' => 'params', 'info' => 'Use "gapfilterredir=nonredirects" option instead of "redirects" when using allpages as a generator' ),
                        array( 'code' => 'params', 'info' => 'prlevel may not be used without prtype' ),
 +                      array( 'code' => '_badcontinue', 'info' => 'Invalid continue param. You should pass the original value returned by the previous query' ),
                ) );
        }
  
@@@ -4,7 -4,7 +4,7 @@@
   *
   * Created on Sep 7, 2006
   *
-  * Copyright © 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+  * Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License as published by
@@@ -571,11 -571,6 +571,11 @@@ abstract class ApiQueryGeneratorBase ex
  
        private $mIsGenerator;
  
 +      /**
 +       * @param $query ApiBase
 +       * @param $moduleName string
 +       * @param $paramPrefix string
 +       */
        public function __construct( $query, $moduleName, $paramPrefix = '' ) {
                parent::__construct( $query, $moduleName, $paramPrefix );
                $this->mIsGenerator = false;
@@@ -4,7 -4,7 +4,7 @@@
   *
   * Created on May 13, 2007
   *
-  * Copyright © 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+  * Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License as published by
@@@ -91,7 -91,7 +91,7 @@@ class ApiQueryCategories extends ApiQue
                        }
                        $op = $params['dir'] == 'descending' ? '<' : '>';
                        $clfrom = intval( $cont[0] );
 -                      $clto = $this->getDB()->addQuotes( $this->titleToKey( $cont[1] ) );
 +                      $clto = $this->getDB()->addQuotes( $cont[1] );
                        $this->addWhere(
                                "cl_from $op $clfrom OR " .
                                "(cl_from = $clfrom AND " .
                                if ( ++$count > $params['limit'] ) {
                                        // We've reached the one extra which shows that
                                        // there are additional pages to be had. Stop here...
 -                                      $this->setContinueEnumParameter( 'continue', $row->cl_from .
 -                                                      '|' . $this->keyToTitle( $row->cl_to ) );
 +                                      $this->setContinueEnumParameter( 'continue', $row->cl_from . '|' . $row->cl_to );
                                        break;
                                }
  
  
                                $fit = $this->addPageSubItem( $row->cl_from, $vals );
                                if ( !$fit ) {
 -                                      $this->setContinueEnumParameter( 'continue', $row->cl_from .
 -                                                      '|' . $this->keyToTitle( $row->cl_to ) );
 +                                      $this->setContinueEnumParameter( 'continue', $row->cl_from . '|' . $row->cl_to );
                                        break;
                                }
                        }
                                if ( ++$count > $params['limit'] ) {
                                        // We've reached the one extra which shows that
                                        // there are additional pages to be had. Stop here...
 -                                      $this->setContinueEnumParameter( 'continue', $row->cl_from .
 -                                                      '|' . $this->keyToTitle( $row->cl_to ) );
 +                                      $this->setContinueEnumParameter( 'continue', $row->cl_from . '|' . $row->cl_to );
                                        break;
                                }
  
@@@ -4,7 -4,7 +4,7 @@@
   *
   * Created on Jul 2, 2007
   *
-  * Copyright © 2007 Roan Kattouw <Firstname>.<Lastname>@gmail.com
+  * Copyright © 2007 Roan Kattouw "<Firstname>.<Lastname>@gmail.com"
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License as published by
@@@ -164,7 -164,7 +164,7 @@@ class ApiQueryDeletedrevs extends ApiQu
                                $this->dieUsage( 'Invalid continue param. You should pass the original value returned by the previous query', 'badcontinue' );
                        }
                        $ns = intval( $cont[0] );
 -                      $title = $db->addQuotes( $this->titleToKey( $cont[1] ) );
 +                      $title = $db->addQuotes( $cont[1] );
                        $ts = $db->addQuotes( $db->timestamp( $cont[2] ) );
                        $op = ( $dir == 'newer' ? '>' : '<' );
                        $this->addWhere( "ar_namespace $op $ns OR " .
                                // We've had enough
                                if ( $mode == 'all' || $mode == 'revs' ) {
                                        $this->setContinueEnumParameter( 'continue', intval( $row->ar_namespace ) . '|' .
 -                                              $this->keyToTitle( $row->ar_title ) . '|' . $row->ar_timestamp );
 +                                              $row->ar_title . '|' . $row->ar_timestamp );
                                } else {
                                        $this->setContinueEnumParameter( 'start', wfTimestamp( TS_ISO_8601, $row->ar_timestamp ) );
                                }
                        if ( !$fit ) {
                                if ( $mode == 'all' || $mode == 'revs' ) {
                                        $this->setContinueEnumParameter( 'continue', intval( $row->ar_namespace ) . '|' .
 -                                              $this->keyToTitle( $row->ar_title ) . '|' . $row->ar_timestamp );
 +                                              $row->ar_title . '|' . $row->ar_timestamp );
                                } else {
                                        $this->setContinueEnumParameter( 'start', wfTimestamp( TS_ISO_8601, $row->ar_timestamp ) );
                                }
@@@ -4,7 -4,7 +4,7 @@@
   *
   * Created on Sep 27, 2008
   *
-  * Copyright © 2008 Roan Kattouw <Firstname>.<Lastname>@gmail.com
+  * Copyright © 2008 Roan Kattouw "<Firstname>.<Lastname>@gmail.com"
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License as published by
@@@ -82,8 -82,8 +82,8 @@@ class ApiQueryDuplicateFiles extends Ap
                        }
                        $op = $params['dir'] == 'descending' ? '<' : '>';
                        $db = $this->getDB();
 -                      $orig = $db->addQuotes( $this->titleTokey( $cont[0] ) );
 -                      $dup = $db->addQuotes( $this->titleToKey( $cont[1] ) );
 +                      $orig = $db->addQuotes( $cont[0] );
 +                      $dup = $db->addQuotes( $cont[1] );
                        $this->addWhere(
                                "i1.img_name $op $orig OR " .
                                "(i1.img_name = $orig AND " .
                        if ( ++$count > $params['limit'] ) {
                                // We've reached the one extra which shows that
                                // there are additional pages to be had. Stop here...
 -                              $this->setContinueEnumParameter( 'continue',
 -                                      $this->keyToTitle( $row->orig_name ) . '|' .
 -                                      $this->keyToTitle( $row->dup_name ) );
 +                              $this->setContinueEnumParameter( 'continue', $row->orig_name . '|' . $row->dup_name );
                                break;
                        }
                        if ( !is_null( $resultPageSet ) ) {
                                );
                                $fit = $this->addPageSubItem( $images[$row->orig_name], $r );
                                if ( !$fit ) {
 -                                      $this->setContinueEnumParameter( 'continue',
 -                                                      $this->keyToTitle( $row->orig_name ) . '|' .
 -                                                      $this->keyToTitle( $row->dup_name ) );
 +                                      $this->setContinueEnumParameter( 'continue', $row->orig_name . '|' . $row->dup_name );
                                        break;
                                }
                        }
@@@ -5,7 -5,7 +5,7 @@@
   * Created on May 14, 2010
   *
   * Copyright © 2010 Sam Reed
-  * Copyright © 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+  * Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License as published by
@@@ -64,7 -64,7 +64,7 @@@ class ApiQueryIWBacklinks extends ApiQu
                        $db = $this->getDB();
                        $op = $params['dir'] == 'descending' ? '<' : '>';
                        $prefix = $db->addQuotes( $cont[0] );
 -                      $title = $db->addQuotes( $this->titleToKey( $cont[1] ) );
 +                      $title = $db->addQuotes( $cont[1] );
                        $from = intval( $cont[2] );
                        $this->addWhere(
                                "iwl_prefix $op $prefix OR " .
@@@ -5,7 -5,7 +5,7 @@@
   * Created on May 14, 2010
   *
   * Copyright © 2010 Sam Reed
-  * Copyright © 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+  * Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License as published by
@@@ -66,7 -66,7 +66,7 @@@ class ApiQueryIWLinks extends ApiQueryB
                        $db = $this->getDB();
                        $iwlfrom = intval( $cont[0] );
                        $iwlprefix = $db->addQuotes( $cont[1] );
 -                      $iwltitle = $db->addQuotes( $this->titleToKey( $cont[2] ) );
 +                      $iwltitle = $db->addQuotes( $cont[2] );
                        $this->addWhere(
                                "iwl_from $op $iwlfrom OR " .
                                "(iwl_from = $iwlfrom AND " .
@@@ -4,7 -4,7 +4,7 @@@
   *
   * Created on July 6, 2007
   *
-  * Copyright © 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+  * Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License as published by
@@@ -490,7 -490,7 +490,7 @@@ class ApiQueryImageInfo extends ApiQuer
         *
         * @return array
         */
 -      private static function getProperties() {
 +      private static function getProperties( $modulePrefix = '' ) {
                return array(
                        'timestamp' =>      ' timestamp     - Adds timestamp for the uploaded version',
                        'user' =>           ' user          - Adds the user who uploaded the image version',
                        'dimensions' =>     ' dimensions    - Alias for size', // For backwards compatibility with Allimages
                        'sha1' =>           ' sha1          - Adds SHA-1 hash for the image',
                        'mime' =>           ' mime          - Adds MIME type of the image',
 -                      'thumbmime' =>      ' thumbmime     - Adds MIME type of the image thumbnail (requires url)',
 +                      'thumbmime' =>      ' thumbmime     - Adds MIME type of the image thumbnail' .
 +                              ' (requires url and param ' . $modulePrefix . 'urlwidth)',
                        'mediatype' =>      ' mediatype     - Adds the media type of the image',
                        'metadata' =>       ' metadata      - Lists EXIF metadata for the version of the image',
                        'archivename' =>    ' archivename   - Adds the file name of the archive version for non-latest versions',
         *
         * @return array
         */
 -      public static function getPropertyDescriptions( $filter = array() ) {
 +      public static function getPropertyDescriptions( $filter = array(), $modulePrefix = '' ) {
                return array_merge(
                        array( 'What image information to get:' ),
 -                      array_values( array_diff_key( self::getProperties(), array_flip( $filter ) ) )
 +                      array_values( array_diff_key( self::getProperties( $modulePrefix ), array_flip( $filter ) ) )
                );
        }
  
        public function getParamDescription() {
                $p = $this->getModulePrefix();
                return array(
 -                      'prop' => self::getPropertyDescriptions(),
 +                      'prop' => self::getPropertyDescriptions( array(), $p ),
                        'urlwidth' => array( "If {$p}prop=url is set, a URL to an image scaled to this width will be returned.",
                                            'Only the current version of the image can be scaled' ),
                        'urlheight' => "Similar to {$p}urlwidth. Cannot be used without {$p}urlwidth",
@@@ -4,7 -4,7 +4,7 @@@
   *
   * Created on May 13, 2007
   *
-  * Copyright © 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+  * Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License as published by
@@@ -68,7 -68,7 +68,7 @@@ class ApiQueryImages extends ApiQueryGe
                        }
                        $op = $params['dir'] == 'descending' ? '<' : '>';
                        $ilfrom = intval( $cont[0] );
 -                      $ilto = $this->getDB()->addQuotes( $this->titleToKey( $cont[1] ) );
 +                      $ilto = $this->getDB()->addQuotes( $cont[1] );
                        $this->addWhere(
                                "il_from $op $ilfrom OR " .
                                "(il_from = $ilfrom AND " .
                                if ( ++$count > $params['limit'] ) {
                                        // We've reached the one extra which shows that
                                        // there are additional pages to be had. Stop here...
 -                                      $this->setContinueEnumParameter( 'continue', $row->il_from .
 -                                                      '|' . $this->keyToTitle( $row->il_to ) );
 +                                      $this->setContinueEnumParameter( 'continue', $row->il_from . '|' . $row->il_to );
                                        break;
                                }
                                $vals = array();
                                ApiQueryBase::addTitleInfo( $vals, Title::makeTitle( NS_FILE, $row->il_to ) );
                                $fit = $this->addPageSubItem( $row->il_from, $vals );
                                if ( !$fit ) {
 -                                      $this->setContinueEnumParameter( 'continue', $row->il_from .
 -                                                      '|' . $this->keyToTitle( $row->il_to ) );
 +                                      $this->setContinueEnumParameter( 'continue', $row->il_from . '|' . $row->il_to );
                                        break;
                                }
                        }
                                if ( ++$count > $params['limit'] ) {
                                        // We've reached the one extra which shows that
                                        // there are additional pages to be had. Stop here...
 -                                      $this->setContinueEnumParameter( 'continue', $row->il_from .
 -                                                      '|' . $this->keyToTitle( $row->il_to ) );
 +                                      $this->setContinueEnumParameter( 'continue', $row->il_from . '|' . $row->il_to );
                                        break;
                                }
                                $titles[] = Title::makeTitle( NS_FILE, $row->il_to );
@@@ -4,7 -4,7 +4,7 @@@
   *
   * Created on Sep 25, 2006
   *
-  * Copyright © 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+  * Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License as published by
@@@ -57,10 -57,7 +57,10 @@@ class ApiQueryInfo extends ApiQueryBas
                global $wgDisableCounters;
  
                $pageSet->requestField( 'page_restrictions' );
 -              $pageSet->requestField( 'page_is_redirect' );
 +              // when resolving redirects, no page will have this field
 +              if( !$pageSet->isResolvingRedirects() ) {
 +                      $pageSet->requestField( 'page_is_redirect' );
 +              }
                $pageSet->requestField( 'page_is_new' );
                if ( !$wgDisableCounters ) {
                        $pageSet->requestField( 'page_counter' );
                }
  
                $this->pageRestrictions = $pageSet->getCustomField( 'page_restrictions' );
 -              $this->pageIsRedir = $pageSet->getCustomField( 'page_is_redirect' );
 +              // when resolving redirects, no page will have this field
 +              $this->pageIsRedir = !$pageSet->isResolvingRedirects()
 +                      ? $pageSet->getCustomField( 'page_is_redirect' )
 +                      : array();
                $this->pageIsNew = $pageSet->getCustomField( 'page_is_new' );
  
                global $wgDisableCounters;
                                : intval( $this->pageCounter[$pageid] );
                        $pageInfo['length'] = intval( $this->pageLength[$pageid] );
  
 -                      if ( $this->pageIsRedir[$pageid] ) {
 +                      if ( isset( $this->pageIsRedir[$pageid] ) && $this->pageIsRedir[$pageid] ) {
                                $pageInfo['redirect'] = '';
                        }
                        if ( $this->pageIsNew[$pageid] ) {
@@@ -5,7 -5,7 +5,7 @@@
   * Created on May 14, 2011
   *
   * Copyright © 2011 Sam Reed
-  * Copyright © 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+  * Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License as published by
@@@ -64,7 -64,7 +64,7 @@@ class ApiQueryLangBacklinks extends Api
                        $db = $this->getDB();
                        $op = $params['dir'] == 'descending' ? '<' : '>';
                        $prefix = $db->addQuotes( $cont[0] );
 -                      $title = $db->addQuotes( $this->titleToKey( $cont[1] ) );
 +                      $title = $db->addQuotes( $cont[1] );
                        $from = intval( $cont[2] );
                        $this->addWhere(
                                "ll_lang $op $prefix OR " .
@@@ -4,7 -4,7 +4,7 @@@
   *
   * Created on May 12, 2007
   *
-  * Copyright © 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+  * Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License as published by
@@@ -119,7 -119,7 +119,7 @@@ class ApiQueryLinks extends ApiQueryGen
                        $op = $params['dir'] == 'descending' ? '<' : '>';
                        $plfrom = intval( $cont[0] );
                        $plns = intval( $cont[1] );
 -                      $pltitle = $this->getDB()->addQuotes( $this->titleToKey( $cont[2] ) );
 +                      $pltitle = $this->getDB()->addQuotes( $cont[2] );
                        $this->addWhere(
                                "{$this->prefix}_from $op $plfrom OR " .
                                "({$this->prefix}_from = $plfrom AND " .
                                        // We've reached the one extra which shows that
                                        // there are additional pages to be had. Stop here...
                                        $this->setContinueEnumParameter( 'continue',
 -                                              "{$row->pl_from}|{$row->pl_namespace}|" .
 -                                              $this->keyToTitle( $row->pl_title ) );
 +                                              "{$row->pl_from}|{$row->pl_namespace}|{$row->pl_title}" );
                                        break;
                                }
                                $vals = array();
                                $fit = $this->addPageSubItem( $row->pl_from, $vals );
                                if ( !$fit ) {
                                        $this->setContinueEnumParameter( 'continue',
 -                                              "{$row->pl_from}|{$row->pl_namespace}|" .
 -                                              $this->keyToTitle( $row->pl_title ) );
 +                                              "{$row->pl_from}|{$row->pl_namespace}|{$row->pl_title}" );
                                        break;
                                }
                        }
                                        // We've reached the one extra which shows that
                                        // there are additional pages to be had. Stop here...
                                        $this->setContinueEnumParameter( 'continue',
 -                                              "{$row->pl_from}|{$row->pl_namespace}|" .
 -                                              $this->keyToTitle( $row->pl_title ) );
 +                                              "{$row->pl_from}|{$row->pl_namespace}|{$row->pl_title}" );
                                        break;
                                }
                                $titles[] = Title::makeTitle( $row->pl_namespace, $row->pl_title );
@@@ -4,7 -4,7 +4,7 @@@
   *
   * Created on Sep 25, 2006
   *
-  * Copyright © 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+  * Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License as published by
@@@ -257,8 -257,9 +257,8 @@@ class ApiQuerySiteinfo extends ApiQuery
        protected function appendSpecialPageAliases( $property ) {
                global $wgContLang;
                $data = array();
 -              $aliases = $wgContLang->getSpecialPageAliases();
 -              foreach ( SpecialPageFactory::getList() as $specialpage => $stuff ) {
 -                      $arr = array( 'realname' => $specialpage, 'aliases' => $aliases[$specialpage] );
 +              foreach ( $wgContLang->getSpecialPageAliases() as $specialpage => $aliases ) {
 +                      $arr = array( 'realname' => $specialpage, 'aliases' => $aliases );
                        $this->getResult()->setIndexedTagName( $arr['aliases'], 'alias' );
                        $data[] = $arr;
                }
@@@ -4,7 -4,7 +4,7 @@@
   *
   * Created on Oct 16, 2006
   *
-  * Copyright © 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+  * Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License as published by
@@@ -256,7 -256,7 +256,7 @@@ class ApiQueryContributions extends Api
                $this->addFieldsIf( 'page_latest', $this->fld_flags );
                // $this->addFieldsIf( 'rev_text_id', $this->fld_ids ); // Should this field be exposed?
                $this->addFieldsIf( 'rev_comment', $this->fld_comment || $this->fld_parsedcomment );
 -              $this->addFieldsIf( 'rev_len', $this->fld_size );
 +              $this->addFieldsIf( 'rev_len', $this->fld_size || $this->fld_sizediff );
                $this->addFieldsIf( 'rev_minor_edit', $this->fld_flags );
                $this->addFieldsIf( 'rev_parent_id', $this->fld_flags || $this->fld_sizediff );
                $this->addFieldsIf( 'rc_patrolled', $this->fld_patrolled );
@@@ -4,7 -4,7 +4,7 @@@
   *
   * Created on Oct 4, 2008
   *
-  * Copyright © 2008 Roan Kattouw <Firstname>.<Lastname>@gmail.com
+  * Copyright © 2008 Roan Kattouw "<Firstname>.<Lastname>@gmail.com"
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License as published by
@@@ -76,7 -76,7 +76,7 @@@ class ApiQueryWatchlistRaw extends ApiQ
                                        "original value returned by the previous query", "_badcontinue" );
                        }
                        $ns = intval( $cont[0] );
 -                      $title = $this->getDB()->addQuotes( $this->titleToKey( $cont[1] ) );
 +                      $title = $this->getDB()->addQuotes( $cont[1] );
                        $op = $params['dir'] == 'ascending' ? '>' : '<';
                        $this->addWhere(
                                "wl_namespace $op $ns OR " .
                foreach ( $res as $row ) {
                        if ( ++$count > $params['limit'] ) {
                                // We've reached the one extra which shows that there are additional pages to be had. Stop here...
 -                              $this->setContinueEnumParameter( 'continue', $row->wl_namespace . '|' .
 -                                                                      $this->keyToTitle( $row->wl_title ) );
 +                              $this->setContinueEnumParameter( 'continue', $row->wl_namespace . '|' . $row->wl_title );
                                break;
                        }
                        $t = Title::makeTitle( $row->wl_namespace, $row->wl_title );
                                }
                                $fit = $this->getResult()->addValue( $this->getModuleName(), null, $vals );
                                if ( !$fit ) {
 -                                      $this->setContinueEnumParameter( 'continue', $row->wl_namespace . '|' .
 -                                                                      $this->keyToTitle( $row->wl_title ) );
 +                                      $this->setContinueEnumParameter( 'continue', $row->wl_namespace . '|' . $row->wl_title );
                                        break;
                                }
                        } else {
@@@ -4,7 -4,7 +4,7 @@@
   *
   * Created on Jun 20, 2007
   *
-  * Copyright © 2007 Roan Kattouw <Firstname>.<Lastname>@gmail.com
+  * Copyright © 2007 Roan Kattouw "<Firstname>.<Lastname>@gmail.com"
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License as published by
@@@ -49,7 -49,7 +49,7 @@@ class ApiRollback extends ApiBase 
                // User and title already validated in call to getTokenSalt from Main
                $titleObj = $this->getRbTitle();
                $pageObj = WikiPage::factory( $titleObj );
 -              $summary = ( isset( $params['summary'] ) ? $params['summary'] : '' );
 +              $summary = $params['summary'];
                $details = array();
                $retval = $pageObj->doRollback( $this->getRbUser(), $summary, $params['token'], $params['markbot'], $details, $this->getUser() );
  
@@@ -91,7 -91,7 +91,7 @@@
                                ApiBase::PARAM_REQUIRED => true
                        ),
                        'token' => null,
 -                      'summary' => null,
 +                      'summary' => '',
                        'markbot' => false,
                        'watchlist' => array(
                                ApiBase::PARAM_DFLT => 'preferences',
                        'title' => 'Title of the page you want to rollback.',
                        'user' => 'Name of the user whose edits are to be rolled back. If set incorrectly, you\'ll get a badtoken error.',
                        'token' => "A rollback token previously retrieved through {$this->getModulePrefix()}prop=revisions",
 -                      'summary' => 'Custom edit summary. If not set, default summary will be used',
 +                      'summary' => 'Custom edit summary. If empty, default summary will be used',
                        'markbot' => 'Mark the reverted edits and the revert as bot edits',
                        'watchlist' => 'Unconditionally add or remove the page from your watchlist, use preferences or do not change watch',
                );
@@@ -4,7 -4,7 +4,7 @@@
   *
   * Created on Sep 7, 2007
   *
-  * Copyright © 2007 Roan Kattouw <Firstname>.<Lastname>@gmail.com
+  * Copyright © 2007 Roan Kattouw "<Firstname>.<Lastname>@gmail.com"
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License as published by
@@@ -69,7 -69,7 +69,7 @@@ class ApiUnblock extends ApiBase 
  
                $data = array(
                        'Target' => is_null( $params['id'] ) ? $params['user'] : "#{$params['id']}",
 -                      'Reason' => is_null( $params['reason'] ) ? '' : $params['reason']
 +                      'Reason' => $params['reason']
                );
                $block = Block::newFromTarget( $data['Target'] );
                $retval = SpecialUnblock::processUnblock( $data, $this->getContext() );
                                ApiBase::PARAM_DFLT => false,
                                ApiBase::PARAM_DEPRECATED => true,
                        ),
 -                      'reason' => null,
 +                      'reason' => '',
                );
        }
  
                        'user' => "Username, IP address or IP range you want to unblock. Cannot be used together with {$p}id",
                        'token' => "An unblock token previously obtained through prop=info",
                        'gettoken' => 'If set, an unblock token will be returned, and no other action will be taken',
 -                      'reason' => 'Reason for unblock (optional)',
 +                      'reason' => 'Reason for unblock',
                );
        }
  
@@@ -4,7 -4,7 +4,7 @@@
   *
   * Created on Jul 3, 2007
   *
-  * Copyright © 2007 Roan Kattouw <Firstname>.<Lastname>@gmail.com
+  * Copyright © 2007 Roan Kattouw "<Firstname>.<Lastname>@gmail.com"
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License as published by
@@@ -116,7 -116,7 +116,7 @@@ class ApiUndelete extends ApiBase 
                return array(
                        'title' => 'Title of the page you want to restore',
                        'token' => 'An undelete token previously retrieved through list=deletedrevs',
 -                      'reason' => 'Reason for restoring (optional)',
 +                      'reason' => 'Reason for restoring',
                        'timestamps' => 'Timestamps of the revisions to restore. If not set, all revisions will be restored.',
                        'watchlist' => 'Unconditionally add or remove the page from your watchlist, use preferences or do not change watch',
                );