MediaWiki  REL1_23
ApiQueryAllImages.php
Go to the documentation of this file.
00001 <?php
00002 
00034 class ApiQueryAllImages extends ApiQueryGeneratorBase {
00035     protected $mRepo;
00036 
00037     public function __construct( $query, $moduleName ) {
00038         parent::__construct( $query, $moduleName, 'ai' );
00039         $this->mRepo = RepoGroup::singleton()->getLocalRepo();
00040     }
00041 
00049     protected function getDB() {
00050         return $this->mRepo->getSlaveDB();
00051     }
00052 
00053     public function execute() {
00054         $this->run();
00055     }
00056 
00057     public function getCacheMode( $params ) {
00058         return 'public';
00059     }
00060 
00065     public function executeGenerator( $resultPageSet ) {
00066         if ( $resultPageSet->isResolvingRedirects() ) {
00067             $this->dieUsage(
00068                 'Use "gaifilterredir=nonredirects" option instead of "redirects" ' .
00069                     'when using allimages as a generator',
00070                 'params'
00071             );
00072         }
00073 
00074         $this->run( $resultPageSet );
00075     }
00076 
00081     private function run( $resultPageSet = null ) {
00082         $repo = $this->mRepo;
00083         if ( !$repo instanceof LocalRepo ) {
00084             $this->dieUsage(
00085                 'Local file repository does not support querying all images',
00086                 'unsupportedrepo'
00087             );
00088         }
00089 
00090         $prefix = $this->getModulePrefix();
00091 
00092         $db = $this->getDB();
00093 
00094         $params = $this->extractRequestParams();
00095 
00096         // Table and return fields
00097         $this->addTables( 'image' );
00098 
00099         $prop = array_flip( $params['prop'] );
00100         $this->addFields( LocalFile::selectFields() );
00101 
00102         $ascendingOrder = true;
00103         if ( $params['dir'] == 'descending' || $params['dir'] == 'older' ) {
00104             $ascendingOrder = false;
00105         }
00106 
00107         if ( $params['sort'] == 'name' ) {
00108             // Check mutually exclusive params
00109             $disallowed = array( 'start', 'end', 'user' );
00110             foreach ( $disallowed as $pname ) {
00111                 if ( isset( $params[$pname] ) ) {
00112                     $this->dieUsage(
00113                         "Parameter '{$prefix}{$pname}' can only be used with {$prefix}sort=timestamp",
00114                         'badparams'
00115                     );
00116                 }
00117             }
00118             if ( $params['filterbots'] != 'all' ) {
00119                 $this->dieUsage(
00120                     "Parameter '{$prefix}filterbots' can only be used with {$prefix}sort=timestamp",
00121                     'badparams'
00122                 );
00123             }
00124 
00125             // Pagination
00126             if ( !is_null( $params['continue'] ) ) {
00127                 $cont = explode( '|', $params['continue'] );
00128                 $this->dieContinueUsageIf( count( $cont ) != 1 );
00129                 $op = ( $ascendingOrder ? '>' : '<' );
00130                 $continueFrom = $db->addQuotes( $cont[0] );
00131                 $this->addWhere( "img_name $op= $continueFrom" );
00132             }
00133 
00134             // Image filters
00135             $from = ( $params['from'] === null ? null : $this->titlePartToKey( $params['from'], NS_FILE ) );
00136             $to = ( $params['to'] === null ? null : $this->titlePartToKey( $params['to'], NS_FILE ) );
00137             $this->addWhereRange( 'img_name', ( $ascendingOrder ? 'newer' : 'older' ), $from, $to );
00138 
00139             if ( isset( $params['prefix'] ) ) {
00140                 $this->addWhere( 'img_name' . $db->buildLike(
00141                     $this->titlePartToKey( $params['prefix'], NS_FILE ),
00142                     $db->anyString() ) );
00143             }
00144         } else {
00145             // Check mutually exclusive params
00146             $disallowed = array( 'from', 'to', 'prefix' );
00147             foreach ( $disallowed as $pname ) {
00148                 if ( isset( $params[$pname] ) ) {
00149                     $this->dieUsage(
00150                         "Parameter '{$prefix}{$pname}' can only be used with {$prefix}sort=name",
00151                         'badparams'
00152                     );
00153                 }
00154             }
00155             if ( !is_null( $params['user'] ) && $params['filterbots'] != 'all' ) {
00156                 // Since filterbots checks if each user has the bot right, it
00157                 // doesn't make sense to use it with user
00158                 $this->dieUsage(
00159                     "Parameters '{$prefix}user' and '{$prefix}filterbots' cannot be used together",
00160                     'badparams'
00161                 );
00162             }
00163 
00164             // Pagination
00165             $this->addTimestampWhereRange(
00166                 'img_timestamp',
00167                 $ascendingOrder ? 'newer' : 'older',
00168                 $params['start'],
00169                 $params['end']
00170             );
00171             // Include in ORDER BY for uniqueness
00172             $this->addWhereRange( 'img_name', $ascendingOrder ? 'newer' : 'older', null, null );
00173 
00174             if ( !is_null( $params['continue'] ) ) {
00175                 $cont = explode( '|', $params['continue'] );
00176                 $this->dieContinueUsageIf( count( $cont ) != 2 );
00177                 $op = ( $ascendingOrder ? '>' : '<' );
00178                 $continueTimestamp = $db->addQuotes( $db->timestamp( $cont[0] ) );
00179                 $continueName = $db->addQuotes( $cont[1] );
00180                 $this->addWhere( "img_timestamp $op $continueTimestamp OR " .
00181                     "(img_timestamp = $continueTimestamp AND " .
00182                     "img_name $op= $continueName)"
00183                 );
00184             }
00185 
00186             // Image filters
00187             if ( !is_null( $params['user'] ) ) {
00188                 $this->addWhereFld( 'img_user_text', $params['user'] );
00189             }
00190             if ( $params['filterbots'] != 'all' ) {
00191                 $this->addTables( 'user_groups' );
00192                 $this->addJoinConds( array( 'user_groups' => array(
00193                     'LEFT JOIN',
00194                     array(
00195                         'ug_group' => User::getGroupsWithPermission( 'bot' ),
00196                         'ug_user = img_user'
00197                     )
00198                 ) ) );
00199                 $groupCond = ( $params['filterbots'] == 'nobots' ? 'NULL' : 'NOT NULL' );
00200                 $this->addWhere( "ug_group IS $groupCond" );
00201             }
00202         }
00203 
00204         // Filters not depending on sort
00205         if ( isset( $params['minsize'] ) ) {
00206             $this->addWhere( 'img_size>=' . intval( $params['minsize'] ) );
00207         }
00208 
00209         if ( isset( $params['maxsize'] ) ) {
00210             $this->addWhere( 'img_size<=' . intval( $params['maxsize'] ) );
00211         }
00212 
00213         $sha1 = false;
00214         if ( isset( $params['sha1'] ) ) {
00215             $sha1 = strtolower( $params['sha1'] );
00216             if ( !$this->validateSha1Hash( $sha1 ) ) {
00217                 $this->dieUsage( 'The SHA1 hash provided is not valid', 'invalidsha1hash' );
00218             }
00219             $sha1 = wfBaseConvert( $sha1, 16, 36, 31 );
00220         } elseif ( isset( $params['sha1base36'] ) ) {
00221             $sha1 = strtolower( $params['sha1base36'] );
00222             if ( !$this->validateSha1Base36Hash( $sha1 ) ) {
00223                 $this->dieUsage( 'The SHA1Base36 hash provided is not valid', 'invalidsha1base36hash' );
00224             }
00225         }
00226         if ( $sha1 ) {
00227             $this->addWhereFld( 'img_sha1', $sha1 );
00228         }
00229 
00230         if ( !is_null( $params['mime'] ) ) {
00231             global $wgMiserMode;
00232             if ( $wgMiserMode ) {
00233                 $this->dieUsage( 'MIME search disabled in Miser Mode', 'mimesearchdisabled' );
00234             }
00235 
00236             list( $major, $minor ) = File::splitMime( $params['mime'] );
00237 
00238             $this->addWhereFld( 'img_major_mime', $major );
00239             $this->addWhereFld( 'img_minor_mime', $minor );
00240         }
00241 
00242         $limit = $params['limit'];
00243         $this->addOption( 'LIMIT', $limit + 1 );
00244         $sortFlag = '';
00245         if ( !$ascendingOrder ) {
00246             $sortFlag = ' DESC';
00247         }
00248         if ( $params['sort'] == 'timestamp' ) {
00249             $this->addOption( 'ORDER BY', 'img_timestamp' . $sortFlag );
00250             if ( !is_null( $params['user'] ) ) {
00251                 $this->addOption( 'USE INDEX', array( 'image' => 'img_usertext_timestamp' ) );
00252             } else {
00253                 $this->addOption( 'USE INDEX', array( 'image' => 'img_timestamp' ) );
00254             }
00255         } else {
00256             $this->addOption( 'ORDER BY', 'img_name' . $sortFlag );
00257         }
00258 
00259         $res = $this->select( __METHOD__ );
00260 
00261         $titles = array();
00262         $count = 0;
00263         $result = $this->getResult();
00264         foreach ( $res as $row ) {
00265             if ( ++$count > $limit ) {
00266                 // We've reached the one extra which shows that there are
00267                 // additional pages to be had. Stop here...
00268                 if ( $params['sort'] == 'name' ) {
00269                     $this->setContinueEnumParameter( 'continue', $row->img_name );
00270                 } else {
00271                     $this->setContinueEnumParameter( 'continue', "$row->img_timestamp|$row->img_name" );
00272                 }
00273                 break;
00274             }
00275 
00276             if ( is_null( $resultPageSet ) ) {
00277                 $file = $repo->newFileFromRow( $row );
00278                 $info = array_merge( array( 'name' => $row->img_name ),
00279                     ApiQueryImageInfo::getInfo( $file, $prop, $result ) );
00280                 self::addTitleInfo( $info, $file->getTitle() );
00281 
00282                 $fit = $result->addValue( array( 'query', $this->getModuleName() ), null, $info );
00283                 if ( !$fit ) {
00284                     if ( $params['sort'] == 'name' ) {
00285                         $this->setContinueEnumParameter( 'continue', $row->img_name );
00286                     } else {
00287                         $this->setContinueEnumParameter( 'continue', "$row->img_timestamp|$row->img_name" );
00288                     }
00289                     break;
00290                 }
00291             } else {
00292                 $titles[] = Title::makeTitle( NS_FILE, $row->img_name );
00293             }
00294         }
00295 
00296         if ( is_null( $resultPageSet ) ) {
00297             $result->setIndexedTagName_internal( array( 'query', $this->getModuleName() ), 'img' );
00298         } else {
00299             $resultPageSet->populateFromTitles( $titles );
00300         }
00301     }
00302 
00303     public function getAllowedParams() {
00304         return array(
00305             'sort' => array(
00306                 ApiBase::PARAM_DFLT => 'name',
00307                 ApiBase::PARAM_TYPE => array(
00308                     'name',
00309                     'timestamp'
00310                 )
00311             ),
00312             'dir' => array(
00313                 ApiBase::PARAM_DFLT => 'ascending',
00314                 ApiBase::PARAM_TYPE => array(
00315                     // sort=name
00316                     'ascending',
00317                     'descending',
00318                     // sort=timestamp
00319                     'newer',
00320                     'older'
00321                 )
00322             ),
00323             'from' => null,
00324             'to' => null,
00325             'continue' => null,
00326             'start' => array(
00327                 ApiBase::PARAM_TYPE => 'timestamp'
00328             ),
00329             'end' => array(
00330                 ApiBase::PARAM_TYPE => 'timestamp'
00331             ),
00332             'prop' => array(
00333                 ApiBase::PARAM_TYPE => ApiQueryImageInfo::getPropertyNames( $this->propertyFilter ),
00334                 ApiBase::PARAM_DFLT => 'timestamp|url',
00335                 ApiBase::PARAM_ISMULTI => true
00336             ),
00337             'prefix' => null,
00338             'minsize' => array(
00339                 ApiBase::PARAM_TYPE => 'integer',
00340             ),
00341             'maxsize' => array(
00342                 ApiBase::PARAM_TYPE => 'integer',
00343             ),
00344             'sha1' => null,
00345             'sha1base36' => null,
00346             'user' => array(
00347                 ApiBase::PARAM_TYPE => 'user'
00348             ),
00349             'filterbots' => array(
00350                 ApiBase::PARAM_DFLT => 'all',
00351                 ApiBase::PARAM_TYPE => array(
00352                     'all',
00353                     'bots',
00354                     'nobots'
00355                 )
00356             ),
00357             'mime' => null,
00358             'limit' => array(
00359                 ApiBase::PARAM_DFLT => 10,
00360                 ApiBase::PARAM_TYPE => 'limit',
00361                 ApiBase::PARAM_MIN => 1,
00362                 ApiBase::PARAM_MAX => ApiBase::LIMIT_BIG1,
00363                 ApiBase::PARAM_MAX2 => ApiBase::LIMIT_BIG2
00364             ),
00365         );
00366     }
00367 
00368     public function getParamDescription() {
00369         $p = $this->getModulePrefix();
00370 
00371         return array(
00372             'sort' => 'Property to sort by',
00373             'dir' => 'The direction in which to list',
00374             'from' => "The image title to start enumerating from. Can only be used with {$p}sort=name",
00375             'to' => "The image title to stop enumerating at. Can only be used with {$p}sort=name",
00376             'continue' => 'When more results are available, use this to continue',
00377             'start' => "The timestamp to start enumerating from. Can only be used with {$p}sort=timestamp",
00378             'end' => "The timestamp to end enumerating. Can only be used with {$p}sort=timestamp",
00379             'prop' => ApiQueryImageInfo::getPropertyDescriptions( $this->propertyFilter ),
00380             'prefix' => "Search for all image titles that begin with this " .
00381                 "value. Can only be used with {$p}sort=name",
00382             'minsize' => 'Limit to images with at least this many bytes',
00383             'maxsize' => 'Limit to images with at most this many bytes',
00384             'sha1' => "SHA1 hash of image. Overrides {$p}sha1base36",
00385             'sha1base36' => 'SHA1 hash of image in base 36 (used in MediaWiki)',
00386             'user' => "Only return files uploaded by this user. Can only be used " .
00387                 "with {$p}sort=timestamp. Cannot be used together with {$p}filterbots",
00388             'filterbots' => "How to filter files uploaded by bots. Can only be " .
00389                 "used with {$p}sort=timestamp. Cannot be used together with {$p}user",
00390             'mime' => 'What MIME type to search for. e.g. image/jpeg. Disabled in Miser Mode',
00391             'limit' => 'How many images in total to return',
00392         );
00393     }
00394 
00395     private $propertyFilter = array( 'archivename', 'thumbmime', 'uploadwarning' );
00396 
00397     public function getResultProperties() {
00398         return array_merge(
00399             array(
00400                 '' => array(
00401                     'name' => 'string',
00402                     'ns' => 'namespace',
00403                     'title' => 'string'
00404                 )
00405             ),
00406             ApiQueryImageInfo::getResultPropertiesFiltered( $this->propertyFilter )
00407         );
00408     }
00409 
00410     public function getDescription() {
00411         return 'Enumerate all images sequentially.';
00412     }
00413 
00414     public function getPossibleErrors() {
00415         $p = $this->getModulePrefix();
00416 
00417         return array_merge( parent::getPossibleErrors(), array(
00418             array(
00419                 'code' => 'params',
00420                 'info' => 'Use "gaifilterredir=nonredirects" option instead ' .
00421                     'of "redirects" when using allimages as a generator'
00422             ),
00423             array(
00424                 'code' => 'badparams',
00425                 'info' => "Parameter'{$p}start' can only be used with {$p}sort=timestamp"
00426             ),
00427             array(
00428                 'code' => 'badparams',
00429                 'info' => "Parameter'{$p}end' can only be used with {$p}sort=timestamp"
00430             ),
00431             array(
00432                 'code' => 'badparams',
00433                 'info' => "Parameter'{$p}user' can only be used with {$p}sort=timestamp"
00434             ),
00435             array(
00436                 'code' => 'badparams',
00437                 'info' => "Parameter'{$p}filterbots' can only be used with {$p}sort=timestamp"
00438             ),
00439             array(
00440                 'code' => 'badparams',
00441                 'info' => "Parameter'{$p}from' can only be used with {$p}sort=name"
00442             ),
00443             array(
00444                 'code' => 'badparams',
00445                 'info' => "Parameter'{$p}to' can only be used with {$p}sort=name"
00446             ),
00447             array(
00448                 'code' => 'badparams',
00449                 'info' => "Parameter'{$p}prefix' can only be used with {$p}sort=name"
00450             ),
00451             array(
00452                 'code' => 'badparams',
00453                 'info' => "Parameters '{$p}user' and '{$p}filterbots' cannot be used together"
00454             ),
00455             array(
00456                 'code' => 'unsupportedrepo',
00457                 'info' => 'Local file repository does not support querying all images' ),
00458             array( 'code' => 'mimesearchdisabled', 'info' => 'MIME search disabled in Miser Mode' ),
00459             array( 'code' => 'invalidsha1hash', 'info' => 'The SHA1 hash provided is not valid' ),
00460             array(
00461                 'code' => 'invalidsha1base36hash',
00462                 'info' => 'The SHA1Base36 hash provided is not valid'
00463             ),
00464         ) );
00465     }
00466 
00467     public function getExamples() {
00468         return array(
00469             'api.php?action=query&list=allimages&aifrom=B' => array(
00470                 'Simple Use',
00471                 'Show a list of files starting at the letter "B"',
00472             ),
00473             'api.php?action=query&list=allimages&aiprop=user|timestamp|url&' .
00474                 'aisort=timestamp&aidir=older' => array(
00475                 'Simple Use',
00476                 'Show a list of recently uploaded files similar to Special:NewFiles',
00477             ),
00478             'api.php?action=query&generator=allimages&gailimit=4&' .
00479                 'gaifrom=T&prop=imageinfo' => array(
00480                 'Using as Generator',
00481                 'Show info about 4 files starting at the letter "T"',
00482             ),
00483         );
00484     }
00485 
00486     public function getHelpUrls() {
00487         return 'https://www.mediawiki.org/wiki/API:Allimages';
00488     }
00489 }