MediaWiki  1.33.0
DerivedPageDataUpdaterTest.php
Go to the documentation of this file.
1 <?php
2 
4 
19 use PHPUnit\Framework\MockObject\MockObject;
22 use Title;
23 use User;
24 use Wikimedia\TestingAccessWrapper;
27 
34 
35  public function tearDown() {
36  MWTimestamp::setFakeTime( false );
37 
38  parent::tearDown();
39  }
40 
46  private function getTitle( $title ) {
48  }
49 
55  private function getPage( $title ) {
56  $title = ( $title instanceof Title ) ? $title : $this->getTitle( $title );
57 
58  return WikiPage::factory( $title );
59  }
60 
66  private function getDerivedPageDataUpdater( $page, RevisionRecord $rec = null ) {
67  if ( is_string( $page ) || $page instanceof Title ) {
68  $page = $this->getPage( $page );
69  }
70 
71  $page = TestingAccessWrapper::newFromObject( $page );
72  return $page->getDerivedDataUpdater( null, $rec );
73  }
74 
84  private function createRevision( WikiPage $page, $summary, $content = null ) {
85  $user = $this->getTestUser()->getUser();
86  $comment = CommentStoreComment::newUnsavedComment( $summary );
87 
88  if ( $content === null || is_string( $content ) ) {
89  $content = new WikitextContent( $content ?? $summary );
90  }
91 
92  if ( !is_array( $content ) ) {
93  $content = [ 'main' => $content ];
94  }
95 
96  $this->getDerivedPageDataUpdater( $page ); // flush cached instance before.
97 
98  $updater = $page->newPageUpdater( $user );
99 
100  foreach ( $content as $role => $c ) {
101  $updater->setContent( $role, $c );
102  }
103 
104  $rev = $updater->saveRevision( $comment );
105  if ( !$updater->wasSuccessful() ) {
106  $this->fail( $updater->getStatus()->getWikiText() );
107  }
108 
109  $this->getDerivedPageDataUpdater( $page ); // flush cached instance after.
110  return $rev;
111  }
112 
113  // TODO: test setArticleCountMethod() and isCountable();
114  // TODO: test isRedirect() and wasRedirect()
115 
119  public function testGetCanonicalParserOptions() {
120  $user = $this->getTestUser()->getUser();
121  $page = $this->getPage( __METHOD__ );
122 
123  $parentRev = $this->createRevision( $page, 'first' );
124 
125  $mainContent = new WikitextContent( 'Lorem ipsum' );
126 
127  $update = new RevisionSlotsUpdate();
128  $update->modifyContent( SlotRecord::MAIN, $mainContent );
129  $updater = $this->getDerivedPageDataUpdater( $page );
130  $updater->prepareContent( $user, $update, false );
131 
132  $options1 = $updater->getCanonicalParserOptions();
133  $this->assertSame( MediaWikiServices::getInstance()->getContentLanguage(),
134  $options1->getUserLangObj() );
135 
136  $speculativeId = $options1->getSpeculativeRevId();
137  $this->assertSame( $parentRev->getId() + 1, $speculativeId );
138 
139  $rev = $this->makeRevision(
140  $page->getTitle(),
141  $update,
142  $user,
143  $parentRev->getId() + 7,
144  $parentRev->getId()
145  );
146  $updater->prepareUpdate( $rev );
147 
148  $options2 = $updater->getCanonicalParserOptions();
149 
150  $currentRev = call_user_func( $options2->getCurrentRevisionCallback(), $page->getTitle() );
151  $this->assertSame( $rev->getId(), $currentRev->getId() );
152  }
153 
158  public function testGrabCurrentRevision() {
159  $page = $this->getPage( __METHOD__ );
160 
161  $updater0 = $this->getDerivedPageDataUpdater( $page );
162  $this->assertNull( $updater0->grabCurrentRevision() );
163  $this->assertFalse( $updater0->pageExisted() );
164 
165  $rev1 = $this->createRevision( $page, 'first' );
166  $updater1 = $this->getDerivedPageDataUpdater( $page );
167  $this->assertSame( $rev1->getId(), $updater1->grabCurrentRevision()->getId() );
168  $this->assertFalse( $updater0->pageExisted() );
169  $this->assertTrue( $updater1->pageExisted() );
170 
171  $rev2 = $this->createRevision( $page, 'second' );
172  $updater2 = $this->getDerivedPageDataUpdater( $page );
173  $this->assertSame( $rev1->getId(), $updater1->grabCurrentRevision()->getId() );
174  $this->assertSame( $rev2->getId(), $updater2->grabCurrentRevision()->getId() );
175  }
176 
191  public function testPrepareContent() {
192  MediaWikiServices::getInstance()->getSlotRoleRegistry()->defineRoleWithModel(
193  'aux',
195  );
196 
197  $sysop = $this->getTestUser( [ 'sysop' ] )->getUser();
198  $updater = $this->getDerivedPageDataUpdater( __METHOD__ );
199 
200  $this->assertFalse( $updater->isContentPrepared() );
201 
202  // TODO: test stash
203  // TODO: MCR: Test multiple slots. Test slot removal.
204  $mainContent = new WikitextContent( 'first [[main]] ~~~' );
205  $auxContent = new WikitextContent( 'inherited ~~~ content' );
206  $auxSlot = SlotRecord::newSaved(
207  10, 7, 'tt:7',
208  SlotRecord::newUnsaved( 'aux', $auxContent )
209  );
210 
211  $update = new RevisionSlotsUpdate();
212  $update->modifyContent( SlotRecord::MAIN, $mainContent );
213  $update->modifySlot( SlotRecord::newInherited( $auxSlot ) );
214  // TODO: MCR: test removing slots!
215 
216  $updater->prepareContent( $sysop, $update, false );
217 
218  // second be ok to call again with the same params
219  $updater->prepareContent( $sysop, $update, false );
220 
221  $this->assertNull( $updater->grabCurrentRevision() );
222  $this->assertTrue( $updater->isContentPrepared() );
223  $this->assertFalse( $updater->isUpdatePrepared() );
224  $this->assertFalse( $updater->pageExisted() );
225  $this->assertTrue( $updater->isCreation() );
226  $this->assertTrue( $updater->isChange() );
227  $this->assertFalse( $updater->isContentDeleted() );
228 
229  $this->assertNotNull( $updater->getRevision() );
230  $this->assertNotNull( $updater->getRenderedRevision() );
231 
232  $this->assertEquals( [ 'main', 'aux' ], $updater->getSlots()->getSlotRoles() );
233  $this->assertEquals( [ 'main' ], array_keys( $updater->getSlots()->getOriginalSlots() ) );
234  $this->assertEquals( [ 'aux' ], array_keys( $updater->getSlots()->getInheritedSlots() ) );
235  $this->assertEquals( [ 'main', 'aux' ], $updater->getModifiedSlotRoles() );
236  $this->assertEquals( [ 'main', 'aux' ], $updater->getTouchedSlotRoles() );
237 
238  $mainSlot = $updater->getRawSlot( SlotRecord::MAIN );
239  $this->assertInstanceOf( SlotRecord::class, $mainSlot );
240  $this->assertNotContains( '~~~', $mainSlot->getContent()->serialize(), 'PST should apply.' );
241  $this->assertContains( $sysop->getName(), $mainSlot->getContent()->serialize() );
242 
243  $auxSlot = $updater->getRawSlot( 'aux' );
244  $this->assertInstanceOf( SlotRecord::class, $auxSlot );
245  $this->assertContains( '~~~', $auxSlot->getContent()->serialize(), 'No PST should apply.' );
246 
247  $mainOutput = $updater->getCanonicalParserOutput();
248  $this->assertContains( 'first', $mainOutput->getText() );
249  $this->assertContains( '<a ', $mainOutput->getText() );
250  $this->assertNotEmpty( $mainOutput->getLinks() );
251 
252  $canonicalOutput = $updater->getCanonicalParserOutput();
253  $this->assertContains( 'first', $canonicalOutput->getText() );
254  $this->assertContains( '<a ', $canonicalOutput->getText() );
255  $this->assertContains( 'inherited ', $canonicalOutput->getText() );
256  $this->assertNotEmpty( $canonicalOutput->getLinks() );
257  }
258 
265  public function testPrepareContentInherit() {
266  $sysop = $this->getTestUser( [ 'sysop' ] )->getUser();
267  $page = $this->getPage( __METHOD__ );
268 
269  $mainContent1 = new WikitextContent( 'first [[main]] ({{REVISIONUSER}}) #~~~#' );
270  $mainContent2 = new WikitextContent( 'second ({{subst:REVISIONUSER}}) #~~~#' );
271 
272  $rev = $this->createRevision( $page, 'first', $mainContent1 );
273  $mainContent1 = $rev->getContent( SlotRecord::MAIN ); // get post-pst content
274  $userName = $rev->getUser()->getName();
275  $sysopName = $sysop->getName();
276 
277  $update = new RevisionSlotsUpdate();
278  $update->modifyContent( SlotRecord::MAIN, $mainContent1 );
279  $updater1 = $this->getDerivedPageDataUpdater( $page );
280  $updater1->prepareContent( $sysop, $update, false );
281 
282  $this->assertNotNull( $updater1->grabCurrentRevision() );
283  $this->assertTrue( $updater1->isContentPrepared() );
284  $this->assertTrue( $updater1->pageExisted() );
285  $this->assertFalse( $updater1->isCreation() );
286  $this->assertFalse( $updater1->isChange() );
287 
288  $this->assertNotNull( $updater1->getRevision() );
289  $this->assertNotNull( $updater1->getRenderedRevision() );
290 
291  // parser-output for null-edit uses the original author's name
292  $html = $updater1->getRenderedRevision()->getRevisionParserOutput()->getText();
293  $this->assertNotContains( $sysopName, $html, '{{REVISIONUSER}}' );
294  $this->assertNotContains( '{{REVISIONUSER}}', $html, '{{REVISIONUSER}}' );
295  $this->assertNotContains( '~~~', $html, 'signature ~~~' );
296  $this->assertContains( '(' . $userName . ')', $html, '{{REVISIONUSER}}' );
297  $this->assertContains( '>' . $userName . '<', $html, 'signature ~~~' );
298 
299  // TODO: MCR: test inheritance from parent
300  $update = new RevisionSlotsUpdate();
301  $update->modifyContent( SlotRecord::MAIN, $mainContent2 );
302  $updater2 = $this->getDerivedPageDataUpdater( $page );
303  $updater2->prepareContent( $sysop, $update, false );
304 
305  // non-null edit use the new user name in PST
306  $pstText = $updater2->getSlots()->getContent( SlotRecord::MAIN )->serialize();
307  $this->assertNotContains( '{{subst:REVISIONUSER}}', $pstText, '{{subst:REVISIONUSER}}' );
308  $this->assertNotContains( '~~~', $pstText, 'signature ~~~' );
309  $this->assertContains( '(' . $sysopName . ')', $pstText, '{{subst:REVISIONUSER}}' );
310  $this->assertContains( ':' . $sysopName . '|', $pstText, 'signature ~~~' );
311 
312  $this->assertFalse( $updater2->isCreation() );
313  $this->assertTrue( $updater2->isChange() );
314  }
315 
316  // TODO: test failure of prepareContent() when called again...
317  // - with different user
318  // - with different update
319  // - after calling prepareUpdate()
320 
333  public function testPrepareUpdate() {
334  $page = $this->getPage( __METHOD__ );
335 
336  $mainContent1 = new WikitextContent( 'first [[main]] ~~~' );
337  $rev1 = $this->createRevision( $page, 'first', $mainContent1 );
338  $updater1 = $this->getDerivedPageDataUpdater( $page, $rev1 );
339 
340  $options = []; // TODO: test *all* the options...
341  $updater1->prepareUpdate( $rev1, $options );
342 
343  $this->assertTrue( $updater1->isUpdatePrepared() );
344  $this->assertTrue( $updater1->isContentPrepared() );
345  $this->assertTrue( $updater1->isCreation() );
346  $this->assertTrue( $updater1->isChange() );
347  $this->assertFalse( $updater1->isContentDeleted() );
348 
349  $this->assertNotNull( $updater1->getRevision() );
350  $this->assertNotNull( $updater1->getRenderedRevision() );
351 
352  $this->assertEquals( [ 'main' ], $updater1->getSlots()->getSlotRoles() );
353  $this->assertEquals( [ 'main' ], array_keys( $updater1->getSlots()->getOriginalSlots() ) );
354  $this->assertEquals( [], array_keys( $updater1->getSlots()->getInheritedSlots() ) );
355  $this->assertEquals( [ 'main' ], $updater1->getModifiedSlotRoles() );
356  $this->assertEquals( [ 'main' ], $updater1->getTouchedSlotRoles() );
357 
358  // TODO: MCR: test multiple slots, test slot removal!
359 
360  $this->assertInstanceOf( SlotRecord::class, $updater1->getRawSlot( SlotRecord::MAIN ) );
361  $this->assertNotContains( '~~~~', $updater1->getRawContent( SlotRecord::MAIN )->serialize() );
362 
363  $mainOutput = $updater1->getCanonicalParserOutput();
364  $this->assertContains( 'first', $mainOutput->getText() );
365  $this->assertContains( '<a ', $mainOutput->getText() );
366  $this->assertNotEmpty( $mainOutput->getLinks() );
367 
368  $canonicalOutput = $updater1->getCanonicalParserOutput();
369  $this->assertContains( 'first', $canonicalOutput->getText() );
370  $this->assertContains( '<a ', $canonicalOutput->getText() );
371  $this->assertNotEmpty( $canonicalOutput->getLinks() );
372 
373  $mainContent2 = new WikitextContent( 'second' );
374  $rev2 = $this->createRevision( $page, 'second', $mainContent2 );
375  $updater2 = $this->getDerivedPageDataUpdater( $page, $rev2 );
376 
377  $options = []; // TODO: test *all* the options...
378  $updater2->prepareUpdate( $rev2, $options );
379 
380  $this->assertFalse( $updater2->isCreation() );
381  $this->assertTrue( $updater2->isChange() );
382 
383  $canonicalOutput = $updater2->getCanonicalParserOutput();
384  $this->assertContains( 'second', $canonicalOutput->getText() );
385  }
386 
391  $user = $this->getTestUser()->getUser();
392  $page = $this->getPage( __METHOD__ );
393 
394  $mainContent1 = new WikitextContent( 'first [[main]] ~~~' );
395 
396  $update = new RevisionSlotsUpdate();
397  $update->modifyContent( SlotRecord::MAIN, $mainContent1 );
398  $updater = $this->getDerivedPageDataUpdater( $page );
399  $updater->prepareContent( $user, $update, false );
400 
401  $mainOutput = $updater->getSlotParserOutput( SlotRecord::MAIN );
402  $canonicalOutput = $updater->getCanonicalParserOutput();
403 
404  $rev = $this->createRevision( $page, 'first', $mainContent1 );
405 
406  $options = []; // TODO: test *all* the options...
407  $updater->prepareUpdate( $rev, $options );
408 
409  $this->assertTrue( $updater->isUpdatePrepared() );
410  $this->assertTrue( $updater->isContentPrepared() );
411 
412  $this->assertSame( $mainOutput, $updater->getSlotParserOutput( SlotRecord::MAIN ) );
413  $this->assertSame( $canonicalOutput, $updater->getCanonicalParserOutput() );
414  }
415 
420  public function testPrepareUpdateOutputReset() {
421  $user = $this->getTestUser()->getUser();
422  $page = $this->getPage( __METHOD__ );
423 
424  $mainContent1 = new WikitextContent( 'first --{{REVISIONID}}--' );
425 
426  $update = new RevisionSlotsUpdate();
427  $update->modifyContent( SlotRecord::MAIN, $mainContent1 );
428  $updater = $this->getDerivedPageDataUpdater( $page );
429  $updater->prepareContent( $user, $update, false );
430 
431  $mainOutput = $updater->getSlotParserOutput( SlotRecord::MAIN );
432  $canonicalOutput = $updater->getCanonicalParserOutput();
433 
434  // prevent optimization on matching speculative ID
435  $mainOutput->setSpeculativeRevIdUsed( 0 );
436  $canonicalOutput->setSpeculativeRevIdUsed( 0 );
437 
438  $rev = $this->createRevision( $page, 'first', $mainContent1 );
439 
440  $options = []; // TODO: test *all* the options...
441  $updater->prepareUpdate( $rev, $options );
442 
443  $this->assertTrue( $updater->isUpdatePrepared() );
444  $this->assertTrue( $updater->isContentPrepared() );
445 
446  // ParserOutput objects should have been flushed.
447  $this->assertNotSame( $mainOutput, $updater->getSlotParserOutput( SlotRecord::MAIN ) );
448  $this->assertNotSame( $canonicalOutput, $updater->getCanonicalParserOutput() );
449 
450  $html = $updater->getCanonicalParserOutput()->getText();
451  $this->assertContains( '--' . $rev->getId() . '--', $html );
452 
453  // TODO: MCR: ensure that when the main slot uses {{REVISIONID}} but another slot is
454  // updated, the main slot is still re-rendered!
455  }
456 
457  // TODO: test failure of prepareUpdate() when called again with a different revision
458  // TODO: test failure of prepareUpdate() on inconsistency with prepareContent.
459 
464  $user = $this->getTestUser()->getUser();
465 
466  $mainContent = new WikitextContent( 'first [[main]] ~~~' );
467  $update = new RevisionSlotsUpdate();
468  $update->modifyContent( SlotRecord::MAIN, $mainContent );
469 
470  $updater = $this->getDerivedPageDataUpdater( __METHOD__ );
471  $updater->prepareContent( $user, $update, false );
472 
473  $canonicalOutput = $updater->getCanonicalParserOutput();
474 
475  $preparedEdit = $updater->getPreparedEdit();
476  $this->assertSame( $canonicalOutput->getCacheTime(), $preparedEdit->timestamp );
477  $this->assertSame( $canonicalOutput, $preparedEdit->output );
478  $this->assertSame( $mainContent, $preparedEdit->newContent );
479  $this->assertSame( $updater->getRawContent( SlotRecord::MAIN ), $preparedEdit->pstContent );
480  $this->assertSame( $updater->getCanonicalParserOptions(), $preparedEdit->popts );
481  $this->assertSame( null, $preparedEdit->revid );
482  }
483 
488  $clock = MWTimestamp::convert( TS_UNIX, '20100101000000' );
489  MWTimestamp::setFakeTime( function () use ( &$clock ) {
490  return $clock++;
491  } );
492 
493  $page = $this->getPage( __METHOD__ );
494 
495  $mainContent = new WikitextContent( 'first [[main]] ~~~' );
496  $update = new MutableRevisionSlots();
497  $update->setContent( SlotRecord::MAIN, $mainContent );
498 
499  $rev = $this->createRevision( $page, __METHOD__ );
500 
501  $updater = $this->getDerivedPageDataUpdater( $page );
502  $updater->prepareUpdate( $rev );
503 
504  $canonicalOutput = $updater->getCanonicalParserOutput();
505 
506  $preparedEdit = $updater->getPreparedEdit();
507  $this->assertSame( $canonicalOutput->getCacheTime(), $preparedEdit->timestamp );
508  $this->assertSame( $canonicalOutput, $preparedEdit->output );
509  $this->assertSame( $updater->getRawContent( SlotRecord::MAIN ), $preparedEdit->pstContent );
510  $this->assertSame( $updater->getCanonicalParserOptions(), $preparedEdit->popts );
511  $this->assertSame( $rev->getId(), $preparedEdit->revid );
512  }
513 
515  $user = $this->getTestUser()->getUser();
516  $page = $this->getPage( __METHOD__ );
517  $this->createRevision( $page, __METHOD__ );
518 
519  $mainContent1 = new WikitextContent( 'first' );
520 
521  $update = new RevisionSlotsUpdate();
522  $update->modifyContent( SlotRecord::MAIN, $mainContent1 );
523  $updater = $this->getDerivedPageDataUpdater( $page );
524  $updater->prepareContent( $user, $update, false );
525 
526  $dataUpdates = $updater->getSecondaryDataUpdates();
527 
528  $this->assertNotEmpty( $dataUpdates );
529 
530  $linksUpdates = array_filter( $dataUpdates, function ( $du ) {
531  return $du instanceof LinksUpdate;
532  } );
533  $this->assertCount( 1, $linksUpdates );
534  }
535 
543  $handler = $this->getMockBuilder( TextContentHandler::class )
544  ->setConstructorArgs( [ $name ] )
545  ->setMethods(
546  [ 'getSecondaryDataUpdates', 'getDeletionUpdates', 'unserializeContent' ]
547  )
548  ->getMock();
549 
550  $dataUpdate = new MWCallableUpdate( 'time' );
551  $dataUpdate->_name = "$name data update";
552 
553  $deletionUpdate = new MWCallableUpdate( 'time' );
554  $deletionUpdate->_name = "$name deletion update";
555 
556  $handler->method( 'getSecondaryDataUpdates' )->willReturn( [ $dataUpdate ] );
557  $handler->method( 'getDeletionUpdates' )->willReturn( [ $deletionUpdate ] );
558  $handler->method( 'unserializeContent' )->willReturnCallback(
559  function ( $text ) use ( $handler ) {
560  return $this->createMockContent( $handler, $text );
561  }
562  );
563 
565  'wgContentHandlers', [
566  $name => function () use ( $handler ){
567  return $handler;
568  }
569  ]
570  );
571 
572  return $handler;
573  }
574 
581  private function createMockContent( ContentHandler $handler, $text ) {
583  $content = $this->getMockBuilder( TextContent::class )
584  ->setConstructorArgs( [ $text ] )
585  ->setMethods( [ 'getModel', 'getContentHandler' ] )
586  ->getMock();
587 
588  $content->method( 'getModel' )->willReturn( $handler->getModelID() );
589  $content->method( 'getContentHandler' )->willReturn( $handler );
590 
591  return $content;
592  }
593 
595  if ( !$this->hasMultiSlotSupport() ) {
596  $this->markTestSkipped( 'Slot removal cannot happen with MCR being enabled' );
597  }
598 
599  $m1 = $this->defineMockContentModelForUpdateTesting( 'M1' );
600  $a1 = $this->defineMockContentModelForUpdateTesting( 'A1' );
601  $m2 = $this->defineMockContentModelForUpdateTesting( 'M2' );
602 
603  MediaWikiServices::getInstance()->getSlotRoleRegistry()->defineRoleWithModel(
604  'aux',
605  $a1->getModelID()
606  );
607 
608  $mainContent1 = $this->createMockContent( $m1, 'main 1' );
609  $auxContent1 = $this->createMockContent( $a1, 'aux 1' );
610  $mainContent2 = $this->createMockContent( $m2, 'main 2' );
611 
612  $user = $this->getTestUser()->getUser();
613  $page = $this->getPage( __METHOD__ );
614  $this->createRevision(
615  $page,
616  __METHOD__,
617  [ 'main' => $mainContent1, 'aux' => $auxContent1 ]
618  );
619 
620  $update = new RevisionSlotsUpdate();
621  $update->modifyContent( SlotRecord::MAIN, $mainContent2 );
622  $update->removeSlot( 'aux' );
623 
624  $page = $this->getPage( __METHOD__ );
625  $updater = $this->getDerivedPageDataUpdater( $page );
626  $updater->prepareContent( $user, $update, false );
627 
628  $dataUpdates = $updater->getSecondaryDataUpdates();
629 
630  $this->assertNotEmpty( $dataUpdates );
631 
632  $updateNames = array_map( function ( $du ) {
633  return isset( $du->_name ) ? $du->_name : get_class( $du );
634  }, $dataUpdates );
635 
636  $this->assertContains( LinksUpdate::class, $updateNames );
637  $this->assertContains( 'A1 deletion update', $updateNames );
638  $this->assertContains( 'M2 data update', $updateNames );
639  $this->assertNotContains( 'M1 data update', $updateNames );
640  }
641 
654  private function makeRevision(
655  Title $title,
656  RevisionSlotsUpdate $update,
657  User $user,
658  $comment,
659  $id = 0,
660  $parentId = 0
661  ) {
663 
664  $rev->applyUpdate( $update );
665  $rev->setUser( $user );
666  $rev->setComment( CommentStoreComment::newUnsavedComment( $comment ) );
667  $rev->setPageId( $title->getArticleID() );
668  $rev->setParentId( $parentId );
669 
670  if ( $id ) {
671  $rev->setId( $id );
672  }
673 
674  return $rev;
675  }
676 
681  private function getMockTitle( $id = 23 ) {
682  $mock = $this->getMockBuilder( Title::class )
683  ->disableOriginalConstructor()
684  ->getMock();
685  $mock->expects( $this->any() )
686  ->method( 'getDBkey' )
687  ->will( $this->returnValue( __CLASS__ ) );
688  $mock->expects( $this->any() )
689  ->method( 'getArticleID' )
690  ->will( $this->returnValue( $id ) );
691 
692  return $mock;
693  }
694 
695  public function provideIsReusableFor() {
696  $title = $this->getMockTitle();
697 
698  $user1 = User::newFromName( 'Alice' );
699  $user2 = User::newFromName( 'Bob' );
700 
701  $content1 = new WikitextContent( 'one' );
702  $content2 = new WikitextContent( 'two' );
703 
704  $update1 = new RevisionSlotsUpdate();
705  $update1->modifyContent( SlotRecord::MAIN, $content1 );
706 
707  $update1b = new RevisionSlotsUpdate();
708  $update1b->modifyContent( 'xyz', $content1 );
709 
710  $update2 = new RevisionSlotsUpdate();
711  $update2->modifyContent( SlotRecord::MAIN, $content2 );
712 
713  $rev1 = $this->makeRevision( $title, $update1, $user1, 'rev1', 11 );
714  $rev1b = $this->makeRevision( $title, $update1b, $user1, 'rev1', 11 );
715 
716  $rev2 = $this->makeRevision( $title, $update2, $user1, 'rev2', 12 );
717  $rev2x = $this->makeRevision( $title, $update2, $user2, 'rev2', 12 );
718  $rev2y = $this->makeRevision( $title, $update2, $user1, 'rev2', 122 );
719 
720  yield 'any' => [
721  '$prepUser' => null,
722  '$prepRevision' => null,
723  '$prepUpdate' => null,
724  '$forUser' => null,
725  '$forRevision' => null,
726  '$forUpdate' => null,
727  '$forParent' => null,
728  '$isReusable' => true,
729  ];
730  yield 'for any' => [
731  '$prepUser' => $user1,
732  '$prepRevision' => $rev1,
733  '$prepUpdate' => $update1,
734  '$forUser' => null,
735  '$forRevision' => null,
736  '$forUpdate' => null,
737  '$forParent' => null,
738  '$isReusable' => true,
739  ];
740  yield 'unprepared' => [
741  '$prepUser' => null,
742  '$prepRevision' => null,
743  '$prepUpdate' => null,
744  '$forUser' => $user1,
745  '$forRevision' => $rev1,
746  '$forUpdate' => $update1,
747  '$forParent' => 0,
748  '$isReusable' => true,
749  ];
750  yield 'match prepareContent' => [
751  '$prepUser' => $user1,
752  '$prepRevision' => null,
753  '$prepUpdate' => $update1,
754  '$forUser' => $user1,
755  '$forRevision' => null,
756  '$forUpdate' => $update1,
757  '$forParent' => 0,
758  '$isReusable' => true,
759  ];
760  yield 'match prepareUpdate' => [
761  '$prepUser' => null,
762  '$prepRevision' => $rev1,
763  '$prepUpdate' => null,
764  '$forUser' => $user1,
765  '$forRevision' => $rev1,
766  '$forUpdate' => null,
767  '$forParent' => 0,
768  '$isReusable' => true,
769  ];
770  yield 'match all' => [
771  '$prepUser' => $user1,
772  '$prepRevision' => $rev1,
773  '$prepUpdate' => $update1,
774  '$forUser' => $user1,
775  '$forRevision' => $rev1,
776  '$forUpdate' => $update1,
777  '$forParent' => 0,
778  '$isReusable' => true,
779  ];
780  yield 'mismatch prepareContent update' => [
781  '$prepUser' => $user1,
782  '$prepRevision' => null,
783  '$prepUpdate' => $update1,
784  '$forUser' => $user1,
785  '$forRevision' => null,
786  '$forUpdate' => $update1b,
787  '$forParent' => 0,
788  '$isReusable' => false,
789  ];
790  yield 'mismatch prepareContent user' => [
791  '$prepUser' => $user1,
792  '$prepRevision' => null,
793  '$prepUpdate' => $update1,
794  '$forUser' => $user2,
795  '$forRevision' => null,
796  '$forUpdate' => $update1,
797  '$forParent' => 0,
798  '$isReusable' => false,
799  ];
800  yield 'mismatch prepareContent parent' => [
801  '$prepUser' => $user1,
802  '$prepRevision' => null,
803  '$prepUpdate' => $update1,
804  '$forUser' => $user1,
805  '$forRevision' => null,
806  '$forUpdate' => $update1,
807  '$forParent' => 7,
808  '$isReusable' => false,
809  ];
810  yield 'mismatch prepareUpdate revision update' => [
811  '$prepUser' => null,
812  '$prepRevision' => $rev1,
813  '$prepUpdate' => null,
814  '$forUser' => null,
815  '$forRevision' => $rev1b,
816  '$forUpdate' => null,
817  '$forParent' => 0,
818  '$isReusable' => false,
819  ];
820  yield 'mismatch prepareUpdate revision id' => [
821  '$prepUser' => null,
822  '$prepRevision' => $rev2,
823  '$prepUpdate' => null,
824  '$forUser' => null,
825  '$forRevision' => $rev2y,
826  '$forUpdate' => null,
827  '$forParent' => 0,
828  '$isReusable' => false,
829  ];
830  }
831 
845  public function testIsReusableFor(
846  User $prepUser = null,
847  RevisionRecord $prepRevision = null,
848  RevisionSlotsUpdate $prepUpdate = null,
849  User $forUser = null,
850  RevisionRecord $forRevision = null,
851  RevisionSlotsUpdate $forUpdate = null,
852  $forParent = null,
853  $isReusable = null
854  ) {
855  $updater = $this->getDerivedPageDataUpdater( __METHOD__ );
856 
857  if ( $prepUpdate ) {
858  $updater->prepareContent( $prepUser, $prepUpdate, false );
859  }
860 
861  if ( $prepRevision ) {
862  $updater->prepareUpdate( $prepRevision );
863  }
864 
865  $this->assertSame(
866  $isReusable,
867  $updater->isReusableFor( $forUser, $forRevision, $forUpdate, $forParent )
868  );
869  }
870 
876  public function testDoUpdates() {
877  $page = $this->getPage( __METHOD__ );
878 
879  $content = [ 'main' => new WikitextContent( 'first [[main]]' ) ];
880 
881  if ( $this->hasMultiSlotSupport() ) {
882  $content['aux'] = new WikitextContent( 'Aux [[Nix]]' );
883 
884  MediaWikiServices::getInstance()->getSlotRoleRegistry()->defineRoleWithModel(
885  'aux',
887  );
888  }
889 
890  $rev = $this->createRevision( $page, 'first', $content );
891  $pageId = $page->getId();
892 
893  $oldStats = $this->db->selectRow( 'site_stats', '*', '1=1' );
894  $this->db->delete( 'pagelinks', '*' );
895 
896  $pcache = MediaWikiServices::getInstance()->getParserCache();
897  $pcache->deleteOptionsKey( $page );
898 
899  $updater = $this->getDerivedPageDataUpdater( $page, $rev );
900  $updater->setArticleCountMethod( 'link' );
901 
902  $options = []; // TODO: test *all* the options...
903  $updater->prepareUpdate( $rev, $options );
904 
905  $updater->doUpdates();
906 
907  // links table update
908  $pageLinks = $this->db->select(
909  'pagelinks',
910  '*',
911  [ 'pl_from' => $pageId ],
912  __METHOD__,
913  [ 'ORDER BY' => 'pl_namespace, pl_title' ]
914  );
915 
916  $pageLinksRow = $pageLinks->fetchObject();
917  $this->assertInternalType( 'object', $pageLinksRow );
918  $this->assertSame( 'Main', $pageLinksRow->pl_title );
919 
920  if ( $this->hasMultiSlotSupport() ) {
921  $pageLinksRow = $pageLinks->fetchObject();
922  $this->assertInternalType( 'object', $pageLinksRow );
923  $this->assertSame( 'Nix', $pageLinksRow->pl_title );
924  }
925 
926  // parser cache update
927  $cached = $pcache->get( $page, $updater->getCanonicalParserOptions() );
928  $this->assertInternalType( 'object', $cached );
929  $this->assertSame( $updater->getCanonicalParserOutput(), $cached );
930 
931  // site stats
932  $stats = $this->db->selectRow( 'site_stats', '*', '1=1' );
933  $this->assertSame( $oldStats->ss_total_pages + 1, (int)$stats->ss_total_pages );
934  $this->assertSame( $oldStats->ss_total_edits + 1, (int)$stats->ss_total_edits );
935  $this->assertSame( $oldStats->ss_good_articles + 1, (int)$stats->ss_good_articles );
936 
937  // TODO: MCR: test data updates for additional slots!
938  // TODO: test update for edit without page creation
939  // TODO: test message cache purge
940  // TODO: test module cache purge
941  // TODO: test CDN purge
942  // TODO: test newtalk update
943  // TODO: test search update
944  // TODO: test site stats good_articles while turning the page into (or back from) a redir.
945  // TODO: test category membership update (with setRcWatchCategoryMembership())
946  }
947 
951  public function testDoParserCacheUpdate() {
952  if ( $this->hasMultiSlotSupport() ) {
953  MediaWikiServices::getInstance()->getSlotRoleRegistry()->defineRoleWithModel(
954  'aux',
956  );
957  }
958 
959  $page = $this->getPage( __METHOD__ );
960  $this->createRevision( $page, 'Dummy' );
961 
962  $user = $this->getTestUser()->getUser();
963 
964  $update = new RevisionSlotsUpdate();
965  $update->modifyContent( 'main', new WikitextContent( 'first [[Main]]' ) );
966 
967  if ( $this->hasMultiSlotSupport() ) {
968  $update->modifyContent( 'aux', new WikitextContent( 'Aux [[Nix]]' ) );
969  }
970 
971  // Emulate update after edit ----------
972  $pcache = MediaWikiServices::getInstance()->getParserCache();
973  $pcache->deleteOptionsKey( $page );
974 
975  $rev = $this->makeRevision( $page->getTitle(), $update, $user, 'rev', null );
976  $rev->setTimestamp( '20100101000000' );
977  $rev->setParentId( $page->getLatest() );
978 
979  $updater = $this->getDerivedPageDataUpdater( $page );
980  $updater->prepareContent( $user, $update, false );
981 
982  $rev->setId( 11 );
983  $updater->prepareUpdate( $rev );
984 
985  // Force the page timestamp, so we notice whether ParserOutput::getTimestamp
986  // or ParserOutput::getCacheTime are used.
987  $page->setTimestamp( $rev->getTimestamp() );
988  $updater->doParserCacheUpdate();
989 
990  // The cached ParserOutput should not use the revision timestamp
991  $cached = $pcache->get( $page, $updater->getCanonicalParserOptions(), true );
992  $this->assertInternalType( 'object', $cached );
993  $this->assertSame( $updater->getCanonicalParserOutput(), $cached );
994 
995  $this->assertSame( $rev->getTimestamp(), $cached->getCacheTime() );
996  $this->assertSame( $rev->getId(), $cached->getCacheRevisionId() );
997 
998  // Emulate forced update of an old revision ----------
999  $pcache->deleteOptionsKey( $page );
1000 
1001  $updater = $this->getDerivedPageDataUpdater( $page );
1002  $updater->prepareUpdate( $rev );
1003 
1004  // Force the page timestamp, so we notice whether ParserOutput::getTimestamp
1005  // or ParserOutput::getCacheTime are used.
1006  $page->setTimestamp( $rev->getTimestamp() );
1007  $updater->doParserCacheUpdate();
1008 
1009  // The cached ParserOutput should not use the revision timestamp
1010  $cached = $pcache->get( $page, $updater->getCanonicalParserOptions(), true );
1011  $this->assertInternalType( 'object', $cached );
1012  $this->assertSame( $updater->getCanonicalParserOutput(), $cached );
1013 
1014  $this->assertGreaterThan( $rev->getTimestamp(), $cached->getCacheTime() );
1015  $this->assertSame( $rev->getId(), $cached->getCacheRevisionId() );
1016  }
1017 
1021  private function hasMultiSlotSupport() {
1023 
1026  }
1027 
1028 }
MWTimestamp
Library for creating and parsing MW-style timestamps.
Definition: MWTimestamp.php:32
ContentHandler
A content handler knows how do deal with a specific type of content on a wiki page.
Definition: ContentHandler.php:53
CommentStoreComment\newUnsavedComment
static newUnsavedComment( $comment, array $data=null)
Create a new, unsaved CommentStoreComment.
Definition: CommentStoreComment.php:66
$user
return true to allow those checks to and false if checking is done & $user
Definition: hooks.txt:1476
MediaWiki\Tests\Storage\DerivedPageDataUpdaterTest\testIsReusableFor
testIsReusableFor(User $prepUser=null, RevisionRecord $prepRevision=null, RevisionSlotsUpdate $prepUpdate=null, User $forUser=null, RevisionRecord $forRevision=null, RevisionSlotsUpdate $forUpdate=null, $forParent=null, $isReusable=null)
provideIsReusableFor \MediaWiki\Storage\DerivedPageDataUpdater::isReusableFor()
Definition: DerivedPageDataUpdaterTest.php:845
Revision\RevisionRecord
Page revision base class.
Definition: RevisionRecord.php:45
SCHEMA_COMPAT_READ_NEW
const SCHEMA_COMPAT_READ_NEW
Definition: Defines.php:287
MediaWiki\Tests\Storage\DerivedPageDataUpdaterTest\provideIsReusableFor
provideIsReusableFor()
Definition: DerivedPageDataUpdaterTest.php:695
MediaWikiTestCase\mergeMwGlobalArrayValue
mergeMwGlobalArrayValue( $name, $values)
Merges the given values into a MW global array variable.
Definition: MediaWikiTestCase.php:904
MediaWikiTestCase\getTestUser
static getTestUser( $groups=[])
Convenience method for getting an immutable test user.
Definition: MediaWikiTestCase.php:180
Revision\SlotRecord\newInherited
static newInherited(SlotRecord $slot)
Constructs a new SlotRecord for a new revision, inheriting the content of the given SlotRecord of a p...
Definition: SlotRecord.php:103
WikiPage\newPageUpdater
newPageUpdater(User $user, RevisionSlotsUpdate $forUpdate=null)
Returns a PageUpdater for creating new revisions on this page (or creating the page).
Definition: WikiPage.php:1789
MediaWiki\Tests\Storage\DerivedPageDataUpdaterTest\tearDown
tearDown()
Definition: DerivedPageDataUpdaterTest.php:35
MediaWiki\Tests\Storage\DerivedPageDataUpdaterTest\testPrepareContentInherit
testPrepareContentInherit()
\MediaWiki\Storage\DerivedPageDataUpdater::prepareContent() \MediaWiki\Storage\DerivedPageDataUpdater...
Definition: DerivedPageDataUpdaterTest.php:265
MediaWiki\Tests\Storage\DerivedPageDataUpdaterTest\createMockContent
createMockContent(ContentHandler $handler, $text)
Definition: DerivedPageDataUpdaterTest.php:581
MediaWiki\Tests\Storage\DerivedPageDataUpdaterTest\getTitle
getTitle( $title)
Definition: DerivedPageDataUpdaterTest.php:46
WikiPage
Class representing a MediaWiki article and history.
Definition: WikiPage.php:45
MediaWiki\Tests\Storage\DerivedPageDataUpdaterTest\testDoParserCacheUpdate
testDoParserCacheUpdate()
\MediaWiki\Storage\DerivedPageDataUpdater::doParserCacheUpdate()
Definition: DerivedPageDataUpdaterTest.php:951
LinksUpdate
Class the manages updates of *_link tables as well as similar extension-managed tables.
Definition: LinksUpdate.php:35
MediaWiki\Tests\Storage\DerivedPageDataUpdaterTest\testGetPreparedEditAfterPrepareUpdate
testGetPreparedEditAfterPrepareUpdate()
\MediaWiki\Storage\DerivedPageDataUpdater::getPreparedEdit()
Definition: DerivedPageDataUpdaterTest.php:487
$wgMultiContentRevisionSchemaMigrationStage
int $wgMultiContentRevisionSchemaMigrationStage
RevisionStore table schema migration stage (content, slots, content_models & slot_roles tables).
Definition: DefaultSettings.php:8955
User\newFromName
static newFromName( $name, $validate='valid')
Static factory method for creation from username.
Definition: User.php:585
MediaWiki\Tests\Storage\DerivedPageDataUpdaterTest\getMockTitle
getMockTitle( $id=23)
Definition: DerivedPageDataUpdaterTest.php:681
MediaWiki\Tests\Storage\DerivedPageDataUpdaterTest\testPrepareUpdateReusesParserOutput
testPrepareUpdateReusesParserOutput()
\MediaWiki\Storage\DerivedPageDataUpdater::prepareUpdate()
Definition: DerivedPageDataUpdaterTest.php:390
Revision\MutableRevisionSlots
Mutable version of RevisionSlots, for constructing a new revision.
Definition: MutableRevisionSlots.php:33
User
User
Definition: All_system_messages.txt:425
CONTENT_MODEL_WIKITEXT
const CONTENT_MODEL_WIKITEXT
Definition: Defines.php:235
php
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
MediaWiki\Tests\Storage\DerivedPageDataUpdaterTest\testGetPreparedEditAfterPrepareContent
testGetPreparedEditAfterPrepareContent()
\MediaWiki\Storage\DerivedPageDataUpdater::getPreparedEdit()
Definition: DerivedPageDataUpdaterTest.php:463
DerivedPageDataUpdater
This document provides an overview of the usage of PageUpdater and DerivedPageDataUpdater
Definition: pageupdater.txt:3
MediaWiki\Tests\Storage
Definition: BlobStoreFactoryTest.php:3
MediaWiki\Tests\Storage\DerivedPageDataUpdaterTest\testGetSecondaryDataUpdatesAfterPrepareContent
testGetSecondaryDataUpdatesAfterPrepareContent()
Definition: DerivedPageDataUpdaterTest.php:514
$html
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 & $html
Definition: hooks.txt:1985
$title
namespace and then decline to actually register it file or subcat img or subcat $title
Definition: hooks.txt:925
WikiPage\factory
static factory(Title $title)
Create a WikiPage object of the appropriate class for the given title.
Definition: WikiPage.php:138
$handler
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 it can be in the form of< username >< more info > e g for bot passwords intended to be added to log contexts Fields it might only if the login was with a bot password 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:780
MediaWiki\Tests\Storage\DerivedPageDataUpdaterTest\hasMultiSlotSupport
hasMultiSlotSupport()
Definition: DerivedPageDataUpdaterTest.php:1021
MediaWikiTestCase
Definition: MediaWikiTestCase.php:17
MediaWiki\Tests\Storage\DerivedPageDataUpdaterTest\testDoUpdates
testDoUpdates()
\MediaWiki\Storage\DerivedPageDataUpdater::doUpdates() \MediaWiki\Storage\DerivedPageDataUpdater::doS...
Definition: DerivedPageDataUpdaterTest.php:876
use
as see the revision history and available at free of to any person obtaining a copy of this software and associated documentation to deal in the Software without including without limitation the rights to use
Definition: MIT-LICENSE.txt:10
MediaWiki\Tests\Storage\DerivedPageDataUpdaterTest\testPrepareContent
testPrepareContent()
\MediaWiki\Storage\DerivedPageDataUpdater::prepareContent() \MediaWiki\Storage\DerivedPageDataUpdater...
Definition: DerivedPageDataUpdaterTest.php:191
MediaWiki\Tests\Storage\DerivedPageDataUpdaterTest\getDerivedPageDataUpdater
getDerivedPageDataUpdater( $page, RevisionRecord $rec=null)
Definition: DerivedPageDataUpdaterTest.php:66
MediaWikiTestCase\getDefaultWikitextNS
getDefaultWikitextNS()
Returns the ID of a namespace that defaults to Wikitext.
Definition: MediaWikiTestCase.php:2211
WikitextContent
Content object for wiki text pages.
Definition: WikitextContent.php:36
MediaWiki\Tests\Storage\DerivedPageDataUpdaterTest\getPage
getPage( $title)
Definition: DerivedPageDataUpdaterTest.php:55
MediaWiki\Tests\Storage\DerivedPageDataUpdaterTest\createRevision
createRevision(WikiPage $page, $summary, $content=null)
Creates a revision in the database.
Definition: DerivedPageDataUpdaterTest.php:84
Revision\SlotRecord\newUnsaved
static newUnsaved( $role, Content $content)
Constructs a new Slot from a Content object for a new revision.
Definition: SlotRecord.php:129
MediaWiki\Tests\Storage\DerivedPageDataUpdaterTest\testGrabCurrentRevision
testGrabCurrentRevision()
\MediaWiki\Storage\DerivedPageDataUpdater::grabCurrentRevision() \MediaWiki\Storage\DerivedPageDataUp...
Definition: DerivedPageDataUpdaterTest.php:158
null
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
$name
Allows to change the fields on the form that will be generated $name
Definition: hooks.txt:271
MediaWiki\Storage\RevisionSlotsUpdate
Value object representing a modification of revision slots.
Definition: RevisionSlotsUpdate.php:36
MediaWiki\MediaWikiServices\getInstance
static getInstance()
Returns the global default instance of the top level service locator.
Definition: MediaWikiServices.php:124
any
they could even be mouse clicks or menu items whatever suits your program You should also get your if any
Definition: COPYING.txt:326
Title\makeTitleSafe
static makeTitleSafe( $ns, $title, $fragment='', $interwiki='')
Create a new Title from a namespace index and a DB key.
Definition: Title.php:604
MediaWiki\Tests\Storage\DerivedPageDataUpdaterTest
Database.
Definition: DerivedPageDataUpdaterTest.php:33
Revision\MutableRevisionRecord
Mutable RevisionRecord implementation, for building new revision entries programmatically.
Definition: MutableRevisionRecord.php:41
TextContentHandler
Base content handler implementation for flat text contents.
Definition: TextContentHandler.php:31
SCHEMA_COMPAT_WRITE_NEW
const SCHEMA_COMPAT_WRITE_NEW
Definition: Defines.php:286
MediaWiki\Tests\Storage\DerivedPageDataUpdaterTest\testPrepareUpdateOutputReset
testPrepareUpdateOutputReset()
\MediaWiki\Storage\DerivedPageDataUpdater::prepareUpdate() \MediaWiki\Storage\DerivedPageDataUpdater:...
Definition: DerivedPageDataUpdaterTest.php:420
Revision\SlotRecord\MAIN
const MAIN
Definition: SlotRecord.php:41
TextContent
Content object implementation for representing flat text.
Definition: TextContent.php:37
MediaWiki\Tests\Storage\DerivedPageDataUpdaterTest\testGetSecondaryDataUpdatesWithSlotRemoval
testGetSecondaryDataUpdatesWithSlotRemoval()
Definition: DerivedPageDataUpdaterTest.php:594
Content
Base interface for content objects.
Definition: Content.php:34
MWCallableUpdate
Deferrable Update for closure/callback.
Definition: MWCallableUpdate.php:8
Title
Represents a title within MediaWiki.
Definition: Title.php:40
$options
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped & $options
Definition: hooks.txt:1985
Revision\SlotRecord\newSaved
static newSaved( $revisionId, $contentId, $contentAddress, SlotRecord $protoSlot)
Constructs a complete SlotRecord for a newly saved revision, based on the incomplete proto-slot.
Definition: SlotRecord.php:164
$rev
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:1769
as
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
MediaWiki\Tests\Storage\DerivedPageDataUpdaterTest\defineMockContentModelForUpdateTesting
defineMockContentModelForUpdateTesting( $name)
Definition: DerivedPageDataUpdaterTest.php:541
$updater
$page->newPageUpdater($user) $updater
Definition: pageupdater.txt:63
true
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:1985
$content
$content
Definition: pageupdater.txt:72
MediaWiki\Tests\Storage\DerivedPageDataUpdaterTest\makeRevision
makeRevision(Title $title, RevisionSlotsUpdate $update, User $user, $comment, $id=0, $parentId=0)
Creates a dummy revision object without touching the database.
Definition: DerivedPageDataUpdaterTest.php:654
MediaWiki\Tests\Storage\DerivedPageDataUpdaterTest\testPrepareUpdate
testPrepareUpdate()
\MediaWiki\Storage\DerivedPageDataUpdater::prepareUpdate() \MediaWiki\Storage\DerivedPageDataUpdater:...
Definition: DerivedPageDataUpdaterTest.php:333
class
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
MediaWikiServices
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
MediaWiki\Tests\Storage\DerivedPageDataUpdaterTest\testGetCanonicalParserOptions
testGetCanonicalParserOptions()
\MediaWiki\Storage\DerivedPageDataUpdater::getCanonicalParserOptions()
Definition: DerivedPageDataUpdaterTest.php:119
User
The User object encapsulates all of the user-specific settings (user_id, name, rights,...
Definition: User.php:48
CommentStoreComment
CommentStoreComment represents a comment stored by CommentStore.
Definition: CommentStoreComment.php:29
Revision\SlotRecord
Value object representing a content slot associated with a page revision.
Definition: SlotRecord.php:39