Merge "Improve sorting on SpecialWanted*-Pages"
[mediawiki.git] / includes / RevisionList.php
blobd909a652d7f0e03c66c0b0f5737aeaa4ad9003f1
1 <?php
2 /**
3 * Holders of revision list for a single page
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 * http://www.gnu.org/copyleft/gpl.html
20 * @file
23 use MediaWiki\MediaWikiServices;
24 use Wikimedia\Rdbms\ResultWrapper;
26 /**
27 * List for revision table items for a single page
29 abstract class RevisionListBase extends ContextSource implements Iterator {
30 /** @var Title */
31 public $title;
33 /** @var array */
34 protected $ids;
36 /** @var ResultWrapper|bool */
37 protected $res;
39 /** @var bool|object */
40 protected $current;
42 /**
43 * Construct a revision list for a given title
44 * @param IContextSource $context
45 * @param Title $title
47 function __construct( IContextSource $context, Title $title ) {
48 $this->setContext( $context );
49 $this->title = $title;
52 /**
53 * Select items only where the ID is any of the specified values
54 * @param array $ids
56 function filterByIds( array $ids ) {
57 $this->ids = $ids;
60 /**
61 * Get the internal type name of this list. Equal to the table name.
62 * Override this function.
63 * @return null
65 public function getType() {
66 return null;
69 /**
70 * Initialise the current iteration pointer
72 protected function initCurrent() {
73 $row = $this->res->current();
74 if ( $row ) {
75 $this->current = $this->newItem( $row );
76 } else {
77 $this->current = false;
81 /**
82 * Start iteration. This must be called before current() or next().
83 * @return Revision First list item
85 public function reset() {
86 if ( !$this->res ) {
87 $this->res = $this->doQuery( wfGetDB( DB_REPLICA ) );
88 } else {
89 $this->res->rewind();
91 $this->initCurrent();
92 return $this->current;
95 public function rewind() {
96 $this->reset();
99 /**
100 * Get the current list item, or false if we are at the end
101 * @return Revision
103 public function current() {
104 return $this->current;
108 * Move the iteration pointer to the next list item, and return it.
109 * @return Revision
111 public function next() {
112 $this->res->next();
113 $this->initCurrent();
114 return $this->current;
117 public function key() {
118 return $this->res ? $this->res->key(): 0;
121 public function valid() {
122 return $this->res ? $this->res->valid() : false;
126 * Get the number of items in the list.
127 * @return int
129 public function length() {
130 if ( !$this->res ) {
131 return 0;
132 } else {
133 return $this->res->numRows();
138 * Do the DB query to iterate through the objects.
139 * @param IDatabase $db DB object to use for the query
141 abstract public function doQuery( $db );
144 * Create an item object from a DB result row
145 * @param object $row
147 abstract public function newItem( $row );
151 * Abstract base class for revision items
153 abstract class RevisionItemBase {
154 /** @var RevisionListBase The parent */
155 protected $list;
157 /** The database result row */
158 protected $row;
161 * @param RevisionListBase $list
162 * @param object $row DB result row
164 public function __construct( $list, $row ) {
165 $this->list = $list;
166 $this->row = $row;
170 * Get the DB field name associated with the ID list.
171 * Override this function.
172 * @return null
174 public function getIdField() {
175 return null;
179 * Get the DB field name storing timestamps.
180 * Override this function.
181 * @return bool
183 public function getTimestampField() {
184 return false;
188 * Get the DB field name storing user ids.
189 * Override this function.
190 * @return bool
192 public function getAuthorIdField() {
193 return false;
197 * Get the DB field name storing user names.
198 * Override this function.
199 * @return bool
201 public function getAuthorNameField() {
202 return false;
206 * Get the ID, as it would appear in the ids URL parameter
207 * @return int
209 public function getId() {
210 $field = $this->getIdField();
211 return $this->row->$field;
215 * Get the date, formatted in user's language
216 * @return string
218 public function formatDate() {
219 return $this->list->getLanguage()->userDate( $this->getTimestamp(),
220 $this->list->getUser() );
224 * Get the time, formatted in user's language
225 * @return string
227 public function formatTime() {
228 return $this->list->getLanguage()->userTime( $this->getTimestamp(),
229 $this->list->getUser() );
233 * Get the timestamp in MW 14-char form
234 * @return mixed
236 public function getTimestamp() {
237 $field = $this->getTimestampField();
238 return wfTimestamp( TS_MW, $this->row->$field );
242 * Get the author user ID
243 * @return int
245 public function getAuthorId() {
246 $field = $this->getAuthorIdField();
247 return intval( $this->row->$field );
251 * Get the author user name
252 * @return string
254 public function getAuthorName() {
255 $field = $this->getAuthorNameField();
256 return strval( $this->row->$field );
260 * Returns true if the current user can view the item
262 abstract public function canView();
265 * Returns true if the current user can view the item text/file
267 abstract public function canViewContent();
270 * Get the HTML of the list item. Should be include "<li></li>" tags.
271 * This is used to show the list in HTML form, by the special page.
273 abstract public function getHTML();
276 * Returns an instance of LinkRenderer
277 * @return \MediaWiki\Linker\LinkRenderer
279 protected function getLinkRenderer() {
280 return MediaWikiServices::getInstance()->getLinkRenderer();
284 class RevisionList extends RevisionListBase {
285 public function getType() {
286 return 'revision';
290 * @param IDatabase $db
291 * @return mixed
293 public function doQuery( $db ) {
294 $conds = [ 'rev_page' => $this->title->getArticleID() ];
295 if ( $this->ids !== null ) {
296 $conds['rev_id'] = array_map( 'intval', $this->ids );
298 return $db->select(
299 [ 'revision', 'page', 'user' ],
300 array_merge( Revision::selectFields(), Revision::selectUserFields() ),
301 $conds,
302 __METHOD__,
303 [ 'ORDER BY' => 'rev_id DESC' ],
305 'page' => Revision::pageJoinCond(),
306 'user' => Revision::userJoinCond() ]
310 public function newItem( $row ) {
311 return new RevisionItem( $this, $row );
316 * Item class for a live revision table row
318 class RevisionItem extends RevisionItemBase {
319 /** @var Revision */
320 protected $revision;
322 /** @var RequestContext */
323 protected $context;
325 public function __construct( $list, $row ) {
326 parent::__construct( $list, $row );
327 $this->revision = new Revision( $row );
328 $this->context = $list->getContext();
331 public function getIdField() {
332 return 'rev_id';
335 public function getTimestampField() {
336 return 'rev_timestamp';
339 public function getAuthorIdField() {
340 return 'rev_user';
343 public function getAuthorNameField() {
344 return 'rev_user_text';
347 public function canView() {
348 return $this->revision->userCan( Revision::DELETED_RESTRICTED, $this->context->getUser() );
351 public function canViewContent() {
352 return $this->revision->userCan( Revision::DELETED_TEXT, $this->context->getUser() );
355 public function isDeleted() {
356 return $this->revision->isDeleted( Revision::DELETED_TEXT );
360 * Get the HTML link to the revision text.
361 * @todo Essentially a copy of RevDelRevisionItem::getRevisionLink. That class
362 * should inherit from this one, and implement an appropriate interface instead
363 * of extending RevDelItem
364 * @return string
366 protected function getRevisionLink() {
367 $date = $this->list->getLanguage()->userTimeAndDate(
368 $this->revision->getTimestamp(), $this->list->getUser() );
370 if ( $this->isDeleted() && !$this->canViewContent() ) {
371 return htmlspecialchars( $date );
373 $linkRenderer = $this->getLinkRenderer();
374 return $linkRenderer->makeKnownLink(
375 $this->list->title,
376 $date,
379 'oldid' => $this->revision->getId(),
380 'unhide' => 1
386 * Get the HTML link to the diff.
387 * @todo Essentially a copy of RevDelRevisionItem::getDiffLink. That class
388 * should inherit from this one, and implement an appropriate interface instead
389 * of extending RevDelItem
390 * @return string
392 protected function getDiffLink() {
393 if ( $this->isDeleted() && !$this->canViewContent() ) {
394 return $this->context->msg( 'diff' )->escaped();
395 } else {
396 $linkRenderer = $this->getLinkRenderer();
397 return $linkRenderer->makeKnownLink(
398 $this->list->title,
399 $this->list->msg( 'diff' )->text(),
402 'diff' => $this->revision->getId(),
403 'oldid' => 'prev',
404 'unhide' => 1
411 * @todo Essentially a copy of RevDelRevisionItem::getHTML. That class
412 * should inherit from this one, and implement an appropriate interface instead
413 * of extending RevDelItem
414 * @return string
416 public function getHTML() {
417 $difflink = $this->context->msg( 'parentheses' )
418 ->rawParams( $this->getDiffLink() )->escaped();
419 $revlink = $this->getRevisionLink();
420 $userlink = Linker::revUserLink( $this->revision );
421 $comment = Linker::revComment( $this->revision );
422 if ( $this->isDeleted() ) {
423 $revlink = "<span class=\"history-deleted\">$revlink</span>";
425 return "<li>$difflink $revlink $userlink $comment</li>";