Localisation updates from https://translatewiki.net.
[mediawiki.git] / includes / specials / pagers / ProtectedPagesPager.php
blob5b57559153203b1a5d10ded0aca60daf40f81174
1 <?php
2 /**
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License along
14 * with this program; if not, write to the Free Software Foundation, Inc.,
15 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 * http://www.gnu.org/copyleft/gpl.html
18 * @file
19 * @ingroup Pager
22 namespace MediaWiki\Pager;
24 use LogEventsList;
25 use LogPage;
26 use MediaWiki\Cache\LinkBatchFactory;
27 use MediaWiki\CommentFormatter\RowCommentFormatter;
28 use MediaWiki\CommentStore\CommentStore;
29 use MediaWiki\Context\IContextSource;
30 use MediaWiki\Html\Html;
31 use MediaWiki\Linker\Linker;
32 use MediaWiki\Linker\LinkRenderer;
33 use MediaWiki\Title\Title;
34 use UnexpectedValueException;
35 use Wikimedia\Rdbms\FakeResultWrapper;
36 use Wikimedia\Rdbms\IConnectionProvider;
38 class ProtectedPagesPager extends TablePager {
40 /** @var string */
41 private $type;
42 /** @var string */
43 private $level;
44 /** @var int|null */
45 private $namespace;
46 /** @var string */
47 private $sizetype;
48 /** @var int */
49 private $size;
50 /** @var bool */
51 private $indefonly;
52 /** @var bool */
53 private $cascadeonly;
54 /** @var bool */
55 private $noredirect;
57 private CommentStore $commentStore;
58 private LinkBatchFactory $linkBatchFactory;
59 private RowCommentFormatter $rowCommentFormatter;
61 /** @var string[] */
62 private $formattedComments = [];
64 /**
65 * @param IContextSource $context
66 * @param CommentStore $commentStore
67 * @param LinkBatchFactory $linkBatchFactory
68 * @param LinkRenderer $linkRenderer
69 * @param IConnectionProvider $dbProvider
70 * @param RowCommentFormatter $rowCommentFormatter
71 * @param string $type
72 * @param string $level
73 * @param int|null $namespace
74 * @param string $sizetype
75 * @param int|null $size
76 * @param bool $indefonly
77 * @param bool $cascadeonly
78 * @param bool $noredirect
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;
112 public function preprocessResults( $result ) {
113 # Do a link batch query
114 $lb = $this->linkBatchFactory->newLinkBatch();
115 $rowsWithComments = [];
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 );
124 if ( $row->log_timestamp !== null ) {
125 $rowsWithComments[] = $row;
129 $lb->execute();
131 // Format the comments
132 $this->formattedComments = $this->rowCommentFormatter->formatRows(
133 new FakeResultWrapper( $rowsWithComments ),
134 'log_comment',
135 null,
136 null,
137 'pr_id'
141 protected function getFieldNames() {
142 static $headers = null;
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',
153 foreach ( $headers as $key => $val ) {
154 $headers[$key] = $this->msg( $val )->text();
158 return $headers;
162 * @param string $field
163 * @param string|null $value
164 * @return string HTML
166 public function formatValue( $field, $value ) {
167 /** @var stdClass $row */
168 $row = $this->mCurrentRow;
169 $linkRenderer = $this->getLinkRenderer();
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()
180 } else {
181 $formatted = htmlspecialchars( $this->getLanguage()->userTimeAndDate(
182 $value, $this->getUser() ) );
184 break;
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
198 } else {
199 $formatted = $linkRenderer->makeLink( $title );
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 )
211 break;
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(),
222 [ 'action' => 'unprotect' ]
224 $formatted .= ' ' . Html::rawElement(
225 'span',
226 [ 'class' => 'mw-protectedpages-actions' ],
227 $this->msg( 'parentheses' )->rawParams( $changeProtection )->escaped()
230 break;
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()
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();
252 if ( LogEventsList::isDeleted( $row, LogPage::DELETED_USER ) ) {
253 $formatted = '<span class="history-deleted">' . $formatted . '</span>';
256 break;
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();
265 $formatted = $this->getLanguage()->commaList( $params );
266 break;
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()
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();
286 if ( LogEventsList::isDeleted( $row, LogPage::DELETED_COMMENT ) ) {
287 $formatted = '<span class="history-deleted">' . $formatted . '</span>';
290 break;
292 default:
293 throw new UnexpectedValueException( "Unknown field '$field'" );
296 return $formatted;
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 ),
308 if ( $this->sizetype == 'min' ) {
309 $conds[] = 'page_len>=' . $this->size;
310 } elseif ( $this->sizetype == 'max' ) {
311 $conds[] = 'page_len<=' . $this->size;
314 if ( $this->indefonly ) {
315 $conds['pr_expiry'] = [ $dbr->getInfinity(), null ];
317 if ( $this->cascadeonly ) {
318 $conds['pr_cascade'] = 1;
320 if ( $this->noredirect ) {
321 $conds['page_is_redirect'] = 0;
324 if ( $this->level ) {
325 $conds[] = $dbr->expr( 'pr_level', '=', $this->level );
327 if ( $this->namespace !== null ) {
328 $conds[] = $dbr->expr( 'page_namespace', '=', $this->namespace );
331 $commentQuery = $this->commentStore->getJoin( 'log_comment' );
333 return [
334 'tables' => [
335 'page', 'page_restrictions', 'log_search',
336 'logparen' => [ 'logging', 'actor' ] + $commentQuery['tables'],
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' )
359 'logparen' => [
360 'LEFT JOIN', [
361 'ls_log_id = log_id'
364 'actor' => [
365 'JOIN', [
366 'actor_id=log_actor'
369 ] + $commentQuery['joins']
373 protected function getTableClass() {
374 return parent::getTableClass() . ' mw-protectedpages';
377 public function getIndexField() {
378 return 'pr_id';
381 public function getDefaultSort() {
382 return 'pr_id';
385 protected function isFieldSortable( $field ) {
386 // no index for sorting exists
387 return false;
392 * Retain the old class name for backwards compatibility.
393 * @deprecated since 1.41
395 class_alias( ProtectedPagesPager::class, 'ProtectedPagesPager' );