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 ) {
67 $matchId = IntVal( $id );
69 $matchId = 'page_latest';
71 return Revision
::loadFromConds(
73 array( "rev_id=$matchId",
74 'rev_page' => IntVal( $pageid ),
75 'page_id=rev_page' ) );
79 * Load either the current, or a specified, revision
80 * that's attached to a given page. If not attached
81 * to that page, will return null.
89 function &loadFromTitle( &$db, $title, $id = 0 ) {
91 $matchId = IntVal( $id );
93 $matchId = 'page_latest';
95 return Revision
::loadFromConds(
97 array( "rev_id=$matchId",
99 'page_namespace' => $title->getNamespace(),
100 'page_title' => $title->getDbkey() ) );
104 * Load the revision for the given title with the given timestamp.
105 * WARNING: Timestamps may in some circumstances not be unique,
106 * so this isn't the best key to use.
108 * @param Database $db
109 * @param Title $title
110 * @param string $timestamp
115 function &loadFromTimestamp( &$db, &$title, $timestamp ) {
116 return Revision
::loadFromConds(
118 array( 'rev_timestamp' => $db->timestamp( $timestamp ),
120 'page_namespace' => $title->getNamespace(),
121 'page_title' => $title->getDbkey() ) );
125 * Given a set of conditions, fetch a revision.
127 * @param array $conditions
132 function &newFromConds( $conditions ) {
133 $db =& wfGetDB( DB_SLAVE
);
134 $row = Revision
::loadFromConds( $db, $conditions );
135 if( is_null( $row ) ) {
136 $dbw =& wfGetDB( DB_MASTER
);
137 $row = Revision
::loadFromConds( $dbw, $conditions );
143 * Given a set of conditions, fetch a revision from
144 * the given database connection.
146 * @param Database $db
147 * @param array $conditions
152 function &loadFromConds( &$db, $conditions ) {
153 $res =& Revision
::fetchFromConds( $db, $conditions );
155 $row = $res->fetchObject();
158 return new Revision( $row );
165 * Return a wrapper for a series of database rows to
166 * fetch all of a given page's revisions in turn.
167 * Each row can be fed to the constructor to get objects.
169 * @param Title $title
170 * @return ResultWrapper
174 function &fetchAllRevisions( &$title ) {
175 return Revision
::fetchFromConds(
177 array( 'page_namespace' => $title->getNamespace(),
178 'page_title' => $title->getDbkey(),
179 'page_id=rev_page' ) );
183 * Return a wrapper for a series of database rows to
184 * fetch all of a given page's revisions in turn.
185 * Each row can be fed to the constructor to get objects.
187 * @param Title $title
188 * @return ResultWrapper
192 function &fetchRevision( &$title ) {
193 return Revision
::fetchFromConds(
195 array( 'rev_id=page_latest',
196 'page_namespace' => $title->getNamespace(),
197 'page_title' => $title->getDbkey(),
198 'page_id=rev_page' ) );
202 * Given a set of conditions, return a ResultWrapper
203 * which will return matching database rows with the
204 * fields necessary to build Revision objects.
206 * @param Database $db
207 * @param array $conditions
208 * @return ResultWrapper
212 function &fetchFromConds( &$db, $conditions ) {
214 array( 'page', 'revision' ),
215 array( 'page_namespace',
228 'Revision::fetchRow',
229 array( 'LIMIT' => 1 ) );
230 return $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 $this->mTimestamp
;
385 function isCurrent() {
386 return $this->mCurrent
;
392 function &getPrevious() {
393 $prev = $this->mTitle
->getPreviousRevisionID( $this->mId
);
394 return Revision
::newFromTitle( $this->mTitle
, $prev );
400 function &getNext() {
401 $next = $this->mTitle
->getNextRevisionID( $this->mId
);
402 return Revision
::newFromTitle( $this->mTitle
, $next );
407 * Get revision text associated with an old or archive row
408 * $row is usually an object from wfFetchRow(), both the flags and the text
409 * field must be included
411 * @param integer $row Id of a row
412 * @param string $prefix table prefix (default 'old_')
413 * @return string $text|false the text requested
415 function getRevisionText( $row, $prefix = 'old_' ) {
416 $fname = 'Revision::getRevisionText';
417 wfProfileIn( $fname );
420 $textField = $prefix . 'text';
421 $flagsField = $prefix . 'flags';
423 if( isset( $row->$flagsField ) ) {
424 $flags = explode( ',', $row->$flagsField );
429 if( isset( $row->$textField ) ) {
430 $text = $row->$textField;
432 wfProfileOut( $fname );
436 # Use external methods for external objects, text in table is URL-only then
437 if ( in_array( 'external', $flags ) ) {
439 @list
($proto,$path)=explode('://',$url,2);
441 wfProfileOut( $fname );
444 require_once('ExternalStore.php');
445 $text=ExternalStore
::fetchFromURL($url);
448 if( in_array( 'gzip', $flags ) ) {
449 # Deal with optional compression of archived pages.
450 # This can be done periodically via maintenance/compressOld.php, and
451 # as pages are saved if $wgCompressRevisions is set.
452 $text = gzinflate( $text );
455 if( in_array( 'object', $flags ) ) {
456 # Generic compressed storage
457 $obj = unserialize( $text );
459 # Bugger, corrupted my test database by double-serializing
460 if ( !is_object( $obj ) ) {
461 $obj = unserialize( $obj );
464 $text = $obj->getText();
467 global $wgLegacyEncoding;
468 if( $wgLegacyEncoding && !in_array( 'utf-8', $flags ) ) {
469 # Old revisions kept around in a legacy encoding?
470 # Upconvert on demand.
471 global $wgInputEncoding, $wgContLang;
472 $text = $wgContLang->iconv( $wgLegacyEncoding, $wgInputEncoding, $text );
474 wfProfileOut( $fname );
479 * If $wgCompressRevisions is enabled, we will compress data.
480 * The input string is modified in place.
481 * Return value is the flags field: contains 'gzip' if the
482 * data is compressed, and 'utf-8' if we're saving in UTF-8
486 * @param mixed $text reference to a text
489 function compressRevisionText( &$text ) {
490 global $wgCompressRevisions;
493 # Revisions not marked this way will be converted
494 # on load if $wgLegacyCharset is set in the future.
497 if( $wgCompressRevisions ) {
498 if( function_exists( 'gzdeflate' ) ) {
499 $text = gzdeflate( $text );
502 wfDebug( "Revision::compressRevisionText() -- no zlib support, not compressing\n" );
505 return implode( ',', $flags );
509 * Insert a new revision into the database, returning the new revision ID
510 * number on success and dies horribly on failure.
512 * @param Database $dbw
515 function insertOn( &$dbw ) {
516 $fname = 'Revision::insertOn';
517 wfProfileIn( $fname );
519 $mungedText = $this->mText
;
520 $flags = Revision
::compressRevisionText( $mungedText );
522 # Record the text to the text table
523 if( !isset( $this->mTextId
) ) {
524 $old_id = $dbw->nextSequenceValue( 'text_old_id_val' );
525 $dbw->insert( 'text',
528 'old_text' => $mungedText,
529 'old_flags' => $flags,
532 $this->mTextId
= $dbw->insertId();
535 # Record the edit in revisions
536 $rev_id = isset( $this->mId
)
538 : $dbw->nextSequenceValue( 'rev_rev_id_val' );
539 $dbw->insert( 'revision',
542 'rev_page' => $this->mPage
,
543 'rev_text_id' => $this->mTextId
,
544 'rev_comment' => $this->mComment
,
545 'rev_minor_edit' => $this->mMinorEdit ?
1 : 0,
546 'rev_user' => $this->mUser
,
547 'rev_user_text' => $this->mUserText
,
548 'rev_timestamp' => $dbw->timestamp( $this->mTimestamp
),
549 'rev_deleted' => $this->mDeleted
,
553 $this->mId
= $dbw->insertId();
555 wfProfileOut( $fname );
560 * Lazy-load the revision's text.
561 * Currently hardcoded to the 'text' table storage engine.
566 function loadText() {
567 $fname = 'Revision::loadText';
568 wfProfileIn( $fname );
570 $dbr =& wfGetDB( DB_SLAVE
);
571 $row = $dbr->selectRow( 'text',
572 array( 'old_text', 'old_flags' ),
573 array( 'old_id' => $this->getTextId() ),
577 $dbw =& wfGetDB( DB_MASTER
);
578 $row = $dbw->selectRow( 'text',
579 array( 'old_text', 'old_flags' ),
580 array( 'old_id' => $this->getTextId() ),
584 $text = Revision
::getRevisionText( $row );
585 wfProfileOut( $fname );
591 * Create a new null-revision for insertion into a page's
592 * history. This will not re-save the text, but simply refer
593 * to the text from the previous version.
595 * Such revisions can for instance identify page rename
596 * operations and other such meta-modifications.
598 * @param Database $dbw
599 * @param int $pageId ID number of the page to read from
600 * @param string $summary
604 function &newNullRevision( &$dbw, $pageId, $summary, $minor ) {
605 $fname = 'Revision::newNullRevision';
606 wfProfileIn( $fname );
608 $current = $dbw->selectRow(
609 array( 'page', 'revision' ),
610 array( 'page_latest', 'rev_text_id' ),
612 'page_id' => $pageId,
613 'page_latest=rev_id',
618 $revision = new Revision( array(
620 'comment' => $summary,
621 'minor_edit' => $minor,
622 'text_id' => $current->rev_text_id
,
628 wfProfileOut( $fname );