Make sure BlockHideName really defaults to 0, not false. Fixes bug 10007.
[lhc/web/wiklou.git] / includes / Revision.php
1 <?php
2 /**
3 * @todo document
4 */
5
6 /**
7 * @todo document
8 */
9 class Revision {
10 const DELETED_TEXT = 1;
11 const DELETED_COMMENT = 2;
12 const DELETED_USER = 4;
13 const DELETED_RESTRICTED = 8;
14
15 /**
16 * Load a page revision from a given revision ID number.
17 * Returns null if no such revision can be found.
18 *
19 * @param int $id
20 * @access public
21 * @static
22 */
23 public static function newFromId( $id ) {
24 return Revision::newFromConds(
25 array( 'page_id=rev_page',
26 'rev_id' => intval( $id ) ) );
27 }
28
29 /**
30 * Load either the current, or a specified, revision
31 * that's attached to a given title. If not attached
32 * to that title, will return null.
33 *
34 * @param Title $title
35 * @param int $id
36 * @return Revision
37 * @access public
38 * @static
39 */
40 public static function newFromTitle( &$title, $id = 0 ) {
41 if( $id ) {
42 $matchId = intval( $id );
43 } else {
44 $matchId = 'page_latest';
45 }
46 return Revision::newFromConds(
47 array( "rev_id=$matchId",
48 'page_id=rev_page',
49 'page_namespace' => $title->getNamespace(),
50 'page_title' => $title->getDbkey() ) );
51 }
52
53 /**
54 * Load a page revision from a given revision ID number.
55 * Returns null if no such revision can be found.
56 *
57 * @param Database $db
58 * @param int $id
59 * @access public
60 * @static
61 */
62 public static function loadFromId( &$db, $id ) {
63 return Revision::loadFromConds( $db,
64 array( 'page_id=rev_page',
65 'rev_id' => intval( $id ) ) );
66 }
67
68 /**
69 * Load either the current, or a specified, revision
70 * that's attached to a given page. If not attached
71 * to that page, will return null.
72 *
73 * @param Database $db
74 * @param int $pageid
75 * @param int $id
76 * @return Revision
77 * @access public
78 * @static
79 */
80 public static function loadFromPageId( $db, $pageid, $id = 0 ) {
81 $conds=array('page_id=rev_page','rev_page'=>intval( $pageid ), 'page_id'=>intval( $pageid ));
82 if( $id ) {
83 $conds['rev_id']=intval($id);
84 } else {
85 $conds[]='rev_id=page_latest';
86 }
87 return Revision::loadFromConds( $db, $conds );
88 }
89
90 /**
91 * Load either the current, or a specified, revision
92 * that's attached to a given page. If not attached
93 * to that page, will return null.
94 *
95 * @param Database $db
96 * @param Title $title
97 * @param int $id
98 * @return Revision
99 * @access public
100 * @static
101 */
102 public static function loadFromTitle( &$db, $title, $id = 0 ) {
103 if( $id ) {
104 $matchId = intval( $id );
105 } else {
106 $matchId = 'page_latest';
107 }
108 return Revision::loadFromConds(
109 $db,
110 array( "rev_id=$matchId",
111 'page_id=rev_page',
112 'page_namespace' => $title->getNamespace(),
113 'page_title' => $title->getDbkey() ) );
114 }
115
116 /**
117 * Load the revision for the given title with the given timestamp.
118 * WARNING: Timestamps may in some circumstances not be unique,
119 * so this isn't the best key to use.
120 *
121 * @param Database $db
122 * @param Title $title
123 * @param string $timestamp
124 * @return Revision
125 * @access public
126 * @static
127 */
128 public static function loadFromTimestamp( &$db, &$title, $timestamp ) {
129 return Revision::loadFromConds(
130 $db,
131 array( 'rev_timestamp' => $db->timestamp( $timestamp ),
132 'page_id=rev_page',
133 'page_namespace' => $title->getNamespace(),
134 'page_title' => $title->getDbkey() ) );
135 }
136
137 /**
138 * Given a set of conditions, fetch a revision.
139 *
140 * @param array $conditions
141 * @return Revision
142 * @access private
143 * @static
144 */
145 private static function newFromConds( $conditions ) {
146 $db = wfGetDB( DB_SLAVE );
147 $row = Revision::loadFromConds( $db, $conditions );
148 if( is_null( $row ) ) {
149 $dbw = wfGetDB( DB_MASTER );
150 $row = Revision::loadFromConds( $dbw, $conditions );
151 }
152 return $row;
153 }
154
155 /**
156 * Given a set of conditions, fetch a revision from
157 * the given database connection.
158 *
159 * @param Database $db
160 * @param array $conditions
161 * @return Revision
162 * @access private
163 * @static
164 */
165 private static function loadFromConds( $db, $conditions ) {
166 $res = Revision::fetchFromConds( $db, $conditions );
167 if( $res ) {
168 $row = $res->fetchObject();
169 $res->free();
170 if( $row ) {
171 $ret = new Revision( $row );
172 return $ret;
173 }
174 }
175 $ret = null;
176 return $ret;
177 }
178
179 /**
180 * Return a wrapper for a series of database rows to
181 * fetch all of a given page's revisions in turn.
182 * Each row can be fed to the constructor to get objects.
183 *
184 * @param Title $title
185 * @return ResultWrapper
186 * @access public
187 * @static
188 */
189 public static function fetchAllRevisions( &$title ) {
190 return Revision::fetchFromConds(
191 wfGetDB( DB_SLAVE ),
192 array( 'page_namespace' => $title->getNamespace(),
193 'page_title' => $title->getDbkey(),
194 'page_id=rev_page' ) );
195 }
196
197 /**
198 * Return a wrapper for a series of database rows to
199 * fetch all of a given page's revisions in turn.
200 * Each row can be fed to the constructor to get objects.
201 *
202 * @param Title $title
203 * @return ResultWrapper
204 * @access public
205 * @static
206 */
207 public static function fetchRevision( &$title ) {
208 return Revision::fetchFromConds(
209 wfGetDB( DB_SLAVE ),
210 array( 'rev_id=page_latest',
211 'page_namespace' => $title->getNamespace(),
212 'page_title' => $title->getDbkey(),
213 'page_id=rev_page' ) );
214 }
215
216 /**
217 * Given a set of conditions, return a ResultWrapper
218 * which will return matching database rows with the
219 * fields necessary to build Revision objects.
220 *
221 * @param Database $db
222 * @param array $conditions
223 * @return ResultWrapper
224 * @access private
225 * @static
226 */
227 private static function fetchFromConds( $db, $conditions ) {
228 $res = $db->select(
229 array( 'page', 'revision' ),
230 array( 'page_namespace',
231 'page_title',
232 'page_latest',
233 'rev_id',
234 'rev_page',
235 'rev_text_id',
236 'rev_comment',
237 'rev_user_text',
238 'rev_user',
239 'rev_minor_edit',
240 'rev_timestamp',
241 'rev_deleted',
242 'rev_len' ),
243 $conditions,
244 'Revision::fetchRow',
245 array( 'LIMIT' => 1 ) );
246 $ret = $db->resultObject( $res );
247 return $ret;
248 }
249
250 /**
251 * Return the list of revision fields that should be selected to create
252 * a new revision.
253 */
254 static function selectFields() {
255 return array(
256 'rev_id',
257 'rev_page',
258 'rev_text_id',
259 'rev_timestamp',
260 'rev_comment',
261 'rev_minor_edit',
262 'rev_user',
263 'rev_user_text,'.
264 'rev_deleted',
265 'rev_len'
266 );
267 }
268
269 /**
270 * @param object $row
271 * @access private
272 */
273 function Revision( $row ) {
274 if( is_object( $row ) ) {
275 $this->mId = intval( $row->rev_id );
276 $this->mPage = intval( $row->rev_page );
277 $this->mTextId = intval( $row->rev_text_id );
278 $this->mComment = $row->rev_comment;
279 $this->mUserText = $row->rev_user_text;
280 $this->mUser = intval( $row->rev_user );
281 $this->mMinorEdit = intval( $row->rev_minor_edit );
282 $this->mTimestamp = $row->rev_timestamp;
283 $this->mDeleted = intval( $row->rev_deleted );
284
285 if( !isset( $row->rev_len ) || is_null( $row->rev_len ) )
286 $this->mSize = null;
287 else
288 $this->mSize = intval( $row->rev_len );
289
290 if( isset( $row->page_latest ) ) {
291 $this->mCurrent = ( $row->rev_id == $row->page_latest );
292 $this->mTitle = Title::makeTitle( $row->page_namespace,
293 $row->page_title );
294 } else {
295 $this->mCurrent = false;
296 $this->mTitle = null;
297 }
298
299 // Lazy extraction...
300 $this->mText = null;
301 if( isset( $row->old_text ) ) {
302 $this->mTextRow = $row;
303 } else {
304 // 'text' table row entry will be lazy-loaded
305 $this->mTextRow = null;
306 }
307 } elseif( is_array( $row ) ) {
308 // Build a new revision to be saved...
309 global $wgUser;
310
311 $this->mId = isset( $row['id'] ) ? intval( $row['id'] ) : null;
312 $this->mPage = isset( $row['page'] ) ? intval( $row['page'] ) : null;
313 $this->mTextId = isset( $row['text_id'] ) ? intval( $row['text_id'] ) : null;
314 $this->mUserText = isset( $row['user_text'] ) ? strval( $row['user_text'] ) : $wgUser->getName();
315 $this->mUser = isset( $row['user'] ) ? intval( $row['user'] ) : $wgUser->getId();
316 $this->mMinorEdit = isset( $row['minor_edit'] ) ? intval( $row['minor_edit'] ) : 0;
317 $this->mTimestamp = isset( $row['timestamp'] ) ? strval( $row['timestamp'] ) : wfTimestamp( TS_MW );
318 $this->mDeleted = isset( $row['deleted'] ) ? intval( $row['deleted'] ) : 0;
319 $this->mSize = isset( $row['len'] ) ? intval( $row['len'] ) : null;
320
321 // Enforce spacing trimming on supplied text
322 $this->mComment = isset( $row['comment'] ) ? trim( strval( $row['comment'] ) ) : null;
323 $this->mText = isset( $row['text'] ) ? rtrim( strval( $row['text'] ) ) : null;
324 $this->mTextRow = null;
325
326 $this->mTitle = null; # Load on demand if needed
327 $this->mCurrent = false;
328 # If we still have no len_size, see it we have the text to figure it out
329 if ( !$this->mSize )
330 $this->mSize = is_null($this->mText) ? null : strlen($this->mText);
331 } else {
332 throw new MWException( 'Revision constructor passed invalid row format.' );
333 }
334 }
335
336 /**#@+
337 * @access public
338 */
339
340 /**
341 * @return int
342 */
343 function getId() {
344 return $this->mId;
345 }
346
347 /**
348 * @return int
349 */
350 function getTextId() {
351 return $this->mTextId;
352 }
353
354 /**
355 * Returns the length of the text in this revision, or null if unknown.
356 */
357 function getSize() {
358 return $this->mSize;
359 }
360
361 /**
362 * Returns the title of the page associated with this entry.
363 * @return Title
364 */
365 function getTitle() {
366 if( isset( $this->mTitle ) ) {
367 return $this->mTitle;
368 }
369 $dbr = wfGetDB( DB_SLAVE );
370 $row = $dbr->selectRow(
371 array( 'page', 'revision' ),
372 array( 'page_namespace', 'page_title' ),
373 array( 'page_id=rev_page',
374 'rev_id' => $this->mId ),
375 'Revision::getTitle' );
376 if( $row ) {
377 $this->mTitle = Title::makeTitle( $row->page_namespace,
378 $row->page_title );
379 }
380 return $this->mTitle;
381 }
382
383 /**
384 * Set the title of the revision
385 * @param Title $title
386 */
387 function setTitle( $title ) {
388 $this->mTitle = $title;
389 }
390
391 /**
392 * @return int
393 */
394 function getPage() {
395 return $this->mPage;
396 }
397
398 /**
399 * Fetch revision's user id if it's available to all users
400 * @return int
401 */
402 function getUser() {
403 if( $this->isDeleted( self::DELETED_USER ) ) {
404 return 0;
405 } else {
406 return $this->mUser;
407 }
408 }
409
410 /**
411 * Fetch revision's user id without regard for the current user's permissions
412 * @return string
413 */
414 function getRawUser() {
415 return $this->mUser;
416 }
417
418 /**
419 * Fetch revision's username if it's available to all users
420 * @return string
421 */
422 function getUserText() {
423 if( $this->isDeleted( self::DELETED_USER ) ) {
424 return "";
425 } else {
426 return $this->mUserText;
427 }
428 }
429
430 /**
431 * Fetch revision's username without regard for view restrictions
432 * @return string
433 */
434 function getRawUserText() {
435 return $this->mUserText;
436 }
437
438 /**
439 * Fetch revision comment if it's available to all users
440 * @return string
441 */
442 function getComment() {
443 if( $this->isDeleted( self::DELETED_COMMENT ) ) {
444 return "";
445 } else {
446 return $this->mComment;
447 }
448 }
449
450 /**
451 * Fetch revision comment without regard for the current user's permissions
452 * @return string
453 */
454 function getRawComment() {
455 return $this->mComment;
456 }
457
458 /**
459 * @return bool
460 */
461 function isMinor() {
462 return (bool)$this->mMinorEdit;
463 }
464
465 /**
466 * int $field one of DELETED_* bitfield constants
467 * @return bool
468 */
469 function isDeleted( $field ) {
470 return ($this->mDeleted & $field) == $field;
471 }
472
473 /**
474 * Fetch revision text if it's available to all users
475 * @return string
476 */
477 function getText() {
478 if( $this->isDeleted( self::DELETED_TEXT ) ) {
479 return "";
480 } else {
481 return $this->getRawText();
482 }
483 }
484
485 /**
486 * Fetch revision text without regard for view restrictions
487 * @return string
488 */
489 function getRawText() {
490 if( is_null( $this->mText ) ) {
491 // Revision text is immutable. Load on demand:
492 $this->mText = $this->loadText();
493 }
494 return $this->mText;
495 }
496
497 /**
498 * Fetch revision text if it's available to THIS user
499 * @return string
500 */
501 function revText() {
502 if( !$this->userCan( self::DELETED_TEXT ) ) {
503 return "";
504 } else {
505 return $this->getRawText();
506 }
507 }
508
509 /**
510 * @return string
511 */
512 function getTimestamp() {
513 return wfTimestamp(TS_MW, $this->mTimestamp);
514 }
515
516 /**
517 * @return bool
518 */
519 function isCurrent() {
520 return $this->mCurrent;
521 }
522
523 /**
524 * @return Revision
525 */
526 function getPrevious() {
527 $prev = $this->mTitle->getPreviousRevisionID( $this->mId );
528 if ( $prev ) {
529 return Revision::newFromTitle( $this->mTitle, $prev );
530 } else {
531 return null;
532 }
533 }
534
535 /**
536 * @return Revision
537 */
538 function getNext() {
539 $next = $this->mTitle->getNextRevisionID( $this->mId );
540 if ( $next ) {
541 return Revision::newFromTitle( $this->mTitle, $next );
542 } else {
543 return null;
544 }
545 }
546 /**#@-*/
547
548 /**
549 * Get revision text associated with an old or archive row
550 * $row is usually an object from wfFetchRow(), both the flags and the text
551 * field must be included
552 * @static
553 * @param integer $row Id of a row
554 * @param string $prefix table prefix (default 'old_')
555 * @return string $text|false the text requested
556 */
557 public static function getRevisionText( $row, $prefix = 'old_' ) {
558 $fname = 'Revision::getRevisionText';
559 wfProfileIn( $fname );
560
561 # Get data
562 $textField = $prefix . 'text';
563 $flagsField = $prefix . 'flags';
564
565 if( isset( $row->$flagsField ) ) {
566 $flags = explode( ',', $row->$flagsField );
567 } else {
568 $flags = array();
569 }
570
571 if( isset( $row->$textField ) ) {
572 $text = $row->$textField;
573 } else {
574 wfProfileOut( $fname );
575 return false;
576 }
577
578 # Use external methods for external objects, text in table is URL-only then
579 if ( in_array( 'external', $flags ) ) {
580 $url=$text;
581 @list(/* $proto */,$path)=explode('://',$url,2);
582 if ($path=="") {
583 wfProfileOut( $fname );
584 return false;
585 }
586 $text=ExternalStore::fetchFromURL($url);
587 }
588
589 // If the text was fetched without an error, convert it
590 if ( $text !== false ) {
591 if( in_array( 'gzip', $flags ) ) {
592 # Deal with optional compression of archived pages.
593 # This can be done periodically via maintenance/compressOld.php, and
594 # as pages are saved if $wgCompressRevisions is set.
595 $text = gzinflate( $text );
596 }
597
598 if( in_array( 'object', $flags ) ) {
599 # Generic compressed storage
600 $obj = unserialize( $text );
601 if ( !is_object( $obj ) ) {
602 // Invalid object
603 wfProfileOut( $fname );
604 return false;
605 }
606 $text = $obj->getText();
607 }
608
609 global $wgLegacyEncoding;
610 if( $wgLegacyEncoding && !in_array( 'utf-8', $flags ) ) {
611 # Old revisions kept around in a legacy encoding?
612 # Upconvert on demand.
613 global $wgInputEncoding, $wgContLang;
614 $text = $wgContLang->iconv( $wgLegacyEncoding, $wgInputEncoding, $text );
615 }
616 }
617 wfProfileOut( $fname );
618 return $text;
619 }
620
621 /**
622 * If $wgCompressRevisions is enabled, we will compress data.
623 * The input string is modified in place.
624 * Return value is the flags field: contains 'gzip' if the
625 * data is compressed, and 'utf-8' if we're saving in UTF-8
626 * mode.
627 *
628 * @static
629 * @param mixed $text reference to a text
630 * @return string
631 */
632 function compressRevisionText( &$text ) {
633 global $wgCompressRevisions;
634 $flags = array();
635
636 # Revisions not marked this way will be converted
637 # on load if $wgLegacyCharset is set in the future.
638 $flags[] = 'utf-8';
639
640 if( $wgCompressRevisions ) {
641 if( function_exists( 'gzdeflate' ) ) {
642 $text = gzdeflate( $text );
643 $flags[] = 'gzip';
644 } else {
645 wfDebug( "Revision::compressRevisionText() -- no zlib support, not compressing\n" );
646 }
647 }
648 return implode( ',', $flags );
649 }
650
651 /**
652 * Insert a new revision into the database, returning the new revision ID
653 * number on success and dies horribly on failure.
654 *
655 * @param Database $dbw
656 * @return int
657 */
658 function insertOn( &$dbw ) {
659 global $wgDefaultExternalStore;
660
661 $fname = 'Revision::insertOn';
662 wfProfileIn( $fname );
663
664 $data = $this->mText;
665 $flags = Revision::compressRevisionText( $data );
666
667 # Write to external storage if required
668 if ( $wgDefaultExternalStore ) {
669 if ( is_array( $wgDefaultExternalStore ) ) {
670 // Distribute storage across multiple clusters
671 $store = $wgDefaultExternalStore[mt_rand(0, count( $wgDefaultExternalStore ) - 1)];
672 } else {
673 $store = $wgDefaultExternalStore;
674 }
675 // Store and get the URL
676 $data = ExternalStore::insert( $store, $data );
677 if ( !$data ) {
678 # This should only happen in the case of a configuration error, where the external store is not valid
679 throw new MWException( "Unable to store text to external storage $store" );
680 }
681 if ( $flags ) {
682 $flags .= ',';
683 }
684 $flags .= 'external';
685 }
686
687 # Record the text (or external storage URL) to the text table
688 if( !isset( $this->mTextId ) ) {
689 $old_id = $dbw->nextSequenceValue( 'text_old_id_val' );
690 $dbw->insert( 'text',
691 array(
692 'old_id' => $old_id,
693 'old_text' => $data,
694 'old_flags' => $flags,
695 ), $fname
696 );
697 $this->mTextId = $dbw->insertId();
698 }
699
700 # Record the edit in revisions
701 $rev_id = isset( $this->mId )
702 ? $this->mId
703 : $dbw->nextSequenceValue( 'rev_rev_id_val' );
704 $dbw->insert( 'revision',
705 array(
706 'rev_id' => $rev_id,
707 'rev_page' => $this->mPage,
708 'rev_text_id' => $this->mTextId,
709 'rev_comment' => $this->mComment,
710 'rev_minor_edit' => $this->mMinorEdit ? 1 : 0,
711 'rev_user' => $this->mUser,
712 'rev_user_text' => $this->mUserText,
713 'rev_timestamp' => $dbw->timestamp( $this->mTimestamp ),
714 'rev_deleted' => $this->mDeleted,
715 'rev_len' => $this->mSize,
716 ), $fname
717 );
718
719 $this->mId = !is_null($rev_id) ? $rev_id : $dbw->insertId();
720 wfProfileOut( $fname );
721 return $this->mId;
722 }
723
724 /**
725 * Lazy-load the revision's text.
726 * Currently hardcoded to the 'text' table storage engine.
727 *
728 * @return string
729 * @access private
730 */
731 function loadText() {
732 $fname = 'Revision::loadText';
733 wfProfileIn( $fname );
734
735 // Caching may be beneficial for massive use of external storage
736 global $wgRevisionCacheExpiry, $wgMemc;
737 $key = wfMemcKey( 'revisiontext', 'textid', $this->getTextId() );
738 if( $wgRevisionCacheExpiry ) {
739 $text = $wgMemc->get( $key );
740 if( is_string( $text ) ) {
741 wfProfileOut( $fname );
742 return $text;
743 }
744 }
745
746 // If we kept data for lazy extraction, use it now...
747 if ( isset( $this->mTextRow ) ) {
748 $row = $this->mTextRow;
749 $this->mTextRow = null;
750 } else {
751 $row = null;
752 }
753
754 if( !$row ) {
755 // Text data is immutable; check slaves first.
756 $dbr = wfGetDB( DB_SLAVE );
757 $row = $dbr->selectRow( 'text',
758 array( 'old_text', 'old_flags' ),
759 array( 'old_id' => $this->getTextId() ),
760 $fname);
761 }
762
763 if( !$row ) {
764 // Possible slave lag!
765 $dbw = wfGetDB( DB_MASTER );
766 $row = $dbw->selectRow( 'text',
767 array( 'old_text', 'old_flags' ),
768 array( 'old_id' => $this->getTextId() ),
769 $fname);
770 }
771
772 $text = Revision::getRevisionText( $row );
773
774 if( $wgRevisionCacheExpiry ) {
775 $wgMemc->set( $key, $text, $wgRevisionCacheExpiry );
776 }
777
778 wfProfileOut( $fname );
779
780 return $text;
781 }
782
783 /**
784 * Create a new null-revision for insertion into a page's
785 * history. This will not re-save the text, but simply refer
786 * to the text from the previous version.
787 *
788 * Such revisions can for instance identify page rename
789 * operations and other such meta-modifications.
790 *
791 * @param Database $dbw
792 * @param int $pageId ID number of the page to read from
793 * @param string $summary
794 * @param bool $minor
795 * @return Revision
796 */
797 function newNullRevision( &$dbw, $pageId, $summary, $minor ) {
798 $fname = 'Revision::newNullRevision';
799 wfProfileIn( $fname );
800
801 $current = $dbw->selectRow(
802 array( 'page', 'revision' ),
803 array( 'page_latest', 'rev_text_id' ),
804 array(
805 'page_id' => $pageId,
806 'page_latest=rev_id',
807 ),
808 $fname );
809
810 if( $current ) {
811 $revision = new Revision( array(
812 'page' => $pageId,
813 'comment' => $summary,
814 'minor_edit' => $minor,
815 'text_id' => $current->rev_text_id,
816 ) );
817 } else {
818 $revision = null;
819 }
820
821 wfProfileOut( $fname );
822 return $revision;
823 }
824
825 /**
826 * Determine if the current user is allowed to view a particular
827 * field of this revision, if it's marked as deleted.
828 * @param int $field one of self::DELETED_TEXT,
829 * self::DELETED_COMMENT,
830 * self::DELETED_USER
831 * @return bool
832 */
833 function userCan( $field ) {
834 if( ( $this->mDeleted & $field ) == $field ) {
835 global $wgUser;
836 $permission = ( $this->mDeleted & self::DELETED_RESTRICTED ) == self::DELETED_RESTRICTED
837 ? 'hiderevision'
838 : 'deleterevision';
839 wfDebug( "Checking for $permission due to $field match on $this->mDeleted\n" );
840 return $wgUser->isAllowed( $permission );
841 } else {
842 return true;
843 }
844 }
845
846
847 /**
848 * Get rev_timestamp from rev_id, without loading the rest of the row
849 * @param integer $id
850 */
851 static function getTimestampFromID( $id ) {
852 $dbr = wfGetDB( DB_SLAVE );
853 $timestamp = $dbr->selectField( 'revision', 'rev_timestamp',
854 array( 'rev_id' => $id ), __METHOD__ );
855 if ( $timestamp === false ) {
856 # Not in slave, try master
857 $dbw = wfGetDB( DB_MASTER );
858 $timestamp = $dbw->selectField( 'revision', 'rev_timestamp',
859 array( 'rev_id' => $id ), __METHOD__ );
860 }
861 return $timestamp;
862 }
863
864 static function countByPageId( $db, $id ) {
865 $row = $db->selectRow( 'revision', 'COUNT(*) AS revCount',
866 array( 'rev_page' => $id ), __METHOD__ );
867 if( $row ) {
868 return $row->revCount;
869 }
870 return 0;
871 }
872
873 static function countByTitle( $db, $title ) {
874 $id = $title->getArticleId();
875 if( $id ) {
876 return Revision::countByPageId( $db, $id );
877 }
878 return 0;
879 }
880 }
881
882 /**
883 * Aliases for backwards compatibility with 1.6
884 */
885 define( 'MW_REV_DELETED_TEXT', Revision::DELETED_TEXT );
886 define( 'MW_REV_DELETED_COMMENT', Revision::DELETED_COMMENT );
887 define( 'MW_REV_DELETED_USER', Revision::DELETED_USER );
888 define( 'MW_REV_DELETED_RESTRICTED', Revision::DELETED_RESTRICTED );
889
890
891 ?>