Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 93
0.00% covered (danger)
0.00%
0 / 9
CRAP
0.00% covered (danger)
0.00%
0 / 1
ApiQueryQueryPage
0.00% covered (danger)
0.00%
0 / 92
0.00% covered (danger)
0.00%
0 / 9
812
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 5
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
 getSpecialPage
0.00% covered (danger)
0.00%
0 / 12
0.00% covered (danger)
0.00%
0 / 1
12
 run
0.00% covered (danger)
0.00%
0 / 47
0.00% covered (danger)
0.00%
0 / 1
306
 getCacheMode
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 getAllowedParams
0.00% covered (danger)
0.00%
0 / 17
0.00% covered (danger)
0.00%
0 / 1
2
 getExamplesMessages
0.00% covered (danger)
0.00%
0 / 4
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 © 2010 Roan Kattouw <roan.kattouw@gmail.com>
4 *
5 * @license GPL-2.0-or-later
6 * @file
7 */
8
9namespace MediaWiki\Api;
10
11use MediaWiki\MainConfigNames;
12use MediaWiki\SpecialPage\QueryPage;
13use MediaWiki\SpecialPage\SpecialPageFactory;
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 get the results of a QueryPage-based special page
21 *
22 * @ingroup API
23 */
24class ApiQueryQueryPage extends ApiQueryGeneratorBase {
25
26    /**
27     * @var string[] list of special page names
28     */
29    private $queryPages;
30
31    public function __construct(
32        ApiQuery $query,
33        string $moduleName,
34        private readonly SpecialPageFactory $specialPageFactory,
35    ) {
36        parent::__construct( $query, $moduleName, 'qp' );
37        $this->queryPages = array_values( array_diff(
38            array_column( QueryPage::getPages(), 1 ), // [ class, name ]
39            $this->getConfig()->get( MainConfigNames::APIUselessQueryPages )
40        ) );
41    }
42
43    public function execute() {
44        $this->run();
45    }
46
47    /** @inheritDoc */
48    public function executeGenerator( $resultPageSet ) {
49        $this->run( $resultPageSet );
50    }
51
52    /**
53     * @param string $name
54     * @return QueryPage
55     */
56    private function getSpecialPage( $name ): QueryPage {
57        $qp = $this->specialPageFactory->getPage( $name );
58        if ( !$qp ) {
59            self::dieDebug(
60                __METHOD__,
61                'SpecialPageFactory failed to create special page ' . $name
62            );
63        }
64        if ( !( $qp instanceof QueryPage ) ) {
65            self::dieDebug(
66                __METHOD__,
67                'Special page ' . $name . ' is not a QueryPage'
68            );
69        }
70        return $qp;
71    }
72
73    /**
74     * @param ApiPageSet|null $resultPageSet Set when used as a generator, null otherwise
75     */
76    public function run( $resultPageSet = null ) {
77        $params = $this->extractRequestParams();
78        $result = $this->getResult();
79
80        $qp = $this->getSpecialPage( $params['page'] );
81        if ( !$qp->userCanExecute( $this->getUser() ) ) {
82            $this->dieWithError( 'apierror-specialpage-cantexecute' );
83        }
84
85        if ( $resultPageSet === null ) {
86            $r = [ 'name' => $params['page'] ];
87            if ( $qp->isCached() ) {
88                if ( !$qp->isCacheable() ) {
89                    $r['disabled'] = true;
90                } else {
91                    $r['cached'] = true;
92                    $ts = $qp->getCachedTimestamp();
93                    if ( $ts ) {
94                        $r['cachedtimestamp'] = wfTimestamp( TS::ISO_8601, $ts );
95                    }
96                    $r['maxresults'] = $this->getConfig()->get( MainConfigNames::QueryCacheLimit );
97                }
98            }
99            $result->addValue( [ 'query' ], $this->getModuleName(), $r );
100        }
101
102        if ( $qp->isCached() && !$qp->isCacheable() ) {
103            // Disabled query page, don't run the query
104            return;
105        }
106
107        $res = $qp->doQuery( $params['offset'], $params['limit'] + 1 );
108        $count = 0;
109        $titles = [];
110        foreach ( $res as $row ) {
111            if ( ++$count > $params['limit'] ) {
112                // We've had enough
113                $this->setContinueEnumParameter( 'offset', $params['offset'] + $params['limit'] );
114                break;
115            }
116
117            $title = Title::makeTitle( $row->namespace, $row->title );
118            if ( $resultPageSet === null ) {
119                $data = [];
120                if ( isset( $row->value ) ) {
121                    $data['value'] = $row->value;
122                    if ( $qp->usesTimestamps() ) {
123                        $data['timestamp'] = wfTimestamp( TS::ISO_8601, $row->value );
124                    }
125                }
126                self::addTitleInfo( $data, $title );
127
128                foreach ( $row as $field => $value ) {
129                    if ( !in_array( $field, [ 'namespace', 'title', 'value', 'qc_type' ] ) ) {
130                        $data['databaseResult'][$field] = $value;
131                    }
132                }
133
134                $fit = $result->addValue( [ 'query', $this->getModuleName(), 'results' ], null, $data );
135                if ( !$fit ) {
136                    $this->setContinueEnumParameter( 'offset', $params['offset'] + $count - 1 );
137                    break;
138                }
139            } else {
140                $titles[] = $title;
141            }
142        }
143        if ( $resultPageSet === null ) {
144            $result->addIndexedTagName(
145                [ 'query', $this->getModuleName(), 'results' ],
146                'page'
147            );
148        } else {
149            $resultPageSet->populateFromTitles( $titles );
150        }
151    }
152
153    /** @inheritDoc */
154    public function getCacheMode( $params ) {
155        $qp = $this->getSpecialPage( $params['page'] );
156        if ( $qp->getRestriction() != '' ) {
157            return 'private';
158        }
159
160        return 'public';
161    }
162
163    /** @inheritDoc */
164    public function getAllowedParams() {
165        return [
166            'page' => [
167                ParamValidator::PARAM_TYPE => $this->queryPages,
168                ParamValidator::PARAM_REQUIRED => true
169            ],
170            'offset' => [
171                ParamValidator::PARAM_DEFAULT => 0,
172                ApiBase::PARAM_HELP_MSG => 'api-help-param-continue',
173            ],
174            'limit' => [
175                ParamValidator::PARAM_DEFAULT => 10,
176                ParamValidator::PARAM_TYPE => 'limit',
177                IntegerDef::PARAM_MIN => 1,
178                IntegerDef::PARAM_MAX => ApiBase::LIMIT_BIG1,
179                IntegerDef::PARAM_MAX2 => ApiBase::LIMIT_BIG2
180            ],
181        ];
182    }
183
184    /** @inheritDoc */
185    protected function getExamplesMessages() {
186        return [
187            'action=query&list=querypage&qppage=Ancientpages'
188                => 'apihelp-query+querypage-example-ancientpages',
189        ];
190    }
191
192    /** @inheritDoc */
193    public function getHelpUrls() {
194        return 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Querypage';
195    }
196}
197
198/** @deprecated class alias since 1.43 */
199class_alias( ApiQueryQueryPage::class, 'ApiQueryQueryPage' );