MediaWiki REL1_27
Revision.php
Go to the documentation of this file.
1<?php
23
27class Revision implements IDBAccessObject {
28 protected $mId;
29
33 protected $mPage;
34 protected $mUserText;
35 protected $mOrigUserText;
36 protected $mUser;
37 protected $mMinorEdit;
38 protected $mTimestamp;
39 protected $mDeleted;
40 protected $mSize;
41 protected $mSha1;
42 protected $mParentId;
43 protected $mComment;
44 protected $mText;
45 protected $mTextId;
46
50 protected $mTextRow;
51
55 protected $mTitle;
56 protected $mCurrent;
57 protected $mContentModel;
58 protected $mContentFormat;
59
63 protected $mContent;
64
69
73 protected $mQueryFlags = 0;
74
75 // Revision deletion constants
76 const DELETED_TEXT = 1;
77 const DELETED_COMMENT = 2;
78 const DELETED_USER = 4;
80 const SUPPRESSED_USER = 12; // convenience
81
82 // Audience options for accessors
83 const FOR_PUBLIC = 1;
84 const FOR_THIS_USER = 2;
85 const RAW = 3;
86
99 public static function newFromId( $id, $flags = 0 ) {
100 return self::newFromConds( [ 'rev_id' => intval( $id ) ], $flags );
101 }
102
117 public static function newFromTitle( LinkTarget $linkTarget, $id = 0, $flags = 0 ) {
118 $conds = [
119 'page_namespace' => $linkTarget->getNamespace(),
120 'page_title' => $linkTarget->getDBkey()
121 ];
122 if ( $id ) {
123 // Use the specified ID
124 $conds['rev_id'] = $id;
125 return self::newFromConds( $conds, $flags );
126 } else {
127 // Use a join to get the latest revision
128 $conds[] = 'rev_id=page_latest';
129 $db = wfGetDB( ( $flags & self::READ_LATEST ) ? DB_MASTER : DB_SLAVE );
130 return self::loadFromConds( $db, $conds, $flags );
131 }
132 }
133
148 public static function newFromPageId( $pageId, $revId = 0, $flags = 0 ) {
149 $conds = [ 'page_id' => $pageId ];
150 if ( $revId ) {
151 $conds['rev_id'] = $revId;
152 return self::newFromConds( $conds, $flags );
153 } else {
154 // Use a join to get the latest revision
155 $conds[] = 'rev_id = page_latest';
156 $db = wfGetDB( ( $flags & self::READ_LATEST ) ? DB_MASTER : DB_SLAVE );
157 return self::loadFromConds( $db, $conds, $flags );
158 }
159 }
160
172 public static function newFromArchiveRow( $row, $overrides = [] ) {
174
175 $attribs = $overrides + [
176 'page' => isset( $row->ar_page_id ) ? $row->ar_page_id : null,
177 'id' => isset( $row->ar_rev_id ) ? $row->ar_rev_id : null,
178 'comment' => $row->ar_comment,
179 'user' => $row->ar_user,
180 'user_text' => $row->ar_user_text,
181 'timestamp' => $row->ar_timestamp,
182 'minor_edit' => $row->ar_minor_edit,
183 'text_id' => isset( $row->ar_text_id ) ? $row->ar_text_id : null,
184 'deleted' => $row->ar_deleted,
185 'len' => $row->ar_len,
186 'sha1' => isset( $row->ar_sha1 ) ? $row->ar_sha1 : null,
187 'content_model' => isset( $row->ar_content_model ) ? $row->ar_content_model : null,
188 'content_format' => isset( $row->ar_content_format ) ? $row->ar_content_format : null,
189 ];
190
191 if ( !$wgContentHandlerUseDB ) {
192 unset( $attribs['content_model'] );
193 unset( $attribs['content_format'] );
194 }
195
196 if ( !isset( $attribs['title'] )
197 && isset( $row->ar_namespace )
198 && isset( $row->ar_title )
199 ) {
200 $attribs['title'] = Title::makeTitle( $row->ar_namespace, $row->ar_title );
201 }
202
203 if ( isset( $row->ar_text ) && !$row->ar_text_id ) {
204 // Pre-1.5 ar_text row
205 $attribs['text'] = self::getRevisionText( $row, 'ar_' );
206 if ( $attribs['text'] === false ) {
207 throw new MWException( 'Unable to load text from archive row (possibly bug 22624)' );
208 }
209 }
210 return new self( $attribs );
211 }
212
219 public static function newFromRow( $row ) {
220 return new self( $row );
221 }
222
231 public static function loadFromId( $db, $id ) {
232 return self::loadFromConds( $db, [ 'rev_id' => intval( $id ) ] );
233 }
234
245 public static function loadFromPageId( $db, $pageid, $id = 0 ) {
246 $conds = [ 'rev_page' => intval( $pageid ), 'page_id' => intval( $pageid ) ];
247 if ( $id ) {
248 $conds['rev_id'] = intval( $id );
249 } else {
250 $conds[] = 'rev_id=page_latest';
251 }
252 return self::loadFromConds( $db, $conds );
253 }
254
265 public static function loadFromTitle( $db, $title, $id = 0 ) {
266 if ( $id ) {
267 $matchId = intval( $id );
268 } else {
269 $matchId = 'page_latest';
270 }
271 return self::loadFromConds( $db,
272 [
273 "rev_id=$matchId",
274 'page_namespace' => $title->getNamespace(),
275 'page_title' => $title->getDBkey()
276 ]
277 );
278 }
279
290 public static function loadFromTimestamp( $db, $title, $timestamp ) {
291 return self::loadFromConds( $db,
292 [
293 'rev_timestamp' => $db->timestamp( $timestamp ),
294 'page_namespace' => $title->getNamespace(),
295 'page_title' => $title->getDBkey()
296 ]
297 );
298 }
299
310 private static function newFromConds( $conditions, $flags = 0 ) {
311 $db = wfGetDB( ( $flags & self::READ_LATEST ) ? DB_MASTER : DB_SLAVE );
312
313 $rev = self::loadFromConds( $db, $conditions, $flags );
314 // Make sure new pending/committed revision are visibile later on
315 // within web requests to certain avoid bugs like T93866 and T94407.
316 if ( !$rev
317 && !( $flags & self::READ_LATEST )
318 && wfGetLB()->getServerCount() > 1
319 && wfGetLB()->hasOrMadeRecentMasterChanges()
320 ) {
322 $db = wfGetDB( DB_MASTER );
323 $rev = self::loadFromConds( $db, $conditions, $flags );
324 }
325
326 if ( $rev ) {
327 $rev->mQueryFlags = $flags;
328 }
329
330 return $rev;
331 }
332
342 private static function loadFromConds( $db, $conditions, $flags = 0 ) {
343 $res = self::fetchFromConds( $db, $conditions, $flags );
344 if ( $res ) {
345 $row = $res->fetchObject();
346 if ( $row ) {
347 $ret = new Revision( $row );
348 return $ret;
349 }
350 }
351 $ret = null;
352 return $ret;
353 }
354
363 public static function fetchRevision( $title ) {
365 wfGetDB( DB_SLAVE ),
366 [
367 'rev_id=page_latest',
368 'page_namespace' => $title->getNamespace(),
369 'page_title' => $title->getDBkey()
370 ]
371 );
372 }
373
384 private static function fetchFromConds( $db, $conditions, $flags = 0 ) {
385 $fields = array_merge(
386 self::selectFields(),
387 self::selectPageFields(),
388 self::selectUserFields()
389 );
390 $options = [ 'LIMIT' => 1 ];
391 if ( ( $flags & self::READ_LOCKING ) == self::READ_LOCKING ) {
392 $options[] = 'FOR UPDATE';
393 }
394 return $db->select(
395 [ 'revision', 'page', 'user' ],
396 $fields,
397 $conditions,
398 __METHOD__,
399 $options,
400 [ 'page' => self::pageJoinCond(), 'user' => self::userJoinCond() ]
401 );
402 }
403
410 public static function userJoinCond() {
411 return [ 'LEFT JOIN', [ 'rev_user != 0', 'user_id = rev_user' ] ];
412 }
413
420 public static function pageJoinCond() {
421 return [ 'INNER JOIN', [ 'page_id = rev_page' ] ];
422 }
423
429 public static function selectFields() {
431
432 $fields = [
433 'rev_id',
434 'rev_page',
435 'rev_text_id',
436 'rev_timestamp',
437 'rev_comment',
438 'rev_user_text',
439 'rev_user',
440 'rev_minor_edit',
441 'rev_deleted',
442 'rev_len',
443 'rev_parent_id',
444 'rev_sha1',
445 ];
446
448 $fields[] = 'rev_content_format';
449 $fields[] = 'rev_content_model';
450 }
451
452 return $fields;
453 }
454
460 public static function selectArchiveFields() {
462 $fields = [
463 'ar_id',
464 'ar_page_id',
465 'ar_rev_id',
466 'ar_text',
467 'ar_text_id',
468 'ar_timestamp',
469 'ar_comment',
470 'ar_user_text',
471 'ar_user',
472 'ar_minor_edit',
473 'ar_deleted',
474 'ar_len',
475 'ar_parent_id',
476 'ar_sha1',
477 ];
478
480 $fields[] = 'ar_content_format';
481 $fields[] = 'ar_content_model';
482 }
483 return $fields;
484 }
485
491 public static function selectTextFields() {
492 return [
493 'old_text',
494 'old_flags'
495 ];
496 }
497
502 public static function selectPageFields() {
503 return [
504 'page_namespace',
505 'page_title',
506 'page_id',
507 'page_latest',
508 'page_is_redirect',
509 'page_len',
510 ];
511 }
512
517 public static function selectUserFields() {
518 return [ 'user_name' ];
519 }
520
527 public static function getParentLengths( $db, array $revIds ) {
528 $revLens = [];
529 if ( !$revIds ) {
530 return $revLens; // empty
531 }
532 $res = $db->select( 'revision',
533 [ 'rev_id', 'rev_len' ],
534 [ 'rev_id' => $revIds ],
535 __METHOD__ );
536 foreach ( $res as $row ) {
537 $revLens[$row->rev_id] = $row->rev_len;
538 }
539 return $revLens;
540 }
541
549 function __construct( $row ) {
550 if ( is_object( $row ) ) {
551 $this->mId = intval( $row->rev_id );
552 $this->mPage = intval( $row->rev_page );
553 $this->mTextId = intval( $row->rev_text_id );
554 $this->mComment = $row->rev_comment;
555 $this->mUser = intval( $row->rev_user );
556 $this->mMinorEdit = intval( $row->rev_minor_edit );
557 $this->mTimestamp = $row->rev_timestamp;
558 $this->mDeleted = intval( $row->rev_deleted );
559
560 if ( !isset( $row->rev_parent_id ) ) {
561 $this->mParentId = null;
562 } else {
563 $this->mParentId = intval( $row->rev_parent_id );
564 }
565
566 if ( !isset( $row->rev_len ) ) {
567 $this->mSize = null;
568 } else {
569 $this->mSize = intval( $row->rev_len );
570 }
571
572 if ( !isset( $row->rev_sha1 ) ) {
573 $this->mSha1 = null;
574 } else {
575 $this->mSha1 = $row->rev_sha1;
576 }
577
578 if ( isset( $row->page_latest ) ) {
579 $this->mCurrent = ( $row->rev_id == $row->page_latest );
580 $this->mTitle = Title::newFromRow( $row );
581 } else {
582 $this->mCurrent = false;
583 $this->mTitle = null;
584 }
585
586 if ( !isset( $row->rev_content_model ) ) {
587 $this->mContentModel = null; # determine on demand if needed
588 } else {
589 $this->mContentModel = strval( $row->rev_content_model );
590 }
591
592 if ( !isset( $row->rev_content_format ) ) {
593 $this->mContentFormat = null; # determine on demand if needed
594 } else {
595 $this->mContentFormat = strval( $row->rev_content_format );
596 }
597
598 // Lazy extraction...
599 $this->mText = null;
600 if ( isset( $row->old_text ) ) {
601 $this->mTextRow = $row;
602 } else {
603 // 'text' table row entry will be lazy-loaded
604 $this->mTextRow = null;
605 }
606
607 // Use user_name for users and rev_user_text for IPs...
608 $this->mUserText = null; // lazy load if left null
609 if ( $this->mUser == 0 ) {
610 $this->mUserText = $row->rev_user_text; // IP user
611 } elseif ( isset( $row->user_name ) ) {
612 $this->mUserText = $row->user_name; // logged-in user
613 }
614 $this->mOrigUserText = $row->rev_user_text;
615 } elseif ( is_array( $row ) ) {
616 // Build a new revision to be saved...
617 global $wgUser; // ugh
618
619 # if we have a content object, use it to set the model and type
620 if ( !empty( $row['content'] ) ) {
621 // @todo when is that set? test with external store setup! check out insertOn() [dk]
622 if ( !empty( $row['text_id'] ) ) {
623 throw new MWException( "Text already stored in external store (id {$row['text_id']}), " .
624 "can't serialize content object" );
625 }
626
627 $row['content_model'] = $row['content']->getModel();
628 # note: mContentFormat is initializes later accordingly
629 # note: content is serialized later in this method!
630 # also set text to null?
631 }
632
633 $this->mId = isset( $row['id'] ) ? intval( $row['id'] ) : null;
634 $this->mPage = isset( $row['page'] ) ? intval( $row['page'] ) : null;
635 $this->mTextId = isset( $row['text_id'] ) ? intval( $row['text_id'] ) : null;
636 $this->mUserText = isset( $row['user_text'] )
637 ? strval( $row['user_text'] ) : $wgUser->getName();
638 $this->mUser = isset( $row['user'] ) ? intval( $row['user'] ) : $wgUser->getId();
639 $this->mMinorEdit = isset( $row['minor_edit'] ) ? intval( $row['minor_edit'] ) : 0;
640 $this->mTimestamp = isset( $row['timestamp'] )
641 ? strval( $row['timestamp'] ) : wfTimestampNow();
642 $this->mDeleted = isset( $row['deleted'] ) ? intval( $row['deleted'] ) : 0;
643 $this->mSize = isset( $row['len'] ) ? intval( $row['len'] ) : null;
644 $this->mParentId = isset( $row['parent_id'] ) ? intval( $row['parent_id'] ) : null;
645 $this->mSha1 = isset( $row['sha1'] ) ? strval( $row['sha1'] ) : null;
646
647 $this->mContentModel = isset( $row['content_model'] )
648 ? strval( $row['content_model'] ) : null;
649 $this->mContentFormat = isset( $row['content_format'] )
650 ? strval( $row['content_format'] ) : null;
651
652 // Enforce spacing trimming on supplied text
653 $this->mComment = isset( $row['comment'] ) ? trim( strval( $row['comment'] ) ) : null;
654 $this->mText = isset( $row['text'] ) ? rtrim( strval( $row['text'] ) ) : null;
655 $this->mTextRow = null;
656
657 $this->mTitle = isset( $row['title'] ) ? $row['title'] : null;
658
659 // if we have a Content object, override mText and mContentModel
660 if ( !empty( $row['content'] ) ) {
661 if ( !( $row['content'] instanceof Content ) ) {
662 throw new MWException( '`content` field must contain a Content object.' );
663 }
664
665 $handler = $this->getContentHandler();
666 $this->mContent = $row['content'];
667
668 $this->mContentModel = $this->mContent->getModel();
669 $this->mContentHandler = null;
670
671 $this->mText = $handler->serializeContent( $row['content'], $this->getContentFormat() );
672 } elseif ( $this->mText !== null ) {
673 $handler = $this->getContentHandler();
674 $this->mContent = $handler->unserializeContent( $this->mText );
675 }
676
677 // If we have a Title object, make sure it is consistent with mPage.
678 if ( $this->mTitle && $this->mTitle->exists() ) {
679 if ( $this->mPage === null ) {
680 // if the page ID wasn't known, set it now
681 $this->mPage = $this->mTitle->getArticleID();
682 } elseif ( $this->mTitle->getArticleID() !== $this->mPage ) {
683 // Got different page IDs. This may be legit (e.g. during undeletion),
684 // but it seems worth mentioning it in the log.
685 wfDebug( "Page ID " . $this->mPage . " mismatches the ID " .
686 $this->mTitle->getArticleID() . " provided by the Title object." );
687 }
688 }
689
690 $this->mCurrent = false;
691
692 // If we still have no length, see it we have the text to figure it out
693 if ( !$this->mSize && $this->mContent !== null ) {
694 $this->mSize = $this->mContent->getSize();
695 }
696
697 // Same for sha1
698 if ( $this->mSha1 === null ) {
699 $this->mSha1 = $this->mText === null ? null : self::base36Sha1( $this->mText );
700 }
701
702 // force lazy init
703 $this->getContentModel();
704 $this->getContentFormat();
705 } else {
706 throw new MWException( 'Revision constructor passed invalid row format.' );
707 }
708 $this->mUnpatrolled = null;
709 }
710
716 public function getId() {
717 return $this->mId;
718 }
719
726 public function setId( $id ) {
727 $this->mId = $id;
728 }
729
735 public function getTextId() {
736 return $this->mTextId;
737 }
738
744 public function getParentId() {
745 return $this->mParentId;
746 }
747
753 public function getSize() {
754 return $this->mSize;
755 }
756
762 public function getSha1() {
763 return $this->mSha1;
764 }
765
773 public function getTitle() {
774 if ( $this->mTitle !== null ) {
775 return $this->mTitle;
776 }
777 // rev_id is defined as NOT NULL, but this revision may not yet have been inserted.
778 if ( $this->mId !== null ) {
779 $dbr = wfGetDB( DB_SLAVE );
780 $row = $dbr->selectRow(
781 [ 'page', 'revision' ],
782 self::selectPageFields(),
783 [ 'page_id=rev_page',
784 'rev_id' => $this->mId ],
785 __METHOD__ );
786 if ( $row ) {
787 $this->mTitle = Title::newFromRow( $row );
788 }
789 }
790
791 if ( !$this->mTitle && $this->mPage !== null && $this->mPage > 0 ) {
792 $this->mTitle = Title::newFromID( $this->mPage );
793 }
794
795 return $this->mTitle;
796 }
797
803 public function setTitle( $title ) {
804 $this->mTitle = $title;
805 }
806
812 public function getPage() {
813 return $this->mPage;
814 }
815
829 public function getUser( $audience = self::FOR_PUBLIC, User $user = null ) {
830 if ( $audience == self::FOR_PUBLIC && $this->isDeleted( self::DELETED_USER ) ) {
831 return 0;
832 } elseif ( $audience == self::FOR_THIS_USER && !$this->userCan( self::DELETED_USER, $user ) ) {
833 return 0;
834 } else {
835 return $this->mUser;
836 }
837 }
838
845 public function getRawUser() {
846 wfDeprecated( __METHOD__, '1.25' );
847 return $this->getUser( self::RAW );
848 }
849
863 public function getUserText( $audience = self::FOR_PUBLIC, User $user = null ) {
864 if ( $audience == self::FOR_PUBLIC && $this->isDeleted( self::DELETED_USER ) ) {
865 return '';
866 } elseif ( $audience == self::FOR_THIS_USER && !$this->userCan( self::DELETED_USER, $user ) ) {
867 return '';
868 } else {
869 if ( $this->mUserText === null ) {
870 $this->mUserText = User::whoIs( $this->mUser ); // load on demand
871 if ( $this->mUserText === false ) {
872 # This shouldn't happen, but it can if the wiki was recovered
873 # via importing revs and there is no user table entry yet.
874 $this->mUserText = $this->mOrigUserText;
875 }
876 }
877 return $this->mUserText;
878 }
879 }
880
887 public function getRawUserText() {
888 wfDeprecated( __METHOD__, '1.25' );
889 return $this->getUserText( self::RAW );
890 }
891
905 function getComment( $audience = self::FOR_PUBLIC, User $user = null ) {
906 if ( $audience == self::FOR_PUBLIC && $this->isDeleted( self::DELETED_COMMENT ) ) {
907 return '';
908 } elseif ( $audience == self::FOR_THIS_USER && !$this->userCan( self::DELETED_COMMENT, $user ) ) {
909 return '';
910 } else {
911 return $this->mComment;
912 }
913 }
914
921 public function getRawComment() {
922 wfDeprecated( __METHOD__, '1.25' );
923 return $this->getComment( self::RAW );
924 }
925
929 public function isMinor() {
930 return (bool)$this->mMinorEdit;
931 }
932
936 public function isUnpatrolled() {
937 if ( $this->mUnpatrolled !== null ) {
938 return $this->mUnpatrolled;
939 }
940 $rc = $this->getRecentChange();
941 if ( $rc && $rc->getAttribute( 'rc_patrolled' ) == 0 ) {
942 $this->mUnpatrolled = $rc->getAttribute( 'rc_id' );
943 } else {
944 $this->mUnpatrolled = 0;
945 }
946 return $this->mUnpatrolled;
947 }
948
958 public function getRecentChange( $flags = 0 ) {
959 $dbr = wfGetDB( DB_SLAVE );
960
962
964 [
965 'rc_user_text' => $this->getUserText( Revision::RAW ),
966 'rc_timestamp' => $dbr->timestamp( $this->getTimestamp() ),
967 'rc_this_oldid' => $this->getId()
968 ],
969 __METHOD__,
970 $dbType
971 );
972 }
973
979 public function isDeleted( $field ) {
980 return ( $this->mDeleted & $field ) == $field;
981 }
982
988 public function getVisibility() {
989 return (int)$this->mDeleted;
990 }
991
1008 public function getText( $audience = self::FOR_PUBLIC, User $user = null ) {
1009 ContentHandler::deprecated( __METHOD__, '1.21' );
1010
1011 $content = $this->getContent( $audience, $user );
1012 return ContentHandler::getContentText( $content ); # returns the raw content text, if applicable
1013 }
1014
1029 public function getContent( $audience = self::FOR_PUBLIC, User $user = null ) {
1030 if ( $audience == self::FOR_PUBLIC && $this->isDeleted( self::DELETED_TEXT ) ) {
1031 return null;
1032 } elseif ( $audience == self::FOR_THIS_USER && !$this->userCan( self::DELETED_TEXT, $user ) ) {
1033 return null;
1034 } else {
1035 return $this->getContentInternal();
1036 }
1037 }
1038
1045 public function getSerializedData() {
1046 if ( $this->mText === null ) {
1047 $this->mText = $this->loadText();
1048 }
1049
1050 return $this->mText;
1051 }
1052
1062 protected function getContentInternal() {
1063 if ( $this->mContent === null ) {
1064 // Revision is immutable. Load on demand:
1065 if ( $this->mText === null ) {
1066 $this->mText = $this->loadText();
1067 }
1068
1069 if ( $this->mText !== null && $this->mText !== false ) {
1070 // Unserialize content
1071 $handler = $this->getContentHandler();
1072 $format = $this->getContentFormat();
1073
1074 $this->mContent = $handler->unserializeContent( $this->mText, $format );
1075 }
1076 }
1077
1078 // NOTE: copy() will return $this for immutable content objects
1079 return $this->mContent ? $this->mContent->copy() : null;
1080 }
1081
1092 public function getContentModel() {
1093 if ( !$this->mContentModel ) {
1094 $title = $this->getTitle();
1095 if ( $title ) {
1096 $this->mContentModel = ContentHandler::getDefaultModelFor( $title );
1097 } else {
1098 $this->mContentModel = CONTENT_MODEL_WIKITEXT;
1099 }
1100
1101 assert( !empty( $this->mContentModel ) );
1102 }
1103
1104 return $this->mContentModel;
1105 }
1106
1116 public function getContentFormat() {
1117 if ( !$this->mContentFormat ) {
1118 $handler = $this->getContentHandler();
1119 $this->mContentFormat = $handler->getDefaultFormat();
1120
1121 assert( !empty( $this->mContentFormat ) );
1122 }
1123
1124 return $this->mContentFormat;
1125 }
1126
1133 public function getContentHandler() {
1134 if ( !$this->mContentHandler ) {
1135 $model = $this->getContentModel();
1136 $this->mContentHandler = ContentHandler::getForModelID( $model );
1137
1138 $format = $this->getContentFormat();
1139
1140 if ( !$this->mContentHandler->isSupportedFormat( $format ) ) {
1141 throw new MWException( "Oops, the content format $format is not supported for "
1142 . "this content model, $model" );
1143 }
1144 }
1145
1147 }
1148
1152 public function getTimestamp() {
1153 return wfTimestamp( TS_MW, $this->mTimestamp );
1154 }
1155
1159 public function isCurrent() {
1160 return $this->mCurrent;
1161 }
1162
1168 public function getPrevious() {
1169 if ( $this->getTitle() ) {
1170 $prev = $this->getTitle()->getPreviousRevisionID( $this->getId() );
1171 if ( $prev ) {
1172 return self::newFromTitle( $this->getTitle(), $prev );
1173 }
1174 }
1175 return null;
1176 }
1177
1183 public function getNext() {
1184 if ( $this->getTitle() ) {
1185 $next = $this->getTitle()->getNextRevisionID( $this->getId() );
1186 if ( $next ) {
1187 return self::newFromTitle( $this->getTitle(), $next );
1188 }
1189 }
1190 return null;
1191 }
1192
1200 private function getPreviousRevisionId( $db ) {
1201 if ( $this->mPage === null ) {
1202 return 0;
1203 }
1204 # Use page_latest if ID is not given
1205 if ( !$this->mId ) {
1206 $prevId = $db->selectField( 'page', 'page_latest',
1207 [ 'page_id' => $this->mPage ],
1208 __METHOD__ );
1209 } else {
1210 $prevId = $db->selectField( 'revision', 'rev_id',
1211 [ 'rev_page' => $this->mPage, 'rev_id < ' . $this->mId ],
1212 __METHOD__,
1213 [ 'ORDER BY' => 'rev_id DESC' ] );
1214 }
1215 return intval( $prevId );
1216 }
1217
1231 public static function getRevisionText( $row, $prefix = 'old_', $wiki = false ) {
1232
1233 # Get data
1234 $textField = $prefix . 'text';
1235 $flagsField = $prefix . 'flags';
1236
1237 if ( isset( $row->$flagsField ) ) {
1238 $flags = explode( ',', $row->$flagsField );
1239 } else {
1240 $flags = [];
1241 }
1242
1243 if ( isset( $row->$textField ) ) {
1244 $text = $row->$textField;
1245 } else {
1246 return false;
1247 }
1248
1249 # Use external methods for external objects, text in table is URL-only then
1250 if ( in_array( 'external', $flags ) ) {
1251 $url = $text;
1252 $parts = explode( '://', $url, 2 );
1253 if ( count( $parts ) == 1 || $parts[1] == '' ) {
1254 return false;
1255 }
1256 $text = ExternalStore::fetchFromURL( $url, [ 'wiki' => $wiki ] );
1257 }
1258
1259 // If the text was fetched without an error, convert it
1260 if ( $text !== false ) {
1261 $text = self::decompressRevisionText( $text, $flags );
1262 }
1263 return $text;
1264 }
1265
1276 public static function compressRevisionText( &$text ) {
1278 $flags = [];
1279
1280 # Revisions not marked this way will be converted
1281 # on load if $wgLegacyCharset is set in the future.
1282 $flags[] = 'utf-8';
1283
1284 if ( $wgCompressRevisions ) {
1285 if ( function_exists( 'gzdeflate' ) ) {
1286 $deflated = gzdeflate( $text );
1287
1288 if ( $deflated === false ) {
1289 wfLogWarning( __METHOD__ . ': gzdeflate() failed' );
1290 } else {
1291 $text = $deflated;
1292 $flags[] = 'gzip';
1293 }
1294 } else {
1295 wfDebug( __METHOD__ . " -- no zlib support, not compressing\n" );
1296 }
1297 }
1298 return implode( ',', $flags );
1299 }
1300
1308 public static function decompressRevisionText( $text, $flags ) {
1309 if ( in_array( 'gzip', $flags ) ) {
1310 # Deal with optional compression of archived pages.
1311 # This can be done periodically via maintenance/compressOld.php, and
1312 # as pages are saved if $wgCompressRevisions is set.
1313 $text = gzinflate( $text );
1314
1315 if ( $text === false ) {
1316 wfLogWarning( __METHOD__ . ': gzinflate() failed' );
1317 return false;
1318 }
1319 }
1320
1321 if ( in_array( 'object', $flags ) ) {
1322 # Generic compressed storage
1323 $obj = unserialize( $text );
1324 if ( !is_object( $obj ) ) {
1325 // Invalid object
1326 return false;
1327 }
1328 $text = $obj->getText();
1329 }
1330
1332 if ( $text !== false && $wgLegacyEncoding
1333 && !in_array( 'utf-8', $flags ) && !in_array( 'utf8', $flags )
1334 ) {
1335 # Old revisions kept around in a legacy encoding?
1336 # Upconvert on demand.
1337 # ("utf8" checked for compatibility with some broken
1338 # conversion scripts 2008-12-30)
1340 $text = $wgContLang->iconv( $wgLegacyEncoding, 'UTF-8', $text );
1341 }
1342
1343 return $text;
1344 }
1345
1354 public function insertOn( $dbw ) {
1356
1357 // Not allowed to have rev_page equal to 0, false, etc.
1358 if ( !$this->mPage ) {
1359 $title = $this->getTitle();
1360 if ( $title instanceof Title ) {
1361 $titleText = ' for page ' . $title->getPrefixedText();
1362 } else {
1363 $titleText = '';
1364 }
1365 throw new MWException( "Cannot insert revision$titleText: page ID must be nonzero" );
1366 }
1367
1368 $this->checkContentModel();
1369
1370 $data = $this->mText;
1372
1373 # Write to external storage if required
1375 // Store and get the URL
1376 $data = ExternalStore::insertToDefault( $data );
1377 if ( !$data ) {
1378 throw new MWException( "Unable to store text to external storage" );
1379 }
1380 if ( $flags ) {
1381 $flags .= ',';
1382 }
1383 $flags .= 'external';
1384 }
1385
1386 # Record the text (or external storage URL) to the text table
1387 if ( $this->mTextId === null ) {
1388 $old_id = $dbw->nextSequenceValue( 'text_old_id_seq' );
1389 $dbw->insert( 'text',
1390 [
1391 'old_id' => $old_id,
1392 'old_text' => $data,
1393 'old_flags' => $flags,
1394 ], __METHOD__
1395 );
1396 $this->mTextId = $dbw->insertId();
1397 }
1398
1399 if ( $this->mComment === null ) {
1400 $this->mComment = "";
1401 }
1402
1403 # Record the edit in revisions
1404 $rev_id = $this->mId !== null
1405 ? $this->mId
1406 : $dbw->nextSequenceValue( 'revision_rev_id_seq' );
1407 $row = [
1408 'rev_id' => $rev_id,
1409 'rev_page' => $this->mPage,
1410 'rev_text_id' => $this->mTextId,
1411 'rev_comment' => $this->mComment,
1412 'rev_minor_edit' => $this->mMinorEdit ? 1 : 0,
1413 'rev_user' => $this->mUser,
1414 'rev_user_text' => $this->mUserText,
1415 'rev_timestamp' => $dbw->timestamp( $this->mTimestamp ),
1416 'rev_deleted' => $this->mDeleted,
1417 'rev_len' => $this->mSize,
1418 'rev_parent_id' => $this->mParentId === null
1419 ? $this->getPreviousRevisionId( $dbw )
1421 'rev_sha1' => $this->mSha1 === null
1422 ? Revision::base36Sha1( $this->mText )
1423 : $this->mSha1,
1424 ];
1425
1426 if ( $wgContentHandlerUseDB ) {
1427 // NOTE: Store null for the default model and format, to save space.
1428 // XXX: Makes the DB sensitive to changed defaults.
1429 // Make this behavior optional? Only in miser mode?
1430
1431 $model = $this->getContentModel();
1432 $format = $this->getContentFormat();
1433
1434 $title = $this->getTitle();
1435
1436 if ( $title === null ) {
1437 throw new MWException( "Insufficient information to determine the title of the "
1438 . "revision's page!" );
1439 }
1440
1441 $defaultModel = ContentHandler::getDefaultModelFor( $title );
1442 $defaultFormat = ContentHandler::getForModelID( $defaultModel )->getDefaultFormat();
1443
1444 $row['rev_content_model'] = ( $model === $defaultModel ) ? null : $model;
1445 $row['rev_content_format'] = ( $format === $defaultFormat ) ? null : $format;
1446 }
1447
1448 $dbw->insert( 'revision', $row, __METHOD__ );
1449
1450 $this->mId = $rev_id !== null ? $rev_id : $dbw->insertId();
1451
1452 // Assertion to try to catch T92046
1453 if ( (int)$this->mId === 0 ) {
1454 throw new UnexpectedValueException(
1455 'After insert, Revision mId is ' . var_export( $this->mId, 1 ) . ': ' .
1456 var_export( $row, 1 )
1457 );
1458 }
1459
1460 // Avoid PHP 7.1 warning of passing $this by reference
1461 $revision = $this;
1462 Hooks::run( 'RevisionInsertComplete', [ &$revision, $data, $flags ] );
1463
1464 return $this->mId;
1465 }
1466
1467 protected function checkContentModel() {
1469
1470 // Note: may return null for revisions that have not yet been inserted
1471 $title = $this->getTitle();
1472
1473 $model = $this->getContentModel();
1474 $format = $this->getContentFormat();
1475 $handler = $this->getContentHandler();
1476
1477 if ( !$handler->isSupportedFormat( $format ) ) {
1478 $t = $title->getPrefixedDBkey();
1479
1480 throw new MWException( "Can't use format $format with content model $model on $t" );
1481 }
1482
1483 if ( !$wgContentHandlerUseDB && $title ) {
1484 // if $wgContentHandlerUseDB is not set,
1485 // all revisions must use the default content model and format.
1486
1487 $defaultModel = ContentHandler::getDefaultModelFor( $title );
1488 $defaultHandler = ContentHandler::getForModelID( $defaultModel );
1489 $defaultFormat = $defaultHandler->getDefaultFormat();
1490
1491 if ( $this->getContentModel() != $defaultModel ) {
1492 $t = $title->getPrefixedDBkey();
1493
1494 throw new MWException( "Can't save non-default content model with "
1495 . "\$wgContentHandlerUseDB disabled: model is $model, "
1496 . "default for $t is $defaultModel" );
1497 }
1498
1499 if ( $this->getContentFormat() != $defaultFormat ) {
1500 $t = $title->getPrefixedDBkey();
1501
1502 throw new MWException( "Can't use non-default content format with "
1503 . "\$wgContentHandlerUseDB disabled: format is $format, "
1504 . "default for $t is $defaultFormat" );
1505 }
1506 }
1507
1508 $content = $this->getContent( Revision::RAW );
1509 $prefixedDBkey = $title->getPrefixedDBkey();
1511
1512 if ( !$content ) {
1513 throw new MWException(
1514 "Content of revision $revId ($prefixedDBkey) could not be loaded for validation!"
1515 );
1516 }
1517 if ( !$content->isValid() ) {
1518 throw new MWException(
1519 "Content of revision $revId ($prefixedDBkey) is not valid! Content model is $model"
1520 );
1521 }
1522 }
1523
1529 public static function base36Sha1( $text ) {
1530 return Wikimedia\base_convert( sha1( $text ), 16, 36, 31 );
1531 }
1532
1539 protected function loadText() {
1540 // Caching may be beneficial for massive use of external storage
1542 static $processCache = null;
1543
1544 if ( !$processCache ) {
1545 $processCache = new MapCacheLRU( 10 );
1546 }
1547
1549 $textId = $this->getTextId();
1550 $key = wfMemcKey( 'revisiontext', 'textid', $textId );
1551
1552 if ( $wgRevisionCacheExpiry ) {
1553 if ( $processCache->has( $key ) ) {
1554 return $processCache->get( $key );
1555 }
1556 $text = $cache->get( $key );
1557 if ( is_string( $text ) ) {
1558 wfDebug( __METHOD__ . ": got id $textId from cache\n" );
1559 $processCache->set( $key, $text );
1560 return $text;
1561 }
1562 }
1563
1564 // If we kept data for lazy extraction, use it now...
1565 if ( $this->mTextRow !== null ) {
1566 $row = $this->mTextRow;
1567 $this->mTextRow = null;
1568 } else {
1569 $row = null;
1570 }
1571
1572 if ( !$row ) {
1573 // Text data is immutable; check slaves first.
1574 $dbr = wfGetDB( DB_SLAVE );
1575 $row = $dbr->selectRow( 'text',
1576 [ 'old_text', 'old_flags' ],
1577 [ 'old_id' => $textId ],
1578 __METHOD__ );
1579 }
1580
1581 // Fallback to the master in case of slave lag. Also use FOR UPDATE if it was
1582 // used to fetch this revision to avoid missing the row due to REPEATABLE-READ.
1583 $forUpdate = ( $this->mQueryFlags & self::READ_LOCKING == self::READ_LOCKING );
1584 if ( !$row && ( $forUpdate || wfGetLB()->getServerCount() > 1 ) ) {
1585 $dbw = wfGetDB( DB_MASTER );
1586 $row = $dbw->selectRow( 'text',
1587 [ 'old_text', 'old_flags' ],
1588 [ 'old_id' => $textId ],
1589 __METHOD__,
1590 $forUpdate ? [ 'FOR UPDATE' ] : [] );
1591 }
1592
1593 if ( !$row ) {
1594 wfDebugLog( 'Revision', "No text row with ID '$textId' (revision {$this->getId()})." );
1595 }
1596
1597 $text = self::getRevisionText( $row );
1598 if ( $row && $text === false ) {
1599 wfDebugLog( 'Revision', "No blob for text row '$textId' (revision {$this->getId()})." );
1600 }
1601
1602 # No negative caching -- negative hits on text rows may be due to corrupted slave servers
1603 if ( $wgRevisionCacheExpiry && $text !== false ) {
1604 $processCache->set( $key, $text );
1605 $cache->set( $key, $text, $wgRevisionCacheExpiry );
1606 }
1607
1608 return $text;
1609 }
1610
1626 public static function newNullRevision( $dbw, $pageId, $summary, $minor, $user = null ) {
1628
1629 $fields = [ 'page_latest', 'page_namespace', 'page_title',
1630 'rev_text_id', 'rev_len', 'rev_sha1' ];
1631
1632 if ( $wgContentHandlerUseDB ) {
1633 $fields[] = 'rev_content_model';
1634 $fields[] = 'rev_content_format';
1635 }
1636
1637 $current = $dbw->selectRow(
1638 [ 'page', 'revision' ],
1639 $fields,
1640 [
1641 'page_id' => $pageId,
1642 'page_latest=rev_id',
1643 ],
1644 __METHOD__,
1645 [ 'FOR UPDATE' ] // T51581
1646 );
1647
1648 if ( $current ) {
1649 if ( !$user ) {
1651 $user = $wgUser;
1652 }
1653
1654 // Truncate for whole multibyte characters
1655 $summary = $wgContLang->truncate( $summary, 255 );
1656
1657 $row = [
1658 'page' => $pageId,
1659 'user_text' => $user->getName(),
1660 'user' => $user->getId(),
1661 'comment' => $summary,
1662 'minor_edit' => $minor,
1663 'text_id' => $current->rev_text_id,
1664 'parent_id' => $current->page_latest,
1665 'len' => $current->rev_len,
1666 'sha1' => $current->rev_sha1
1667 ];
1668
1669 if ( $wgContentHandlerUseDB ) {
1670 $row['content_model'] = $current->rev_content_model;
1671 $row['content_format'] = $current->rev_content_format;
1672 }
1673
1674 $row['title'] = Title::makeTitle( $current->page_namespace, $current->page_title );
1675
1676 $revision = new Revision( $row );
1677 } else {
1678 $revision = null;
1679 }
1680
1681 return $revision;
1682 }
1683
1694 public function userCan( $field, User $user = null ) {
1695 return self::userCanBitfield( $this->mDeleted, $field, $user );
1696 }
1697
1712 public static function userCanBitfield( $bitfield, $field, User $user = null,
1713 Title $title = null
1714 ) {
1715 if ( $bitfield & $field ) { // aspect is deleted
1716 if ( $user === null ) {
1718 $user = $wgUser;
1719 }
1720 if ( $bitfield & self::DELETED_RESTRICTED ) {
1721 $permissions = [ 'suppressrevision', 'viewsuppressed' ];
1722 } elseif ( $field & self::DELETED_TEXT ) {
1723 $permissions = [ 'deletedtext' ];
1724 } else {
1725 $permissions = [ 'deletedhistory' ];
1726 }
1727 $permissionlist = implode( ', ', $permissions );
1728 if ( $title === null ) {
1729 wfDebug( "Checking for $permissionlist due to $field match on $bitfield\n" );
1730 return call_user_func_array( [ $user, 'isAllowedAny' ], $permissions );
1731 } else {
1732 $text = $title->getPrefixedText();
1733 wfDebug( "Checking for $permissionlist on $text due to $field match on $bitfield\n" );
1734 foreach ( $permissions as $perm ) {
1735 if ( $title->userCan( $perm, $user ) ) {
1736 return true;
1737 }
1738 }
1739 return false;
1740 }
1741 } else {
1742 return true;
1743 }
1744 }
1745
1753 static function getTimestampFromId( $title, $id, $flags = 0 ) {
1754 $db = ( $flags & self::READ_LATEST )
1755 ? wfGetDB( DB_MASTER )
1756 : wfGetDB( DB_SLAVE );
1757 // Casting fix for databases that can't take '' for rev_id
1758 if ( $id == '' ) {
1759 $id = 0;
1760 }
1761 $conds = [ 'rev_id' => $id ];
1762 $conds['rev_page'] = $title->getArticleID();
1763 $timestamp = $db->selectField( 'revision', 'rev_timestamp', $conds, __METHOD__ );
1764
1765 return ( $timestamp !== false ) ? wfTimestamp( TS_MW, $timestamp ) : false;
1766 }
1767
1775 static function countByPageId( $db, $id ) {
1776 $row = $db->selectRow( 'revision', [ 'revCount' => 'COUNT(*)' ],
1777 [ 'rev_page' => $id ], __METHOD__ );
1778 if ( $row ) {
1779 return $row->revCount;
1780 }
1781 return 0;
1782 }
1783
1791 static function countByTitle( $db, $title ) {
1792 $id = $title->getArticleID();
1793 if ( $id ) {
1794 return self::countByPageId( $db, $id );
1795 }
1796 return 0;
1797 }
1798
1815 public static function userWasLastToEdit( $db, $pageId, $userId, $since ) {
1816 if ( !$userId ) {
1817 return false;
1818 }
1819
1820 if ( is_int( $db ) ) {
1821 $db = wfGetDB( $db );
1822 }
1823
1824 $res = $db->select( 'revision',
1825 'rev_user',
1826 [
1827 'rev_page' => $pageId,
1828 'rev_timestamp > ' . $db->addQuotes( $db->timestamp( $since ) )
1829 ],
1830 __METHOD__,
1831 [ 'ORDER BY' => 'rev_timestamp ASC', 'LIMIT' => 50 ] );
1832 foreach ( $res as $row ) {
1833 if ( $row->rev_user != $userId ) {
1834 return false;
1835 }
1836 }
1837 return true;
1838 }
1839}
Apache License January AND DISTRIBUTION Definitions License shall mean the terms and conditions for use
unserialize( $serialized)
$wgLegacyEncoding
Set this to eg 'ISO-8859-1' to perform character set conversion when loading old revisions not marked...
$wgRevisionCacheExpiry
Revision text may be cached in $wgMemc to reduce load on external storage servers and object extracti...
array $wgDefaultExternalStore
The place to put new revisions, false to put them in the local text table.
$wgCompressRevisions
We can also compress text stored in the 'text' table.
$wgContentHandlerUseDB
Set to false to disable use of the database fields introduced by the ContentHandler facility.
wfDebug( $text, $dest='all', array $context=[])
Sends a line to the debug log if enabled or, optionally, to a comment in output.
wfTimestampNow()
Convenience function; returns MediaWiki timestamp for the present time.
wfGetLB( $wiki=false)
Get a load balancer object.
wfGetDB( $db, $groups=[], $wiki=false)
Get a Database object.
wfMemcKey()
Make a cache key for the local wiki.
wfLogWarning( $msg, $callerOffset=1, $level=E_USER_WARNING)
Send a warning as a PHP error and the debug log.
wfDebugLog( $logGroup, $text, $dest='all', array $context=[])
Send a line to a supplementary debug log file, if configured, or main debug log if not.
const TS_MW
MediaWiki concatenated string timestamp (YYYYMMDDHHMMSS)
wfTimestamp( $outputtype=TS_UNIX, $ts=0)
Get a timestamp string in one of various formats.
wfDeprecated( $function, $version=false, $component=false, $callerOffset=2)
Throws a warning that $function is deprecated.
$wgUser
Definition Setup.php:794
A content handler knows how do deal with a specific type of content on a wiki page.
static getForModelID( $modelId)
Returns the ContentHandler singleton for the given model ID.
static getDefaultModelFor(Title $title)
Returns the name of the default content model to be used for the page with the given title.
static getContentText(Content $content=null)
Convenience function for getting flat text from a Content object.
static deprecated( $func, $version, $component=false)
Logs a deprecation warning, visible if $wgDevelopmentWarnings, but only if self::$enableDeprecationWa...
static getDBOptions( $bitfield)
Get an appropriate DB index and options for a query.
static fetchFromURL( $url, array $params=[])
Fetch data from given URL.
static insertToDefault( $data, array $params=[])
Like insert() above, but does more of the work for us.
MediaWiki exception.
Handles a simple LRU key/value map with a maximum number of entries.
static getMainWANInstance()
Get the main WAN cache object.
static newFromConds( $conds, $fname=__METHOD__, $dbType=DB_SLAVE)
Find the first recent change matching some specific conditions.
stdClass null $mTextRow
Definition Revision.php:50
getRecentChange( $flags=0)
Get the RC object belonging to the current revision, if there's one.
Definition Revision.php:958
getUserText( $audience=self::FOR_PUBLIC, User $user=null)
Fetch revision's username if it's available to the specified audience.
Definition Revision.php:863
getTitle()
Returns the title of the page associated with this entry or null.
Definition Revision.php:773
static selectArchiveFields()
Return the list of revision fields that should be selected to create a new revision from an archive r...
Definition Revision.php:460
getPrevious()
Get previous revision for this title.
null ContentHandler $mContentHandler
Definition Revision.php:68
getSize()
Returns the length of the text in this revision, or null if unknown.
Definition Revision.php:753
null Title $mTitle
Definition Revision.php:55
getContentInternal()
Gets the content object for the revision (or null on failure).
getSerializedData()
Fetch original serialized data without regard for view restrictions.
getId()
Get revision ID.
Definition Revision.php:716
static compressRevisionText(&$text)
If $wgCompressRevisions is enabled, we will compress data.
getRawUser()
Fetch revision's user id without regard for the current user's permissions.
Definition Revision.php:845
static userJoinCond()
Return the value of a select() JOIN conds array for the user table.
Definition Revision.php:410
static decompressRevisionText( $text, $flags)
Re-converts revision text according to it's flags.
getContentHandler()
Returns the content handler appropriate for this revision's content model.
static getRevisionText( $row, $prefix='old_', $wiki=false)
Get revision text associated with an old or archive row $row is usually an object from wfFetchRow(),...
getComment( $audience=self::FOR_PUBLIC, User $user=null)
Fetch revision comment if it's available to the specified audience.
Definition Revision.php:905
static fetchFromConds( $db, $conditions, $flags=0)
Given a set of conditions, return a ResultWrapper which will return matching database rows with the f...
Definition Revision.php:384
static loadFromTitle( $db, $title, $id=0)
Load either the current, or a specified, revision that's attached to a given page.
Definition Revision.php:265
getNext()
Get next revision for this title.
getTextId()
Get text row ID.
Definition Revision.php:735
static selectTextFields()
Return the list of text fields that should be selected to read the revision text.
Definition Revision.php:491
static newFromPageId( $pageId, $revId=0, $flags=0)
Load either the current, or a specified, revision that's attached to a given page ID.
Definition Revision.php:148
static selectPageFields()
Return the list of page fields that should be selected from page table.
Definition Revision.php:502
getContentFormat()
Returns the content format for this revision.
static selectFields()
Return the list of revision fields that should be selected to create a new revision.
Definition Revision.php:429
static loadFromTimestamp( $db, $title, $timestamp)
Load the revision for the given title with the given timestamp.
Definition Revision.php:290
static loadFromConds( $db, $conditions, $flags=0)
Given a set of conditions, fetch a revision from the given database connection.
Definition Revision.php:342
getPage()
Get the page ID.
Definition Revision.php:812
static newFromRow( $row)
Definition Revision.php:219
static newNullRevision( $dbw, $pageId, $summary, $minor, $user=null)
Create a new null-revision for insertion into a page's history.
static loadFromId( $db, $id)
Load a page revision from a given revision ID number.
Definition Revision.php:231
static getTimestampFromId( $title, $id, $flags=0)
Get rev_timestamp from rev_id, without loading the rest of the row.
static selectUserFields()
Return the list of user fields that should be selected from user table.
Definition Revision.php:517
getContentModel()
Returns the content model for this revision.
static countByPageId( $db, $id)
Get count of revisions per page...not very efficient.
getUser( $audience=self::FOR_PUBLIC, User $user=null)
Fetch revision's user id if it's available to the specified audience.
Definition Revision.php:829
static newFromArchiveRow( $row, $overrides=[])
Make a fake revision object from an archive table row.
Definition Revision.php:172
getContent( $audience=self::FOR_PUBLIC, User $user=null)
Fetch revision content if it's available to the specified audience.
static countByTitle( $db, $title)
Get count of revisions per page...not very efficient.
static pageJoinCond()
Return the value of a select() page conds array for the page table.
Definition Revision.php:420
checkContentModel()
getText( $audience=self::FOR_PUBLIC, User $user=null)
Fetch revision text if it's available to the specified audience.
const DELETED_USER
Definition Revision.php:78
static newFromConds( $conditions, $flags=0)
Given a set of conditions, fetch a revision.
Definition Revision.php:310
const DELETED_TEXT
Definition Revision.php:76
static base36Sha1( $text)
Get the base 36 SHA-1 value for a string of text.
int $mQueryFlags
Definition Revision.php:73
$mContentFormat
Definition Revision.php:58
__construct( $row)
Constructor.
Definition Revision.php:549
getSha1()
Returns the base36 sha1 of the text in this revision, or null if unknown.
Definition Revision.php:762
const DELETED_RESTRICTED
Definition Revision.php:79
static loadFromPageId( $db, $pageid, $id=0)
Load either the current, or a specified, revision that's attached to a given page.
Definition Revision.php:245
getRawComment()
Fetch revision comment without regard for the current user's permissions.
Definition Revision.php:921
static userCanBitfield( $bitfield, $field, User $user=null, Title $title=null)
Determine if the current user is allowed to view a particular field of this revision,...
getVisibility()
Get the deletion bitfield of the revision.
Definition Revision.php:988
setTitle( $title)
Set the title of the revision.
Definition Revision.php:803
int null $mPage
Definition Revision.php:33
insertOn( $dbw)
Insert a new revision into the database, returning the new revision ID number on success and dies hor...
Content null bool $mContent
Definition Revision.php:63
static userWasLastToEdit( $db, $pageId, $userId, $since)
Check if no edits were made by other users since the time a user started editing the page.
getRawUserText()
Fetch revision's username without regard for view restrictions.
Definition Revision.php:887
static getParentLengths( $db, array $revIds)
Do a batched query to get the parent revision lengths.
Definition Revision.php:527
static fetchRevision( $title)
Return a wrapper for a series of database rows to fetch all of a given page's revisions in turn.
Definition Revision.php:363
isUnpatrolled()
Definition Revision.php:936
userCan( $field, User $user=null)
Determine if the current user is allowed to view a particular field of this revision,...
const FOR_PUBLIC
Definition Revision.php:83
static newFromTitle(LinkTarget $linkTarget, $id=0, $flags=0)
Load either the current, or a specified, revision that's attached to a given link target.
Definition Revision.php:117
getParentId()
Get parent revision ID (the original previous page revision)
Definition Revision.php:744
getPreviousRevisionId( $db)
Get previous revision Id for this page_id This is used to populate rev_parent_id on save.
loadText()
Lazy-load the revision's text.
setId( $id)
Set the revision ID.
Definition Revision.php:726
const RAW
Definition Revision.php:85
isDeleted( $field)
Definition Revision.php:979
const DELETED_COMMENT
Definition Revision.php:77
const FOR_THIS_USER
Definition Revision.php:84
static newFromId( $id, $flags=0)
Load a page revision from a given revision ID number.
Definition Revision.php:99
const SUPPRESSED_USER
Definition Revision.php:80
Represents a title within MediaWiki.
Definition Title.php:34
static newFromID( $id, $flags=0)
Create a new Title from an article ID.
Definition Title.php:417
static & makeTitle( $ns, $title, $fragment='', $interwiki='')
Create a new Title from a namespace index and a DB key.
Definition Title.php:524
static newFromRow( $row)
Make a Title object from a DB row.
Definition Title.php:465
The User object encapsulates all of the user-specific settings (user_id, name, rights,...
Definition User.php:47
per default it will return the text for text based content
$res
Definition database.txt:21
deferred txt A few of the database updates required by various functions here can be deferred until after the result page is displayed to the user For updating the view updating the linked to tables after a etc PHP does not yet have any way to tell the server to actually return and disconnect while still running these but it might have such a feature in the future We handle these by creating a deferred update object and putting those objects on a global list
Definition deferred.txt:11
this class mediates it Skin Encapsulates a look and feel for the wiki All of the functions that render HTML and make choices about how to render it are here and are called from various other places when needed(most notably, OutputPage::addWikiText()). The StandardSkin object is a complete implementation
this class mediates it Skin Encapsulates a look and feel for the wiki All of the functions that render HTML and make choices about how to render it are here and are called from various other places when and is meant to be subclassed with other skins that may override some of its functions The User object contains a reference to a and so rather than having a global skin object we just rely on the global User and get the skin with $wgUser and also has some character encoding functions and other locale stuff The current user interface language is instantiated as and the local content language as $wgContLang
Definition design.txt:57
when a variable name is used in a it is silently declared as a new local masking the global
Definition design.txt:95
design txt This is a brief overview of the new design More thorough and up to date information is available on the documentation wiki at etc Handles the details of getting and saving to the user table of the and dealing with sessions and cookies OutputPage Encapsulates the entire HTML page that will be sent in response to any server request It is used by calling its functions to add text
Definition design.txt:18
This document is intended to provide useful advice for parties seeking to redistribute MediaWiki to end users It s targeted particularly at maintainers for Linux since it s been observed that distribution packages of MediaWiki often break We ve consistently had to recommend that users seeking support use official tarballs instead of their distribution s and this often solves whatever problem the user is having It would be nice if this could such as
const DB_MASTER
Definition Defines.php:48
const CONTENT_MODEL_WIKITEXT
Definition Defines.php:278
const DB_SLAVE
Definition Defines.php:47
this hook is for auditing only RecentChangesLinked and Watchlist RecentChangesLinked and Watchlist e g Watchlist removed from all revisions and log entries to which it was applied This gives extensions a chance to take it off their books as the deletion has already been partly carried out by this point or something similar the user will be unable to create the tag set and then return false from the hook function Ensure you consume the ChangeTagAfterDelete hook to carry out custom deletion actions as context called by AbstractContent::getParserOutput May be used to override the normal model specific rendering of page content as context $revId
Definition hooks.txt:1041
the array() calling protocol came about after MediaWiki 1.4rc1.
please add to it if you re going to add events to the MediaWiki code where normally authentication against an external auth plugin would be creating a local account $user
Definition hooks.txt:249
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped noclasses just before the function returns a value If you return an< a > element with HTML attributes $attribs and contents $html will be returned If you return $ret will be returned and may include noclasses after processing & $attribs
Definition hooks.txt:1819
this hook is for auditing only RecentChangesLinked and Watchlist RecentChangesLinked and Watchlist e g Watchlist removed from all revisions and log entries to which it was applied This gives extensions a chance to take it off their books as the deletion has already been partly carried out by this point or something similar the user will be unable to create the tag set and then return false from the hook function Ensure you consume the ChangeTagAfterDelete hook to carry out custom deletion actions as context called by AbstractContent::getParserOutput May be used to override the normal model specific rendering of page content as context as context $options
Definition hooks.txt:1042
this hook is for auditing only RecentChangesLinked and Watchlist RecentChangesLinked and Watchlist e g Watchlist removed from all revisions and log entries to which it was applied This gives extensions a chance to take it off their books as the deletion has already been partly carried out by this point or something similar the user will be unable to create the tag set and then return false from the hook function Ensure you consume the ChangeTagAfterDelete hook to carry out custom deletion actions as context called by AbstractContent::getParserOutput May be used to override the normal model specific rendering of page content $content
Definition hooks.txt:1040
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped noclasses & $ret
Definition hooks.txt:1810
namespace and then decline to actually register it file or subcat img or subcat $title
Definition hooks.txt:944
it s the revision text itself In either if gzip is the revision text is gzipped $flags
Definition hooks.txt:2555
We ve cleaned up the code here by removing clumps of infrequently used code and moving them off somewhere else It s much easier for someone working with this code to see what s _really_ going on
Definition hooks.txt:88
this hook is for auditing only or null if authentication failed before getting that far or null if we can t even determine that probably a stub it is not rendered in wiki pages or galleries in category pages allow injecting custom HTML after the section Any uses of the hook need to handle escaping see BaseTemplate::getToolbox and BaseTemplate::makeListItem for details on the format of individual items inside of this array or by returning and letting standard HTTP rendering take place modifiable or by returning false and taking over the output modifiable modifiable after all normalizations have been except for the $wgMaxImageArea check set to true or false to override the $wgMaxImageArea check result gives extension the possibility to transform it themselves $handler
Definition hooks.txt:885
presenting them properly to the user as errors is done by the caller return true use this to change the list i e etc $rev
Definition hooks.txt:1597
if( $limit) $timestamp
$summary
injection txt This is an overview of how MediaWiki makes use of dependency injection The design described here grew from the discussion of RFC T384 The term dependency this means that anything an object needs to operate should be injected from the the object itself should only know narrow no concrete implementation of the logic it relies on The requirement to inject everything typically results in an architecture that based on two main types of and essentially stateless service objects that use other service objects to operate on the value objects As of the beginning MediaWiki is only starting to use the DI approach Much of the code still relies on global state or direct resulting in a highly cyclical dependency which acts as the top level factory for services in MediaWiki which can be used to gain access to default instances of various services MediaWikiServices however also allows new services to be defined and default services to be redefined Services are defined or redefined by providing a callback the instantiator that will return a new instance of the service When it will create an instance of MediaWikiServices and populate it with the services defined in the files listed by thereby bootstrapping the DI framework Per $wgServiceWiringFiles lists includes ServiceWiring php
Definition injection.txt:37
Base interface for content objects.
Definition Content.php:34
Interface for database access objects.
getNamespace()
Get the namespace index.
getDBkey()
Get the main part with underscores.
$cache
Definition mcc.php:33
#define the
table suitable for use with IDatabase::select()