Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 152
0.00% covered (danger)
0.00%
0 / 8
CRAP
0.00% covered (danger)
0.00%
0 / 1
ApiQueryProtectedTitles
0.00% covered (danger)
0.00%
0 / 151
0.00% covered (danger)
0.00%
0 / 8
1056
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 execute
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 executeGenerator
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 run
0.00% covered (danger)
0.00%
0 / 83
0.00% covered (danger)
0.00%
0 / 1
552
 getCacheMode
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
12
 getAllowedParams
0.00% covered (danger)
0.00%
0 / 53
0.00% covered (danger)
0.00%
0 / 1
2
 getExamplesMessages
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
2
 getHelpUrls
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2/**
3 * Copyright © 2009 Roan Kattouw <roan.kattouw@gmail.com>
4 *
5 * @license GPL-2.0-or-later
6 * @file
7 */
8
9namespace MediaWiki\Api;
10
11use MediaWiki\CommentFormatter\RowCommentFormatter;
12use MediaWiki\CommentStore\CommentStore;
13use MediaWiki\MainConfigNames;
14use MediaWiki\Title\Title;
15use Wikimedia\ParamValidator\ParamValidator;
16use Wikimedia\ParamValidator\TypeDef\IntegerDef;
17use Wikimedia\Timestamp\TimestampFormat as TS;
18
19/**
20 * Query module to enumerate all create-protected pages.
21 *
22 * @ingroup API
23 */
24class ApiQueryProtectedTitles extends ApiQueryGeneratorBase {
25
26    private CommentStore $commentStore;
27    private RowCommentFormatter $commentFormatter;
28
29    public function __construct(
30        ApiQuery $query,
31        string $moduleName,
32        CommentStore $commentStore,
33        RowCommentFormatter $commentFormatter
34    ) {
35        parent::__construct( $query, $moduleName, 'pt' );
36        $this->commentStore = $commentStore;
37        $this->commentFormatter = $commentFormatter;
38    }
39
40    public function execute() {
41        $this->run();
42    }
43
44    /** @inheritDoc */
45    public function executeGenerator( $resultPageSet ) {
46        $this->run( $resultPageSet );
47    }
48
49    /**
50     * @param ApiPageSet|null $resultPageSet
51     * @return void
52     */
53    private function run( $resultPageSet = null ) {
54        $params = $this->extractRequestParams();
55
56        $this->addTables( 'protected_titles' );
57        $this->addFields( [ 'pt_namespace', 'pt_title', 'pt_timestamp' ] );
58
59        $prop = array_fill_keys( $params['prop'], true );
60        $this->addFieldsIf( 'pt_user', isset( $prop['user'] ) || isset( $prop['userid'] ) );
61        $this->addFieldsIf( 'pt_expiry', isset( $prop['expiry'] ) );
62        $this->addFieldsIf( 'pt_create_perm', isset( $prop['level'] ) );
63
64        if ( isset( $prop['comment'] ) || isset( $prop['parsedcomment'] ) ) {
65            $commentQuery = $this->commentStore->getJoin( 'pt_reason' );
66            $this->addTables( $commentQuery['tables'] );
67            $this->addFields( $commentQuery['fields'] );
68            $this->addJoinConds( $commentQuery['joins'] );
69        }
70
71        $this->addTimestampWhereRange( 'pt_timestamp', $params['dir'], $params['start'], $params['end'] );
72        $this->addWhereFld( 'pt_namespace', $params['namespace'] );
73        $this->addWhereFld( 'pt_create_perm', $params['level'] );
74
75        // Include in ORDER BY for uniqueness
76        $this->addWhereRange( 'pt_namespace', $params['dir'], null, null );
77        $this->addWhereRange( 'pt_title', $params['dir'], null, null );
78
79        if ( $params['continue'] !== null ) {
80            $cont = $this->parseContinueParamOrDie( $params['continue'], [ 'timestamp', 'int', 'string' ] );
81            $op = ( $params['dir'] === 'newer' ? '>=' : '<=' );
82            $db = $this->getDB();
83            $this->addWhere( $db->buildComparison( $op, [
84                'pt_timestamp' => $db->timestamp( $cont[0] ),
85                'pt_namespace' => $cont[1],
86                'pt_title' => $cont[2],
87            ] ) );
88        }
89
90        if ( isset( $prop['user'] ) ) {
91            $this->addTables( 'user' );
92            $this->addFields( 'user_name' );
93            $this->addJoinConds( [ 'user' => [ 'LEFT JOIN',
94                'user_id=pt_user'
95            ] ] );
96        }
97
98        $this->addOption( 'LIMIT', $params['limit'] + 1 );
99        $res = $this->select( __METHOD__ );
100
101        if ( $resultPageSet === null ) {
102            $this->executeGenderCacheFromResultWrapper( $res, __METHOD__, 'pt' );
103            if ( isset( $prop['parsedcomment'] ) ) {
104                $formattedComments = $this->commentFormatter->formatItems(
105                    $this->commentFormatter->rows( $res )
106                        ->commentKey( 'pt_reason' )
107                        ->namespaceField( 'pt_namespace' )
108                        ->titleField( 'pt_title' )
109                );
110            }
111        }
112
113        $count = 0;
114        $result = $this->getResult();
115
116        $titles = [];
117
118        foreach ( $res as $rowOffset => $row ) {
119            if ( ++$count > $params['limit'] ) {
120                // We've reached the one extra which shows that there are
121                // additional pages to be had. Stop here...
122                $this->setContinueEnumParameter( 'continue',
123                    "$row->pt_timestamp|$row->pt_namespace|$row->pt_title"
124                );
125                break;
126            }
127
128            $title = Title::makeTitle( $row->pt_namespace, $row->pt_title );
129            if ( $resultPageSet === null ) {
130                $vals = [];
131                ApiQueryBase::addTitleInfo( $vals, $title );
132                if ( isset( $prop['timestamp'] ) ) {
133                    $vals['timestamp'] = wfTimestamp( TS::ISO_8601, $row->pt_timestamp );
134                }
135
136                if ( isset( $prop['user'] ) && $row->user_name !== null ) {
137                    $vals['user'] = $row->user_name;
138                }
139
140                if ( isset( $prop['userid'] ) || /*B/C*/isset( $prop['user'] ) ) {
141                    $vals['userid'] = (int)$row->pt_user;
142                }
143
144                if ( isset( $prop['comment'] ) ) {
145                    $vals['comment'] = $this->commentStore->getComment( 'pt_reason', $row )->text;
146                }
147
148                if ( isset( $prop['parsedcomment'] ) ) {
149                    // @phan-suppress-next-line PhanTypeArraySuspiciousNullable
150                    $vals['parsedcomment'] = $formattedComments[$rowOffset];
151                }
152
153                if ( isset( $prop['expiry'] ) ) {
154                    $vals['expiry'] = ApiResult::formatExpiry( $row->pt_expiry );
155                }
156
157                if ( isset( $prop['level'] ) ) {
158                    $vals['level'] = $row->pt_create_perm;
159                }
160
161                $fit = $result->addValue( [ 'query', $this->getModuleName() ], null, $vals );
162                if ( !$fit ) {
163                    $this->setContinueEnumParameter( 'continue',
164                        "$row->pt_timestamp|$row->pt_namespace|$row->pt_title"
165                    );
166                    break;
167                }
168            } else {
169                $titles[] = $title;
170            }
171        }
172
173        if ( $resultPageSet === null ) {
174            $result->addIndexedTagName(
175                [ 'query', $this->getModuleName() ],
176                $this->getModulePrefix()
177            );
178        } else {
179            $resultPageSet->populateFromTitles( $titles );
180        }
181    }
182
183    /** @inheritDoc */
184    public function getCacheMode( $params ) {
185        if ( $params['prop'] !== null && in_array( 'parsedcomment', $params['prop'] ) ) {
186            // MediaWiki\CommentFormatter\CommentFormatter::formatItems() calls wfMessage() among other things
187            return 'anon-public-user-private';
188        } else {
189            return 'public';
190        }
191    }
192
193    /** @inheritDoc */
194    public function getAllowedParams() {
195        return [
196            'namespace' => [
197                ParamValidator::PARAM_ISMULTI => true,
198                ParamValidator::PARAM_TYPE => 'namespace',
199            ],
200            'level' => [
201                ParamValidator::PARAM_ISMULTI => true,
202                ParamValidator::PARAM_TYPE => array_diff(
203                    $this->getConfig()->get( MainConfigNames::RestrictionLevels ), [ '' ] )
204            ],
205            'limit' => [
206                ParamValidator::PARAM_DEFAULT => 10,
207                ParamValidator::PARAM_TYPE => 'limit',
208                IntegerDef::PARAM_MIN => 1,
209                IntegerDef::PARAM_MAX => ApiBase::LIMIT_BIG1,
210                IntegerDef::PARAM_MAX2 => ApiBase::LIMIT_BIG2
211            ],
212            'dir' => [
213                ParamValidator::PARAM_DEFAULT => 'older',
214                ParamValidator::PARAM_TYPE => [
215                    'newer',
216                    'older'
217                ],
218                ApiBase::PARAM_HELP_MSG => 'api-help-param-direction',
219                ApiBase::PARAM_HELP_MSG_PER_VALUE => [
220                    'newer' => 'api-help-paramvalue-direction-newer',
221                    'older' => 'api-help-paramvalue-direction-older',
222                ],
223            ],
224            'start' => [
225                ParamValidator::PARAM_TYPE => 'timestamp'
226            ],
227            'end' => [
228                ParamValidator::PARAM_TYPE => 'timestamp'
229            ],
230            'prop' => [
231                ParamValidator::PARAM_ISMULTI => true,
232                ParamValidator::PARAM_DEFAULT => 'timestamp|level',
233                ParamValidator::PARAM_TYPE => [
234                    'timestamp',
235                    'user',
236                    'userid',
237                    'comment',
238                    'parsedcomment',
239                    'expiry',
240                    'level'
241                ],
242                ApiBase::PARAM_HELP_MSG_PER_VALUE => [],
243            ],
244            'continue' => [
245                ApiBase::PARAM_HELP_MSG => 'api-help-param-continue',
246            ],
247        ];
248    }
249
250    /** @inheritDoc */
251    protected function getExamplesMessages() {
252        return [
253            'action=query&list=protectedtitles'
254                => 'apihelp-query+protectedtitles-example-simple',
255            'action=query&generator=protectedtitles&gptnamespace=0&prop=linkshere'
256                => 'apihelp-query+protectedtitles-example-generator',
257        ];
258    }
259
260    /** @inheritDoc */
261    public function getHelpUrls() {
262        return 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Protectedtitles';
263    }
264}
265
266/** @deprecated class alias since 1.43 */
267class_alias( ApiQueryProtectedTitles::class, 'ApiQueryProtectedTitles' );