MediaWiki REL1_28
Revision.php
Go to the documentation of this file.
1<?php
24
28class Revision implements IDBAccessObject {
30 protected $mId;
32 protected $mPage;
34 protected $mUserText;
36 protected $mOrigUserText;
38 protected $mUser;
40 protected $mMinorEdit;
42 protected $mTimestamp;
44 protected $mDeleted;
46 protected $mSize;
48 protected $mSha1;
50 protected $mParentId;
52 protected $mComment;
54 protected $mText;
56 protected $mTextId;
58 protected $mUnpatrolled;
59
61 protected $mTextRow;
62
64 protected $mTitle;
66 protected $mCurrent;
68 protected $mContentModel;
70 protected $mContentFormat;
71
73 protected $mContent;
76
78 protected $mQueryFlags = 0;
80 protected $mRefreshMutableFields = false;
82 protected $mWiki = false;
83
84 // Revision deletion constants
85 const DELETED_TEXT = 1;
86 const DELETED_COMMENT = 2;
87 const DELETED_USER = 4;
89 const SUPPRESSED_USER = 12; // convenience
90
91 // Audience options for accessors
92 const FOR_PUBLIC = 1;
93 const FOR_THIS_USER = 2;
94 const RAW = 3;
95
96 const TEXT_CACHE_GROUP = 'revisiontext:10'; // process cache name and max key count
97
110 public static function newFromId( $id, $flags = 0 ) {
111 return self::newFromConds( [ 'rev_id' => intval( $id ) ], $flags );
112 }
113
128 public static function newFromTitle( LinkTarget $linkTarget, $id = 0, $flags = 0 ) {
129 $conds = [
130 'page_namespace' => $linkTarget->getNamespace(),
131 'page_title' => $linkTarget->getDBkey()
132 ];
133 if ( $id ) {
134 // Use the specified ID
135 $conds['rev_id'] = $id;
136 return self::newFromConds( $conds, $flags );
137 } else {
138 // Use a join to get the latest revision
139 $conds[] = 'rev_id=page_latest';
140 $db = wfGetDB( ( $flags & self::READ_LATEST ) ? DB_MASTER : DB_REPLICA );
141 return self::loadFromConds( $db, $conds, $flags );
142 }
143 }
144
159 public static function newFromPageId( $pageId, $revId = 0, $flags = 0 ) {
160 $conds = [ 'page_id' => $pageId ];
161 if ( $revId ) {
162 $conds['rev_id'] = $revId;
163 return self::newFromConds( $conds, $flags );
164 } else {
165 // Use a join to get the latest revision
166 $conds[] = 'rev_id = page_latest';
167 $db = wfGetDB( ( $flags & self::READ_LATEST ) ? DB_MASTER : DB_REPLICA );
168 return self::loadFromConds( $db, $conds, $flags );
169 }
170 }
171
183 public static function newFromArchiveRow( $row, $overrides = [] ) {
185
186 $attribs = $overrides + [
187 'page' => isset( $row->ar_page_id ) ? $row->ar_page_id : null,
188 'id' => isset( $row->ar_rev_id ) ? $row->ar_rev_id : null,
189 'comment' => $row->ar_comment,
190 'user' => $row->ar_user,
191 'user_text' => $row->ar_user_text,
192 'timestamp' => $row->ar_timestamp,
193 'minor_edit' => $row->ar_minor_edit,
194 'text_id' => isset( $row->ar_text_id ) ? $row->ar_text_id : null,
195 'deleted' => $row->ar_deleted,
196 'len' => $row->ar_len,
197 'sha1' => isset( $row->ar_sha1 ) ? $row->ar_sha1 : null,
198 'content_model' => isset( $row->ar_content_model ) ? $row->ar_content_model : null,
199 'content_format' => isset( $row->ar_content_format ) ? $row->ar_content_format : null,
200 ];
201
202 if ( !$wgContentHandlerUseDB ) {
203 unset( $attribs['content_model'] );
204 unset( $attribs['content_format'] );
205 }
206
207 if ( !isset( $attribs['title'] )
208 && isset( $row->ar_namespace )
209 && isset( $row->ar_title )
210 ) {
211 $attribs['title'] = Title::makeTitle( $row->ar_namespace, $row->ar_title );
212 }
213
214 if ( isset( $row->ar_text ) && !$row->ar_text_id ) {
215 // Pre-1.5 ar_text row
216 $attribs['text'] = self::getRevisionText( $row, 'ar_' );
217 if ( $attribs['text'] === false ) {
218 throw new MWException( 'Unable to load text from archive row (possibly bug 22624)' );
219 }
220 }
221 return new self( $attribs );
222 }
223
230 public static function newFromRow( $row ) {
231 return new self( $row );
232 }
233
242 public static function loadFromId( $db, $id ) {
243 return self::loadFromConds( $db, [ 'rev_id' => intval( $id ) ] );
244 }
245
256 public static function loadFromPageId( $db, $pageid, $id = 0 ) {
257 $conds = [ 'rev_page' => intval( $pageid ), 'page_id' => intval( $pageid ) ];
258 if ( $id ) {
259 $conds['rev_id'] = intval( $id );
260 } else {
261 $conds[] = 'rev_id=page_latest';
262 }
263 return self::loadFromConds( $db, $conds );
264 }
265
276 public static function loadFromTitle( $db, $title, $id = 0 ) {
277 if ( $id ) {
278 $matchId = intval( $id );
279 } else {
280 $matchId = 'page_latest';
281 }
282 return self::loadFromConds( $db,
283 [
284 "rev_id=$matchId",
285 'page_namespace' => $title->getNamespace(),
286 'page_title' => $title->getDBkey()
287 ]
288 );
289 }
290
301 public static function loadFromTimestamp( $db, $title, $timestamp ) {
302 return self::loadFromConds( $db,
303 [
304 'rev_timestamp' => $db->timestamp( $timestamp ),
305 'page_namespace' => $title->getNamespace(),
306 'page_title' => $title->getDBkey()
307 ]
308 );
309 }
310
321 private static function newFromConds( $conditions, $flags = 0 ) {
322 $db = wfGetDB( ( $flags & self::READ_LATEST ) ? DB_MASTER : DB_REPLICA );
323
324 $rev = self::loadFromConds( $db, $conditions, $flags );
325 // Make sure new pending/committed revision are visibile later on
326 // within web requests to certain avoid bugs like T93866 and T94407.
327 if ( !$rev
328 && !( $flags & self::READ_LATEST )
329 && wfGetLB()->getServerCount() > 1
330 && wfGetLB()->hasOrMadeRecentMasterChanges()
331 ) {
332 $flags = self::READ_LATEST;
333 $db = wfGetDB( DB_MASTER );
334 $rev = self::loadFromConds( $db, $conditions, $flags );
335 }
336
337 if ( $rev ) {
338 $rev->mQueryFlags = $flags;
339 }
340
341 return $rev;
342 }
343
353 private static function loadFromConds( $db, $conditions, $flags = 0 ) {
354 $row = self::fetchFromConds( $db, $conditions, $flags );
355 if ( $row ) {
356 $rev = new Revision( $row );
357 $rev->mWiki = $db->getWikiID();
358
359 return $rev;
360 }
361
362 return null;
363 }
364
374 public static function fetchRevision( LinkTarget $title ) {
377 [
378 'rev_id=page_latest',
379 'page_namespace' => $title->getNamespace(),
380 'page_title' => $title->getDBkey()
381 ]
382 );
383
384 return new FakeResultWrapper( $row ? [ $row ] : [] );
385 }
386
397 private static function fetchFromConds( $db, $conditions, $flags = 0 ) {
398 $fields = array_merge(
399 self::selectFields(),
400 self::selectPageFields(),
401 self::selectUserFields()
402 );
403 $options = [];
404 if ( ( $flags & self::READ_LOCKING ) == self::READ_LOCKING ) {
405 $options[] = 'FOR UPDATE';
406 }
407 return $db->selectRow(
408 [ 'revision', 'page', 'user' ],
409 $fields,
410 $conditions,
411 __METHOD__,
412 $options,
413 [ 'page' => self::pageJoinCond(), 'user' => self::userJoinCond() ]
414 );
415 }
416
423 public static function userJoinCond() {
424 return [ 'LEFT JOIN', [ 'rev_user != 0', 'user_id = rev_user' ] ];
425 }
426
433 public static function pageJoinCond() {
434 return [ 'INNER JOIN', [ 'page_id = rev_page' ] ];
435 }
436
442 public static function selectFields() {
444
445 $fields = [
446 'rev_id',
447 'rev_page',
448 'rev_text_id',
449 'rev_timestamp',
450 'rev_comment',
451 'rev_user_text',
452 'rev_user',
453 'rev_minor_edit',
454 'rev_deleted',
455 'rev_len',
456 'rev_parent_id',
457 'rev_sha1',
458 ];
459
461 $fields[] = 'rev_content_format';
462 $fields[] = 'rev_content_model';
463 }
464
465 return $fields;
466 }
467
473 public static function selectArchiveFields() {
475 $fields = [
476 'ar_id',
477 'ar_page_id',
478 'ar_rev_id',
479 'ar_text',
480 'ar_text_id',
481 'ar_timestamp',
482 'ar_comment',
483 'ar_user_text',
484 'ar_user',
485 'ar_minor_edit',
486 'ar_deleted',
487 'ar_len',
488 'ar_parent_id',
489 'ar_sha1',
490 ];
491
493 $fields[] = 'ar_content_format';
494 $fields[] = 'ar_content_model';
495 }
496 return $fields;
497 }
498
504 public static function selectTextFields() {
505 return [
506 'old_text',
507 'old_flags'
508 ];
509 }
510
515 public static function selectPageFields() {
516 return [
517 'page_namespace',
518 'page_title',
519 'page_id',
520 'page_latest',
521 'page_is_redirect',
522 'page_len',
523 ];
524 }
525
530 public static function selectUserFields() {
531 return [ 'user_name' ];
532 }
533
540 public static function getParentLengths( $db, array $revIds ) {
541 $revLens = [];
542 if ( !$revIds ) {
543 return $revLens; // empty
544 }
545 $res = $db->select( 'revision',
546 [ 'rev_id', 'rev_len' ],
547 [ 'rev_id' => $revIds ],
548 __METHOD__ );
549 foreach ( $res as $row ) {
550 $revLens[$row->rev_id] = $row->rev_len;
551 }
552 return $revLens;
553 }
554
562 function __construct( $row ) {
563 if ( is_object( $row ) ) {
564 $this->mId = intval( $row->rev_id );
565 $this->mPage = intval( $row->rev_page );
566 $this->mTextId = intval( $row->rev_text_id );
567 $this->mComment = $row->rev_comment;
568 $this->mUser = intval( $row->rev_user );
569 $this->mMinorEdit = intval( $row->rev_minor_edit );
570 $this->mTimestamp = $row->rev_timestamp;
571 $this->mDeleted = intval( $row->rev_deleted );
572
573 if ( !isset( $row->rev_parent_id ) ) {
574 $this->mParentId = null;
575 } else {
576 $this->mParentId = intval( $row->rev_parent_id );
577 }
578
579 if ( !isset( $row->rev_len ) ) {
580 $this->mSize = null;
581 } else {
582 $this->mSize = intval( $row->rev_len );
583 }
584
585 if ( !isset( $row->rev_sha1 ) ) {
586 $this->mSha1 = null;
587 } else {
588 $this->mSha1 = $row->rev_sha1;
589 }
590
591 if ( isset( $row->page_latest ) ) {
592 $this->mCurrent = ( $row->rev_id == $row->page_latest );
593 $this->mTitle = Title::newFromRow( $row );
594 } else {
595 $this->mCurrent = false;
596 $this->mTitle = null;
597 }
598
599 if ( !isset( $row->rev_content_model ) ) {
600 $this->mContentModel = null; # determine on demand if needed
601 } else {
602 $this->mContentModel = strval( $row->rev_content_model );
603 }
604
605 if ( !isset( $row->rev_content_format ) ) {
606 $this->mContentFormat = null; # determine on demand if needed
607 } else {
608 $this->mContentFormat = strval( $row->rev_content_format );
609 }
610
611 // Lazy extraction...
612 $this->mText = null;
613 if ( isset( $row->old_text ) ) {
614 $this->mTextRow = $row;
615 } else {
616 // 'text' table row entry will be lazy-loaded
617 $this->mTextRow = null;
618 }
619
620 // Use user_name for users and rev_user_text for IPs...
621 $this->mUserText = null; // lazy load if left null
622 if ( $this->mUser == 0 ) {
623 $this->mUserText = $row->rev_user_text; // IP user
624 } elseif ( isset( $row->user_name ) ) {
625 $this->mUserText = $row->user_name; // logged-in user
626 }
627 $this->mOrigUserText = $row->rev_user_text;
628 } elseif ( is_array( $row ) ) {
629 // Build a new revision to be saved...
630 global $wgUser; // ugh
631
632 # if we have a content object, use it to set the model and type
633 if ( !empty( $row['content'] ) ) {
634 // @todo when is that set? test with external store setup! check out insertOn() [dk]
635 if ( !empty( $row['text_id'] ) ) {
636 throw new MWException( "Text already stored in external store (id {$row['text_id']}), " .
637 "can't serialize content object" );
638 }
639
640 $row['content_model'] = $row['content']->getModel();
641 # note: mContentFormat is initializes later accordingly
642 # note: content is serialized later in this method!
643 # also set text to null?
644 }
645
646 $this->mId = isset( $row['id'] ) ? intval( $row['id'] ) : null;
647 $this->mPage = isset( $row['page'] ) ? intval( $row['page'] ) : null;
648 $this->mTextId = isset( $row['text_id'] ) ? intval( $row['text_id'] ) : null;
649 $this->mUserText = isset( $row['user_text'] )
650 ? strval( $row['user_text'] ) : $wgUser->getName();
651 $this->mUser = isset( $row['user'] ) ? intval( $row['user'] ) : $wgUser->getId();
652 $this->mMinorEdit = isset( $row['minor_edit'] ) ? intval( $row['minor_edit'] ) : 0;
653 $this->mTimestamp = isset( $row['timestamp'] )
654 ? strval( $row['timestamp'] ) : wfTimestampNow();
655 $this->mDeleted = isset( $row['deleted'] ) ? intval( $row['deleted'] ) : 0;
656 $this->mSize = isset( $row['len'] ) ? intval( $row['len'] ) : null;
657 $this->mParentId = isset( $row['parent_id'] ) ? intval( $row['parent_id'] ) : null;
658 $this->mSha1 = isset( $row['sha1'] ) ? strval( $row['sha1'] ) : null;
659
660 $this->mContentModel = isset( $row['content_model'] )
661 ? strval( $row['content_model'] ) : null;
662 $this->mContentFormat = isset( $row['content_format'] )
663 ? strval( $row['content_format'] ) : null;
664
665 // Enforce spacing trimming on supplied text
666 $this->mComment = isset( $row['comment'] ) ? trim( strval( $row['comment'] ) ) : null;
667 $this->mText = isset( $row['text'] ) ? rtrim( strval( $row['text'] ) ) : null;
668 $this->mTextRow = null;
669
670 $this->mTitle = isset( $row['title'] ) ? $row['title'] : null;
671
672 // if we have a Content object, override mText and mContentModel
673 if ( !empty( $row['content'] ) ) {
674 if ( !( $row['content'] instanceof Content ) ) {
675 throw new MWException( '`content` field must contain a Content object.' );
676 }
677
678 $handler = $this->getContentHandler();
679 $this->mContent = $row['content'];
680
681 $this->mContentModel = $this->mContent->getModel();
682 $this->mContentHandler = null;
683
684 $this->mText = $handler->serializeContent( $row['content'], $this->getContentFormat() );
685 } elseif ( $this->mText !== null ) {
686 $handler = $this->getContentHandler();
687 $this->mContent = $handler->unserializeContent( $this->mText );
688 }
689
690 // If we have a Title object, make sure it is consistent with mPage.
691 if ( $this->mTitle && $this->mTitle->exists() ) {
692 if ( $this->mPage === null ) {
693 // if the page ID wasn't known, set it now
694 $this->mPage = $this->mTitle->getArticleID();
695 } elseif ( $this->mTitle->getArticleID() !== $this->mPage ) {
696 // Got different page IDs. This may be legit (e.g. during undeletion),
697 // but it seems worth mentioning it in the log.
698 wfDebug( "Page ID " . $this->mPage . " mismatches the ID " .
699 $this->mTitle->getArticleID() . " provided by the Title object." );
700 }
701 }
702
703 $this->mCurrent = false;
704
705 // If we still have no length, see it we have the text to figure it out
706 if ( !$this->mSize && $this->mContent !== null ) {
707 $this->mSize = $this->mContent->getSize();
708 }
709
710 // Same for sha1
711 if ( $this->mSha1 === null ) {
712 $this->mSha1 = $this->mText === null ? null : self::base36Sha1( $this->mText );
713 }
714
715 // force lazy init
716 $this->getContentModel();
717 $this->getContentFormat();
718 } else {
719 throw new MWException( 'Revision constructor passed invalid row format.' );
720 }
721 $this->mUnpatrolled = null;
722 }
723
729 public function getId() {
730 return $this->mId;
731 }
732
741 public function setId( $id ) {
742 $this->mId = (int)$id;
743 }
744
754 public function setUserIdAndName( $id, $name ) {
755 $this->mUser = (int)$id;
756 $this->mUserText = $name;
757 $this->mOrigUserText = $name;
758 }
759
765 public function getTextId() {
766 return $this->mTextId;
767 }
768
774 public function getParentId() {
775 return $this->mParentId;
776 }
777
783 public function getSize() {
784 return $this->mSize;
785 }
786
792 public function getSha1() {
793 return $this->mSha1;
794 }
795
803 public function getTitle() {
804 if ( $this->mTitle !== null ) {
805 return $this->mTitle;
806 }
807 // rev_id is defined as NOT NULL, but this revision may not yet have been inserted.
808 if ( $this->mId !== null ) {
809 $dbr = wfGetLB( $this->mWiki )->getConnectionRef( DB_REPLICA, [], $this->mWiki );
810 $row = $dbr->selectRow(
811 [ 'page', 'revision' ],
812 self::selectPageFields(),
813 [ 'page_id=rev_page', 'rev_id' => $this->mId ],
814 __METHOD__
815 );
816 if ( $row ) {
817 // @TODO: better foreign title handling
818 $this->mTitle = Title::newFromRow( $row );
819 }
820 }
821
822 if ( $this->mWiki === false || $this->mWiki === wfWikiID() ) {
823 // Loading by ID is best, though not possible for foreign titles
824 if ( !$this->mTitle && $this->mPage !== null && $this->mPage > 0 ) {
825 $this->mTitle = Title::newFromID( $this->mPage );
826 }
827 }
828
829 return $this->mTitle;
830 }
831
837 public function setTitle( $title ) {
838 $this->mTitle = $title;
839 }
840
846 public function getPage() {
847 return $this->mPage;
848 }
849
863 public function getUser( $audience = self::FOR_PUBLIC, User $user = null ) {
864 if ( $audience == self::FOR_PUBLIC && $this->isDeleted( self::DELETED_USER ) ) {
865 return 0;
866 } elseif ( $audience == self::FOR_THIS_USER && !$this->userCan( self::DELETED_USER, $user ) ) {
867 return 0;
868 } else {
869 return $this->mUser;
870 }
871 }
872
879 public function getRawUser() {
880 wfDeprecated( __METHOD__, '1.25' );
881 return $this->getUser( self::RAW );
882 }
883
897 public function getUserText( $audience = self::FOR_PUBLIC, User $user = null ) {
898 $this->loadMutableFields();
899
900 if ( $audience == self::FOR_PUBLIC && $this->isDeleted( self::DELETED_USER ) ) {
901 return '';
902 } elseif ( $audience == self::FOR_THIS_USER && !$this->userCan( self::DELETED_USER, $user ) ) {
903 return '';
904 } else {
905 if ( $this->mUserText === null ) {
906 $this->mUserText = User::whoIs( $this->mUser ); // load on demand
907 if ( $this->mUserText === false ) {
908 # This shouldn't happen, but it can if the wiki was recovered
909 # via importing revs and there is no user table entry yet.
910 $this->mUserText = $this->mOrigUserText;
911 }
912 }
913 return $this->mUserText;
914 }
915 }
916
923 public function getRawUserText() {
924 wfDeprecated( __METHOD__, '1.25' );
925 return $this->getUserText( self::RAW );
926 }
927
941 function getComment( $audience = self::FOR_PUBLIC, User $user = null ) {
942 if ( $audience == self::FOR_PUBLIC && $this->isDeleted( self::DELETED_COMMENT ) ) {
943 return '';
944 } elseif ( $audience == self::FOR_THIS_USER && !$this->userCan( self::DELETED_COMMENT, $user ) ) {
945 return '';
946 } else {
947 return $this->mComment;
948 }
949 }
950
957 public function getRawComment() {
958 wfDeprecated( __METHOD__, '1.25' );
959 return $this->getComment( self::RAW );
960 }
961
965 public function isMinor() {
966 return (bool)$this->mMinorEdit;
967 }
968
972 public function isUnpatrolled() {
973 if ( $this->mUnpatrolled !== null ) {
974 return $this->mUnpatrolled;
975 }
976 $rc = $this->getRecentChange();
977 if ( $rc && $rc->getAttribute( 'rc_patrolled' ) == 0 ) {
978 $this->mUnpatrolled = $rc->getAttribute( 'rc_id' );
979 } else {
980 $this->mUnpatrolled = 0;
981 }
982 return $this->mUnpatrolled;
983 }
984
994 public function getRecentChange( $flags = 0 ) {
996
998
1000 [
1001 'rc_user_text' => $this->getUserText( Revision::RAW ),
1002 'rc_timestamp' => $dbr->timestamp( $this->getTimestamp() ),
1003 'rc_this_oldid' => $this->getId()
1004 ],
1005 __METHOD__,
1006 $dbType
1007 );
1008 }
1009
1015 public function isDeleted( $field ) {
1016 if ( $this->isCurrent() && $field === self::DELETED_TEXT ) {
1017 // Current revisions of pages cannot have the content hidden. Skipping this
1018 // check is very useful for Parser as it fetches templates using newKnownCurrent().
1019 // Calling getVisibility() in that case triggers a verification database query.
1020 return false; // no need to check
1021 }
1022
1023 return ( $this->getVisibility() & $field ) == $field;
1024 }
1025
1031 public function getVisibility() {
1032 $this->loadMutableFields();
1033
1034 return (int)$this->mDeleted;
1035 }
1036
1052 public function getText( $audience = self::FOR_PUBLIC, User $user = null ) {
1053 wfDeprecated( __METHOD__, '1.21' );
1054
1055 $content = $this->getContent( $audience, $user );
1056 return ContentHandler::getContentText( $content ); # returns the raw content text, if applicable
1057 }
1058
1073 public function getContent( $audience = self::FOR_PUBLIC, User $user = null ) {
1074 if ( $audience == self::FOR_PUBLIC && $this->isDeleted( self::DELETED_TEXT ) ) {
1075 return null;
1076 } elseif ( $audience == self::FOR_THIS_USER && !$this->userCan( self::DELETED_TEXT, $user ) ) {
1077 return null;
1078 } else {
1079 return $this->getContentInternal();
1080 }
1081 }
1082
1089 public function getSerializedData() {
1090 if ( $this->mText === null ) {
1091 // Revision is immutable. Load on demand.
1092 $this->mText = $this->loadText();
1093 }
1094
1095 return $this->mText;
1096 }
1097
1107 protected function getContentInternal() {
1108 if ( $this->mContent === null ) {
1109 $text = $this->getSerializedData();
1110
1111 if ( $text !== null && $text !== false ) {
1112 // Unserialize content
1113 $handler = $this->getContentHandler();
1114 $format = $this->getContentFormat();
1115
1116 $this->mContent = $handler->unserializeContent( $text, $format );
1117 }
1118 }
1119
1120 // NOTE: copy() will return $this for immutable content objects
1121 return $this->mContent ? $this->mContent->copy() : null;
1122 }
1123
1134 public function getContentModel() {
1135 if ( !$this->mContentModel ) {
1136 $title = $this->getTitle();
1137 if ( $title ) {
1138 $this->mContentModel = ContentHandler::getDefaultModelFor( $title );
1139 } else {
1140 $this->mContentModel = CONTENT_MODEL_WIKITEXT;
1141 }
1142
1143 assert( !empty( $this->mContentModel ) );
1144 }
1145
1146 return $this->mContentModel;
1147 }
1148
1158 public function getContentFormat() {
1159 if ( !$this->mContentFormat ) {
1160 $handler = $this->getContentHandler();
1161 $this->mContentFormat = $handler->getDefaultFormat();
1162
1163 assert( !empty( $this->mContentFormat ) );
1164 }
1165
1166 return $this->mContentFormat;
1167 }
1168
1175 public function getContentHandler() {
1176 if ( !$this->mContentHandler ) {
1177 $model = $this->getContentModel();
1178 $this->mContentHandler = ContentHandler::getForModelID( $model );
1179
1180 $format = $this->getContentFormat();
1181
1182 if ( !$this->mContentHandler->isSupportedFormat( $format ) ) {
1183 throw new MWException( "Oops, the content format $format is not supported for "
1184 . "this content model, $model" );
1185 }
1186 }
1187
1189 }
1190
1194 public function getTimestamp() {
1195 return wfTimestamp( TS_MW, $this->mTimestamp );
1196 }
1197
1201 public function isCurrent() {
1202 return $this->mCurrent;
1203 }
1204
1210 public function getPrevious() {
1211 if ( $this->getTitle() ) {
1212 $prev = $this->getTitle()->getPreviousRevisionID( $this->getId() );
1213 if ( $prev ) {
1214 return self::newFromTitle( $this->getTitle(), $prev );
1215 }
1216 }
1217 return null;
1218 }
1219
1225 public function getNext() {
1226 if ( $this->getTitle() ) {
1227 $next = $this->getTitle()->getNextRevisionID( $this->getId() );
1228 if ( $next ) {
1229 return self::newFromTitle( $this->getTitle(), $next );
1230 }
1231 }
1232 return null;
1233 }
1234
1242 private function getPreviousRevisionId( $db ) {
1243 if ( $this->mPage === null ) {
1244 return 0;
1245 }
1246 # Use page_latest if ID is not given
1247 if ( !$this->mId ) {
1248 $prevId = $db->selectField( 'page', 'page_latest',
1249 [ 'page_id' => $this->mPage ],
1250 __METHOD__ );
1251 } else {
1252 $prevId = $db->selectField( 'revision', 'rev_id',
1253 [ 'rev_page' => $this->mPage, 'rev_id < ' . $this->mId ],
1254 __METHOD__,
1255 [ 'ORDER BY' => 'rev_id DESC' ] );
1256 }
1257 return intval( $prevId );
1258 }
1259
1273 public static function getRevisionText( $row, $prefix = 'old_', $wiki = false ) {
1274
1275 # Get data
1276 $textField = $prefix . 'text';
1277 $flagsField = $prefix . 'flags';
1278
1279 if ( isset( $row->$flagsField ) ) {
1280 $flags = explode( ',', $row->$flagsField );
1281 } else {
1282 $flags = [];
1283 }
1284
1285 if ( isset( $row->$textField ) ) {
1286 $text = $row->$textField;
1287 } else {
1288 return false;
1289 }
1290
1291 # Use external methods for external objects, text in table is URL-only then
1292 if ( in_array( 'external', $flags ) ) {
1293 $url = $text;
1294 $parts = explode( '://', $url, 2 );
1295 if ( count( $parts ) == 1 || $parts[1] == '' ) {
1296 return false;
1297 }
1298 $text = ExternalStore::fetchFromURL( $url, [ 'wiki' => $wiki ] );
1299 }
1300
1301 // If the text was fetched without an error, convert it
1302 if ( $text !== false ) {
1303 $text = self::decompressRevisionText( $text, $flags );
1304 }
1305 return $text;
1306 }
1307
1318 public static function compressRevisionText( &$text ) {
1320 $flags = [];
1321
1322 # Revisions not marked this way will be converted
1323 # on load if $wgLegacyCharset is set in the future.
1324 $flags[] = 'utf-8';
1325
1326 if ( $wgCompressRevisions ) {
1327 if ( function_exists( 'gzdeflate' ) ) {
1328 $deflated = gzdeflate( $text );
1329
1330 if ( $deflated === false ) {
1331 wfLogWarning( __METHOD__ . ': gzdeflate() failed' );
1332 } else {
1333 $text = $deflated;
1334 $flags[] = 'gzip';
1335 }
1336 } else {
1337 wfDebug( __METHOD__ . " -- no zlib support, not compressing\n" );
1338 }
1339 }
1340 return implode( ',', $flags );
1341 }
1342
1350 public static function decompressRevisionText( $text, $flags ) {
1351 if ( in_array( 'gzip', $flags ) ) {
1352 # Deal with optional compression of archived pages.
1353 # This can be done periodically via maintenance/compressOld.php, and
1354 # as pages are saved if $wgCompressRevisions is set.
1355 $text = gzinflate( $text );
1356
1357 if ( $text === false ) {
1358 wfLogWarning( __METHOD__ . ': gzinflate() failed' );
1359 return false;
1360 }
1361 }
1362
1363 if ( in_array( 'object', $flags ) ) {
1364 # Generic compressed storage
1365 $obj = unserialize( $text );
1366 if ( !is_object( $obj ) ) {
1367 // Invalid object
1368 return false;
1369 }
1370 $text = $obj->getText();
1371 }
1372
1374 if ( $text !== false && $wgLegacyEncoding
1375 && !in_array( 'utf-8', $flags ) && !in_array( 'utf8', $flags )
1376 ) {
1377 # Old revisions kept around in a legacy encoding?
1378 # Upconvert on demand.
1379 # ("utf8" checked for compatibility with some broken
1380 # conversion scripts 2008-12-30)
1382 $text = $wgContLang->iconv( $wgLegacyEncoding, 'UTF-8', $text );
1383 }
1384
1385 return $text;
1386 }
1387
1396 public function insertOn( $dbw ) {
1398
1399 // We're inserting a new revision, so we have to use master anyway.
1400 // If it's a null revision, it may have references to rows that
1401 // are not in the replica yet (the text row).
1402 $this->mQueryFlags |= self::READ_LATEST;
1403
1404 // Not allowed to have rev_page equal to 0, false, etc.
1405 if ( !$this->mPage ) {
1406 $title = $this->getTitle();
1407 if ( $title instanceof Title ) {
1408 $titleText = ' for page ' . $title->getPrefixedText();
1409 } else {
1410 $titleText = '';
1411 }
1412 throw new MWException( "Cannot insert revision$titleText: page ID must be nonzero" );
1413 }
1414
1415 $this->checkContentModel();
1416
1417 $data = $this->mText;
1419
1420 # Write to external storage if required
1422 // Store and get the URL
1423 $data = ExternalStore::insertToDefault( $data );
1424 if ( !$data ) {
1425 throw new MWException( "Unable to store text to external storage" );
1426 }
1427 if ( $flags ) {
1428 $flags .= ',';
1429 }
1430 $flags .= 'external';
1431 }
1432
1433 # Record the text (or external storage URL) to the text table
1434 if ( $this->mTextId === null ) {
1435 $old_id = $dbw->nextSequenceValue( 'text_old_id_seq' );
1436 $dbw->insert( 'text',
1437 [
1438 'old_id' => $old_id,
1439 'old_text' => $data,
1440 'old_flags' => $flags,
1441 ], __METHOD__
1442 );
1443 $this->mTextId = $dbw->insertId();
1444 }
1445
1446 if ( $this->mComment === null ) {
1447 $this->mComment = "";
1448 }
1449
1450 # Record the edit in revisions
1451 $rev_id = $this->mId !== null
1452 ? $this->mId
1453 : $dbw->nextSequenceValue( 'revision_rev_id_seq' );
1454 $row = [
1455 'rev_id' => $rev_id,
1456 'rev_page' => $this->mPage,
1457 'rev_text_id' => $this->mTextId,
1458 'rev_comment' => $this->mComment,
1459 'rev_minor_edit' => $this->mMinorEdit ? 1 : 0,
1460 'rev_user' => $this->mUser,
1461 'rev_user_text' => $this->mUserText,
1462 'rev_timestamp' => $dbw->timestamp( $this->mTimestamp ),
1463 'rev_deleted' => $this->mDeleted,
1464 'rev_len' => $this->mSize,
1465 'rev_parent_id' => $this->mParentId === null
1466 ? $this->getPreviousRevisionId( $dbw )
1468 'rev_sha1' => $this->mSha1 === null
1469 ? Revision::base36Sha1( $this->mText )
1470 : $this->mSha1,
1471 ];
1472
1473 if ( $wgContentHandlerUseDB ) {
1474 // NOTE: Store null for the default model and format, to save space.
1475 // XXX: Makes the DB sensitive to changed defaults.
1476 // Make this behavior optional? Only in miser mode?
1477
1478 $model = $this->getContentModel();
1479 $format = $this->getContentFormat();
1480
1481 $title = $this->getTitle();
1482
1483 if ( $title === null ) {
1484 throw new MWException( "Insufficient information to determine the title of the "
1485 . "revision's page!" );
1486 }
1487
1488 $defaultModel = ContentHandler::getDefaultModelFor( $title );
1489 $defaultFormat = ContentHandler::getForModelID( $defaultModel )->getDefaultFormat();
1490
1491 $row['rev_content_model'] = ( $model === $defaultModel ) ? null : $model;
1492 $row['rev_content_format'] = ( $format === $defaultFormat ) ? null : $format;
1493 }
1494
1495 $dbw->insert( 'revision', $row, __METHOD__ );
1496
1497 $this->mId = $rev_id !== null ? $rev_id : $dbw->insertId();
1498
1499 // Assertion to try to catch T92046
1500 if ( (int)$this->mId === 0 ) {
1501 throw new UnexpectedValueException(
1502 'After insert, Revision mId is ' . var_export( $this->mId, 1 ) . ': ' .
1503 var_export( $row, 1 )
1504 );
1505 }
1506
1507 Hooks::run( 'RevisionInsertComplete', [ &$this, $data, $flags ] );
1508
1509 return $this->mId;
1510 }
1511
1512 protected function checkContentModel() {
1514
1515 // Note: may return null for revisions that have not yet been inserted
1516 $title = $this->getTitle();
1517
1518 $model = $this->getContentModel();
1519 $format = $this->getContentFormat();
1520 $handler = $this->getContentHandler();
1521
1522 if ( !$handler->isSupportedFormat( $format ) ) {
1523 $t = $title->getPrefixedDBkey();
1524
1525 throw new MWException( "Can't use format $format with content model $model on $t" );
1526 }
1527
1528 if ( !$wgContentHandlerUseDB && $title ) {
1529 // if $wgContentHandlerUseDB is not set,
1530 // all revisions must use the default content model and format.
1531
1532 $defaultModel = ContentHandler::getDefaultModelFor( $title );
1533 $defaultHandler = ContentHandler::getForModelID( $defaultModel );
1534 $defaultFormat = $defaultHandler->getDefaultFormat();
1535
1536 if ( $this->getContentModel() != $defaultModel ) {
1537 $t = $title->getPrefixedDBkey();
1538
1539 throw new MWException( "Can't save non-default content model with "
1540 . "\$wgContentHandlerUseDB disabled: model is $model, "
1541 . "default for $t is $defaultModel" );
1542 }
1543
1544 if ( $this->getContentFormat() != $defaultFormat ) {
1545 $t = $title->getPrefixedDBkey();
1546
1547 throw new MWException( "Can't use non-default content format with "
1548 . "\$wgContentHandlerUseDB disabled: format is $format, "
1549 . "default for $t is $defaultFormat" );
1550 }
1551 }
1552
1553 $content = $this->getContent( Revision::RAW );
1554 $prefixedDBkey = $title->getPrefixedDBkey();
1556
1557 if ( !$content ) {
1558 throw new MWException(
1559 "Content of revision $revId ($prefixedDBkey) could not be loaded for validation!"
1560 );
1561 }
1562 if ( !$content->isValid() ) {
1563 throw new MWException(
1564 "Content of revision $revId ($prefixedDBkey) is not valid! Content model is $model"
1565 );
1566 }
1567 }
1568
1574 public static function base36Sha1( $text ) {
1575 return Wikimedia\base_convert( sha1( $text ), 16, 36, 31 );
1576 }
1577
1584 private function loadText() {
1586
1587 $cache = ObjectCache::getMainWANInstance();
1588 if ( $cache->getQoS( $cache::ATTR_EMULATION ) <= $cache::QOS_EMULATION_SQL ) {
1589 // Do not cache RDBMs blobs in...the RDBMs store
1590 $ttl = $cache::TTL_UNCACHEABLE;
1591 } else {
1592 $ttl = $wgRevisionCacheExpiry ?: $cache::TTL_UNCACHEABLE;
1593 }
1594
1595 // No negative caching; negative hits on text rows may be due to corrupted replica DBs
1596 return $cache->getWithSetCallback(
1597 $cache->makeKey( 'revisiontext', 'textid', $this->getTextId() ),
1598 $ttl,
1599 function () {
1600 return $this->fetchText();
1601 },
1602 [ 'pcGroup' => self::TEXT_CACHE_GROUP, 'pcTTL' => $cache::TTL_PROC_LONG ]
1603 );
1604 }
1605
1606 private function fetchText() {
1607 $textId = $this->getTextId();
1608
1609 // If we kept data for lazy extraction, use it now...
1610 if ( $this->mTextRow !== null ) {
1611 $row = $this->mTextRow;
1612 $this->mTextRow = null;
1613 } else {
1614 $row = null;
1615 }
1616
1617 // Callers doing updates will pass in READ_LATEST as usual. Since the text/blob tables
1618 // do not normally get rows changed around, set READ_LATEST_IMMUTABLE in those cases.
1620 $flags |= DBAccessObjectUtils::hasFlags( $flags, self::READ_LATEST )
1621 ? self::READ_LATEST_IMMUTABLE
1622 : 0;
1623
1624 list( $index, $options, $fallbackIndex, $fallbackOptions ) =
1626
1627 if ( !$row ) {
1628 // Text data is immutable; check replica DBs first.
1629 $row = wfGetDB( $index )->selectRow(
1630 'text',
1631 [ 'old_text', 'old_flags' ],
1632 [ 'old_id' => $textId ],
1633 __METHOD__,
1634 $options
1635 );
1636 }
1637
1638 // Fallback to DB_MASTER in some cases if the row was not found
1639 if ( !$row && $fallbackIndex !== null ) {
1640 // Use FOR UPDATE if it was used to fetch this revision. This avoids missing the row
1641 // due to REPEATABLE-READ. Also fallback to the master if READ_LATEST is provided.
1642 $row = wfGetDB( $fallbackIndex )->selectRow(
1643 'text',
1644 [ 'old_text', 'old_flags' ],
1645 [ 'old_id' => $textId ],
1646 __METHOD__,
1647 $fallbackOptions
1648 );
1649 }
1650
1651 if ( !$row ) {
1652 wfDebugLog( 'Revision', "No text row with ID '$textId' (revision {$this->getId()})." );
1653 }
1654
1655 $text = self::getRevisionText( $row );
1656 if ( $row && $text === false ) {
1657 wfDebugLog( 'Revision', "No blob for text row '$textId' (revision {$this->getId()})." );
1658 }
1659
1660 return is_string( $text ) ? $text : false;
1661 }
1662
1678 public static function newNullRevision( $dbw, $pageId, $summary, $minor, $user = null ) {
1680
1681 $fields = [ 'page_latest', 'page_namespace', 'page_title',
1682 'rev_text_id', 'rev_len', 'rev_sha1' ];
1683
1684 if ( $wgContentHandlerUseDB ) {
1685 $fields[] = 'rev_content_model';
1686 $fields[] = 'rev_content_format';
1687 }
1688
1689 $current = $dbw->selectRow(
1690 [ 'page', 'revision' ],
1691 $fields,
1692 [
1693 'page_id' => $pageId,
1694 'page_latest=rev_id',
1695 ],
1696 __METHOD__,
1697 [ 'FOR UPDATE' ] // T51581
1698 );
1699
1700 if ( $current ) {
1701 if ( !$user ) {
1703 $user = $wgUser;
1704 }
1705
1706 // Truncate for whole multibyte characters
1707 $summary = $wgContLang->truncate( $summary, 255 );
1708
1709 $row = [
1710 'page' => $pageId,
1711 'user_text' => $user->getName(),
1712 'user' => $user->getId(),
1713 'comment' => $summary,
1714 'minor_edit' => $minor,
1715 'text_id' => $current->rev_text_id,
1716 'parent_id' => $current->page_latest,
1717 'len' => $current->rev_len,
1718 'sha1' => $current->rev_sha1
1719 ];
1720
1721 if ( $wgContentHandlerUseDB ) {
1722 $row['content_model'] = $current->rev_content_model;
1723 $row['content_format'] = $current->rev_content_format;
1724 }
1725
1726 $row['title'] = Title::makeTitle( $current->page_namespace, $current->page_title );
1727
1728 $revision = new Revision( $row );
1729 } else {
1730 $revision = null;
1731 }
1732
1733 return $revision;
1734 }
1735
1746 public function userCan( $field, User $user = null ) {
1747 return self::userCanBitfield( $this->getVisibility(), $field, $user );
1748 }
1749
1764 public static function userCanBitfield( $bitfield, $field, User $user = null,
1765 Title $title = null
1766 ) {
1767 if ( $bitfield & $field ) { // aspect is deleted
1768 if ( $user === null ) {
1770 $user = $wgUser;
1771 }
1772 if ( $bitfield & self::DELETED_RESTRICTED ) {
1773 $permissions = [ 'suppressrevision', 'viewsuppressed' ];
1774 } elseif ( $field & self::DELETED_TEXT ) {
1775 $permissions = [ 'deletedtext' ];
1776 } else {
1777 $permissions = [ 'deletedhistory' ];
1778 }
1779 $permissionlist = implode( ', ', $permissions );
1780 if ( $title === null ) {
1781 wfDebug( "Checking for $permissionlist due to $field match on $bitfield\n" );
1782 return call_user_func_array( [ $user, 'isAllowedAny' ], $permissions );
1783 } else {
1784 $text = $title->getPrefixedText();
1785 wfDebug( "Checking for $permissionlist on $text due to $field match on $bitfield\n" );
1786 foreach ( $permissions as $perm ) {
1787 if ( $title->userCan( $perm, $user ) ) {
1788 return true;
1789 }
1790 }
1791 return false;
1792 }
1793 } else {
1794 return true;
1795 }
1796 }
1797
1805 static function getTimestampFromId( $title, $id, $flags = 0 ) {
1806 $db = ( $flags & self::READ_LATEST )
1807 ? wfGetDB( DB_MASTER )
1808 : wfGetDB( DB_REPLICA );
1809 // Casting fix for databases that can't take '' for rev_id
1810 if ( $id == '' ) {
1811 $id = 0;
1812 }
1813 $conds = [ 'rev_id' => $id ];
1814 $conds['rev_page'] = $title->getArticleID();
1815 $timestamp = $db->selectField( 'revision', 'rev_timestamp', $conds, __METHOD__ );
1816
1817 return ( $timestamp !== false ) ? wfTimestamp( TS_MW, $timestamp ) : false;
1818 }
1819
1827 static function countByPageId( $db, $id ) {
1828 $row = $db->selectRow( 'revision', [ 'revCount' => 'COUNT(*)' ],
1829 [ 'rev_page' => $id ], __METHOD__ );
1830 if ( $row ) {
1831 return $row->revCount;
1832 }
1833 return 0;
1834 }
1835
1843 static function countByTitle( $db, $title ) {
1844 $id = $title->getArticleID();
1845 if ( $id ) {
1846 return self::countByPageId( $db, $id );
1847 }
1848 return 0;
1849 }
1850
1867 public static function userWasLastToEdit( $db, $pageId, $userId, $since ) {
1868 if ( !$userId ) {
1869 return false;
1870 }
1871
1872 if ( is_int( $db ) ) {
1873 $db = wfGetDB( $db );
1874 }
1875
1876 $res = $db->select( 'revision',
1877 'rev_user',
1878 [
1879 'rev_page' => $pageId,
1880 'rev_timestamp > ' . $db->addQuotes( $db->timestamp( $since ) )
1881 ],
1882 __METHOD__,
1883 [ 'ORDER BY' => 'rev_timestamp ASC', 'LIMIT' => 50 ] );
1884 foreach ( $res as $row ) {
1885 if ( $row->rev_user != $userId ) {
1886 return false;
1887 }
1888 }
1889 return true;
1890 }
1891
1905 public static function newKnownCurrent( IDatabase $db, $pageId, $revId ) {
1906 $cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
1907 return $cache->getWithSetCallback(
1908 // Page/rev IDs passed in from DB to reflect history merges
1909 $cache->makeGlobalKey( 'revision', $db->getWikiID(), $pageId, $revId ),
1910 $cache::TTL_WEEK,
1911 function ( $curValue, &$ttl, array &$setOpts ) use ( $db, $pageId, $revId ) {
1912 $setOpts += Database::getCacheSetOptions( $db );
1913
1914 $rev = Revision::loadFromPageId( $db, $pageId, $revId );
1915 // Reflect revision deletion and user renames
1916 if ( $rev ) {
1917 $rev->mTitle = null; // mutable; lazy-load
1918 $rev->mRefreshMutableFields = true;
1919 }
1920
1921 return $rev ?: false; // don't cache negatives
1922 }
1923 );
1924 }
1925
1929 private function loadMutableFields() {
1930 if ( !$this->mRefreshMutableFields ) {
1931 return; // not needed
1932 }
1933
1934 $this->mRefreshMutableFields = false;
1935 $dbr = wfGetLB( $this->mWiki )->getConnectionRef( DB_REPLICA, [], $this->mWiki );
1936 $row = $dbr->selectRow(
1937 [ 'revision', 'user' ],
1938 [ 'rev_deleted', 'user_name' ],
1939 [ 'rev_id' => $this->mId, 'user_id = rev_user' ],
1940 __METHOD__
1941 );
1942 if ( $row ) { // update values
1943 $this->mDeleted = (int)$row->rev_deleted;
1944 $this->mUserText = $row->user_name;
1945 }
1946 }
1947}
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:806
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 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.
Overloads the relevant methods of the real ResultsWrapper so it doesn't go anywhere near an actual da...
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:61
bool $mCurrent
Definition Revision.php:66
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.
Definition Revision.php:994
getUserText( $audience=self::FOR_PUBLIC, User $user=null)
Fetch revision's username if it's available to the specified audience.
Definition Revision.php:897
getTitle()
Returns the title of the page associated with this entry or null.
Definition Revision.php:803
static selectArchiveFields()
Return the list of revision fields that should be selected to create a new revision from an archive r...
Definition Revision.php:473
getPrevious()
Get previous revision for this title.
null ContentHandler $mContentHandler
Definition Revision.php:75
getSize()
Returns the length of the text in this revision, or null if unknown.
Definition Revision.php:783
loadMutableFields()
For cached revisions, make sure the user name and rev_deleted is up-to-date.
null Title $mTitle
Definition Revision.php:64
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:729
int $mUnpatrolled
Definition Revision.php:58
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:879
static userJoinCond()
Return the value of a select() JOIN conds array for the user table.
Definition Revision.php:423
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:80
setUserIdAndName( $id, $name)
Set the user ID/name.
Definition Revision.php:754
getContentHandler()
Returns the content handler appropriate for this revision's content model.
string $mComment
Definition Revision.php:52
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:941
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:397
string $mContentFormat
Definition Revision.php:70
static loadFromTitle( $db, $title, $id=0)
Load either the current, or a specified, revision that's attached to a given page.
Definition Revision.php:276
getNext()
Get next revision for this title.
getTextId()
Get text row ID.
Definition Revision.php:765
static selectTextFields()
Return the list of text fields that should be selected to read the revision text.
Definition Revision.php:504
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:159
static selectPageFields()
Return the list of page fields that should be selected from page table.
Definition Revision.php:515
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:442
static loadFromTimestamp( $db, $title, $timestamp)
Load the revision for the given title with the given timestamp.
Definition Revision.php:301
int $mUser
Definition Revision.php:38
string $mOrigUserText
Definition Revision.php:36
static loadFromConds( $db, $conditions, $flags=0)
Given a set of conditions, fetch a revision from the given database connection.
Definition Revision.php:353
getPage()
Get the page ID.
Definition Revision.php:846
static newFromRow( $row)
Definition Revision.php:230
int $mSize
Definition Revision.php:46
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:242
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:530
getContentModel()
Returns the content model for this revision.
int null $mId
Definition Revision.php:30
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:863
static newFromArchiveRow( $row, $overrides=[])
Make a fake revision object from an archive table row.
Definition Revision.php:183
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:68
static pageJoinCond()
Return the value of a select() page conds array for the page table.
Definition Revision.php:433
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:87
string $mUserText
Definition Revision.php:34
static newFromConds( $conditions, $flags=0)
Given a set of conditions, fetch a revision.
Definition Revision.php:321
const TEXT_CACHE_GROUP
Definition Revision.php:96
const DELETED_TEXT
Definition Revision.php:85
static base36Sha1( $text)
Get the base 36 SHA-1 value for a string of text.
int $mQueryFlags
Definition Revision.php:78
__construct( $row)
Constructor.
Definition Revision.php:562
getSha1()
Returns the base36 sha1 of the text in this revision, or null if unknown.
Definition Revision.php:792
const DELETED_RESTRICTED
Definition Revision.php:88
bool $mMinorEdit
Definition Revision.php:40
static loadFromPageId( $db, $pageid, $id=0)
Load either the current, or a specified, revision that's attached to a given page.
Definition Revision.php:256
getRawComment()
Fetch revision comment without regard for the current user's permissions.
Definition Revision.php:957
string $mTimestamp
Definition Revision.php:42
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:837
int null $mPage
Definition Revision.php:32
string $mSha1
Definition Revision.php:48
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:73
string $mWiki
Wiki ID; false means the current wiki.
Definition Revision.php:82
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:923
static getParentLengths( $db, array $revIds)
Do a batched query to get the parent revision lengths.
Definition Revision.php:540
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:374
isUnpatrolled()
Definition Revision.php:972
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:92
int $mParentId
Definition Revision.php:50
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:128
getParentId()
Get parent revision ID (the original previous page revision)
Definition Revision.php:774
int $mDeleted
Definition Revision.php:44
string $mText
Definition Revision.php:54
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:741
const RAW
Definition Revision.php:94
isDeleted( $field)
const DELETED_COMMENT
Definition Revision.php:86
const FOR_THIS_USER
Definition Revision.php:93
static newFromId( $id, $flags=0)
Load a page revision from a given revision ID number.
Definition Revision.php:110
const SUPPRESSED_USER
Definition Revision.php:89
int $mTextId
Definition Revision.php:56
Represents a title within MediaWiki.
Definition Title.php:36
The User object encapsulates all of the user-specific settings (user_id, name, rights,...
Definition User.php:48
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 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
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 CONTENT_MODEL_WIKITEXT
Definition Defines.php:239
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:1095
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
namespace and then decline to actually register it file or subcat img or subcat $title
Definition hooks.txt:986
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:1096
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:1094
it s the revision text itself In either if gzip is the revision text is gzipped $flags
Definition hooks.txt:2710
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:1958
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
Allows to change the fields on the form that will be generated $name
Definition hooks.txt:304
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:925
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:1734
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.
Basic database interface for live and lazy-loaded relation database handles.
Definition IDatabase.php:34
getWikiID()
Alias for getDomainID()
getNamespace()
Get the namespace index.
getDBkey()
Get the main part with underscores.
$cache
Definition mcc.php:33
const DB_REPLICA
Definition defines.php:22
const DB_MASTER
Definition defines.php:23
const TS_MW
MediaWiki concatenated string timestamp (YYYYMMDDHHMMSS)
Definition defines.php:11