MediaWiki  master
DerivedPageDataUpdaterTest.php
Go to the documentation of this file.
1 <?php
2 
4 
22 use Title;
23 use User;
28 
35 
36  public function tearDown() {
37  MWTimestamp::setFakeTime( false );
38 
39  parent::tearDown();
40  }
41 
47  private function getTitle( $title ) {
48  return Title::makeTitleSafe( $this->getDefaultWikitextNS(), $title );
49  }
50 
56  private function getPage( $title ) {
57  $title = ( $title instanceof Title ) ? $title : $this->getTitle( $title );
58 
59  return WikiPage::factory( $title );
60  }
61 
69  private function getDerivedPageDataUpdater(
70  $page, RevisionRecord $rec = null, User $user = null
71  ) {
72  if ( is_string( $page ) || $page instanceof Title ) {
73  $page = $this->getPage( $page );
74  }
75 
76  $page = TestingAccessWrapper::newFromObject( $page );
77  return $page->getDerivedDataUpdater( $user, $rec );
78  }
79 
90  private function createRevision( WikiPage $page, $summary, $content = null, $user = null ) {
91  $user = $user ?: $this->getTestUser()->getUser();
92  $comment = CommentStoreComment::newUnsavedComment( $summary );
93 
94  if ( $content === null || is_string( $content ) ) {
95  $content = new WikitextContent( $content ?? $summary );
96  }
97 
98  if ( !is_array( $content ) ) {
99  $content = [ 'main' => $content ];
100  }
101 
102  $this->getDerivedPageDataUpdater( $page ); // flush cached instance before.
103 
104  $updater = $page->newPageUpdater( $user );
105 
106  foreach ( $content as $role => $c ) {
107  $updater->setContent( $role, $c );
108  }
109 
110  $rev = $updater->saveRevision( $comment );
111  if ( !$updater->wasSuccessful() ) {
112  $this->fail( $updater->getStatus()->getWikiText() );
113  }
114 
115  $this->getDerivedPageDataUpdater( $page ); // flush cached instance after.
116  return $rev;
117  }
118 
119  // TODO: test setArticleCountMethod() and isCountable();
120  // TODO: test isRedirect() and wasRedirect()
121 
125  public function testGetCanonicalParserOptions() {
126  $user = $this->getTestUser()->getUser();
127  $page = $this->getPage( __METHOD__ );
128 
129  $parentRev = $this->createRevision( $page, 'first' );
130 
131  $mainContent = new WikitextContent( 'Lorem ipsum' );
132 
133  $update = new RevisionSlotsUpdate();
134  $update->modifyContent( SlotRecord::MAIN, $mainContent );
135  $updater = $this->getDerivedPageDataUpdater( $page );
136  $updater->prepareContent( $user, $update, false );
137 
138  $options1 = $updater->getCanonicalParserOptions();
139  $this->assertSame( MediaWikiServices::getInstance()->getContentLanguage(),
140  $options1->getUserLangObj() );
141 
142  $speculativeId = $options1->getSpeculativeRevId();
143  $this->assertSame( $parentRev->getId() + 1, $speculativeId );
144 
145  $rev = $this->makeRevision(
146  $page->getTitle(),
147  $update,
148  $user,
149  $parentRev->getId() + 7,
150  $parentRev->getId()
151  );
152  $updater->prepareUpdate( $rev );
153 
154  $options2 = $updater->getCanonicalParserOptions();
155 
156  $currentRev = call_user_func( $options2->getCurrentRevisionCallback(), $page->getTitle() );
157  $this->assertSame( $rev->getId(), $currentRev->getId() );
158  }
159 
164  public function testGrabCurrentRevision() {
165  $page = $this->getPage( __METHOD__ );
166 
167  $updater0 = $this->getDerivedPageDataUpdater( $page );
168  $this->assertNull( $updater0->grabCurrentRevision() );
169  $this->assertFalse( $updater0->pageExisted() );
170 
171  $rev1 = $this->createRevision( $page, 'first' );
172  $updater1 = $this->getDerivedPageDataUpdater( $page );
173  $this->assertSame( $rev1->getId(), $updater1->grabCurrentRevision()->getId() );
174  $this->assertFalse( $updater0->pageExisted() );
175  $this->assertTrue( $updater1->pageExisted() );
176 
177  $rev2 = $this->createRevision( $page, 'second' );
178  $updater2 = $this->getDerivedPageDataUpdater( $page );
179  $this->assertSame( $rev1->getId(), $updater1->grabCurrentRevision()->getId() );
180  $this->assertSame( $rev2->getId(), $updater2->grabCurrentRevision()->getId() );
181  }
182 
197  public function testPrepareContent() {
198  MediaWikiServices::getInstance()->getSlotRoleRegistry()->defineRoleWithModel(
199  'aux',
201  );
202 
203  $sysop = $this->getTestUser( [ 'sysop' ] )->getUser();
204  $updater = $this->getDerivedPageDataUpdater( __METHOD__ );
205 
206  $this->assertFalse( $updater->isContentPrepared() );
207 
208  // TODO: test stash
209  // TODO: MCR: Test multiple slots. Test slot removal.
210  $mainContent = new WikitextContent( 'first [[main]] ~~~' );
211  $auxContent = new WikitextContent( 'inherited ~~~ content' );
212  $auxSlot = SlotRecord::newSaved(
213  10, 7, 'tt:7',
214  SlotRecord::newUnsaved( 'aux', $auxContent )
215  );
216 
217  $update = new RevisionSlotsUpdate();
218  $update->modifyContent( SlotRecord::MAIN, $mainContent );
219  $update->modifySlot( SlotRecord::newInherited( $auxSlot ) );
220  // TODO: MCR: test removing slots!
221 
222  $updater->prepareContent( $sysop, $update, false );
223 
224  // second be ok to call again with the same params
225  $updater->prepareContent( $sysop, $update, false );
226 
227  $this->assertNull( $updater->grabCurrentRevision() );
228  $this->assertTrue( $updater->isContentPrepared() );
229  $this->assertFalse( $updater->isUpdatePrepared() );
230  $this->assertFalse( $updater->pageExisted() );
231  $this->assertTrue( $updater->isCreation() );
232  $this->assertTrue( $updater->isChange() );
233  $this->assertFalse( $updater->isContentDeleted() );
234 
235  $this->assertNotNull( $updater->getRevision() );
236  $this->assertNotNull( $updater->getRenderedRevision() );
237 
238  $this->assertEquals( [ 'main', 'aux' ], $updater->getSlots()->getSlotRoles() );
239  $this->assertEquals( [ 'main' ], array_keys( $updater->getSlots()->getOriginalSlots() ) );
240  $this->assertEquals( [ 'aux' ], array_keys( $updater->getSlots()->getInheritedSlots() ) );
241  $this->assertEquals( [ 'main', 'aux' ], $updater->getModifiedSlotRoles() );
242  $this->assertEquals( [ 'main', 'aux' ], $updater->getTouchedSlotRoles() );
243 
244  $mainSlot = $updater->getRawSlot( SlotRecord::MAIN );
245  $this->assertInstanceOf( SlotRecord::class, $mainSlot );
246  $this->assertNotContains( '~~~', $mainSlot->getContent()->serialize(), 'PST should apply.' );
247  $this->assertContains( $sysop->getName(), $mainSlot->getContent()->serialize() );
248 
249  $auxSlot = $updater->getRawSlot( 'aux' );
250  $this->assertInstanceOf( SlotRecord::class, $auxSlot );
251  $this->assertContains( '~~~', $auxSlot->getContent()->serialize(), 'No PST should apply.' );
252 
253  $mainOutput = $updater->getCanonicalParserOutput();
254  $this->assertContains( 'first', $mainOutput->getText() );
255  $this->assertContains( '<a ', $mainOutput->getText() );
256  $this->assertNotEmpty( $mainOutput->getLinks() );
257 
258  $canonicalOutput = $updater->getCanonicalParserOutput();
259  $this->assertContains( 'first', $canonicalOutput->getText() );
260  $this->assertContains( '<a ', $canonicalOutput->getText() );
261  $this->assertContains( 'inherited ', $canonicalOutput->getText() );
262  $this->assertNotEmpty( $canonicalOutput->getLinks() );
263  }
264 
271  public function testPrepareContentInherit() {
272  $sysop = $this->getTestUser( [ 'sysop' ] )->getUser();
273  $page = $this->getPage( __METHOD__ );
274 
275  $mainContent1 = new WikitextContent( 'first [[main]] ({{REVISIONUSER}}) #~~~#' );
276  $mainContent2 = new WikitextContent( 'second ({{subst:REVISIONUSER}}) #~~~#' );
277 
278  $rev = $this->createRevision( $page, 'first', $mainContent1 );
279  $mainContent1 = $rev->getContent( SlotRecord::MAIN ); // get post-pst content
280  $userName = $rev->getUser()->getName();
281  $sysopName = $sysop->getName();
282 
283  $update = new RevisionSlotsUpdate();
284  $update->modifyContent( SlotRecord::MAIN, $mainContent1 );
285  $updater1 = $this->getDerivedPageDataUpdater( $page );
286  $updater1->prepareContent( $sysop, $update, false );
287 
288  $this->assertNotNull( $updater1->grabCurrentRevision() );
289  $this->assertTrue( $updater1->isContentPrepared() );
290  $this->assertTrue( $updater1->pageExisted() );
291  $this->assertFalse( $updater1->isCreation() );
292  $this->assertFalse( $updater1->isChange() );
293 
294  $this->assertNotNull( $updater1->getRevision() );
295  $this->assertNotNull( $updater1->getRenderedRevision() );
296 
297  // parser-output for null-edit uses the original author's name
298  $html = $updater1->getRenderedRevision()->getRevisionParserOutput()->getText();
299  $this->assertNotContains( $sysopName, $html, '{{REVISIONUSER}}' );
300  $this->assertNotContains( '{{REVISIONUSER}}', $html, '{{REVISIONUSER}}' );
301  $this->assertNotContains( '~~~', $html, 'signature ~~~' );
302  $this->assertContains( '(' . $userName . ')', $html, '{{REVISIONUSER}}' );
303  $this->assertContains( '>' . $userName . '<', $html, 'signature ~~~' );
304 
305  // TODO: MCR: test inheritance from parent
306  $update = new RevisionSlotsUpdate();
307  $update->modifyContent( SlotRecord::MAIN, $mainContent2 );
308  $updater2 = $this->getDerivedPageDataUpdater( $page );
309  $updater2->prepareContent( $sysop, $update, false );
310 
311  // non-null edit use the new user name in PST
312  $pstText = $updater2->getSlots()->getContent( SlotRecord::MAIN )->serialize();
313  $this->assertNotContains( '{{subst:REVISIONUSER}}', $pstText, '{{subst:REVISIONUSER}}' );
314  $this->assertNotContains( '~~~', $pstText, 'signature ~~~' );
315  $this->assertContains( '(' . $sysopName . ')', $pstText, '{{subst:REVISIONUSER}}' );
316  $this->assertContains( ':' . $sysopName . '|', $pstText, 'signature ~~~' );
317 
318  $this->assertFalse( $updater2->isCreation() );
319  $this->assertTrue( $updater2->isChange() );
320  }
321 
322  // TODO: test failure of prepareContent() when called again...
323  // - with different user
324  // - with different update
325  // - after calling prepareUpdate()
326 
339  public function testPrepareUpdate() {
340  $page = $this->getPage( __METHOD__ );
341 
342  $mainContent1 = new WikitextContent( 'first [[main]] ~~~' );
343  $rev1 = $this->createRevision( $page, 'first', $mainContent1 );
344  $updater1 = $this->getDerivedPageDataUpdater( $page, $rev1 );
345 
346  $options = []; // TODO: test *all* the options...
347  $updater1->prepareUpdate( $rev1, $options );
348 
349  $this->assertTrue( $updater1->isUpdatePrepared() );
350  $this->assertTrue( $updater1->isContentPrepared() );
351  $this->assertTrue( $updater1->isCreation() );
352  $this->assertTrue( $updater1->isChange() );
353  $this->assertFalse( $updater1->isContentDeleted() );
354 
355  $this->assertNotNull( $updater1->getRevision() );
356  $this->assertNotNull( $updater1->getRenderedRevision() );
357 
358  $this->assertEquals( [ 'main' ], $updater1->getSlots()->getSlotRoles() );
359  $this->assertEquals( [ 'main' ], array_keys( $updater1->getSlots()->getOriginalSlots() ) );
360  $this->assertEquals( [], array_keys( $updater1->getSlots()->getInheritedSlots() ) );
361  $this->assertEquals( [ 'main' ], $updater1->getModifiedSlotRoles() );
362  $this->assertEquals( [ 'main' ], $updater1->getTouchedSlotRoles() );
363 
364  // TODO: MCR: test multiple slots, test slot removal!
365 
366  $this->assertInstanceOf( SlotRecord::class, $updater1->getRawSlot( SlotRecord::MAIN ) );
367  $this->assertNotContains( '~~~~', $updater1->getRawContent( SlotRecord::MAIN )->serialize() );
368 
369  $mainOutput = $updater1->getCanonicalParserOutput();
370  $this->assertContains( 'first', $mainOutput->getText() );
371  $this->assertContains( '<a ', $mainOutput->getText() );
372  $this->assertNotEmpty( $mainOutput->getLinks() );
373 
374  $canonicalOutput = $updater1->getCanonicalParserOutput();
375  $this->assertContains( 'first', $canonicalOutput->getText() );
376  $this->assertContains( '<a ', $canonicalOutput->getText() );
377  $this->assertNotEmpty( $canonicalOutput->getLinks() );
378 
379  $mainContent2 = new WikitextContent( 'second' );
380  $rev2 = $this->createRevision( $page, 'second', $mainContent2 );
381  $updater2 = $this->getDerivedPageDataUpdater( $page, $rev2 );
382 
383  $options = []; // TODO: test *all* the options...
384  $updater2->prepareUpdate( $rev2, $options );
385 
386  $this->assertFalse( $updater2->isCreation() );
387  $this->assertTrue( $updater2->isChange() );
388 
389  $canonicalOutput = $updater2->getCanonicalParserOutput();
390  $this->assertContains( 'second', $canonicalOutput->getText() );
391  }
392 
397  $user = $this->getTestUser()->getUser();
398  $page = $this->getPage( __METHOD__ );
399 
400  $mainContent1 = new WikitextContent( 'first [[main]] ~~~' );
401 
402  $update = new RevisionSlotsUpdate();
403  $update->modifyContent( SlotRecord::MAIN, $mainContent1 );
404  $updater = $this->getDerivedPageDataUpdater( $page );
405  $updater->prepareContent( $user, $update, false );
406 
407  $mainOutput = $updater->getSlotParserOutput( SlotRecord::MAIN );
408  $canonicalOutput = $updater->getCanonicalParserOutput();
409 
410  $rev = $this->createRevision( $page, 'first', $mainContent1 );
411 
412  $options = []; // TODO: test *all* the options...
413  $updater->prepareUpdate( $rev, $options );
414 
415  $this->assertTrue( $updater->isUpdatePrepared() );
416  $this->assertTrue( $updater->isContentPrepared() );
417 
418  $this->assertSame( $mainOutput, $updater->getSlotParserOutput( SlotRecord::MAIN ) );
419  $this->assertSame( $canonicalOutput, $updater->getCanonicalParserOutput() );
420  }
421 
426  public function testPrepareUpdateOutputReset() {
427  $user = $this->getTestUser()->getUser();
428  $page = $this->getPage( __METHOD__ );
429 
430  $mainContent1 = new WikitextContent( 'first --{{REVISIONID}}--' );
431 
432  $update = new RevisionSlotsUpdate();
433  $update->modifyContent( SlotRecord::MAIN, $mainContent1 );
434  $updater = $this->getDerivedPageDataUpdater( $page );
435  $updater->prepareContent( $user, $update, false );
436 
437  $mainOutput = $updater->getSlotParserOutput( SlotRecord::MAIN );
438  $canonicalOutput = $updater->getCanonicalParserOutput();
439 
440  // prevent optimization on matching speculative ID
441  $mainOutput->setSpeculativeRevIdUsed( 0 );
442  $canonicalOutput->setSpeculativeRevIdUsed( 0 );
443 
444  $rev = $this->createRevision( $page, 'first', $mainContent1 );
445 
446  $options = []; // TODO: test *all* the options...
447  $updater->prepareUpdate( $rev, $options );
448 
449  $this->assertTrue( $updater->isUpdatePrepared() );
450  $this->assertTrue( $updater->isContentPrepared() );
451 
452  // ParserOutput objects should have been flushed.
453  $this->assertNotSame( $mainOutput, $updater->getSlotParserOutput( SlotRecord::MAIN ) );
454  $this->assertNotSame( $canonicalOutput, $updater->getCanonicalParserOutput() );
455 
456  $html = $updater->getCanonicalParserOutput()->getText();
457  $this->assertContains( '--' . $rev->getId() . '--', $html );
458 
459  // TODO: MCR: ensure that when the main slot uses {{REVISIONID}} but another slot is
460  // updated, the main slot is still re-rendered!
461  }
462 
463  // TODO: test failure of prepareUpdate() when called again with a different revision
464  // TODO: test failure of prepareUpdate() on inconsistency with prepareContent.
465 
470  $user = $this->getTestUser()->getUser();
471 
472  $mainContent = new WikitextContent( 'first [[main]] ~~~' );
473  $update = new RevisionSlotsUpdate();
474  $update->modifyContent( SlotRecord::MAIN, $mainContent );
475 
476  $updater = $this->getDerivedPageDataUpdater( __METHOD__ );
477  $updater->prepareContent( $user, $update, false );
478 
479  $canonicalOutput = $updater->getCanonicalParserOutput();
480 
481  $preparedEdit = $updater->getPreparedEdit();
482  $this->assertSame( $canonicalOutput->getCacheTime(), $preparedEdit->timestamp );
483  $this->assertSame( $canonicalOutput, $preparedEdit->output );
484  $this->assertSame( $mainContent, $preparedEdit->newContent );
485  $this->assertSame( $updater->getRawContent( SlotRecord::MAIN ), $preparedEdit->pstContent );
486  $this->assertSame( $updater->getCanonicalParserOptions(), $preparedEdit->popts );
487  $this->assertSame( null, $preparedEdit->revid );
488  }
489 
494  $clock = MWTimestamp::convert( TS_UNIX, '20100101000000' );
495  MWTimestamp::setFakeTime( function () use ( &$clock ) {
496  return $clock++;
497  } );
498 
499  $page = $this->getPage( __METHOD__ );
500 
501  $mainContent = new WikitextContent( 'first [[main]] ~~~' );
502  $update = new MutableRevisionSlots();
503  $update->setContent( SlotRecord::MAIN, $mainContent );
504 
505  $rev = $this->createRevision( $page, __METHOD__ );
506 
507  $updater = $this->getDerivedPageDataUpdater( $page );
508  $updater->prepareUpdate( $rev );
509 
510  $canonicalOutput = $updater->getCanonicalParserOutput();
511 
512  $preparedEdit = $updater->getPreparedEdit();
513  $this->assertSame( $canonicalOutput->getCacheTime(), $preparedEdit->timestamp );
514  $this->assertSame( $canonicalOutput, $preparedEdit->output );
515  $this->assertSame( $updater->getRawContent( SlotRecord::MAIN ), $preparedEdit->pstContent );
516  $this->assertSame( $updater->getCanonicalParserOptions(), $preparedEdit->popts );
517  $this->assertSame( $rev->getId(), $preparedEdit->revid );
518  }
519 
521  $user = $this->getTestUser()->getUser();
522  $page = $this->getPage( __METHOD__ );
523  $this->createRevision( $page, __METHOD__ );
524 
525  $mainContent1 = new WikitextContent( 'first' );
526 
527  $update = new RevisionSlotsUpdate();
528  $update->modifyContent( SlotRecord::MAIN, $mainContent1 );
529  $updater = $this->getDerivedPageDataUpdater( $page );
530  $updater->prepareContent( $user, $update, false );
531 
532  $dataUpdates = $updater->getSecondaryDataUpdates();
533 
534  $this->assertNotEmpty( $dataUpdates );
535 
536  $linksUpdates = array_filter( $dataUpdates, function ( $du ) {
537  return $du instanceof LinksUpdate;
538  } );
539  $this->assertCount( 1, $linksUpdates );
540  }
541 
549  $handler = $this->getMockBuilder( TextContentHandler::class )
550  ->setConstructorArgs( [ $name ] )
551  ->setMethods(
552  [ 'getSecondaryDataUpdates', 'getDeletionUpdates', 'unserializeContent' ]
553  )
554  ->getMock();
555 
556  $dataUpdate = new MWCallableUpdate( 'time' );
557  $dataUpdate->_name = "$name data update";
558 
559  $deletionUpdate = new MWCallableUpdate( 'time' );
560  $deletionUpdate->_name = "$name deletion update";
561 
562  $handler->method( 'getSecondaryDataUpdates' )->willReturn( [ $dataUpdate ] );
563  $handler->method( 'getDeletionUpdates' )->willReturn( [ $deletionUpdate ] );
564  $handler->method( 'unserializeContent' )->willReturnCallback(
565  function ( $text ) use ( $handler ) {
566  return $this->createMockContent( $handler, $text );
567  }
568  );
569 
570  $this->mergeMwGlobalArrayValue(
571  'wgContentHandlers', [
572  $name => function () use ( $handler ){
573  return $handler;
574  }
575  ]
576  );
577 
578  return $handler;
579  }
580 
587  private function createMockContent( ContentHandler $handler, $text ) {
589  $content = $this->getMockBuilder( TextContent::class )
590  ->setConstructorArgs( [ $text ] )
591  ->setMethods( [ 'getModel', 'getContentHandler' ] )
592  ->getMock();
593 
594  $content->method( 'getModel' )->willReturn( $handler->getModelID() );
595  $content->method( 'getContentHandler' )->willReturn( $handler );
596 
597  return $content;
598  }
599 
601  if ( !$this->hasMultiSlotSupport() ) {
602  $this->markTestSkipped( 'Slot removal cannot happen with MCR being enabled' );
603  }
604 
605  $m1 = $this->defineMockContentModelForUpdateTesting( 'M1' );
606  $a1 = $this->defineMockContentModelForUpdateTesting( 'A1' );
607  $m2 = $this->defineMockContentModelForUpdateTesting( 'M2' );
608 
609  MediaWikiServices::getInstance()->getSlotRoleRegistry()->defineRoleWithModel(
610  'aux',
611  $a1->getModelID()
612  );
613 
614  $mainContent1 = $this->createMockContent( $m1, 'main 1' );
615  $auxContent1 = $this->createMockContent( $a1, 'aux 1' );
616  $mainContent2 = $this->createMockContent( $m2, 'main 2' );
617 
618  $user = $this->getTestUser()->getUser();
619  $page = $this->getPage( __METHOD__ );
620  $this->createRevision(
621  $page,
622  __METHOD__,
623  [ 'main' => $mainContent1, 'aux' => $auxContent1 ]
624  );
625 
626  $update = new RevisionSlotsUpdate();
627  $update->modifyContent( SlotRecord::MAIN, $mainContent2 );
628  $update->removeSlot( 'aux' );
629 
630  $page = $this->getPage( __METHOD__ );
631  $updater = $this->getDerivedPageDataUpdater( $page );
632  $updater->prepareContent( $user, $update, false );
633 
634  $dataUpdates = $updater->getSecondaryDataUpdates();
635 
636  $this->assertNotEmpty( $dataUpdates );
637 
638  $updateNames = array_map( function ( $du ) {
639  return isset( $du->_name ) ? $du->_name : get_class( $du );
640  }, $dataUpdates );
641 
642  $this->assertContains( LinksUpdate::class, $updateNames );
643  $this->assertContains( 'A1 deletion update', $updateNames );
644  $this->assertContains( 'M2 data update', $updateNames );
645  $this->assertNotContains( 'M1 data update', $updateNames );
646  }
647 
660  private function makeRevision(
661  Title $title,
662  RevisionSlotsUpdate $update,
663  User $user,
664  $comment,
665  $id = 0,
666  $parentId = 0
667  ) {
668  $rev = new MutableRevisionRecord( $title );
669 
670  $rev->applyUpdate( $update );
671  $rev->setUser( $user );
672  $rev->setComment( CommentStoreComment::newUnsavedComment( $comment ) );
673  $rev->setPageId( $title->getArticleID() );
674  $rev->setParentId( $parentId );
675 
676  if ( $id ) {
677  $rev->setId( $id );
678  }
679 
680  return $rev;
681  }
682 
687  private function getMockTitle( $id = 23 ) {
688  $mock = $this->getMockBuilder( Title::class )
689  ->disableOriginalConstructor()
690  ->getMock();
691  $mock->expects( $this->any() )
692  ->method( 'getDBkey' )
693  ->will( $this->returnValue( __CLASS__ ) );
694  $mock->expects( $this->any() )
695  ->method( 'getArticleID' )
696  ->will( $this->returnValue( $id ) );
697 
698  return $mock;
699  }
700 
701  public function provideIsReusableFor() {
702  $title = $this->getMockTitle();
703 
704  $user1 = User::newFromName( 'Alice' );
705  $user2 = User::newFromName( 'Bob' );
706 
707  $content1 = new WikitextContent( 'one' );
708  $content2 = new WikitextContent( 'two' );
709 
710  $update1 = new RevisionSlotsUpdate();
711  $update1->modifyContent( SlotRecord::MAIN, $content1 );
712 
713  $update1b = new RevisionSlotsUpdate();
714  $update1b->modifyContent( 'xyz', $content1 );
715 
716  $update2 = new RevisionSlotsUpdate();
717  $update2->modifyContent( SlotRecord::MAIN, $content2 );
718 
719  $rev1 = $this->makeRevision( $title, $update1, $user1, 'rev1', 11 );
720  $rev1b = $this->makeRevision( $title, $update1b, $user1, 'rev1', 11 );
721 
722  $rev2 = $this->makeRevision( $title, $update2, $user1, 'rev2', 12 );
723  $rev2x = $this->makeRevision( $title, $update2, $user2, 'rev2', 12 );
724  $rev2y = $this->makeRevision( $title, $update2, $user1, 'rev2', 122 );
725 
726  yield 'any' => [
727  '$prepUser' => null,
728  '$prepRevision' => null,
729  '$prepUpdate' => null,
730  '$forUser' => null,
731  '$forRevision' => null,
732  '$forUpdate' => null,
733  '$forParent' => null,
734  '$isReusable' => true,
735  ];
736  yield 'for any' => [
737  '$prepUser' => $user1,
738  '$prepRevision' => $rev1,
739  '$prepUpdate' => $update1,
740  '$forUser' => null,
741  '$forRevision' => null,
742  '$forUpdate' => null,
743  '$forParent' => null,
744  '$isReusable' => true,
745  ];
746  yield 'unprepared' => [
747  '$prepUser' => null,
748  '$prepRevision' => null,
749  '$prepUpdate' => null,
750  '$forUser' => $user1,
751  '$forRevision' => $rev1,
752  '$forUpdate' => $update1,
753  '$forParent' => 0,
754  '$isReusable' => true,
755  ];
756  yield 'match prepareContent' => [
757  '$prepUser' => $user1,
758  '$prepRevision' => null,
759  '$prepUpdate' => $update1,
760  '$forUser' => $user1,
761  '$forRevision' => null,
762  '$forUpdate' => $update1,
763  '$forParent' => 0,
764  '$isReusable' => true,
765  ];
766  yield 'match prepareUpdate' => [
767  '$prepUser' => null,
768  '$prepRevision' => $rev1,
769  '$prepUpdate' => null,
770  '$forUser' => $user1,
771  '$forRevision' => $rev1,
772  '$forUpdate' => null,
773  '$forParent' => 0,
774  '$isReusable' => true,
775  ];
776  yield 'match all' => [
777  '$prepUser' => $user1,
778  '$prepRevision' => $rev1,
779  '$prepUpdate' => $update1,
780  '$forUser' => $user1,
781  '$forRevision' => $rev1,
782  '$forUpdate' => $update1,
783  '$forParent' => 0,
784  '$isReusable' => true,
785  ];
786  yield 'mismatch prepareContent update' => [
787  '$prepUser' => $user1,
788  '$prepRevision' => null,
789  '$prepUpdate' => $update1,
790  '$forUser' => $user1,
791  '$forRevision' => null,
792  '$forUpdate' => $update1b,
793  '$forParent' => 0,
794  '$isReusable' => false,
795  ];
796  yield 'mismatch prepareContent user' => [
797  '$prepUser' => $user1,
798  '$prepRevision' => null,
799  '$prepUpdate' => $update1,
800  '$forUser' => $user2,
801  '$forRevision' => null,
802  '$forUpdate' => $update1,
803  '$forParent' => 0,
804  '$isReusable' => false,
805  ];
806  yield 'mismatch prepareContent parent' => [
807  '$prepUser' => $user1,
808  '$prepRevision' => null,
809  '$prepUpdate' => $update1,
810  '$forUser' => $user1,
811  '$forRevision' => null,
812  '$forUpdate' => $update1,
813  '$forParent' => 7,
814  '$isReusable' => false,
815  ];
816  yield 'mismatch prepareUpdate revision update' => [
817  '$prepUser' => null,
818  '$prepRevision' => $rev1,
819  '$prepUpdate' => null,
820  '$forUser' => null,
821  '$forRevision' => $rev1b,
822  '$forUpdate' => null,
823  '$forParent' => 0,
824  '$isReusable' => false,
825  ];
826  yield 'mismatch prepareUpdate revision id' => [
827  '$prepUser' => null,
828  '$prepRevision' => $rev2,
829  '$prepUpdate' => null,
830  '$forUser' => null,
831  '$forRevision' => $rev2y,
832  '$forUpdate' => null,
833  '$forParent' => 0,
834  '$isReusable' => false,
835  ];
836  }
837 
851  public function testIsReusableFor(
852  User $prepUser = null,
853  RevisionRecord $prepRevision = null,
854  RevisionSlotsUpdate $prepUpdate = null,
855  User $forUser = null,
856  RevisionRecord $forRevision = null,
857  RevisionSlotsUpdate $forUpdate = null,
858  $forParent = null,
859  $isReusable = null
860  ) {
861  $updater = $this->getDerivedPageDataUpdater( __METHOD__ );
862 
863  if ( $prepUpdate ) {
864  $updater->prepareContent( $prepUser, $prepUpdate, false );
865  }
866 
867  if ( $prepRevision ) {
868  $updater->prepareUpdate( $prepRevision );
869  }
870 
871  $this->assertSame(
872  $isReusable,
873  $updater->isReusableFor( $forUser, $forRevision, $forUpdate, $forParent )
874  );
875  }
876 
880  public function testIsCountableNotContentPage() {
882  Title::newFromText( 'Main_Page', NS_TALK )
883  );
884  self::assertFalse( $updater->isCountable() );
885  }
886 
887  public function provideIsCountable() {
888  yield 'deleted revision' => [
889  '$articleCountMethod' => 'any',
890  '$wikitextContent' => 'Test',
891  '$revisionVisibility' => RevisionRecord::SUPPRESSED_ALL,
892  '$isCountable' => false
893  ];
894  yield 'redirect' => [
895  '$articleCountMethod' => 'any',
896  '$wikitextContent' => '#REDIRECT [[Main_Page]]',
897  '$revisionVisibility' => 0,
898  '$isCountable' => false
899  ];
900  yield 'no links count method any' => [
901  '$articleCountMethod' => 'any',
902  '$wikitextContent' => 'Test',
903  '$revisionVisibility' => 0,
904  '$isCountable' => true
905  ];
906  yield 'no links count method link' => [
907  '$articleCountMethod' => 'link',
908  '$wikitextContent' => 'Test',
909  '$revisionVisibility' => 0,
910  '$isCountable' => false
911  ];
912  yield 'with links count method link' => [
913  '$articleCountMethod' => 'link',
914  '$wikitextContent' => '[[Test]]',
915  '$revisionVisibility' => 0,
916  '$isCountable' => true
917  ];
918  }
919 
930  public function testIsCountable(
931  $articleCountMethod,
932  $wikitextContent,
933  $revisionVisibility,
934  $isCountable
935  ) {
936  $this->setMwGlobals( [ 'wgArticleCountMethod' => $articleCountMethod ] );
937  $title = $this->getTitle( 'Main_Page' );
938  $content = new WikitextContent( $wikitextContent );
939  $update = new RevisionSlotsUpdate();
940  $update->modifyContent( SlotRecord::MAIN, $content );
941  $revision = $this->makeRevision( $title, $update, User::newFromName( 'Alice' ), 'rev1', 13 );
942  $revision->setVisibility( $revisionVisibility );
944  $updater->prepareUpdate( $revision );
945  self::assertSame( $isCountable, $updater->isCountable() );
946  }
947 
952  public function testIsCountableNoModifiedSlots() {
953  $page = $this->getPage( __METHOD__ );
954  $content = [ 'main' => new WikitextContent( '[[Test]]' ) ];
955  $rev = $this->createRevision( $page, 'first', $content );
957  $nullRevision->setId( 14 );
958  $updater = $this->getDerivedPageDataUpdater( $page, $nullRevision );
959  $updater->prepareUpdate( $nullRevision );
960  $this->assertTrue( $updater->isCountable() );
961  }
962 
968  public function testDoUpdates() {
969  $page = $this->getPage( __METHOD__ );
970 
971  $content = [ 'main' => new WikitextContent( 'first [[main]]' ) ];
972 
973  if ( $this->hasMultiSlotSupport() ) {
974  $content['aux'] = new WikitextContent( 'Aux [[Nix]]' );
975 
976  MediaWikiServices::getInstance()->getSlotRoleRegistry()->defineRoleWithModel(
977  'aux',
979  );
980  }
981 
982  $rev = $this->createRevision( $page, 'first', $content );
983  $pageId = $page->getId();
984 
985  $oldStats = $this->db->selectRow( 'site_stats', '*', '1=1' );
986  $this->db->delete( 'pagelinks', '*' );
987 
988  $pcache = MediaWikiServices::getInstance()->getParserCache();
989  $pcache->deleteOptionsKey( $page );
990 
991  $updater = $this->getDerivedPageDataUpdater( $page, $rev );
992  $updater->setArticleCountMethod( 'link' );
993 
994  $options = []; // TODO: test *all* the options...
995  $updater->prepareUpdate( $rev, $options );
996 
997  $updater->doUpdates();
998 
999  // links table update
1000  $pageLinks = $this->db->select(
1001  'pagelinks',
1002  '*',
1003  [ 'pl_from' => $pageId ],
1004  __METHOD__,
1005  [ 'ORDER BY' => 'pl_namespace, pl_title' ]
1006  );
1007 
1008  $pageLinksRow = $pageLinks->fetchObject();
1009  $this->assertInternalType( 'object', $pageLinksRow );
1010  $this->assertSame( 'Main', $pageLinksRow->pl_title );
1011 
1012  if ( $this->hasMultiSlotSupport() ) {
1013  $pageLinksRow = $pageLinks->fetchObject();
1014  $this->assertInternalType( 'object', $pageLinksRow );
1015  $this->assertSame( 'Nix', $pageLinksRow->pl_title );
1016  }
1017 
1018  // parser cache update
1019  $cached = $pcache->get( $page, $updater->getCanonicalParserOptions() );
1020  $this->assertInternalType( 'object', $cached );
1021  $this->assertSame( $updater->getCanonicalParserOutput(), $cached );
1022 
1023  // site stats
1024  $stats = $this->db->selectRow( 'site_stats', '*', '1=1' );
1025  $this->assertSame( $oldStats->ss_total_pages + 1, (int)$stats->ss_total_pages );
1026  $this->assertSame( $oldStats->ss_total_edits + 1, (int)$stats->ss_total_edits );
1027  $this->assertSame( $oldStats->ss_good_articles + 1, (int)$stats->ss_good_articles );
1028 
1029  // TODO: MCR: test data updates for additional slots!
1030  // TODO: test update for edit without page creation
1031  // TODO: test message cache purge
1032  // TODO: test module cache purge
1033  // TODO: test CDN purge
1034  // TODO: test newtalk update
1035  // TODO: test search update
1036  // TODO: test site stats good_articles while turning the page into (or back from) a redir.
1037  // TODO: test category membership update (with setRcWatchCategoryMembership())
1038  }
1039 
1046  $page = $this->getPage( __METHOD__ );
1047 
1048  // Case where user has canonical parser options
1049  $content = [ 'main' => new WikitextContent( 'rev ID ver #1: {{REVISIONID}}' ) ];
1050  $rev = $this->createRevision( $page, 'first', $content );
1051  $pcache = MediaWikiServices::getInstance()->getParserCache();
1052  $pcache->deleteOptionsKey( $page );
1053 
1054  $this->db->startAtomic( __METHOD__ ); // let deferred updates queue up
1055 
1056  $updater = $this->getDerivedPageDataUpdater( $page, $rev );
1057  $updater->prepareUpdate( $rev, [] );
1058  $updater->doUpdates();
1059 
1060  $this->assertGreaterThan( 0, DeferredUpdates::pendingUpdatesCount(), 'Pending updates' );
1061  $this->assertNotFalse( $pcache->get( $page, $updater->getCanonicalParserOptions() ) );
1062 
1063  $this->db->endAtomic( __METHOD__ ); // run deferred updates
1064 
1065  $this->assertEquals( 0, DeferredUpdates::pendingUpdatesCount(), 'No pending updates' );
1066  }
1067 
1074  $page = $this->getPage( __METHOD__ );
1075 
1076  // Case where user does not have canonical parser options
1077  $user = $this->getMutableTestUser()->getUser();
1078  $user->setOption(
1079  'thumbsize',
1080  $user->getOption( 'thumbsize' ) + 1
1081  );
1082  $content = [ 'main' => new WikitextContent( 'rev ID ver #2: {{REVISIONID}}' ) ];
1083  $rev = $this->createRevision( $page, 'first', $content, $user );
1084  $pcache = MediaWikiServices::getInstance()->getParserCache();
1085  $pcache->deleteOptionsKey( $page );
1086 
1087  $this->db->startAtomic( __METHOD__ ); // let deferred updates queue up
1088 
1089  $updater = $this->getDerivedPageDataUpdater( $page, $rev, $user );
1090  $updater->prepareUpdate( $rev, [] );
1091  $updater->doUpdates();
1092 
1093  $this->assertGreaterThan( 1, DeferredUpdates::pendingUpdatesCount(), 'Pending updates' );
1094  $this->assertFalse( $pcache->get( $page, $updater->getCanonicalParserOptions() ) );
1095 
1096  $this->db->endAtomic( __METHOD__ ); // run deferred updates
1097 
1098  $this->assertEquals( 0, DeferredUpdates::pendingUpdatesCount(), 'No pending updates' );
1099  $this->assertNotFalse( $pcache->get( $page, $updater->getCanonicalParserOptions() ) );
1100  }
1101 
1105  public function testDoParserCacheUpdate() {
1106  if ( $this->hasMultiSlotSupport() ) {
1107  MediaWikiServices::getInstance()->getSlotRoleRegistry()->defineRoleWithModel(
1108  'aux',
1110  );
1111  }
1112 
1113  $page = $this->getPage( __METHOD__ );
1114  $this->createRevision( $page, 'Dummy' );
1115 
1116  $user = $this->getTestUser()->getUser();
1117 
1118  $update = new RevisionSlotsUpdate();
1119  $update->modifyContent( 'main', new WikitextContent( 'first [[Main]]' ) );
1120 
1121  if ( $this->hasMultiSlotSupport() ) {
1122  $update->modifyContent( 'aux', new WikitextContent( 'Aux [[Nix]]' ) );
1123  }
1124 
1125  // Emulate update after edit ----------
1126  $pcache = MediaWikiServices::getInstance()->getParserCache();
1127  $pcache->deleteOptionsKey( $page );
1128 
1129  $rev = $this->makeRevision( $page->getTitle(), $update, $user, 'rev', null );
1130  $rev->setTimestamp( '20100101000000' );
1131  $rev->setParentId( $page->getLatest() );
1132 
1133  $updater = $this->getDerivedPageDataUpdater( $page );
1134  $updater->prepareContent( $user, $update, false );
1135 
1136  $rev->setId( 11 );
1137  $updater->prepareUpdate( $rev );
1138 
1139  // Force the page timestamp, so we notice whether ParserOutput::getTimestamp
1140  // or ParserOutput::getCacheTime are used.
1141  $page->setTimestamp( $rev->getTimestamp() );
1142  $updater->doParserCacheUpdate();
1143 
1144  // The cached ParserOutput should not use the revision timestamp
1145  $cached = $pcache->get( $page, $updater->getCanonicalParserOptions(), true );
1146  $this->assertInternalType( 'object', $cached );
1147  $this->assertSame( $updater->getCanonicalParserOutput(), $cached );
1148 
1149  $this->assertSame( $rev->getTimestamp(), $cached->getCacheTime() );
1150  $this->assertSame( $rev->getId(), $cached->getCacheRevisionId() );
1151 
1152  // Emulate forced update of an old revision ----------
1153  $pcache->deleteOptionsKey( $page );
1154 
1155  $updater = $this->getDerivedPageDataUpdater( $page );
1156  $updater->prepareUpdate( $rev );
1157 
1158  // Force the page timestamp, so we notice whether ParserOutput::getTimestamp
1159  // or ParserOutput::getCacheTime are used.
1160  $page->setTimestamp( $rev->getTimestamp() );
1161  $updater->doParserCacheUpdate();
1162 
1163  // The cached ParserOutput should not use the revision timestamp
1164  $cached = $pcache->get( $page, $updater->getCanonicalParserOptions(), true );
1165  $this->assertInternalType( 'object', $cached );
1166  $this->assertSame( $updater->getCanonicalParserOutput(), $cached );
1167 
1168  $this->assertGreaterThan( $rev->getTimestamp(), $cached->getCacheTime() );
1169  $this->assertSame( $rev->getId(), $cached->getCacheRevisionId() );
1170  }
1171 
1175  private function hasMultiSlotSupport() {
1177 
1178  return ( $wgMultiContentRevisionSchemaMigrationStage & SCHEMA_COMPAT_WRITE_NEW )
1179  && ( $wgMultiContentRevisionSchemaMigrationStage & SCHEMA_COMPAT_READ_NEW );
1180  }
1181 
1182 }
static factory(Title $title)
Create a WikiPage object of the appropriate class for the given title.
Definition: WikiPage.php:138
testGetPreparedEditAfterPrepareContent()
\MediaWiki\Storage\DerivedPageDataUpdater::getPreparedEdit()
testDoUpdates()
\MediaWiki\Storage\DerivedPageDataUpdater::doUpdates() \MediaWiki\Storage\DerivedPageDataUpdater::doS...
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:1978
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
getArticleID( $flags=0)
Get the article ID for this Title from the link cache, adding it if necessary.
Definition: Title.php:3028
const CONTENT_MODEL_WIKITEXT
Definition: Defines.php:215
processing should stop and the error should be shown to the user * false
Definition: hooks.txt:193
int $wgMultiContentRevisionSchemaMigrationStage
RevisionStore table schema migration stage (content, slots, content_models & slot_roles tables)...
Apache License January AND DISTRIBUTION Definitions License shall mean the terms and conditions for use
const SCHEMA_COMPAT_READ_NEW
Definition: Defines.php:267
getModelID()
Returns the model id that identifies the content model this ContentHandler can handle.
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
Value object representing a modification of revision slots.
testPrepareUpdate()
\MediaWiki\Storage\DerivedPageDataUpdater::prepareUpdate() \MediaWiki\Storage\DerivedPageDataUpdater:...
testGetCanonicalParserOptions()
\MediaWiki\Storage\DerivedPageDataUpdater::getCanonicalParserOptions()
static getInstance()
Returns the global default instance of the top level service locator.
static newUnsavedComment( $comment, array $data=null)
Create a new, unsaved CommentStoreComment.
This document provides an overview of the usage of PageUpdater and DerivedPageDataUpdater
Definition: pageupdater.txt:3
Mutable RevisionRecord implementation, for building new revision entries programmatically.
The User object encapsulates all of the user-specific settings (user_id, name, rights, email address, options, last login time).
Definition: User.php:51
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:773
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:1978
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
static pendingUpdatesCount()
newPageUpdater(User $user, RevisionSlotsUpdate $forUpdate=null)
Returns a PageUpdater for creating new revisions on this page (or creating the page).
Definition: WikiPage.php:1797
testDoUpdatesCacheSaveDeferral_canonical()
\MediaWiki\Storage\DerivedPageDataUpdater::doUpdates() \MediaWiki\Storage\DerivedPageDataUpdater::doS...
testGetPreparedEditAfterPrepareUpdate()
\MediaWiki\Storage\DerivedPageDataUpdater::getPreparedEdit()
createRevision(WikiPage $page, $summary, $content=null, $user=null)
Creates a revision in the database.
const SCHEMA_COMPAT_WRITE_NEW
Definition: Defines.php:266
makeRevision(Title $title, RevisionSlotsUpdate $update, User $user, $comment, $id=0, $parentId=0)
Creates a dummy revision object without touching the database.
testPrepareUpdateOutputReset()
\MediaWiki\Storage\DerivedPageDataUpdater::prepareUpdate() \MediaWiki\Storage\DerivedPageDataUpdater:...
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:1978
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:773
namespace and then decline to actually register it file or subcat img or subcat $title
Definition: hooks.txt:918
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:1754
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
static newFromParentRevision(RevisionRecord $parent)
Returns an incomplete MutableRevisionRecord which uses $parent as its parent revision, and inherits all slots form it.
static makeTitleSafe( $ns, $title, $fragment='', $interwiki='')
Create a new Title from a namespace index and a DB key.
Definition: Title.php:620
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
testPrepareContent()
\MediaWiki\Storage\DerivedPageDataUpdater::prepareContent() \MediaWiki\Storage\DerivedPageDataUpdater...
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
testIsCountable( $articleCountMethod, $wikitextContent, $revisionVisibility, $isCountable)
provideIsCountable
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()
$page->newPageUpdater($user) $updater
Definition: pageupdater.txt:63
testPrepareUpdateReusesParserOutput()
\MediaWiki\Storage\DerivedPageDataUpdater::prepareUpdate()
static newUnsaved( $role, Content $content)
Constructs a new Slot from a Content object for a new revision.
Definition: SlotRecord.php:129
Allows to change the fields on the form that will be generated $name
Definition: hooks.txt:277
testDoParserCacheUpdate()
\MediaWiki\Storage\DerivedPageDataUpdater::doParserCacheUpdate()
Page revision base class.
const NS_TALK
Definition: Defines.php:61
getDerivedPageDataUpdater( $page, RevisionRecord $rec=null, User $user=null)
Mutable version of RevisionSlots, for constructing a new revision.
static newFromName( $name, $validate='valid')
Static factory method for creation from username.
Definition: User.php:535
$content
Definition: pageupdater.txt:72
testGrabCurrentRevision()
\MediaWiki\Storage\DerivedPageDataUpdater::grabCurrentRevision() \MediaWiki\Storage\DerivedPageDataUp...
testPrepareContentInherit()
\MediaWiki\Storage\DerivedPageDataUpdater::prepareContent() \MediaWiki\Storage\DerivedPageDataUpdater...
testDoUpdatesCacheSaveDeferral_noncanonical()
\MediaWiki\Storage\DerivedPageDataUpdater::doUpdates() \MediaWiki\Storage\DerivedPageDataUpdater::doS...
return true to allow those checks to and false if checking is done & $user
Definition: hooks.txt:1460
static newFromText( $text, $defaultNamespace=NS_MAIN)
Create a new Title from text, such as what one would find in a link.
Definition: Title.php:322