Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 127
0.00% covered (danger)
0.00%
0 / 10
CRAP
0.00% covered (danger)
0.00%
0 / 1
ApiQueryProjectPages
0.00% covered (danger)
0.00%
0 / 127
0.00% covered (danger)
0.00%
0 / 10
870
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 / 34
0.00% covered (danger)
0.00%
0 / 1
132
 buildDbQuery
0.00% covered (danger)
0.00%
0 / 44
0.00% covered (danger)
0.00%
0 / 1
72
 handleQueryContinuation
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
2
 generateResultVals
0.00% covered (danger)
0.00%
0 / 12
0.00% covered (danger)
0.00%
0 / 1
12
 getAllowedParams
0.00% covered (danger)
0.00%
0 / 20
0.00% covered (danger)
0.00%
0 / 1
2
 getExamplesMessages
0.00% covered (danger)
0.00%
0 / 8
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
3namespace MediaWiki\Extension\PageAssessments\Api;
4
5use MediaWiki\Api\ApiBase;
6use MediaWiki\Api\ApiPageSet;
7use MediaWiki\Api\ApiQuery;
8use MediaWiki\Api\ApiQueryGeneratorBase;
9use MediaWiki\Extension\PageAssessments\PageAssessmentsDAO;
10use MediaWiki\Title\Title;
11use Wikimedia\ParamValidator\ParamValidator;
12use Wikimedia\ParamValidator\TypeDef\IntegerDef;
13
14/*
15 * API module for retrieving all the pages associated with a project, for example,
16 * WikiProject Medicine. (T119997)
17 */
18class ApiQueryProjectPages extends ApiQueryGeneratorBase {
19
20    /**
21     * Array of project IDs for the projects listed in the API query
22     * @var array
23     */
24    private $projectIds = [];
25
26    public function __construct( ApiQuery $query, string $moduleName ) {
27        // The prefix pp is already used by the pageprops module, so using wpp instead.
28        parent::__construct( $query, $moduleName, 'wpp' );
29    }
30
31    /**
32     * Evaluate the parameters, perform the requested query, and set up the result
33     */
34    public function execute() {
35        $this->run();
36    }
37
38    /**
39     * Evaluate the parameters, perform the requested query, and set up the result for generator mode
40     * @param ApiPageSet $resultPageSet
41     */
42    public function executeGenerator( $resultPageSet ) {
43        $this->run( $resultPageSet );
44    }
45
46    /**
47     * @param ApiPageSet|null $resultPageSet
48     */
49    private function run( $resultPageSet = null ) {
50        $params = $this->extractRequestParams();
51
52        if ( $params['assessments'] && $resultPageSet !== null ) {
53            $this->addWarning( 'apiwarn-pageassessments-nogeneratorassessments' );
54        }
55
56        $this->buildDbQuery( $params, $resultPageSet );
57
58        // If matching projects were found, run the query.
59        if ( $this->projectIds ) {
60            $db_res = $this->select( __METHOD__ );
61        // Otherwise, just set the result to an empty array (still works with foreach).
62        } else {
63            $db_res = [];
64        }
65
66        if ( $resultPageSet === null ) {
67            $result = $this->getResult();
68            $count = 0;
69            foreach ( $db_res as $row ) {
70                if ( ++$count > $params['limit'] ) {
71                    $this->setContinueEnumParameter( 'continue', "$row->project_id|$row->page_id" );
72                    break;
73                }
74
75                // Change project id back into its corresponding project title
76                $projectTitle = $row->project_name;
77                if ( !$projectTitle ) {
78                    continue;
79                }
80
81                // Add information to result
82                $vals = $this->generateResultVals( $row );
83                $fit = $result->addValue(
84                    [ 'query', 'projects', $projectTitle ], $row->page_id, $vals
85                );
86
87                if ( !$fit ) {
88                    $this->setContinueEnumParameter( 'continue', "$row->project_id|$row->page_id" );
89                    break;
90                }
91
92                // Add metadata to make XML results for pages parse better
93                $result->addIndexedTagName( [ 'query', 'projects', $projectTitle ], 'page' );
94                $result->addArrayType( [ 'query', 'projects', $projectTitle ], 'array' );
95            }
96            // Add metadata to make XML results for projects parse better
97            $result->addIndexedTagName( [ 'query', 'projects' ], 'project' );
98            $result->addArrayType( [ 'query', 'projects' ], 'kvp', 'name' );
99        } else {
100            $count = 0;
101            foreach ( $db_res as $row ) {
102                if ( ++$count > $params['limit'] ) {
103                    $this->setContinueEnumParameter( 'continue', "$row->project_id|$row->page_id" );
104                    break;
105                }
106
107                $resultPageSet->processDbRow( $row );
108            }
109        }
110    }
111
112    /**
113     * @param array $params
114     * @param ApiPageSet|null $resultPageSet
115     */
116    private function buildDbQuery( array $params, $resultPageSet ) {
117        $this->addTables( [ 'page', 'page_assessments' ] );
118        $this->addFields( [
119            'page_id' => 'pa_page_id',
120            'project_id' => 'pa_project_id',
121        ] );
122        $this->addJoinConds( [
123            'page' => [
124                'JOIN',
125                [ 'page_id = pa_page_id' ],
126            ]
127        ] );
128
129        if ( $resultPageSet === null ) {
130            $this->addTables( 'page_assessments_projects' );
131            $this->addFields( [
132                'title' => 'page_title',
133                'namespace' => 'page_namespace',
134                'project_name' => 'pap_project_title'
135            ] );
136            $this->addJoinConds( [
137                'page_assessments_projects' => [
138                    'JOIN',
139                    [ 'pa_project_id = pap_project_id' ],
140                ]
141            ] );
142            if ( $params['assessments'] ) {
143                $this->addFields( [
144                    'class' => 'pa_class',
145                    'importance' => 'pa_importance'
146                ] );
147            }
148        } else {
149            $this->addFields( $resultPageSet->getPageTableFields() );
150        }
151
152        if ( isset( $params['projects'] ) ) {
153            // Convert the project names into corresponding IDs
154            foreach ( $params['projects'] as $project ) {
155                $id = PageAssessmentsDAO::getProjectId( $project );
156                if ( $id ) {
157                    $this->projectIds[] = $id;
158                } else {
159                    $this->addWarning( [ 'apiwarn-pageassessments-badproject',
160                        wfEscapeWikiText( $project ) ] );
161                }
162            }
163        }
164
165        // DB stores project IDs, so that's what goes into the where field
166        $this->addWhereFld( 'pa_project_id', $this->projectIds );
167        $this->addOption( 'LIMIT', $params['limit'] + 1 );
168
169        if ( $params['continue'] !== null ) {
170            $this->handleQueryContinuation( $params['continue'] );
171        }
172
173        // If more than 1 project is requested, order by project first.
174        if ( count( $this->projectIds ) > 1 ) {
175            $this->addOption( 'ORDER BY', [ 'pa_project_id', 'pa_page_id' ] );
176        } else {
177            $this->addOption( 'ORDER BY', 'pa_page_id' );
178        }
179    }
180
181    /**
182     * @param string $continueParam
183     */
184    private function handleQueryContinuation( $continueParam ) {
185        $continues = $this->parseContinueParamOrDie( $continueParam, [ 'int', 'int' ] );
186        $this->addWhere( $this->getDB()->buildComparison( '>=', [
187            'pa_project_id' => $continues[0],
188            'pa_page_id' => $continues[1],
189        ] ) );
190    }
191
192    /**
193     * @param \stdClass $row
194     * @return array
195     */
196    private function generateResultVals( $row ) {
197        $title = Title::makeTitle( $row->namespace, $row->title );
198
199        $vals = [
200            'pageid' => (int)$row->page_id,
201            'ns' => (int)$row->namespace,
202            'title' => $title->getPrefixedText(),
203        ];
204
205        if ( isset( $row->class ) && isset( $row->importance ) ) {
206            $vals['assessment'] = [
207                'class' => $row->class,
208                'importance' => $row->importance,
209            ];
210        }
211
212        return $vals;
213    }
214
215    /** @inheritDoc */
216    public function getAllowedParams() {
217        return [
218            'assessments' => [
219                ParamValidator::PARAM_DEFAULT => false,
220                ParamValidator::PARAM_TYPE => 'boolean',
221            ],
222            'projects' => [
223                ParamValidator::PARAM_ISMULTI => true,
224                ParamValidator::PARAM_REQUIRED => true,
225            ],
226            'limit' => [
227                ParamValidator::PARAM_DEFAULT => 10,
228                ParamValidator::PARAM_TYPE => 'limit',
229                IntegerDef::PARAM_MIN => 1,
230                IntegerDef::PARAM_MAX => ApiBase::LIMIT_BIG1,
231                IntegerDef::PARAM_MAX2 => ApiBase::LIMIT_BIG2,
232            ],
233            'continue' => [
234                ApiBase::PARAM_HELP_MSG => 'api-help-param-continue',
235            ],
236        ];
237    }
238
239    /** @inheritDoc */
240    public function getExamplesMessages() {
241        return [
242            'action=query&list=projectpages&wppprojects=Medicine|Anatomy'
243                => 'apihelp-query+projectpages-example-simple-1',
244            'action=query&list=projectpages&wppprojects=Medicine&wppassessments=true'
245                => 'apihelp-query+projectpages-example-simple-2',
246            'action=query&generator=projectpages&prop=info&gwppprojects=Textile%20Arts'
247                => 'apihelp-query+projectpages-example-generator',
248        ];
249    }
250
251    /** @inheritDoc */
252    public function getHelpUrls() {
253        return 'https://www.mediawiki.org/wiki/Special:MyLanguage/Extension:PageAssessments';
254    }
255}