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