8 require_once( 'Database.php' );
9 require_once( 'Article.php' );
17 * Load a page revision from a given revision ID number.
18 * Returns null if no such revision can be found.
24 function newFromId( $id ) {
25 return Revision
::newFromConds(
26 array( 'page_id=rev_page',
27 'rev_id' => intval( $id ) ) );
31 * Load either the current, or a specified, revision
32 * that's attached to a given title. If not attached
33 * to that title, will return null.
41 function newFromTitle( &$title, $id = 0 ) {
43 $matchId = intval( $id );
45 $matchId = 'page_latest';
47 return Revision
::newFromConds(
48 array( "rev_id=$matchId",
50 'page_namespace' => $title->getNamespace(),
51 'page_title' => $title->getDbkey() ) );
55 * Load either the current, or a specified, revision
56 * that's attached to a given page. If not attached
57 * to that page, will return null.
65 function loadFromPageId( &$db, $pageid, $id = 0 ) {
66 $conds=array('page_id=rev_page','rev_page'=>intval( $pageid ), 'page_id'=>intval( $pageid ));
68 $conds['rev_id']=intval($id);
70 $conds[]='rev_id=page_latest';
72 return Revision
::loadFromConds( $db, $conds );
76 * Load either the current, or a specified, revision
77 * that's attached to a given page. If not attached
78 * to that page, will return null.
86 function loadFromTitle( &$db, $title, $id = 0 ) {
88 $matchId = intval( $id );
90 $matchId = 'page_latest';
92 return Revision
::loadFromConds(
94 array( "rev_id=$matchId",
96 'page_namespace' => $title->getNamespace(),
97 'page_title' => $title->getDbkey() ) );
101 * Load the revision for the given title with the given timestamp.
102 * WARNING: Timestamps may in some circumstances not be unique,
103 * so this isn't the best key to use.
105 * @param Database $db
106 * @param Title $title
107 * @param string $timestamp
112 function loadFromTimestamp( &$db, &$title, $timestamp ) {
113 return Revision
::loadFromConds(
115 array( 'rev_timestamp' => $db->timestamp( $timestamp ),
117 'page_namespace' => $title->getNamespace(),
118 'page_title' => $title->getDbkey() ) );
122 * Given a set of conditions, fetch a revision.
124 * @param array $conditions
129 function newFromConds( $conditions ) {
130 $db =& wfGetDB( DB_SLAVE
);
131 $row = Revision
::loadFromConds( $db, $conditions );
132 if( is_null( $row ) ) {
133 $dbw =& wfGetDB( DB_MASTER
);
134 $row = Revision
::loadFromConds( $dbw, $conditions );
140 * Given a set of conditions, fetch a revision from
141 * the given database connection.
143 * @param Database $db
144 * @param array $conditions
149 function loadFromConds( &$db, $conditions ) {
150 $res = Revision
::fetchFromConds( $db, $conditions );
152 $row = $res->fetchObject();
155 $ret = new Revision( $row );
164 * Return a wrapper for a series of database rows to
165 * fetch all of a given page's revisions in turn.
166 * Each row can be fed to the constructor to get objects.
168 * @param Title $title
169 * @return ResultWrapper
173 function fetchAllRevisions( &$title ) {
174 return Revision
::fetchFromConds(
176 array( 'page_namespace' => $title->getNamespace(),
177 'page_title' => $title->getDbkey(),
178 'page_id=rev_page' ) );
182 * Return a wrapper for a series of database rows to
183 * fetch all of a given page's revisions in turn.
184 * Each row can be fed to the constructor to get objects.
186 * @param Title $title
187 * @return ResultWrapper
191 function fetchRevision( &$title ) {
192 return Revision
::fetchFromConds(
194 array( 'rev_id=page_latest',
195 'page_namespace' => $title->getNamespace(),
196 'page_title' => $title->getDbkey(),
197 'page_id=rev_page' ) );
201 * Given a set of conditions, return a ResultWrapper
202 * which will return matching database rows with the
203 * fields necessary to build Revision objects.
205 * @param Database $db
206 * @param array $conditions
207 * @return ResultWrapper
211 function fetchFromConds( &$db, $conditions ) {
213 array( 'page', 'revision' ),
214 array( 'page_namespace',
227 'Revision::fetchRow',
228 array( 'LIMIT' => 1 ) );
229 $ret = $db->resultObject( $res );
237 function Revision( $row ) {
238 if( is_object( $row ) ) {
239 $this->mId
= intval( $row->rev_id
);
240 $this->mPage
= intval( $row->rev_page
);
241 $this->mTextId
= intval( $row->rev_text_id
);
242 $this->mComment
= $row->rev_comment
;
243 $this->mUserText
= $row->rev_user_text
;
244 $this->mUser
= intval( $row->rev_user
);
245 $this->mMinorEdit
= intval( $row->rev_minor_edit
);
246 $this->mTimestamp
= $row->rev_timestamp
;
247 $this->mDeleted
= intval( $row->rev_deleted
);
249 $this->mCurrent
= ( $row->rev_id
== $row->page_latest
);
250 $this->mTitle
= Title
::makeTitle( $row->page_namespace
,
253 if( isset( $row->old_text
) ) {
254 $this->mText
= $this->getRevisionText( $row );
258 } elseif( is_array( $row ) ) {
259 // Build a new revision to be saved...
262 $this->mId
= isset( $row['id'] ) ?
intval( $row['id'] ) : null;
263 $this->mPage
= isset( $row['page'] ) ?
intval( $row['page'] ) : null;
264 $this->mTextId
= isset( $row['text_id'] ) ?
intval( $row['text_id'] ) : null;
265 $this->mUserText
= isset( $row['user_text'] ) ?
strval( $row['user_text'] ) : $wgUser->getName();
266 $this->mUser
= isset( $row['user'] ) ?
intval( $row['user'] ) : $wgUser->getId();
267 $this->mMinorEdit
= isset( $row['minor_edit'] ) ?
intval( $row['minor_edit'] ) : 0;
268 $this->mTimestamp
= isset( $row['timestamp'] ) ?
strval( $row['timestamp'] ) : wfTimestamp( TS_MW
);
269 $this->mDeleted
= isset( $row['deleted'] ) ?
intval( $row['deleted'] ) : 0;
271 // Enforce spacing trimming on supplied text
272 $this->mComment
= isset( $row['comment'] ) ?
trim( strval( $row['comment'] ) ) : null;
273 $this->mText
= isset( $row['text'] ) ?
rtrim( strval( $row['text'] ) ) : null;
275 $this->mTitle
= null; # Load on demand if needed
276 $this->mCurrent
= false;
278 wfDebugDieBacktrace( 'Revision constructor passed invalid row format.' );
296 function getTextId() {
297 return $this->mTextId
;
301 * Returns the title of the page associated with this entry.
304 function getTitle() {
305 if( isset( $this->mTitle
) ) {
306 return $this->mTitle
;
308 $dbr =& wfGetDB( DB_SLAVE
);
309 $row = $dbr->selectRow(
310 array( 'page', 'revision' ),
311 array( 'page_namespace', 'page_title' ),
312 array( 'page_id=rev_page',
313 'rev_id' => $this->mId
),
314 'Revision::getTItle' );
316 $this->mTitle
= Title
::makeTitle( $row->page_namespace
,
319 return $this->mTitle
;
339 function getUserText() {
340 return $this->mUserText
;
346 function getComment() {
347 return $this->mComment
;
354 return (bool)$this->mMinorEdit
;
360 function isDeleted() {
361 return (bool)$this->mDeleted
;
368 if( is_null( $this->mText
) ) {
369 // Revision text is immutable. Load on demand:
370 $this->mText
= $this->loadText();
378 function getTimestamp() {
379 return wfTimestamp(TS_MW
, $this->mTimestamp
);
385 function isCurrent() {
386 return $this->mCurrent
;
392 function getPrevious() {
393 $prev = $this->mTitle
->getPreviousRevisionID( $this->mId
);
395 return Revision
::newFromTitle( $this->mTitle
, $prev );
405 $next = $this->mTitle
->getNextRevisionID( $this->mId
);
407 return Revision
::newFromTitle( $this->mTitle
, $next );
415 * Get revision text associated with an old or archive row
416 * $row is usually an object from wfFetchRow(), both the flags and the text
417 * field must be included
419 * @param integer $row Id of a row
420 * @param string $prefix table prefix (default 'old_')
421 * @return string $text|false the text requested
423 function getRevisionText( $row, $prefix = 'old_' ) {
424 $fname = 'Revision::getRevisionText';
425 wfProfileIn( $fname );
428 $textField = $prefix . 'text';
429 $flagsField = $prefix . 'flags';
431 if( isset( $row->$flagsField ) ) {
432 $flags = explode( ',', $row->$flagsField );
437 if( isset( $row->$textField ) ) {
438 $text = $row->$textField;
440 wfProfileOut( $fname );
444 # Use external methods for external objects, text in table is URL-only then
445 if ( in_array( 'external', $flags ) ) {
447 @list
($proto,$path)=explode('://',$url,2);
449 wfProfileOut( $fname );
452 require_once('ExternalStore.php');
453 $text=ExternalStore
::fetchFromURL($url);
456 // If the text was fetched without an error, convert it
457 if ( $text !== false ) {
458 if( in_array( 'gzip', $flags ) ) {
459 # Deal with optional compression of archived pages.
460 # This can be done periodically via maintenance/compressOld.php, and
461 # as pages are saved if $wgCompressRevisions is set.
462 $text = gzinflate( $text );
465 if( in_array( 'object', $flags ) ) {
466 # Generic compressed storage
467 $obj = unserialize( $text );
468 $text = $obj->getText();
471 global $wgLegacyEncoding;
472 if( $wgLegacyEncoding && !in_array( 'utf-8', $flags ) ) {
473 # Old revisions kept around in a legacy encoding?
474 # Upconvert on demand.
475 global $wgInputEncoding, $wgContLang;
476 $text = $wgContLang->iconv( $wgLegacyEncoding, $wgInputEncoding . '//IGNORE', $text );
479 wfProfileOut( $fname );
484 * If $wgCompressRevisions is enabled, we will compress data.
485 * The input string is modified in place.
486 * Return value is the flags field: contains 'gzip' if the
487 * data is compressed, and 'utf-8' if we're saving in UTF-8
491 * @param mixed $text reference to a text
494 function compressRevisionText( &$text ) {
495 global $wgCompressRevisions;
498 # Revisions not marked this way will be converted
499 # on load if $wgLegacyCharset is set in the future.
502 if( $wgCompressRevisions ) {
503 if( function_exists( 'gzdeflate' ) ) {
504 $text = gzdeflate( $text );
507 wfDebug( "Revision::compressRevisionText() -- no zlib support, not compressing\n" );
510 return implode( ',', $flags );
514 * Insert a new revision into the database, returning the new revision ID
515 * number on success and dies horribly on failure.
517 * @param Database $dbw
520 function insertOn( &$dbw ) {
521 global $wgDefaultExternalStore;
523 $fname = 'Revision::insertOn';
524 wfProfileIn( $fname );
526 $data = $this->mText
;
527 $flags = Revision
::compressRevisionText( $data );
529 # Write to external storage if required
530 if ( $wgDefaultExternalStore ) {
531 require_once('ExternalStore.php');
532 // Store and get the URL
533 $data = ExternalStore
::insert( $wgDefaultExternalStore, $data );
535 # This should only happen in the case of a configuration error, where the external store is not valid
536 wfDebugDieBacktrace( "Unable to store text to external storage $wgDefaultExternalStore" );
541 $flags .= 'external';
544 # Record the text (or external storage URL) to the text table
545 if( !isset( $this->mTextId
) ) {
546 $old_id = $dbw->nextSequenceValue( 'text_old_id_val' );
547 $dbw->insert( 'text',
551 'old_flags' => $flags,
554 $this->mTextId
= $dbw->insertId();
557 # Record the edit in revisions
558 $rev_id = isset( $this->mId
)
560 : $dbw->nextSequenceValue( 'rev_rev_id_val' );
561 $dbw->insert( 'revision',
564 'rev_page' => $this->mPage
,
565 'rev_text_id' => $this->mTextId
,
566 'rev_comment' => $this->mComment
,
567 'rev_minor_edit' => $this->mMinorEdit ?
1 : 0,
568 'rev_user' => $this->mUser
,
569 'rev_user_text' => $this->mUserText
,
570 'rev_timestamp' => $dbw->timestamp( $this->mTimestamp
),
571 'rev_deleted' => $this->mDeleted
,
575 $this->mId
= !is_null($rev_id) ?
$rev_id : $dbw->insertId();
577 wfProfileOut( $fname );
582 * Lazy-load the revision's text.
583 * Currently hardcoded to the 'text' table storage engine.
588 function loadText() {
589 $fname = 'Revision::loadText';
590 wfProfileIn( $fname );
592 $dbr =& wfGetDB( DB_SLAVE
);
593 $row = $dbr->selectRow( 'text',
594 array( 'old_text', 'old_flags' ),
595 array( 'old_id' => $this->getTextId() ),
599 $dbw =& wfGetDB( DB_MASTER
);
600 $row = $dbw->selectRow( 'text',
601 array( 'old_text', 'old_flags' ),
602 array( 'old_id' => $this->getTextId() ),
606 $text = Revision
::getRevisionText( $row );
607 wfProfileOut( $fname );
613 * Create a new null-revision for insertion into a page's
614 * history. This will not re-save the text, but simply refer
615 * to the text from the previous version.
617 * Such revisions can for instance identify page rename
618 * operations and other such meta-modifications.
620 * @param Database $dbw
621 * @param int $pageId ID number of the page to read from
622 * @param string $summary
626 function newNullRevision( &$dbw, $pageId, $summary, $minor ) {
627 $fname = 'Revision::newNullRevision';
628 wfProfileIn( $fname );
630 $current = $dbw->selectRow(
631 array( 'page', 'revision' ),
632 array( 'page_latest', 'rev_text_id' ),
634 'page_id' => $pageId,
635 'page_latest=rev_id',
640 $revision = new Revision( array(
642 'comment' => $summary,
643 'minor_edit' => $minor,
644 'text_id' => $current->rev_text_id
,
650 wfProfileOut( $fname );