From fb9a36c04916abad824e1f0f5c6f23bb859d5c17 Mon Sep 17 00:00:00 2001 From: WMDE-Fisch Date: Wed, 6 Dec 2017 15:25:39 +0100 Subject: [PATCH] Moved textbox1 building into TextConflictHelper on edit conflicts Factored out some minor parts about building editor CSS classes. getEditConflictMainTextBox() mainly mirrors showTextbox1 parts not included were moved to the EditPage. Change-Id: I671e095acc08382dd0a1c3d167fdaaa623ec5499 --- includes/EditPage.php | 35 ++---- includes/editpage/TextConflictHelper.php | 27 ++++ includes/editpage/TextboxBuilder.php | 44 +++++++ .../includes/editpage/TextboxBuilderTest.php | 116 ++++++++++++++++++ 4 files changed, 199 insertions(+), 23 deletions(-) diff --git a/includes/EditPage.php b/includes/EditPage.php index bcaab3a3d7..3c109f63c1 100644 --- a/includes/EditPage.php +++ b/includes/EditPage.php @@ -2861,7 +2861,14 @@ class EditPage { // and fallback to the raw wpTextbox1 since editconflicts can't be // resolved between page source edits and custom ui edits using the // custom edit ui. - $this->showTextbox1(); + $conflictTextBoxAttribs = []; + if ( $this->wasDeletedSinceLastEdit() ) { + $conflictTextBoxAttribs['style'] = 'display:none;'; + } elseif ( $this->isOldRev ) { + $conflictTextBoxAttribs['class'] = 'mw-textarea-oldrev'; + } + + $out->addHTML( $editConflictHelper->getEditConflictMainTextBox( $conflictTextBoxAttribs ) ); $out->addHTML( $editConflictHelper->getEditFormHtmlAfterContent() ); } else { $this->showContentForm(); @@ -3339,22 +3346,9 @@ class EditPage { if ( $this->wasDeletedSinceLastEdit() && $this->formtype == 'save' ) { $attribs = [ 'style' => 'display:none;' ]; } else { - $classes = []; // Textarea CSS - if ( $this->mTitle->isProtected( 'edit' ) && - MWNamespace::getRestrictionLevels( $this->mTitle->getNamespace() ) !== [ '' ] - ) { - # Is the title semi-protected? - if ( $this->mTitle->isSemiProtected() ) { - $classes[] = 'mw-textarea-sprotected'; - } else { - # Then it must be protected based on static groups (regular) - $classes[] = 'mw-textarea-protected'; - } - # Is the title cascade-protected? - if ( $this->mTitle->isCascadeProtected() ) { - $classes[] = 'mw-textarea-cprotected'; - } - } + $builder = new TextboxBuilder(); + $classes = $builder->getTextboxProtectionCSSClasses( $this->getTitle() ); + # Is an old revision being edited? if ( $this->isOldRev ) { $classes[] = 'mw-textarea-oldrev'; @@ -3366,12 +3360,7 @@ class EditPage { $attribs += $customAttribs; } - if ( count( $classes ) ) { - if ( isset( $attribs['class'] ) ) { - $classes[] = $attribs['class']; - } - $attribs['class'] = implode( ' ', $classes ); - } + $attribs = $builder->mergeClassesIntoAttributes( $classes, $attribs ); } $this->showTextbox( diff --git a/includes/editpage/TextConflictHelper.php b/includes/editpage/TextConflictHelper.php index 6e7e7ee6ee..b447b18f94 100644 --- a/includes/editpage/TextConflictHelper.php +++ b/includes/editpage/TextConflictHelper.php @@ -162,6 +162,33 @@ class TextConflictHelper { ); } + /** + * HTML to build the textbox1 on edit conflicts + * + * @param mixed[]|null $customAttribs + * @return string HTML + */ + public function getEditConflictMainTextBox( $customAttribs = [] ) { + $builder = new TextboxBuilder(); + $classes = $builder->getTextboxProtectionCSSClasses( $this->title ); + + $attribs = [ 'tabindex' => 1 ]; + $attribs += $customAttribs; + + $attribs = $builder->mergeClassesIntoAttributes( $classes, $attribs ); + + $attribs = $builder->buildTextboxAttribs( + 'wpTextbox1', + $attribs, + $this->out->getUser(), + $this->title + ); + + $this->out->addHTML( + Html::textarea( 'wpTextbox1', $builder->addNewLineAtEnd( $this->storedversion ), $attribs ) + ); + } + /** * Content to go in the edit form before textbox1 * diff --git a/includes/editpage/TextboxBuilder.php b/includes/editpage/TextboxBuilder.php index a6ae9bcb49..d0a2f8f4c3 100644 --- a/includes/editpage/TextboxBuilder.php +++ b/includes/editpage/TextboxBuilder.php @@ -24,6 +24,7 @@ namespace MediaWiki\EditPage; +use MWNamespace; use Title; use User; @@ -50,6 +51,49 @@ class TextboxBuilder { return $wikitext; } + /** + * @param string[] $classes + * @param mixed[] $attribs + * @return mixed[] + */ + public function mergeClassesIntoAttributes( array $classes, array $attribs ) { + if ( !count( $classes ) ) { + return $attribs; + } + + if ( isset( $attribs['class'] ) ) { + $classes[] = $attribs['class']; + } + $attribs['class'] = implode( ' ', $classes ); + + return $attribs; + } + + /** + * @param Title $title + * @return string[] + */ + public function getTextboxProtectionCSSClasses( Title $title ) { + $classes = []; // Textarea CSS + if ( $title->isProtected( 'edit' ) && + MWNamespace::getRestrictionLevels( $title->getNamespace() ) !== [ '' ] + ) { + # Is the title semi-protected? + if ( $title->isSemiProtected() ) { + $classes[] = 'mw-textarea-sprotected'; + } else { + # Then it must be protected based on static groups (regular) + $classes[] = 'mw-textarea-protected'; + } + # Is the title cascade-protected? + if ( $title->isCascadeProtected() ) { + $classes[] = 'mw-textarea-cprotected'; + } + } + + return $classes; + } + /** * @param string $name * @param mixed[] $customAttribs diff --git a/tests/phpunit/includes/editpage/TextboxBuilderTest.php b/tests/phpunit/includes/editpage/TextboxBuilderTest.php index 668baddfe6..b9bf5b9be7 100644 --- a/tests/phpunit/includes/editpage/TextboxBuilderTest.php +++ b/tests/phpunit/includes/editpage/TextboxBuilderTest.php @@ -86,4 +86,120 @@ class TextboxBuilderTest extends MediaWikiTestCase { // classes ok when nothing to be merged $this->assertSame( 'mw-editfont-monospace', $attribs3['class'] ); } + + public function provideMergeClassesIntoAttributes() { + return [ + [ + [], + [], + [], + ], + [ + [ 'mw-new-classname' ], + [], + [ 'class' => 'mw-new-classname' ], + ], + [ + [], + [ 'title' => 'My Title' ], + [ 'title' => 'My Title' ], + ], + [ + [ 'mw-new-classname' ], + [ 'title' => 'My Title' ], + [ 'title' => 'My Title', 'class' => 'mw-new-classname' ], + ], + [ + [ 'mw-new-classname' ], + [ 'class' => 'mw-existing-classname' ], + [ 'class' => 'mw-new-classname mw-existing-classname' ], + ], + ]; + } + + /** + * @dataProvider provideMergeClassesIntoAttributes + */ + public function testMergeClassesIntoAttributes( $inputClasses, $inputAttributes, $expected ) { + $builder = new TextboxBuilder(); + $this->assertSame( + $expected, + $builder->mergeClassesIntoAttributes( $inputClasses, $inputAttributes ) + ); + } + + public function provideGetTextboxProtectionCSSClasses() { + return [ + [ + [ '' ], + [ 'isProtected' ], + [], + ], + [ + true, + [], + [], + ], + [ + true, + [ 'isProtected' ], + [ 'mw-textarea-protected' ] + ], + [ + true, + [ 'isProtected', 'isSemiProtected' ], + [ 'mw-textarea-sprotected' ], + ], + [ + true, + [ 'isProtected', 'isCascadeProtected' ], + [ 'mw-textarea-protected', 'mw-textarea-cprotected' ], + ], + [ + true, + [ 'isProtected', 'isCascadeProtected', 'isSemiProtected' ], + [ 'mw-textarea-sprotected', 'mw-textarea-cprotected' ], + ], + ]; + } + + /** + * @dataProvider provideGetTextboxProtectionCSSClasses + */ + public function testGetTextboxProtectionCSSClasses( + $restrictionLevels, + $protectionModes, + $expected + ) { + $this->setMwGlobals( [ + // set to trick MWNamespace::getRestrictionLevels + 'wgRestrictionLevels' => $restrictionLevels + ] ); + + $builder = new TextboxBuilder(); + $this->assertSame( $expected, $builder->getTextboxProtectionCSSClasses( + $this->mockProtectedTitle( $protectionModes ) + ) ); + } + + /** + * @return Title + */ + private function mockProtectedTitle( $methodsToReturnTrue ) { + $title = $this->getMockBuilder( Title::class ) + ->disableOriginalConstructor() + ->getMock(); + + $title->expects( $this->any() ) + ->method( 'getNamespace' ) + ->will( $this->returnValue( 1 ) ); + + foreach ( $methodsToReturnTrue as $method ) { + $title->expects( $this->any() ) + ->method( $method ) + ->will( $this->returnValue( true ) ); + } + + return $title; + } } -- 2.20.1