MediaWiki  1.30.1
WikiPageTest.php
Go to the documentation of this file.
1 <?php
2 
10 
11  protected $pages_to_delete;
12 
13  function __construct( $name = null, array $data = [], $dataName = '' ) {
14  parent::__construct( $name, $data, $dataName );
15 
16  $this->tablesUsed = array_merge(
17  $this->tablesUsed,
18  [ 'page',
19  'revision',
20  'archive',
21  'ip_changes',
22  'text',
23 
24  'recentchanges',
25  'logging',
26 
27  'page_props',
28  'pagelinks',
29  'categorylinks',
30  'langlinks',
31  'externallinks',
32  'imagelinks',
33  'templatelinks',
34  'iwlinks' ] );
35  }
36 
37  protected function setUp() {
38  parent::setUp();
39  $this->pages_to_delete = [];
40 
41  LinkCache::singleton()->clear(); # avoid cached redirect status, etc
42  }
43 
44  protected function tearDown() {
45  foreach ( $this->pages_to_delete as $p ) {
46  /* @var $p WikiPage */
47 
48  try {
49  if ( $p->exists() ) {
50  $p->doDeleteArticle( "testing done." );
51  }
52  } catch ( MWException $ex ) {
53  // fail silently
54  }
55  }
56  parent::tearDown();
57  }
58 
64  protected function newPage( $title, $model = null ) {
65  if ( is_string( $title ) ) {
66  $ns = $this->getDefaultWikitextNS();
68  }
69 
70  $p = new WikiPage( $title );
71 
72  $this->pages_to_delete[] = $p;
73 
74  return $p;
75  }
76 
84  protected function createPage( $page, $text, $model = null ) {
85  if ( is_string( $page ) || $page instanceof Title ) {
86  $page = $this->newPage( $page, $model );
87  }
88 
89  $content = ContentHandler::makeContent( $text, $page->getTitle(), $model );
90  $page->doEditContent( $content, "testing", EDIT_NEW );
91 
92  return $page;
93  }
94 
101  public function testDoEditContent() {
102  $page = $this->newPage( "WikiPageTest_testDoEditContent" );
103  $title = $page->getTitle();
104 
105  $content = ContentHandler::makeContent(
106  "[[Lorem ipsum]] dolor sit amet, consetetur sadipscing elitr, sed diam "
107  . " nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat.",
108  $title,
110  );
111 
112  $page->doEditContent( $content, "[[testing]] 1" );
113 
114  $this->assertTrue( $title->getArticleID() > 0, "Title object should have new page id" );
115  $this->assertTrue( $page->getId() > 0, "WikiPage should have new page id" );
116  $this->assertTrue( $title->exists(), "Title object should indicate that the page now exists" );
117  $this->assertTrue( $page->exists(), "WikiPage object should indicate that the page now exists" );
118 
119  $id = $page->getId();
120 
121  # ------------------------
122  $dbr = wfGetDB( DB_REPLICA );
123  $res = $dbr->select( 'pagelinks', '*', [ 'pl_from' => $id ] );
124  $n = $res->numRows();
125  $res->free();
126 
127  $this->assertEquals( 1, $n, 'pagelinks should contain one link from the page' );
128 
129  # ------------------------
130  $page = new WikiPage( $title );
131 
132  $retrieved = $page->getContent();
133  $this->assertTrue( $content->equals( $retrieved ), 'retrieved content doesn\'t equal original' );
134 
135  # ------------------------
136  $content = ContentHandler::makeContent(
137  "At vero eos et accusam et justo duo [[dolores]] et ea rebum. "
138  . "Stet clita kasd [[gubergren]], no sea takimata sanctus est.",
139  $title,
141  );
142 
143  $page->doEditContent( $content, "testing 2" );
144 
145  # ------------------------
146  $page = new WikiPage( $title );
147 
148  $retrieved = $page->getContent();
149  $this->assertTrue( $content->equals( $retrieved ), 'retrieved content doesn\'t equal original' );
150 
151  # ------------------------
152  $dbr = wfGetDB( DB_REPLICA );
153  $res = $dbr->select( 'pagelinks', '*', [ 'pl_from' => $id ] );
154  $n = $res->numRows();
155  $res->free();
156 
157  $this->assertEquals( 2, $n, 'pagelinks should contain two links from the page' );
158  }
159 
163  public function testDoDeleteArticle() {
164  $page = $this->createPage(
165  "WikiPageTest_testDoDeleteArticle",
166  "[[original text]] foo",
168  );
169  $id = $page->getId();
170 
171  $page->doDeleteArticle( "testing deletion" );
172 
173  $this->assertFalse(
174  $page->getTitle()->getArticleID() > 0,
175  "Title object should now have page id 0"
176  );
177  $this->assertFalse( $page->getId() > 0, "WikiPage should now have page id 0" );
178  $this->assertFalse(
179  $page->exists(),
180  "WikiPage::exists should return false after page was deleted"
181  );
182  $this->assertNull(
183  $page->getContent(),
184  "WikiPage::getContent should return null after page was deleted"
185  );
186 
187  $t = Title::newFromText( $page->getTitle()->getPrefixedText() );
188  $this->assertFalse(
189  $t->exists(),
190  "Title::exists should return false after page was deleted"
191  );
192 
193  // Run the job queue
195  $jobs = new RunJobs;
196  $jobs->loadParamsAndArgs( null, [ 'quiet' => true ], null );
197  $jobs->execute();
198 
199  # ------------------------
200  $dbr = wfGetDB( DB_REPLICA );
201  $res = $dbr->select( 'pagelinks', '*', [ 'pl_from' => $id ] );
202  $n = $res->numRows();
203  $res->free();
204 
205  $this->assertEquals( 0, $n, 'pagelinks should contain no more links from the page' );
206  }
207 
211  public function testDoDeleteUpdates() {
212  $page = $this->createPage(
213  "WikiPageTest_testDoDeleteArticle",
214  "[[original text]] foo",
216  );
217  $id = $page->getId();
218 
219  // Similar to MovePage logic
220  wfGetDB( DB_MASTER )->delete( 'page', [ 'page_id' => $id ], __METHOD__ );
221  $page->doDeleteUpdates( $id );
222 
223  // Run the job queue
225  $jobs = new RunJobs;
226  $jobs->loadParamsAndArgs( null, [ 'quiet' => true ], null );
227  $jobs->execute();
228 
229  # ------------------------
230  $dbr = wfGetDB( DB_REPLICA );
231  $res = $dbr->select( 'pagelinks', '*', [ 'pl_from' => $id ] );
232  $n = $res->numRows();
233  $res->free();
234 
235  $this->assertEquals( 0, $n, 'pagelinks should contain no more links from the page' );
236  }
237 
241  public function testGetRevision() {
242  $page = $this->newPage( "WikiPageTest_testGetRevision" );
243 
244  $rev = $page->getRevision();
245  $this->assertNull( $rev );
246 
247  # -----------------
248  $this->createPage( $page, "some text", CONTENT_MODEL_WIKITEXT );
249 
250  $rev = $page->getRevision();
251 
252  $this->assertEquals( $page->getLatest(), $rev->getId() );
253  $this->assertEquals( "some text", $rev->getContent()->getNativeData() );
254  }
255 
259  public function testGetContent() {
260  $page = $this->newPage( "WikiPageTest_testGetContent" );
261 
262  $content = $page->getContent();
263  $this->assertNull( $content );
264 
265  # -----------------
266  $this->createPage( $page, "some text", CONTENT_MODEL_WIKITEXT );
267 
268  $content = $page->getContent();
269  $this->assertEquals( "some text", $content->getNativeData() );
270  }
271 
275  public function testGetContentModel() {
277 
278  if ( !$wgContentHandlerUseDB ) {
279  $this->markTestSkipped( '$wgContentHandlerUseDB is disabled' );
280  }
281 
282  $page = $this->createPage(
283  "WikiPageTest_testGetContentModel",
284  "some text",
286  );
287 
288  $page = new WikiPage( $page->getTitle() );
289  $this->assertEquals( CONTENT_MODEL_JAVASCRIPT, $page->getContentModel() );
290  }
291 
295  public function testGetContentHandler() {
297 
298  if ( !$wgContentHandlerUseDB ) {
299  $this->markTestSkipped( '$wgContentHandlerUseDB is disabled' );
300  }
301 
302  $page = $this->createPage(
303  "WikiPageTest_testGetContentHandler",
304  "some text",
306  );
307 
308  $page = new WikiPage( $page->getTitle() );
309  $this->assertEquals( 'JavaScriptContentHandler', get_class( $page->getContentHandler() ) );
310  }
311 
315  public function testExists() {
316  $page = $this->newPage( "WikiPageTest_testExists" );
317  $this->assertFalse( $page->exists() );
318 
319  # -----------------
320  $this->createPage( $page, "some text", CONTENT_MODEL_WIKITEXT );
321  $this->assertTrue( $page->exists() );
322 
323  $page = new WikiPage( $page->getTitle() );
324  $this->assertTrue( $page->exists() );
325 
326  # -----------------
327  $page->doDeleteArticle( "done testing" );
328  $this->assertFalse( $page->exists() );
329 
330  $page = new WikiPage( $page->getTitle() );
331  $this->assertFalse( $page->exists() );
332  }
333 
334  public static function provideHasViewableContent() {
335  return [
336  [ 'WikiPageTest_testHasViewableContent', false, true ],
337  [ 'Special:WikiPageTest_testHasViewableContent', false ],
338  [ 'MediaWiki:WikiPageTest_testHasViewableContent', false ],
339  [ 'Special:Userlogin', true ],
340  [ 'MediaWiki:help', true ],
341  ];
342  }
343 
348  public function testHasViewableContent( $title, $viewable, $create = false ) {
349  $page = $this->newPage( $title );
350  $this->assertEquals( $viewable, $page->hasViewableContent() );
351 
352  if ( $create ) {
353  $this->createPage( $page, "some text", CONTENT_MODEL_WIKITEXT );
354  $this->assertTrue( $page->hasViewableContent() );
355 
356  $page = new WikiPage( $page->getTitle() );
357  $this->assertTrue( $page->hasViewableContent() );
358  }
359  }
360 
361  public static function provideGetRedirectTarget() {
362  return [
363  [ 'WikiPageTest_testGetRedirectTarget_1', CONTENT_MODEL_WIKITEXT, "hello world", null ],
364  [
365  'WikiPageTest_testGetRedirectTarget_2',
367  "#REDIRECT [[hello world]]",
368  "Hello world"
369  ],
370  ];
371  }
372 
377  public function testGetRedirectTarget( $title, $model, $text, $target ) {
378  $this->setMwGlobals( [
379  'wgCapitalLinks' => true,
380  ] );
381 
382  $page = $this->createPage( $title, $text, $model );
383 
384  # sanity check, because this test seems to fail for no reason for some people.
385  $c = $page->getContent();
386  $this->assertEquals( 'WikitextContent', get_class( $c ) );
387 
388  # now, test the actual redirect
389  $t = $page->getRedirectTarget();
390  $this->assertEquals( $target, is_null( $t ) ? null : $t->getPrefixedText() );
391  }
392 
397  public function testIsRedirect( $title, $model, $text, $target ) {
398  $page = $this->createPage( $title, $text, $model );
399  $this->assertEquals( !is_null( $target ), $page->isRedirect() );
400  }
401 
402  public static function provideIsCountable() {
403  return [
404 
405  // any
406  [ 'WikiPageTest_testIsCountable',
408  '',
409  'any',
410  true
411  ],
412  [ 'WikiPageTest_testIsCountable',
414  'Foo',
415  'any',
416  true
417  ],
418 
419  // comma
420  [ 'WikiPageTest_testIsCountable',
422  'Foo',
423  'comma',
424  false
425  ],
426  [ 'WikiPageTest_testIsCountable',
428  'Foo, bar',
429  'comma',
430  true
431  ],
432 
433  // link
434  [ 'WikiPageTest_testIsCountable',
436  'Foo',
437  'link',
438  false
439  ],
440  [ 'WikiPageTest_testIsCountable',
442  'Foo [[bar]]',
443  'link',
444  true
445  ],
446 
447  // redirects
448  [ 'WikiPageTest_testIsCountable',
450  '#REDIRECT [[bar]]',
451  'any',
452  false
453  ],
454  [ 'WikiPageTest_testIsCountable',
456  '#REDIRECT [[bar]]',
457  'comma',
458  false
459  ],
460  [ 'WikiPageTest_testIsCountable',
462  '#REDIRECT [[bar]]',
463  'link',
464  false
465  ],
466 
467  // not a content namespace
468  [ 'Talk:WikiPageTest_testIsCountable',
470  'Foo',
471  'any',
472  false
473  ],
474  [ 'Talk:WikiPageTest_testIsCountable',
476  'Foo, bar',
477  'comma',
478  false
479  ],
480  [ 'Talk:WikiPageTest_testIsCountable',
482  'Foo [[bar]]',
483  'link',
484  false
485  ],
486 
487  // not a content namespace, different model
488  [ 'MediaWiki:WikiPageTest_testIsCountable.js',
489  null,
490  'Foo',
491  'any',
492  false
493  ],
494  [ 'MediaWiki:WikiPageTest_testIsCountable.js',
495  null,
496  'Foo, bar',
497  'comma',
498  false
499  ],
500  [ 'MediaWiki:WikiPageTest_testIsCountable.js',
501  null,
502  'Foo [[bar]]',
503  'link',
504  false
505  ],
506  ];
507  }
508 
513  public function testIsCountable( $title, $model, $text, $mode, $expected ) {
515 
516  $this->setMwGlobals( 'wgArticleCountMethod', $mode );
517 
519 
521  && $model
523  ) {
524  $this->markTestSkipped( "Can not use non-default content model $model for "
525  . $title->getPrefixedDBkey() . " with \$wgContentHandlerUseDB disabled." );
526  }
527 
528  $page = $this->createPage( $title, $text, $model );
529 
530  $editInfo = $page->prepareContentForEdit( $page->getContent() );
531 
532  $v = $page->isCountable();
533  $w = $page->isCountable( $editInfo );
534 
535  $this->assertEquals(
536  $expected,
537  $v,
538  "isCountable( null ) returned unexpected value " . var_export( $v, true )
539  . " instead of " . var_export( $expected, true )
540  . " in mode `$mode` for text \"$text\""
541  );
542 
543  $this->assertEquals(
544  $expected,
545  $w,
546  "isCountable( \$editInfo ) returned unexpected value " . var_export( $v, true )
547  . " instead of " . var_export( $expected, true )
548  . " in mode `$mode` for text \"$text\""
549  );
550  }
551 
552  public static function provideGetParserOutput() {
553  return [
554  [
556  "hello ''world''\n",
557  "<div class=\"mw-parser-output\"><p>hello <i>world</i></p></div>"
558  ],
559  // @todo more...?
560  ];
561  }
562 
567  public function testGetParserOutput( $model, $text, $expectedHtml ) {
568  $page = $this->createPage( 'WikiPageTest_testGetParserOutput', $text, $model );
569 
570  $opt = $page->makeParserOptions( 'canonical' );
571  $po = $page->getParserOutput( $opt );
572  $text = $po->getText();
573 
574  $text = trim( preg_replace( '/<!--.*?-->/sm', '', $text ) ); # strip injected comments
575  $text = preg_replace( '!\s*(</p>|</div>)!sm', '\1', $text ); # don't let tidy confuse us
576 
577  $this->assertEquals( $expectedHtml, $text );
578 
579  return $po;
580  }
581 
585  public function testGetParserOutput_nonexisting() {
586  static $count = 0;
587  $count++;
588 
589  $page = new WikiPage( new Title( "WikiPageTest_testGetParserOutput_nonexisting_$count" ) );
590 
591  $opt = new ParserOptions();
592  $po = $page->getParserOutput( $opt );
593 
594  $this->assertFalse( $po, "getParserOutput() shall return false for non-existing pages." );
595  }
596 
600  public function testGetParserOutput_badrev() {
601  $page = $this->createPage( 'WikiPageTest_testGetParserOutput', "dummy", CONTENT_MODEL_WIKITEXT );
602 
603  $opt = new ParserOptions();
604  $po = $page->getParserOutput( $opt, $page->getLatest() + 1234 );
605 
606  // @todo would be neat to also test deleted revision
607 
608  $this->assertFalse( $po, "getParserOutput() shall return false for non-existing revisions." );
609  }
610 
611  public static $sections =
612 
613  "Intro
614 
615 == stuff ==
616 hello world
617 
618 == test ==
619 just a test
620 
621 == foo ==
622 more stuff
623 ";
624 
625  public function dataReplaceSection() {
626  // NOTE: assume the Help namespace to contain wikitext
627  return [
628  [ 'Help:WikiPageTest_testReplaceSection',
629  CONTENT_MODEL_WIKITEXT,
630  self::$sections,
631  "0",
632  "No more",
633  null,
634  trim( preg_replace( '/^Intro/sm', 'No more', self::$sections ) )
635  ],
636  [ 'Help:WikiPageTest_testReplaceSection',
637  CONTENT_MODEL_WIKITEXT,
638  self::$sections,
639  "",
640  "No more",
641  null,
642  "No more"
643  ],
644  [ 'Help:WikiPageTest_testReplaceSection',
645  CONTENT_MODEL_WIKITEXT,
646  self::$sections,
647  "2",
648  "== TEST ==\nmore fun",
649  null,
650  trim( preg_replace( '/^== test ==.*== foo ==/sm',
651  "== TEST ==\nmore fun\n\n== foo ==",
652  self::$sections ) )
653  ],
654  [ 'Help:WikiPageTest_testReplaceSection',
655  CONTENT_MODEL_WIKITEXT,
656  self::$sections,
657  "8",
658  "No more",
659  null,
660  trim( self::$sections )
661  ],
662  [ 'Help:WikiPageTest_testReplaceSection',
663  CONTENT_MODEL_WIKITEXT,
664  self::$sections,
665  "new",
666  "No more",
667  "New",
668  trim( self::$sections ) . "\n\n== New ==\n\nNo more"
669  ],
670  ];
671  }
672 
677  public function testReplaceSectionContent( $title, $model, $text, $section,
678  $with, $sectionTitle, $expected
679  ) {
680  $page = $this->createPage( $title, $text, $model );
681 
682  $content = ContentHandler::makeContent( $with, $page->getTitle(), $page->getContentModel() );
683  $c = $page->replaceSectionContent( $section, $content, $sectionTitle );
684 
685  $this->assertEquals( $expected, is_null( $c ) ? null : trim( $c->getNativeData() ) );
686  }
687 
692  public function testReplaceSectionAtRev( $title, $model, $text, $section,
693  $with, $sectionTitle, $expected
694  ) {
695  $page = $this->createPage( $title, $text, $model );
696  $baseRevId = $page->getLatest();
697 
698  $content = ContentHandler::makeContent( $with, $page->getTitle(), $page->getContentModel() );
699  $c = $page->replaceSectionAtRev( $section, $content, $sectionTitle, $baseRevId );
700 
701  $this->assertEquals( $expected, is_null( $c ) ? null : trim( $c->getNativeData() ) );
702  }
703 
704  /* @todo FIXME: fix this!
705  public function testGetUndoText() {
706  $this->markTestSkippedIfNoDiff3();
707 
708  $text = "one";
709  $page = $this->createPage( "WikiPageTest_testGetUndoText", $text );
710  $rev1 = $page->getRevision();
711 
712  $text .= "\n\ntwo";
713  $page->doEditContent(
714  ContentHandler::makeContent( $text, $page->getTitle() ),
715  "adding section two"
716  );
717  $rev2 = $page->getRevision();
718 
719  $text .= "\n\nthree";
720  $page->doEditContent(
721  ContentHandler::makeContent( $text, $page->getTitle() ),
722  "adding section three"
723  );
724  $rev3 = $page->getRevision();
725 
726  $text .= "\n\nfour";
727  $page->doEditContent(
728  ContentHandler::makeContent( $text, $page->getTitle() ),
729  "adding section four"
730  );
731  $rev4 = $page->getRevision();
732 
733  $text .= "\n\nfive";
734  $page->doEditContent(
735  ContentHandler::makeContent( $text, $page->getTitle() ),
736  "adding section five"
737  );
738  $rev5 = $page->getRevision();
739 
740  $text .= "\n\nsix";
741  $page->doEditContent(
742  ContentHandler::makeContent( $text, $page->getTitle() ),
743  "adding section six"
744  );
745  $rev6 = $page->getRevision();
746 
747  $undo6 = $page->getUndoText( $rev6 );
748  if ( $undo6 === false ) $this->fail( "getUndoText failed for rev6" );
749  $this->assertEquals( "one\n\ntwo\n\nthree\n\nfour\n\nfive", $undo6 );
750 
751  $undo3 = $page->getUndoText( $rev4, $rev2 );
752  if ( $undo3 === false ) $this->fail( "getUndoText failed for rev4..rev2" );
753  $this->assertEquals( "one\n\ntwo\n\nfive", $undo3 );
754 
755  $undo2 = $page->getUndoText( $rev2 );
756  if ( $undo2 === false ) $this->fail( "getUndoText failed for rev2" );
757  $this->assertEquals( "one\n\nfive", $undo2 );
758  }
759  */
760 
764  public function testGetOldestRevision() {
765  $page = $this->newPage( "WikiPageTest_testGetOldestRevision" );
766  $page->doEditContent(
767  new WikitextContent( 'one' ),
768  "first edit",
769  EDIT_NEW
770  );
771  $rev1 = $page->getRevision();
772 
773  $page = new WikiPage( $page->getTitle() );
774  $page->doEditContent(
775  new WikitextContent( 'two' ),
776  "second edit",
777  EDIT_UPDATE
778  );
779 
780  $page = new WikiPage( $page->getTitle() );
781  $page->doEditContent(
782  new WikitextContent( 'three' ),
783  "third edit",
784  EDIT_UPDATE
785  );
786 
787  // sanity check
788  $this->assertNotEquals(
789  $rev1->getId(),
790  $page->getRevision()->getId(),
791  '$page->getRevision()->getId()'
792  );
793 
794  // actual test
795  $this->assertEquals(
796  $rev1->getId(),
797  $page->getOldestRevision()->getId(),
798  '$page->getOldestRevision()->getId()'
799  );
800  }
801 
806  public function broken_testDoRollback() {
807  $admin = new User();
808  $admin->setName( "Admin" );
809 
810  $text = "one";
811  $page = $this->newPage( "WikiPageTest_testDoRollback" );
812  $page->doEditContent( ContentHandler::makeContent( $text, $page->getTitle() ),
813  "section one", EDIT_NEW, false, $admin );
814 
815  $user1 = new User();
816  $user1->setName( "127.0.1.11" );
817  $text .= "\n\ntwo";
818  $page = new WikiPage( $page->getTitle() );
819  $page->doEditContent( ContentHandler::makeContent( $text, $page->getTitle() ),
820  "adding section two", 0, false, $user1 );
821 
822  $user2 = new User();
823  $user2->setName( "127.0.2.13" );
824  $text .= "\n\nthree";
825  $page = new WikiPage( $page->getTitle() );
826  $page->doEditContent( ContentHandler::makeContent( $text, $page->getTitle() ),
827  "adding section three", 0, false, $user2 );
828 
829  # we are having issues with doRollback spuriously failing. Apparently
830  # the last revision somehow goes missing or not committed under some
831  # circumstances. So, make sure the last revision has the right user name.
832  $dbr = wfGetDB( DB_REPLICA );
833  $this->assertEquals( 3, Revision::countByPageId( $dbr, $page->getId() ) );
834 
835  $page = new WikiPage( $page->getTitle() );
836  $rev3 = $page->getRevision();
837  $this->assertEquals( '127.0.2.13', $rev3->getUserText() );
838 
839  $rev2 = $rev3->getPrevious();
840  $this->assertEquals( '127.0.1.11', $rev2->getUserText() );
841 
842  $rev1 = $rev2->getPrevious();
843  $this->assertEquals( 'Admin', $rev1->getUserText() );
844 
845  # now, try the actual rollback
846  $admin->addToDatabase();
847  $admin->addGroup( "sysop" ); # XXX: make the test user a sysop...
848  $token = $admin->getEditToken(
849  [ $page->getTitle()->getPrefixedText(), $user2->getName() ],
850  null
851  );
852  $errors = $page->doRollback(
853  $user2->getName(),
854  "testing revert",
855  $token,
856  false,
857  $details,
858  $admin
859  );
860 
861  if ( $errors ) {
862  $this->fail( "Rollback failed:\n" . print_r( $errors, true )
863  . ";\n" . print_r( $details, true ) );
864  }
865 
866  $page = new WikiPage( $page->getTitle() );
867  $this->assertEquals( $rev2->getSha1(), $page->getRevision()->getSha1(),
868  "rollback did not revert to the correct revision" );
869  $this->assertEquals( "one\n\ntwo", $page->getContent()->getNativeData() );
870  }
871 
876  public function testDoRollback() {
877  $admin = new User();
878  $admin->setName( "Admin" );
879  $admin->addToDatabase();
880 
881  $text = "one";
882  $page = $this->newPage( "WikiPageTest_testDoRollback" );
883  $page->doEditContent(
884  ContentHandler::makeContent( $text, $page->getTitle(), CONTENT_MODEL_WIKITEXT ),
885  "section one",
886  EDIT_NEW,
887  false,
888  $admin
889  );
890  $rev1 = $page->getRevision();
891 
892  $user1 = new User();
893  $user1->setName( "127.0.1.11" );
894  $text .= "\n\ntwo";
895  $page = new WikiPage( $page->getTitle() );
896  $page->doEditContent(
897  ContentHandler::makeContent( $text, $page->getTitle(), CONTENT_MODEL_WIKITEXT ),
898  "adding section two",
899  0,
900  false,
901  $user1
902  );
903 
904  # now, try the rollback
905  $admin->addGroup( "sysop" ); # XXX: make the test user a sysop...
906  $token = $admin->getEditToken( 'rollback' );
907  $errors = $page->doRollback(
908  $user1->getName(),
909  "testing revert",
910  $token,
911  false,
912  $details,
913  $admin
914  );
915 
916  if ( $errors ) {
917  $this->fail( "Rollback failed:\n" . print_r( $errors, true )
918  . ";\n" . print_r( $details, true ) );
919  }
920 
921  $page = new WikiPage( $page->getTitle() );
922  $this->assertEquals( $rev1->getSha1(), $page->getRevision()->getSha1(),
923  "rollback did not revert to the correct revision" );
924  $this->assertEquals( "one", $page->getContent()->getNativeData() );
925  }
926 
930  public function testDoRollbackFailureSameContent() {
931  $admin = new User();
932  $admin->setName( "Admin" );
933  $admin->addToDatabase();
934  $admin->addGroup( "sysop" ); # XXX: make the test user a sysop...
935 
936  $text = "one";
937  $page = $this->newPage( "WikiPageTest_testDoRollback" );
938  $page->doEditContent(
939  ContentHandler::makeContent( $text, $page->getTitle(), CONTENT_MODEL_WIKITEXT ),
940  "section one",
941  EDIT_NEW,
942  false,
943  $admin
944  );
945  $rev1 = $page->getRevision();
946 
947  $user1 = new User();
948  $user1->setName( "127.0.1.11" );
949  $user1->addToDatabase();
950  $user1->addGroup( "sysop" ); # XXX: make the test user a sysop...
951  $text .= "\n\ntwo";
952  $page = new WikiPage( $page->getTitle() );
953  $page->doEditContent(
954  ContentHandler::makeContent( $text, $page->getTitle(), CONTENT_MODEL_WIKITEXT ),
955  "adding section two",
956  0,
957  false,
958  $user1
959  );
960 
961  # now, do a the rollback from the same user was doing the edit before
962  $resultDetails = [];
963  $token = $user1->getEditToken( 'rollback' );
964  $errors = $page->doRollback(
965  $user1->getName(),
966  "testing revert same user",
967  $token,
968  false,
969  $resultDetails,
970  $admin
971  );
972 
973  $this->assertEquals( [], $errors, "Rollback failed same user" );
974 
975  # now, try the rollback
976  $resultDetails = [];
977  $token = $admin->getEditToken( 'rollback' );
978  $errors = $page->doRollback(
979  $user1->getName(),
980  "testing revert",
981  $token,
982  false,
983  $resultDetails,
984  $admin
985  );
986 
987  $this->assertEquals( [ [ 'alreadyrolled', 'WikiPageTest testDoRollback',
988  '127.0.1.11', 'Admin' ] ], $errors, "Rollback not failed" );
989 
990  $page = new WikiPage( $page->getTitle() );
991  $this->assertEquals( $rev1->getSha1(), $page->getRevision()->getSha1(),
992  "rollback did not revert to the correct revision" );
993  $this->assertEquals( "one", $page->getContent()->getNativeData() );
994  }
995 
996  public static function provideGetAutoDeleteReason() {
997  return [
998  [
999  [],
1000  false,
1001  false
1002  ],
1003 
1004  [
1005  [
1006  [ "first edit", null ],
1007  ],
1008  "/first edit.*only contributor/",
1009  false
1010  ],
1011 
1012  [
1013  [
1014  [ "first edit", null ],
1015  [ "second edit", null ],
1016  ],
1017  "/second edit.*only contributor/",
1018  true
1019  ],
1020 
1021  [
1022  [
1023  [ "first edit", "127.0.2.22" ],
1024  [ "second edit", "127.0.3.33" ],
1025  ],
1026  "/second edit/",
1027  true
1028  ],
1029 
1030  [
1031  [
1032  [
1033  "first edit: "
1034  . "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam "
1035  . " nonumy eirmod tempor invidunt ut labore et dolore magna "
1036  . "aliquyam erat, sed diam voluptua. At vero eos et accusam "
1037  . "et justo duo dolores et ea rebum. Stet clita kasd gubergren, "
1038  . "no sea takimata sanctus est Lorem ipsum dolor sit amet.'",
1039  null
1040  ],
1041  ],
1042  '/first edit:.*\.\.\."/',
1043  false
1044  ],
1045 
1046  [
1047  [
1048  [ "first edit", "127.0.2.22" ],
1049  [ "", "127.0.3.33" ],
1050  ],
1051  "/before blanking.*first edit/",
1052  true
1053  ],
1054 
1055  ];
1056  }
1057 
1062  public function testGetAutoDeleteReason( $edits, $expectedResult, $expectedHistory ) {
1063  global $wgUser;
1064 
1065  // NOTE: assume Help namespace to contain wikitext
1066  $page = $this->newPage( "Help:WikiPageTest_testGetAutoDeleteReason" );
1067 
1068  $c = 1;
1069 
1070  foreach ( $edits as $edit ) {
1071  $user = new User();
1072 
1073  if ( !empty( $edit[1] ) ) {
1074  $user->setName( $edit[1] );
1075  } else {
1076  $user = $wgUser;
1077  }
1078 
1079  $content = ContentHandler::makeContent( $edit[0], $page->getTitle(), $page->getContentModel() );
1080 
1081  $page->doEditContent( $content, "test edit $c", $c < 2 ? EDIT_NEW : 0, false, $user );
1082 
1083  $c += 1;
1084  }
1085 
1086  $reason = $page->getAutoDeleteReason( $hasHistory );
1087 
1088  if ( is_bool( $expectedResult ) || is_null( $expectedResult ) ) {
1089  $this->assertEquals( $expectedResult, $reason );
1090  } else {
1091  $this->assertTrue( (bool)preg_match( $expectedResult, $reason ),
1092  "Autosummary didn't match expected pattern $expectedResult: $reason" );
1093  }
1094 
1095  $this->assertEquals( $expectedHistory, $hasHistory,
1096  "expected \$hasHistory to be " . var_export( $expectedHistory, true ) );
1097 
1098  $page->doDeleteArticle( "done" );
1099  }
1100 
1101  public static function providePreSaveTransform() {
1102  return [
1103  [ 'hello this is ~~~',
1104  "hello this is [[Special:Contributions/127.0.0.1|127.0.0.1]]",
1105  ],
1106  [ 'hello \'\'this\'\' is <nowiki>~~~</nowiki>',
1107  'hello \'\'this\'\' is <nowiki>~~~</nowiki>',
1108  ],
1109  ];
1110  }
1111 
1115  public function testWikiPageFactory() {
1116  $title = Title::makeTitle( NS_FILE, 'Someimage.png' );
1117  $page = WikiPage::factory( $title );
1118  $this->assertEquals( 'WikiFilePage', get_class( $page ) );
1119 
1120  $title = Title::makeTitle( NS_CATEGORY, 'SomeCategory' );
1121  $page = WikiPage::factory( $title );
1122  $this->assertEquals( 'WikiCategoryPage', get_class( $page ) );
1123 
1124  $title = Title::makeTitle( NS_MAIN, 'SomePage' );
1125  $page = WikiPage::factory( $title );
1126  $this->assertEquals( 'WikiPage', get_class( $page ) );
1127  }
1128 
1134  public function testCommentMigrationOnDeletion( $wstage, $rstage ) {
1135  $this->setMwGlobals( 'wgCommentTableSchemaMigrationStage', $wstage );
1136  $dbr = wfGetDB( DB_REPLICA );
1137 
1138  $page = $this->createPage(
1139  "WikiPageTest_testCommentMigrationOnDeletion",
1140  "foo",
1141  CONTENT_MODEL_WIKITEXT
1142  );
1143  $revid = $page->getLatest();
1144  if ( $wstage > MIGRATION_OLD ) {
1145  $comment_id = $dbr->selectField(
1146  'revision_comment_temp',
1147  'revcomment_comment_id',
1148  [ 'revcomment_rev' => $revid ],
1149  __METHOD__
1150  );
1151  }
1152 
1153  $this->setMwGlobals( 'wgCommentTableSchemaMigrationStage', $rstage );
1154 
1155  $page->doDeleteArticle( "testing deletion" );
1156 
1157  if ( $rstage > MIGRATION_OLD ) {
1158  // Didn't leave behind any 'revision_comment_temp' rows
1159  $n = $dbr->selectField(
1160  'revision_comment_temp', 'COUNT(*)', [ 'revcomment_rev' => $revid ], __METHOD__
1161  );
1162  $this->assertEquals( 0, $n, 'no entry in revision_comment_temp after deletion' );
1163 
1164  // Copied or upgraded the comment_id, as applicable
1165  $ar_comment_id = $dbr->selectField(
1166  'archive',
1167  'ar_comment_id',
1168  [ 'ar_rev_id' => $revid ],
1169  __METHOD__
1170  );
1171  if ( $wstage > MIGRATION_OLD ) {
1172  $this->assertSame( $comment_id, $ar_comment_id );
1173  } else {
1174  $this->assertNotEquals( 0, $ar_comment_id );
1175  }
1176  }
1177 
1178  // Copied rev_comment, if applicable
1179  if ( $rstage <= MIGRATION_WRITE_BOTH && $wstage <= MIGRATION_WRITE_BOTH ) {
1180  $ar_comment = $dbr->selectField(
1181  'archive',
1182  'ar_comment',
1183  [ 'ar_rev_id' => $revid ],
1184  __METHOD__
1185  );
1186  $this->assertSame( 'testing', $ar_comment );
1187  }
1188  }
1189 
1190  public static function provideCommentMigrationOnDeletion() {
1191  return [
1192  [ MIGRATION_OLD, MIGRATION_OLD ],
1193  [ MIGRATION_OLD, MIGRATION_WRITE_BOTH ],
1194  [ MIGRATION_OLD, MIGRATION_WRITE_NEW ],
1195  [ MIGRATION_WRITE_BOTH, MIGRATION_OLD ],
1196  [ MIGRATION_WRITE_BOTH, MIGRATION_WRITE_BOTH ],
1197  [ MIGRATION_WRITE_BOTH, MIGRATION_WRITE_NEW ],
1198  [ MIGRATION_WRITE_BOTH, MIGRATION_NEW ],
1199  [ MIGRATION_WRITE_NEW, MIGRATION_WRITE_BOTH ],
1200  [ MIGRATION_WRITE_NEW, MIGRATION_WRITE_NEW ],
1201  [ MIGRATION_WRITE_NEW, MIGRATION_NEW ],
1202  [ MIGRATION_NEW, MIGRATION_WRITE_BOTH ],
1203  [ MIGRATION_NEW, MIGRATION_WRITE_NEW ],
1204  [ MIGRATION_NEW, MIGRATION_NEW ],
1205  ];
1206  }
1207 
1208 }
WikiPageTest\testGetContent
testGetContent()
WikiPage::getContent.
Definition: WikiPageTest.php:259
RunJobs
Maintenance script that runs pending jobs.
Definition: runJobs.php:33
Title\newFromText
static newFromText( $text, $defaultNamespace=NS_MAIN)
Create a new Title from text, such as what one would find in a link.
Definition: Title.php:268
WikiPageTest\testHasViewableContent
testHasViewableContent( $title, $viewable, $create=false)
provideHasViewableContent WikiPage::hasViewableContent
Definition: WikiPageTest.php:348
false
processing should stop and the error should be shown to the user * false
Definition: hooks.txt:187
WikiPageTest\testGetParserOutput
testGetParserOutput( $model, $text, $expectedHtml)
provideGetParserOutput WikiPage::getParserOutput
Definition: WikiPageTest.php:567
WikiPageTest\testGetContentHandler
testGetContentHandler()
WikiPage::getContentHandler.
Definition: WikiPageTest.php:295
WikiPageTest\tearDown
tearDown()
Definition: WikiPageTest.php:44
is
We use the convention $dbr for read and $dbw for write to help you keep track of whether the database object is a the world will explode Or to be a subsequent write query which succeeded on the master may fail when replicated to the slave due to a unique key collision Replication on the slave will stop and it may take hours to repair the database and get it back online Setting read_only in my cnf on the slave will avoid this but given the dire we prefer to have as many checks as possible We provide a but the wrapper functions like please read the documentation for except in special pages derived from QueryPage It s a common pitfall for new developers to submit code containing SQL queries which examine huge numbers of rows Remember that COUNT * is(N), counting rows in atable is like counting beans in a bucket.------------------------------------------------------------------------ Replication------------------------------------------------------------------------The largest installation of MediaWiki, Wikimedia, uses a large set ofslave MySQL servers replicating writes made to a master MySQL server. Itis important to understand the issues associated with this setup if youwant to write code destined for Wikipedia.It 's often the case that the best algorithm to use for a given taskdepends on whether or not replication is in use. Due to our unabashedWikipedia-centrism, we often just use the replication-friendly version, but if you like, you can use wfGetLB() ->getServerCount() > 1 tocheck to see if replication is in use.===Lag===Lag primarily occurs when large write queries are sent to the master.Writes on the master are executed in parallel, but they are executed inserial when they are replicated to the slaves. The master writes thequery to the binlog when the transaction is committed. The slaves pollthe binlog and start executing the query as soon as it appears. They canservice reads while they are performing a write query, but will not readanything more from the binlog and thus will perform no more writes. Thismeans that if the write query runs for a long time, the slaves will lagbehind the master for the time it takes for the write query to complete.Lag can be exacerbated by high read load. MediaWiki 's load balancer willstop sending reads to a slave when it is lagged by more than 30 seconds.If the load ratios are set incorrectly, or if there is too much loadgenerally, this may lead to a slave permanently hovering around 30seconds lag.If all slaves are lagged by more than 30 seconds, MediaWiki will stopwriting to the database. All edits and other write operations will berefused, with an error returned to the user. This gives the slaves achance to catch up. Before we had this mechanism, the slaves wouldregularly lag by several minutes, making review of recent editsdifficult.In addition to this, MediaWiki attempts to ensure that the user seesevents occurring on the wiki in chronological order. A few seconds of lagcan be tolerated, as long as the user sees a consistent picture fromsubsequent requests. This is done by saving the master binlog positionin the session, and then at the start of each request, waiting for theslave to catch up to that position before doing any reads from it. Ifthis wait times out, reads are allowed anyway, but the request isconsidered to be in "lagged slave mode". Lagged slave mode can bechecked by calling wfGetLB() ->getLaggedSlaveMode(). The onlypractical consequence at present is a warning displayed in the pagefooter.===Lag avoidance===To avoid excessive lag, queries which write large numbers of rows shouldbe split up, generally to write one row at a time. Multi-row INSERT ...SELECT queries are the worst offenders should be avoided altogether.Instead do the select first and then the insert.===Working with lag===Despite our best efforts, it 's not practical to guarantee a low-lagenvironment. Lag will usually be less than one second, but mayoccasionally be up to 30 seconds. For scalability, it 's very importantto keep load on the master low, so simply sending all your queries tothe master is not the answer. So when you have a genuine need forup-to-date data, the following approach is advised:1) Do a quick query to the master for a sequence number or timestamp 2) Run the full query on the slave and check if it matches the data you gotfrom the master 3) If it doesn 't, run the full query on the masterTo avoid swamping the master every time the slaves lag, use of thisapproach should be kept to a minimum. In most cases you should just readfrom the slave and let the user deal with the delay.------------------------------------------------------------------------ Lock contention------------------------------------------------------------------------Due to the high write rate on Wikipedia(and some other wikis), MediaWiki developers need to be very careful to structure their writesto avoid long-lasting locks. By default, MediaWiki opens a transactionat the first query, and commits it before the output is sent. Locks willbe held from the time when the query is done until the commit. So youcan reduce lock time by doing as much processing as possible before youdo your write queries.Often this approach is not good enough, and it becomes necessary toenclose small groups of queries in their own transaction. Use thefollowing syntax:$dbw=wfGetDB(DB_MASTER
$opt
$opt
Definition: postprocess-phan.php:115
WikiPageTest\createPage
createPage( $page, $text, $model=null)
Definition: WikiPageTest.php:84
WikiPage\doDeleteArticle
doDeleteArticle( $reason, $suppress=false, $u1=null, $u2=null, &$error='', User $user=null)
Same as doDeleteArticleReal(), but returns a simple boolean.
Definition: WikiPage.php:2717
WikiPageTest\testGetRedirectTarget
testGetRedirectTarget( $title, $model, $text, $target)
provideGetRedirectTarget WikiPage::getRedirectTarget
Definition: WikiPageTest.php:377
Maintenance\loadParamsAndArgs
loadParamsAndArgs( $self=null, $opts=null, $args=null)
Process command line arguments $mOptions becomes an array with keys set to the option names $mArgs be...
Definition: Maintenance.php:876
WikiPageTest\testGetContentModel
testGetContentModel()
WikiPage::getContentModel.
Definition: WikiPageTest.php:275
WikiPage
Class representing a MediaWiki article and history.
Definition: WikiPage.php:37
WikiPageTest\setUp
setUp()
Definition: WikiPageTest.php:37
WikiPageTest\testIsRedirect
testIsRedirect( $title, $model, $text, $target)
provideGetRedirectTarget WikiPage::isRedirect
Definition: WikiPageTest.php:397
WikiPageTest\testDoRollback
testDoRollback()
Definition: WikiPageTest.php:876
$res
$res
Definition: database.txt:21
JobQueueGroup\destroySingletons
static destroySingletons()
Destroy the singleton instances.
Definition: JobQueueGroup.php:94
$name
Allows to change the fields on the form that will be generated $name
Definition: hooks.txt:302
CONTENT_MODEL_WIKITEXT
const CONTENT_MODEL_WIKITEXT
Definition: Defines.php:236
$wgContentHandlerUseDB
$wgContentHandlerUseDB
Set to false to disable use of the database fields introduced by the ContentHandler facility.
Definition: DefaultSettings.php:8476
WikiPageTest\testDoDeleteUpdates
testDoDeleteUpdates()
WikiPage::doDeleteUpdates.
Definition: WikiPageTest.php:211
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
WikiPageTest\__construct
__construct( $name=null, array $data=[], $dataName='')
Definition: WikiPageTest.php:13
WikiPageTest\testDoDeleteArticle
testDoDeleteArticle()
WikiPage::doDeleteArticle.
Definition: WikiPageTest.php:163
WikiPageTest\$pages_to_delete
$pages_to_delete
Definition: WikiPageTest.php:11
ContentHandler\getDefaultModelFor
static getDefaultModelFor(Title $title)
Returns the name of the default content model to be used for the page with the given title.
Definition: ContentHandler.php:178
MWException
MediaWiki exception.
Definition: MWException.php:26
rollback
presenting them properly to the user as errors is done by the caller return true use this to change the list i e rollback
Definition: hooks.txt:1750
$title
namespace and then decline to actually register it file or subcat img or subcat $title
Definition: hooks.txt:932
wfGetDB
wfGetDB( $db, $groups=[], $wiki=false)
Get a Database object.
Definition: GlobalFunctions.php:2856
MediaWikiTestCase\setMwGlobals
setMwGlobals( $pairs, $value=null)
Sets a global, maintaining a stashed version of the previous global to be restored in tearDown.
Definition: MediaWikiTestCase.php:672
WikiPageTest\testExists
testExists()
WikiPage::exists.
Definition: WikiPageTest.php:315
WikiPageTest\provideIsCountable
static provideIsCountable()
Definition: WikiPageTest.php:402
MediaWikiTestCase\getDefaultWikitextNS
getDefaultWikitextNS()
Returns the ID of a namespace that defaults to Wikitext.
Definition: MediaWikiTestCase.php:1693
WikiPageTest\provideGetRedirectTarget
static provideGetRedirectTarget()
Definition: WikiPageTest.php:361
global
when a variable name is used in a it is silently declared as a new masking the global
Definition: design.txt:93
WikiPageTest
ContentHandler Database ^— important, causes temporary tables to be used instead of the real database...
Definition: WikiPageTest.php:9
DB_REPLICA
const DB_REPLICA
Definition: defines.php:25
DB_MASTER
const DB_MASTER
Definition: defines.php:26
ContentHandler\makeContent
static makeContent( $text, Title $title=null, $modelId=null, $format=null)
Convenience function for creating a Content object from a given textual representation.
Definition: ContentHandler.php:129
WikiPageTest\testIsCountable
testIsCountable( $title, $model, $text, $mode, $expected)
provideIsCountable WikiPage::isCountable
Definition: WikiPageTest.php:513
WikiPageTest\newPage
newPage( $title, $model=null)
Definition: WikiPageTest.php:64
etc
design txt This is a brief overview of the new design More thorough and up to date information is available on the documentation wiki at etc Handles the details of getting and saving to the user table of the and dealing with sessions and cookies OutputPage Encapsulates the entire HTML page that will be sent in response to any server request It is used by calling its functions to add etc
Definition: design.txt:12
MediaWikiLangTestCase
Base class that store and restore the Language objects.
Definition: MediaWikiLangTestCase.php:6
WikiPageTest\provideHasViewableContent
static provideHasViewableContent()
Definition: WikiPageTest.php:334
Special
wiki Special
Definition: All_system_messages.txt:2667
EDIT_NEW
const EDIT_NEW
Definition: Defines.php:153
Title
Represents a title within MediaWiki.
Definition: Title.php:39
$dbr
if(! $regexes) $dbr
Definition: cleanup.php:94
WikiPageTest\provideGetParserOutput
static provideGetParserOutput()
Definition: WikiPageTest.php:552
LinkCache\singleton
static singleton()
Get an instance of this class.
Definition: LinkCache.php:67
$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:1750
WikiPageTest\testDoEditContent
testDoEditContent()
WikiPage::doEditContent WikiPage::doModify WikiPage::doCreate WikiPage::doEditUpdates.
Definition: WikiPageTest.php:101
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
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:1965
CONTENT_MODEL_JAVASCRIPT
const CONTENT_MODEL_JAVASCRIPT
Definition: Defines.php:237
$t
$t
Definition: testCompression.php:67
WikiPageTest\testGetRevision
testGetRevision()
WikiPage::getRevision.
Definition: WikiPageTest.php:241
redirect
This list may contain false positives That usually means there is additional text with links below the first Each row contains links to the first and second redirect
Definition: All_system_messages.txt:1267
array
the array() calling protocol came about after MediaWiki 1.4rc1.