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  /*
259  * There may be more than just revision rows. To make sure that we'll only be processing
260  * revisions here, let's _try_ to build a revision out of our row (without displaying
261  * notices though) and then trying to grab data from the built object. If we succeed,
262  * we're definitely dealing with revision data and we may proceed, if not, we'll leave it
263  * to extensions to subscribe to the hook to parse the row.
264  */
265  Wikimedia\suppressWarnings();
266  try {
267  $revRecord = $this->revisionFactory->newRevisionFromArchiveRow( $row );
268  $validRevision = (bool)$revRecord->getId();
269  } catch ( Exception $e ) {
270  $validRevision = false;
271  }
272  Wikimedia\restoreWarnings();
273 
274  if ( $validRevision ) {
275  $attribs['data-mw-revid'] = $revRecord->getId();
276  $ret = $this->formatRevisionRow( $row );
277  }
278 
279  // Let extensions add data
280  $this->hookRunner->onDeletedContributionsLineEnding(
281  $this, $ret, $row, $classes, $attribs );
282  $attribs = array_filter( $attribs,
283  [ Sanitizer::class, 'isReservedDataAttribute' ],
284  ARRAY_FILTER_USE_KEY
285  );
286 
287  if ( $classes === [] && $attribs === [] && $ret === '' ) {
288  wfDebug( "Dropping Special:DeletedContribution row that could not be formatted" );
289  $ret = "<!-- Could not format Special:DeletedContribution row. -->\n";
290  } else {
291  $attribs['class'] = $classes;
292  $ret = Html::rawElement( 'li', $attribs, $ret ) . "\n";
293  }
294 
295  return $ret;
296  }
297 
310  private function formatRevisionRow( $row ) {
311  $page = Title::makeTitle( $row->ar_namespace, $row->ar_title );
312 
313  $linkRenderer = $this->getLinkRenderer();
314 
315  $revRecord = $this->revisionFactory->newRevisionFromArchiveRow(
316  $row,
317  RevisionFactory::READ_NORMAL,
318  $page
319  );
320 
321  $undelete = SpecialPage::getTitleFor( 'Undelete' );
322 
323  $logs = SpecialPage::getTitleFor( 'Log' );
324  $dellog = $linkRenderer->makeKnownLink(
325  $logs,
326  $this->messages['deletionlog'],
327  [],
328  [
329  'type' => 'delete',
330  'page' => $page->getPrefixedText()
331  ]
332  );
333 
334  $reviewlink = $linkRenderer->makeKnownLink(
335  SpecialPage::getTitleFor( 'Undelete', $page->getPrefixedDBkey() ),
336  $this->messages['undeleteviewlink']
337  );
338 
339  $user = $this->getUser();
340 
341  if ( $this->getAuthority()->isAllowed( 'deletedtext' ) ) {
342  $last = $linkRenderer->makeKnownLink(
343  $undelete,
344  $this->messages['diff'],
345  [],
346  [
347  'target' => $page->getPrefixedText(),
348  'timestamp' => $revRecord->getTimestamp(),
349  'diff' => 'prev'
350  ]
351  );
352  } else {
353  $last = htmlspecialchars( $this->messages['diff'] );
354  }
355 
356  $comment = Linker::revComment( $revRecord );
357  $date = $this->getLanguage()->userTimeAndDate( $revRecord->getTimestamp(), $user );
358 
359  if ( !$this->getAuthority()->isAllowed( 'undelete' ) ||
360  !$revRecord->userCan( RevisionRecord::DELETED_TEXT, $this->getAuthority() )
361  ) {
362  $link = htmlspecialchars( $date ); // unusable link
363  } else {
364  $link = $linkRenderer->makeKnownLink(
365  $undelete,
366  $date,
367  [ 'class' => 'mw-changeslist-date' ],
368  [
369  'target' => $page->getPrefixedText(),
370  'timestamp' => $revRecord->getTimestamp()
371  ]
372  );
373  }
374  // Style deleted items
375  if ( $revRecord->isDeleted( RevisionRecord::DELETED_TEXT ) ) {
376  $class = Linker::getRevisionDeletedClass( $revRecord );
377  $link = '<span class="' . $class . '">' . $link . '</span>';
378  }
379 
380  $pagelink = $linkRenderer->makeLink(
381  $page,
382  null,
383  [ 'class' => 'mw-changeslist-title' ]
384  );
385 
386  if ( $revRecord->isMinor() ) {
387  $mflag = ChangesList::flag( 'minor' );
388  } else {
389  $mflag = '';
390  }
391 
392  // Revision delete link
393  $del = Linker::getRevDeleteLink( $user, $revRecord, $page );
394  if ( $del ) {
395  $del .= ' ';
396  }
397 
398  $tools = Html::rawElement(
399  'span',
400  [ 'class' => 'mw-deletedcontribs-tools' ],
401  $this->msg( 'parentheses' )->rawParams( $this->getLanguage()->pipeList(
402  [ $last, $dellog, $reviewlink ] ) )->escaped()
403  );
404 
405  $separator = '<span class="mw-changeslist-separator">. .</span>';
406  $ret = "{$del}{$link} {$tools} {$separator} {$mflag} {$pagelink} {$comment}";
407 
408  # Denote if username is redacted for this edit
409  if ( $revRecord->isDeleted( RevisionRecord::DELETED_USER ) ) {
410  $ret .= " <strong>" . $this->msg( 'rev-deleted-user-contribs' )->escaped() . "</strong>";
411  }
412 
413  return $ret;
414  }
415 }
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:244
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:25
Linker\getRevisionDeletedClass
static getRevisionDeletedClass(RevisionRecord $revisionRecord)
Returns css class of a deleted revision.
Definition: Linker.php:1303
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:272
Title\makeTitle
static makeTitle( $ns, $title, $fragment='', $interwiki='')
Create a new Title from a namespace index and a DB key.
Definition: Title.php:680
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:1784
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:555
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:2362
DeletedContribsPager\formatRevisionRow
formatRevisionRow( $row)
Generates each row in the contributions list for archive entries.
Definition: DeletedContribsPager.php:310
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