MediaWiki
REL1_19
|
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 }