MediaWiki master
ProtectedPagesPager.php
Go to the documentation of this file.
1<?php
22namespace MediaWiki\Pager;
23
25use LogPage;
35use UnexpectedValueException;
38
40
41 public $mConds;
42 private $type;
43 private $level;
45 private $namespace;
46 private $sizetype;
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
59 private $formattedComments = [];
60
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
180 public function formatValue( $field, $value ) {
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
406class_alias( ProtectedPagesPager::class, 'ProtectedPagesPager' );
const NS_USER
Definition Defines.php:67
const NS_USER_TALK
Definition Defines.php:68
array $params
The job parameters.
Class to simplify the use of log pages.
Definition LogPage.php:45
This is basically a CommentFormatter with a CommentStore dependency, allowing it to retrieve comment ...
Handle database storage of comments such as edit summaries and log reasons.
msg( $key,... $params)
Get a Message object with context set Parameters are the same as wfMessage()
getContext()
Get the base IContextSource object.
This class is a collection of static functions that serve two purposes:
Definition Html.php:56
Class that generates HTML for internal links.
Some internal bits split of from Skin.php.
Definition Linker.php:63
getDatabase()
Get the Database object in use.
getDefaultSort()
The database field name used as a default sort order.
preprocessResults( $result)
Pre-process results; useful for performing batch existence checks, etc.
getQueryInfo()
Provides all parameters needed for the main paged query.
getTableClass()
TablePager relies on mw-datatable for styling, see T214208.
isFieldSortable( $field)
Return true if the named field should be sortable by the UI, false otherwise.
__construct(IContextSource $context, CommentStore $commentStore, LinkBatchFactory $linkBatchFactory, LinkRenderer $linkRenderer, IConnectionProvider $dbProvider, RowCommentFormatter $rowCommentFormatter, UserCache $userCache, $conds, $type, $level, $namespace, $sizetype, $size, $indefonly, $cascadeonly, $noredirect)
getFieldNames()
An array mapping database field names to a textual description of the field name, for use in the tabl...
getIndexField()
Returns the name of the index field.If the pager supports multiple orders, it may return an array of ...
Table-based display with a user-selectable sort order.
Represents a title within MediaWiki.
Definition Title.php:79
Overloads the relevant methods of the real ResultWrapper so it doesn't go anywhere near an actual dat...
Interface for objects which can provide a MediaWiki context on request.
Provide primary and replica IDatabase connections.
getReplicaDatabase( $domain=false, $group=null)
Get connection to a replica database.
element(SerializerNode $parent, SerializerNode $node, $contents)