MediaWiki  master
RevisionDbTestBase.php
Go to the documentation of this file.
1 <?php
2 
9 
16 abstract class RevisionDbTestBase extends MediaWikiTestCase {
17 
21  private $testPage;
22 
23  public function __construct( $name = null, array $data = [], $dataName = '' ) {
24  parent::__construct( $name, $data, $dataName );
25 
26  $this->tablesUsed = array_merge( $this->tablesUsed,
27  [
28  'page',
29  'revision',
30  'comment',
31  'ip_changes',
32  'text',
33  'archive',
34 
35  'recentchanges',
36  'logging',
37 
38  'page_props',
39  'pagelinks',
40  'categorylinks',
41  'langlinks',
42  'externallinks',
43  'imagelinks',
44  'templatelinks',
45  'iwlinks'
46  ]
47  );
48  }
49 
50  protected function addCoreDBData() {
51  // Blank out. This would fail with a modified schema, and we don't need it.
52  }
53 
57  abstract protected function getMcrMigrationStage();
58 
62  abstract protected function getMcrTablesToReset();
63 
64  protected function setUp() {
65  $this->tablesUsed += $this->getMcrTablesToReset();
66 
67  parent::setUp();
68 
70  'wgExtraNamespaces',
71  [
72  12312 => 'Dummy',
73  12313 => 'Dummy_talk',
74  ]
75  );
76 
78  'wgNamespaceContentModels',
79  [
81  ]
82  );
83 
85  'wgContentHandlers',
86  [
87  DummyContentForTesting::MODEL_ID => 'DummyContentHandlerForTesting',
88  RevisionTestModifyableContent::MODEL_ID => 'RevisionTestModifyableContentHandler',
89  ]
90  );
91 
92  $this->setMwGlobals( [
93  'wgMultiContentRevisionSchemaMigrationStage' => $this->getMcrMigrationStage(),
94  'wgContentHandlerUseDB' => $this->getContentHandlerUseDB(),
95  'wgActorTableSchemaMigrationStage' => SCHEMA_COMPAT_NEW,
96  ] );
97 
98  $this->overrideMwServices();
99 
100  if ( !$this->testPage ) {
105  $this->testPage = $this->createPage( __CLASS__, __CLASS__ );
106  }
107  }
108 
113  protected function getMockTitle() {
114  $mock = $this->getMockBuilder( Title::class )
115  ->disableOriginalConstructor()
116  ->getMock();
117  $mock->expects( $this->any() )
118  ->method( 'getNamespace' )
119  ->will( $this->returnValue( $this->getDefaultWikitextNS() ) );
120  $mock->expects( $this->any() )
121  ->method( 'getPrefixedText' )
122  ->will( $this->returnValue( __CLASS__ ) );
123  $mock->expects( $this->any() )
124  ->method( 'getDBkey' )
125  ->will( $this->returnValue( __CLASS__ ) );
126  $mock->expects( $this->any() )
127  ->method( 'getArticleID' )
128  ->will( $this->returnValue( 23 ) );
129 
130  return $mock;
131  }
132 
133  abstract protected function getContentHandlerUseDB();
134 
135  private function makeRevisionWithProps( $props = null ) {
136  if ( $props === null ) {
137  $props = [];
138  }
139 
140  if ( !isset( $props['content'] ) && !isset( $props['text'] ) ) {
141  $props['text'] = 'Lorem Ipsum';
142  }
143 
144  if ( !isset( $props['user_text'] ) ) {
145  $user = $this->getTestUser()->getUser();
146  $props['user_text'] = $user->getName();
147  $props['user'] = $user->getId();
148  }
149 
150  if ( !isset( $props['user'] ) ) {
151  $props['user'] = 0;
152  }
153 
154  if ( !isset( $props['comment'] ) ) {
155  $props['comment'] = 'just a test';
156  }
157 
158  if ( !isset( $props['page'] ) ) {
159  $props['page'] = $this->testPage->getId();
160  }
161 
162  if ( !isset( $props['content_model'] ) ) {
163  $props['content_model'] = CONTENT_MODEL_WIKITEXT;
164  }
165 
166  $rev = new Revision( $props );
167 
168  $dbw = wfGetDB( DB_MASTER );
169  $rev->insertOn( $dbw );
170 
171  return $rev;
172  }
173 
181  private function createPage( $titleString, $text, $model = null ) {
182  if ( !preg_match( '/:/', $titleString ) &&
183  ( $model === null || $model === CONTENT_MODEL_WIKITEXT )
184  ) {
185  $ns = $this->getDefaultWikitextNS();
186  $titleString = MediaWikiServices::getInstance()->getNamespaceInfo()->
187  getCanonicalName( $ns ) . ':' . $titleString;
188  }
189 
190  $title = Title::newFromText( $titleString );
191  $wikipage = new WikiPage( $title );
192 
193  // Delete the article if it already exists
194  if ( $wikipage->exists() ) {
195  $wikipage->doDeleteArticle( "done" );
196  }
197 
198  $content = ContentHandler::makeContent( $text, $title, $model );
199  $wikipage->doEditContent( $content, __METHOD__, EDIT_NEW );
200 
201  return $wikipage;
202  }
203 
204  private function assertRevEquals( Revision $orig, Revision $rev = null ) {
205  $this->assertNotNull( $rev, 'missing revision' );
206 
207  $this->assertEquals( $orig->getId(), $rev->getId() );
208  $this->assertEquals( $orig->getPage(), $rev->getPage() );
209  $this->assertEquals( $orig->getTimestamp(), $rev->getTimestamp() );
210  $this->assertEquals( $orig->getUser(), $rev->getUser() );
211  $this->assertEquals( $orig->getContentModel(), $rev->getContentModel() );
212  $this->assertEquals( $orig->getContentFormat(), $rev->getContentFormat() );
213  $this->assertEquals( $orig->getSha1(), $rev->getSha1() );
214  }
215 
219  public function testGetRecentChange() {
220  $rev = $this->testPage->getRevision();
221  $recentChange = $rev->getRecentChange();
222 
223  // Make sure various attributes look right / the correct entry has been retrieved.
224  $this->assertEquals( $rev->getTimestamp(), $recentChange->getAttribute( 'rc_timestamp' ) );
225  $this->assertEquals(
226  $rev->getTitle()->getNamespace(),
227  $recentChange->getAttribute( 'rc_namespace' )
228  );
229  $this->assertEquals(
230  $rev->getTitle()->getDBkey(),
231  $recentChange->getAttribute( 'rc_title' )
232  );
233  $this->assertEquals( $rev->getUser(), $recentChange->getAttribute( 'rc_user' ) );
234  $this->assertEquals( $rev->getUserText(), $recentChange->getAttribute( 'rc_user_text' ) );
235  $this->assertEquals( $rev->getComment(), $recentChange->getAttribute( 'rc_comment' ) );
236  $this->assertEquals( $rev->getPage(), $recentChange->getAttribute( 'rc_cur_id' ) );
237  $this->assertEquals( $rev->getId(), $recentChange->getAttribute( 'rc_this_oldid' ) );
238  }
239 
243  public function testInsertOn_success() {
244  $parentId = $this->testPage->getLatest();
245 
246  // If an ExternalStore is set don't use it.
247  $this->setMwGlobals( 'wgDefaultExternalStore', false );
248 
249  $rev = new Revision( [
250  'page' => $this->testPage->getId(),
251  'title' => $this->testPage->getTitle(),
252  'text' => 'Revision Text',
253  'comment' => 'Revision comment',
254  ] );
255 
256  $revId = $rev->insertOn( wfGetDB( DB_MASTER ) );
257 
258  $this->assertInternalType( 'integer', $revId );
259  $this->assertSame( $revId, $rev->getId() );
260 
261  // getTextId() must be an int!
262  $this->assertInternalType( 'integer', $rev->getTextId() );
263 
264  $mainSlot = $rev->getRevisionRecord()->getSlot( SlotRecord::MAIN, RevisionRecord::RAW );
265 
266  // we currently only support storage in the text table
267  $textId = MediaWikiServices::getInstance()
268  ->getBlobStore()
269  ->getTextIdFromAddress( $mainSlot->getAddress() );
270 
271  $this->assertSelect(
272  'text',
273  [ 'old_id', 'old_text' ],
274  "old_id = $textId",
275  [ [ strval( $textId ), 'Revision Text' ] ]
276  );
277  $this->assertSelect(
278  'revision',
279  [
280  'rev_id',
281  'rev_page',
282  'rev_minor_edit',
283  'rev_deleted',
284  'rev_len',
285  'rev_parent_id',
286  'rev_sha1',
287  ],
288  "rev_id = {$rev->getId()}",
289  [ [
290  strval( $rev->getId() ),
291  strval( $this->testPage->getId() ),
292  '0',
293  '0',
294  '13',
295  strval( $parentId ),
296  's0ngbdoxagreuf2vjtuxzwdz64n29xm',
297  ] ]
298  );
299  }
300 
302  $content = new TextContent( '' );
303  $user = User::newFromName( 'Foo' );
304 
305  yield 'no parent' => [
306  [
307  'content' => $content,
308  'comment' => 'test',
309  'user' => $user,
310  ],
312  "rev_page field must not be 0!"
313  ];
314 
315  yield 'no comment' => [
316  [
317  'content' => $content,
318  'page' => 7,
319  'user' => $user,
320  ],
322  "comment must not be NULL!"
323  ];
324 
325  yield 'no content' => [
326  [
327  'comment' => 'test',
328  'page' => 7,
329  'user' => $user,
330  ],
332  "Uninitialized field: content_address" // XXX: message may change
333  ];
334  }
335 
340  public function testInsertOn_exceptionOnIncomplete( $array, $expException, $expMessage ) {
341  // If an ExternalStore is set don't use it.
342  $this->setMwGlobals( 'wgDefaultExternalStore', false );
343  $this->setExpectedException( $expException, $expMessage );
344 
345  $title = Title::newFromText( 'Nonexistant-' . __METHOD__ );
346  $rev = new Revision( $array, 0, $title );
347 
348  $rev->insertOn( wfGetDB( DB_MASTER ) );
349  }
350 
354  public function testNewFromTitle_withoutId() {
355  $latestRevId = $this->testPage->getLatest();
356 
357  $rev = Revision::newFromTitle( $this->testPage->getTitle() );
358 
359  $this->assertTrue( $this->testPage->getTitle()->equals( $rev->getTitle() ) );
360  $this->assertEquals( $latestRevId, $rev->getId() );
361  }
362 
366  public function testNewFromTitle_withId() {
367  $latestRevId = $this->testPage->getLatest();
368 
369  $rev = Revision::newFromTitle( $this->testPage->getTitle(), $latestRevId );
370 
371  $this->assertTrue( $this->testPage->getTitle()->equals( $rev->getTitle() ) );
372  $this->assertEquals( $latestRevId, $rev->getId() );
373  }
374 
378  public function testNewFromTitle_withBadId() {
379  $latestRevId = $this->testPage->getLatest();
380 
381  $rev = Revision::newFromTitle( $this->testPage->getTitle(), $latestRevId + 1 );
382 
383  $this->assertNull( $rev );
384  }
385 
389  public function testNewFromRow() {
390  $orig = $this->makeRevisionWithProps();
391 
392  $dbr = wfGetDB( DB_REPLICA );
394  $res = $dbr->select( $revQuery['tables'], $revQuery['fields'], [ 'rev_id' => $orig->getId() ],
395  __METHOD__, [], $revQuery['joins'] );
396  $this->assertTrue( is_object( $res ), 'query failed' );
397 
398  $row = $res->fetchObject();
399  $res->free();
400 
401  $rev = Revision::newFromRow( $row );
402 
403  $this->assertRevEquals( $orig, $rev );
404  }
405 
406  public function provideNewFromArchiveRow() {
407  yield [
408  function ( $f ) {
409  return $f;
410  },
411  ];
412  yield [
413  function ( $f ) {
414  return $f + [ 'ar_namespace', 'ar_title' ];
415  },
416  ];
417  yield [
418  function ( $f ) {
419  unset( $f['ar_text_id'] );
420  return $f;
421  },
422  ];
423  yield [
424  function ( $f ) {
425  unset( $f['ar_page_id'] );
426  return $f;
427  },
428  ];
429  yield [
430  function ( $f ) {
431  unset( $f['ar_parent_id'] );
432  return $f;
433  },
434  ];
435  yield [
436  function ( $f ) {
437  unset( $f['ar_rev_id'] );
438  return $f;
439  },
440  ];
441  yield [
442  function ( $f ) {
443  unset( $f['ar_sha1'] );
444  return $f;
445  },
446  ];
447  }
448 
453  public function testNewFromArchiveRow( $selectModifier ) {
454  $services = MediaWikiServices::getInstance();
455 
456  $store = new RevisionStore(
457  $services->getDBLoadBalancer(),
458  $services->getService( '_SqlBlobStore' ),
459  $services->getMainWANObjectCache(),
460  $services->getCommentStore(),
461  $services->getContentModelStore(),
462  $services->getSlotRoleStore(),
463  $services->getSlotRoleRegistry(),
464  $this->getMcrMigrationStage(),
465  $services->getActorMigration()
466  );
467 
469  $this->setService( 'RevisionStore', $store );
470 
471  $page = $this->createPage(
472  'RevisionStorageTest_testNewFromArchiveRow',
473  'Lorem Ipsum',
475  );
476  $orig = $page->getRevision();
477  $page->doDeleteArticle( 'test Revision::newFromArchiveRow' );
478 
479  $dbr = wfGetDB( DB_REPLICA );
480  $arQuery = Revision::getArchiveQueryInfo();
481  $arQuery['fields'] = $selectModifier( $arQuery['fields'] );
482  $res = $dbr->select(
483  $arQuery['tables'], $arQuery['fields'], [ 'ar_rev_id' => $orig->getId() ],
484  __METHOD__, [], $arQuery['joins']
485  );
486  $this->assertTrue( is_object( $res ), 'query failed' );
487 
488  $row = $res->fetchObject();
489  $res->free();
490 
491  // MCR migration note: $row is now required to contain ar_title and ar_namespace.
492  // Alternatively, a Title object can be passed to RevisionStore::newRevisionFromArchiveRow
494 
495  $this->assertRevEquals( $orig, $rev );
496  }
497 
501  public function testNewFromArchiveRowOverrides() {
502  $page = $this->createPage(
503  'RevisionStorageTest_testNewFromArchiveRow',
504  'Lorem Ipsum',
506  );
507  $orig = $page->getRevision();
508  $page->doDeleteArticle( 'test Revision::newFromArchiveRow' );
509 
510  $dbr = wfGetDB( DB_REPLICA );
511  $arQuery = Revision::getArchiveQueryInfo();
512  $res = $dbr->select(
513  $arQuery['tables'], $arQuery['fields'], [ 'ar_rev_id' => $orig->getId() ],
514  __METHOD__, [], $arQuery['joins']
515  );
516  $this->assertTrue( is_object( $res ), 'query failed' );
517 
518  $row = $res->fetchObject();
519  $res->free();
520 
521  $rev = Revision::newFromArchiveRow( $row, [ 'comment_text' => 'SOMEOVERRIDE' ] );
522 
523  $this->assertNotEquals( $orig->getComment(), $rev->getComment() );
524  $this->assertEquals( 'SOMEOVERRIDE', $rev->getComment() );
525  }
526 
530  public function testNewFromId() {
531  $orig = $this->testPage->getRevision();
532  $rev = Revision::newFromId( $orig->getId() );
533  $this->assertRevEquals( $orig, $rev );
534  }
535 
539  public function testNewFromPageId() {
540  $rev = Revision::newFromPageId( $this->testPage->getId() );
541  $this->assertRevEquals(
542  $this->testPage->getRevision(),
543  $rev
544  );
545  }
546 
550  public function testNewFromPageIdWithLatestId() {
552  $this->testPage->getId(),
553  $this->testPage->getLatest()
554  );
555  $this->assertRevEquals(
556  $this->testPage->getRevision(),
557  $rev
558  );
559  }
560 
565  $content = new WikitextContent( __METHOD__ );
566  $this->testPage->doEditContent( $content, __METHOD__ );
568  $this->testPage->getId(),
569  $this->testPage->getRevision()->getPrevious()->getId()
570  );
571  $this->assertRevEquals(
572  $this->testPage->getRevision()->getPrevious(),
573  $rev
574  );
575  }
576 
580  public function testGetPage() {
581  $page = $this->testPage;
582 
583  $orig = $this->makeRevisionWithProps( [ 'page' => $page->getId() ] );
584  $rev = Revision::newFromId( $orig->getId() );
585 
586  $this->assertEquals( $page->getId(), $rev->getPage() );
587  }
588 
592  public function testIsCurrent() {
593  $rev1 = $this->testPage->getRevision();
594 
595  # @todo find out if this should be true
596  # $this->assertTrue( $rev1->isCurrent() );
597 
598  $rev1x = Revision::newFromId( $rev1->getId() );
599  $this->assertTrue( $rev1x->isCurrent() );
600 
601  $this->testPage->doEditContent( new WikitextContent( __METHOD__ ), __METHOD__ );
602  $rev2 = $this->testPage->getRevision();
603 
604  # @todo find out if this should be true
605  # $this->assertTrue( $rev2->isCurrent() );
606 
607  $rev1x = Revision::newFromId( $rev1->getId() );
608  $this->assertFalse( $rev1x->isCurrent() );
609 
610  $rev2x = Revision::newFromId( $rev2->getId() );
611  $this->assertTrue( $rev2x->isCurrent() );
612  }
613 
617  public function testGetPrevious() {
618  $oldestRevision = $this->testPage->getOldestRevision();
619  $latestRevision = $this->testPage->getLatest();
620 
621  $this->assertNull( $oldestRevision->getPrevious() );
622 
623  $this->testPage->doEditContent( new WikitextContent( __METHOD__ ), __METHOD__ );
624  $newRevision = $this->testPage->getRevision();
625 
626  $this->assertNotNull( $newRevision->getPrevious() );
627  $this->assertEquals( $latestRevision, $newRevision->getPrevious()->getId() );
628  }
629 
636  public function testTitleGetPreviousRevisionID() {
637  $oldestId = $this->testPage->getOldestRevision()->getId();
638  $latestId = $this->testPage->getLatest();
639 
640  $title = $this->testPage->getTitle();
641 
642  $this->assertFalse( $title->getPreviousRevisionID( $oldestId ) );
643 
644  $this->testPage->doEditContent( new WikitextContent( __METHOD__ ), __METHOD__ );
645  $newId = $this->testPage->getRevision()->getId();
646 
647  $this->assertEquals( $latestId, $title->getPreviousRevisionID( $newId ) );
648  }
649 
655  $this->assertFalse( $this->testPage->getTitle()->getPreviousRevisionID( 123456789 ) );
656  }
657 
661  public function testGetNext() {
662  $rev1 = $this->testPage->getRevision();
663 
664  $this->assertNull( $rev1->getNext() );
665 
666  $this->testPage->doEditContent( new WikitextContent( __METHOD__ ), __METHOD__ );
667  $rev2 = $this->testPage->getRevision();
668 
669  $this->assertNotNull( $rev1->getNext() );
670  $this->assertEquals( $rev2->getId(), $rev1->getNext()->getId() );
671  }
672 
679  public function testTitleGetNextRevisionID() {
680  $title = $this->testPage->getTitle();
681 
682  $origId = $this->testPage->getLatest();
683 
684  $this->assertFalse( $title->getNextRevisionID( $origId ) );
685 
686  $this->testPage->doEditContent( new WikitextContent( __METHOD__ ), __METHOD__ );
687  $newId = $this->testPage->getLatest();
688 
689  $this->assertSame( $this->testPage->getLatest(), $title->getNextRevisionID( $origId ) );
690  }
691 
697  $this->assertFalse( $this->testPage->getTitle()->getNextRevisionID( 123456789 ) );
698  }
699 
703  public function testNewNullRevision() {
704  $this->testPage->doEditContent( new WikitextContent( __METHOD__ ), __METHOD__ );
705  $orig = $this->testPage->getRevision();
706 
707  $dbw = wfGetDB( DB_MASTER );
708  $rev = Revision::newNullRevision( $dbw, $this->testPage->getId(), 'a null revision', false );
709 
710  $this->assertNotEquals( $orig->getId(), $rev->getId(),
711  'new null revision should have a different id from the original revision' );
712  $this->assertEquals( $orig->getTextId(), $rev->getTextId(),
713  'new null revision should have the same text id as the original revision' );
714  $this->assertEquals( $orig->getSha1(), $rev->getSha1(),
715  'new null revision should have the same SHA1 as the original revision' );
716  $this->assertTrue( $orig->getRevisionRecord()->hasSameContent( $rev->getRevisionRecord() ),
717  'new null revision should have the same content as the original revision' );
718  $this->assertEquals( __METHOD__, $rev->getContent()->getText() );
719  }
720 
724  public function testNewNullRevision_badPage() {
725  $dbw = wfGetDB( DB_MASTER );
726  $rev = Revision::newNullRevision( $dbw, -1, 'a null revision', false );
727 
728  $this->assertNull( $rev );
729  }
730 
734  public function testInsertOn() {
735  $ip = '2600:387:ed7:947e:8c16:a1ad:dd34:1dd7';
736 
737  $orig = $this->makeRevisionWithProps( [
738  'user_text' => $ip
739  ] );
740 
741  // Make sure the revision was copied to ip_changes
742  $dbr = wfGetDB( DB_REPLICA );
743  $res = $dbr->select( 'ip_changes', '*', [ 'ipc_rev_id' => $orig->getId() ] );
744  $row = $res->fetchObject();
745 
746  $this->assertEquals( IP::toHex( $ip ), $row->ipc_hex );
747  $this->assertEquals(
748  $orig->getTimestamp(),
749  wfTimestamp( TS_MW, $row->ipc_rev_timestamp )
750  );
751  }
752 
753  public static function provideUserWasLastToEdit() {
754  yield 'actually the last edit' => [ 3, true ];
755  yield 'not the current edit, but still by this user' => [ 2, true ];
756  yield 'edit by another user' => [ 1, false ];
757  yield 'first edit, by this user, but another user edited in the mean time' => [ 0, false ];
758  }
759 
764  public function testUserWasLastToEdit( $sinceIdx, $expectedLast ) {
765  $userA = User::newFromName( "RevisionStorageTest_userA" );
766  $userB = User::newFromName( "RevisionStorageTest_userB" );
767 
768  if ( $userA->getId() === 0 ) {
769  $userA = User::createNew( $userA->getName() );
770  }
771 
772  if ( $userB->getId() === 0 ) {
773  $userB = User::createNew( $userB->getName() );
774  }
775 
776  $ns = $this->getDefaultWikitextNS();
777 
778  $dbw = wfGetDB( DB_MASTER );
779  $revisions = [];
780 
781  // create revisions -----------------------------
783  'RevisionStorageTest_testUserWasLastToEdit', $ns ) );
784  $page->insertOn( $dbw );
785 
786  $revisions[0] = new Revision( [
787  'page' => $page->getId(),
788  // we need the title to determine the page's default content model
789  'title' => $page->getTitle(),
790  'timestamp' => '20120101000000',
791  'user' => $userA->getId(),
792  'text' => 'zero',
793  'content_model' => CONTENT_MODEL_WIKITEXT,
794  'comment' => 'edit zero'
795  ] );
796  $revisions[0]->insertOn( $dbw );
797 
798  $revisions[1] = new Revision( [
799  'page' => $page->getId(),
800  // still need the title, because $page->getId() is 0 (there's no entry in the page table)
801  'title' => $page->getTitle(),
802  'timestamp' => '20120101000100',
803  'user' => $userA->getId(),
804  'text' => 'one',
805  'content_model' => CONTENT_MODEL_WIKITEXT,
806  'comment' => 'edit one'
807  ] );
808  $revisions[1]->insertOn( $dbw );
809 
810  $revisions[2] = new Revision( [
811  'page' => $page->getId(),
812  'title' => $page->getTitle(),
813  'timestamp' => '20120101000200',
814  'user' => $userB->getId(),
815  'text' => 'two',
816  'content_model' => CONTENT_MODEL_WIKITEXT,
817  'comment' => 'edit two'
818  ] );
819  $revisions[2]->insertOn( $dbw );
820 
821  $revisions[3] = new Revision( [
822  'page' => $page->getId(),
823  'title' => $page->getTitle(),
824  'timestamp' => '20120101000300',
825  'user' => $userA->getId(),
826  'text' => 'three',
827  'content_model' => CONTENT_MODEL_WIKITEXT,
828  'comment' => 'edit three'
829  ] );
830  $revisions[3]->insertOn( $dbw );
831 
832  $revisions[4] = new Revision( [
833  'page' => $page->getId(),
834  'title' => $page->getTitle(),
835  'timestamp' => '20120101000200',
836  'user' => $userA->getId(),
837  'text' => 'zero',
838  'content_model' => CONTENT_MODEL_WIKITEXT,
839  'comment' => 'edit four'
840  ] );
841  $revisions[4]->insertOn( $dbw );
842 
843  // test it ---------------------------------
844  $since = $revisions[$sinceIdx]->getTimestamp();
845 
847  $allRows = iterator_to_array( $dbw->select(
848  $revQuery['tables'],
849  [ 'rev_id', 'rev_timestamp', 'rev_user' => $revQuery['fields']['rev_user'] ],
850  [
851  'rev_page' => $page->getId(),
852  //'rev_timestamp > ' . $dbw->addQuotes( $dbw->timestamp( $since ) )
853  ],
854  __METHOD__,
855  [ 'ORDER BY' => 'rev_timestamp ASC', 'LIMIT' => 50 ],
856  $revQuery['joins']
857  ) );
858 
859  $wasLast = Revision::userWasLastToEdit( $dbw, $page->getId(), $userA->getId(), $since );
860 
861  $this->assertEquals( $expectedLast, $wasLast );
862  }
863 
872  private function newTestRevision( $text, $title = "Test",
873  $model = CONTENT_MODEL_WIKITEXT, $format = null
874  ) {
875  if ( is_string( $title ) ) {
877  }
878 
879  $content = ContentHandler::makeContent( $text, $title, $model, $format );
880 
881  $rev = new Revision(
882  [
883  'id' => 42,
884  'page' => 23,
885  'title' => $title,
886 
887  'content' => $content,
888  'length' => $content->getSize(),
889  'comment' => "testing",
890  'minor_edit' => false,
891 
892  'content_format' => $format,
893  ]
894  );
895 
896  return $rev;
897  }
898 
899  public function provideGetContentModel() {
900  // NOTE: we expect the help namespace to always contain wikitext
901  return [
902  [ 'hello world', 'Help:Hello', null, null, CONTENT_MODEL_WIKITEXT ],
903  [ 'hello world', 'User:hello/there.css', null, null, CONTENT_MODEL_CSS ],
904  [ serialize( 'hello world' ), 'Dummy:Hello', null, null, DummyContentForTesting::MODEL_ID ],
905  ];
906  }
907 
912  public function testGetContentModel( $text, $title, $model, $format, $expectedModel ) {
913  $rev = $this->newTestRevision( $text, $title, $model, $format );
914 
915  $this->assertEquals( $expectedModel, $rev->getContentModel() );
916  }
917 
918  public function provideGetContentFormat() {
919  // NOTE: we expect the help namespace to always contain wikitext
920  return [
921  [ 'hello world', 'Help:Hello', null, null, CONTENT_FORMAT_WIKITEXT ],
922  [ 'hello world', 'Help:Hello', CONTENT_MODEL_CSS, null, CONTENT_FORMAT_CSS ],
923  [ 'hello world', 'User:hello/there.css', null, null, CONTENT_FORMAT_CSS ],
924  [ serialize( 'hello world' ), 'Dummy:Hello', null, null, DummyContentForTesting::MODEL_ID ],
925  ];
926  }
927 
932  public function testGetContentFormat( $text, $title, $model, $format, $expectedFormat ) {
933  $rev = $this->newTestRevision( $text, $title, $model, $format );
934 
935  $this->assertEquals( $expectedFormat, $rev->getContentFormat() );
936  }
937 
938  public function provideGetContentHandler() {
939  // NOTE: we expect the help namespace to always contain wikitext
940  return [
941  [ 'hello world', 'Help:Hello', null, null, WikitextContentHandler::class ],
942  [ 'hello world', 'User:hello/there.css', null, null, CssContentHandler::class ],
943  [ serialize( 'hello world' ), 'Dummy:Hello', null, null, DummyContentHandlerForTesting::class ],
944  ];
945  }
946 
951  public function testGetContentHandler( $text, $title, $model, $format, $expectedClass ) {
952  $rev = $this->newTestRevision( $text, $title, $model, $format );
953 
954  $this->assertEquals( $expectedClass, get_class( $rev->getContentHandler() ) );
955  }
956 
957  public function provideGetContent() {
958  // NOTE: we expect the help namespace to always contain wikitext
959  return [
960  [ 'hello world', 'Help:Hello', null, null, Revision::FOR_PUBLIC, 'hello world' ],
961  [
962  serialize( 'hello world' ),
963  'Hello',
965  null,
967  serialize( 'hello world' )
968  ],
969  [
970  serialize( 'hello world' ),
971  'Dummy:Hello',
972  null,
973  null,
975  serialize( 'hello world' )
976  ],
977  ];
978  }
979 
984  public function testGetContent( $text, $title, $model, $format,
985  $audience, $expectedSerialization
986  ) {
987  $rev = $this->newTestRevision( $text, $title, $model, $format );
988  $content = $rev->getContent( $audience );
989 
990  $this->assertEquals(
991  $expectedSerialization,
992  is_null( $content ) ? null : $content->serialize( $format )
993  );
994  }
995 
999  public function testGetContent_failure() {
1000  $rev = new Revision( [
1001  'page' => $this->testPage->getId(),
1002  'content_model' => $this->testPage->getContentModel(),
1003  'id' => 123456789, // not in the test DB
1004  ] );
1005 
1006  Wikimedia\suppressWarnings(); // bad text_id will trigger a warning.
1007 
1008  $this->assertNull( $rev->getContent(),
1009  "getContent() should return null if the revision's text blob could not be loaded." );
1010 
1011  // NOTE: check this twice, once for lazy initialization, and once with the cached value.
1012  $this->assertNull( $rev->getContent(),
1013  "getContent() should return null if the revision's text blob could not be loaded." );
1014 
1015  Wikimedia\restoreWarnings();
1016  }
1017 
1018  public function provideGetSize() {
1019  return [
1020  [ "hello world.", CONTENT_MODEL_WIKITEXT, 12 ],
1021  [ serialize( "hello world." ), DummyContentForTesting::MODEL_ID, 12 ],
1022  ];
1023  }
1024 
1029  public function testGetSize( $text, $model, $expected_size ) {
1030  $rev = $this->newTestRevision( $text, 'RevisionTest_testGetSize', $model );
1031  $this->assertEquals( $expected_size, $rev->getSize() );
1032  }
1033 
1034  public function provideGetSha1() {
1035  return [
1036  [ "hello world.", CONTENT_MODEL_WIKITEXT, Revision::base36Sha1( "hello world." ) ],
1037  [
1038  serialize( "hello world." ),
1040  Revision::base36Sha1( serialize( "hello world." ) )
1041  ],
1042  ];
1043  }
1044 
1049  public function testGetSha1( $text, $model, $expected_hash ) {
1050  $rev = $this->newTestRevision( $text, 'RevisionTest_testGetSha1', $model );
1051  $this->assertEquals( $expected_hash, $rev->getSha1() );
1052  }
1053 
1059  public function testGetContentClone() {
1060  $content = new RevisionTestModifyableContent( "foo" );
1061 
1062  $rev = new Revision(
1063  [
1064  'id' => 42,
1065  'page' => 23,
1066  'title' => Title::newFromText( "testGetContentClone_dummy" ),
1067 
1068  'content' => $content,
1069  'length' => $content->getSize(),
1070  'comment' => "testing",
1071  'minor_edit' => false,
1072  ]
1073  );
1074 
1076  $content = $rev->getContent( Revision::RAW );
1077  $content->setText( "bar" );
1078 
1080  $content2 = $rev->getContent( Revision::RAW );
1081  // content is mutable, expect clone
1082  $this->assertNotSame( $content, $content2, "expected a clone" );
1083  // clone should contain the original text
1084  $this->assertEquals( "foo", $content2->getText() );
1085 
1086  $content2->setText( "bla bla" );
1087  // clones should be independent
1088  $this->assertEquals( "bar", $content->getText() );
1089  }
1090 
1095  public function testGetContentUncloned() {
1096  $rev = $this->newTestRevision( "hello", "testGetContentUncloned_dummy", CONTENT_MODEL_WIKITEXT );
1097  $content = $rev->getContent( Revision::RAW );
1098  $content2 = $rev->getContent( Revision::RAW );
1099 
1100  // for immutable content like wikitext, this should be the same object
1101  $this->assertSame( $content, $content2 );
1102  }
1103 
1107  public function testLoadFromId() {
1108  $rev = $this->testPage->getRevision();
1109  $this->hideDeprecated( 'Revision::loadFromId' );
1110  $this->assertRevEquals(
1111  $rev,
1112  Revision::loadFromId( wfGetDB( DB_MASTER ), $rev->getId() )
1113  );
1114  }
1115 
1119  public function testLoadFromPageId() {
1120  $this->assertRevEquals(
1121  $this->testPage->getRevision(),
1122  Revision::loadFromPageId( wfGetDB( DB_MASTER ), $this->testPage->getId() )
1123  );
1124  }
1125 
1130  $this->assertRevEquals(
1131  $this->testPage->getRevision(),
1133  wfGetDB( DB_MASTER ),
1134  $this->testPage->getId(),
1135  $this->testPage->getLatest()
1136  )
1137  );
1138  }
1139 
1144  $this->testPage->doEditContent( new WikitextContent( __METHOD__ ), __METHOD__ );
1145  $this->assertRevEquals(
1146  $this->testPage->getRevision()->getPrevious(),
1148  wfGetDB( DB_MASTER ),
1149  $this->testPage->getId(),
1150  $this->testPage->getRevision()->getPrevious()->getId()
1151  )
1152  );
1153  }
1154 
1158  public function testLoadFromTitle() {
1159  $this->assertRevEquals(
1160  $this->testPage->getRevision(),
1161  Revision::loadFromTitle( wfGetDB( DB_MASTER ), $this->testPage->getTitle() )
1162  );
1163  }
1164 
1169  $this->assertRevEquals(
1170  $this->testPage->getRevision(),
1172  wfGetDB( DB_MASTER ),
1173  $this->testPage->getTitle(),
1174  $this->testPage->getLatest()
1175  )
1176  );
1177  }
1178 
1183  $this->testPage->doEditContent( new WikitextContent( __METHOD__ ), __METHOD__ );
1184  $this->assertRevEquals(
1185  $this->testPage->getRevision()->getPrevious(),
1187  wfGetDB( DB_MASTER ),
1188  $this->testPage->getTitle(),
1189  $this->testPage->getRevision()->getPrevious()->getId()
1190  )
1191  );
1192  }
1193 
1197  public function testLoadFromTimestamp() {
1198  $this->assertRevEquals(
1199  $this->testPage->getRevision(),
1201  wfGetDB( DB_MASTER ),
1202  $this->testPage->getTitle(),
1203  $this->testPage->getRevision()->getTimestamp()
1204  )
1205  );
1206  }
1207 
1211  public function testGetParentLengths_noRevIds() {
1212  $this->assertSame(
1213  [],
1215  wfGetDB( DB_MASTER ),
1216  []
1217  )
1218  );
1219  }
1220 
1224  public function testGetParentLengths_oneRevId() {
1225  $text = '831jr091jr0921kr21kr0921kjr0921j09rj1';
1226  $textLength = strlen( $text );
1227 
1228  $this->testPage->doEditContent( new WikitextContent( $text ), __METHOD__ );
1229  $rev[1] = $this->testPage->getLatest();
1230 
1231  $this->assertSame(
1232  [ $rev[1] => $textLength ],
1234  wfGetDB( DB_MASTER ),
1235  [ $rev[1] ]
1236  )
1237  );
1238  }
1239 
1244  $textOne = '831jr091jr0921kr21kr0921kjr0921j09rj1';
1245  $textOneLength = strlen( $textOne );
1246  $textTwo = '831jr091jr092121j09rj1';
1247  $textTwoLength = strlen( $textTwo );
1248 
1249  $this->testPage->doEditContent( new WikitextContent( $textOne ), __METHOD__ );
1250  $rev[1] = $this->testPage->getLatest();
1251  $this->testPage->doEditContent( new WikitextContent( $textTwo ), __METHOD__ );
1252  $rev[2] = $this->testPage->getLatest();
1253 
1254  $this->assertSame(
1255  [ $rev[1] => $textOneLength, $rev[2] => $textTwoLength ],
1257  wfGetDB( DB_MASTER ),
1258  [ $rev[1], $rev[2] ]
1259  )
1260  );
1261  }
1262 
1267  $this->assertTrue(
1268  $this->testPage->getTitle()->equals(
1269  $this->testPage->getRevision()->getTitle()
1270  )
1271  );
1272  }
1273 
1278  $rev = new Revision( [ 'id' => $this->testPage->getLatest() ] );
1279  $this->assertTrue(
1280  $this->testPage->getTitle()->equals(
1281  $rev->getTitle()
1282  )
1283  );
1284  }
1285 
1289  public function testIsMinor_true() {
1290  // Use a sysop to ensure we can mark edits as minor
1291  $sysop = $this->getTestSysop()->getUser();
1292 
1293  $this->testPage->doEditContent(
1294  new WikitextContent( __METHOD__ ),
1295  __METHOD__,
1296  EDIT_MINOR,
1297  false,
1298  $sysop
1299  );
1300  $rev = $this->testPage->getRevision();
1301 
1302  $this->assertSame( true, $rev->isMinor() );
1303  }
1304 
1308  public function testIsMinor_false() {
1309  $this->testPage->doEditContent(
1310  new WikitextContent( __METHOD__ ),
1311  __METHOD__,
1312  0
1313  );
1314  $rev = $this->testPage->getRevision();
1315 
1316  $this->assertSame( false, $rev->isMinor() );
1317  }
1318 
1322  public function testGetTimestamp() {
1323  $testTimestamp = wfTimestampNow();
1324 
1325  $this->testPage->doEditContent(
1326  new WikitextContent( __METHOD__ ),
1327  __METHOD__
1328  );
1329  $rev = $this->testPage->getRevision();
1330 
1331  $this->assertInternalType( 'string', $rev->getTimestamp() );
1332  $this->assertTrue( strlen( $rev->getTimestamp() ) == strlen( 'YYYYMMDDHHMMSS' ) );
1333  $this->assertContains( substr( $testTimestamp, 0, 10 ), $rev->getTimestamp() );
1334  }
1335 
1340  public function testGetUserAndText() {
1341  $sysop = $this->getTestSysop()->getUser();
1342 
1343  $this->testPage->doEditContent(
1344  new WikitextContent( __METHOD__ ),
1345  __METHOD__,
1346  0,
1347  false,
1348  $sysop
1349  );
1350  $rev = $this->testPage->getRevision();
1351 
1352  $this->assertSame( $sysop->getId(), $rev->getUser() );
1353  $this->assertSame( $sysop->getName(), $rev->getUserText() );
1354  }
1355 
1359  public function testIsDeleted_nothingDeleted() {
1360  $rev = $this->testPage->getRevision();
1361 
1362  $this->assertSame( false, $rev->isDeleted( Revision::DELETED_TEXT ) );
1363  $this->assertSame( false, $rev->isDeleted( Revision::DELETED_COMMENT ) );
1364  $this->assertSame( false, $rev->isDeleted( Revision::DELETED_RESTRICTED ) );
1365  $this->assertSame( false, $rev->isDeleted( Revision::DELETED_USER ) );
1366  }
1367 
1372  $rev = $this->testPage->getRevision();
1373 
1374  $this->assertSame( 0, $rev->getVisibility() );
1375  }
1376 
1380  public function testGetComment_notDeleted() {
1381  $expectedSummary = 'goatlicious summary';
1382 
1383  $this->testPage->doEditContent(
1384  new WikitextContent( __METHOD__ ),
1385  $expectedSummary
1386  );
1387  $rev = $this->testPage->getRevision();
1388 
1389  $this->assertSame( $expectedSummary, $rev->getComment() );
1390  }
1391 
1396  $this->testPage->doEditContent( new WikitextContent( __METHOD__ ), __METHOD__ );
1397  $rev = $this->testPage->getRevision();
1398 
1399  $this->assertGreaterThan( 0, $rev->isUnpatrolled() );
1400  $this->assertSame( $rev->getRecentChange()->getAttribute( 'rc_id' ), $rev->isUnpatrolled() );
1401  }
1402 
1407  // This assumes that sysops are auto patrolled
1408  $sysop = $this->getTestSysop()->getUser();
1409  $this->testPage->doEditContent(
1410  new WikitextContent( __METHOD__ ),
1411  __METHOD__,
1412  0,
1413  false,
1414  $sysop
1415  );
1416  $rev = $this->testPage->getRevision();
1417 
1418  $this->assertSame( 0, $rev->isUnpatrolled() );
1419  }
1420 
1430  public function testSimpleContentGetters() {
1431  $expectedText = 'testSimpleContentGetters in Revision. Goats love MCR...';
1432  $expectedSummary = 'goatlicious testSimpleContentGetters summary';
1433 
1434  $this->testPage->doEditContent(
1435  new WikitextContent( $expectedText ),
1436  $expectedSummary
1437  );
1438  $rev = $this->testPage->getRevision();
1439 
1440  $this->assertSame( $expectedText, $rev->getContent()->getText() );
1441  $this->assertSame( $expectedText, $rev->getSerializedData() );
1442  $this->assertSame( $this->testPage->getContentModel(), $rev->getContentModel() );
1443  $this->assertSame( $this->testPage->getContent()->getDefaultFormat(), $rev->getContentFormat() );
1444  $this->assertSame( $this->testPage->getContentHandler(), $rev->getContentHandler() );
1445  }
1446 
1450  public function testNewKnownCurrent() {
1451  // Setup the services
1452  $this->overrideMwServices();
1453  $cache = new WANObjectCache( [ 'cache' => new HashBagOStuff() ] );
1454  $this->setService( 'MainWANObjectCache', $cache );
1455  $db = wfGetDB( DB_MASTER );
1456 
1457  $now = 1553893742;
1458  $cache->setMockTime( $now );
1459 
1460  // Get a fresh revision to use during testing
1461  $this->testPage->doEditContent( new WikitextContent( __METHOD__ ), __METHOD__ );
1462  $rev = $this->testPage->getRevision();
1463 
1464  // Clear any previous cache for the revision during creation
1465  $key = $cache->makeGlobalKey(
1466  RevisionStore::ROW_CACHE_KEY,
1467  $db->getDomainID(),
1468  $rev->getPage(),
1469  $rev->getId()
1470  );
1471  $cache->delete( $key, WANObjectCache::HOLDOFF_NONE );
1472  $this->assertFalse( $cache->get( $key ) );
1473 
1474  ++$now;
1475 
1476  // Get the new revision and make sure it is in the cache and correct
1477  $newRev = Revision::newKnownCurrent( $db, $rev->getPage(), $rev->getId() );
1478  $this->assertRevEquals( $rev, $newRev );
1479 
1480  $cachedRow = $cache->get( $key );
1481  $this->assertNotFalse( $cachedRow );
1482  $this->assertEquals( $rev->getId(), $cachedRow->rev_id );
1483  }
1484 
1486  $db = wfGetDB( DB_MASTER );
1487 
1488  $this->testPage->doEditContent( new WikitextContent( __METHOD__ ), __METHOD__ );
1489  $rev = $this->testPage->getRevision();
1490 
1491  $pageId = $this->testPage->getId();
1492 
1493  $newRev = Revision::newKnownCurrent( $db, $pageId, $rev->getId() );
1494  $this->assertRevEquals( $rev, $newRev );
1495  }
1496 
1498  $db = wfGetDB( DB_MASTER );
1499 
1500  $this->assertFalse( Revision::newKnownCurrent( $db, 0 ) );
1501  }
1502 
1503  public function provideUserCanBitfield() {
1504  yield [ 0, 0, [], null, true ];
1505  // Bitfields match, user has no permissions
1510  // Bitfields match, user (admin) does have permissions
1511  yield [ Revision::DELETED_TEXT, Revision::DELETED_TEXT, [ 'sysop' ], null, true ];
1513  yield [ Revision::DELETED_USER, Revision::DELETED_USER, [ 'sysop' ], null, true ];
1514  // Bitfields match, user (admin) does not have permissions
1516  // Bitfields match, user (oversight) does have permissions
1518  // Check permissions using the title
1519  yield [
1522  [ 'sysop' ],
1523  __METHOD__,
1524  true,
1525  ];
1526  yield [
1529  [],
1530  __METHOD__,
1531  false,
1532  ];
1533  }
1534 
1539  public function testUserCanBitfield( $bitField, $field, $userGroups, $title, $expected ) {
1541 
1542  $this->setMwGlobals(
1543  'wgGroupPermissions',
1544  [
1545  'sysop' => [
1546  'deletedtext' => true,
1547  'deletedhistory' => true,
1548  ],
1549  'oversight' => [
1550  'viewsuppressed' => true,
1551  'suppressrevision' => true,
1552  ],
1553  ]
1554  );
1555  $user = $this->getTestUser( $userGroups )->getUser();
1556 
1557  $this->assertSame(
1558  $expected,
1559  Revision::userCanBitfield( $bitField, $field, $user, $title )
1560  );
1561 
1562  // Fallback to $wgUser
1563  $this->setMwGlobals(
1564  'wgUser',
1565  $user
1566  );
1567  $this->assertSame(
1568  $expected,
1569  Revision::userCanBitfield( $bitField, $field, null, $title )
1570  );
1571  }
1572 
1573  public function provideUserCan() {
1574  yield [ 0, 0, [], true ];
1575  // Bitfields match, user has no permissions
1580  // Bitfields match, user (admin) does have permissions
1581  yield [ Revision::DELETED_TEXT, Revision::DELETED_TEXT, [ 'sysop' ], true ];
1582  yield [ Revision::DELETED_COMMENT, Revision::DELETED_COMMENT, [ 'sysop' ], true ];
1583  yield [ Revision::DELETED_USER, Revision::DELETED_USER, [ 'sysop' ], true ];
1584  // Bitfields match, user (admin) does not have permissions
1586  // Bitfields match, user (oversight) does have permissions
1587  yield [ Revision::DELETED_RESTRICTED, Revision::DELETED_RESTRICTED, [ 'oversight' ], true ];
1588  }
1589 
1594  public function testUserCan( $bitField, $field, $userGroups, $expected ) {
1595  $this->setMwGlobals(
1596  'wgGroupPermissions',
1597  [
1598  'sysop' => [
1599  'deletedtext' => true,
1600  'deletedhistory' => true,
1601  ],
1602  'oversight' => [
1603  'viewsuppressed' => true,
1604  'suppressrevision' => true,
1605  ],
1606  ]
1607  );
1608  $user = $this->getTestUser( $userGroups )->getUser();
1609  $revision = new Revision( [ 'deleted' => $bitField ], 0, $this->testPage->getTitle() );
1610 
1611  $this->assertSame(
1612  $expected,
1613  $revision->userCan( $field, $user )
1614  );
1615  }
1616 
1617  public function provideGetTextId() {
1618  yield [ [], null ];
1619 
1620  $slot = new SlotRecord( (object)[
1621  'slot_revision_id' => 42,
1622  'slot_content_id' => 1,
1623  'content_address' => 'tt:789',
1624  'model_name' => CONTENT_MODEL_WIKITEXT,
1625  'role_name' => SlotRecord::MAIN,
1626  'slot_origin' => 1,
1627  ], new WikitextContent( 'Test' ) );
1628 
1629  $rec = new MutableRevisionRecord( $this->testPage->getTitle() );
1630  $rec->setId( 42 );
1631  $rec->setSlot( $slot );
1632 
1633  yield [ $rec, 789 ];
1634  }
1635 
1640  public function testGetTextId( $spec, $expected ) {
1641  $rev = new Revision( $spec, 0, $this->testPage->getTitle() );
1642  $this->assertSame( $expected, $rev->getTextId() );
1643  }
1644 
1645  abstract public function provideGetRevisionText();
1646 
1651  public function testGetRevisionText( array $queryInfoOptions, array $queryInfoExtra = [] ) {
1652  $rev = $this->testPage->getRevisionRecord();
1653 
1654  $queryInfo = Revision::getQueryInfo( $queryInfoOptions );
1655  $queryInfo['tables'] = array_merge( $queryInfo['tables'], $queryInfoExtra['tables'] ?? [] );
1656  $queryInfo['fields'] = array_merge( $queryInfo['fields'], $queryInfoExtra['fields'] ?? [] );
1657  $queryInfo['joins'] = array_merge( $queryInfo['joins'], $queryInfoExtra['joins'] ?? [] );
1658 
1659  $conds = [ 'rev_id' => $rev->getId() ];
1660  $row = $this->db->selectRow(
1661  $queryInfo['tables'],
1662  $queryInfo['fields'],
1663  $conds,
1664  __METHOD__,
1665  [],
1666  $queryInfo['joins']
1667  );
1668 
1669  $expected = $rev->getContent( SlotRecord::MAIN )->serialize();
1670 
1671  $this->hideDeprecated( 'Revision::getRevisionText (MCR without SCHEMA_COMPAT_WRITE_OLD)' );
1672  $this->assertSame( $expected, Revision::getRevisionText( $row ) );
1673  }
1674 
1675 }
static factory(Title $title)
Create a WikiPage object of the appropriate class for the given title.
Definition: WikiPage.php:138
testLoadFromTitleWithLatestRevId()
Revision::loadFromTitle.
testUserWasLastToEdit( $sinceIdx, $expectedLast)
Revision::userWasLastToEdit provideUserWasLastToEdit.
testGetParentLengths_noRevIds()
Revision::getParentLengths.
testInsertOn_exceptionOnIncomplete( $array, $expException, $expMessage)
provideInsertOn_exceptionOnIncomplete Revision::insertOn
testGetContentModel( $text, $title, $model, $format, $expectedModel)
provideGetContentModel Revision::getContentModel
testNewFromRow()
Revision::newFromRow.
static newFromArchiveRow( $row, $overrides=[])
Make a fake revision object from an archive table row.
Definition: Revision.php:171
testGetTextId( $spec, $expected)
provideGetTextId Revision::getTextId()
static toHex( $ip)
Return a zero-padded upper case hexadecimal representation of an IP address.
Definition: IP.php:404
testNewFromId()
Revision::newFromId.
const CONTENT_MODEL_WIKITEXT
Definition: Defines.php:231
testSimpleContentGetters()
This is a simple blanket test for all simple content getters and is methods to provide some coverage ...
getPage()
Get the page ID.
Definition: Revision.php:782
processing should stop and the error should be shown to the user * false
Definition: hooks.txt:187
serialize()
testUserCanBitfield( $bitField, $field, $userGroups, $title, $expected)
provideUserCanBitfield Revision::userCanBitfield
Apache License January AND DISTRIBUTION Definitions License shall mean the terms and conditions for use
getTimestamp()
Definition: Revision.php:994
testLoadFromPageId()
Revision::loadFromPageId.
testTitleGetNextRevisionID_invalid()
Title::getNextRevisionID Title::getRelativeRevisionID.
testGetContent( $text, $title, $model, $format, $audience, $expectedSerialization)
provideGetContent Revision::getContent
testLoadFromTitle()
Revision::loadFromTitle.
wfGetDB( $db, $groups=[], $wiki=false)
Get a Database object.
static loadFromTimestamp( $db, $title, $timestamp)
Load the revision for the given title with the given timestamp.
Definition: Revision.php:295
static getParentLengths( $db, array $revIds)
Do a batched query to get the parent revision lengths.
Definition: Revision.php:538
testGetRecentChange()
Revision::getRecentChange.
__construct( $name=null, array $data=[], $dataName='')
static newFromPageId( $pageId, $revId=0, $flags=0)
Load either the current, or a specified, revision that&#39;s attached to a given page ID...
Definition: Revision.php:156
testTitleGetNextRevisionID()
Title::getNextRevisionID Title::getRelativeRevisionID MediaWiki\Revision\RevisionStore::getNextRevisi...
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 MediaWikiServices
Definition: injection.txt:23
static loadFromPageId( $db, $pageid, $id=0)
Load either the current, or a specified, revision that&#39;s attached to a given page.
Definition: Revision.php:260
testGetSize( $text, $model, $expected_size)
Revision::getSize provideGetSize.
const EDIT_MINOR
Definition: Defines.php:150
testLoadFromPageIdWithNotLatestRevId()
Revision::loadFromPageId.
mergeMwGlobalArrayValue( $name, $values)
Merges the given values into a MW global array variable.
Value object representing a content slot associated with a page revision.
Definition: SlotRecord.php:39
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:1304
static newFromTitle(LinkTarget $linkTarget, $id=0, $flags=0)
Load either the current, or a specified, revision that&#39;s attached to a given link target...
Definition: Revision.php:137
testNewFromArchiveRowOverrides()
Revision::newFromArchiveRow.
const DB_MASTER
Definition: defines.php:26
testNewFromPageIdWithLatestId()
Revision::newFromPageId.
static getRevisionText( $row, $prefix='old_', $wiki=false)
Get revision text associated with an old or archive row.
Definition: Revision.php:1046
testNewFromPageId()
Revision::newFromPageId.
testIsUnpatrolled_returnsRecentChangesId()
Revision::isUnpatrolled.
testInsertOn_success()
Revision::insertOn.
testGetVisibility_nothingDeleted()
Revision::getVisibility.
Mutable RevisionRecord implementation, for building new revision entries programmatically.
const CONTENT_FORMAT_CSS
Definition: Defines.php:250
testNewFromTitle_withoutId()
Revision::newFromTitle.
$newRev
Definition: pageupdater.txt:66
getDefaultWikitextNS()
Returns the ID of a namespace that defaults to Wikitext.
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 true
Definition: hooks.txt:1982
testGetParentLengths_oneRevId()
Revision::getParentLengths.
wfTimestamp( $outputtype=TS_UNIX, $ts=0)
Get a timestamp string in one of various formats.
testUserCan( $bitField, $field, $userGroups, $expected)
provideUserCan Revision::userCan
static getTestSysop()
Convenience method for getting an immutable admin test user.
testNewFromTitle_withBadId()
Revision::newFromTitle.
assertRevEquals(Revision $orig, Revision $rev=null)
testInsertOn()
Revision::insertOn.
testTitleGetPreviousRevisionID()
Title::getPreviousRevisionID Title::getRelativeRevisionID MediaWiki\Revision\RevisionStore::getPrevio...
getUser( $audience=self::FOR_PUBLIC, User $user=null)
Fetch revision&#39;s user id if it&#39;s available to the specified audience.
Definition: Revision.php:799
const FOR_PUBLIC
Definition: Revision.php:54
testGetContentFormat( $text, $title, $model, $format, $expectedFormat)
provideGetContentFormat Revision::getContentFormat
static configuration should be added through ResourceLoaderGetConfigVars instead can be used to get the real title e g db for database replication lag or jobqueue for job queue size converted to pseudo seconds It is possible to add more fields and they will be returned to the user in the API response after the basic globals have been set but before ordinary actions take place or wrap services the preferred way to define a new service is the $wgServiceWiringFiles array $services
Definition: hooks.txt:2217
testIsCurrent()
Revision::isCurrent.
static newKnownCurrent(IDatabase $db, $pageIdOrTitle, $revId=0)
Load a revision based on a known page ID and current revision ID from the DB.
Definition: Revision.php:1325
getId()
Get revision ID.
Definition: Revision.php:638
testIsUnpatrolled_returnsZeroIfPatrolled()
Revision::isUnpatrolled.
Service for looking up page revisions.
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&#39;s marked as deleted.
Definition: Revision.php:1242
$res
Definition: database.txt:21
testIsMinor_false()
Revision::isMinor.
testIsMinor_true()
Revision::isMinor.
wfTimestampNow()
Convenience function; returns MediaWiki timestamp for the present time.
static loadFromId( $db, $id)
Load a page revision from a given revision ID number.
Definition: Revision.php:242
$cache
Definition: mcc.php:33
testGetPage()
Revision::getPage.
testNewFromTitle_withId()
Revision::newFromTitle.
static loadFromTitle( $db, $title, $id=0)
Load either the current, or a specified, revision that&#39;s attached to a given page.
Definition: Revision.php:277
const DELETED_RESTRICTED
Definition: Revision.php:49
this hook is for auditing only or null if authentication failed before getting that far or null if we can t even determine that When $user is not null
Definition: hooks.txt:780
static getArchiveQueryInfo()
Return the tables, fields, and join conditions to be selected to create a new archived revision objec...
Definition: Revision.php:525
namespace and then decline to actually register it file or subcat img or subcat $title
Definition: hooks.txt:925
static getQueryInfo( $options=[])
Return the tables, fields, and join conditions to be selected to create a new revision object...
Definition: Revision.php:511
testNewFromPageIdWithNotLatestId()
Revision::newFromPageId.
const HOLDOFF_NONE
Idiom for delete() for "no hold-off".
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:1766
overrideMwServices(Config $configOverrides=null, array $services=[])
Stashes the global instance of MediaWikiServices, and installs a new one, allowing test cases to over...
testGetTimestamp()
Revision::getTimestamp.
setMwGlobals( $pairs, $value=null)
Sets a global, maintaining a stashed version of the previous global to be restored in tearDown...
const RAW
Definition: Revision.php:56
testNewNullRevision_badPage()
Revision::newNullRevision.
testNewKnownCurrent()
Revision::newKnownCurrent.
hideDeprecated( $function)
Don&#39;t throw a warning if $function is deprecated and called later.
testGetContentUncloned()
Tests whether $rev->getContent() returns the same object repeatedly if appropriate.
assertSelect( $table, $fields, $condition, array $expectedRows, array $options=[], array $join_conds=[])
Asserts that the given database query yields the rows given by $expectedRows.
getContentModel()
Returns the content model for the main slot of this revision.
Definition: Revision.php:955
insertOn( $dbw)
Insert a new revision into the database, returning the new revision ID number on success and dies hor...
Definition: Revision.php:1150
const DELETED_TEXT
Definition: Revision.php:46
testLoadFromPageIdWithLatestRevId()
Revision::loadFromPageId.
testGetTitle_fromRevisionWhichWillLoadTheTitle()
Revision::getTitle.
testGetRevisionText(array $queryInfoOptions, array $queryInfoExtra=[])
provideGetRevisionText Revision::getRevisionText
setService( $name, $object)
Sets a service, maintaining a stashed version of the previous service to be restored in tearDown...
testGetContentClone()
Tests whether $rev->getContent() returns a clone when needed.
testGetComment_notDeleted()
Revision::getComment.
testGetParentLengths_multipleRevIds()
Revision::getParentLengths.
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
testGetContent_failure()
Revision::getContent.
const DELETED_USER
Definition: Revision.php:48
testLoadFromId()
Revision::loadFromId.
static newFromRow( $row)
Definition: Revision.php:222
setId( $id)
Set the revision ID.
you have access to all of the normal MediaWiki so you can get a DB use the etc For full docs on the Maintenance class
Definition: maintenance.txt:52
const EDIT_NEW
Definition: Defines.php:148
testNewFromArchiveRow( $selectModifier)
provideNewFromArchiveRow Revision::newFromArchiveRow
testLoadFromTimestamp()
Revision::loadFromTimestamp()
setContentHandlerUseDB( $contentHandlerUseDB)
$revQuery
testGetPrevious()
Revision::getPrevious.
static base36Sha1( $text)
Get the base 36 SHA-1 value for a string of text.
Definition: Revision.php:1177
makeRevisionWithProps( $props=null)
const SCHEMA_COMPAT_NEW
Definition: Defines.php:287
const CONTENT_MODEL_CSS
Definition: Defines.php:233
$f
Definition: router.php:79
Database $db
Primary database.
testGetNext()
Revision::getNext.
testNewNullRevision()
Revision::newNullRevision.
testIsDeleted_nothingDeleted()
Revision::isDeleted.
Allows to change the fields on the form that will be generated $name
Definition: hooks.txt:271
const CONTENT_FORMAT_WIKITEXT
Definition: Defines.php:246
testGetTitle_fromExistingRevision()
Revision::getTitle.
RevisionDbTestBase contains test cases for the Revision class that have Database interactions.
const DB_REPLICA
Definition: defines.php:25
testGetUserAndText()
Revision::getUser Revision::getUserText.
testGetSha1( $text, $model, $expected_hash)
Revision::getSha1 provideGetSha1.
const DELETED_COMMENT
Definition: Revision.php:47
static createNew( $name, $params=[])
Add a user to the database, return the user object.
Definition: User.php:4174
testNewKnownCurrent_returnsFalseWhenTitleDoesntExist()
static newFromName( $name, $validate='valid')
Static factory method for creation from username.
Definition: User.php:594
static newFromId( $id, $flags=0)
Load a page revision from a given revision ID number.
Definition: Revision.php:118
getContentFormat()
Returns the content format for the main slot of this revision.
Definition: Revision.php:970
createPage( $titleString, $text, $model=null)
static makeContent( $text, Title $title=null, $modelId=null, $format=null)
Convenience function for creating a Content object from a given textual representation.
$content
Definition: pageupdater.txt:72
testLoadFromTitleWithNotLatestRevId()
Revision::loadFromTitle.
static getTestUser( $groups=[])
Convenience method for getting an immutable test user.
static newNullRevision( $dbw, $pageId, $summary, $minor, $user=null)
Create a new null-revision for insertion into a page&#39;s history.
Definition: Revision.php:1196
testGetContentHandler( $text, $title, $model, $format, $expectedClass)
provideGetContentHandler Revision::getContentHandler
newTestRevision( $text, $title="Test", $model=CONTENT_MODEL_WIKITEXT, $format=null)
getSha1()
Returns the base36 sha1 of the content in this revision, or null if unknown.
Definition: Revision.php:739
testTitleGetPreviousRevisionID_invalid()
Title::getPreviousRevisionID Title::getRelativeRevisionID.
return true to allow those checks to and false if checking is done & $user
Definition: hooks.txt:1473
static newFromText( $text, $defaultNamespace=NS_MAIN)
Create a new Title from text, such as what one would find in a link.
Definition: Title.php:319