MediaWiki  REL1_19
Revision.php
Go to the documentation of this file.
00001 <?php
00002 
00006 class Revision {
00007         protected $mId;
00008         protected $mPage;
00009         protected $mUserText;
00010         protected $mOrigUserText;
00011         protected $mUser;
00012         protected $mMinorEdit;
00013         protected $mTimestamp;
00014         protected $mDeleted;
00015         protected $mSize;
00016         protected $mSha1;
00017         protected $mParentId;
00018         protected $mComment;
00019         protected $mText;
00020         protected $mTextRow;
00021         protected $mTitle;
00022         protected $mCurrent;
00023 
00024         const DELETED_TEXT = 1;
00025         const DELETED_COMMENT = 2;
00026         const DELETED_USER = 4;
00027         const DELETED_RESTRICTED = 8;
00028         // Convenience field
00029         const SUPPRESSED_USER = 12;
00030         // Audience options for Revision::getText()
00031         const FOR_PUBLIC = 1;
00032         const FOR_THIS_USER = 2;
00033         const RAW = 3;
00034 
00042         public static function newFromId( $id ) {
00043                 return Revision::newFromConds( array( 'rev_id' => intval( $id ) ) );
00044         }
00045 
00055         public static function newFromTitle( $title, $id = 0 ) {
00056                 $conds = array(
00057                         'page_namespace' => $title->getNamespace(),
00058                         'page_title'     => $title->getDBkey()
00059                 );
00060                 if ( $id ) {
00061                         // Use the specified ID
00062                         $conds['rev_id'] = $id;
00063                 } elseif ( wfGetLB()->getServerCount() > 1 ) {
00064                         // Get the latest revision ID from the master
00065                         $dbw = wfGetDB( DB_MASTER );
00066                         $latest = $dbw->selectField( 'page', 'page_latest', $conds, __METHOD__ );
00067                         if ( $latest === false ) {
00068                                 return null; // page does not exist
00069                         }
00070                         $conds['rev_id'] = $latest;
00071                 } else {
00072                         // Use a join to get the latest revision
00073                         $conds[] = 'rev_id=page_latest';
00074                 }
00075                 return Revision::newFromConds( $conds );
00076         }
00077 
00087         public static function newFromPageId( $pageId, $revId = 0 ) {
00088                 $conds = array( 'page_id' => $pageId );
00089                 if ( $revId ) {
00090                         $conds['rev_id'] = $revId;
00091                 } elseif ( wfGetLB()->getServerCount() > 1 ) {
00092                         // Get the latest revision ID from the master
00093                         $dbw = wfGetDB( DB_MASTER );
00094                         $latest = $dbw->selectField( 'page', 'page_latest', $conds, __METHOD__ );
00095                         if ( $latest === false ) {
00096                                 return null; // page does not exist
00097                         }
00098                         $conds['rev_id'] = $latest;
00099                 } else {
00100                         $conds[] = 'rev_id = page_latest';
00101                 }
00102                 return Revision::newFromConds( $conds );
00103         }
00104 
00115         public static function newFromArchiveRow( $row, $overrides = array() ) {
00116                 $attribs = $overrides + array(
00117                         'page'       => isset( $row->ar_page_id ) ? $row->ar_page_id : null,
00118                         'id'         => isset( $row->ar_rev_id ) ? $row->ar_rev_id : null,
00119                         'comment'    => $row->ar_comment,
00120                         'user'       => $row->ar_user,
00121                         'user_text'  => $row->ar_user_text,
00122                         'timestamp'  => $row->ar_timestamp,
00123                         'minor_edit' => $row->ar_minor_edit,
00124                         'text_id'    => isset( $row->ar_text_id ) ? $row->ar_text_id : null,
00125                         'deleted'    => $row->ar_deleted,
00126                         'len'        => $row->ar_len,
00127                         'sha1'       => isset( $row->ar_sha1 ) ? $row->ar_sha1 : null,
00128                 );
00129                 if ( isset( $row->ar_text ) && !$row->ar_text_id ) {
00130                         // Pre-1.5 ar_text row
00131                         $attribs['text'] = self::getRevisionText( $row, 'ar_' );
00132                         if ( $attribs['text'] === false ) {
00133                                 throw new MWException( 'Unable to load text from archive row (possibly bug 22624)' );
00134                         }
00135                 }
00136                 return new self( $attribs );
00137         }
00138 
00145         public static function newFromRow( $row ) {
00146                 return new self( $row );
00147         }
00148 
00157         public static function loadFromId( $db, $id ) {
00158                 return Revision::loadFromConds( $db, array( 'rev_id' => intval( $id ) ) );
00159         }
00160 
00171         public static function loadFromPageId( $db, $pageid, $id = 0 ) {
00172                 $conds = array( 'rev_page' => intval( $pageid ), 'page_id'  => intval( $pageid ) );
00173                 if( $id ) {
00174                         $conds['rev_id'] = intval( $id );
00175                 } else {
00176                         $conds[] = 'rev_id=page_latest';
00177                 }
00178                 return Revision::loadFromConds( $db, $conds );
00179         }
00180 
00191         public static function loadFromTitle( $db, $title, $id = 0 ) {
00192                 if( $id ) {
00193                         $matchId = intval( $id );
00194                 } else {
00195                         $matchId = 'page_latest';
00196                 }
00197                 return Revision::loadFromConds( $db,
00198                         array( "rev_id=$matchId",
00199                                    'page_namespace' => $title->getNamespace(),
00200                                    'page_title'     => $title->getDBkey() )
00201                 );
00202         }
00203 
00214         public static function loadFromTimestamp( $db, $title, $timestamp ) {
00215                 return Revision::loadFromConds( $db,
00216                         array( 'rev_timestamp'  => $db->timestamp( $timestamp ),
00217                                    'page_namespace' => $title->getNamespace(),
00218                                    'page_title'     => $title->getDBkey() )
00219                 );
00220         }
00221 
00228         public static function newFromConds( $conditions ) {
00229                 $db = wfGetDB( DB_SLAVE );
00230                 $rev = Revision::loadFromConds( $db, $conditions );
00231                 if( is_null( $rev ) && wfGetLB()->getServerCount() > 1 ) {
00232                         $dbw = wfGetDB( DB_MASTER );
00233                         $rev = Revision::loadFromConds( $dbw, $conditions );
00234                 }
00235                 return $rev;
00236         }
00237 
00246         private static function loadFromConds( $db, $conditions ) {
00247                 $res = Revision::fetchFromConds( $db, $conditions );
00248                 if( $res ) {
00249                         $row = $res->fetchObject();
00250                         if( $row ) {
00251                                 $ret = new Revision( $row );
00252                                 return $ret;
00253                         }
00254                 }
00255                 $ret = null;
00256                 return $ret;
00257         }
00258 
00267         public static function fetchRevision( $title ) {
00268                 return Revision::fetchFromConds(
00269                         wfGetDB( DB_SLAVE ),
00270                         array( 'rev_id=page_latest',
00271                                    'page_namespace' => $title->getNamespace(),
00272                                    'page_title'     => $title->getDBkey() )
00273                 );
00274         }
00275 
00285         private static function fetchFromConds( $db, $conditions ) {
00286                 $fields = array_merge(
00287                         self::selectFields(),
00288                         self::selectPageFields(),
00289                         self::selectUserFields()
00290                 );
00291                 return $db->select(
00292                         array( 'revision', 'page', 'user' ),
00293                         $fields,
00294                         $conditions,
00295                         __METHOD__,
00296                         array( 'LIMIT' => 1 ),
00297                         array( 'page' => self::pageJoinCond(), 'user' => self::userJoinCond() )
00298                 );
00299         }
00300 
00307         public static function userJoinCond() {
00308                 return array( 'LEFT JOIN', array( 'rev_user != 0', 'user_id = rev_user' ) );
00309         }
00310 
00317         public static function pageJoinCond() {
00318                 return array( 'INNER JOIN', array( 'page_id = rev_page' ) );
00319         }
00320 
00325         public static function selectFields() {
00326                 return array(
00327                         'rev_id',
00328                         'rev_page',
00329                         'rev_text_id',
00330                         'rev_timestamp',
00331                         'rev_comment',
00332                         'rev_user_text',
00333                         'rev_user',
00334                         'rev_minor_edit',
00335                         'rev_deleted',
00336                         'rev_len',
00337                         'rev_parent_id',
00338                         'rev_sha1'
00339                 );
00340         }
00341 
00346         public static function selectTextFields() {
00347                 return array(
00348                         'old_text',
00349                         'old_flags'
00350                 );
00351         }
00352 
00356         public static function selectPageFields() {
00357                 return array(
00358                         'page_namespace',
00359                         'page_title',
00360                         'page_id',
00361                         'page_latest'
00362                 );
00363         }
00364 
00368         public static function selectUserFields() {
00369                 return array( 'user_name' );
00370         }
00371 
00378         function __construct( $row ) {
00379                 if( is_object( $row ) ) {
00380                         $this->mId        = intval( $row->rev_id );
00381                         $this->mPage      = intval( $row->rev_page );
00382                         $this->mTextId    = intval( $row->rev_text_id );
00383                         $this->mComment   =         $row->rev_comment;
00384                         $this->mUser      = intval( $row->rev_user );
00385                         $this->mMinorEdit = intval( $row->rev_minor_edit );
00386                         $this->mTimestamp =         $row->rev_timestamp;
00387                         $this->mDeleted   = intval( $row->rev_deleted );
00388 
00389                         if( !isset( $row->rev_parent_id ) ) {
00390                                 $this->mParentId = is_null( $row->rev_parent_id ) ? null : 0;
00391                         } else {
00392                                 $this->mParentId  = intval( $row->rev_parent_id );
00393                         }
00394 
00395                         if( !isset( $row->rev_len ) || is_null( $row->rev_len ) ) {
00396                                 $this->mSize = null;
00397                         } else {
00398                                 $this->mSize = intval( $row->rev_len );
00399                         }
00400 
00401                         if ( !isset( $row->rev_sha1 ) ) {
00402                                 $this->mSha1 = null;
00403                         } else {
00404                                 $this->mSha1 = $row->rev_sha1;
00405                         }
00406 
00407                         if( isset( $row->page_latest ) ) {
00408                                 $this->mCurrent = ( $row->rev_id == $row->page_latest );
00409                                 $this->mTitle = Title::newFromRow( $row );
00410                         } else {
00411                                 $this->mCurrent = false;
00412                                 $this->mTitle = null;
00413                         }
00414 
00415                         // Lazy extraction...
00416                         $this->mText      = null;
00417                         if( isset( $row->old_text ) ) {
00418                                 $this->mTextRow = $row;
00419                         } else {
00420                                 // 'text' table row entry will be lazy-loaded
00421                                 $this->mTextRow = null;
00422                         }
00423 
00424                         // Use user_name for users and rev_user_text for IPs...
00425                         $this->mUserText = null; // lazy load if left null
00426                         if ( $this->mUser == 0 ) {
00427                                 $this->mUserText = $row->rev_user_text; // IP user
00428                         } elseif ( isset( $row->user_name ) ) {
00429                                 $this->mUserText = $row->user_name; // logged-in user
00430                         }
00431                         $this->mOrigUserText = $row->rev_user_text;
00432                 } elseif( is_array( $row ) ) {
00433                         // Build a new revision to be saved...
00434                         global $wgUser; // ugh
00435 
00436                         $this->mId        = isset( $row['id']         ) ? intval( $row['id']         ) : null;
00437                         $this->mPage      = isset( $row['page']       ) ? intval( $row['page']       ) : null;
00438                         $this->mTextId    = isset( $row['text_id']    ) ? intval( $row['text_id']    ) : null;
00439                         $this->mUserText  = isset( $row['user_text']  ) ? strval( $row['user_text']  ) : $wgUser->getName();
00440                         $this->mUser      = isset( $row['user']       ) ? intval( $row['user']       ) : $wgUser->getId();
00441                         $this->mMinorEdit = isset( $row['minor_edit'] ) ? intval( $row['minor_edit'] ) : 0;
00442                         $this->mTimestamp = isset( $row['timestamp']  ) ? strval( $row['timestamp']  ) : wfTimestampNow();
00443                         $this->mDeleted   = isset( $row['deleted']    ) ? intval( $row['deleted']    ) : 0;
00444                         $this->mSize      = isset( $row['len']        ) ? intval( $row['len']        ) : null;
00445                         $this->mParentId  = isset( $row['parent_id']  ) ? intval( $row['parent_id']  ) : null;
00446                         $this->mSha1      = isset( $row['sha1']  )      ? strval( $row['sha1']  )      : null;
00447 
00448                         // Enforce spacing trimming on supplied text
00449                         $this->mComment   = isset( $row['comment']    ) ?  trim( strval( $row['comment'] ) ) : null;
00450                         $this->mText      = isset( $row['text']       ) ? rtrim( strval( $row['text']    ) ) : null;
00451                         $this->mTextRow   = null;
00452 
00453                         $this->mTitle     = null; # Load on demand if needed
00454                         $this->mCurrent   = false;
00455                         # If we still have no length, see it we have the text to figure it out
00456                         if ( !$this->mSize ) {
00457                                 $this->mSize = is_null( $this->mText ) ? null : strlen( $this->mText );
00458                         }
00459                         # Same for sha1
00460                         if ( $this->mSha1 === null ) {
00461                                 $this->mSha1 = is_null( $this->mText ) ? null : self::base36Sha1( $this->mText );
00462                         }
00463                 } else {
00464                         throw new MWException( 'Revision constructor passed invalid row format.' );
00465                 }
00466                 $this->mUnpatrolled = null;
00467         }
00468 
00474         public function getId() {
00475                 return $this->mId;
00476         }
00477 
00484         public function setId( $id ) {
00485                 $this->mId = $id;
00486         }
00487 
00493         public function getTextId() {
00494                 return $this->mTextId;
00495         }
00496 
00502         public function getParentId() {
00503                 return $this->mParentId;
00504         }
00505 
00511         public function getSize() {
00512                 return $this->mSize;
00513         }
00514 
00520         public function getSha1() {
00521                 return $this->mSha1;
00522         }
00523 
00529         public function getTitle() {
00530                 if( isset( $this->mTitle ) ) {
00531                         return $this->mTitle;
00532                 }
00533                 $dbr = wfGetDB( DB_SLAVE );
00534                 $row = $dbr->selectRow(
00535                         array( 'page', 'revision' ),
00536                         self::selectPageFields(),
00537                         array( 'page_id=rev_page',
00538                                    'rev_id' => $this->mId ),
00539                         __METHOD__ );
00540                 if ( $row ) {
00541                         $this->mTitle = Title::newFromRow( $row );
00542                 }
00543                 return $this->mTitle;
00544         }
00545 
00551         public function setTitle( $title ) {
00552                 $this->mTitle = $title;
00553         }
00554 
00560         public function getPage() {
00561                 return $this->mPage;
00562         }
00563 
00577         public function getUser( $audience = self::FOR_PUBLIC, User $user = null ) {
00578                 if( $audience == self::FOR_PUBLIC && $this->isDeleted( self::DELETED_USER ) ) {
00579                         return 0;
00580                 } elseif( $audience == self::FOR_THIS_USER && !$this->userCan( self::DELETED_USER, $user ) ) {
00581                         return 0;
00582                 } else {
00583                         return $this->mUser;
00584                 }
00585         }
00586 
00592         public function getRawUser() {
00593                 return $this->mUser;
00594         }
00595 
00609         public function getUserText( $audience = self::FOR_PUBLIC, User $user = null ) {
00610                 if( $audience == self::FOR_PUBLIC && $this->isDeleted( self::DELETED_USER ) ) {
00611                         return '';
00612                 } elseif( $audience == self::FOR_THIS_USER && !$this->userCan( self::DELETED_USER, $user ) ) {
00613                         return '';
00614                 } else {
00615                         return $this->getRawUserText();
00616                 }
00617         }
00618 
00624         public function getRawUserText() {
00625                 if ( $this->mUserText === null ) {
00626                         $this->mUserText = User::whoIs( $this->mUser ); // load on demand
00627                         if ( $this->mUserText === false ) {
00628                                 # This shouldn't happen, but it can if the wiki was recovered
00629                                 # via importing revs and there is no user table entry yet.
00630                                 $this->mUserText = $this->mOrigUserText;
00631                         }
00632                 }
00633                 return $this->mUserText;
00634         }
00635 
00649         function getComment( $audience = self::FOR_PUBLIC, User $user = null ) {
00650                 if( $audience == self::FOR_PUBLIC && $this->isDeleted( self::DELETED_COMMENT ) ) {
00651                         return '';
00652                 } elseif( $audience == self::FOR_THIS_USER && !$this->userCan( self::DELETED_COMMENT, $user ) ) {
00653                         return '';
00654                 } else {
00655                         return $this->mComment;
00656                 }
00657         }
00658 
00664         public function getRawComment() {
00665                 return $this->mComment;
00666         }
00667 
00671         public function isMinor() {
00672                 return (bool)$this->mMinorEdit;
00673         }
00674 
00678         public function isUnpatrolled() {
00679                 if( $this->mUnpatrolled !== null ) {
00680                         return $this->mUnpatrolled;
00681                 }
00682                 $dbr = wfGetDB( DB_SLAVE );
00683                 $this->mUnpatrolled = $dbr->selectField( 'recentchanges',
00684                         'rc_id',
00685                         array( // Add redundant user,timestamp condition so we can use the existing index
00686                                 'rc_user_text'  => $this->getRawUserText(),
00687                                 'rc_timestamp'  => $dbr->timestamp( $this->getTimestamp() ),
00688                                 'rc_this_oldid' => $this->getId(),
00689                                 'rc_patrolled'  => 0
00690                         ),
00691                         __METHOD__
00692                 );
00693                 return (int)$this->mUnpatrolled;
00694         }
00695 
00701         public function isDeleted( $field ) {
00702                 return ( $this->mDeleted & $field ) == $field;
00703         }
00704 
00710         public function getVisibility() {
00711                 return (int)$this->mDeleted;
00712         }
00713 
00727         public function getText( $audience = self::FOR_PUBLIC, User $user = null ) {
00728                 if( $audience == self::FOR_PUBLIC && $this->isDeleted( self::DELETED_TEXT ) ) {
00729                         return '';
00730                 } elseif( $audience == self::FOR_THIS_USER && !$this->userCan( self::DELETED_TEXT, $user ) ) {
00731                         return '';
00732                 } else {
00733                         return $this->getRawText();
00734                 }
00735         }
00736 
00743         public function revText() {
00744                 wfDeprecated( __METHOD__, '1.17' );
00745                 return $this->getText( self::FOR_THIS_USER );
00746         }
00747 
00753         public function getRawText() {
00754                 if( is_null( $this->mText ) ) {
00755                         // Revision text is immutable. Load on demand:
00756                         $this->mText = $this->loadText();
00757                 }
00758                 return $this->mText;
00759         }
00760 
00764         public function getTimestamp() {
00765                 return wfTimestamp( TS_MW, $this->mTimestamp );
00766         }
00767 
00771         public function isCurrent() {
00772                 return $this->mCurrent;
00773         }
00774 
00780         public function getPrevious() {
00781                 if( $this->getTitle() ) {
00782                         $prev = $this->getTitle()->getPreviousRevisionID( $this->getId() );
00783                         if( $prev ) {
00784                                 return Revision::newFromTitle( $this->getTitle(), $prev );
00785                         }
00786                 }
00787                 return null;
00788         }
00789 
00795         public function getNext() {
00796                 if( $this->getTitle() ) {
00797                         $next = $this->getTitle()->getNextRevisionID( $this->getId() );
00798                         if ( $next ) {
00799                                 return Revision::newFromTitle( $this->getTitle(), $next );
00800                         }
00801                 }
00802                 return null;
00803         }
00804 
00812         private function getPreviousRevisionId( $db ) {
00813                 if( is_null( $this->mPage ) ) {
00814                         return 0;
00815                 }
00816                 # Use page_latest if ID is not given
00817                 if( !$this->mId ) {
00818                         $prevId = $db->selectField( 'page', 'page_latest',
00819                                 array( 'page_id' => $this->mPage ),
00820                                 __METHOD__ );
00821                 } else {
00822                         $prevId = $db->selectField( 'revision', 'rev_id',
00823                                 array( 'rev_page' => $this->mPage, 'rev_id < ' . $this->mId ),
00824                                 __METHOD__,
00825                                 array( 'ORDER BY' => 'rev_id DESC' ) );
00826                 }
00827                 return intval( $prevId );
00828         }
00829 
00839         public static function getRevisionText( $row, $prefix = 'old_' ) {
00840                 wfProfileIn( __METHOD__ );
00841 
00842                 # Get data
00843                 $textField = $prefix . 'text';
00844                 $flagsField = $prefix . 'flags';
00845 
00846                 if( isset( $row->$flagsField ) ) {
00847                         $flags = explode( ',', $row->$flagsField );
00848                 } else {
00849                         $flags = array();
00850                 }
00851 
00852                 if( isset( $row->$textField ) ) {
00853                         $text = $row->$textField;
00854                 } else {
00855                         wfProfileOut( __METHOD__ );
00856                         return false;
00857                 }
00858 
00859                 # Use external methods for external objects, text in table is URL-only then
00860                 if ( in_array( 'external', $flags ) ) {
00861                         $url = $text;
00862                         $parts = explode( '://', $url, 2 );
00863                         if( count( $parts ) == 1 || $parts[1] == '' ) {
00864                                 wfProfileOut( __METHOD__ );
00865                                 return false;
00866                         }
00867                         $text = ExternalStore::fetchFromURL( $url );
00868                 }
00869 
00870                 // If the text was fetched without an error, convert it
00871                 if ( $text !== false ) {
00872                         if( in_array( 'gzip', $flags ) ) {
00873                                 # Deal with optional compression of archived pages.
00874                                 # This can be done periodically via maintenance/compressOld.php, and
00875                                 # as pages are saved if $wgCompressRevisions is set.
00876                                 $text = gzinflate( $text );
00877                         }
00878 
00879                         if( in_array( 'object', $flags ) ) {
00880                                 # Generic compressed storage
00881                                 $obj = unserialize( $text );
00882                                 if ( !is_object( $obj ) ) {
00883                                         // Invalid object
00884                                         wfProfileOut( __METHOD__ );
00885                                         return false;
00886                                 }
00887                                 $text = $obj->getText();
00888                         }
00889 
00890                         global $wgLegacyEncoding;
00891                         if( $text !== false && $wgLegacyEncoding
00892                                 && !in_array( 'utf-8', $flags ) && !in_array( 'utf8', $flags ) )
00893                         {
00894                                 # Old revisions kept around in a legacy encoding?
00895                                 # Upconvert on demand.
00896                                 # ("utf8" checked for compatibility with some broken
00897                                 #  conversion scripts 2008-12-30)
00898                                 global $wgContLang;
00899                                 $text = $wgContLang->iconv( $wgLegacyEncoding, 'UTF-8', $text );
00900                         }
00901                 }
00902                 wfProfileOut( __METHOD__ );
00903                 return $text;
00904         }
00905 
00916         public static function compressRevisionText( &$text ) {
00917                 global $wgCompressRevisions;
00918                 $flags = array();
00919 
00920                 # Revisions not marked this way will be converted
00921                 # on load if $wgLegacyCharset is set in the future.
00922                 $flags[] = 'utf-8';
00923 
00924                 if( $wgCompressRevisions ) {
00925                         if( function_exists( 'gzdeflate' ) ) {
00926                                 $text = gzdeflate( $text );
00927                                 $flags[] = 'gzip';
00928                         } else {
00929                                 wfDebug( "Revision::compressRevisionText() -- no zlib support, not compressing\n" );
00930                         }
00931                 }
00932                 return implode( ',', $flags );
00933         }
00934 
00942         public function insertOn( $dbw ) {
00943                 global $wgDefaultExternalStore;
00944 
00945                 wfProfileIn( __METHOD__ );
00946 
00947                 $data = $this->mText;
00948                 $flags = Revision::compressRevisionText( $data );
00949 
00950                 # Write to external storage if required
00951                 if( $wgDefaultExternalStore ) {
00952                         // Store and get the URL
00953                         $data = ExternalStore::insertToDefault( $data );
00954                         if( !$data ) {
00955                                 throw new MWException( "Unable to store text to external storage" );
00956                         }
00957                         if( $flags ) {
00958                                 $flags .= ',';
00959                         }
00960                         $flags .= 'external';
00961                 }
00962 
00963                 # Record the text (or external storage URL) to the text table
00964                 if( !isset( $this->mTextId ) ) {
00965                         $old_id = $dbw->nextSequenceValue( 'text_old_id_seq' );
00966                         $dbw->insert( 'text',
00967                                 array(
00968                                         'old_id'    => $old_id,
00969                                         'old_text'  => $data,
00970                                         'old_flags' => $flags,
00971                                 ), __METHOD__
00972                         );
00973                         $this->mTextId = $dbw->insertId();
00974                 }
00975 
00976                 if ( $this->mComment === null ) $this->mComment = "";
00977 
00978                 # Record the edit in revisions
00979                 $rev_id = isset( $this->mId )
00980                         ? $this->mId
00981                         : $dbw->nextSequenceValue( 'revision_rev_id_seq' );
00982                 $dbw->insert( 'revision',
00983                         array(
00984                                 'rev_id'         => $rev_id,
00985                                 'rev_page'       => $this->mPage,
00986                                 'rev_text_id'    => $this->mTextId,
00987                                 'rev_comment'    => $this->mComment,
00988                                 'rev_minor_edit' => $this->mMinorEdit ? 1 : 0,
00989                                 'rev_user'       => $this->mUser,
00990                                 'rev_user_text'  => $this->mUserText,
00991                                 'rev_timestamp'  => $dbw->timestamp( $this->mTimestamp ),
00992                                 'rev_deleted'    => $this->mDeleted,
00993                                 'rev_len'        => $this->mSize,
00994                                 'rev_parent_id'  => is_null( $this->mParentId )
00995                                         ? $this->getPreviousRevisionId( $dbw )
00996                                         : $this->mParentId,
00997                                 'rev_sha1'       => is_null( $this->mSha1 )
00998                                         ? Revision::base36Sha1( $this->mText )
00999                                         : $this->mSha1
01000                         ), __METHOD__
01001                 );
01002 
01003                 $this->mId = !is_null( $rev_id ) ? $rev_id : $dbw->insertId();
01004 
01005                 wfRunHooks( 'RevisionInsertComplete', array( &$this, $data, $flags ) );
01006 
01007                 wfProfileOut( __METHOD__ );
01008                 return $this->mId;
01009         }
01010 
01016         public static function base36Sha1( $text ) {
01017                 return wfBaseConvert( sha1( $text ), 16, 36, 31 );
01018         }
01019 
01026         protected function loadText() {
01027                 wfProfileIn( __METHOD__ );
01028 
01029                 // Caching may be beneficial for massive use of external storage
01030                 global $wgRevisionCacheExpiry, $wgMemc;
01031                 $textId = $this->getTextId();
01032                 $key = wfMemcKey( 'revisiontext', 'textid', $textId );
01033                 if( $wgRevisionCacheExpiry ) {
01034                         $text = $wgMemc->get( $key );
01035                         if( is_string( $text ) ) {
01036                                 wfDebug( __METHOD__ . ": got id $textId from cache\n" );
01037                                 wfProfileOut( __METHOD__ );
01038                                 return $text;
01039                         }
01040                 }
01041 
01042                 // If we kept data for lazy extraction, use it now...
01043                 if ( isset( $this->mTextRow ) ) {
01044                         $row = $this->mTextRow;
01045                         $this->mTextRow = null;
01046                 } else {
01047                         $row = null;
01048                 }
01049 
01050                 if( !$row ) {
01051                         // Text data is immutable; check slaves first.
01052                         $dbr = wfGetDB( DB_SLAVE );
01053                         $row = $dbr->selectRow( 'text',
01054                                 array( 'old_text', 'old_flags' ),
01055                                 array( 'old_id' => $this->getTextId() ),
01056                                 __METHOD__ );
01057                 }
01058 
01059                 if( !$row && wfGetLB()->getServerCount() > 1 ) {
01060                         // Possible slave lag!
01061                         $dbw = wfGetDB( DB_MASTER );
01062                         $row = $dbw->selectRow( 'text',
01063                                 array( 'old_text', 'old_flags' ),
01064                                 array( 'old_id' => $this->getTextId() ),
01065                                 __METHOD__ );
01066                 }
01067 
01068                 $text = self::getRevisionText( $row );
01069 
01070                 # No negative caching -- negative hits on text rows may be due to corrupted slave servers
01071                 if( $wgRevisionCacheExpiry && $text !== false ) {
01072                         $wgMemc->set( $key, $text, $wgRevisionCacheExpiry );
01073                 }
01074 
01075                 wfProfileOut( __METHOD__ );
01076 
01077                 return $text;
01078         }
01079 
01094         public static function newNullRevision( $dbw, $pageId, $summary, $minor ) {
01095                 wfProfileIn( __METHOD__ );
01096 
01097                 $current = $dbw->selectRow(
01098                         array( 'page', 'revision' ),
01099                         array( 'page_latest', 'rev_text_id', 'rev_len', 'rev_sha1' ),
01100                         array(
01101                                 'page_id' => $pageId,
01102                                 'page_latest=rev_id',
01103                                 ),
01104                         __METHOD__ );
01105 
01106                 if( $current ) {
01107                         $revision = new Revision( array(
01108                                 'page'       => $pageId,
01109                                 'comment'    => $summary,
01110                                 'minor_edit' => $minor,
01111                                 'text_id'    => $current->rev_text_id,
01112                                 'parent_id'  => $current->page_latest,
01113                                 'len'        => $current->rev_len,
01114                                 'sha1'       => $current->rev_sha1
01115                                 ) );
01116                 } else {
01117                         $revision = null;
01118                 }
01119 
01120                 wfProfileOut( __METHOD__ );
01121                 return $revision;
01122         }
01123 
01134         public function userCan( $field, User $user = null ) {
01135                 return self::userCanBitfield( $this->mDeleted, $field, $user );
01136         }
01137 
01150         public static function userCanBitfield( $bitfield, $field, User $user = null ) {
01151                 if( $bitfield & $field ) { // aspect is deleted
01152                         if ( $bitfield & self::DELETED_RESTRICTED ) {
01153                                 $permission = 'suppressrevision';
01154                         } elseif ( $field & self::DELETED_TEXT ) {
01155                                 $permission = 'deletedtext';
01156                         } else {
01157                                 $permission = 'deletedhistory';
01158                         }
01159                         wfDebug( "Checking for $permission due to $field match on $bitfield\n" );
01160                         if ( $user === null ) {
01161                                 global $wgUser;
01162                                 $user = $wgUser;
01163                         }
01164                         return $user->isAllowed( $permission );
01165                 } else {
01166                         return true;
01167                 }
01168         }
01169 
01177         static function getTimestampFromId( $title, $id ) {
01178                 $dbr = wfGetDB( DB_SLAVE );
01179                 // Casting fix for DB2
01180                 if ( $id == '' ) {
01181                         $id = 0;
01182                 }
01183                 $conds = array( 'rev_id' => $id );
01184                 $conds['rev_page'] = $title->getArticleId();
01185                 $timestamp = $dbr->selectField( 'revision', 'rev_timestamp', $conds, __METHOD__ );
01186                 if ( $timestamp === false && wfGetLB()->getServerCount() > 1 ) {
01187                         # Not in slave, try master
01188                         $dbw = wfGetDB( DB_MASTER );
01189                         $timestamp = $dbw->selectField( 'revision', 'rev_timestamp', $conds, __METHOD__ );
01190                 }
01191                 return wfTimestamp( TS_MW, $timestamp );
01192         }
01193 
01201         static function countByPageId( $db, $id ) {
01202                 $row = $db->selectRow( 'revision', 'COUNT(*) AS revCount',
01203                         array( 'rev_page' => $id ), __METHOD__ );
01204                 if( $row ) {
01205                         return $row->revCount;
01206                 }
01207                 return 0;
01208         }
01209 
01217         static function countByTitle( $db, $title ) {
01218                 $id = $title->getArticleId();
01219                 if( $id ) {
01220                         return Revision::countByPageId( $db, $id );
01221                 }
01222                 return 0;
01223         }
01224 }