ac1c9ef8d230123cd0621e3b95bbea3311fc3660
[lhc/web/wiklou.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 $fields = self::selectFields();
229 $fields[] = 'page_namespace';
230 $fields[] = 'page_title';
231 $fields[] = 'page_latest';
232 $res = $db->select(
233 array( 'page', 'revision' ),
234 $fields,
235 $conditions,
236 'Revision::fetchRow',
237 array( 'LIMIT' => 1 ) );
238 $ret = $db->resultObject( $res );
239 return $ret;
240 }
241
242 /**
243 * Return the list of revision fields that should be selected to create
244 * a new revision.
245 */
246 static function selectFields() {
247 return array(
248 'rev_id',
249 'rev_page',
250 'rev_text_id',
251 'rev_timestamp',
252 'rev_comment',
253 'rev_user_text,'.
254 'rev_user',
255 'rev_minor_edit',
256 'rev_deleted',
257 'rev_len',
258 'rev_parent_id'
259 );
260 }
261
262 /**
263 * @param object $row
264 * @access private
265 */
266 function Revision( $row ) {
267 if( is_object( $row ) ) {
268 $this->mId = intval( $row->rev_id );
269 $this->mPage = intval( $row->rev_page );
270 $this->mTextId = intval( $row->rev_text_id );
271 $this->mComment = $row->rev_comment;
272 $this->mUserText = $row->rev_user_text;
273 $this->mUser = intval( $row->rev_user );
274 $this->mMinorEdit = intval( $row->rev_minor_edit );
275 $this->mTimestamp = $row->rev_timestamp;
276 $this->mDeleted = intval( $row->rev_deleted );
277 $this->mParentId = intval( $row->rev_parent_id );
278
279 if( !isset( $row->rev_len ) || is_null( $row->rev_len ) )
280 $this->mSize = null;
281 else
282 $this->mSize = intval( $row->rev_len );
283
284 if( isset( $row->page_latest ) ) {
285 $this->mCurrent = ( $row->rev_id == $row->page_latest );
286 $this->mTitle = Title::makeTitle( $row->page_namespace,
287 $row->page_title );
288 } else {
289 $this->mCurrent = false;
290 $this->mTitle = null;
291 }
292
293 // Lazy extraction...
294 $this->mText = null;
295 if( isset( $row->old_text ) ) {
296 $this->mTextRow = $row;
297 } else {
298 // 'text' table row entry will be lazy-loaded
299 $this->mTextRow = null;
300 }
301 } elseif( is_array( $row ) ) {
302 // Build a new revision to be saved...
303 global $wgUser;
304
305 $this->mId = isset( $row['id'] ) ? intval( $row['id'] ) : null;
306 $this->mPage = isset( $row['page'] ) ? intval( $row['page'] ) : null;
307 $this->mTextId = isset( $row['text_id'] ) ? intval( $row['text_id'] ) : null;
308 $this->mUserText = isset( $row['user_text'] ) ? strval( $row['user_text'] ) : $wgUser->getName();
309 $this->mUser = isset( $row['user'] ) ? intval( $row['user'] ) : $wgUser->getId();
310 $this->mMinorEdit = isset( $row['minor_edit'] ) ? intval( $row['minor_edit'] ) : 0;
311 $this->mTimestamp = isset( $row['timestamp'] ) ? strval( $row['timestamp'] ) : wfTimestamp( TS_MW );
312 $this->mDeleted = isset( $row['deleted'] ) ? intval( $row['deleted'] ) : 0;
313 $this->mSize = isset( $row['len'] ) ? intval( $row['len'] ) : null;
314 $this->mParentId = isset( $row['parent_id'] ) ? intval( $row['parent_id'] ) : 0;
315
316 // Enforce spacing trimming on supplied text
317 $this->mComment = isset( $row['comment'] ) ? trim( strval( $row['comment'] ) ) : null;
318 $this->mText = isset( $row['text'] ) ? rtrim( strval( $row['text'] ) ) : null;
319 $this->mTextRow = null;
320
321 $this->mTitle = null; # Load on demand if needed
322 $this->mCurrent = false;
323 # If we still have no len_size, see it we have the text to figure it out
324 if ( !$this->mSize )
325 $this->mSize = is_null($this->mText) ? null : strlen($this->mText);
326 } else {
327 throw new MWException( 'Revision constructor passed invalid row format.' );
328 }
329 }
330
331 /**#@+
332 * @access public
333 */
334
335 /**
336 * Get revision ID
337 * @return int
338 */
339 public function getId() {
340 return $this->mId;
341 }
342
343 /**
344 * Get text row ID
345 * @return int
346 */
347 public function getTextId() {
348 return $this->mTextId;
349 }
350
351 /**
352 * Get parent revision ID (the original previous page revision)
353 * @return int
354 */
355 public function getParentId() {
356 return $this->mParentId;
357 }
358
359 /**
360 * Returns the length of the text in this revision, or null if unknown.
361 * @return int
362 */
363 public function getSize() {
364 return $this->mSize;
365 }
366
367 /**
368 * Returns the title of the page associated with this entry.
369 * @return Title
370 */
371 public function getTitle() {
372 if( isset( $this->mTitle ) ) {
373 return $this->mTitle;
374 }
375 $dbr = wfGetDB( DB_SLAVE );
376 $row = $dbr->selectRow(
377 array( 'page', 'revision' ),
378 array( 'page_namespace', 'page_title' ),
379 array( 'page_id=rev_page',
380 'rev_id' => $this->mId ),
381 'Revision::getTitle' );
382 if( $row ) {
383 $this->mTitle = Title::makeTitle( $row->page_namespace,
384 $row->page_title );
385 }
386 return $this->mTitle;
387 }
388
389 /**
390 * Set the title of the revision
391 * @param Title $title
392 */
393 public function setTitle( $title ) {
394 $this->mTitle = $title;
395 }
396
397 /**
398 * Get the page ID
399 * @return int
400 */
401 public function getPage() {
402 return $this->mPage;
403 }
404
405 /**
406 * Fetch revision's user id if it's available to all users
407 * @return int
408 */
409 public function getUser() {
410 if( $this->isDeleted( self::DELETED_USER ) ) {
411 return 0;
412 } else {
413 return $this->mUser;
414 }
415 }
416
417 /**
418 * Fetch revision's user id without regard for the current user's permissions
419 * @return string
420 */
421 public function getRawUser() {
422 return $this->mUser;
423 }
424
425 /**
426 * Fetch revision's username if it's available to all users
427 * @return string
428 */
429 public function getUserText() {
430 if( $this->isDeleted( self::DELETED_USER ) ) {
431 return "";
432 } else {
433 return $this->mUserText;
434 }
435 }
436
437 /**
438 * Fetch revision's username without regard for view restrictions
439 * @return string
440 */
441 public function getRawUserText() {
442 return $this->mUserText;
443 }
444
445 /**
446 * Fetch revision comment if it's available to all users
447 * @return string
448 */
449 function getComment() {
450 if( $this->isDeleted( self::DELETED_COMMENT ) ) {
451 return "";
452 } else {
453 return $this->mComment;
454 }
455 }
456
457 /**
458 * Fetch revision comment without regard for the current user's permissions
459 * @return string
460 */
461 public function getRawComment() {
462 return $this->mComment;
463 }
464
465 /**
466 * @return bool
467 */
468 public function isMinor() {
469 return (bool)$this->mMinorEdit;
470 }
471
472 /**
473 * int $field one of DELETED_* bitfield constants
474 * @return bool
475 */
476 public function isDeleted( $field ) {
477 return ($this->mDeleted & $field) == $field;
478 }
479
480 /**
481 * Fetch revision text if it's available to all users
482 * @return string
483 */
484 public function getText() {
485 if( $this->isDeleted( self::DELETED_TEXT ) ) {
486 return "";
487 } else {
488 return $this->getRawText();
489 }
490 }
491
492 /**
493 * Fetch revision text without regard for view restrictions
494 * @return string
495 */
496 public function getRawText() {
497 if( is_null( $this->mText ) ) {
498 // Revision text is immutable. Load on demand:
499 $this->mText = $this->loadText();
500 }
501 return $this->mText;
502 }
503
504 /**
505 * Fetch revision text if it's available to THIS user
506 * @return string
507 */
508 public function revText() {
509 if( !$this->userCan( self::DELETED_TEXT ) ) {
510 return "";
511 } else {
512 return $this->getRawText();
513 }
514 }
515
516 /**
517 * @return string
518 */
519 public function getTimestamp() {
520 return wfTimestamp(TS_MW, $this->mTimestamp);
521 }
522
523 /**
524 * @return bool
525 */
526 public function isCurrent() {
527 return $this->mCurrent;
528 }
529
530 /**
531 * Get previous revision for this title
532 * @return Revision
533 */
534 public function getPrevious() {
535 $prev = $this->mTitle->getPreviousRevisionID( $this->mId );
536 if( $prev ) {
537 return Revision::newFromTitle( $this->mTitle, $prev );
538 } else {
539 return null;
540 }
541 }
542
543 /**
544 * @return Revision
545 */
546 public function getNext() {
547 $next = $this->mTitle->getNextRevisionID( $this->mId );
548 if ( $next ) {
549 return Revision::newFromTitle( $this->mTitle, $next );
550 } else {
551 return null;
552 }
553 }
554
555 /**
556 * Get previous revision Id for this page_id
557 * This is used to populate rev_parent_id on save
558 * @param Database $db
559 * @return int
560 */
561 private function getPreviousRevisionId( $db ) {
562 if( is_null($this->mPage) ) {
563 return 0;
564 }
565 # Use page_latest if ID is not given
566 if( !$this->mId ) {
567 $revID = $db->selectField( 'page', 'page_latest',
568 array( 'page_id' => $this->mPage ),
569 __METHOD__ );
570 } else {
571 $revID = $this->mId;
572 }
573 if( !$revID ) {
574 return 0;
575 }
576 $prevId = $db->selectField( 'revision', 'rev_id',
577 array( 'rev_page' => $this->mPage, 'rev_id < ' . $revID ),
578 __METHOD__,
579 array( 'ORDER BY' => 'rev_id DESC' ) );
580 # Always return an integer
581 return intval($prevId);
582 }
583
584 /**
585 * Get revision text associated with an old or archive row
586 * $row is usually an object from wfFetchRow(), both the flags and the text
587 * field must be included
588 *
589 * @param integer $row Id of a row
590 * @param string $prefix table prefix (default 'old_')
591 * @return string $text|false the text requested
592 */
593 public static function getRevisionText( $row, $prefix = 'old_' ) {
594 wfProfileIn( __METHOD__ );
595
596 # Get data
597 $textField = $prefix . 'text';
598 $flagsField = $prefix . 'flags';
599
600 if( isset( $row->$flagsField ) ) {
601 $flags = explode( ',', $row->$flagsField );
602 } else {
603 $flags = array();
604 }
605
606 if( isset( $row->$textField ) ) {
607 $text = $row->$textField;
608 } else {
609 wfProfileOut( __METHOD__ );
610 return false;
611 }
612
613 # Use external methods for external objects, text in table is URL-only then
614 if ( in_array( 'external', $flags ) ) {
615 $url=$text;
616 @list(/* $proto */,$path)=explode('://',$url,2);
617 if ($path=="") {
618 wfProfileOut( __METHOD__ );
619 return false;
620 }
621 $text=ExternalStore::fetchFromURL($url);
622 }
623
624 // If the text was fetched without an error, convert it
625 if ( $text !== false ) {
626 if( in_array( 'gzip', $flags ) ) {
627 # Deal with optional compression of archived pages.
628 # This can be done periodically via maintenance/compressOld.php, and
629 # as pages are saved if $wgCompressRevisions is set.
630 $text = gzinflate( $text );
631 }
632
633 if( in_array( 'object', $flags ) ) {
634 # Generic compressed storage
635 $obj = unserialize( $text );
636 if ( !is_object( $obj ) ) {
637 // Invalid object
638 wfProfileOut( __METHOD__ );
639 return false;
640 }
641 $text = $obj->getText();
642 }
643
644 global $wgLegacyEncoding;
645 if( $wgLegacyEncoding && !in_array( 'utf-8', $flags ) ) {
646 # Old revisions kept around in a legacy encoding?
647 # Upconvert on demand.
648 global $wgInputEncoding, $wgContLang;
649 $text = $wgContLang->iconv( $wgLegacyEncoding, $wgInputEncoding, $text );
650 }
651 }
652 wfProfileOut( __METHOD__ );
653 return $text;
654 }
655
656 /**
657 * If $wgCompressRevisions is enabled, we will compress data.
658 * The input string is modified in place.
659 * Return value is the flags field: contains 'gzip' if the
660 * data is compressed, and 'utf-8' if we're saving in UTF-8
661 * mode.
662 *
663 * @param mixed $text reference to a text
664 * @return string
665 */
666 public static function compressRevisionText( &$text ) {
667 global $wgCompressRevisions;
668 $flags = array();
669
670 # Revisions not marked this way will be converted
671 # on load if $wgLegacyCharset is set in the future.
672 $flags[] = 'utf-8';
673
674 if( $wgCompressRevisions ) {
675 if( function_exists( 'gzdeflate' ) ) {
676 $text = gzdeflate( $text );
677 $flags[] = 'gzip';
678 } else {
679 wfDebug( "Revision::compressRevisionText() -- no zlib support, not compressing\n" );
680 }
681 }
682 return implode( ',', $flags );
683 }
684
685 /**
686 * Insert a new revision into the database, returning the new revision ID
687 * number on success and dies horribly on failure.
688 *
689 * @param Database $dbw
690 * @return int
691 */
692 public function insertOn( &$dbw ) {
693 global $wgDefaultExternalStore;
694
695 wfProfileIn( __METHOD__ );
696
697 $data = $this->mText;
698 $flags = Revision::compressRevisionText( $data );
699
700 # Write to external storage if required
701 if ( $wgDefaultExternalStore ) {
702 if ( is_array( $wgDefaultExternalStore ) ) {
703 // Distribute storage across multiple clusters
704 $store = $wgDefaultExternalStore[mt_rand(0, count( $wgDefaultExternalStore ) - 1)];
705 } else {
706 $store = $wgDefaultExternalStore;
707 }
708 // Store and get the URL
709 $data = ExternalStore::insert( $store, $data );
710 if ( !$data ) {
711 # This should only happen in the case of a configuration error, where the external store is not valid
712 throw new MWException( "Unable to store text to external storage $store" );
713 }
714 if ( $flags ) {
715 $flags .= ',';
716 }
717 $flags .= 'external';
718 }
719
720 # Record the text (or external storage URL) to the text table
721 if( !isset( $this->mTextId ) ) {
722 $old_id = $dbw->nextSequenceValue( 'text_old_id_val' );
723 $dbw->insert( 'text',
724 array(
725 'old_id' => $old_id,
726 'old_text' => $data,
727 'old_flags' => $flags,
728 ), __METHOD__
729 );
730 $this->mTextId = $dbw->insertId();
731 }
732
733 # Record the edit in revisions
734 $rev_id = isset( $this->mId )
735 ? $this->mId
736 : $dbw->nextSequenceValue( 'rev_rev_id_val' );
737 $dbw->insert( 'revision',
738 array(
739 'rev_id' => $rev_id,
740 'rev_page' => $this->mPage,
741 'rev_text_id' => $this->mTextId,
742 'rev_comment' => $this->mComment,
743 'rev_minor_edit' => $this->mMinorEdit ? 1 : 0,
744 'rev_user' => $this->mUser,
745 'rev_user_text' => $this->mUserText,
746 'rev_timestamp' => $dbw->timestamp( $this->mTimestamp ),
747 'rev_deleted' => $this->mDeleted,
748 'rev_len' => $this->mSize,
749 'rev_parent_id' => $this->mParentId ? $this->mParentId : $this->getPreviousRevisionId( $dbw )
750 ), __METHOD__
751 );
752
753 $this->mId = !is_null($rev_id) ? $rev_id : $dbw->insertId();
754 wfProfileOut( __METHOD__ );
755 return $this->mId;
756 }
757
758 /**
759 * Lazy-load the revision's text.
760 * Currently hardcoded to the 'text' table storage engine.
761 *
762 * @return string
763 */
764 private function loadText() {
765 wfProfileIn( __METHOD__ );
766
767 // Caching may be beneficial for massive use of external storage
768 global $wgRevisionCacheExpiry, $wgMemc;
769 $key = wfMemcKey( 'revisiontext', 'textid', $this->getTextId() );
770 if( $wgRevisionCacheExpiry ) {
771 $text = $wgMemc->get( $key );
772 if( is_string( $text ) ) {
773 wfProfileOut( __METHOD__ );
774 return $text;
775 }
776 }
777
778 // If we kept data for lazy extraction, use it now...
779 if ( isset( $this->mTextRow ) ) {
780 $row = $this->mTextRow;
781 $this->mTextRow = null;
782 } else {
783 $row = null;
784 }
785
786 if( !$row ) {
787 // Text data is immutable; check slaves first.
788 $dbr = wfGetDB( DB_SLAVE );
789 $row = $dbr->selectRow( 'text',
790 array( 'old_text', 'old_flags' ),
791 array( 'old_id' => $this->getTextId() ),
792 __METHOD__ );
793 }
794
795 if( !$row ) {
796 // Possible slave lag!
797 $dbw = wfGetDB( DB_MASTER );
798 $row = $dbw->selectRow( 'text',
799 array( 'old_text', 'old_flags' ),
800 array( 'old_id' => $this->getTextId() ),
801 __METHOD__ );
802 }
803
804 $text = self::getRevisionText( $row );
805
806 if( $wgRevisionCacheExpiry ) {
807 $wgMemc->set( $key, $text, $wgRevisionCacheExpiry );
808 }
809
810 wfProfileOut( __METHOD__ );
811
812 return $text;
813 }
814
815 /**
816 * Create a new null-revision for insertion into a page's
817 * history. This will not re-save the text, but simply refer
818 * to the text from the previous version.
819 *
820 * Such revisions can for instance identify page rename
821 * operations and other such meta-modifications.
822 *
823 * @param Database $dbw
824 * @param int $pageId ID number of the page to read from
825 * @param string $summary
826 * @param bool $minor
827 * @return Revision
828 */
829 public static function newNullRevision( &$dbw, $pageId, $summary, $minor ) {
830 wfProfileIn( __METHOD__ );
831
832 $current = $dbw->selectRow(
833 array( 'page', 'revision' ),
834 array( 'page_latest', 'rev_text_id' ),
835 array(
836 'page_id' => $pageId,
837 'page_latest=rev_id',
838 ),
839 __METHOD__ );
840
841 if( $current ) {
842 $revision = new Revision( array(
843 'page' => $pageId,
844 'comment' => $summary,
845 'minor_edit' => $minor,
846 'text_id' => $current->rev_text_id,
847 'parent_id' => $current->page_latest
848 ) );
849 } else {
850 $revision = null;
851 }
852
853 wfProfileOut( __METHOD__ );
854 return $revision;
855 }
856
857 /**
858 * Determine if the current user is allowed to view a particular
859 * field of this revision, if it's marked as deleted.
860 * @param int $field one of self::DELETED_TEXT,
861 * self::DELETED_COMMENT,
862 * self::DELETED_USER
863 * @return bool
864 */
865 public function userCan( $field ) {
866 if( ( $this->mDeleted & $field ) == $field ) {
867 global $wgUser;
868 $permission = ( $this->mDeleted & self::DELETED_RESTRICTED ) == self::DELETED_RESTRICTED
869 ? 'hiderevision'
870 : 'deleterevision';
871 wfDebug( "Checking for $permission due to $field match on $this->mDeleted\n" );
872 return $wgUser->isAllowed( $permission );
873 } else {
874 return true;
875 }
876 }
877
878
879 /**
880 * Get rev_timestamp from rev_id, without loading the rest of the row
881 * @param integer $id
882 */
883 static function getTimestampFromID( $id ) {
884 $dbr = wfGetDB( DB_SLAVE );
885 $timestamp = $dbr->selectField( 'revision', 'rev_timestamp',
886 array( 'rev_id' => $id ), __METHOD__ );
887 if ( $timestamp === false ) {
888 # Not in slave, try master
889 $dbw = wfGetDB( DB_MASTER );
890 $timestamp = $dbw->selectField( 'revision', 'rev_timestamp',
891 array( 'rev_id' => $id ), __METHOD__ );
892 }
893 return $timestamp;
894 }
895
896 /**
897 * Get count of revisions per page...not very efficient
898 * @param Database $db
899 * @param int $id, page id
900 */
901 static function countByPageId( $db, $id ) {
902 $row = $db->selectRow( 'revision', 'COUNT(*) AS revCount',
903 array( 'rev_page' => $id ), __METHOD__ );
904 if( $row ) {
905 return $row->revCount;
906 }
907 return 0;
908 }
909
910 /**
911 * Get count of revisions per page...not very efficient
912 * @param Database $db
913 * @param Title $title
914 */
915 static function countByTitle( $db, $title ) {
916 $id = $title->getArticleId();
917 if( $id ) {
918 return Revision::countByPageId( $db, $id );
919 }
920 return 0;
921 }
922 }
923
924 /**
925 * Aliases for backwards compatibility with 1.6
926 */
927 define( 'MW_REV_DELETED_TEXT', Revision::DELETED_TEXT );
928 define( 'MW_REV_DELETED_COMMENT', Revision::DELETED_COMMENT );
929 define( 'MW_REV_DELETED_USER', Revision::DELETED_USER );
930 define( 'MW_REV_DELETED_RESTRICTED', Revision::DELETED_RESTRICTED );
931
932
933