Allow visualeditor-cite-tool-name-… messages to be missing

The information read from the …cite-tool-definition.json files is
effectively user input, even if only interface administrators can
edit it. Usually we carefully validate user input. But as of now
this code starts failing with all kinds of uncatched errors.
* An entry with no name, an empty name, or a name that's not a
  string will cause all kinds of undefined behavior.
* An entry with an empty title results in an invisible button.
* A missing message results in a technical <…> placeholder, even if
  the name is usually a sensible fallback.

Note that hard-coding titles as plain text strings in the ….json
file was already possible.

Change-Id: Iddcedbe859e86ac4c3f79a53d36237daff86c0db
This commit is contained in:
thiemowmde 2024-03-06 13:59:51 +01:00
parent 61880c7535
commit 2b32f15c8c
2 changed files with 38 additions and 9 deletions

View file

@ -30,13 +30,23 @@ class CitationToolDefinition {
$citationTools = [];
if ( is_array( $citationDefinition ) ) {
foreach ( $citationDefinition as $tool ) {
// The following messages are generated here:
// * visualeditor-cite-tool-name-book
// * visualeditor-cite-tool-name-journal
// * visualeditor-cite-tool-name-news
// * visualeditor-cite-tool-name-web
$tool->title ??= $context->msg( 'visualeditor-cite-tool-name-' . $tool->name )
->text();
// Skip incomplete entries that don't even have a name
if ( empty( $tool->name ) || !is_string( $tool->name ) ) {
continue;
}
// Users can hard-code titles in MediaWiki:Cite-tool-definition.json if they want
if ( empty( $tool->title ) || !is_string( $tool->title ) ) {
// The following messages are generated here:
// * visualeditor-cite-tool-name-book
// * visualeditor-cite-tool-name-journal
// * visualeditor-cite-tool-name-news
// * visualeditor-cite-tool-name-web
$msg = $context->msg( 'visualeditor-cite-tool-name-' . $tool->name );
// Fall back to the raw name if there is no message
$tool->title = $msg->isDisabled() ? $tool->name : $msg->text();
}
$citationTools[] = $tool;
}
}

View file

@ -15,26 +15,45 @@ class CitationToolDefinitionTest extends \MediaWikiUnitTestCase {
public function testGetScript() {
$context = $this->createResourceLoaderContext();
$expected = [
[ 'name' => 'no-message', 'title' => 'Hard-coded title' ],
[ 'name' => 'missing-message', 'title' => 'missing-message' ],
[ 'name' => 'n', 'title' => 't' ],
];
$this->assertSame(
've.ui.mwCitationTools = [{"name":"n","title":"t"}];',
've.ui.mwCitationTools = ' . json_encode( $expected ) . ';',
CitationToolDefinition::makeScript( $context )
);
}
private function createResourceLoaderContext(): Context {
$definition = [
// We expect broken and incomplete entries to be skipped
[],
[ 'name' => '' ],
[ 'name' => 'no-message', 'title' => 'Hard-coded title' ],
[ 'name' => 'missing-message' ],
[ 'name' => 'n' ],
];
$msg = $this->createMock( Message::class );
$msg->method( 'inContentLanguage' )
->willReturnSelf();
$msg->method( 'plain' )
->willReturnOnConsecutiveCalls( '', '[{"name":"n"}]' );
->willReturn( json_encode( $definition ) );
$msg->method( 'text' )
->willReturn( 't' );
$disabled = $this->createMock( Message::class );
$disabled->method( 'isDisabled' )->willReturn( true );
$context = $this->createStub( Context::class );
$context->method( 'msg' )
->willReturnMap( [
[ 'cite-tool-definition.json', $msg ],
[ 'visualeditor-cite-tool-definition.json', $msg ],
[ 'visualeditor-cite-tool-name-missing-message', $disabled ],
[ 'visualeditor-cite-tool-name-n', $msg ]
] );
$context->method( 'encodeJson' )->willReturnCallback( 'json_encode' );