]> scripts.mit.edu Git - autoinstallsdev/mediawiki.git/blob - includes/Revision.php
MediaWiki 1.11.0
[autoinstallsdev/mediawiki.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