Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 211 |
|
0.00% |
0 / 9 |
CRAP | |
0.00% |
0 / 1 |
ProtectedPagesPager | |
0.00% |
0 / 210 |
|
0.00% |
0 / 9 |
1980 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 15 |
|
0.00% |
0 / 1 |
6 | |||
preprocessResults | |
0.00% |
0 / 24 |
|
0.00% |
0 / 1 |
56 | |||
getFieldNames | |
0.00% |
0 / 13 |
|
0.00% |
0 / 1 |
12 | |||
formatValue | |
0.00% |
0 / 95 |
|
0.00% |
0 / 1 |
420 | |||
getQueryInfo | |
0.00% |
0 / 59 |
|
0.00% |
0 / 1 |
72 | |||
getTableClass | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getIndexField | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getDefaultSort | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
isFieldSortable | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 |
1 | <?php |
2 | /** |
3 | * This program is free software; you can redistribute it and/or modify |
4 | * it under the terms of the GNU General Public License as published by |
5 | * the Free Software Foundation; either version 2 of the License, or |
6 | * (at your option) any later version. |
7 | * |
8 | * This program is distributed in the hope that it will be useful, |
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
11 | * GNU General Public License for more details. |
12 | * |
13 | * You should have received a copy of the GNU General Public License along |
14 | * with this program; if not, write to the Free Software Foundation, Inc., |
15 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
16 | * http://www.gnu.org/copyleft/gpl.html |
17 | * |
18 | * @file |
19 | * @ingroup Pager |
20 | */ |
21 | |
22 | namespace MediaWiki\Pager; |
23 | |
24 | use LogEventsList; |
25 | use LogPage; |
26 | use MediaWiki\Cache\LinkBatchFactory; |
27 | use MediaWiki\Cache\UserCache; |
28 | use MediaWiki\CommentFormatter\RowCommentFormatter; |
29 | use MediaWiki\CommentStore\CommentStore; |
30 | use MediaWiki\Context\IContextSource; |
31 | use MediaWiki\Html\Html; |
32 | use MediaWiki\Linker\Linker; |
33 | use MediaWiki\Linker\LinkRenderer; |
34 | use MediaWiki\Title\Title; |
35 | use UnexpectedValueException; |
36 | use Wikimedia\Rdbms\FakeResultWrapper; |
37 | use Wikimedia\Rdbms\IConnectionProvider; |
38 | |
39 | class ProtectedPagesPager extends TablePager { |
40 | |
41 | public $mConds; |
42 | private $type; |
43 | private $level; |
44 | /** @var int|null */ |
45 | private $namespace; |
46 | private $sizetype; |
47 | /** @var int */ |
48 | private $size; |
49 | private $indefonly; |
50 | private $cascadeonly; |
51 | private $noredirect; |
52 | |
53 | private CommentStore $commentStore; |
54 | private LinkBatchFactory $linkBatchFactory; |
55 | private UserCache $userCache; |
56 | private RowCommentFormatter $rowCommentFormatter; |
57 | |
58 | /** @var string[] */ |
59 | private $formattedComments = []; |
60 | |
61 | /** |
62 | * @param IContextSource $context |
63 | * @param CommentStore $commentStore |
64 | * @param LinkBatchFactory $linkBatchFactory |
65 | * @param LinkRenderer $linkRenderer |
66 | * @param IConnectionProvider $dbProvider |
67 | * @param RowCommentFormatter $rowCommentFormatter |
68 | * @param UserCache $userCache |
69 | * @param array $conds |
70 | * @param string $type |
71 | * @param string $level |
72 | * @param int|null $namespace |
73 | * @param string $sizetype |
74 | * @param int|null $size |
75 | * @param bool $indefonly |
76 | * @param bool $cascadeonly |
77 | * @param bool $noredirect |
78 | */ |
79 | public function __construct( |
80 | IContextSource $context, |
81 | CommentStore $commentStore, |
82 | LinkBatchFactory $linkBatchFactory, |
83 | LinkRenderer $linkRenderer, |
84 | IConnectionProvider $dbProvider, |
85 | RowCommentFormatter $rowCommentFormatter, |
86 | UserCache $userCache, |
87 | $conds, |
88 | $type, |
89 | $level, |
90 | $namespace, |
91 | $sizetype, |
92 | $size, |
93 | $indefonly, |
94 | $cascadeonly, |
95 | $noredirect |
96 | ) { |
97 | // Set database before parent constructor to avoid setting it there |
98 | $this->mDb = $dbProvider->getReplicaDatabase(); |
99 | parent::__construct( $context, $linkRenderer ); |
100 | $this->commentStore = $commentStore; |
101 | $this->linkBatchFactory = $linkBatchFactory; |
102 | $this->rowCommentFormatter = $rowCommentFormatter; |
103 | $this->userCache = $userCache; |
104 | $this->mConds = $conds; |
105 | $this->type = $type ?: 'edit'; |
106 | $this->level = $level; |
107 | $this->namespace = $namespace; |
108 | $this->sizetype = $sizetype; |
109 | $this->size = intval( $size ); |
110 | $this->indefonly = (bool)$indefonly; |
111 | $this->cascadeonly = (bool)$cascadeonly; |
112 | $this->noredirect = (bool)$noredirect; |
113 | } |
114 | |
115 | public function preprocessResults( $result ) { |
116 | # Do a link batch query |
117 | $lb = $this->linkBatchFactory->newLinkBatch(); |
118 | $userids = []; |
119 | $rowsWithComments = []; |
120 | |
121 | foreach ( $result as $row ) { |
122 | $lb->add( $row->page_namespace, $row->page_title ); |
123 | if ( $row->actor_user !== null ) { |
124 | $userids[] = $row->actor_user; |
125 | } |
126 | if ( $row->log_timestamp !== null ) { |
127 | $rowsWithComments[] = $row; |
128 | } |
129 | } |
130 | |
131 | // fill LinkBatch with user page and user talk |
132 | if ( count( $userids ) ) { |
133 | $this->userCache->doQuery( $userids, [], __METHOD__ ); |
134 | foreach ( $userids as $userid ) { |
135 | $name = $this->userCache->getProp( $userid, 'name' ); |
136 | if ( $name !== false ) { |
137 | $lb->add( NS_USER, $name ); |
138 | $lb->add( NS_USER_TALK, $name ); |
139 | } |
140 | } |
141 | } |
142 | |
143 | $lb->execute(); |
144 | |
145 | // Format the comments |
146 | $this->formattedComments = $this->rowCommentFormatter->formatRows( |
147 | new FakeResultWrapper( $rowsWithComments ), |
148 | 'log_comment', |
149 | null, |
150 | null, |
151 | 'pr_id' |
152 | ); |
153 | } |
154 | |
155 | protected function getFieldNames() { |
156 | static $headers = null; |
157 | |
158 | if ( $headers == [] ) { |
159 | $headers = [ |
160 | 'log_timestamp' => 'protectedpages-timestamp', |
161 | 'pr_page' => 'protectedpages-page', |
162 | 'pr_expiry' => 'protectedpages-expiry', |
163 | 'actor_user' => 'protectedpages-performer', |
164 | 'pr_params' => 'protectedpages-params', |
165 | 'log_comment' => 'protectedpages-reason', |
166 | ]; |
167 | foreach ( $headers as $key => $val ) { |
168 | $headers[$key] = $this->msg( $val )->text(); |
169 | } |
170 | } |
171 | |
172 | return $headers; |
173 | } |
174 | |
175 | /** |
176 | * @param string $field |
177 | * @param string|null $value |
178 | * @return string HTML |
179 | */ |
180 | public function formatValue( $field, $value ) { |
181 | /** @var stdClass $row */ |
182 | $row = $this->mCurrentRow; |
183 | $linkRenderer = $this->getLinkRenderer(); |
184 | |
185 | switch ( $field ) { |
186 | case 'log_timestamp': |
187 | // when timestamp is null, this is a old protection row |
188 | if ( $value === null ) { |
189 | $formatted = Html::rawElement( |
190 | 'span', |
191 | [ 'class' => 'mw-protectedpages-unknown' ], |
192 | $this->msg( 'protectedpages-unknown-timestamp' )->escaped() |
193 | ); |
194 | } else { |
195 | $formatted = htmlspecialchars( $this->getLanguage()->userTimeAndDate( |
196 | $value, $this->getUser() ) ); |
197 | } |
198 | break; |
199 | |
200 | case 'pr_page': |
201 | $title = Title::makeTitleSafe( $row->page_namespace, $row->page_title ); |
202 | if ( !$title ) { |
203 | $formatted = Html::element( |
204 | 'span', |
205 | [ 'class' => 'mw-invalidtitle' ], |
206 | Linker::getInvalidTitleDescription( |
207 | $this->getContext(), |
208 | $row->page_namespace, |
209 | $row->page_title |
210 | ) |
211 | ); |
212 | } else { |
213 | $formatted = $linkRenderer->makeLink( $title ); |
214 | } |
215 | if ( $row->page_len !== null ) { |
216 | $formatted .= $this->getLanguage()->getDirMark() . |
217 | ' ' . Html::rawElement( |
218 | 'span', |
219 | [ 'class' => 'mw-protectedpages-length' ], |
220 | Linker::formatRevisionSize( $row->page_len ) |
221 | ); |
222 | } |
223 | break; |
224 | |
225 | case 'pr_expiry': |
226 | $formatted = htmlspecialchars( $this->getLanguage()->formatExpiry( |
227 | $value, /* User preference timezone */true, 'infinity', $this->getUser() ) ); |
228 | $title = Title::makeTitleSafe( $row->page_namespace, $row->page_title ); |
229 | if ( $title && $this->getAuthority()->isAllowed( 'protect' ) ) { |
230 | $changeProtection = $linkRenderer->makeKnownLink( |
231 | $title, |
232 | $this->msg( 'protect_change' )->text(), |
233 | [], |
234 | [ 'action' => 'unprotect' ] |
235 | ); |
236 | $formatted .= ' ' . Html::rawElement( |
237 | 'span', |
238 | [ 'class' => 'mw-protectedpages-actions' ], |
239 | $this->msg( 'parentheses' )->rawParams( $changeProtection )->escaped() |
240 | ); |
241 | } |
242 | break; |
243 | |
244 | case 'actor_user': |
245 | // when timestamp is null, this is a old protection row |
246 | if ( $row->log_timestamp === null ) { |
247 | $formatted = Html::rawElement( |
248 | 'span', |
249 | [ 'class' => 'mw-protectedpages-unknown' ], |
250 | $this->msg( 'protectedpages-unknown-performer' )->escaped() |
251 | ); |
252 | } else { |
253 | $username = $row->actor_name; |
254 | if ( LogEventsList::userCanBitfield( |
255 | $row->log_deleted, |
256 | LogPage::DELETED_USER, |
257 | $this->getAuthority() |
258 | ) ) { |
259 | $formatted = Linker::userLink( (int)$value, $username ) |
260 | . Linker::userToolLinks( (int)$value, $username ); |
261 | } else { |
262 | $formatted = $this->msg( 'rev-deleted-user' )->escaped(); |
263 | } |
264 | if ( LogEventsList::isDeleted( $row, LogPage::DELETED_USER ) ) { |
265 | $formatted = '<span class="history-deleted">' . $formatted . '</span>'; |
266 | } |
267 | } |
268 | break; |
269 | |
270 | case 'pr_params': |
271 | $params = []; |
272 | // Messages: restriction-level-sysop, restriction-level-autoconfirmed |
273 | $params[] = $this->msg( 'restriction-level-' . $row->pr_level )->escaped(); |
274 | if ( $row->pr_cascade ) { |
275 | $params[] = $this->msg( 'protect-summary-cascade' )->escaped(); |
276 | } |
277 | $formatted = $this->getLanguage()->commaList( $params ); |
278 | break; |
279 | |
280 | case 'log_comment': |
281 | // when timestamp is null, this is an old protection row |
282 | if ( $row->log_timestamp === null ) { |
283 | $formatted = Html::rawElement( |
284 | 'span', |
285 | [ 'class' => 'mw-protectedpages-unknown' ], |
286 | $this->msg( 'protectedpages-unknown-reason' )->escaped() |
287 | ); |
288 | } else { |
289 | if ( LogEventsList::userCanBitfield( |
290 | $row->log_deleted, |
291 | LogPage::DELETED_COMMENT, |
292 | $this->getAuthority() |
293 | ) ) { |
294 | $formatted = $this->formattedComments[$row->pr_id]; |
295 | } else { |
296 | $formatted = $this->msg( 'rev-deleted-comment' )->escaped(); |
297 | } |
298 | if ( LogEventsList::isDeleted( $row, LogPage::DELETED_COMMENT ) ) { |
299 | $formatted = '<span class="history-deleted">' . $formatted . '</span>'; |
300 | } |
301 | } |
302 | break; |
303 | |
304 | default: |
305 | throw new UnexpectedValueException( "Unknown field '$field'" ); |
306 | } |
307 | |
308 | return $formatted; |
309 | } |
310 | |
311 | public function getQueryInfo() { |
312 | $dbr = $this->getDatabase(); |
313 | $conds = $this->mConds; |
314 | $conds[] = $dbr->expr( 'pr_expiry', '>', $dbr->timestamp() ) |
315 | ->or( 'pr_expiry', '=', null ); |
316 | $conds[] = 'page_id=pr_page'; |
317 | $conds[] = $dbr->expr( 'pr_type', '=', $this->type ); |
318 | |
319 | if ( $this->sizetype == 'min' ) { |
320 | $conds[] = 'page_len>=' . $this->size; |
321 | } elseif ( $this->sizetype == 'max' ) { |
322 | $conds[] = 'page_len<=' . $this->size; |
323 | } |
324 | |
325 | if ( $this->indefonly ) { |
326 | $conds['pr_expiry'] = [ $dbr->getInfinity(), null ]; |
327 | } |
328 | if ( $this->cascadeonly ) { |
329 | $conds[] = 'pr_cascade = 1'; |
330 | } |
331 | if ( $this->noredirect ) { |
332 | $conds[] = 'page_is_redirect = 0'; |
333 | } |
334 | |
335 | if ( $this->level ) { |
336 | $conds[] = $dbr->expr( 'pr_level', '=', $this->level ); |
337 | } |
338 | if ( $this->namespace !== null ) { |
339 | $conds[] = $dbr->expr( 'page_namespace', '=', $this->namespace ); |
340 | } |
341 | |
342 | $commentQuery = $this->commentStore->getJoin( 'log_comment' ); |
343 | |
344 | return [ |
345 | 'tables' => [ |
346 | 'page', 'page_restrictions', 'log_search', |
347 | 'logparen' => [ 'logging', 'actor' ] + $commentQuery['tables'], |
348 | ], |
349 | 'fields' => [ |
350 | 'pr_id', |
351 | 'page_namespace', |
352 | 'page_title', |
353 | 'page_len', |
354 | 'pr_type', |
355 | 'pr_level', |
356 | 'pr_expiry', |
357 | 'pr_cascade', |
358 | 'log_timestamp', |
359 | 'log_deleted', |
360 | 'actor_name', |
361 | 'actor_user' |
362 | ] + $commentQuery['fields'], |
363 | 'conds' => $conds, |
364 | 'join_conds' => [ |
365 | 'log_search' => [ |
366 | 'LEFT JOIN', [ |
367 | 'ls_field' => 'pr_id', 'ls_value = ' . $dbr->buildStringCast( 'pr_id' ) |
368 | ] |
369 | ], |
370 | 'logparen' => [ |
371 | 'LEFT JOIN', [ |
372 | 'ls_log_id = log_id' |
373 | ] |
374 | ], |
375 | 'actor' => [ |
376 | 'JOIN', [ |
377 | 'actor_id=log_actor' |
378 | ] |
379 | ] |
380 | ] + $commentQuery['joins'] |
381 | ]; |
382 | } |
383 | |
384 | protected function getTableClass() { |
385 | return parent::getTableClass() . ' mw-protectedpages'; |
386 | } |
387 | |
388 | public function getIndexField() { |
389 | return 'pr_id'; |
390 | } |
391 | |
392 | public function getDefaultSort() { |
393 | return 'pr_id'; |
394 | } |
395 | |
396 | protected function isFieldSortable( $field ) { |
397 | // no index for sorting exists |
398 | return false; |
399 | } |
400 | } |
401 | |
402 | /** |
403 | * Retain the old class name for backwards compatibility. |
404 | * @deprecated since 1.41 |
405 | */ |
406 | class_alias( ProtectedPagesPager::class, 'ProtectedPagesPager' ); |