MediaWiki  REL1_24
ApiQueryAllLinks.php
Go to the documentation of this file.
00001 <?php
00032 class ApiQueryAllLinks extends ApiQueryGeneratorBase {
00033 
00034     private $table, $tablePrefix, $indexTag,
00035         $description, $descriptionWhat, $descriptionTargets, $descriptionLinking;
00036     private $fieldTitle = 'title';
00037     private $dfltNamespace = NS_MAIN;
00038     private $hasNamespace = true;
00039     private $useIndex = null;
00040     private $props = array(), $propHelp = array();
00041 
00042     public function __construct( ApiQuery $query, $moduleName ) {
00043         switch ( $moduleName ) {
00044             case 'alllinks':
00045                 $prefix = 'al';
00046                 $this->table = 'pagelinks';
00047                 $this->tablePrefix = 'pl_';
00048                 $this->useIndex = 'pl_namespace';
00049                 $this->indexTag = 'l';
00050                 $this->description = 'Enumerate all links that point to a given namespace';
00051                 $this->descriptionWhat = 'link';
00052                 $this->descriptionTargets = 'linked titles';
00053                 $this->descriptionLinking = 'linking';
00054                 break;
00055             case 'alltransclusions':
00056                 $prefix = 'at';
00057                 $this->table = 'templatelinks';
00058                 $this->tablePrefix = 'tl_';
00059                 $this->dfltNamespace = NS_TEMPLATE;
00060                 $this->useIndex = 'tl_namespace';
00061                 $this->indexTag = 't';
00062                 $this->description =
00063                     'List all transclusions (pages embedded using {{x}}), including non-existing';
00064                 $this->descriptionWhat = 'transclusion';
00065                 $this->descriptionTargets = 'transcluded titles';
00066                 $this->descriptionLinking = 'transcluding';
00067                 break;
00068             case 'allfileusages':
00069                 $prefix = 'af';
00070                 $this->table = 'imagelinks';
00071                 $this->tablePrefix = 'il_';
00072                 $this->fieldTitle = 'to';
00073                 $this->dfltNamespace = NS_FILE;
00074                 $this->hasNamespace = false;
00075                 $this->indexTag = 'f';
00076                 $this->description = 'List all file usages, including non-existing';
00077                 $this->descriptionWhat = 'file';
00078                 $this->descriptionTargets = 'file titles';
00079                 $this->descriptionLinking = 'using';
00080                 break;
00081             case 'allredirects':
00082                 $prefix = 'ar';
00083                 $this->table = 'redirect';
00084                 $this->tablePrefix = 'rd_';
00085                 $this->indexTag = 'r';
00086                 $this->description = 'List all redirects to a namespace';
00087                 $this->descriptionWhat = 'redirect';
00088                 $this->descriptionTargets = 'target pages';
00089                 $this->descriptionLinking = 'redirecting';
00090                 $this->props = array(
00091                     'fragment' => 'rd_fragment',
00092                     'interwiki' => 'rd_interwiki',
00093                 );
00094                 $this->propHelp = array(
00095                     ' fragment - Adds the fragment from the redirect, if any',
00096                     ' interwiki - Adds the interwiki prefix from the redirect, if any',
00097                 );
00098                 break;
00099             default:
00100                 ApiBase::dieDebug( __METHOD__, 'Unknown module name' );
00101         }
00102 
00103         parent::__construct( $query, $moduleName, $prefix );
00104     }
00105 
00106     public function execute() {
00107         $this->run();
00108     }
00109 
00110     public function getCacheMode( $params ) {
00111         return 'public';
00112     }
00113 
00114     public function executeGenerator( $resultPageSet ) {
00115         $this->run( $resultPageSet );
00116     }
00117 
00122     private function run( $resultPageSet = null ) {
00123         $db = $this->getDB();
00124         $params = $this->extractRequestParams();
00125 
00126         $pfx = $this->tablePrefix;
00127         $fieldTitle = $this->fieldTitle;
00128         $prop = array_flip( $params['prop'] );
00129         $fld_ids = isset( $prop['ids'] );
00130         $fld_title = isset( $prop['title'] );
00131         if ( $this->hasNamespace ) {
00132             $namespace = $params['namespace'];
00133         } else {
00134             $namespace = $this->dfltNamespace;
00135         }
00136 
00137         if ( $params['unique'] ) {
00138             $matches = array_intersect_key( $prop, $this->props + array( 'ids' => 1 ) );
00139             if ( $matches ) {
00140                 $p = $this->getModulePrefix();
00141                 $this->dieUsage(
00142                     "Cannot use {$p}prop=" . join( '|', array_keys( $matches ) ) . " with {$p}unique",
00143                     'params'
00144                 );
00145             }
00146             $this->addOption( 'DISTINCT' );
00147         }
00148 
00149         $this->addTables( $this->table );
00150         if ( $this->hasNamespace ) {
00151             $this->addWhereFld( $pfx . 'namespace', $namespace );
00152         }
00153 
00154         $continue = !is_null( $params['continue'] );
00155         if ( $continue ) {
00156             $continueArr = explode( '|', $params['continue'] );
00157             $op = $params['dir'] == 'descending' ? '<' : '>';
00158             if ( $params['unique'] ) {
00159                 $this->dieContinueUsageIf( count( $continueArr ) != 1 );
00160                 $continueTitle = $db->addQuotes( $continueArr[0] );
00161                 $this->addWhere( "{$pfx}{$fieldTitle} $op= $continueTitle" );
00162             } else {
00163                 $this->dieContinueUsageIf( count( $continueArr ) != 2 );
00164                 $continueTitle = $db->addQuotes( $continueArr[0] );
00165                 $continueFrom = intval( $continueArr[1] );
00166                 $this->addWhere(
00167                     "{$pfx}{$fieldTitle} $op $continueTitle OR " .
00168                     "({$pfx}{$fieldTitle} = $continueTitle AND " .
00169                     "{$pfx}from $op= $continueFrom)"
00170                 );
00171             }
00172         }
00173 
00174         // 'continue' always overrides 'from'
00175         $from = ( $continue || $params['from'] === null ? null :
00176             $this->titlePartToKey( $params['from'], $namespace ) );
00177         $to = ( $params['to'] === null ? null :
00178             $this->titlePartToKey( $params['to'], $namespace ) );
00179         $this->addWhereRange( $pfx . $fieldTitle, 'newer', $from, $to );
00180 
00181         if ( isset( $params['prefix'] ) ) {
00182             $this->addWhere( $pfx . $fieldTitle . $db->buildLike( $this->titlePartToKey(
00183                 $params['prefix'], $namespace ), $db->anyString() ) );
00184         }
00185 
00186         $this->addFields( array( 'pl_title' => $pfx . $fieldTitle ) );
00187         $this->addFieldsIf( array( 'pl_from' => $pfx . 'from' ), !$params['unique'] );
00188         foreach ( $this->props as $name => $field ) {
00189             $this->addFieldsIf( $field, isset( $prop[$name] ) );
00190         }
00191 
00192         if ( $this->useIndex ) {
00193             $this->addOption( 'USE INDEX', $this->useIndex );
00194         }
00195         $limit = $params['limit'];
00196         $this->addOption( 'LIMIT', $limit + 1 );
00197 
00198         $sort = ( $params['dir'] == 'descending' ? ' DESC' : '' );
00199         $orderBy = array();
00200         $orderBy[] = $pfx . $fieldTitle . $sort;
00201         if ( !$params['unique'] ) {
00202             $orderBy[] = $pfx . 'from' . $sort;
00203         }
00204         $this->addOption( 'ORDER BY', $orderBy );
00205 
00206         $res = $this->select( __METHOD__ );
00207 
00208         $pageids = array();
00209         $titles = array();
00210         $count = 0;
00211         $result = $this->getResult();
00212         foreach ( $res as $row ) {
00213             if ( ++$count > $limit ) {
00214                 // We've reached the one extra which shows that there are
00215                 // additional pages to be had. Stop here...
00216                 if ( $params['unique'] ) {
00217                     $this->setContinueEnumParameter( 'continue', $row->pl_title );
00218                 } else {
00219                     $this->setContinueEnumParameter( 'continue', $row->pl_title . '|' . $row->pl_from );
00220                 }
00221                 break;
00222             }
00223 
00224             if ( is_null( $resultPageSet ) ) {
00225                 $vals = array();
00226                 if ( $fld_ids ) {
00227                     $vals['fromid'] = intval( $row->pl_from );
00228                 }
00229                 if ( $fld_title ) {
00230                     $title = Title::makeTitle( $namespace, $row->pl_title );
00231                     ApiQueryBase::addTitleInfo( $vals, $title );
00232                 }
00233                 foreach ( $this->props as $name => $field ) {
00234                     if ( isset( $prop[$name] ) && $row->$field !== null && $row->$field !== '' ) {
00235                         $vals[$name] = $row->$field;
00236                     }
00237                 }
00238                 $fit = $result->addValue( array( 'query', $this->getModuleName() ), null, $vals );
00239                 if ( !$fit ) {
00240                     if ( $params['unique'] ) {
00241                         $this->setContinueEnumParameter( 'continue', $row->pl_title );
00242                     } else {
00243                         $this->setContinueEnumParameter( 'continue', $row->pl_title . '|' . $row->pl_from );
00244                     }
00245                     break;
00246                 }
00247             } elseif ( $params['unique'] ) {
00248                 $titles[] = Title::makeTitle( $namespace, $row->pl_title );
00249             } else {
00250                 $pageids[] = $row->pl_from;
00251             }
00252         }
00253 
00254         if ( is_null( $resultPageSet ) ) {
00255             $result->setIndexedTagName_internal( array( 'query', $this->getModuleName() ), $this->indexTag );
00256         } elseif ( $params['unique'] ) {
00257             $resultPageSet->populateFromTitles( $titles );
00258         } else {
00259             $resultPageSet->populateFromPageIDs( $pageids );
00260         }
00261     }
00262 
00263     public function getAllowedParams() {
00264         $allowedParams = array(
00265             'continue' => null,
00266             'from' => null,
00267             'to' => null,
00268             'prefix' => null,
00269             'unique' => false,
00270             'prop' => array(
00271                 ApiBase::PARAM_ISMULTI => true,
00272                 ApiBase::PARAM_DFLT => 'title',
00273                 ApiBase::PARAM_TYPE => array_merge(
00274                     array( 'ids', 'title' ), array_keys( $this->props )
00275                 ),
00276             ),
00277             'namespace' => array(
00278                 ApiBase::PARAM_DFLT => $this->dfltNamespace,
00279                 ApiBase::PARAM_TYPE => 'namespace'
00280             ),
00281             'limit' => array(
00282                 ApiBase::PARAM_DFLT => 10,
00283                 ApiBase::PARAM_TYPE => 'limit',
00284                 ApiBase::PARAM_MIN => 1,
00285                 ApiBase::PARAM_MAX => ApiBase::LIMIT_BIG1,
00286                 ApiBase::PARAM_MAX2 => ApiBase::LIMIT_BIG2
00287             ),
00288             'dir' => array(
00289                 ApiBase::PARAM_DFLT => 'ascending',
00290                 ApiBase::PARAM_TYPE => array(
00291                     'ascending',
00292                     'descending'
00293                 )
00294             ),
00295         );
00296         if ( !$this->hasNamespace ) {
00297             unset( $allowedParams['namespace'] );
00298         }
00299 
00300         return $allowedParams;
00301     }
00302 
00303     public function getParamDescription() {
00304         $p = $this->getModulePrefix();
00305         $what = $this->descriptionWhat;
00306         $targets = $this->descriptionTargets;
00307         $linking = $this->descriptionLinking;
00308         $paramDescription = array(
00309             'from' => "The title of the $what to start enumerating from",
00310             'to' => "The title of the $what to stop enumerating at",
00311             'prefix' => "Search for all $targets that begin with this value",
00312             'unique' => array(
00313                 "Only show distinct $targets. Cannot be used with {$p}prop=" .
00314                     join( '|', array_keys( array( 'ids' => 1 ) + $this->props ) ) . '.',
00315                 'When used as a generator, yields target pages instead of source pages.',
00316             ),
00317             'prop' => array(
00318                 'What pieces of information to include',
00319                 " ids      - Adds the pageid of the $linking page (Cannot be used with {$p}unique)",
00320                 " title    - Adds the title of the $what",
00321             ),
00322             'namespace' => 'The namespace to enumerate',
00323             'limit' => 'How many total items to return',
00324             'continue' => 'When more results are available, use this to continue',
00325             'dir' => 'The direction in which to list',
00326         );
00327         foreach ( $this->propHelp as $help ) {
00328             $paramDescription['prop'][] = "$help (Cannot be used with {$p}unique)";
00329         }
00330         if ( !$this->hasNamespace ) {
00331             unset( $paramDescription['namespace'] );
00332         }
00333 
00334         return $paramDescription;
00335     }
00336 
00337     public function getDescription() {
00338         return $this->description;
00339     }
00340 
00341     public function getExamples() {
00342         $p = $this->getModulePrefix();
00343         $name = $this->getModuleName();
00344         $what = $this->descriptionWhat;
00345         $targets = $this->descriptionTargets;
00346 
00347         return array(
00348             "api.php?action=query&list={$name}&{$p}from=B&{$p}prop=ids|title"
00349                 => "List $targets with page ids they are from, including missing ones. Start at B",
00350             "api.php?action=query&list={$name}&{$p}unique=&{$p}from=B"
00351                 => "List unique $targets",
00352             "api.php?action=query&generator={$name}&g{$p}unique=&g{$p}from=B"
00353                 => "Gets all $targets, marking the missing ones",
00354             "api.php?action=query&generator={$name}&g{$p}from=B"
00355                 => "Gets pages containing the {$what}s",
00356         );
00357     }
00358 
00359     public function getHelpUrls() {
00360         $name = ucfirst( $this->getModuleName() );
00361 
00362         return "https://www.mediawiki.org/wiki/API:{$name}";
00363     }
00364 }