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