Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 97 |
|
0.00% |
0 / 9 |
CRAP | |
0.00% |
0 / 1 |
UnreviewedPagesPager | |
0.00% |
0 / 97 |
|
0.00% |
0 / 9 |
650 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 13 |
|
0.00% |
0 / 1 |
30 | |||
setLimit | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
formatRow | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getQueryInfo | |
0.00% |
0 / 39 |
|
0.00% |
0 / 1 |
56 | |||
getQueryCacheInfo | |
0.00% |
0 / 36 |
|
0.00% |
0 / 1 |
42 | |||
getIndexField | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
doBatchLookups | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
6 | |||
getStartBody | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getEndBody | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 |
1 | <?php |
2 | |
3 | use MediaWiki\MediaWikiServices; |
4 | use MediaWiki\Pager\AlphabeticPager; |
5 | |
6 | /** |
7 | * Query to list out unreviewed pages |
8 | */ |
9 | class UnreviewedPagesPager extends AlphabeticPager { |
10 | /** @var UnreviewedPages */ |
11 | private $mForm; |
12 | |
13 | /** @var bool */ |
14 | private $live; |
15 | |
16 | /** @var int */ |
17 | private $namespace; |
18 | |
19 | /** @var string|null */ |
20 | private $category; |
21 | |
22 | /** @var bool */ |
23 | private $showredirs; |
24 | |
25 | /** @var int */ |
26 | private $level; |
27 | |
28 | // Don't get too expensive |
29 | private const PAGE_LIMIT = 50; |
30 | |
31 | /** |
32 | * @param UnreviewedPages $form |
33 | * @param bool $live |
34 | * @param int|null $namespace |
35 | * @param bool $redirs |
36 | * @param string|null $category |
37 | * @param int $level |
38 | */ |
39 | public function __construct( |
40 | $form, $live, $namespace, $redirs = false, $category = null, $level = 0 |
41 | ) { |
42 | $this->mForm = $form; |
43 | $this->live = (bool)$live; |
44 | # Must be a content page... |
45 | if ( $namespace !== null ) { |
46 | $namespace = (int)$namespace; |
47 | } |
48 | # Must be a single NS for performance reasons |
49 | if ( $namespace === null || !FlaggedRevs::isReviewNamespace( $namespace ) ) { |
50 | $namespace = FlaggedRevs::getFirstReviewNamespace(); |
51 | } |
52 | $this->namespace = $namespace; |
53 | $this->category = $category ? str_replace( ' ', '_', $category ) : null; |
54 | $this->level = intval( $level ); |
55 | $this->showredirs = (bool)$redirs; |
56 | parent::__construct(); |
57 | // Don't get too expensive |
58 | $this->mLimitsShown = [ 20, 50 ]; |
59 | $this->setLimit( $this->mLimit ); // apply max limit |
60 | } |
61 | |
62 | /** |
63 | * @inheritDoc |
64 | */ |
65 | public function setLimit( $limit ) { |
66 | $this->mLimit = min( $limit, self::PAGE_LIMIT ); |
67 | } |
68 | |
69 | /** |
70 | * @inheritDoc |
71 | */ |
72 | public function formatRow( $row ) { |
73 | return $this->mForm->formatRow( $row ); |
74 | } |
75 | |
76 | /** |
77 | * @inheritDoc |
78 | */ |
79 | public function getQueryInfo() { |
80 | if ( !$this->live ) { |
81 | return $this->getQueryCacheInfo(); |
82 | } |
83 | $fields = [ 'page_namespace', 'page_title', 'page_len', 'page_id', |
84 | 'creation' => 'MIN(rev_timestamp)' ]; |
85 | $groupBy = [ 'page_namespace', 'page_title', 'page_len', 'page_id' ]; |
86 | # Filter by level |
87 | $conds = []; |
88 | if ( $this->level == 1 ) { |
89 | $conds[] = $this->mDb->expr( 'fp_page_id', '=', null )->or( 'fp_quality', '=', 0 ); |
90 | } else { |
91 | $conds['fp_page_id'] = null; |
92 | } |
93 | # Reviewable pages only |
94 | $conds['page_namespace'] = $this->namespace; |
95 | # No redirects |
96 | if ( !$this->showredirs ) { |
97 | $conds['page_is_redirect'] = 0; |
98 | } |
99 | # Filter by category |
100 | if ( $this->category != '' ) { |
101 | $tables = [ 'categorylinks', 'page', 'flaggedpages', 'revision' ]; |
102 | $fields[] = 'cl_sortkey'; |
103 | $groupBy[] = 'cl_sortkey'; |
104 | $conds['cl_to'] = $this->category; |
105 | $conds[] = 'cl_from = page_id'; |
106 | # Note: single NS always specified |
107 | if ( $this->namespace === NS_FILE ) { |
108 | $conds['cl_type'] = 'file'; |
109 | } elseif ( $this->namespace === NS_CATEGORY ) { |
110 | $conds['cl_type'] = 'subcat'; |
111 | } else { |
112 | $conds['cl_type'] = 'page'; |
113 | } |
114 | $this->mIndexField = 'cl_sortkey'; |
115 | $useIndex = [ 'categorylinks' => 'cl_sortkey' ]; |
116 | } else { |
117 | $tables = [ 'page', 'flaggedpages', 'revision' ]; |
118 | $this->mIndexField = 'page_title'; |
119 | $useIndex = [ 'page' => 'page_name_title' ]; |
120 | } |
121 | $useIndex['revision'] = 'rev_page_timestamp'; |
122 | return [ |
123 | 'tables' => $tables, |
124 | 'fields' => $fields, |
125 | 'conds' => $conds, |
126 | 'options' => [ 'USE INDEX' => $useIndex, 'GROUP BY' => $groupBy ], |
127 | 'join_conds' => [ |
128 | 'revision' => [ 'LEFT JOIN', 'rev_page=page_id' ], // Get creation date |
129 | 'flaggedpages' => [ 'LEFT JOIN', 'fp_page_id=page_id' ] |
130 | ] |
131 | ]; |
132 | } |
133 | |
134 | /** |
135 | * @return array |
136 | */ |
137 | private function getQueryCacheInfo() { |
138 | $conds = []; |
139 | $fields = [ 'page_namespace', 'page_title', 'page_len', 'page_id', |
140 | 'qc_value', 'creation' => 'MIN(rev_timestamp)' ]; |
141 | # Re-join on flaggedpages to double-check since things |
142 | # could have changed since the cache date. Also, use |
143 | # the proper cache for this level. |
144 | if ( $this->level == 1 ) { |
145 | $conds['qc_type'] = 'fr_unreviewedpages_q'; |
146 | $conds[] = $this->mDb->expr( 'fp_page_id', '=', null )->or( 'fp_quality', '<', 1 ); |
147 | } else { |
148 | $conds['qc_type'] = 'fr_unreviewedpages'; |
149 | $conds['fp_page_id'] = null; |
150 | } |
151 | # Reviewable pages only |
152 | $conds['qc_namespace'] = $this->namespace; |
153 | # No redirects |
154 | if ( !$this->showredirs ) { |
155 | $conds['page_is_redirect'] = 0; |
156 | } |
157 | $this->mIndexField = 'qc_value'; // page_id |
158 | # Filter by category |
159 | if ( $this->category != '' ) { |
160 | $tables = [ 'page', 'categorylinks', 'querycache', 'flaggedpages', 'revision' ]; |
161 | $conds['cl_to'] = $this->category; |
162 | $conds[] = 'cl_from = qc_value'; // page_id |
163 | # Note: single NS always specified |
164 | if ( $this->namespace === NS_FILE ) { |
165 | $conds['cl_type'] = 'file'; |
166 | } elseif ( $this->namespace === NS_CATEGORY ) { |
167 | $conds['cl_type'] = 'subcat'; |
168 | } else { |
169 | $conds['cl_type'] = 'page'; |
170 | } |
171 | } else { |
172 | $tables = [ 'page', 'querycache', 'flaggedpages', 'revision' ]; |
173 | } |
174 | |
175 | $useIndex = [ 'querycache' => 'qc_type', 'page' => 'PRIMARY', 'revision' => 'rev_page_timestamp' ]; |
176 | return [ |
177 | 'tables' => $tables, |
178 | 'fields' => $fields, |
179 | 'conds' => $conds, |
180 | 'options' => [ 'USE INDEX' => $useIndex, 'GROUP BY' => 'qc_value' ], |
181 | 'join_conds' => [ |
182 | 'querycache' => [ 'LEFT JOIN', 'qc_value=page_id' ], |
183 | 'revision' => [ 'LEFT JOIN', 'rev_page=page_id' ], // Get creation date |
184 | 'flaggedpages' => [ 'LEFT JOIN', 'fp_page_id=page_id' ], |
185 | 'categorylinks' => [ 'LEFT JOIN', |
186 | [ 'cl_from=page_id', 'cl_to' => $this->category ] ] |
187 | ] |
188 | ]; |
189 | } |
190 | |
191 | /** |
192 | * @inheritDoc |
193 | */ |
194 | public function getIndexField() { |
195 | return $this->mIndexField; |
196 | } |
197 | |
198 | /** |
199 | * @inheritDoc |
200 | */ |
201 | protected function doBatchLookups() { |
202 | $lb = MediaWikiServices::getInstance()->getLinkBatchFactory()->newLinkBatch(); |
203 | foreach ( $this->mResult as $row ) { |
204 | $lb->add( $row->page_namespace, $row->page_title ); |
205 | } |
206 | $lb->execute(); |
207 | } |
208 | |
209 | /** |
210 | * @return string HTML |
211 | */ |
212 | protected function getStartBody() { |
213 | return '<ul>'; |
214 | } |
215 | |
216 | /** |
217 | * @return string HTML |
218 | */ |
219 | protected function getEndBody() { |
220 | return '</ul>'; |
221 | } |
222 | } |