MediaWiki REL1_30
Revision.php
Go to the documentation of this file.
1<?php
29
33class Revision implements IDBAccessObject {
35 protected $mId;
37 protected $mPage;
39 protected $mUserText;
41 protected $mOrigUserText;
43 protected $mUser;
45 protected $mMinorEdit;
47 protected $mTimestamp;
49 protected $mDeleted;
51 protected $mSize;
53 protected $mSha1;
55 protected $mParentId;
57 protected $mComment;
59 protected $mText;
61 protected $mTextId;
63 protected $mUnpatrolled;
64
66 protected $mTextRow;
67
69 protected $mTitle;
71 protected $mCurrent;
73 protected $mContentModel;
75 protected $mContentFormat;
76
78 protected $mContent;
81
83 protected $mQueryFlags = 0;
85 protected $mRefreshMutableFields = false;
87 protected $mWiki = false;
88
89 // Revision deletion constants
90 const DELETED_TEXT = 1;
91 const DELETED_COMMENT = 2;
92 const DELETED_USER = 4;
94 const SUPPRESSED_USER = 12; // convenience
95 const SUPPRESSED_ALL = 15; // convenience
96
97 // Audience options for accessors
98 const FOR_PUBLIC = 1;
99 const FOR_THIS_USER = 2;
100 const RAW = 3;
101
102 const TEXT_CACHE_GROUP = 'revisiontext:10'; // process cache name and max key count
103
116 public static function newFromId( $id, $flags = 0 ) {
117 return self::newFromConds( [ 'rev_id' => intval( $id ) ], $flags );
118 }
119
134 public static function newFromTitle( LinkTarget $linkTarget, $id = 0, $flags = 0 ) {
135 $conds = [
136 'page_namespace' => $linkTarget->getNamespace(),
137 'page_title' => $linkTarget->getDBkey()
138 ];
139 if ( $id ) {
140 // Use the specified ID
141 $conds['rev_id'] = $id;
142 return self::newFromConds( $conds, $flags );
143 } else {
144 // Use a join to get the latest revision
145 $conds[] = 'rev_id=page_latest';
146 $db = wfGetDB( ( $flags & self::READ_LATEST ) ? DB_MASTER : DB_REPLICA );
147 return self::loadFromConds( $db, $conds, $flags );
148 }
149 }
150
165 public static function newFromPageId( $pageId, $revId = 0, $flags = 0 ) {
166 $conds = [ 'page_id' => $pageId ];
167 if ( $revId ) {
168 $conds['rev_id'] = $revId;
169 return self::newFromConds( $conds, $flags );
170 } else {
171 // Use a join to get the latest revision
172 $conds[] = 'rev_id = page_latest';
173 $db = wfGetDB( ( $flags & self::READ_LATEST ) ? DB_MASTER : DB_REPLICA );
174 return self::loadFromConds( $db, $conds, $flags );
175 }
176 }
177
189 public static function newFromArchiveRow( $row, $overrides = [] ) {
191
192 $attribs = $overrides + [
193 'page' => isset( $row->ar_page_id ) ? $row->ar_page_id : null,
194 'id' => isset( $row->ar_rev_id ) ? $row->ar_rev_id : null,
195 'comment' => CommentStore::newKey( 'ar_comment' )
196 // Legacy because $row probably came from self::selectArchiveFields()
197 ->getCommentLegacy( wfGetDB( DB_REPLICA ), $row, true )->text,
198 'user' => $row->ar_user,
199 'user_text' => $row->ar_user_text,
200 'timestamp' => $row->ar_timestamp,
201 'minor_edit' => $row->ar_minor_edit,
202 'text_id' => isset( $row->ar_text_id ) ? $row->ar_text_id : null,
203 'deleted' => $row->ar_deleted,
204 'len' => $row->ar_len,
205 'sha1' => isset( $row->ar_sha1 ) ? $row->ar_sha1 : null,
206 'content_model' => isset( $row->ar_content_model ) ? $row->ar_content_model : null,
207 'content_format' => isset( $row->ar_content_format ) ? $row->ar_content_format : null,
208 ];
209
210 if ( !$wgContentHandlerUseDB ) {
211 unset( $attribs['content_model'] );
212 unset( $attribs['content_format'] );
213 }
214
215 if ( !isset( $attribs['title'] )
216 && isset( $row->ar_namespace )
217 && isset( $row->ar_title )
218 ) {
219 $attribs['title'] = Title::makeTitle( $row->ar_namespace, $row->ar_title );
220 }
221
222 if ( isset( $row->ar_text ) && !$row->ar_text_id ) {
223 // Pre-1.5 ar_text row
224 $attribs['text'] = self::getRevisionText( $row, 'ar_' );
225 if ( $attribs['text'] === false ) {
226 throw new MWException( 'Unable to load text from archive row (possibly T24624)' );
227 }
228 }
229 return new self( $attribs );
230 }
231
238 public static function newFromRow( $row ) {
239 return new self( $row );
240 }
241
250 public static function loadFromId( $db, $id ) {
251 return self::loadFromConds( $db, [ 'rev_id' => intval( $id ) ] );
252 }
253
264 public static function loadFromPageId( $db, $pageid, $id = 0 ) {
265 $conds = [ 'rev_page' => intval( $pageid ), 'page_id' => intval( $pageid ) ];
266 if ( $id ) {
267 $conds['rev_id'] = intval( $id );
268 } else {
269 $conds[] = 'rev_id=page_latest';
270 }
271 return self::loadFromConds( $db, $conds );
272 }
273
284 public static function loadFromTitle( $db, $title, $id = 0 ) {
285 if ( $id ) {
286 $matchId = intval( $id );
287 } else {
288 $matchId = 'page_latest';
289 }
290 return self::loadFromConds( $db,
291 [
292 "rev_id=$matchId",
293 'page_namespace' => $title->getNamespace(),
294 'page_title' => $title->getDBkey()
295 ]
296 );
297 }
298
309 public static function loadFromTimestamp( $db, $title, $timestamp ) {
310 return self::loadFromConds( $db,
311 [
312 'rev_timestamp' => $db->timestamp( $timestamp ),
313 'page_namespace' => $title->getNamespace(),
314 'page_title' => $title->getDBkey()
315 ]
316 );
317 }
318
329 private static function newFromConds( $conditions, $flags = 0 ) {
330 $db = wfGetDB( ( $flags & self::READ_LATEST ) ? DB_MASTER : DB_REPLICA );
331
332 $rev = self::loadFromConds( $db, $conditions, $flags );
333 // Make sure new pending/committed revision are visibile later on
334 // within web requests to certain avoid bugs like T93866 and T94407.
335 if ( !$rev
336 && !( $flags & self::READ_LATEST )
337 && wfGetLB()->getServerCount() > 1
338 && wfGetLB()->hasOrMadeRecentMasterChanges()
339 ) {
340 $flags = self::READ_LATEST;
341 $db = wfGetDB( DB_MASTER );
342 $rev = self::loadFromConds( $db, $conditions, $flags );
343 }
344
345 if ( $rev ) {
346 $rev->mQueryFlags = $flags;
347 }
348
349 return $rev;
350 }
351
361 private static function loadFromConds( $db, $conditions, $flags = 0 ) {
362 $row = self::fetchFromConds( $db, $conditions, $flags );
363 if ( $row ) {
364 $rev = new Revision( $row );
365 $rev->mWiki = $db->getDomainID();
366
367 return $rev;
368 }
369
370 return null;
371 }
372
382 public static function fetchRevision( LinkTarget $title ) {
385 [
386 'rev_id=page_latest',
387 'page_namespace' => $title->getNamespace(),
388 'page_title' => $title->getDBkey()
389 ]
390 );
391
392 return new FakeResultWrapper( $row ? [ $row ] : [] );
393 }
394
405 private static function fetchFromConds( $db, $conditions, $flags = 0 ) {
406 $fields = array_merge(
407 self::selectFields(),
408 self::selectPageFields(),
409 self::selectUserFields()
410 );
411 $options = [];
412 if ( ( $flags & self::READ_LOCKING ) == self::READ_LOCKING ) {
413 $options[] = 'FOR UPDATE';
414 }
415 return $db->selectRow(
416 [ 'revision', 'page', 'user' ],
417 $fields,
418 $conditions,
419 __METHOD__,
420 $options,
421 [ 'page' => self::pageJoinCond(), 'user' => self::userJoinCond() ]
422 );
423 }
424
431 public static function userJoinCond() {
432 return [ 'LEFT JOIN', [ 'rev_user != 0', 'user_id = rev_user' ] ];
433 }
434
441 public static function pageJoinCond() {
442 return [ 'INNER JOIN', [ 'page_id = rev_page' ] ];
443 }
444
452 public static function selectFields() {
454
455 $fields = [
456 'rev_id',
457 'rev_page',
458 'rev_text_id',
459 'rev_timestamp',
460 'rev_user_text',
461 'rev_user',
462 'rev_minor_edit',
463 'rev_deleted',
464 'rev_len',
465 'rev_parent_id',
466 'rev_sha1',
467 ];
468
469 $fields += CommentStore::newKey( 'rev_comment' )->getFields();
470
472 $fields[] = 'rev_content_format';
473 $fields[] = 'rev_content_model';
474 }
475
476 return $fields;
477 }
478
486 public static function selectArchiveFields() {
488 $fields = [
489 'ar_id',
490 'ar_page_id',
491 'ar_rev_id',
492 'ar_text',
493 'ar_text_id',
494 'ar_timestamp',
495 'ar_user_text',
496 'ar_user',
497 'ar_minor_edit',
498 'ar_deleted',
499 'ar_len',
500 'ar_parent_id',
501 'ar_sha1',
502 ];
503
504 $fields += CommentStore::newKey( 'ar_comment' )->getFields();
505
507 $fields[] = 'ar_content_format';
508 $fields[] = 'ar_content_model';
509 }
510 return $fields;
511 }
512
518 public static function selectTextFields() {
519 return [
520 'old_text',
521 'old_flags'
522 ];
523 }
524
529 public static function selectPageFields() {
530 return [
531 'page_namespace',
532 'page_title',
533 'page_id',
534 'page_latest',
535 'page_is_redirect',
536 'page_len',
537 ];
538 }
539
544 public static function selectUserFields() {
545 return [ 'user_name' ];
546 }
547
554 public static function getParentLengths( $db, array $revIds ) {
555 $revLens = [];
556 if ( !$revIds ) {
557 return $revLens; // empty
558 }
559 $res = $db->select( 'revision',
560 [ 'rev_id', 'rev_len' ],
561 [ 'rev_id' => $revIds ],
562 __METHOD__ );
563 foreach ( $res as $row ) {
564 $revLens[$row->rev_id] = $row->rev_len;
565 }
566 return $revLens;
567 }
568
574 function __construct( $row ) {
575 if ( is_object( $row ) ) {
576 $this->mId = intval( $row->rev_id );
577 $this->mPage = intval( $row->rev_page );
578 $this->mTextId = intval( $row->rev_text_id );
579 $this->mComment = CommentStore::newKey( 'rev_comment' )
580 // Legacy because $row probably came from self::selectFields()
581 ->getCommentLegacy( wfGetDB( DB_REPLICA ), $row, true )->text;
582 $this->mUser = intval( $row->rev_user );
583 $this->mMinorEdit = intval( $row->rev_minor_edit );
584 $this->mTimestamp = $row->rev_timestamp;
585 $this->mDeleted = intval( $row->rev_deleted );
586
587 if ( !isset( $row->rev_parent_id ) ) {
588 $this->mParentId = null;
589 } else {
590 $this->mParentId = intval( $row->rev_parent_id );
591 }
592
593 if ( !isset( $row->rev_len ) ) {
594 $this->mSize = null;
595 } else {
596 $this->mSize = intval( $row->rev_len );
597 }
598
599 if ( !isset( $row->rev_sha1 ) ) {
600 $this->mSha1 = null;
601 } else {
602 $this->mSha1 = $row->rev_sha1;
603 }
604
605 if ( isset( $row->page_latest ) ) {
606 $this->mCurrent = ( $row->rev_id == $row->page_latest );
607 $this->mTitle = Title::newFromRow( $row );
608 } else {
609 $this->mCurrent = false;
610 $this->mTitle = null;
611 }
612
613 if ( !isset( $row->rev_content_model ) ) {
614 $this->mContentModel = null; # determine on demand if needed
615 } else {
616 $this->mContentModel = strval( $row->rev_content_model );
617 }
618
619 if ( !isset( $row->rev_content_format ) ) {
620 $this->mContentFormat = null; # determine on demand if needed
621 } else {
622 $this->mContentFormat = strval( $row->rev_content_format );
623 }
624
625 // Lazy extraction...
626 $this->mText = null;
627 if ( isset( $row->old_text ) ) {
628 $this->mTextRow = $row;
629 } else {
630 // 'text' table row entry will be lazy-loaded
631 $this->mTextRow = null;
632 }
633
634 // Use user_name for users and rev_user_text for IPs...
635 $this->mUserText = null; // lazy load if left null
636 if ( $this->mUser == 0 ) {
637 $this->mUserText = $row->rev_user_text; // IP user
638 } elseif ( isset( $row->user_name ) ) {
639 $this->mUserText = $row->user_name; // logged-in user
640 }
641 $this->mOrigUserText = $row->rev_user_text;
642 } elseif ( is_array( $row ) ) {
643 // Build a new revision to be saved...
644 global $wgUser; // ugh
645
646 # if we have a content object, use it to set the model and type
647 if ( !empty( $row['content'] ) ) {
648 // @todo when is that set? test with external store setup! check out insertOn() [dk]
649 if ( !empty( $row['text_id'] ) ) {
650 throw new MWException( "Text already stored in external store (id {$row['text_id']}), " .
651 "can't serialize content object" );
652 }
653
654 $row['content_model'] = $row['content']->getModel();
655 # note: mContentFormat is initializes later accordingly
656 # note: content is serialized later in this method!
657 # also set text to null?
658 }
659
660 $this->mId = isset( $row['id'] ) ? intval( $row['id'] ) : null;
661 $this->mPage = isset( $row['page'] ) ? intval( $row['page'] ) : null;
662 $this->mTextId = isset( $row['text_id'] ) ? intval( $row['text_id'] ) : null;
663 $this->mUserText = isset( $row['user_text'] )
664 ? strval( $row['user_text'] ) : $wgUser->getName();
665 $this->mUser = isset( $row['user'] ) ? intval( $row['user'] ) : $wgUser->getId();
666 $this->mMinorEdit = isset( $row['minor_edit'] ) ? intval( $row['minor_edit'] ) : 0;
667 $this->mTimestamp = isset( $row['timestamp'] )
668 ? strval( $row['timestamp'] ) : wfTimestampNow();
669 $this->mDeleted = isset( $row['deleted'] ) ? intval( $row['deleted'] ) : 0;
670 $this->mSize = isset( $row['len'] ) ? intval( $row['len'] ) : null;
671 $this->mParentId = isset( $row['parent_id'] ) ? intval( $row['parent_id'] ) : null;
672 $this->mSha1 = isset( $row['sha1'] ) ? strval( $row['sha1'] ) : null;
673
674 $this->mContentModel = isset( $row['content_model'] )
675 ? strval( $row['content_model'] ) : null;
676 $this->mContentFormat = isset( $row['content_format'] )
677 ? strval( $row['content_format'] ) : null;
678
679 // Enforce spacing trimming on supplied text
680 $this->mComment = isset( $row['comment'] ) ? trim( strval( $row['comment'] ) ) : null;
681 $this->mText = isset( $row['text'] ) ? rtrim( strval( $row['text'] ) ) : null;
682 $this->mTextRow = null;
683
684 $this->mTitle = isset( $row['title'] ) ? $row['title'] : null;
685
686 // if we have a Content object, override mText and mContentModel
687 if ( !empty( $row['content'] ) ) {
688 if ( !( $row['content'] instanceof Content ) ) {
689 throw new MWException( '`content` field must contain a Content object.' );
690 }
691
692 $handler = $this->getContentHandler();
693 $this->mContent = $row['content'];
694
695 $this->mContentModel = $this->mContent->getModel();
696 $this->mContentHandler = null;
697
698 $this->mText = $handler->serializeContent( $row['content'], $this->getContentFormat() );
699 } elseif ( $this->mText !== null ) {
700 $handler = $this->getContentHandler();
701 $this->mContent = $handler->unserializeContent( $this->mText );
702 }
703
704 // If we have a Title object, make sure it is consistent with mPage.
705 if ( $this->mTitle && $this->mTitle->exists() ) {
706 if ( $this->mPage === null ) {
707 // if the page ID wasn't known, set it now
708 $this->mPage = $this->mTitle->getArticleID();
709 } elseif ( $this->mTitle->getArticleID() !== $this->mPage ) {
710 // Got different page IDs. This may be legit (e.g. during undeletion),
711 // but it seems worth mentioning it in the log.
712 wfDebug( "Page ID " . $this->mPage . " mismatches the ID " .
713 $this->mTitle->getArticleID() . " provided by the Title object." );
714 }
715 }
716
717 $this->mCurrent = false;
718
719 // If we still have no length, see it we have the text to figure it out
720 if ( !$this->mSize && $this->mContent !== null ) {
721 $this->mSize = $this->mContent->getSize();
722 }
723
724 // Same for sha1
725 if ( $this->mSha1 === null ) {
726 $this->mSha1 = $this->mText === null ? null : self::base36Sha1( $this->mText );
727 }
728
729 // force lazy init
730 $this->getContentModel();
731 $this->getContentFormat();
732 } else {
733 throw new MWException( 'Revision constructor passed invalid row format.' );
734 }
735 $this->mUnpatrolled = null;
736 }
737
743 public function getId() {
744 return $this->mId;
745 }
746
755 public function setId( $id ) {
756 $this->mId = (int)$id;
757 }
758
768 public function setUserIdAndName( $id, $name ) {
769 $this->mUser = (int)$id;
770 $this->mUserText = $name;
771 $this->mOrigUserText = $name;
772 }
773
779 public function getTextId() {
780 return $this->mTextId;
781 }
782
788 public function getParentId() {
789 return $this->mParentId;
790 }
791
797 public function getSize() {
798 return $this->mSize;
799 }
800
806 public function getSha1() {
807 return $this->mSha1;
808 }
809
817 public function getTitle() {
818 if ( $this->mTitle !== null ) {
819 return $this->mTitle;
820 }
821 // rev_id is defined as NOT NULL, but this revision may not yet have been inserted.
822 if ( $this->mId !== null ) {
823 $dbr = wfGetLB( $this->mWiki )->getConnectionRef( DB_REPLICA, [], $this->mWiki );
824 $row = $dbr->selectRow(
825 [ 'page', 'revision' ],
826 self::selectPageFields(),
827 [ 'page_id=rev_page', 'rev_id' => $this->mId ],
828 __METHOD__
829 );
830 if ( $row ) {
831 // @TODO: better foreign title handling
832 $this->mTitle = Title::newFromRow( $row );
833 }
834 }
835
836 if ( $this->mWiki === false || $this->mWiki === wfWikiID() ) {
837 // Loading by ID is best, though not possible for foreign titles
838 if ( !$this->mTitle && $this->mPage !== null && $this->mPage > 0 ) {
839 $this->mTitle = Title::newFromID( $this->mPage );
840 }
841 }
842
843 return $this->mTitle;
844 }
845
851 public function setTitle( $title ) {
852 $this->mTitle = $title;
853 }
854
860 public function getPage() {
861 return $this->mPage;
862 }
863
877 public function getUser( $audience = self::FOR_PUBLIC, User $user = null ) {
878 if ( $audience == self::FOR_PUBLIC && $this->isDeleted( self::DELETED_USER ) ) {
879 return 0;
880 } elseif ( $audience == self::FOR_THIS_USER && !$this->userCan( self::DELETED_USER, $user ) ) {
881 return 0;
882 } else {
883 return $this->mUser;
884 }
885 }
886
893 public function getRawUser() {
894 wfDeprecated( __METHOD__, '1.25' );
895 return $this->getUser( self::RAW );
896 }
897
911 public function getUserText( $audience = self::FOR_PUBLIC, User $user = null ) {
912 $this->loadMutableFields();
913
914 if ( $audience == self::FOR_PUBLIC && $this->isDeleted( self::DELETED_USER ) ) {
915 return '';
916 } elseif ( $audience == self::FOR_THIS_USER && !$this->userCan( self::DELETED_USER, $user ) ) {
917 return '';
918 } else {
919 if ( $this->mUserText === null ) {
920 $this->mUserText = User::whoIs( $this->mUser ); // load on demand
921 if ( $this->mUserText === false ) {
922 # This shouldn't happen, but it can if the wiki was recovered
923 # via importing revs and there is no user table entry yet.
924 $this->mUserText = $this->mOrigUserText;
925 }
926 }
927 return $this->mUserText;
928 }
929 }
930
937 public function getRawUserText() {
938 wfDeprecated( __METHOD__, '1.25' );
939 return $this->getUserText( self::RAW );
940 }
941
955 function getComment( $audience = self::FOR_PUBLIC, User $user = null ) {
956 if ( $audience == self::FOR_PUBLIC && $this->isDeleted( self::DELETED_COMMENT ) ) {
957 return '';
958 } elseif ( $audience == self::FOR_THIS_USER && !$this->userCan( self::DELETED_COMMENT, $user ) ) {
959 return '';
960 } else {
961 return $this->mComment;
962 }
963 }
964
971 public function getRawComment() {
972 wfDeprecated( __METHOD__, '1.25' );
973 return $this->getComment( self::RAW );
974 }
975
979 public function isMinor() {
980 return (bool)$this->mMinorEdit;
981 }
982
986 public function isUnpatrolled() {
987 if ( $this->mUnpatrolled !== null ) {
988 return $this->mUnpatrolled;
989 }
990 $rc = $this->getRecentChange();
991 if ( $rc && $rc->getAttribute( 'rc_patrolled' ) == 0 ) {
992 $this->mUnpatrolled = $rc->getAttribute( 'rc_id' );
993 } else {
994 $this->mUnpatrolled = 0;
995 }
996 return $this->mUnpatrolled;
997 }
998
1008 public function getRecentChange( $flags = 0 ) {
1009 $dbr = wfGetDB( DB_REPLICA );
1010
1012
1014 [
1015 'rc_user_text' => $this->getUserText( self::RAW ),
1016 'rc_timestamp' => $dbr->timestamp( $this->getTimestamp() ),
1017 'rc_this_oldid' => $this->getId()
1018 ],
1019 __METHOD__,
1020 $dbType
1021 );
1022 }
1023
1029 public function isDeleted( $field ) {
1030 if ( $this->isCurrent() && $field === self::DELETED_TEXT ) {
1031 // Current revisions of pages cannot have the content hidden. Skipping this
1032 // check is very useful for Parser as it fetches templates using newKnownCurrent().
1033 // Calling getVisibility() in that case triggers a verification database query.
1034 return false; // no need to check
1035 }
1036
1037 return ( $this->getVisibility() & $field ) == $field;
1038 }
1039
1045 public function getVisibility() {
1046 $this->loadMutableFields();
1047
1048 return (int)$this->mDeleted;
1049 }
1050
1065 public function getContent( $audience = self::FOR_PUBLIC, User $user = null ) {
1066 if ( $audience == self::FOR_PUBLIC && $this->isDeleted( self::DELETED_TEXT ) ) {
1067 return null;
1068 } elseif ( $audience == self::FOR_THIS_USER && !$this->userCan( self::DELETED_TEXT, $user ) ) {
1069 return null;
1070 } else {
1071 return $this->getContentInternal();
1072 }
1073 }
1074
1081 public function getSerializedData() {
1082 if ( $this->mText === null ) {
1083 // Revision is immutable. Load on demand.
1084 $this->mText = $this->loadText();
1085 }
1086
1087 return $this->mText;
1088 }
1089
1099 protected function getContentInternal() {
1100 if ( $this->mContent === null ) {
1101 $text = $this->getSerializedData();
1102
1103 if ( $text !== null && $text !== false ) {
1104 // Unserialize content
1105 $handler = $this->getContentHandler();
1106 $format = $this->getContentFormat();
1107
1108 $this->mContent = $handler->unserializeContent( $text, $format );
1109 }
1110 }
1111
1112 // NOTE: copy() will return $this for immutable content objects
1113 return $this->mContent ? $this->mContent->copy() : null;
1114 }
1115
1126 public function getContentModel() {
1127 if ( !$this->mContentModel ) {
1128 $title = $this->getTitle();
1129 if ( $title ) {
1130 $this->mContentModel = ContentHandler::getDefaultModelFor( $title );
1131 } else {
1132 $this->mContentModel = CONTENT_MODEL_WIKITEXT;
1133 }
1134
1135 assert( !empty( $this->mContentModel ) );
1136 }
1137
1138 return $this->mContentModel;
1139 }
1140
1150 public function getContentFormat() {
1151 if ( !$this->mContentFormat ) {
1152 $handler = $this->getContentHandler();
1153 $this->mContentFormat = $handler->getDefaultFormat();
1154
1155 assert( !empty( $this->mContentFormat ) );
1156 }
1157
1158 return $this->mContentFormat;
1159 }
1160
1167 public function getContentHandler() {
1168 if ( !$this->mContentHandler ) {
1169 $model = $this->getContentModel();
1170 $this->mContentHandler = ContentHandler::getForModelID( $model );
1171
1172 $format = $this->getContentFormat();
1173
1174 if ( !$this->mContentHandler->isSupportedFormat( $format ) ) {
1175 throw new MWException( "Oops, the content format $format is not supported for "
1176 . "this content model, $model" );
1177 }
1178 }
1179
1181 }
1182
1186 public function getTimestamp() {
1187 return wfTimestamp( TS_MW, $this->mTimestamp );
1188 }
1189
1193 public function isCurrent() {
1194 return $this->mCurrent;
1195 }
1196
1202 public function getPrevious() {
1203 if ( $this->getTitle() ) {
1204 $prev = $this->getTitle()->getPreviousRevisionID( $this->getId() );
1205 if ( $prev ) {
1206 return self::newFromTitle( $this->getTitle(), $prev );
1207 }
1208 }
1209 return null;
1210 }
1211
1217 public function getNext() {
1218 if ( $this->getTitle() ) {
1219 $next = $this->getTitle()->getNextRevisionID( $this->getId() );
1220 if ( $next ) {
1221 return self::newFromTitle( $this->getTitle(), $next );
1222 }
1223 }
1224 return null;
1225 }
1226
1234 private function getPreviousRevisionId( $db ) {
1235 if ( $this->mPage === null ) {
1236 return 0;
1237 }
1238 # Use page_latest if ID is not given
1239 if ( !$this->mId ) {
1240 $prevId = $db->selectField( 'page', 'page_latest',
1241 [ 'page_id' => $this->mPage ],
1242 __METHOD__ );
1243 } else {
1244 $prevId = $db->selectField( 'revision', 'rev_id',
1245 [ 'rev_page' => $this->mPage, 'rev_id < ' . $this->mId ],
1246 __METHOD__,
1247 [ 'ORDER BY' => 'rev_id DESC' ] );
1248 }
1249 return intval( $prevId );
1250 }
1251
1266 public static function getRevisionText( $row, $prefix = 'old_', $wiki = false ) {
1267 $textField = $prefix . 'text';
1268 $flagsField = $prefix . 'flags';
1269
1270 if ( isset( $row->$flagsField ) ) {
1271 $flags = explode( ',', $row->$flagsField );
1272 } else {
1273 $flags = [];
1274 }
1275
1276 if ( isset( $row->$textField ) ) {
1277 $text = $row->$textField;
1278 } else {
1279 return false;
1280 }
1281
1282 // Use external methods for external objects, text in table is URL-only then
1283 if ( in_array( 'external', $flags ) ) {
1284 $url = $text;
1285 $parts = explode( '://', $url, 2 );
1286 if ( count( $parts ) == 1 || $parts[1] == '' ) {
1287 return false;
1288 }
1289
1290 if ( isset( $row->old_id ) && $wiki === false ) {
1291 // Make use of the wiki-local revision text cache
1292 $cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
1293 // The cached value should be decompressed, so handle that and return here
1294 return $cache->getWithSetCallback(
1295 $cache->makeKey( 'revisiontext', 'textid', $row->old_id ),
1296 self::getCacheTTL( $cache ),
1297 function () use ( $url, $wiki, $flags ) {
1298 // No negative caching per Revision::loadText()
1299 $text = ExternalStore::fetchFromURL( $url, [ 'wiki' => $wiki ] );
1300
1301 return self::decompressRevisionText( $text, $flags );
1302 },
1303 [ 'pcGroup' => self::TEXT_CACHE_GROUP, 'pcTTL' => $cache::TTL_PROC_LONG ]
1304 );
1305 } else {
1306 $text = ExternalStore::fetchFromURL( $url, [ 'wiki' => $wiki ] );
1307 }
1308 }
1309
1310 return self::decompressRevisionText( $text, $flags );
1311 }
1312
1323 public static function compressRevisionText( &$text ) {
1325 $flags = [];
1326
1327 # Revisions not marked this way will be converted
1328 # on load if $wgLegacyCharset is set in the future.
1329 $flags[] = 'utf-8';
1330
1331 if ( $wgCompressRevisions ) {
1332 if ( function_exists( 'gzdeflate' ) ) {
1333 $deflated = gzdeflate( $text );
1334
1335 if ( $deflated === false ) {
1336 wfLogWarning( __METHOD__ . ': gzdeflate() failed' );
1337 } else {
1338 $text = $deflated;
1339 $flags[] = 'gzip';
1340 }
1341 } else {
1342 wfDebug( __METHOD__ . " -- no zlib support, not compressing\n" );
1343 }
1344 }
1345 return implode( ',', $flags );
1346 }
1347
1355 public static function decompressRevisionText( $text, $flags ) {
1357
1358 if ( $text === false ) {
1359 // Text failed to be fetched; nothing to do
1360 return false;
1361 }
1362
1363 if ( in_array( 'gzip', $flags ) ) {
1364 # Deal with optional compression of archived pages.
1365 # This can be done periodically via maintenance/compressOld.php, and
1366 # as pages are saved if $wgCompressRevisions is set.
1367 $text = gzinflate( $text );
1368
1369 if ( $text === false ) {
1370 wfLogWarning( __METHOD__ . ': gzinflate() failed' );
1371 return false;
1372 }
1373 }
1374
1375 if ( in_array( 'object', $flags ) ) {
1376 # Generic compressed storage
1377 $obj = unserialize( $text );
1378 if ( !is_object( $obj ) ) {
1379 // Invalid object
1380 return false;
1381 }
1382 $text = $obj->getText();
1383 }
1384
1385 if ( $text !== false && $wgLegacyEncoding
1386 && !in_array( 'utf-8', $flags ) && !in_array( 'utf8', $flags )
1387 ) {
1388 # Old revisions kept around in a legacy encoding?
1389 # Upconvert on demand.
1390 # ("utf8" checked for compatibility with some broken
1391 # conversion scripts 2008-12-30)
1392 $text = $wgContLang->iconv( $wgLegacyEncoding, 'UTF-8', $text );
1393 }
1394
1395 return $text;
1396 }
1397
1406 public function insertOn( $dbw ) {
1408
1409 // We're inserting a new revision, so we have to use master anyway.
1410 // If it's a null revision, it may have references to rows that
1411 // are not in the replica yet (the text row).
1412 $this->mQueryFlags |= self::READ_LATEST;
1413
1414 // Not allowed to have rev_page equal to 0, false, etc.
1415 if ( !$this->mPage ) {
1416 $title = $this->getTitle();
1417 if ( $title instanceof Title ) {
1418 $titleText = ' for page ' . $title->getPrefixedText();
1419 } else {
1420 $titleText = '';
1421 }
1422 throw new MWException( "Cannot insert revision$titleText: page ID must be nonzero" );
1423 }
1424
1425 $this->checkContentModel();
1426
1427 $data = $this->mText;
1429
1430 # Write to external storage if required
1432 // Store and get the URL
1433 $data = ExternalStore::insertToDefault( $data );
1434 if ( !$data ) {
1435 throw new MWException( "Unable to store text to external storage" );
1436 }
1437 if ( $flags ) {
1438 $flags .= ',';
1439 }
1440 $flags .= 'external';
1441 }
1442
1443 # Record the text (or external storage URL) to the text table
1444 if ( $this->mTextId === null ) {
1445 $dbw->insert( 'text',
1446 [
1447 'old_text' => $data,
1448 'old_flags' => $flags,
1449 ], __METHOD__
1450 );
1451 $this->mTextId = $dbw->insertId();
1452 }
1453
1454 if ( $this->mComment === null ) {
1455 $this->mComment = "";
1456 }
1457
1458 # Record the edit in revisions
1459 $row = [
1460 'rev_page' => $this->mPage,
1461 'rev_text_id' => $this->mTextId,
1462 'rev_minor_edit' => $this->mMinorEdit ? 1 : 0,
1463 'rev_user' => $this->mUser,
1464 'rev_user_text' => $this->mUserText,
1465 'rev_timestamp' => $dbw->timestamp( $this->mTimestamp ),
1466 'rev_deleted' => $this->mDeleted,
1467 'rev_len' => $this->mSize,
1468 'rev_parent_id' => $this->mParentId === null
1469 ? $this->getPreviousRevisionId( $dbw )
1471 'rev_sha1' => $this->mSha1 === null
1472 ? self::base36Sha1( $this->mText )
1473 : $this->mSha1,
1474 ];
1475 if ( $this->mId !== null ) {
1476 $row['rev_id'] = $this->mId;
1477 }
1478
1479 list( $commentFields, $commentCallback ) =
1480 CommentStore::newKey( 'rev_comment' )->insertWithTempTable( $dbw, $this->mComment );
1481 $row += $commentFields;
1482
1483 if ( $wgContentHandlerUseDB ) {
1484 // NOTE: Store null for the default model and format, to save space.
1485 // XXX: Makes the DB sensitive to changed defaults.
1486 // Make this behavior optional? Only in miser mode?
1487
1488 $model = $this->getContentModel();
1489 $format = $this->getContentFormat();
1490
1491 $title = $this->getTitle();
1492
1493 if ( $title === null ) {
1494 throw new MWException( "Insufficient information to determine the title of the "
1495 . "revision's page!" );
1496 }
1497
1498 $defaultModel = ContentHandler::getDefaultModelFor( $title );
1499 $defaultFormat = ContentHandler::getForModelID( $defaultModel )->getDefaultFormat();
1500
1501 $row['rev_content_model'] = ( $model === $defaultModel ) ? null : $model;
1502 $row['rev_content_format'] = ( $format === $defaultFormat ) ? null : $format;
1503 }
1504
1505 $dbw->insert( 'revision', $row, __METHOD__ );
1506
1507 if ( $this->mId === null ) {
1508 // Only if auto-increment was used
1509 $this->mId = $dbw->insertId();
1510 }
1511 $commentCallback( $this->mId );
1512
1513 // Assertion to try to catch T92046
1514 if ( (int)$this->mId === 0 ) {
1515 throw new UnexpectedValueException(
1516 'After insert, Revision mId is ' . var_export( $this->mId, 1 ) . ': ' .
1517 var_export( $row, 1 )
1518 );
1519 }
1520
1521 // Insert IP revision into ip_changes for use when querying for a range.
1522 if ( $this->mUser === 0 && IP::isValid( $this->mUserText ) ) {
1523 $ipcRow = [
1524 'ipc_rev_id' => $this->mId,
1525 'ipc_rev_timestamp' => $row['rev_timestamp'],
1526 'ipc_hex' => IP::toHex( $row['rev_user_text'] ),
1527 ];
1528 $dbw->insert( 'ip_changes', $ipcRow, __METHOD__ );
1529 }
1530
1531 // Avoid PHP 7.1 warning of passing $this by reference
1532 $revision = $this;
1533 Hooks::run( 'RevisionInsertComplete', [ &$revision, $data, $flags ] );
1534
1535 return $this->mId;
1536 }
1537
1538 protected function checkContentModel() {
1540
1541 // Note: may return null for revisions that have not yet been inserted
1542 $title = $this->getTitle();
1543
1544 $model = $this->getContentModel();
1545 $format = $this->getContentFormat();
1546 $handler = $this->getContentHandler();
1547
1548 if ( !$handler->isSupportedFormat( $format ) ) {
1549 $t = $title->getPrefixedDBkey();
1550
1551 throw new MWException( "Can't use format $format with content model $model on $t" );
1552 }
1553
1554 if ( !$wgContentHandlerUseDB && $title ) {
1555 // if $wgContentHandlerUseDB is not set,
1556 // all revisions must use the default content model and format.
1557
1558 $defaultModel = ContentHandler::getDefaultModelFor( $title );
1559 $defaultHandler = ContentHandler::getForModelID( $defaultModel );
1560 $defaultFormat = $defaultHandler->getDefaultFormat();
1561
1562 if ( $this->getContentModel() != $defaultModel ) {
1563 $t = $title->getPrefixedDBkey();
1564
1565 throw new MWException( "Can't save non-default content model with "
1566 . "\$wgContentHandlerUseDB disabled: model is $model, "
1567 . "default for $t is $defaultModel" );
1568 }
1569
1570 if ( $this->getContentFormat() != $defaultFormat ) {
1571 $t = $title->getPrefixedDBkey();
1572
1573 throw new MWException( "Can't use non-default content format with "
1574 . "\$wgContentHandlerUseDB disabled: format is $format, "
1575 . "default for $t is $defaultFormat" );
1576 }
1577 }
1578
1579 $content = $this->getContent( self::RAW );
1580 $prefixedDBkey = $title->getPrefixedDBkey();
1581 $revId = $this->mId;
1582
1583 if ( !$content ) {
1584 throw new MWException(
1585 "Content of revision $revId ($prefixedDBkey) could not be loaded for validation!"
1586 );
1587 }
1588 if ( !$content->isValid() ) {
1589 throw new MWException(
1590 "Content of revision $revId ($prefixedDBkey) is not valid! Content model is $model"
1591 );
1592 }
1593 }
1594
1600 public static function base36Sha1( $text ) {
1601 return Wikimedia\base_convert( sha1( $text ), 16, 36, 31 );
1602 }
1603
1610 private static function getCacheTTL( WANObjectCache $cache ) {
1612
1613 if ( $cache->getQoS( $cache::ATTR_EMULATION ) <= $cache::QOS_EMULATION_SQL ) {
1614 // Do not cache RDBMs blobs in...the RDBMs store
1615 $ttl = $cache::TTL_UNCACHEABLE;
1616 } else {
1617 $ttl = $wgRevisionCacheExpiry ?: $cache::TTL_UNCACHEABLE;
1618 }
1619
1620 return $ttl;
1621 }
1622
1629 private function loadText() {
1630 $cache = ObjectCache::getMainWANInstance();
1631
1632 // No negative caching; negative hits on text rows may be due to corrupted replica DBs
1633 return $cache->getWithSetCallback(
1634 $cache->makeKey( 'revisiontext', 'textid', $this->getTextId() ),
1635 self::getCacheTTL( $cache ),
1636 function () {
1637 return $this->fetchText();
1638 },
1639 [ 'pcGroup' => self::TEXT_CACHE_GROUP, 'pcTTL' => $cache::TTL_PROC_LONG ]
1640 );
1641 }
1642
1643 private function fetchText() {
1644 $textId = $this->getTextId();
1645
1646 // If we kept data for lazy extraction, use it now...
1647 if ( $this->mTextRow !== null ) {
1648 $row = $this->mTextRow;
1649 $this->mTextRow = null;
1650 } else {
1651 $row = null;
1652 }
1653
1654 // Callers doing updates will pass in READ_LATEST as usual. Since the text/blob tables
1655 // do not normally get rows changed around, set READ_LATEST_IMMUTABLE in those cases.
1657 $flags |= DBAccessObjectUtils::hasFlags( $flags, self::READ_LATEST )
1658 ? self::READ_LATEST_IMMUTABLE
1659 : 0;
1660
1661 list( $index, $options, $fallbackIndex, $fallbackOptions ) =
1663
1664 if ( !$row ) {
1665 // Text data is immutable; check replica DBs first.
1666 $row = wfGetDB( $index )->selectRow(
1667 'text',
1668 [ 'old_text', 'old_flags' ],
1669 [ 'old_id' => $textId ],
1670 __METHOD__,
1671 $options
1672 );
1673 }
1674
1675 // Fallback to DB_MASTER in some cases if the row was not found
1676 if ( !$row && $fallbackIndex !== null ) {
1677 // Use FOR UPDATE if it was used to fetch this revision. This avoids missing the row
1678 // due to REPEATABLE-READ. Also fallback to the master if READ_LATEST is provided.
1679 $row = wfGetDB( $fallbackIndex )->selectRow(
1680 'text',
1681 [ 'old_text', 'old_flags' ],
1682 [ 'old_id' => $textId ],
1683 __METHOD__,
1684 $fallbackOptions
1685 );
1686 }
1687
1688 if ( !$row ) {
1689 wfDebugLog( 'Revision', "No text row with ID '$textId' (revision {$this->getId()})." );
1690 }
1691
1692 $text = self::getRevisionText( $row );
1693 if ( $row && $text === false ) {
1694 wfDebugLog( 'Revision', "No blob for text row '$textId' (revision {$this->getId()})." );
1695 }
1696
1697 return is_string( $text ) ? $text : false;
1698 }
1699
1715 public static function newNullRevision( $dbw, $pageId, $summary, $minor, $user = null ) {
1717
1718 $fields = [ 'page_latest', 'page_namespace', 'page_title',
1719 'rev_text_id', 'rev_len', 'rev_sha1' ];
1720
1721 if ( $wgContentHandlerUseDB ) {
1722 $fields[] = 'rev_content_model';
1723 $fields[] = 'rev_content_format';
1724 }
1725
1726 $current = $dbw->selectRow(
1727 [ 'page', 'revision' ],
1728 $fields,
1729 [
1730 'page_id' => $pageId,
1731 'page_latest=rev_id',
1732 ],
1733 __METHOD__,
1734 [ 'FOR UPDATE' ] // T51581
1735 );
1736
1737 if ( $current ) {
1738 if ( !$user ) {
1740 $user = $wgUser;
1741 }
1742
1743 $row = [
1744 'page' => $pageId,
1745 'user_text' => $user->getName(),
1746 'user' => $user->getId(),
1747 'comment' => $summary,
1748 'minor_edit' => $minor,
1749 'text_id' => $current->rev_text_id,
1750 'parent_id' => $current->page_latest,
1751 'len' => $current->rev_len,
1752 'sha1' => $current->rev_sha1
1753 ];
1754
1755 if ( $wgContentHandlerUseDB ) {
1756 $row['content_model'] = $current->rev_content_model;
1757 $row['content_format'] = $current->rev_content_format;
1758 }
1759
1760 $row['title'] = Title::makeTitle( $current->page_namespace, $current->page_title );
1761
1762 $revision = new Revision( $row );
1763 } else {
1764 $revision = null;
1765 }
1766
1767 return $revision;
1768 }
1769
1780 public function userCan( $field, User $user = null ) {
1781 return self::userCanBitfield( $this->getVisibility(), $field, $user );
1782 }
1783
1798 public static function userCanBitfield( $bitfield, $field, User $user = null,
1799 Title $title = null
1800 ) {
1801 if ( $bitfield & $field ) { // aspect is deleted
1802 if ( $user === null ) {
1804 $user = $wgUser;
1805 }
1806 if ( $bitfield & self::DELETED_RESTRICTED ) {
1807 $permissions = [ 'suppressrevision', 'viewsuppressed' ];
1808 } elseif ( $field & self::DELETED_TEXT ) {
1809 $permissions = [ 'deletedtext' ];
1810 } else {
1811 $permissions = [ 'deletedhistory' ];
1812 }
1813 $permissionlist = implode( ', ', $permissions );
1814 if ( $title === null ) {
1815 wfDebug( "Checking for $permissionlist due to $field match on $bitfield\n" );
1816 return call_user_func_array( [ $user, 'isAllowedAny' ], $permissions );
1817 } else {
1818 $text = $title->getPrefixedText();
1819 wfDebug( "Checking for $permissionlist on $text due to $field match on $bitfield\n" );
1820 foreach ( $permissions as $perm ) {
1821 if ( $title->userCan( $perm, $user ) ) {
1822 return true;
1823 }
1824 }
1825 return false;
1826 }
1827 } else {
1828 return true;
1829 }
1830 }
1831
1840 static function getTimestampFromId( $title, $id, $flags = 0 ) {
1841 $db = ( $flags & self::READ_LATEST )
1842 ? wfGetDB( DB_MASTER )
1843 : wfGetDB( DB_REPLICA );
1844 // Casting fix for databases that can't take '' for rev_id
1845 if ( $id == '' ) {
1846 $id = 0;
1847 }
1848 $conds = [ 'rev_id' => $id ];
1849 $conds['rev_page'] = $title->getArticleID();
1850 $timestamp = $db->selectField( 'revision', 'rev_timestamp', $conds, __METHOD__ );
1851
1852 return ( $timestamp !== false ) ? wfTimestamp( TS_MW, $timestamp ) : false;
1853 }
1854
1862 static function countByPageId( $db, $id ) {
1863 $row = $db->selectRow( 'revision', [ 'revCount' => 'COUNT(*)' ],
1864 [ 'rev_page' => $id ], __METHOD__ );
1865 if ( $row ) {
1866 return $row->revCount;
1867 }
1868 return 0;
1869 }
1870
1878 static function countByTitle( $db, $title ) {
1879 $id = $title->getArticleID();
1880 if ( $id ) {
1881 return self::countByPageId( $db, $id );
1882 }
1883 return 0;
1884 }
1885
1902 public static function userWasLastToEdit( $db, $pageId, $userId, $since ) {
1903 if ( !$userId ) {
1904 return false;
1905 }
1906
1907 if ( is_int( $db ) ) {
1908 $db = wfGetDB( $db );
1909 }
1910
1911 $res = $db->select( 'revision',
1912 'rev_user',
1913 [
1914 'rev_page' => $pageId,
1915 'rev_timestamp > ' . $db->addQuotes( $db->timestamp( $since ) )
1916 ],
1917 __METHOD__,
1918 [ 'ORDER BY' => 'rev_timestamp ASC', 'LIMIT' => 50 ] );
1919 foreach ( $res as $row ) {
1920 if ( $row->rev_user != $userId ) {
1921 return false;
1922 }
1923 }
1924 return true;
1925 }
1926
1940 public static function newKnownCurrent( IDatabase $db, $pageId, $revId ) {
1941 $cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
1942 return $cache->getWithSetCallback(
1943 // Page/rev IDs passed in from DB to reflect history merges
1944 $cache->makeGlobalKey( 'revision', $db->getDomainID(), $pageId, $revId ),
1945 $cache::TTL_WEEK,
1946 function ( $curValue, &$ttl, array &$setOpts ) use ( $db, $pageId, $revId ) {
1947 $setOpts += Database::getCacheSetOptions( $db );
1948
1949 $rev = Revision::loadFromPageId( $db, $pageId, $revId );
1950 // Reflect revision deletion and user renames
1951 if ( $rev ) {
1952 $rev->mTitle = null; // mutable; lazy-load
1953 $rev->mRefreshMutableFields = true;
1954 }
1955
1956 return $rev ?: false; // don't cache negatives
1957 }
1958 );
1959 }
1960
1964 private function loadMutableFields() {
1965 if ( !$this->mRefreshMutableFields ) {
1966 return; // not needed
1967 }
1968
1969 $this->mRefreshMutableFields = false;
1970 $dbr = wfGetLB( $this->mWiki )->getConnectionRef( DB_REPLICA, [], $this->mWiki );
1971 $row = $dbr->selectRow(
1972 [ 'revision', 'user' ],
1973 [ 'rev_deleted', 'user_name' ],
1974 [ 'rev_id' => $this->mId, 'user_id = rev_user' ],
1975 __METHOD__
1976 );
1977 if ( $row ) { // update values
1978 $this->mDeleted = (int)$row->rev_deleted;
1979 $this->mUserText = $row->user_name;
1980 }
1981 }
1982}
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.
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.
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.
wfWikiID()
Get an ASCII string identifying this wiki This is used as a prefix in memcached keys.
$wgUser
Definition Setup.php:817
static newKey( $key)
Static constructor for easier chaining.
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 getDBOptions( $bitfield)
Get an appropriate DB index, options, and fallback DB index for a query.
static hasFlags( $bitfield, $flags)
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.
MediaWikiServices is the service locator for the application scope of MediaWiki.
static newFromConds( $conds, $fname=__METHOD__, $dbType=DB_REPLICA)
Find the first recent change matching some specific conditions.
stdClass null $mTextRow
Definition Revision.php:66
bool $mCurrent
Definition Revision.php:71
static newKnownCurrent(IDatabase $db, $pageId, $revId)
Load a revision based on a known page ID and current revision ID from the DB.
getRecentChange( $flags=0)
Get the RC object belonging to the current revision, if there's one.
getUserText( $audience=self::FOR_PUBLIC, User $user=null)
Fetch revision's username if it's available to the specified audience.
Definition Revision.php:911
getTitle()
Returns the title of the page associated with this entry or null.
Definition Revision.php:817
static selectArchiveFields()
Return the list of revision fields that should be selected to create a new revision from an archive r...
Definition Revision.php:486
getPrevious()
Get previous revision for this title.
null ContentHandler $mContentHandler
Definition Revision.php:80
getSize()
Returns the length of the text in this revision, or null if unknown.
Definition Revision.php:797
loadMutableFields()
For cached revisions, make sure the user name and rev_deleted is up-to-date.
static getCacheTTL(WANObjectCache $cache)
Get the text cache TTL.
null Title $mTitle
Definition Revision.php:69
getContentInternal()
Gets the content object for the revision (or null on failure).
getSerializedData()
Get original serialized data (without checking view restrictions)
getId()
Get revision ID.
Definition Revision.php:743
int $mUnpatrolled
Definition Revision.php:63
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:893
static userJoinCond()
Return the value of a select() JOIN conds array for the user table.
Definition Revision.php:431
static decompressRevisionText( $text, $flags)
Re-converts revision text according to it's flags.
bool $mRefreshMutableFields
Used for cached values to reload user text and rev_deleted.
Definition Revision.php:85
setUserIdAndName( $id, $name)
Set the user ID/name.
Definition Revision.php:768
getContentHandler()
Returns the content handler appropriate for this revision's content model.
string $mComment
Definition Revision.php:57
static getRevisionText( $row, $prefix='old_', $wiki=false)
Get revision text associated with an old or archive row.
getComment( $audience=self::FOR_PUBLIC, User $user=null)
Fetch revision comment if it's available to the specified audience.
Definition Revision.php:955
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:405
string $mContentFormat
Definition Revision.php:75
static loadFromTitle( $db, $title, $id=0)
Load either the current, or a specified, revision that's attached to a given page.
Definition Revision.php:284
getNext()
Get next revision for this title.
getTextId()
Get text row ID.
Definition Revision.php:779
static selectTextFields()
Return the list of text fields that should be selected to read the revision text.
Definition Revision.php:518
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:165
static selectPageFields()
Return the list of page fields that should be selected from page table.
Definition Revision.php:529
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:452
static loadFromTimestamp( $db, $title, $timestamp)
Load the revision for the given title with the given timestamp.
Definition Revision.php:309
int $mUser
Definition Revision.php:43
string $mOrigUserText
Definition Revision.php:41
static loadFromConds( $db, $conditions, $flags=0)
Given a set of conditions, fetch a revision from the given database connection.
Definition Revision.php:361
getPage()
Get the page ID.
Definition Revision.php:860
static newFromRow( $row)
Definition Revision.php:238
int $mSize
Definition Revision.php:51
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:250
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:544
getContentModel()
Returns the content model for this revision.
int null $mId
Definition Revision.php:35
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:877
static newFromArchiveRow( $row, $overrides=[])
Make a fake revision object from an archive table row.
Definition Revision.php:189
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.
string $mContentModel
Definition Revision.php:73
static pageJoinCond()
Return the value of a select() page conds array for the page table.
Definition Revision.php:441
checkContentModel()
const DELETED_USER
Definition Revision.php:92
string $mUserText
Definition Revision.php:39
static newFromConds( $conditions, $flags=0)
Given a set of conditions, fetch a revision.
Definition Revision.php:329
const TEXT_CACHE_GROUP
Definition Revision.php:102
const DELETED_TEXT
Definition Revision.php:90
static base36Sha1( $text)
Get the base 36 SHA-1 value for a string of text.
int $mQueryFlags
Definition Revision.php:83
__construct( $row)
Definition Revision.php:574
getSha1()
Returns the base36 sha1 of the text in this revision, or null if unknown.
Definition Revision.php:806
const DELETED_RESTRICTED
Definition Revision.php:93
bool $mMinorEdit
Definition Revision.php:45
static loadFromPageId( $db, $pageid, $id=0)
Load either the current, or a specified, revision that's attached to a given page.
Definition Revision.php:264
getRawComment()
Fetch revision comment without regard for the current user's permissions.
Definition Revision.php:971
string $mTimestamp
Definition Revision.php:47
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.
setTitle( $title)
Set the title of the revision.
Definition Revision.php:851
int null $mPage
Definition Revision.php:37
string $mSha1
Definition Revision.php:53
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:78
string $mWiki
Wiki ID; false means the current wiki.
Definition Revision.php:87
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:937
static getParentLengths( $db, array $revIds)
Do a batched query to get the parent revision lengths.
Definition Revision.php:554
static fetchRevision(LinkTarget $title)
Return a wrapper for a series of database rows to fetch all of a given page's revisions in turn.
Definition Revision.php:382
isUnpatrolled()
Definition Revision.php:986
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:98
int $mParentId
Definition Revision.php:55
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:134
getParentId()
Get parent revision ID (the original previous page revision)
Definition Revision.php:788
int $mDeleted
Definition Revision.php:49
const SUPPRESSED_ALL
Definition Revision.php:95
string $mText
Definition Revision.php:59
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:755
const RAW
Definition Revision.php:100
isDeleted( $field)
const DELETED_COMMENT
Definition Revision.php:91
const FOR_THIS_USER
Definition Revision.php:99
static newFromId( $id, $flags=0)
Load a page revision from a given revision ID number.
Definition Revision.php:116
const SUPPRESSED_USER
Definition Revision.php:94
int $mTextId
Definition Revision.php:61
Represents a title within MediaWiki.
Definition Title.php:39
The User object encapsulates all of the user-specific settings (user_id, name, rights,...
Definition User.php:51
Multi-datacenter aware caching interface.
Relational database abstraction object.
Definition Database.php:45
Overloads the relevant methods of the real ResultsWrapper so it doesn't go anywhere near an actual da...
Result wrapper for grabbing data queried from an IDatabase object.
$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 function
Definition design.txt:94
when a variable name is used in a it is silently declared as a new local masking the global
Definition design.txt:95
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 CONTENT_MODEL_WIKITEXT
Definition Defines.php:236
the array() calling protocol came about after MediaWiki 1.4rc1.
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 & $options
Definition hooks.txt:1971
namespace and then decline to actually register it file or subcat img or subcat $title
Definition hooks.txt:962
it s the revision text itself In either if gzip is the revision text is gzipped $flags
Definition hooks.txt:2805
Allows to change the fields on the form that will be generated $name
Definition hooks.txt:302
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:1984
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:86
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:901
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:1760
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:247
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.
Basic database interface for live and lazy-loaded relation database handles.
Definition IDatabase.php:40
$cache
Definition mcc.php:33
const DB_REPLICA
Definition defines.php:25
const DB_MASTER
Definition defines.php:26