MediaWiki  master
DeletedContribsPager.php
Go to the documentation of this file.
1 <?php
30 
35 
40 
44  public $messages;
45 
49  public $target;
50 
54  public $namespace = '';
55 
59  protected $mNavigationBar;
60 
62  private $hookRunner;
63 
65  private $commentStore;
66 
69 
80  public function __construct(
82  $target,
83  $namespace,
85  HookContainer $hookContainer,
86  ILoadBalancer $loadBalancer,
89  ) {
90  // Set database before parent constructor to avoid setting it there with wfGetDB
91  $this->mDb = $loadBalancer->getConnectionRef( ILoadBalancer::DB_REPLICA, 'contributions' );
92  parent::__construct( $context, $linkRenderer );
93  $msgs = [ 'deletionlog', 'undeleteviewlink', 'diff' ];
94  foreach ( $msgs as $msg ) {
95  $this->messages[$msg] = $this->msg( $msg )->text();
96  }
97  $this->target = $target;
98  $this->namespace = $namespace;
99  $this->hookRunner = new HookRunner( $hookContainer );
100  $this->commentStore = $commentStore;
101  $this->revisionFactory = $revisionFactory;
102  }
103 
104  public function getDefaultQuery() {
105  $query = parent::getDefaultQuery();
106  $query['target'] = $this->target;
107 
108  return $query;
109  }
110 
111  public function getQueryInfo() {
112  $dbr = $this->getDatabase();
113  $userCond = [ 'actor_name' => $this->target ];
114  $conds = array_merge( $userCond, $this->getNamespaceCond() );
115  // Paranoia: avoid brute force searches (T19792)
116  if ( !$this->getAuthority()->isAllowed( 'deletedhistory' ) ) {
117  $conds[] = $dbr->bitAnd( 'ar_deleted', RevisionRecord::DELETED_USER ) . ' = 0';
118  } elseif ( !$this->getAuthority()->isAllowedAny( 'suppressrevision', 'viewsuppressed' ) ) {
119  $conds[] = $dbr->bitAnd( 'ar_deleted', RevisionRecord::SUPPRESSED_USER ) .
120  ' != ' . RevisionRecord::SUPPRESSED_USER;
121  }
122 
123  $queryInfo = $this->revisionFactory->getArchiveQueryInfo();
124  $queryInfo['conds'] = $conds;
125  $queryInfo['options'] = [];
126 
127  // rename the "joins" field to "join_conds" as expected by the base class.
128  $queryInfo['join_conds'] = $queryInfo['joins'];
129  unset( $queryInfo['joins'] );
130 
131  return $queryInfo;
132  }
133 
143  public function reallyDoQuery( $offset, $limit, $order ) {
144  $data = [ parent::reallyDoQuery( $offset, $limit, $order ) ];
145 
146  // This hook will allow extensions to add in additional queries, nearly
147  // identical to ContribsPager::reallyDoQuery.
148  $this->hookRunner->onDeletedContribsPager__reallyDoQuery(
149  $data, $this, $offset, $limit, $order );
150 
151  $result = [];
152 
153  // loop all results and collect them in an array
154  foreach ( $data as $query ) {
155  foreach ( $query as $i => $row ) {
156  // use index column as key, allowing us to easily sort in PHP
157  $result[$row->{$this->getIndexField()} . "-$i"] = $row;
158  }
159  }
160 
161  // sort results
162  if ( $order === self::QUERY_ASCENDING ) {
163  ksort( $result );
164  } else {
165  krsort( $result );
166  }
167 
168  // enforce limit
169  $result = array_slice( $result, 0, $limit );
170 
171  // get rid of array keys
172  $result = array_values( $result );
173 
174  return new FakeResultWrapper( $result );
175  }
176 
177  public function getIndexField() {
178  return 'ar_timestamp';
179  }
180 
184  public function getTarget() {
185  return $this->target;
186  }
187 
191  public function getNamespace() {
192  return $this->namespace;
193  }
194 
195  protected function getStartBody() {
196  return "<ul>\n";
197  }
198 
199  protected function getEndBody() {
200  return "</ul>\n";
201  }
202 
203  public function getNavigationBar() {
204  if ( !$this->isNavigationBarShown() ) {
205  return '';
206  }
207 
208  if ( isset( $this->mNavigationBar ) ) {
209  return $this->mNavigationBar;
210  }
211 
212  $linkTexts = [
213  'prev' => $this->msg( 'pager-newer-n' )->numParams( $this->mLimit )->escaped(),
214  'next' => $this->msg( 'pager-older-n' )->numParams( $this->mLimit )->escaped(),
215  'first' => $this->msg( 'histlast' )->escaped(),
216  'last' => $this->msg( 'histfirst' )->escaped()
217  ];
218 
219  $pagingLinks = $this->getPagingLinks( $linkTexts );
220  $limitLinks = $this->getLimitLinks();
221  $lang = $this->getLanguage();
222  $limits = $lang->pipeList( $limitLinks );
223 
224  $firstLast = $lang->pipeList( [ $pagingLinks['first'], $pagingLinks['last'] ] );
225  $firstLast = $this->msg( 'parentheses' )->rawParams( $firstLast )->escaped();
226  $prevNext = $this->msg( 'viewprevnext' )
227  ->rawParams(
228  $pagingLinks['prev'],
229  $pagingLinks['next'],
230  $limits
231  )->escaped();
232  $separator = $this->msg( 'word-separator' )->escaped();
233  $this->mNavigationBar = $firstLast . $separator . $prevNext;
234 
235  return $this->mNavigationBar;
236  }
237 
238  private function getNamespaceCond() {
239  if ( $this->namespace !== '' ) {
240  return [ 'ar_namespace' => (int)$this->namespace ];
241  } else {
242  return [];
243  }
244  }
245 
253  public function formatRow( $row ) {
254  $ret = '';
255  $classes = [];
256  $attribs = [];
257 
258  if ( $this->revisionFactory->isRevisionRow( $row, 'archive' ) ) {
259  $revRecord = $this->revisionFactory->newRevisionFromArchiveRow( $row );
260  $validRevision = (bool)$revRecord->getId();
261  } else {
262  $validRevision = false;
263  }
264 
265  if ( $validRevision ) {
266  $attribs['data-mw-revid'] = $revRecord->getId();
267  $ret = $this->formatRevisionRow( $row );
268  }
269 
270  // Let extensions add data
271  $this->hookRunner->onDeletedContributionsLineEnding(
272  $this, $ret, $row, $classes, $attribs );
273  $attribs = array_filter( $attribs,
274  [ Sanitizer::class, 'isReservedDataAttribute' ],
275  ARRAY_FILTER_USE_KEY
276  );
277 
278  if ( $classes === [] && $attribs === [] && $ret === '' ) {
279  wfDebug( "Dropping Special:DeletedContribution row that could not be formatted" );
280  $ret = "<!-- Could not format Special:DeletedContribution row. -->\n";
281  } else {
282  $attribs['class'] = $classes;
283  $ret = Html::rawElement( 'li', $attribs, $ret ) . "\n";
284  }
285 
286  return $ret;
287  }
288 
301  private function formatRevisionRow( $row ) {
302  $page = Title::makeTitle( $row->ar_namespace, $row->ar_title );
303 
304  $linkRenderer = $this->getLinkRenderer();
305 
306  $revRecord = $this->revisionFactory->newRevisionFromArchiveRow(
307  $row,
308  RevisionFactory::READ_NORMAL,
309  $page
310  );
311 
312  $undelete = SpecialPage::getTitleFor( 'Undelete' );
313 
314  $logs = SpecialPage::getTitleFor( 'Log' );
315  $dellog = $linkRenderer->makeKnownLink(
316  $logs,
317  $this->messages['deletionlog'],
318  [],
319  [
320  'type' => 'delete',
321  'page' => $page->getPrefixedText()
322  ]
323  );
324 
325  $reviewlink = $linkRenderer->makeKnownLink(
326  SpecialPage::getTitleFor( 'Undelete', $page->getPrefixedDBkey() ),
327  $this->messages['undeleteviewlink']
328  );
329 
330  $user = $this->getUser();
331 
332  if ( $this->getAuthority()->isAllowed( 'deletedtext' ) ) {
333  $last = $linkRenderer->makeKnownLink(
334  $undelete,
335  $this->messages['diff'],
336  [],
337  [
338  'target' => $page->getPrefixedText(),
339  'timestamp' => $revRecord->getTimestamp(),
340  'diff' => 'prev'
341  ]
342  );
343  } else {
344  $last = htmlspecialchars( $this->messages['diff'] );
345  }
346 
347  $comment = Linker::revComment( $revRecord );
348  $date = $this->getLanguage()->userTimeAndDate( $revRecord->getTimestamp(), $user );
349 
350  if ( !$this->getAuthority()->isAllowed( 'undelete' ) ||
351  !$revRecord->userCan( RevisionRecord::DELETED_TEXT, $this->getAuthority() )
352  ) {
353  $link = htmlspecialchars( $date ); // unusable link
354  } else {
355  $link = $linkRenderer->makeKnownLink(
356  $undelete,
357  $date,
358  [ 'class' => 'mw-changeslist-date' ],
359  [
360  'target' => $page->getPrefixedText(),
361  'timestamp' => $revRecord->getTimestamp()
362  ]
363  );
364  }
365  // Style deleted items
366  if ( $revRecord->isDeleted( RevisionRecord::DELETED_TEXT ) ) {
367  $class = Linker::getRevisionDeletedClass( $revRecord );
368  $link = '<span class="' . $class . '">' . $link . '</span>';
369  }
370 
371  $pagelink = $linkRenderer->makeLink(
372  $page,
373  null,
374  [ 'class' => 'mw-changeslist-title' ]
375  );
376 
377  if ( $revRecord->isMinor() ) {
378  $mflag = ChangesList::flag( 'minor' );
379  } else {
380  $mflag = '';
381  }
382 
383  // Revision delete link
384  $del = Linker::getRevDeleteLink( $user, $revRecord, $page );
385  if ( $del ) {
386  $del .= ' ';
387  }
388 
389  $tools = Html::rawElement(
390  'span',
391  [ 'class' => 'mw-deletedcontribs-tools' ],
392  $this->msg( 'parentheses' )->rawParams( $this->getLanguage()->pipeList(
393  [ $last, $dellog, $reviewlink ] ) )->escaped()
394  );
395 
396  $separator = '<span class="mw-changeslist-separator">. .</span>';
397  $ret = "{$del}{$link} {$tools} {$separator} {$mflag} {$pagelink} {$comment}";
398 
399  # Denote if username is redacted for this edit
400  if ( $revRecord->isDeleted( RevisionRecord::DELETED_USER ) ) {
401  $ret .= " <strong>" . $this->msg( 'rev-deleted-user-contribs' )->escaped() . "</strong>";
402  }
403 
404  return $ret;
405  }
406 }
ContextSource\$context
IContextSource $context
Definition: ContextSource.php:39
DeletedContribsPager\$namespace
string int $namespace
A single namespace number, or an empty string for all namespaces.
Definition: DeletedContribsPager.php:54
DeletedContribsPager\$revisionFactory
RevisionFactory $revisionFactory
Definition: DeletedContribsPager.php:68
MediaWiki\Revision\RevisionRecord
Page revision base class.
Definition: RevisionRecord.php:47
$lang
if(!isset( $args[0])) $lang
Definition: testCompression.php:37
DeletedContribsPager\$target
string $target
User name, or a string describing an IP address range.
Definition: DeletedContribsPager.php:49
DeletedContribsPager
Definition: DeletedContribsPager.php:34
MediaWiki\Linker\LinkRenderer
Class that generates HTML links for pages.
Definition: LinkRenderer.php:43
DeletedContribsPager\formatRow
formatRow( $row)
Generates each row in the contributions list.
Definition: DeletedContribsPager.php:253
DeletedContribsPager\$mNavigationBar
string $mNavigationBar
Navigation bar with paging links.
Definition: DeletedContribsPager.php:59
IndexPager\getDatabase
getDatabase()
Get the Database object in use.
Definition: IndexPager.php:248
getAuthority
getAuthority()
CommentStore
Handle database storage of comments such as edit summaries and log reasons.
Definition: CommentStore.php:42
MediaWiki\Revision\RevisionFactory
Service for constructing RevisionRecord objects.
Definition: RevisionFactory.php:37
IndexPager\$linkRenderer
LinkRenderer $linkRenderer
Definition: IndexPager.php:167
SpecialPage\getTitleFor
static getTitleFor( $name, $subpage=false, $fragment='')
Get a localised Title object for a specified special page name If you don't need a full Title object,...
Definition: SpecialPage.php:107
DeletedContribsPager\$messages
string[] $messages
Local cache for escaped messages.
Definition: DeletedContribsPager.php:44
Wikimedia\Rdbms\FakeResultWrapper
Overloads the relevant methods of the real ResultWrapper so it doesn't go anywhere near an actual dat...
Definition: FakeResultWrapper.php:12
Wikimedia\Rdbms\ILoadBalancer\getConnectionRef
getConnectionRef( $i, $groups=[], $domain=false, $flags=0)
Get a live database handle reference for a server index.
$dbr
$dbr
Definition: testCompression.php:54
DeletedContribsPager\getNavigationBar
getNavigationBar()
Definition: DeletedContribsPager.php:203
DeletedContribsPager\getNamespace
getNamespace()
Definition: DeletedContribsPager.php:191
Wikimedia\Rdbms\IResultWrapper
Result wrapper for grabbing data queried from an IDatabase object.
Definition: IResultWrapper.php:26
Linker\getRevisionDeletedClass
static getRevisionDeletedClass(RevisionRecord $revisionRecord)
Returns css class of a deleted revision.
Definition: Linker.php:1299
DeletedContribsPager\getEndBody
getEndBody()
Hook into getBody() for the end of the list.
Definition: DeletedContribsPager.php:199
ChangesList\flag
static flag( $flag, IContextSource $context=null)
Make an "<abbr>" element for a given change flag.
Definition: ChangesList.php:273
Title\makeTitle
static makeTitle( $ns, $title, $fragment='', $interwiki='')
Create a new Title from a namespace index and a DB key.
Definition: Title.php:650
DB_REPLICA
const DB_REPLICA
Definition: defines.php:25
wfDebug
wfDebug( $text, $dest='all', array $context=[])
Sends a line to the debug log if enabled or, optionally, to a comment in output.
Definition: GlobalFunctions.php:894
DeletedContribsPager\getStartBody
getStartBody()
Hook into getBody(), allows text to be inserted at the start.
Definition: DeletedContribsPager.php:195
DeletedContribsPager\reallyDoQuery
reallyDoQuery( $offset, $limit, $order)
This method basically executes the exact same code as the parent class, though with a hook added,...
Definition: DeletedContribsPager.php:143
ContextSource\msg
msg( $key,... $params)
Get a Message object with context set Parameters are the same as wfMessage()
Definition: ContextSource.php:197
Linker\revComment
static revComment(RevisionRecord $revRecord, $local=false, $isPublic=false, $useParentheses=true)
Wrap and format the given revision's comment block, if the current user is allowed to view it.
Definition: Linker.php:1530
DeletedContribsPager\getDefaultQuery
getDefaultQuery()
Get an array of query parameters that should be put into self-links.
Definition: DeletedContribsPager.php:104
DeletedContribsPager\getTarget
getTarget()
Definition: DeletedContribsPager.php:184
ContextSource\getAuthority
getAuthority()
Definition: ContextSource.php:144
DeletedContribsPager\$hookRunner
HookRunner $hookRunner
Definition: DeletedContribsPager.php:62
DeletedContribsPager\$mDefaultDirection
bool $mDefaultDirection
Default direction for pager.
Definition: DeletedContribsPager.php:39
IContextSource
Interface for objects which can provide a MediaWiki context on request.
Definition: IContextSource.php:58
IndexPager\DIR_DESCENDING
const DIR_DESCENDING
Backwards-compatible constant for $mDefaultDirection field (do not change)
Definition: IndexPager.php:80
DeletedContribsPager\$commentStore
CommentStore $commentStore
Definition: DeletedContribsPager.php:65
Html\rawElement
static rawElement( $element, $attribs=[], $contents='')
Returns an HTML element in a string.
Definition: Html.php:210
IndexPager
IndexPager is an efficient pager which uses a (roughly unique) index in the data set to implement pag...
Definition: IndexPager.php:74
DeletedContribsPager\getNamespaceCond
getNamespaceCond()
Definition: DeletedContribsPager.php:238
DeletedContribsPager\__construct
__construct(IContextSource $context, $target, $namespace, LinkRenderer $linkRenderer, HookContainer $hookContainer, ILoadBalancer $loadBalancer, CommentStore $commentStore, RevisionFactory $revisionFactory)
Definition: DeletedContribsPager.php:80
MediaWiki\HookContainer\HookContainer
HookContainer class.
Definition: HookContainer.php:45
MediaWiki\HookContainer\HookRunner
This class provides an implementation of the core hook interfaces, forwarding hook calls to HookConta...
Definition: HookRunner.php:557
Linker\getRevDeleteLink
static getRevDeleteLink(Authority $performer, RevisionRecord $revRecord, LinkTarget $title)
Get a revision-deletion link, or disabled link, or nothing, depending on user permissions & the setti...
Definition: Linker.php:2086
DeletedContribsPager\formatRevisionRow
formatRevisionRow( $row)
Generates each row in the contributions list for archive entries.
Definition: DeletedContribsPager.php:301
DeletedContribsPager\getQueryInfo
getQueryInfo()
Provides all parameters needed for the main paged query.
Definition: DeletedContribsPager.php:111
Wikimedia\Rdbms\ILoadBalancer
Database cluster connection, tracking, load balancing, and transaction manager interface.
Definition: ILoadBalancer.php:81
DeletedContribsPager\getIndexField
getIndexField()
Returns the name of the index field.
Definition: DeletedContribsPager.php:177