diff --git a/includes/ApiDiscussionToolsEdit.php b/includes/ApiDiscussionToolsEdit.php index 6d3bb7339..2e3ba7bc8 100644 --- a/includes/ApiDiscussionToolsEdit.php +++ b/includes/ApiDiscussionToolsEdit.php @@ -106,9 +106,8 @@ class ApiDiscussionToolsEdit extends ApiBase { $html = $params['html']; if ( $wikitext !== null ) { - $wikitext = CommentUtils::htmlTrim( $wikitext ); if ( $signature !== null ) { - $wikitext .= $signature; + $wikitext = CommentModifier::appendSignatureWikitext( $wikitext, $signature ); } } else { $doc = DOMUtils::parseHTML( '' ); diff --git a/includes/ApiDiscussionToolsTrait.php b/includes/ApiDiscussionToolsTrait.php index 71c251de1..b95e7548f 100644 --- a/includes/ApiDiscussionToolsTrait.php +++ b/includes/ApiDiscussionToolsTrait.php @@ -60,9 +60,8 @@ trait ApiDiscussionToolsTrait { switch ( $type ) { case 'topic': if ( $wikitext !== null ) { - $wikitext = CommentUtils::htmlTrim( $wikitext ); if ( $signature !== null ) { - $wikitext .= $signature; + $wikitext = CommentModifier::appendSignatureWikitext( $wikitext, $signature ); } } else { $doc = DOMUtils::parseHTML( '' ); diff --git a/includes/CommentModifier.php b/includes/CommentModifier.php index dfe00c576..5f231d1a0 100644 --- a/includes/CommentModifier.php +++ b/includes/CommentModifier.php @@ -446,9 +446,24 @@ class CommentModifier { public static function appendSignature( DocumentFragment $container, string $signature ): void { $doc = $container->ownerDocument; - // If the last node isn't a paragraph (e.g. it's a list created in visual mode), then - // add another paragraph to contain the signature. - if ( !$container->lastChild || strtolower( $container->lastChild->nodeName ) !== 'p' ) { + // If the last node isn't a paragraph (e.g. it's a list created in visual mode), + // or looks like a list item created in wikitext mode (T263217), + // then add another paragraph to contain the signature. + $wrapperNode = $container->lastChild; + if ( + !( $wrapperNode instanceof Element ) || + strtolower( $wrapperNode->tagName ) !== 'p' || + ( + // This would be easier to check in prepareWikitextReply(), but that would result + // in an empty list item being added at the end if we don't need to add a signature. + ( $wtNode = $wrapperNode->lastChild ) && + $wtNode instanceof Element && + ( $dataMw = json_decode( $wtNode->getAttribute( 'data-mw' ) ?? '', true ) ) && + ( $wikitextLine = $dataMw['parts'][0] ?? null ) && + is_string( $wikitextLine ) && + in_array( $wikitextLine[0], [ '*', '#', ':', ';' ] ) + ) + ) { $container->appendChild( $doc->createElement( 'p' ) ); } // If the last node is empty, trim the signature to prevent leading whitespace triggering @@ -465,6 +480,29 @@ class CommentModifier { ); } + /** + * Append a user signature to the comment in the provided wikitext. + * + * @param string $wikitext + * @param string $signature + * @return string + */ + public static function appendSignatureWikitext( string $wikitext, string $signature ): string { + $wikitext = CommentUtils::htmlTrim( $wikitext ); + + $lines = explode( "\n", $wikitext ); + $lastLine = end( $lines ); + + // If last line looks like a list item, add an empty line afterwards for the signature (T263217) + if ( $lastLine && in_array( $lastLine[0], [ '*', '#', ':', ';' ] ) ) { + $wikitext .= "\n"; + // Trim the signature to prevent leading whitespace triggering preformatted text (T269188, T276612) + $signature = ltrim( $signature, ' ' ); + } + + return $wikitext . $signature; + } + /** * Add a reply to a specific comment * diff --git a/tests/cases/appendSignature.json b/tests/cases/appendSignature.json index cdd6ce7cf..ddea3cdfa 100644 --- a/tests/cases/appendSignature.json +++ b/tests/cases/appendSignature.json @@ -4,11 +4,21 @@ "html": "

Foo bar

", "expected": "

Foo bar

" }, + { + "msg": "Simple message (from wikitext mode)", + "html": "

", + "expected": "

" + }, { "msg": "List", "html": "

Foo bar

", "expected": "

Foo bar

" }, + { + "msg": "List (from wikitext mode)", + "html": "

", + "expected": "

" + }, { "msg": "Empty trailing paragraph", "html": "

Foo bar

", diff --git a/tests/phpunit/CommentModifierTest.php b/tests/phpunit/CommentModifierTest.php index 2e1027604..bb80b23c6 100644 --- a/tests/phpunit/CommentModifierTest.php +++ b/tests/phpunit/CommentModifierTest.php @@ -147,6 +147,22 @@ class CommentModifierTest extends IntegrationTestCase { return static::getJson( '../cases/appendSignature.json' ); } + /** + * @covers ::appendSignatureWikitext + */ + public function testAppendSignatureWikitext(): void { + static::assertEquals( + 'Foo bar ~~~~', + CommentModifier::appendSignatureWikitext( 'Foo bar', ' ~~~~' ), + 'Simple message' + ); + static::assertEquals( + "Foo bar\n*A\n*B\n~~~~", + CommentModifier::appendSignatureWikitext( "Foo bar\n*A\n*B", ' ~~~~' ), + 'List' + ); + } + /** * @dataProvider provideSanitizeWikitextLinebreaks * @covers ::sanitizeWikitextLinebreaks