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