]> scripts.mit.edu Git - autoinstalls/mediawiki.git/blob - includes/api/ApiQueryAllRevisions.php
MediaWiki 1.30.2
[autoinstalls/mediawiki.git] / includes / api / ApiQueryAllRevisions.php
1 <?php
2 /**
3  * Created on Sep 27, 2015
4  *
5  * Copyright © 2015 Wikimedia Foundation and contributors
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License along
18  * with this program; if not, write to the Free Software Foundation, Inc.,
19  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  * http://www.gnu.org/copyleft/gpl.html
21  *
22  * @file
23  */
24
25 /**
26  * Query module to enumerate all revisions.
27  *
28  * @ingroup API
29  * @since 1.27
30  */
31 class ApiQueryAllRevisions extends ApiQueryRevisionsBase {
32
33         public function __construct( ApiQuery $query, $moduleName ) {
34                 parent::__construct( $query, $moduleName, 'arv' );
35         }
36
37         /**
38          * @param ApiPageSet $resultPageSet
39          * @return void
40          */
41         protected function run( ApiPageSet $resultPageSet = null ) {
42                 $db = $this->getDB();
43                 $params = $this->extractRequestParams( false );
44
45                 $result = $this->getResult();
46
47                 $this->requireMaxOneParameter( $params, 'user', 'excludeuser' );
48
49                 // Namespace check is likely to be desired, but can't be done
50                 // efficiently in SQL.
51                 $miser_ns = null;
52                 $needPageTable = false;
53                 if ( $params['namespace'] !== null ) {
54                         $params['namespace'] = array_unique( $params['namespace'] );
55                         sort( $params['namespace'] );
56                         if ( $params['namespace'] != MWNamespace::getValidNamespaces() ) {
57                                 $needPageTable = true;
58                                 if ( $this->getConfig()->get( 'MiserMode' ) ) {
59                                         $miser_ns = $params['namespace'];
60                                 } else {
61                                         $this->addWhere( [ 'page_namespace' => $params['namespace'] ] );
62                                 }
63                         }
64                 }
65
66                 $this->addTables( 'revision' );
67                 if ( $resultPageSet === null ) {
68                         $this->parseParameters( $params );
69                         $this->addTables( 'page' );
70                         $this->addJoinConds(
71                                 [ 'page' => [ 'INNER JOIN', [ 'rev_page = page_id' ] ] ]
72                         );
73                         $this->addFields( Revision::selectFields() );
74                         $this->addFields( Revision::selectPageFields() );
75
76                         // Review this depeneding on the outcome of T113901
77                         $this->addOption( 'STRAIGHT_JOIN' );
78                 } else {
79                         $this->limit = $this->getParameter( 'limit' ) ?: 10;
80                         $this->addFields( [ 'rev_timestamp', 'rev_id' ] );
81                         if ( $params['generatetitles'] ) {
82                                 $this->addFields( [ 'rev_page' ] );
83                         }
84
85                         if ( $needPageTable ) {
86                                 $this->addTables( 'page' );
87                                 $this->addJoinConds(
88                                         [ 'page' => [ 'INNER JOIN', [ 'rev_page = page_id' ] ] ]
89                                 );
90                                 $this->addFieldsIf( [ 'page_namespace' ], (bool)$miser_ns );
91
92                                 // Review this depeneding on the outcome of T113901
93                                 $this->addOption( 'STRAIGHT_JOIN' );
94                         }
95                 }
96
97                 $dir = $params['dir'];
98                 $this->addTimestampWhereRange( 'rev_timestamp', $dir, $params['start'], $params['end'] );
99
100                 if ( $this->fld_tags ) {
101                         $this->addTables( 'tag_summary' );
102                         $this->addJoinConds(
103                                 [ 'tag_summary' => [ 'LEFT JOIN', [ 'rev_id=ts_rev_id' ] ] ]
104                         );
105                         $this->addFields( 'ts_tags' );
106                 }
107
108                 if ( $this->fetchContent ) {
109                         $this->addTables( 'text' );
110                         $this->addJoinConds(
111                                 [ 'text' => [ 'INNER JOIN', [ 'rev_text_id=old_id' ] ] ]
112                         );
113                         $this->addFields( 'old_id' );
114                         $this->addFields( Revision::selectTextFields() );
115                 }
116
117                 if ( $params['user'] !== null ) {
118                         $id = User::idFromName( $params['user'] );
119                         if ( $id ) {
120                                 $this->addWhereFld( 'rev_user', $id );
121                         } else {
122                                 $this->addWhereFld( 'rev_user_text', $params['user'] );
123                         }
124                 } elseif ( $params['excludeuser'] !== null ) {
125                         $id = User::idFromName( $params['excludeuser'] );
126                         if ( $id ) {
127                                 $this->addWhere( 'rev_user != ' . $id );
128                         } else {
129                                 $this->addWhere( 'rev_user_text != ' . $db->addQuotes( $params['excludeuser'] ) );
130                         }
131                 }
132
133                 if ( $params['user'] !== null || $params['excludeuser'] !== null ) {
134                         // Paranoia: avoid brute force searches (T19342)
135                         if ( !$this->getUser()->isAllowed( 'deletedhistory' ) ) {
136                                 $bitmask = Revision::DELETED_USER;
137                         } elseif ( !$this->getUser()->isAllowedAny( 'suppressrevision', 'viewsuppressed' ) ) {
138                                 $bitmask = Revision::DELETED_USER | Revision::DELETED_RESTRICTED;
139                         } else {
140                                 $bitmask = 0;
141                         }
142                         if ( $bitmask ) {
143                                 $this->addWhere( $db->bitAnd( 'rev_deleted', $bitmask ) . " != $bitmask" );
144                         }
145                 }
146
147                 if ( $params['continue'] !== null ) {
148                         $op = ( $dir == 'newer' ? '>' : '<' );
149                         $cont = explode( '|', $params['continue'] );
150                         $this->dieContinueUsageIf( count( $cont ) != 2 );
151                         $ts = $db->addQuotes( $db->timestamp( $cont[0] ) );
152                         $rev_id = (int)$cont[1];
153                         $this->dieContinueUsageIf( strval( $rev_id ) !== $cont[1] );
154                         $this->addWhere( "rev_timestamp $op $ts OR " .
155                                 "(rev_timestamp = $ts AND " .
156                                 "rev_id $op= $rev_id)" );
157                 }
158
159                 $this->addOption( 'LIMIT', $this->limit + 1 );
160
161                 $sort = ( $dir == 'newer' ? '' : ' DESC' );
162                 $orderby = [];
163                 // Targeting index rev_timestamp, user_timestamp, or usertext_timestamp
164                 // But 'user' is always constant for the latter two, so it doesn't matter here.
165                 $orderby[] = "rev_timestamp $sort";
166                 $orderby[] = "rev_id $sort";
167                 $this->addOption( 'ORDER BY', $orderby );
168
169                 $hookData = [];
170                 $res = $this->select( __METHOD__, [], $hookData );
171                 $pageMap = []; // Maps rev_page to array index
172                 $count = 0;
173                 $nextIndex = 0;
174                 $generated = [];
175                 foreach ( $res as $row ) {
176                         if ( $count === 0 && $resultPageSet !== null ) {
177                                 // Set the non-continue since the list of all revisions is
178                                 // prone to having entries added at the start frequently.
179                                 $this->getContinuationManager()->addGeneratorNonContinueParam(
180                                         $this, 'continue', "$row->rev_timestamp|$row->rev_id"
181                                 );
182                         }
183                         if ( ++$count > $this->limit ) {
184                                 // We've had enough
185                                 $this->setContinueEnumParameter( 'continue', "$row->rev_timestamp|$row->rev_id" );
186                                 break;
187                         }
188
189                         // Miser mode namespace check
190                         if ( $miser_ns !== null && !in_array( $row->page_namespace, $miser_ns ) ) {
191                                 continue;
192                         }
193
194                         if ( $resultPageSet !== null ) {
195                                 if ( $params['generatetitles'] ) {
196                                         $generated[$row->rev_page] = $row->rev_page;
197                                 } else {
198                                         $generated[] = $row->rev_id;
199                                 }
200                         } else {
201                                 $revision = Revision::newFromRow( $row );
202                                 $rev = $this->extractRevisionInfo( $revision, $row );
203
204                                 if ( !isset( $pageMap[$row->rev_page] ) ) {
205                                         $index = $nextIndex++;
206                                         $pageMap[$row->rev_page] = $index;
207                                         $title = $revision->getTitle();
208                                         $a = [
209                                                 'pageid' => $title->getArticleID(),
210                                                 'revisions' => [ $rev ],
211                                         ];
212                                         ApiResult::setIndexedTagName( $a['revisions'], 'rev' );
213                                         ApiQueryBase::addTitleInfo( $a, $title );
214                                         $fit = $this->processRow( $row, $a['revisions'][0], $hookData ) &&
215                                                 $result->addValue( [ 'query', $this->getModuleName() ], $index, $a );
216                                 } else {
217                                         $index = $pageMap[$row->rev_page];
218                                         $fit = $this->processRow( $row, $rev, $hookData ) &&
219                                                 $result->addValue( [ 'query', $this->getModuleName(), $index, 'revisions' ], null, $rev );
220                                 }
221                                 if ( !$fit ) {
222                                         $this->setContinueEnumParameter( 'continue', "$row->rev_timestamp|$row->rev_id" );
223                                         break;
224                                 }
225                         }
226                 }
227
228                 if ( $resultPageSet !== null ) {
229                         if ( $params['generatetitles'] ) {
230                                 $resultPageSet->populateFromPageIDs( $generated );
231                         } else {
232                                 $resultPageSet->populateFromRevisionIDs( $generated );
233                         }
234                 } else {
235                         $result->addIndexedTagName( [ 'query', $this->getModuleName() ], 'page' );
236                 }
237         }
238
239         public function getAllowedParams() {
240                 $ret = parent::getAllowedParams() + [
241                         'user' => [
242                                 ApiBase::PARAM_TYPE => 'user',
243                         ],
244                         'namespace' => [
245                                 ApiBase::PARAM_ISMULTI => true,
246                                 ApiBase::PARAM_TYPE => 'namespace',
247                                 ApiBase::PARAM_DFLT => null,
248                         ],
249                         'start' => [
250                                 ApiBase::PARAM_TYPE => 'timestamp',
251                         ],
252                         'end' => [
253                                 ApiBase::PARAM_TYPE => 'timestamp',
254                         ],
255                         'dir' => [
256                                 ApiBase::PARAM_TYPE => [
257                                         'newer',
258                                         'older'
259                                 ],
260                                 ApiBase::PARAM_DFLT => 'older',
261                                 ApiBase::PARAM_HELP_MSG => 'api-help-param-direction',
262                         ],
263                         'excludeuser' => [
264                                 ApiBase::PARAM_TYPE => 'user',
265                         ],
266                         'continue' => [
267                                 ApiBase::PARAM_HELP_MSG => 'api-help-param-continue',
268                         ],
269                         'generatetitles' => [
270                                 ApiBase::PARAM_DFLT => false,
271                         ],
272                 ];
273
274                 if ( $this->getConfig()->get( 'MiserMode' ) ) {
275                         $ret['namespace'][ApiBase::PARAM_HELP_MSG_APPEND] = [
276                                 'api-help-param-limited-in-miser-mode',
277                         ];
278                 }
279
280                 return $ret;
281         }
282
283         protected function getExamplesMessages() {
284                 return [
285                         'action=query&list=allrevisions&arvuser=Example&arvlimit=50'
286                                 => 'apihelp-query+allrevisions-example-user',
287                         'action=query&list=allrevisions&arvdir=newer&arvlimit=50'
288                                 => 'apihelp-query+allrevisions-example-ns-main',
289                 ];
290         }
291
292         public function getHelpUrls() {
293                 return 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Allrevisions';
294         }
295 }