EditPage: Migrate Title::userCan() calls to PermissionManager
[lhc/web/wiklou.git] / tests / phpunit / includes / EditPageTest.php
1 <?php
2
3 use MediaWiki\MediaWikiServices;
4
5 /**
6 * @group Editing
7 *
8 * @group Database
9 * ^--- tell jenkins this test needs the database
10 *
11 * @group medium
12 * ^--- tell phpunit that these test cases may take longer than 2 seconds.
13 */
14 class EditPageTest extends MediaWikiLangTestCase {
15
16 protected function setUp() {
17 parent::setUp();
18
19 $contLang = MediaWikiServices::getInstance()->getContentLanguage();
20 $this->setContentLang( $contLang );
21
22 $this->setMwGlobals( [
23 'wgExtraNamespaces' => [
24 12312 => 'Dummy',
25 12313 => 'Dummy_talk',
26 ],
27 'wgNamespaceContentModels' => [ 12312 => 'testing' ],
28 ] );
29 $this->mergeMwGlobalArrayValue(
30 'wgContentHandlers',
31 [ 'testing' => 'DummyContentHandlerForTesting' ]
32 );
33 }
34
35 /**
36 * @dataProvider provideExtractSectionTitle
37 * @covers EditPage::extractSectionTitle
38 */
39 public function testExtractSectionTitle( $section, $title ) {
40 $extracted = EditPage::extractSectionTitle( $section );
41 $this->assertEquals( $title, $extracted );
42 }
43
44 public static function provideExtractSectionTitle() {
45 return [
46 [
47 "== Test ==\n\nJust a test section.",
48 "Test"
49 ],
50 [
51 "An initial section, no header.",
52 false
53 ],
54 [
55 "An initial section with a fake heder (T34617)\n\n== Test == ??\nwtf",
56 false
57 ],
58 [
59 "== Section ==\nfollowed by a fake == Non-section == ??\nnoooo",
60 "Section"
61 ],
62 [
63 "== Section== \t\r\n followed by whitespace (T37051)",
64 'Section',
65 ],
66 ];
67 }
68
69 protected function forceRevisionDate( WikiPage $page, $timestamp ) {
70 $dbw = wfGetDB( DB_MASTER );
71
72 $dbw->update( 'revision',
73 [ 'rev_timestamp' => $dbw->timestamp( $timestamp ) ],
74 [ 'rev_id' => $page->getLatest() ] );
75
76 $page->clear();
77 }
78
79 /**
80 * User input text is passed to rtrim() by edit page. This is a simple
81 * wrapper around assertEquals() which calls rrtrim() to normalize the
82 * expected and actual texts.
83 * @param string $expected
84 * @param string $actual
85 * @param string $msg
86 */
87 protected function assertEditedTextEquals( $expected, $actual, $msg = '' ) {
88 $this->assertEquals( rtrim( $expected ), rtrim( $actual ), $msg );
89 }
90
91 /**
92 * Performs an edit and checks the result.
93 *
94 * @param string|Title $title The title of the page to edit
95 * @param string|null $baseText Some text to create the page with before attempting the edit.
96 * @param User|string|null $user The user to perform the edit as.
97 * @param array $edit An array of request parameters used to define the edit to perform.
98 * Some well known fields are:
99 * * wpTextbox1: the text to submit
100 * * wpSummary: the edit summary
101 * * wpEditToken: the edit token (will be inserted if not provided)
102 * * wpEdittime: timestamp of the edit's base revision (will be inserted
103 * if not provided)
104 * * wpStarttime: timestamp when the edit started (will be inserted if not provided)
105 * * wpSectionTitle: the section to edit
106 * * wpMinorEdit: mark as minor edit
107 * * wpWatchthis: whether to watch the page
108 * @param int|null $expectedCode The expected result code (EditPage::AS_XXX constants).
109 * Set to null to skip the check.
110 * @param string|null $expectedText The text expected to be on the page after the edit.
111 * Set to null to skip the check.
112 * @param string|null $message An optional message to show along with any error message.
113 *
114 * @return WikiPage The page that was just edited, useful for getting the edit's rev_id, etc.
115 */
116 protected function assertEdit( $title, $baseText, $user = null, array $edit,
117 $expectedCode = null, $expectedText = null, $message = null
118 ) {
119 if ( is_string( $title ) ) {
120 $ns = $this->getDefaultWikitextNS();
121 $title = Title::newFromText( $title, $ns );
122 }
123 $this->assertNotNull( $title );
124
125 if ( is_string( $user ) ) {
126 $user = User::newFromName( $user );
127
128 if ( $user->getId() === 0 ) {
129 $user->addToDatabase();
130 }
131 }
132
133 $page = WikiPage::factory( $title );
134
135 if ( $baseText !== null ) {
136 $content = ContentHandler::makeContent( $baseText, $title );
137 $page->doEditContent( $content, "base text for test" );
138 $this->forceRevisionDate( $page, '20120101000000' );
139
140 // sanity check
141 $page->clear();
142 $currentText = ContentHandler::getContentText( $page->getContent() );
143
144 # EditPage rtrim() the user input, so we alter our expected text
145 # to reflect that.
146 $this->assertEditedTextEquals( $baseText, $currentText );
147 }
148
149 if ( $user == null ) {
150 $user = $GLOBALS['wgUser'];
151 } else {
152 $this->setMwGlobals( 'wgUser', $user );
153 }
154
155 if ( !isset( $edit['wpEditToken'] ) ) {
156 $edit['wpEditToken'] = $user->getEditToken();
157 }
158
159 if ( !isset( $edit['wpEdittime'] ) ) {
160 $edit['wpEdittime'] = $page->exists() ? $page->getTimestamp() : '';
161 }
162
163 if ( !isset( $edit['wpStarttime'] ) ) {
164 $edit['wpStarttime'] = wfTimestampNow();
165 }
166
167 if ( !isset( $edit['wpUnicodeCheck'] ) ) {
168 $edit['wpUnicodeCheck'] = EditPage::UNICODE_CHECK;
169 }
170
171 $req = new FauxRequest( $edit, true ); // session ??
172
173 $article = new Article( $title );
174 $article->getContext()->setTitle( $title );
175 $ep = new EditPage( $article );
176 $ep->setContextTitle( $title );
177 $ep->importFormData( $req );
178
179 $bot = isset( $edit['bot'] ) ? (bool)$edit['bot'] : false;
180
181 // this is where the edit happens!
182 // Note: don't want to use EditPage::AttemptSave, because it messes with $wgOut
183 // and throws exceptions like PermissionsError
184 $status = $ep->internalAttemptSave( $result, $bot );
185
186 if ( $expectedCode !== null ) {
187 // check edit code
188 $this->assertEquals( $expectedCode, $status->value,
189 "Expected result code mismatch. $message" );
190 }
191
192 $page = WikiPage::factory( $title );
193
194 if ( $expectedText !== null ) {
195 // check resulting page text
196 $content = $page->getContent();
197 $text = ContentHandler::getContentText( $content );
198
199 # EditPage rtrim() the user input, so we alter our expected text
200 # to reflect that.
201 $this->assertEditedTextEquals( $expectedText, $text,
202 "Expected article text mismatch. $message" );
203 }
204
205 return $page;
206 }
207
208 public static function provideCreatePages() {
209 return [
210 [ 'expected article being created',
211 'EditPageTest_testCreatePage',
212 null,
213 'Hello World!',
214 EditPage::AS_SUCCESS_NEW_ARTICLE,
215 'Hello World!'
216 ],
217 [ 'expected article not being created if empty',
218 'EditPageTest_testCreatePage',
219 null,
220 '',
221 EditPage::AS_BLANK_ARTICLE,
222 null
223 ],
224 [ 'expected MediaWiki: page being created',
225 'MediaWiki:January',
226 'UTSysop',
227 'Not January',
228 EditPage::AS_SUCCESS_NEW_ARTICLE,
229 'Not January'
230 ],
231 [ 'expected not-registered MediaWiki: page not being created if empty',
232 'MediaWiki:EditPageTest_testCreatePage',
233 'UTSysop',
234 '',
235 EditPage::AS_BLANK_ARTICLE,
236 null
237 ],
238 [ 'expected registered MediaWiki: page being created even if empty',
239 'MediaWiki:January',
240 'UTSysop',
241 '',
242 EditPage::AS_SUCCESS_NEW_ARTICLE,
243 ''
244 ],
245 [ 'expected registered MediaWiki: page whose default content is empty'
246 . ' not being created if empty',
247 'MediaWiki:Ipb-default-expiry',
248 'UTSysop',
249 '',
250 EditPage::AS_BLANK_ARTICLE,
251 ''
252 ],
253 [ 'expected MediaWiki: page not being created if text equals default message',
254 'MediaWiki:January',
255 'UTSysop',
256 'January',
257 EditPage::AS_BLANK_ARTICLE,
258 null
259 ],
260 [ 'expected empty article being created',
261 'EditPageTest_testCreatePage',
262 null,
263 '',
264 EditPage::AS_SUCCESS_NEW_ARTICLE,
265 '',
266 true
267 ],
268 ];
269 }
270
271 /**
272 * @dataProvider provideCreatePages
273 * @covers EditPage
274 */
275 public function testCreatePage(
276 $desc, $pageTitle, $user, $editText, $expectedCode, $expectedText, $ignoreBlank = false
277 ) {
278 $checkId = null;
279
280 $this->setMwGlobals( 'wgHooks', [
281 'PageContentInsertComplete' => [ function (
282 WikiPage &$page, User &$user, Content $content,
283 $summary, $minor, $u1, $u2, &$flags, Revision $revision
284 ) {
285 // types/refs checked
286 } ],
287 'PageContentSaveComplete' => [ function (
288 WikiPage &$page, User &$user, Content $content,
289 $summary, $minor, $u1, $u2, &$flags, Revision $revision,
290 Status &$status, $baseRevId
291 ) use ( &$checkId ) {
292 $checkId = $status->value['revision']->getId();
293 // types/refs checked
294 } ],
295 ] );
296
297 $edit = [ 'wpTextbox1' => $editText ];
298 if ( $ignoreBlank ) {
299 $edit['wpIgnoreBlankArticle'] = 1;
300 }
301
302 $page = $this->assertEdit( $pageTitle, null, $user, $edit, $expectedCode, $expectedText, $desc );
303
304 if ( $expectedCode != EditPage::AS_BLANK_ARTICLE ) {
305 $latest = $page->getLatest();
306 $page->doDeleteArticleReal( $pageTitle );
307
308 $this->assertGreaterThan( 0, $latest, "Page revision ID updated in object" );
309 $this->assertEquals( $latest, $checkId, "Revision in Status for hook" );
310 }
311 }
312
313 /**
314 * @dataProvider provideCreatePages
315 * @covers EditPage
316 */
317 public function testCreatePageTrx(
318 $desc, $pageTitle, $user, $editText, $expectedCode, $expectedText, $ignoreBlank = false
319 ) {
320 $checkIds = [];
321 $this->setMwGlobals( 'wgHooks', [
322 'PageContentInsertComplete' => [ function (
323 WikiPage &$page, User &$user, Content $content,
324 $summary, $minor, $u1, $u2, &$flags, Revision $revision
325 ) {
326 // types/refs checked
327 } ],
328 'PageContentSaveComplete' => [ function (
329 WikiPage &$page, User &$user, Content $content,
330 $summary, $minor, $u1, $u2, &$flags, Revision $revision,
331 Status &$status, $baseRevId
332 ) use ( &$checkIds ) {
333 $checkIds[] = $status->value['revision']->getId();
334 // types/refs checked
335 } ],
336 ] );
337
338 wfGetDB( DB_MASTER )->begin( __METHOD__ );
339
340 $edit = [ 'wpTextbox1' => $editText ];
341 if ( $ignoreBlank ) {
342 $edit['wpIgnoreBlankArticle'] = 1;
343 }
344
345 $page = $this->assertEdit(
346 $pageTitle, null, $user, $edit, $expectedCode, $expectedText, $desc );
347
348 $pageTitle2 = (string)$pageTitle . '/x';
349 $page2 = $this->assertEdit(
350 $pageTitle2, null, $user, $edit, $expectedCode, $expectedText, $desc );
351
352 wfGetDB( DB_MASTER )->commit( __METHOD__ );
353
354 $this->assertEquals( 0, DeferredUpdates::pendingUpdatesCount(), 'No deferred updates' );
355
356 if ( $expectedCode != EditPage::AS_BLANK_ARTICLE ) {
357 $latest = $page->getLatest();
358 $page->doDeleteArticleReal( $pageTitle );
359
360 $this->assertGreaterThan( 0, $latest, "Page #1 revision ID updated in object" );
361 $this->assertEquals( $latest, $checkIds[0], "Revision #1 in Status for hook" );
362
363 $latest2 = $page2->getLatest();
364 $page2->doDeleteArticleReal( $pageTitle2 );
365
366 $this->assertGreaterThan( 0, $latest2, "Page #2 revision ID updated in object" );
367 $this->assertEquals( $latest2, $checkIds[1], "Revision #2 in Status for hook" );
368 }
369 }
370
371 /**
372 * @covers EditPage
373 */
374 public function testUpdatePage() {
375 $checkIds = [];
376
377 $this->setMwGlobals( 'wgHooks', [
378 'PageContentInsertComplete' => [ function (
379 WikiPage &$page, User &$user, Content $content,
380 $summary, $minor, $u1, $u2, &$flags, Revision $revision
381 ) {
382 // types/refs checked
383 } ],
384 'PageContentSaveComplete' => [ function (
385 WikiPage &$page, User &$user, Content $content,
386 $summary, $minor, $u1, $u2, &$flags, Revision $revision,
387 Status &$status, $baseRevId
388 ) use ( &$checkIds ) {
389 $checkIds[] = $status->value['revision']->getId();
390 // types/refs checked
391 } ],
392 ] );
393
394 $text = "one";
395 $edit = [
396 'wpTextbox1' => $text,
397 'wpSummary' => 'first update',
398 ];
399
400 $page = $this->assertEdit( 'EditPageTest_testUpdatePage', "zero", null, $edit,
401 EditPage::AS_SUCCESS_UPDATE, $text,
402 "expected successful update with given text" );
403 $this->assertGreaterThan( 0, $checkIds[0], "First event rev ID set" );
404
405 $this->forceRevisionDate( $page, '20120101000000' );
406
407 $text = "two";
408 $edit = [
409 'wpTextbox1' => $text,
410 'wpSummary' => 'second update',
411 ];
412
413 $this->assertEdit( 'EditPageTest_testUpdatePage', null, null, $edit,
414 EditPage::AS_SUCCESS_UPDATE, $text,
415 "expected successful update with given text" );
416 $this->assertGreaterThan( 0, $checkIds[1], "Second edit hook rev ID set" );
417 $this->assertGreaterThan( $checkIds[0], $checkIds[1], "Second event rev ID is higher" );
418 }
419
420 /**
421 * @covers EditPage
422 */
423 public function testUpdatePageTrx() {
424 $text = "one";
425 $edit = [
426 'wpTextbox1' => $text,
427 'wpSummary' => 'first update',
428 ];
429
430 $page = $this->assertEdit( 'EditPageTest_testTrxUpdatePage', "zero", null, $edit,
431 EditPage::AS_SUCCESS_UPDATE, $text,
432 "expected successful update with given text" );
433
434 $this->forceRevisionDate( $page, '20120101000000' );
435
436 $checkIds = [];
437 $this->setMwGlobals( 'wgHooks', [
438 'PageContentSaveComplete' => [ function (
439 WikiPage &$page, User &$user, Content $content,
440 $summary, $minor, $u1, $u2, &$flags, Revision $revision,
441 Status &$status, $baseRevId
442 ) use ( &$checkIds ) {
443 $checkIds[] = $status->value['revision']->getId();
444 // types/refs checked
445 } ],
446 ] );
447
448 wfGetDB( DB_MASTER )->begin( __METHOD__ );
449
450 $text = "two";
451 $edit = [
452 'wpTextbox1' => $text,
453 'wpSummary' => 'second update',
454 ];
455
456 $this->assertEdit( 'EditPageTest_testTrxUpdatePage', null, null, $edit,
457 EditPage::AS_SUCCESS_UPDATE, $text,
458 "expected successful update with given text" );
459
460 $text = "three";
461 $edit = [
462 'wpTextbox1' => $text,
463 'wpSummary' => 'third update',
464 ];
465
466 $this->assertEdit( 'EditPageTest_testTrxUpdatePage', null, null, $edit,
467 EditPage::AS_SUCCESS_UPDATE, $text,
468 "expected successful update with given text" );
469
470 wfGetDB( DB_MASTER )->commit( __METHOD__ );
471
472 $this->assertGreaterThan( 0, $checkIds[0], "First event rev ID set" );
473 $this->assertGreaterThan( 0, $checkIds[1], "Second edit hook rev ID set" );
474 $this->assertGreaterThan( $checkIds[0], $checkIds[1], "Second event rev ID is higher" );
475 }
476
477 public static function provideSectionEdit() {
478 $text = 'Intro
479
480 == one ==
481 first section.
482
483 == two ==
484 second section.
485 ';
486
487 $sectionOne = '== one ==
488 hello
489 ';
490
491 $newSection = '== new section ==
492
493 hello
494 ';
495
496 $textWithNewSectionOne = preg_replace(
497 '/== one ==.*== two ==/ms',
498 "$sectionOne\n== two ==", $text
499 );
500
501 $textWithNewSectionAdded = "$text\n$newSection";
502
503 return [
504 [ # 0
505 $text,
506 '',
507 'hello',
508 'replace all',
509 'hello'
510 ],
511
512 [ # 1
513 $text,
514 '1',
515 $sectionOne,
516 'replace first section',
517 $textWithNewSectionOne,
518 ],
519
520 [ # 2
521 $text,
522 'new',
523 'hello',
524 'new section',
525 $textWithNewSectionAdded,
526 ],
527 ];
528 }
529
530 /**
531 * @dataProvider provideSectionEdit
532 * @covers EditPage
533 */
534 public function testSectionEdit( $base, $section, $text, $summary, $expected ) {
535 $edit = [
536 'wpTextbox1' => $text,
537 'wpSummary' => $summary,
538 'wpSection' => $section,
539 ];
540
541 $this->assertEdit( 'EditPageTest_testSectionEdit', $base, null, $edit,
542 EditPage::AS_SUCCESS_UPDATE, $expected,
543 "expected successful update of section" );
544 }
545
546 public static function provideAutoMerge() {
547 $tests = [];
548
549 $tests[] = [ # 0: plain conflict
550 "Elmo", # base edit user
551 "one\n\ntwo\n\nthree\n",
552 [ # adam's edit
553 'wpStarttime' => 1,
554 'wpTextbox1' => "ONE\n\ntwo\n\nthree\n",
555 ],
556 [ # berta's edit
557 'wpStarttime' => 2,
558 'wpTextbox1' => "(one)\n\ntwo\n\nthree\n",
559 ],
560 EditPage::AS_CONFLICT_DETECTED, # expected code
561 "ONE\n\ntwo\n\nthree\n", # expected text
562 'expected edit conflict', # message
563 ];
564
565 $tests[] = [ # 1: successful merge
566 "Elmo", # base edit user
567 "one\n\ntwo\n\nthree\n",
568 [ # adam's edit
569 'wpStarttime' => 1,
570 'wpTextbox1' => "ONE\n\ntwo\n\nthree\n",
571 ],
572 [ # berta's edit
573 'wpStarttime' => 2,
574 'wpTextbox1' => "one\n\ntwo\n\nTHREE\n",
575 ],
576 EditPage::AS_SUCCESS_UPDATE, # expected code
577 "ONE\n\ntwo\n\nTHREE\n", # expected text
578 'expected automatic merge', # message
579 ];
580
581 $text = "Intro\n\n";
582 $text .= "== first section ==\n\n";
583 $text .= "one\n\ntwo\n\nthree\n\n";
584 $text .= "== second section ==\n\n";
585 $text .= "four\n\nfive\n\nsix\n\n";
586
587 // extract the first section.
588 $section = preg_replace( '/.*(== first section ==.*)== second section ==.*/sm', '$1', $text );
589
590 // generate expected text after merge
591 $expected = str_replace( 'one', 'ONE', str_replace( 'three', 'THREE', $text ) );
592
593 $tests[] = [ # 2: merge in section
594 "Elmo", # base edit user
595 $text,
596 [ # adam's edit
597 'wpStarttime' => 1,
598 'wpTextbox1' => str_replace( 'one', 'ONE', $section ),
599 'wpSection' => '1'
600 ],
601 [ # berta's edit
602 'wpStarttime' => 2,
603 'wpTextbox1' => str_replace( 'three', 'THREE', $section ),
604 'wpSection' => '1'
605 ],
606 EditPage::AS_SUCCESS_UPDATE, # expected code
607 $expected, # expected text
608 'expected automatic section merge', # message
609 ];
610
611 // see whether it makes a difference who did the base edit
612 $testsWithAdam = array_map( function ( $test ) {
613 $test[0] = 'Adam'; // change base edit user
614 return $test;
615 }, $tests );
616
617 $testsWithBerta = array_map( function ( $test ) {
618 $test[0] = 'Berta'; // change base edit user
619 return $test;
620 }, $tests );
621
622 return array_merge( $tests, $testsWithAdam, $testsWithBerta );
623 }
624
625 /**
626 * @dataProvider provideAutoMerge
627 * @covers EditPage
628 */
629 public function testAutoMerge( $baseUser, $text, $adamsEdit, $bertasEdit,
630 $expectedCode, $expectedText, $message = null
631 ) {
632 $this->markTestSkippedIfNoDiff3();
633
634 // create page
635 $ns = $this->getDefaultWikitextNS();
636 $title = Title::newFromText( 'EditPageTest_testAutoMerge', $ns );
637 $page = WikiPage::factory( $title );
638
639 if ( $page->exists() ) {
640 $page->doDeleteArticle( "clean slate for testing" );
641 }
642
643 $baseEdit = [
644 'wpTextbox1' => $text,
645 ];
646
647 $page = $this->assertEdit( 'EditPageTest_testAutoMerge', null,
648 $baseUser, $baseEdit, null, null, __METHOD__ );
649
650 $this->forceRevisionDate( $page, '20120101000000' );
651
652 $edittime = $page->getTimestamp();
653
654 // start timestamps for conflict detection
655 if ( !isset( $adamsEdit['wpStarttime'] ) ) {
656 $adamsEdit['wpStarttime'] = 1;
657 }
658
659 if ( !isset( $bertasEdit['wpStarttime'] ) ) {
660 $bertasEdit['wpStarttime'] = 2;
661 }
662
663 $starttime = wfTimestampNow();
664 $adamsTime = wfTimestamp(
665 TS_MW,
666 (int)wfTimestamp( TS_UNIX, $starttime ) + (int)$adamsEdit['wpStarttime']
667 );
668 $bertasTime = wfTimestamp(
669 TS_MW,
670 (int)wfTimestamp( TS_UNIX, $starttime ) + (int)$bertasEdit['wpStarttime']
671 );
672
673 $adamsEdit['wpStarttime'] = $adamsTime;
674 $bertasEdit['wpStarttime'] = $bertasTime;
675
676 $adamsEdit['wpSummary'] = 'Adam\'s edit';
677 $bertasEdit['wpSummary'] = 'Bertas\'s edit';
678
679 $adamsEdit['wpEdittime'] = $edittime;
680 $bertasEdit['wpEdittime'] = $edittime;
681
682 // first edit
683 $this->assertEdit( 'EditPageTest_testAutoMerge', null, 'Adam', $adamsEdit,
684 EditPage::AS_SUCCESS_UPDATE, null, "expected successful update" );
685
686 // second edit
687 $this->assertEdit( 'EditPageTest_testAutoMerge', null, 'Berta', $bertasEdit,
688 $expectedCode, $expectedText, $message );
689 }
690
691 /**
692 * @depends testAutoMerge
693 * @covers EditPage
694 */
695 public function testCheckDirectEditingDisallowed_forNonTextContent() {
696 $user = $GLOBALS['wgUser'];
697
698 $edit = [
699 'wpTextbox1' => serialize( 'non-text content' ),
700 'wpEditToken' => $user->getEditToken(),
701 'wpEdittime' => '',
702 'wpStarttime' => wfTimestampNow(),
703 'wpUnicodeCheck' => EditPage::UNICODE_CHECK,
704 ];
705
706 $this->setExpectedException(
707 MWException::class,
708 'This content model is not supported: testing'
709 );
710
711 $this->doEditDummyNonTextPage( $edit );
712 }
713
714 /** @covers EditPage */
715 public function testShouldPreventChangingContentModelWhenUserCannotChangeModelForTitle() {
716 $this->setTemporaryHook( 'getUserPermissionsErrors',
717 function ( Title $page, $user, $action, &$result ) {
718 if ( $action === 'editcontentmodel' &&
719 $page->getContentModel() === CONTENT_MODEL_WIKITEXT ) {
720 $result = false;
721
722 return false;
723 }
724 } );
725
726 $user = $GLOBALS['wgUser'];
727
728 $status = $this->doEditDummyNonTextPage( [
729 'wpTextbox1' => 'some text',
730 'wpEditToken' => $user->getEditToken(),
731 'wpEdittime' => '',
732 'wpStarttime' => wfTimestampNow(),
733 'wpUnicodeCheck' => EditPage::UNICODE_CHECK,
734 'model' => CONTENT_MODEL_WIKITEXT,
735 'format' => CONTENT_FORMAT_WIKITEXT,
736 ] );
737
738 $this->assertFalse( $status->isOK() );
739 $this->assertEquals( EditPage::AS_NO_CHANGE_CONTENT_MODEL, $status->getValue() );
740 }
741
742 /** @covers EditPage */
743 public function testShouldPreventChangingContentModelWhenUserCannotEditTargetTitle() {
744 $this->setTemporaryHook( 'getUserPermissionsErrors',
745 function ( Title $page, $user, $action, &$result ) {
746 if ( $action === 'edit' && $page->getContentModel() === CONTENT_MODEL_WIKITEXT ) {
747 $result = false;
748 return false;
749 }
750 } );
751
752 $user = $GLOBALS['wgUser'];
753
754 $status = $this->doEditDummyNonTextPage( [
755 'wpTextbox1' => 'some text',
756 'wpEditToken' => $user->getEditToken(),
757 'wpEdittime' => '',
758 'wpStarttime' => wfTimestampNow(),
759 'wpUnicodeCheck' => EditPage::UNICODE_CHECK,
760 'model' => CONTENT_MODEL_WIKITEXT,
761 'format' => CONTENT_FORMAT_WIKITEXT,
762 ] );
763
764 $this->assertFalse( $status->isOK() );
765 $this->assertEquals( EditPage::AS_NO_CHANGE_CONTENT_MODEL, $status->getValue() );
766 }
767
768 private function doEditDummyNonTextPage( array $edit ): Status {
769 $title = Title::newFromText( 'Dummy:NonTextPageForEditPage' );
770
771 $article = new Article( $title );
772 $article->getContext()->setTitle( $title );
773 $ep = new EditPage( $article );
774 $ep->setContextTitle( $title );
775
776 $req = new FauxRequest( $edit, true );
777 $ep->importFormData( $req );
778
779 return $ep->internalAttemptSave( $result, false );
780 }
781 }