MediaWiki master
ProtectedPagesPager.php
Go to the documentation of this file.
1<?php
8namespace MediaWiki\Pager;
9
10use MediaWiki\Cache\LinkBatchFactory;
21use UnexpectedValueException;
24
26
27 private string $type;
28 private ?string $level;
29 private ?int $namespace;
30 private ?string $sizetype;
31 private int $size;
32 private bool $indefonly;
33 private bool $cascadeonly;
34 private bool $noredirect;
35
36 private CommentStore $commentStore;
37 private LinkBatchFactory $linkBatchFactory;
38 private RowCommentFormatter $rowCommentFormatter;
39
41 private array $formattedComments = [];
42
43 public function __construct(
44 IContextSource $context,
45 CommentStore $commentStore,
46 LinkBatchFactory $linkBatchFactory,
47 LinkRenderer $linkRenderer,
48 IConnectionProvider $dbProvider,
49 RowCommentFormatter $rowCommentFormatter,
50 ?string $type,
51 ?string $level,
52 ?int $namespace,
53 ?string $sizetype,
54 ?int $size,
55 bool $indefonly,
56 bool $cascadeonly,
57 bool $noredirect
58 ) {
59 // Set database before parent constructor to avoid setting it there
60 $this->mDb = $dbProvider->getReplicaDatabase();
61 parent::__construct( $context, $linkRenderer );
62 $this->commentStore = $commentStore;
63 $this->linkBatchFactory = $linkBatchFactory;
64 $this->rowCommentFormatter = $rowCommentFormatter;
65 $this->type = $type ?? 'edit';
66 $this->level = $level;
67 $this->namespace = $namespace;
68 $this->sizetype = $sizetype;
69 $this->size = $size ?? 0;
70 $this->indefonly = $indefonly;
71 $this->cascadeonly = $cascadeonly;
72 $this->noredirect = $noredirect;
73 }
74
76 public function preprocessResults( $result ) {
77 # Do a link batch query
78 $lb = $this->linkBatchFactory->newLinkBatch();
79 $rowsWithComments = [];
80
81 foreach ( $result as $row ) {
82 $lb->add( $row->page_namespace, $row->page_title );
83 // for old protection rows, user and comment are missing
84 if ( $row->actor_name !== null ) {
85 $lb->addUser( new UserIdentityValue( $row->actor_user, $row->actor_name ) );
86 }
87 if ( $row->log_timestamp !== null ) {
88 $rowsWithComments[] = $row;
89 }
90 }
91
92 $lb->execute();
93
94 // Format the comments
95 $this->formattedComments = $this->rowCommentFormatter->formatRows(
96 new FakeResultWrapper( $rowsWithComments ),
97 'log_comment',
98 null,
99 null,
100 'pr_id'
101 );
102 }
103
105 protected function getFieldNames() {
106 static $headers = null;
107
108 if ( $headers === null ) {
109 $headers = [
110 'log_timestamp' => 'protectedpages-timestamp',
111 'pr_page' => 'protectedpages-page',
112 'pr_expiry' => 'protectedpages-expiry',
113 'actor_user' => 'protectedpages-performer',
114 'pr_params' => 'protectedpages-params',
115 'log_comment' => 'protectedpages-reason',
116 ];
117 foreach ( $headers as $key => $val ) {
118 $headers[$key] = $this->msg( $val )->text();
119 }
120 }
121
122 return $headers;
123 }
124
130 public function formatValue( $field, $value ) {
132 $row = $this->mCurrentRow;
133 $linkRenderer = $this->getLinkRenderer();
134
135 switch ( $field ) {
136 case 'log_timestamp':
137 // when timestamp is null, this is a old protection row
138 if ( $value === null ) {
139 $formatted = Html::rawElement(
140 'span',
141 [ 'class' => 'mw-protectedpages-unknown' ],
142 $this->msg( 'protectedpages-unknown-timestamp' )->escaped()
143 );
144 } else {
145 $formatted = htmlspecialchars( $this->getLanguage()->userTimeAndDate(
146 $value, $this->getUser() ) );
147 }
148 break;
149
150 case 'pr_page':
151 $title = Title::makeTitleSafe( $row->page_namespace, $row->page_title );
152 if ( !$title ) {
153 $formatted = Html::element(
154 'span',
155 [ 'class' => 'mw-invalidtitle' ],
156 Linker::getInvalidTitleDescription(
157 $this->getContext(),
158 $row->page_namespace,
159 $row->page_title
160 )
161 );
162 } else {
163 $formatted = $linkRenderer->makeLink( $title );
164 }
165 $formatted = Html::rawElement( 'bdi', [
166 'dir' => $this->getLanguage()->getDir()
167 ], $formatted );
168 if ( $row->page_len !== null ) {
169 $formatted .= ' ' . Html::rawElement(
170 'span',
171 [ 'class' => 'mw-protectedpages-length' ],
172 Linker::formatRevisionSize( $row->page_len )
173 );
174 }
175 break;
176
177 case 'pr_expiry':
178 $formatted = htmlspecialchars( $this->getLanguage()->formatExpiry(
179 $value, /* User preference timezone */true, 'infinity', $this->getUser() ) );
180 $title = Title::makeTitleSafe( $row->page_namespace, $row->page_title );
181 if ( $title && $this->getAuthority()->isAllowed( 'protect' ) ) {
182 $changeProtection = $linkRenderer->makeKnownLink(
183 $title,
184 $this->msg( 'protect_change' )->text(),
185 [],
186 [ 'action' => 'unprotect' ]
187 );
188 $formatted .= ' ' . Html::rawElement(
189 'span',
190 [ 'class' => 'mw-protectedpages-actions' ],
191 $this->msg( 'parentheses' )->rawParams( $changeProtection )->escaped()
192 );
193 }
194 break;
195
196 case 'actor_user':
197 // when timestamp is null, this is a old protection row
198 if ( $row->log_timestamp === null ) {
199 $formatted = Html::rawElement(
200 'span',
201 [ 'class' => 'mw-protectedpages-unknown' ],
202 $this->msg( 'protectedpages-unknown-performer' )->escaped()
203 );
204 } else {
205 $username = $row->actor_name;
206 if ( LogEventsList::userCanBitfield(
207 $row->log_deleted,
208 LogPage::DELETED_USER,
209 $this->getAuthority()
210 ) ) {
211 $formatted = Linker::userLink( (int)$value, $username )
212 . Linker::userToolLinks( (int)$value, $username );
213 } else {
214 $formatted = $this->msg( 'rev-deleted-user' )->escaped();
215 }
216 if ( LogEventsList::isDeleted( $row, LogPage::DELETED_USER ) ) {
217 $formatted = '<span class="history-deleted">' . $formatted . '</span>';
218 }
219 }
220 break;
221
222 case 'pr_params':
223 $params = [];
224 // Messages: restriction-level-sysop, restriction-level-autoconfirmed
225 $params[] = $this->msg( 'restriction-level-' . $row->pr_level )->escaped();
226 if ( $row->pr_cascade ) {
227 $params[] = $this->msg( 'protect-summary-cascade' )->escaped();
228 }
229 $formatted = $this->getLanguage()->commaList( $params );
230 break;
231
232 case 'log_comment':
233 // when timestamp is null, this is an 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-reason' )->escaped()
239 );
240 } else {
241 if ( LogEventsList::userCanBitfield(
242 $row->log_deleted,
243 LogPage::DELETED_COMMENT,
244 $this->getAuthority()
245 ) ) {
246 $formatted = $this->formattedComments[$row->pr_id];
247 } else {
248 $formatted = $this->msg( 'rev-deleted-comment' )->escaped();
249 }
250 if ( LogEventsList::isDeleted( $row, LogPage::DELETED_COMMENT ) ) {
251 $formatted = '<span class="history-deleted">' . $formatted . '</span>';
252 }
253 }
254 break;
255
256 default:
257 throw new UnexpectedValueException( "Unknown field '$field'" );
258 }
259
260 return $formatted;
261 }
262
264 public function getQueryInfo() {
265 $dbr = $this->getDatabase();
266 $conds = [
267 $dbr->expr( 'pr_expiry', '>', $dbr->timestamp() )
268 ->or( 'pr_expiry', '=', null ),
269 'page_id=pr_page',
270 $dbr->expr( 'pr_type', '=', $this->type ),
271 ];
272
273 if ( $this->sizetype == 'min' ) {
274 $conds[] = 'page_len>=' . $this->size;
275 } elseif ( $this->sizetype == 'max' ) {
276 $conds[] = 'page_len<=' . $this->size;
277 }
278
279 if ( $this->indefonly ) {
280 $conds['pr_expiry'] = [ $dbr->getInfinity(), null ];
281 }
282 if ( $this->cascadeonly ) {
283 $conds['pr_cascade'] = 1;
284 }
285 if ( $this->noredirect ) {
286 $conds['page_is_redirect'] = 0;
287 }
288
289 if ( $this->level ) {
290 $conds[] = $dbr->expr( 'pr_level', '=', $this->level );
291 }
292 if ( $this->namespace !== null ) {
293 $conds[] = $dbr->expr( 'page_namespace', '=', $this->namespace );
294 }
295
296 $commentQuery = $this->commentStore->getJoin( 'log_comment' );
297
298 return [
299 'tables' => [
300 'page', 'page_restrictions', 'log_search',
301 'logparen' => [ 'logging', 'actor' ] + $commentQuery['tables'],
302 ],
303 'fields' => [
304 'pr_id',
305 'page_namespace',
306 'page_title',
307 'page_len',
308 'pr_type',
309 'pr_level',
310 'pr_expiry',
311 'pr_cascade',
312 'log_timestamp',
313 'log_deleted',
314 'actor_name',
315 'actor_user'
316 ] + $commentQuery['fields'],
317 'conds' => $conds,
318 'join_conds' => [
319 'log_search' => [
320 'LEFT JOIN', [
321 'ls_field' => 'pr_id', 'ls_value = ' . $dbr->buildStringCast( 'pr_id' )
322 ]
323 ],
324 'logparen' => [
325 'LEFT JOIN', [
326 'ls_log_id = log_id'
327 ]
328 ],
329 'actor' => [
330 'JOIN', [
331 'actor_id=log_actor'
332 ]
333 ]
334 ] + $commentQuery['joins']
335 ];
336 }
337
339 protected function getTableClass() {
340 return parent::getTableClass() . ' mw-protectedpages';
341 }
342
344 public function getIndexField() {
345 return 'pr_id';
346 }
347
349 public function getDefaultSort() {
350 return 'pr_id';
351 }
352
354 protected function isFieldSortable( $field ) {
355 // no index for sorting exists
356 return false;
357 }
358}
359
364class_alias( ProtectedPagesPager::class, 'ProtectedPagesPager' );
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:43
Class that generates HTML for internal links.
Some internal bits split of from Skin.php.
Definition Linker.php:47
Class to simplify the use of log pages.
Definition LogPage.php:35
getDatabase()
Get the Database object in use.
__construct(IContextSource $context, CommentStore $commentStore, LinkBatchFactory $linkBatchFactory, LinkRenderer $linkRenderer, IConnectionProvider $dbProvider, RowCommentFormatter $rowCommentFormatter, ?string $type, ?string $level, ?int $namespace, ?string $sizetype, ?int $size, bool $indefonly, bool $cascadeonly, bool $noredirect)
getDefaultSort()
The database field name used as a default sort order.Note that this field will only be sorted on if i...
preprocessResults( $result)
Pre-process results; useful for performing batch existence checks, etc.to override
getQueryInfo()
Provides all parameters needed for the main paged query.It returns an associative array with the foll...
getTableClass()
TablePager relies on mw-datatable for styling, see T214208.to override string
isFieldSortable( $field)
Return true if the named field should be sortable by the UI, false otherwise.bool
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:69
Value object representing a user's identity.
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(string|false $domain=false, $group=null)
Get connection to a replica database.
element(SerializerNode $parent, SerializerNode $node, $contents)