MediaWiki  1.29.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  'text',
21 
22  'recentchanges',
23  'logging',
24 
25  'page_props',
26  'pagelinks',
27  'categorylinks',
28  'langlinks',
29  'externallinks',
30  'imagelinks',
31  'templatelinks',
32  'iwlinks' ] );
33  }
34 
35  protected function setUp() {
36  parent::setUp();
37  $this->pages_to_delete = [];
38 
39  LinkCache::singleton()->clear(); # avoid cached redirect status, etc
40  }
41 
42  protected function tearDown() {
43  foreach ( $this->pages_to_delete as $p ) {
44  /* @var $p WikiPage */
45 
46  try {
47  if ( $p->exists() ) {
48  $p->doDeleteArticle( "testing done." );
49  }
50  } catch ( MWException $ex ) {
51  // fail silently
52  }
53  }
54  parent::tearDown();
55  }
56 
62  protected function newPage( $title, $model = null ) {
63  if ( is_string( $title ) ) {
64  $ns = $this->getDefaultWikitextNS();
66  }
67 
68  $p = new WikiPage( $title );
69 
70  $this->pages_to_delete[] = $p;
71 
72  return $p;
73  }
74 
82  protected function createPage( $page, $text, $model = null ) {
83  if ( is_string( $page ) || $page instanceof Title ) {
84  $page = $this->newPage( $page, $model );
85  }
86 
87  $content = ContentHandler::makeContent( $text, $page->getTitle(), $model );
88  $page->doEditContent( $content, "testing", EDIT_NEW );
89 
90  return $page;
91  }
92 
99  public function testDoEditContent() {
100  $page = $this->newPage( "WikiPageTest_testDoEditContent" );
101  $title = $page->getTitle();
102 
104  "[[Lorem ipsum]] dolor sit amet, consetetur sadipscing elitr, sed diam "
105  . " nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat.",
106  $title,
108  );
109 
110  $page->doEditContent( $content, "[[testing]] 1" );
111 
112  $this->assertTrue( $title->getArticleID() > 0, "Title object should have new page id" );
113  $this->assertTrue( $page->getId() > 0, "WikiPage should have new page id" );
114  $this->assertTrue( $title->exists(), "Title object should indicate that the page now exists" );
115  $this->assertTrue( $page->exists(), "WikiPage object should indicate that the page now exists" );
116 
117  $id = $page->getId();
118 
119  # ------------------------
120  $dbr = wfGetDB( DB_SLAVE );
121  $res = $dbr->select( 'pagelinks', '*', [ 'pl_from' => $id ] );
122  $n = $res->numRows();
123  $res->free();
124 
125  $this->assertEquals( 1, $n, 'pagelinks should contain one link from the page' );
126 
127  # ------------------------
128  $page = new WikiPage( $title );
129 
130  $retrieved = $page->getContent();
131  $this->assertTrue( $content->equals( $retrieved ), 'retrieved content doesn\'t equal original' );
132 
133  # ------------------------
135  "At vero eos et accusam et justo duo [[dolores]] et ea rebum. "
136  . "Stet clita kasd [[gubergren]], no sea takimata sanctus est.",
137  $title,
139  );
140 
141  $page->doEditContent( $content, "testing 2" );
142 
143  # ------------------------
144  $page = new WikiPage( $title );
145 
146  $retrieved = $page->getContent();
147  $this->assertTrue( $content->equals( $retrieved ), 'retrieved content doesn\'t equal original' );
148 
149  # ------------------------
150  $dbr = wfGetDB( DB_SLAVE );
151  $res = $dbr->select( 'pagelinks', '*', [ 'pl_from' => $id ] );
152  $n = $res->numRows();
153  $res->free();
154 
155  $this->assertEquals( 2, $n, 'pagelinks should contain two links from the page' );
156  }
157 
161  public function testDoDeleteArticle() {
162  $page = $this->createPage(
163  "WikiPageTest_testDoDeleteArticle",
164  "[[original text]] foo",
166  );
167  $id = $page->getId();
168 
169  $page->doDeleteArticle( "testing deletion" );
170 
171  $this->assertFalse(
172  $page->getTitle()->getArticleID() > 0,
173  "Title object should now have page id 0"
174  );
175  $this->assertFalse( $page->getId() > 0, "WikiPage should now have page id 0" );
176  $this->assertFalse(
177  $page->exists(),
178  "WikiPage::exists should return false after page was deleted"
179  );
180  $this->assertNull(
181  $page->getContent(),
182  "WikiPage::getContent should return null after page was deleted"
183  );
184 
185  $t = Title::newFromText( $page->getTitle()->getPrefixedText() );
186  $this->assertFalse(
187  $t->exists(),
188  "Title::exists should return false after page was deleted"
189  );
190 
191  // Run the job queue
193  $jobs = new RunJobs;
194  $jobs->loadParamsAndArgs( null, [ 'quiet' => true ], null );
195  $jobs->execute();
196 
197  # ------------------------
198  $dbr = wfGetDB( DB_SLAVE );
199  $res = $dbr->select( 'pagelinks', '*', [ 'pl_from' => $id ] );
200  $n = $res->numRows();
201  $res->free();
202 
203  $this->assertEquals( 0, $n, 'pagelinks should contain no more links from the page' );
204  }
205 
209  public function testDoDeleteUpdates() {
210  $page = $this->createPage(
211  "WikiPageTest_testDoDeleteArticle",
212  "[[original text]] foo",
214  );
215  $id = $page->getId();
216 
217  // Similar to MovePage logic
218  wfGetDB( DB_MASTER )->delete( 'page', [ 'page_id' => $id ], __METHOD__ );
219  $page->doDeleteUpdates( $id );
220 
221  // Run the job queue
223  $jobs = new RunJobs;
224  $jobs->loadParamsAndArgs( null, [ 'quiet' => true ], null );
225  $jobs->execute();
226 
227  # ------------------------
228  $dbr = wfGetDB( DB_SLAVE );
229  $res = $dbr->select( 'pagelinks', '*', [ 'pl_from' => $id ] );
230  $n = $res->numRows();
231  $res->free();
232 
233  $this->assertEquals( 0, $n, 'pagelinks should contain no more links from the page' );
234  }
235 
239  public function testGetRevision() {
240  $page = $this->newPage( "WikiPageTest_testGetRevision" );
241 
242  $rev = $page->getRevision();
243  $this->assertNull( $rev );
244 
245  # -----------------
246  $this->createPage( $page, "some text", CONTENT_MODEL_WIKITEXT );
247 
248  $rev = $page->getRevision();
249 
250  $this->assertEquals( $page->getLatest(), $rev->getId() );
251  $this->assertEquals( "some text", $rev->getContent()->getNativeData() );
252  }
253 
257  public function testGetContent() {
258  $page = $this->newPage( "WikiPageTest_testGetContent" );
259 
260  $content = $page->getContent();
261  $this->assertNull( $content );
262 
263  # -----------------
264  $this->createPage( $page, "some text", CONTENT_MODEL_WIKITEXT );
265 
266  $content = $page->getContent();
267  $this->assertEquals( "some text", $content->getNativeData() );
268  }
269 
273  public function testGetContentModel() {
274  global $wgContentHandlerUseDB;
275 
276  if ( !$wgContentHandlerUseDB ) {
277  $this->markTestSkipped( '$wgContentHandlerUseDB is disabled' );
278  }
279 
280  $page = $this->createPage(
281  "WikiPageTest_testGetContentModel",
282  "some text",
284  );
285 
286  $page = new WikiPage( $page->getTitle() );
287  $this->assertEquals( CONTENT_MODEL_JAVASCRIPT, $page->getContentModel() );
288  }
289 
293  public function testGetContentHandler() {
294  global $wgContentHandlerUseDB;
295 
296  if ( !$wgContentHandlerUseDB ) {
297  $this->markTestSkipped( '$wgContentHandlerUseDB is disabled' );
298  }
299 
300  $page = $this->createPage(
301  "WikiPageTest_testGetContentHandler",
302  "some text",
304  );
305 
306  $page = new WikiPage( $page->getTitle() );
307  $this->assertEquals( 'JavaScriptContentHandler', get_class( $page->getContentHandler() ) );
308  }
309 
313  public function testExists() {
314  $page = $this->newPage( "WikiPageTest_testExists" );
315  $this->assertFalse( $page->exists() );
316 
317  # -----------------
318  $this->createPage( $page, "some text", CONTENT_MODEL_WIKITEXT );
319  $this->assertTrue( $page->exists() );
320 
321  $page = new WikiPage( $page->getTitle() );
322  $this->assertTrue( $page->exists() );
323 
324  # -----------------
325  $page->doDeleteArticle( "done testing" );
326  $this->assertFalse( $page->exists() );
327 
328  $page = new WikiPage( $page->getTitle() );
329  $this->assertFalse( $page->exists() );
330  }
331 
332  public static function provideHasViewableContent() {
333  return [
334  [ 'WikiPageTest_testHasViewableContent', false, true ],
335  [ 'Special:WikiPageTest_testHasViewableContent', false ],
336  [ 'MediaWiki:WikiPageTest_testHasViewableContent', false ],
337  [ 'Special:Userlogin', true ],
338  [ 'MediaWiki:help', true ],
339  ];
340  }
341 
346  public function testHasViewableContent( $title, $viewable, $create = false ) {
347  $page = $this->newPage( $title );
348  $this->assertEquals( $viewable, $page->hasViewableContent() );
349 
350  if ( $create ) {
351  $this->createPage( $page, "some text", CONTENT_MODEL_WIKITEXT );
352  $this->assertTrue( $page->hasViewableContent() );
353 
354  $page = new WikiPage( $page->getTitle() );
355  $this->assertTrue( $page->hasViewableContent() );
356  }
357  }
358 
359  public static function provideGetRedirectTarget() {
360  return [
361  [ 'WikiPageTest_testGetRedirectTarget_1', CONTENT_MODEL_WIKITEXT, "hello world", null ],
362  [
363  'WikiPageTest_testGetRedirectTarget_2',
365  "#REDIRECT [[hello world]]",
366  "Hello world"
367  ],
368  ];
369  }
370 
375  public function testGetRedirectTarget( $title, $model, $text, $target ) {
376  $this->setMwGlobals( [
377  'wgCapitalLinks' => true,
378  ] );
379 
380  $page = $this->createPage( $title, $text, $model );
381 
382  # sanity check, because this test seems to fail for no reason for some people.
383  $c = $page->getContent();
384  $this->assertEquals( 'WikitextContent', get_class( $c ) );
385 
386  # now, test the actual redirect
387  $t = $page->getRedirectTarget();
388  $this->assertEquals( $target, is_null( $t ) ? null : $t->getPrefixedText() );
389  }
390 
395  public function testIsRedirect( $title, $model, $text, $target ) {
396  $page = $this->createPage( $title, $text, $model );
397  $this->assertEquals( !is_null( $target ), $page->isRedirect() );
398  }
399 
400  public static function provideIsCountable() {
401  return [
402 
403  // any
404  [ 'WikiPageTest_testIsCountable',
406  '',
407  'any',
408  true
409  ],
410  [ 'WikiPageTest_testIsCountable',
412  'Foo',
413  'any',
414  true
415  ],
416 
417  // comma
418  [ 'WikiPageTest_testIsCountable',
420  'Foo',
421  'comma',
422  false
423  ],
424  [ 'WikiPageTest_testIsCountable',
426  'Foo, bar',
427  'comma',
428  true
429  ],
430 
431  // link
432  [ 'WikiPageTest_testIsCountable',
434  'Foo',
435  'link',
436  false
437  ],
438  [ 'WikiPageTest_testIsCountable',
440  'Foo [[bar]]',
441  'link',
442  true
443  ],
444 
445  // redirects
446  [ 'WikiPageTest_testIsCountable',
448  '#REDIRECT [[bar]]',
449  'any',
450  false
451  ],
452  [ 'WikiPageTest_testIsCountable',
454  '#REDIRECT [[bar]]',
455  'comma',
456  false
457  ],
458  [ 'WikiPageTest_testIsCountable',
460  '#REDIRECT [[bar]]',
461  'link',
462  false
463  ],
464 
465  // not a content namespace
466  [ 'Talk:WikiPageTest_testIsCountable',
468  'Foo',
469  'any',
470  false
471  ],
472  [ 'Talk:WikiPageTest_testIsCountable',
474  'Foo, bar',
475  'comma',
476  false
477  ],
478  [ 'Talk:WikiPageTest_testIsCountable',
480  'Foo [[bar]]',
481  'link',
482  false
483  ],
484 
485  // not a content namespace, different model
486  [ 'MediaWiki:WikiPageTest_testIsCountable.js',
487  null,
488  'Foo',
489  'any',
490  false
491  ],
492  [ 'MediaWiki:WikiPageTest_testIsCountable.js',
493  null,
494  'Foo, bar',
495  'comma',
496  false
497  ],
498  [ 'MediaWiki:WikiPageTest_testIsCountable.js',
499  null,
500  'Foo [[bar]]',
501  'link',
502  false
503  ],
504  ];
505  }
506 
511  public function testIsCountable( $title, $model, $text, $mode, $expected ) {
512  global $wgContentHandlerUseDB;
513 
514  $this->setMwGlobals( 'wgArticleCountMethod', $mode );
515 
517 
518  if ( !$wgContentHandlerUseDB
519  && $model
521  ) {
522  $this->markTestSkipped( "Can not use non-default content model $model for "
523  . $title->getPrefixedDBkey() . " with \$wgContentHandlerUseDB disabled." );
524  }
525 
526  $page = $this->createPage( $title, $text, $model );
527 
528  $editInfo = $page->prepareContentForEdit( $page->getContent() );
529 
530  $v = $page->isCountable();
531  $w = $page->isCountable( $editInfo );
532 
533  $this->assertEquals(
534  $expected,
535  $v,
536  "isCountable( null ) returned unexpected value " . var_export( $v, true )
537  . " instead of " . var_export( $expected, true )
538  . " in mode `$mode` for text \"$text\""
539  );
540 
541  $this->assertEquals(
542  $expected,
543  $w,
544  "isCountable( \$editInfo ) returned unexpected value " . var_export( $v, true )
545  . " instead of " . var_export( $expected, true )
546  . " in mode `$mode` for text \"$text\""
547  );
548  }
549 
550  public static function provideGetParserOutput() {
551  return [
552  [ CONTENT_MODEL_WIKITEXT, "hello ''world''\n", "<p>hello <i>world</i></p>" ],
553  // @todo more...?
554  ];
555  }
556 
561  public function testGetParserOutput( $model, $text, $expectedHtml ) {
562  $page = $this->createPage( 'WikiPageTest_testGetParserOutput', $text, $model );
563 
564  $opt = $page->makeParserOptions( 'canonical' );
565  $po = $page->getParserOutput( $opt );
566  $text = $po->getText();
567 
568  $text = trim( preg_replace( '/<!--.*?-->/sm', '', $text ) ); # strip injected comments
569  $text = preg_replace( '!\s*(</p>)!sm', '\1', $text ); # don't let tidy confuse us
570 
571  $this->assertEquals( $expectedHtml, $text );
572 
573  return $po;
574  }
575 
579  public function testGetParserOutput_nonexisting() {
580  static $count = 0;
581  $count++;
582 
583  $page = new WikiPage( new Title( "WikiPageTest_testGetParserOutput_nonexisting_$count" ) );
584 
585  $opt = new ParserOptions();
586  $po = $page->getParserOutput( $opt );
587 
588  $this->assertFalse( $po, "getParserOutput() shall return false for non-existing pages." );
589  }
590 
594  public function testGetParserOutput_badrev() {
595  $page = $this->createPage( 'WikiPageTest_testGetParserOutput', "dummy", CONTENT_MODEL_WIKITEXT );
596 
597  $opt = new ParserOptions();
598  $po = $page->getParserOutput( $opt, $page->getLatest() + 1234 );
599 
600  // @todo would be neat to also test deleted revision
601 
602  $this->assertFalse( $po, "getParserOutput() shall return false for non-existing revisions." );
603  }
604 
605  public static $sections =
606 
607  "Intro
608 
609 == stuff ==
610 hello world
611 
612 == test ==
613 just a test
614 
615 == foo ==
616 more stuff
617 ";
618 
619  public function dataReplaceSection() {
620  // NOTE: assume the Help namespace to contain wikitext
621  return [
622  [ 'Help:WikiPageTest_testReplaceSection',
623  CONTENT_MODEL_WIKITEXT,
624  WikiPageTest::$sections,
625  "0",
626  "No more",
627  null,
628  trim( preg_replace( '/^Intro/sm', 'No more', WikiPageTest::$sections ) )
629  ],
630  [ 'Help:WikiPageTest_testReplaceSection',
631  CONTENT_MODEL_WIKITEXT,
632  WikiPageTest::$sections,
633  "",
634  "No more",
635  null,
636  "No more"
637  ],
638  [ 'Help:WikiPageTest_testReplaceSection',
639  CONTENT_MODEL_WIKITEXT,
640  WikiPageTest::$sections,
641  "2",
642  "== TEST ==\nmore fun",
643  null,
644  trim( preg_replace( '/^== test ==.*== foo ==/sm',
645  "== TEST ==\nmore fun\n\n== foo ==",
646  WikiPageTest::$sections ) )
647  ],
648  [ 'Help:WikiPageTest_testReplaceSection',
649  CONTENT_MODEL_WIKITEXT,
650  WikiPageTest::$sections,
651  "8",
652  "No more",
653  null,
654  trim( WikiPageTest::$sections )
655  ],
656  [ 'Help:WikiPageTest_testReplaceSection',
657  CONTENT_MODEL_WIKITEXT,
658  WikiPageTest::$sections,
659  "new",
660  "No more",
661  "New",
662  trim( WikiPageTest::$sections ) . "\n\n== New ==\n\nNo more"
663  ],
664  ];
665  }
666 
671  public function testReplaceSectionContent( $title, $model, $text, $section,
672  $with, $sectionTitle, $expected
673  ) {
674  $page = $this->createPage( $title, $text, $model );
675 
676  $content = ContentHandler::makeContent( $with, $page->getTitle(), $page->getContentModel() );
677  $c = $page->replaceSectionContent( $section, $content, $sectionTitle );
678 
679  $this->assertEquals( $expected, is_null( $c ) ? null : trim( $c->getNativeData() ) );
680  }
681 
686  public function testReplaceSectionAtRev( $title, $model, $text, $section,
687  $with, $sectionTitle, $expected
688  ) {
689  $page = $this->createPage( $title, $text, $model );
690  $baseRevId = $page->getLatest();
691 
692  $content = ContentHandler::makeContent( $with, $page->getTitle(), $page->getContentModel() );
693  $c = $page->replaceSectionAtRev( $section, $content, $sectionTitle, $baseRevId );
694 
695  $this->assertEquals( $expected, is_null( $c ) ? null : trim( $c->getNativeData() ) );
696  }
697 
698  /* @todo FIXME: fix this!
699  public function testGetUndoText() {
700  $this->markTestSkippedIfNoDiff3();
701 
702  $text = "one";
703  $page = $this->createPage( "WikiPageTest_testGetUndoText", $text );
704  $rev1 = $page->getRevision();
705 
706  $text .= "\n\ntwo";
707  $page->doEditContent(
708  ContentHandler::makeContent( $text, $page->getTitle() ),
709  "adding section two"
710  );
711  $rev2 = $page->getRevision();
712 
713  $text .= "\n\nthree";
714  $page->doEditContent(
715  ContentHandler::makeContent( $text, $page->getTitle() ),
716  "adding section three"
717  );
718  $rev3 = $page->getRevision();
719 
720  $text .= "\n\nfour";
721  $page->doEditContent(
722  ContentHandler::makeContent( $text, $page->getTitle() ),
723  "adding section four"
724  );
725  $rev4 = $page->getRevision();
726 
727  $text .= "\n\nfive";
728  $page->doEditContent(
729  ContentHandler::makeContent( $text, $page->getTitle() ),
730  "adding section five"
731  );
732  $rev5 = $page->getRevision();
733 
734  $text .= "\n\nsix";
735  $page->doEditContent(
736  ContentHandler::makeContent( $text, $page->getTitle() ),
737  "adding section six"
738  );
739  $rev6 = $page->getRevision();
740 
741  $undo6 = $page->getUndoText( $rev6 );
742  if ( $undo6 === false ) $this->fail( "getUndoText failed for rev6" );
743  $this->assertEquals( "one\n\ntwo\n\nthree\n\nfour\n\nfive", $undo6 );
744 
745  $undo3 = $page->getUndoText( $rev4, $rev2 );
746  if ( $undo3 === false ) $this->fail( "getUndoText failed for rev4..rev2" );
747  $this->assertEquals( "one\n\ntwo\n\nfive", $undo3 );
748 
749  $undo2 = $page->getUndoText( $rev2 );
750  if ( $undo2 === false ) $this->fail( "getUndoText failed for rev2" );
751  $this->assertEquals( "one\n\nfive", $undo2 );
752  }
753  */
754 
758  public function testGetOldestRevision() {
759  $page = $this->newPage( "WikiPageTest_testGetOldestRevision" );
760  $page->doEditContent(
761  new WikitextContent( 'one' ),
762  "first edit",
763  EDIT_NEW
764  );
765  $rev1 = $page->getRevision();
766 
767  $page = new WikiPage( $page->getTitle() );
768  $page->doEditContent(
769  new WikitextContent( 'two' ),
770  "second edit",
771  EDIT_UPDATE
772  );
773 
774  $page = new WikiPage( $page->getTitle() );
775  $page->doEditContent(
776  new WikitextContent( 'three' ),
777  "third edit",
778  EDIT_UPDATE
779  );
780 
781  // sanity check
782  $this->assertNotEquals(
783  $rev1->getId(),
784  $page->getRevision()->getId(),
785  '$page->getRevision()->getId()'
786  );
787 
788  // actual test
789  $this->assertEquals(
790  $rev1->getId(),
791  $page->getOldestRevision()->getId(),
792  '$page->getOldestRevision()->getId()'
793  );
794  }
795 
800  public function broken_testDoRollback() {
801  $admin = new User();
802  $admin->setName( "Admin" );
803 
804  $text = "one";
805  $page = $this->newPage( "WikiPageTest_testDoRollback" );
806  $page->doEditContent( ContentHandler::makeContent( $text, $page->getTitle() ),
807  "section one", EDIT_NEW, false, $admin );
808 
809  $user1 = new User();
810  $user1->setName( "127.0.1.11" );
811  $text .= "\n\ntwo";
812  $page = new WikiPage( $page->getTitle() );
813  $page->doEditContent( ContentHandler::makeContent( $text, $page->getTitle() ),
814  "adding section two", 0, false, $user1 );
815 
816  $user2 = new User();
817  $user2->setName( "127.0.2.13" );
818  $text .= "\n\nthree";
819  $page = new WikiPage( $page->getTitle() );
820  $page->doEditContent( ContentHandler::makeContent( $text, $page->getTitle() ),
821  "adding section three", 0, false, $user2 );
822 
823  # we are having issues with doRollback spuriously failing. Apparently
824  # the last revision somehow goes missing or not committed under some
825  # circumstances. So, make sure the last revision has the right user name.
826  $dbr = wfGetDB( DB_SLAVE );
827  $this->assertEquals( 3, Revision::countByPageId( $dbr, $page->getId() ) );
828 
829  $page = new WikiPage( $page->getTitle() );
830  $rev3 = $page->getRevision();
831  $this->assertEquals( '127.0.2.13', $rev3->getUserText() );
832 
833  $rev2 = $rev3->getPrevious();
834  $this->assertEquals( '127.0.1.11', $rev2->getUserText() );
835 
836  $rev1 = $rev2->getPrevious();
837  $this->assertEquals( 'Admin', $rev1->getUserText() );
838 
839  # now, try the actual rollback
840  $admin->addToDatabase();
841  $admin->addGroup( "sysop" ); # XXX: make the test user a sysop...
842  $token = $admin->getEditToken(
843  [ $page->getTitle()->getPrefixedText(), $user2->getName() ],
844  null
845  );
846  $errors = $page->doRollback(
847  $user2->getName(),
848  "testing revert",
849  $token,
850  false,
851  $details,
852  $admin
853  );
854 
855  if ( $errors ) {
856  $this->fail( "Rollback failed:\n" . print_r( $errors, true )
857  . ";\n" . print_r( $details, true ) );
858  }
859 
860  $page = new WikiPage( $page->getTitle() );
861  $this->assertEquals( $rev2->getSha1(), $page->getRevision()->getSha1(),
862  "rollback did not revert to the correct revision" );
863  $this->assertEquals( "one\n\ntwo", $page->getContent()->getNativeData() );
864  }
865 
870  public function testDoRollback() {
871  $admin = new User();
872  $admin->setName( "Admin" );
873  $admin->addToDatabase();
874 
875  $text = "one";
876  $page = $this->newPage( "WikiPageTest_testDoRollback" );
877  $page->doEditContent(
878  ContentHandler::makeContent( $text, $page->getTitle(), CONTENT_MODEL_WIKITEXT ),
879  "section one",
880  EDIT_NEW,
881  false,
882  $admin
883  );
884  $rev1 = $page->getRevision();
885 
886  $user1 = new User();
887  $user1->setName( "127.0.1.11" );
888  $text .= "\n\ntwo";
889  $page = new WikiPage( $page->getTitle() );
890  $page->doEditContent(
891  ContentHandler::makeContent( $text, $page->getTitle(), CONTENT_MODEL_WIKITEXT ),
892  "adding section two",
893  0,
894  false,
895  $user1
896  );
897 
898  # now, try the rollback
899  $admin->addGroup( "sysop" ); # XXX: make the test user a sysop...
900  $token = $admin->getEditToken( 'rollback' );
901  $errors = $page->doRollback(
902  $user1->getName(),
903  "testing revert",
904  $token,
905  false,
906  $details,
907  $admin
908  );
909 
910  if ( $errors ) {
911  $this->fail( "Rollback failed:\n" . print_r( $errors, true )
912  . ";\n" . print_r( $details, true ) );
913  }
914 
915  $page = new WikiPage( $page->getTitle() );
916  $this->assertEquals( $rev1->getSha1(), $page->getRevision()->getSha1(),
917  "rollback did not revert to the correct revision" );
918  $this->assertEquals( "one", $page->getContent()->getNativeData() );
919  }
920 
924  public function testDoRollbackFailureSameContent() {
925  $admin = new User();
926  $admin->setName( "Admin" );
927  $admin->addToDatabase();
928  $admin->addGroup( "sysop" ); # XXX: make the test user a sysop...
929 
930  $text = "one";
931  $page = $this->newPage( "WikiPageTest_testDoRollback" );
932  $page->doEditContent(
933  ContentHandler::makeContent( $text, $page->getTitle(), CONTENT_MODEL_WIKITEXT ),
934  "section one",
935  EDIT_NEW,
936  false,
937  $admin
938  );
939  $rev1 = $page->getRevision();
940 
941  $user1 = new User();
942  $user1->setName( "127.0.1.11" );
943  $user1->addToDatabase();
944  $user1->addGroup( "sysop" ); # XXX: make the test user a sysop...
945  $text .= "\n\ntwo";
946  $page = new WikiPage( $page->getTitle() );
947  $page->doEditContent(
948  ContentHandler::makeContent( $text, $page->getTitle(), CONTENT_MODEL_WIKITEXT ),
949  "adding section two",
950  0,
951  false,
952  $user1
953  );
954 
955  # now, do a the rollback from the same user was doing the edit before
956  $resultDetails = [];
957  $token = $user1->getEditToken( 'rollback' );
958  $errors = $page->doRollback(
959  $user1->getName(),
960  "testing revert same user",
961  $token,
962  false,
963  $resultDetails,
964  $admin
965  );
966 
967  $this->assertEquals( [], $errors, "Rollback failed same user" );
968 
969  # now, try the rollback
970  $resultDetails = [];
971  $token = $admin->getEditToken( 'rollback' );
972  $errors = $page->doRollback(
973  $user1->getName(),
974  "testing revert",
975  $token,
976  false,
977  $resultDetails,
978  $admin
979  );
980 
981  $this->assertEquals( [ [ 'alreadyrolled', 'WikiPageTest testDoRollback',
982  '127.0.1.11', 'Admin' ] ], $errors, "Rollback not failed" );
983 
984  $page = new WikiPage( $page->getTitle() );
985  $this->assertEquals( $rev1->getSha1(), $page->getRevision()->getSha1(),
986  "rollback did not revert to the correct revision" );
987  $this->assertEquals( "one", $page->getContent()->getNativeData() );
988  }
989 
990  public static function provideGetAutoDeleteReason() {
991  return [
992  [
993  [],
994  false,
995  false
996  ],
997 
998  [
999  [
1000  [ "first edit", null ],
1001  ],
1002  "/first edit.*only contributor/",
1003  false
1004  ],
1005 
1006  [
1007  [
1008  [ "first edit", null ],
1009  [ "second edit", null ],
1010  ],
1011  "/second edit.*only contributor/",
1012  true
1013  ],
1014 
1015  [
1016  [
1017  [ "first edit", "127.0.2.22" ],
1018  [ "second edit", "127.0.3.33" ],
1019  ],
1020  "/second edit/",
1021  true
1022  ],
1023 
1024  [
1025  [
1026  [
1027  "first edit: "
1028  . "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam "
1029  . " nonumy eirmod tempor invidunt ut labore et dolore magna "
1030  . "aliquyam erat, sed diam voluptua. At vero eos et accusam "
1031  . "et justo duo dolores et ea rebum. Stet clita kasd gubergren, "
1032  . "no sea takimata sanctus est Lorem ipsum dolor sit amet.'",
1033  null
1034  ],
1035  ],
1036  '/first edit:.*\.\.\."/',
1037  false
1038  ],
1039 
1040  [
1041  [
1042  [ "first edit", "127.0.2.22" ],
1043  [ "", "127.0.3.33" ],
1044  ],
1045  "/before blanking.*first edit/",
1046  true
1047  ],
1048 
1049  ];
1050  }
1051 
1056  public function testGetAutoDeleteReason( $edits, $expectedResult, $expectedHistory ) {
1057  global $wgUser;
1058 
1059  // NOTE: assume Help namespace to contain wikitext
1060  $page = $this->newPage( "Help:WikiPageTest_testGetAutoDeleteReason" );
1061 
1062  $c = 1;
1063 
1064  foreach ( $edits as $edit ) {
1065  $user = new User();
1066 
1067  if ( !empty( $edit[1] ) ) {
1068  $user->setName( $edit[1] );
1069  } else {
1070  $user = $wgUser;
1071  }
1072 
1073  $content = ContentHandler::makeContent( $edit[0], $page->getTitle(), $page->getContentModel() );
1074 
1075  $page->doEditContent( $content, "test edit $c", $c < 2 ? EDIT_NEW : 0, false, $user );
1076 
1077  $c += 1;
1078  }
1079 
1080  $reason = $page->getAutoDeleteReason( $hasHistory );
1081 
1082  if ( is_bool( $expectedResult ) || is_null( $expectedResult ) ) {
1083  $this->assertEquals( $expectedResult, $reason );
1084  } else {
1085  $this->assertTrue( (bool)preg_match( $expectedResult, $reason ),
1086  "Autosummary didn't match expected pattern $expectedResult: $reason" );
1087  }
1088 
1089  $this->assertEquals( $expectedHistory, $hasHistory,
1090  "expected \$hasHistory to be " . var_export( $expectedHistory, true ) );
1091 
1092  $page->doDeleteArticle( "done" );
1093  }
1094 
1095  public static function providePreSaveTransform() {
1096  return [
1097  [ 'hello this is ~~~',
1098  "hello this is [[Special:Contributions/127.0.0.1|127.0.0.1]]",
1099  ],
1100  [ 'hello \'\'this\'\' is <nowiki>~~~</nowiki>',
1101  'hello \'\'this\'\' is <nowiki>~~~</nowiki>',
1102  ],
1103  ];
1104  }
1105 
1109  public function testWikiPageFactory() {
1110  $title = Title::makeTitle( NS_FILE, 'Someimage.png' );
1111  $page = WikiPage::factory( $title );
1112  $this->assertEquals( 'WikiFilePage', get_class( $page ) );
1113 
1114  $title = Title::makeTitle( NS_CATEGORY, 'SomeCategory' );
1115  $page = WikiPage::factory( $title );
1116  $this->assertEquals( 'WikiCategoryPage', get_class( $page ) );
1117 
1118  $title = Title::makeTitle( NS_MAIN, 'SomePage' );
1119  $page = WikiPage::factory( $title );
1120  $this->assertEquals( 'WikiPage', get_class( $page ) );
1121  }
1122 }
WikiPageTest\testGetContent
testGetContent()
WikiPage::getContent.
Definition: WikiPageTest.php:257
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:265
WikiPageTest\testHasViewableContent
testHasViewableContent( $title, $viewable, $create=false)
provideHasViewableContent WikiPage::hasViewableContent
Definition: WikiPageTest.php:346
false
processing should stop and the error should be shown to the user * false
Definition: hooks.txt:189
WikiPageTest\testGetParserOutput
testGetParserOutput( $model, $text, $expectedHtml)
provideGetParserOutput WikiPage::getParserOutput
Definition: WikiPageTest.php:561
WikiPageTest\testGetContentHandler
testGetContentHandler()
WikiPage::getContentHandler.
Definition: WikiPageTest.php:293
WikiPageTest\tearDown
tearDown()
Definition: WikiPageTest.php:42
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:82
WikiPageTest\testGetRedirectTarget
testGetRedirectTarget( $title, $model, $text, $target)
provideGetRedirectTarget WikiPage::getRedirectTarget
Definition: WikiPageTest.php:375
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:273
WikiPage
Class representing a MediaWiki article and history.
Definition: WikiPage.php:36
WikiPageTest\setUp
setUp()
Definition: WikiPageTest.php:35
DB_SLAVE
const DB_SLAVE
Definition: Defines.php:34
WikiPageTest\testIsRedirect
testIsRedirect( $title, $model, $text, $target)
provideGetRedirectTarget WikiPage::isRedirect
Definition: WikiPageTest.php:395
WikiPageTest\testDoRollback
testDoRollback()
Definition: WikiPageTest.php:870
$res
$res
Definition: database.txt:21
JobQueueGroup\destroySingletons
static destroySingletons()
Destroy the singleton instances.
Definition: JobQueueGroup.php:85
$name
Allows to change the fields on the form that will be generated $name
Definition: hooks.txt:304
CONTENT_MODEL_WIKITEXT
const CONTENT_MODEL_WIKITEXT
Definition: Defines.php:233
WikiPageTest\testDoDeleteUpdates
testDoDeleteUpdates()
WikiPage::doDeleteUpdates.
Definition: WikiPageTest.php:209
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:161
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:1741
$title
namespace and then decline to actually register it file or subcat img or subcat $title
Definition: hooks.txt:934
$content
this hook is for auditing only RecentChangesLinked and Watchlist RecentChangesLinked and Watchlist Do not use this to implement individual filters if they are compatible with the ChangesListFilter and ChangesListFilterGroup structure use sub classes of those in conjunction with the ChangesListSpecialPageStructuredFilters hook This hook can be used to implement filters that do not implement that or custom behavior that is not an individual filter e g Watchlist and Watchlist you will want to construct new ChangesListBooleanFilter or ChangesListStringOptionsFilter objects When constructing you specify which group they belong to You can reuse existing or create your you must register them with $special registerFilterGroup removed from all revisions and log entries to which it was applied This gives extensions a chance to take it off their books as the deletion has already been partly carried out by this point or something similar the user will be unable to create the tag set and then return false from the hook function Ensure you consume the ChangeTagAfterDelete hook to carry out custom deletion actions as context called by AbstractContent::getParserOutput May be used to override the normal model specific rendering of page content $content
Definition: hooks.txt:1049
wfGetDB
wfGetDB( $db, $groups=[], $wiki=false)
Get a Database object.
Definition: GlobalFunctions.php:3060
MediaWikiTestCase\setMwGlobals
setMwGlobals( $pairs, $value=null)
Definition: MediaWikiTestCase.php:658
$page
do that in ParserLimitReportFormat instead use this to modify the parameters of the image and a DIV can begin in one section and end in another Make sure your code can handle that case gracefully See the EditSectionClearerLink extension for an example zero but section is usually empty its values are the globals values before the output is cached $page
Definition: hooks.txt:2536
WikiPageTest\testExists
testExists()
WikiPage::exists.
Definition: WikiPageTest.php:313
WikiPageTest\provideIsCountable
static provideIsCountable()
Definition: WikiPageTest.php:400
MediaWikiTestCase\getDefaultWikitextNS
getDefaultWikitextNS()
Returns the ID of a namespace that defaults to Wikitext.
Definition: MediaWikiTestCase.php:1639
WikiPageTest\provideGetRedirectTarget
static provideGetRedirectTarget()
Definition: WikiPageTest.php:359
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_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:511
WikiPageTest\newPage
newPage( $title, $model=null)
Definition: WikiPageTest.php:62
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:332
Special
wiki Special
Definition: All_system_messages.txt:2667
EDIT_NEW
const EDIT_NEW
Definition: Defines.php:150
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:550
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:1741
WikiPageTest\testDoEditContent
testDoEditContent()
WikiPage::doEditContent WikiPage::doModify WikiPage::doCreate WikiPage::doEditUpdates.
Definition: WikiPageTest.php:99
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:1956
CONTENT_MODEL_JAVASCRIPT
const CONTENT_MODEL_JAVASCRIPT
Definition: Defines.php:234
$t
$t
Definition: testCompression.php:67
WikiPageTest\testGetRevision
testGetRevision()
WikiPage::getRevision.
Definition: WikiPageTest.php:239
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.