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