MediaWiki master
ProtectedPagesPager.php
Go to the documentation of this file.
1<?php
22namespace MediaWiki\Pager;
23
25use LogPage;
34use UnexpectedValueException;
37
39
41 private $type;
43 private $level;
45 private $namespace;
47 private $sizetype;
49 private $size;
51 private $indefonly;
53 private $cascadeonly;
55 private $noredirect;
56
57 private CommentStore $commentStore;
58 private LinkBatchFactory $linkBatchFactory;
59 private RowCommentFormatter $rowCommentFormatter;
60
62 private $formattedComments = [];
63
80 public function __construct(
81 IContextSource $context,
82 CommentStore $commentStore,
83 LinkBatchFactory $linkBatchFactory,
84 LinkRenderer $linkRenderer,
85 IConnectionProvider $dbProvider,
86 RowCommentFormatter $rowCommentFormatter,
87 $type,
88 $level,
89 $namespace,
90 $sizetype,
91 $size,
92 $indefonly,
93 $cascadeonly,
94 $noredirect
95 ) {
96 // Set database before parent constructor to avoid setting it there
97 $this->mDb = $dbProvider->getReplicaDatabase();
98 parent::__construct( $context, $linkRenderer );
99 $this->commentStore = $commentStore;
100 $this->linkBatchFactory = $linkBatchFactory;
101 $this->rowCommentFormatter = $rowCommentFormatter;
102 $this->type = $type ?: 'edit';
103 $this->level = $level;
104 $this->namespace = $namespace;
105 $this->sizetype = $sizetype;
106 $this->size = intval( $size );
107 $this->indefonly = (bool)$indefonly;
108 $this->cascadeonly = (bool)$cascadeonly;
109 $this->noredirect = (bool)$noredirect;
110 }
111
112 public function preprocessResults( $result ) {
113 # Do a link batch query
114 $lb = $this->linkBatchFactory->newLinkBatch();
115 $rowsWithComments = [];
116
117 foreach ( $result as $row ) {
118 $lb->add( $row->page_namespace, $row->page_title );
119 // for old protection rows, user and comment are missing
120 if ( $row->actor_name !== null ) {
121 $lb->add( NS_USER, $row->actor_name );
122 $lb->add( NS_USER_TALK, $row->actor_name );
123 }
124 if ( $row->log_timestamp !== null ) {
125 $rowsWithComments[] = $row;
126 }
127 }
128
129 $lb->execute();
130
131 // Format the comments
132 $this->formattedComments = $this->rowCommentFormatter->formatRows(
133 new FakeResultWrapper( $rowsWithComments ),
134 'log_comment',
135 null,
136 null,
137 'pr_id'
138 );
139 }
140
141 protected function getFieldNames() {
142 static $headers = null;
143
144 if ( $headers === null ) {
145 $headers = [
146 'log_timestamp' => 'protectedpages-timestamp',
147 'pr_page' => 'protectedpages-page',
148 'pr_expiry' => 'protectedpages-expiry',
149 'actor_user' => 'protectedpages-performer',
150 'pr_params' => 'protectedpages-params',
151 'log_comment' => 'protectedpages-reason',
152 ];
153 foreach ( $headers as $key => $val ) {
154 $headers[$key] = $this->msg( $val )->text();
155 }
156 }
157
158 return $headers;
159 }
160
166 public function formatValue( $field, $value ) {
168 $row = $this->mCurrentRow;
169 $linkRenderer = $this->getLinkRenderer();
170
171 switch ( $field ) {
172 case 'log_timestamp':
173 // when timestamp is null, this is a old protection row
174 if ( $value === null ) {
175 $formatted = Html::rawElement(
176 'span',
177 [ 'class' => 'mw-protectedpages-unknown' ],
178 $this->msg( 'protectedpages-unknown-timestamp' )->escaped()
179 );
180 } else {
181 $formatted = htmlspecialchars( $this->getLanguage()->userTimeAndDate(
182 $value, $this->getUser() ) );
183 }
184 break;
185
186 case 'pr_page':
187 $title = Title::makeTitleSafe( $row->page_namespace, $row->page_title );
188 if ( !$title ) {
189 $formatted = Html::element(
190 'span',
191 [ 'class' => 'mw-invalidtitle' ],
192 Linker::getInvalidTitleDescription(
193 $this->getContext(),
194 $row->page_namespace,
195 $row->page_title
196 )
197 );
198 } else {
199 $formatted = $linkRenderer->makeLink( $title );
200 }
201 $formatted = Html::rawElement( 'bdi', [
202 'dir' => $this->getLanguage()->getDir()
203 ], $formatted );
204 if ( $row->page_len !== null ) {
205 $formatted .= ' ' . Html::rawElement(
206 'span',
207 [ 'class' => 'mw-protectedpages-length' ],
208 Linker::formatRevisionSize( $row->page_len )
209 );
210 }
211 break;
212
213 case 'pr_expiry':
214 $formatted = htmlspecialchars( $this->getLanguage()->formatExpiry(
215 $value, /* User preference timezone */true, 'infinity', $this->getUser() ) );
216 $title = Title::makeTitleSafe( $row->page_namespace, $row->page_title );
217 if ( $title && $this->getAuthority()->isAllowed( 'protect' ) ) {
218 $changeProtection = $linkRenderer->makeKnownLink(
219 $title,
220 $this->msg( 'protect_change' )->text(),
221 [],
222 [ 'action' => 'unprotect' ]
223 );
224 $formatted .= ' ' . Html::rawElement(
225 'span',
226 [ 'class' => 'mw-protectedpages-actions' ],
227 $this->msg( 'parentheses' )->rawParams( $changeProtection )->escaped()
228 );
229 }
230 break;
231
232 case 'actor_user':
233 // when timestamp is null, this is a old protection row
234 if ( $row->log_timestamp === null ) {
235 $formatted = Html::rawElement(
236 'span',
237 [ 'class' => 'mw-protectedpages-unknown' ],
238 $this->msg( 'protectedpages-unknown-performer' )->escaped()
239 );
240 } else {
241 $username = $row->actor_name;
242 if ( LogEventsList::userCanBitfield(
243 $row->log_deleted,
244 LogPage::DELETED_USER,
245 $this->getAuthority()
246 ) ) {
247 $formatted = Linker::userLink( (int)$value, $username )
248 . Linker::userToolLinks( (int)$value, $username );
249 } else {
250 $formatted = $this->msg( 'rev-deleted-user' )->escaped();
251 }
252 if ( LogEventsList::isDeleted( $row, LogPage::DELETED_USER ) ) {
253 $formatted = '<span class="history-deleted">' . $formatted . '</span>';
254 }
255 }
256 break;
257
258 case 'pr_params':
259 $params = [];
260 // Messages: restriction-level-sysop, restriction-level-autoconfirmed
261 $params[] = $this->msg( 'restriction-level-' . $row->pr_level )->escaped();
262 if ( $row->pr_cascade ) {
263 $params[] = $this->msg( 'protect-summary-cascade' )->escaped();
264 }
265 $formatted = $this->getLanguage()->commaList( $params );
266 break;
267
268 case 'log_comment':
269 // when timestamp is null, this is an old protection row
270 if ( $row->log_timestamp === null ) {
271 $formatted = Html::rawElement(
272 'span',
273 [ 'class' => 'mw-protectedpages-unknown' ],
274 $this->msg( 'protectedpages-unknown-reason' )->escaped()
275 );
276 } else {
277 if ( LogEventsList::userCanBitfield(
278 $row->log_deleted,
279 LogPage::DELETED_COMMENT,
280 $this->getAuthority()
281 ) ) {
282 $formatted = $this->formattedComments[$row->pr_id];
283 } else {
284 $formatted = $this->msg( 'rev-deleted-comment' )->escaped();
285 }
286 if ( LogEventsList::isDeleted( $row, LogPage::DELETED_COMMENT ) ) {
287 $formatted = '<span class="history-deleted">' . $formatted . '</span>';
288 }
289 }
290 break;
291
292 default:
293 throw new UnexpectedValueException( "Unknown field '$field'" );
294 }
295
296 return $formatted;
297 }
298
299 public function getQueryInfo() {
300 $dbr = $this->getDatabase();
301 $conds = [
302 $dbr->expr( 'pr_expiry', '>', $dbr->timestamp() )
303 ->or( 'pr_expiry', '=', null ),
304 'page_id=pr_page',
305 $dbr->expr( 'pr_type', '=', $this->type ),
306 ];
307
308 if ( $this->sizetype == 'min' ) {
309 $conds[] = 'page_len>=' . $this->size;
310 } elseif ( $this->sizetype == 'max' ) {
311 $conds[] = 'page_len<=' . $this->size;
312 }
313
314 if ( $this->indefonly ) {
315 $conds['pr_expiry'] = [ $dbr->getInfinity(), null ];
316 }
317 if ( $this->cascadeonly ) {
318 $conds['pr_cascade'] = 1;
319 }
320 if ( $this->noredirect ) {
321 $conds['page_is_redirect'] = 0;
322 }
323
324 if ( $this->level ) {
325 $conds[] = $dbr->expr( 'pr_level', '=', $this->level );
326 }
327 if ( $this->namespace !== null ) {
328 $conds[] = $dbr->expr( 'page_namespace', '=', $this->namespace );
329 }
330
331 $commentQuery = $this->commentStore->getJoin( 'log_comment' );
332
333 return [
334 'tables' => [
335 'page', 'page_restrictions', 'log_search',
336 'logparen' => [ 'logging', 'actor' ] + $commentQuery['tables'],
337 ],
338 'fields' => [
339 'pr_id',
340 'page_namespace',
341 'page_title',
342 'page_len',
343 'pr_type',
344 'pr_level',
345 'pr_expiry',
346 'pr_cascade',
347 'log_timestamp',
348 'log_deleted',
349 'actor_name',
350 'actor_user'
351 ] + $commentQuery['fields'],
352 'conds' => $conds,
353 'join_conds' => [
354 'log_search' => [
355 'LEFT JOIN', [
356 'ls_field' => 'pr_id', 'ls_value = ' . $dbr->buildStringCast( 'pr_id' )
357 ]
358 ],
359 'logparen' => [
360 'LEFT JOIN', [
361 'ls_log_id = log_id'
362 ]
363 ],
364 'actor' => [
365 'JOIN', [
366 'actor_id=log_actor'
367 ]
368 ]
369 ] + $commentQuery['joins']
370 ];
371 }
372
373 protected function getTableClass() {
374 return parent::getTableClass() . ' mw-protectedpages';
375 }
376
377 public function getIndexField() {
378 return 'pr_id';
379 }
380
381 public function getDefaultSort() {
382 return 'pr_id';
383 }
384
385 protected function isFieldSortable( $field ) {
386 // no index for sorting exists
387 return false;
388 }
389}
390
395class_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.
__construct(IContextSource $context, CommentStore $commentStore, LinkBatchFactory $linkBatchFactory, LinkRenderer $linkRenderer, IConnectionProvider $dbProvider, RowCommentFormatter $rowCommentFormatter, $type, $level, $namespace, $sizetype, $size, $indefonly, $cascadeonly, $noredirect)
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.
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:78
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)