MediaWiki REL1_39
DeletedContribsPager.php
Go to the documentation of this file.
1<?php
30
35
36 public $mGroupByDate = true;
37
41 public $messages;
42
46 public $target;
47
51 public $namespace = '';
52
54 private $commentStore;
55
57 private $hookRunner;
58
60 private $revisionFactory;
61
72 public function __construct(
73 IContextSource $context,
74 CommentStore $commentStore,
75 HookContainer $hookContainer,
76 LinkRenderer $linkRenderer,
77 ILoadBalancer $loadBalancer,
78 RevisionFactory $revisionFactory,
79 $target,
80 $namespace
81 ) {
82 // Set database before parent constructor to avoid setting it there with wfGetDB
83 $this->mDb = $loadBalancer->getConnectionRef( ILoadBalancer::DB_REPLICA, 'contributions' );
84 parent::__construct( $context, $linkRenderer );
85 $msgs = [ 'deletionlog', 'undeleteviewlink', 'diff' ];
86 foreach ( $msgs as $msg ) {
87 $this->messages[$msg] = $this->msg( $msg )->text();
88 }
89 $this->target = $target;
90 $this->namespace = $namespace;
91 $this->hookRunner = new HookRunner( $hookContainer );
92 $this->commentStore = $commentStore;
93 $this->revisionFactory = $revisionFactory;
94 }
95
96 public function getDefaultQuery() {
97 $query = parent::getDefaultQuery();
98 $query['target'] = $this->target;
99
100 return $query;
101 }
102
103 public function getQueryInfo() {
104 $dbr = $this->getDatabase();
105 $userCond = [ 'actor_name' => $this->target ];
106 $conds = array_merge( $userCond, $this->getNamespaceCond() );
107 // Paranoia: avoid brute force searches (T19792)
108 if ( !$this->getAuthority()->isAllowed( 'deletedhistory' ) ) {
109 $conds[] = $dbr->bitAnd( 'ar_deleted', RevisionRecord::DELETED_USER ) . ' = 0';
110 } elseif ( !$this->getAuthority()->isAllowedAny( 'suppressrevision', 'viewsuppressed' ) ) {
111 $conds[] = $dbr->bitAnd( 'ar_deleted', RevisionRecord::SUPPRESSED_USER ) .
112 ' != ' . RevisionRecord::SUPPRESSED_USER;
113 }
114
115 $queryInfo = $this->revisionFactory->getArchiveQueryInfo();
116 $queryInfo['conds'] = $conds;
117 $queryInfo['options'] = [];
118
119 // rename the "joins" field to "join_conds" as expected by the base class.
120 $queryInfo['join_conds'] = $queryInfo['joins'];
121 unset( $queryInfo['joins'] );
122
124 $queryInfo['tables'],
125 $queryInfo['fields'],
126 $queryInfo['conds'],
127 $queryInfo['join_conds'],
128 $queryInfo['options'],
129 ''
130 );
131
132 return $queryInfo;
133 }
134
144 public function reallyDoQuery( $offset, $limit, $order ) {
145 $data = [ parent::reallyDoQuery( $offset, $limit, $order ) ];
146
147 // This hook will allow extensions to add in additional queries, nearly
148 // identical to ContribsPager::reallyDoQuery.
149 $this->hookRunner->onDeletedContribsPager__reallyDoQuery(
150 $data, $this, $offset, $limit, $order );
151
152 $result = [];
153
154 // loop all results and collect them in an array
155 foreach ( $data as $query ) {
156 foreach ( $query as $i => $row ) {
157 // use index column as key, allowing us to easily sort in PHP
158 $result[$row->{$this->getIndexField()} . "-$i"] = $row;
159 }
160 }
161
162 // sort results
163 if ( $order === self::QUERY_ASCENDING ) {
164 ksort( $result );
165 } else {
166 krsort( $result );
167 }
168
169 // enforce limit
170 $result = array_slice( $result, 0, $limit );
171
172 // get rid of array keys
173 $result = array_values( $result );
174
175 return new FakeResultWrapper( $result );
176 }
177
178 public function getIndexField() {
179 return 'ar_timestamp';
180 }
181
185 public function getTarget() {
186 return $this->target;
187 }
188
192 public function getNamespace() {
193 return $this->namespace;
194 }
195
199 protected function getStartBody() {
200 return "<section class='mw-pager-body'>\n";
201 }
202
206 protected function getEndBody() {
207 return "</section>\n";
208 }
209
210 private function getNamespaceCond() {
211 if ( $this->namespace !== '' ) {
212 return [ 'ar_namespace' => (int)$this->namespace ];
213 } else {
214 return [];
215 }
216 }
217
225 public function formatRow( $row ) {
226 $ret = '';
227 $classes = [];
228 $attribs = [];
229
230 if ( $this->revisionFactory->isRevisionRow( $row, 'archive' ) ) {
231 $revRecord = $this->revisionFactory->newRevisionFromArchiveRow( $row );
232 $revId = $revRecord->getId();
233 if ( $revId ) {
234 $attribs['data-mw-revid'] = $revId;
235 [ $ret, $classes ] = $this->formatRevisionRow( $row );
236 }
237 }
238
239 // Let extensions add data
240 $this->hookRunner->onDeletedContributionsLineEnding(
241 $this, $ret, $row, $classes, $attribs );
242 $attribs = array_filter( $attribs,
243 [ Sanitizer::class, 'isReservedDataAttribute' ],
244 ARRAY_FILTER_USE_KEY
245 );
246
247 if ( $classes === [] && $attribs === [] && $ret === '' ) {
248 wfDebug( "Dropping Special:DeletedContribution row that could not be formatted" );
249 $ret = "<!-- Could not format Special:DeletedContribution row. -->\n";
250 } else {
251 $attribs['class'] = $classes;
252 $ret = Html::rawElement( 'li', $attribs, $ret ) . "\n";
253 }
254
255 return $ret;
256 }
257
270 private function formatRevisionRow( $row ) {
271 $page = Title::makeTitle( $row->ar_namespace, $row->ar_title );
272
273 $linkRenderer = $this->getLinkRenderer();
274
275 $revRecord = $this->revisionFactory->newRevisionFromArchiveRow(
276 $row,
277 RevisionFactory::READ_NORMAL,
278 $page
279 );
280
281 $undelete = SpecialPage::getTitleFor( 'Undelete' );
282
283 $logs = SpecialPage::getTitleFor( 'Log' );
284 $dellog = $linkRenderer->makeKnownLink(
285 $logs,
286 $this->messages['deletionlog'],
287 [],
288 [
289 'type' => 'delete',
290 'page' => $page->getPrefixedText()
291 ]
292 );
293
294 $reviewlink = $linkRenderer->makeKnownLink(
295 SpecialPage::getTitleFor( 'Undelete', $page->getPrefixedDBkey() ),
296 $this->messages['undeleteviewlink']
297 );
298
299 $user = $this->getUser();
300
301 if ( $this->getAuthority()->isAllowed( 'deletedtext' ) ) {
302 $last = $linkRenderer->makeKnownLink(
303 $undelete,
304 $this->messages['diff'],
305 [],
306 [
307 'target' => $page->getPrefixedText(),
308 'timestamp' => $revRecord->getTimestamp(),
309 'diff' => 'prev'
310 ]
311 );
312 } else {
313 $last = htmlspecialchars( $this->messages['diff'] );
314 }
315
316 $comment = Linker::revComment( $revRecord );
317 $date = $this->getLanguage()->userTimeAndDate( $revRecord->getTimestamp(), $user );
318
319 if ( !$this->getAuthority()->isAllowed( 'undelete' ) ||
320 !$revRecord->userCan( RevisionRecord::DELETED_TEXT, $this->getAuthority() )
321 ) {
322 $link = htmlspecialchars( $date ); // unusable link
323 } else {
324 $link = $linkRenderer->makeKnownLink(
325 $undelete,
326 $date,
327 [ 'class' => 'mw-changeslist-date' ],
328 [
329 'target' => $page->getPrefixedText(),
330 'timestamp' => $revRecord->getTimestamp()
331 ]
332 );
333 }
334 // Style deleted items
335 if ( $revRecord->isDeleted( RevisionRecord::DELETED_TEXT ) ) {
336 $class = Linker::getRevisionDeletedClass( $revRecord );
337 $link = '<span class="' . $class . '">' . $link . '</span>';
338 }
339
340 $pagelink = $linkRenderer->makeLink(
341 $page,
342 null,
343 [ 'class' => 'mw-changeslist-title' ]
344 );
345
346 if ( $revRecord->isMinor() ) {
347 $mflag = ChangesList::flag( 'minor' );
348 } else {
349 $mflag = '';
350 }
351
352 // Revision delete link
353 $del = Linker::getRevDeleteLink( $user, $revRecord, $page );
354 if ( $del ) {
355 $del .= ' ';
356 }
357
358 $tools = Html::rawElement(
359 'span',
360 [ 'class' => 'mw-deletedcontribs-tools' ],
361 $this->msg( 'parentheses' )->rawParams( $this->getLanguage()->pipeList(
362 [ $last, $dellog, $reviewlink ] ) )->escaped()
363 );
364
365 // Tags, if any.
366 list( $tagSummary, $classes ) = ChangeTags::formatSummaryRow(
367 $row->ts_tags,
368 'deletedcontributions',
369 $this->getContext()
370 );
371
372 $separator = '<span class="mw-changeslist-separator">. .</span>';
373 $ret = "{$del}{$link} {$tools} {$separator} {$mflag} {$pagelink} {$comment} {$tagSummary}";
374
375 # Denote if username is redacted for this edit
376 if ( $revRecord->isDeleted( RevisionRecord::DELETED_USER ) ) {
377 $ret .= " <strong>" . $this->msg( 'rev-deleted-user-contribs' )->escaped() . "</strong>";
378 }
379
380 return [ $ret, $classes ];
381 }
382}
getUser()
getAuthority()
wfDebug( $text, $dest='all', array $context=[])
Sends a line to the debug log if enabled or, optionally, to a comment in output.
static modifyDisplayQuery(&$tables, &$fields, &$conds, &$join_conds, &$options, $filter_tag='', bool $exclude=false)
Applies all tags-related changes to a query.
static formatSummaryRow( $tags, $page, MessageLocalizer $localizer=null)
Creates HTML for the given tags.
static flag( $flag, IContextSource $context=null)
Make an "<abbr>" element for a given change flag.
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()
string int $namespace
A single namespace number, or an empty string for all namespaces.
__construct(IContextSource $context, CommentStore $commentStore, HookContainer $hookContainer, LinkRenderer $linkRenderer, ILoadBalancer $loadBalancer, RevisionFactory $revisionFactory, $target, $namespace)
string[] $messages
Local cache for escaped messages.
string $target
User name, or a string describing an IP address range.
getDefaultQuery()
Get an array of query parameters that should be put into self-links.
getIndexField()
Returns the name of the index field.
formatRow( $row)
Generates each row in the contributions list.
reallyDoQuery( $offset, $limit, $order)
This method basically executes the exact same code as the parent class, though with a hook added,...
getEndBody()
Hook into getBody() for the end of the list.to overridestring
getQueryInfo()
Provides all parameters needed for the main paged query.
getStartBody()
Hook into getBody(), allows text to be inserted at the start.This will be called even if there are no...
static rawElement( $element, $attribs=[], $contents='')
Returns an HTML element in a string.
Definition Html.php:214
getDatabase()
Get the Database object in use.
static getRevisionDeletedClass(RevisionRecord $revisionRecord)
Returns css class of a deleted revision.
Definition Linker.php:1351
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:1607
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:2153
This class provides an implementation of the core hook interfaces, forwarding hook calls to HookConta...
Class that generates HTML anchor link elements for pages.
Page revision base class.
Efficient paging for SQL queries.
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,...
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.
Service for constructing RevisionRecord objects.
Create and track the database connections and transactions for a given database cluster.
getConnectionRef( $i, $groups=[], $domain=false, $flags=0)
Result wrapper for grabbing data queried from an IDatabase object.