2022-02-19 04:03:09 +00:00
|
|
|
<?php
|
|
|
|
/**
|
|
|
|
* Utilities for ResourceLoader modules used by DiscussionTools.
|
|
|
|
*
|
|
|
|
* @file
|
|
|
|
* @ingroup Extensions
|
|
|
|
* @license MIT
|
|
|
|
*/
|
|
|
|
|
|
|
|
namespace MediaWiki\Extension\DiscussionTools;
|
|
|
|
|
|
|
|
use ExtensionRegistry;
|
2023-12-11 15:38:02 +00:00
|
|
|
use MediaWiki\Config\Config;
|
2023-06-04 18:12:56 +00:00
|
|
|
use MediaWiki\Extension\DiscussionTools\Hooks\HookRunner;
|
2022-02-19 04:03:09 +00:00
|
|
|
use MediaWiki\MediaWikiServices;
|
2022-05-20 02:11:31 +00:00
|
|
|
use MediaWiki\ResourceLoader as RL;
|
2023-08-19 18:16:15 +00:00
|
|
|
use MediaWiki\Title\Title;
|
2022-02-19 04:03:09 +00:00
|
|
|
use MessageLocalizer;
|
|
|
|
|
|
|
|
class ResourceLoaderData {
|
|
|
|
/**
|
|
|
|
* Used for the 'ext.discussionTools.init' module and the test module.
|
|
|
|
*
|
|
|
|
* We need all of this data *in content language*. Some of it is already available in JS, but only
|
|
|
|
* in client language, so it's useless for us (e.g. digit transform table, month name messages).
|
|
|
|
*
|
2022-05-20 02:11:31 +00:00
|
|
|
* @param RL\Context $context
|
2022-02-19 04:03:09 +00:00
|
|
|
* @param Config $config
|
|
|
|
* @param string|null $langCode
|
|
|
|
* @return array
|
|
|
|
*/
|
|
|
|
public static function getLocalData(
|
2022-05-20 02:11:31 +00:00
|
|
|
RL\Context $context, Config $config, ?string $langCode = null
|
2022-02-19 04:03:09 +00:00
|
|
|
): array {
|
Change CommentParser into a service
Goal:
-----
To have a method like CommentParser::parse(), which just takes a node
to parse and a title and returns plain data, so that we don't need to
keep track of the config to construct a CommentParser object (the
required config like content language is provided by services) and
we don't need to keep that object around after parsing.
Changes:
--------
CommentParser.php:
* …is now a service. Constructor only takes services as arguments.
The node and title are passed to a new parse() method.
* parse() should return plain data, but I split this part to a separate
patch for ease of review: I49bfe019aa460651447fd383f73eafa9d7180a92.
* CommentParser still cheats and accesses global state in a few places,
e.g. calling Title::makeTitleSafe or CommentUtils::getTitleFromUrl,
so we can't turn its tests into true unit tests. This work is left
for future commits.
LanguageData.php:
* …is now a service, instead of a static class.
Parser.js:
* …is not a real service, but it's changed to behave in a similar way.
Constructor takes only the required config as argument,
and node and title are instead passed to a new parse() method.
CommentParserTest.php:
parser.test.js:
* Can be simplified, now that we don't need a useless node and title
to test internal methods that don't use them.
testUtils.js:
* Can be simplified, now that we don't need to override internal
ResourceLoader stuff just to change the parser config.
Change-Id: Iadb7757debe000025e52770ca51ebcf24ca8ee66
2022-02-19 02:43:21 +00:00
|
|
|
$services = MediaWikiServices::getInstance();
|
|
|
|
|
2022-02-19 04:03:09 +00:00
|
|
|
if ( $langCode === null ) {
|
Change CommentParser into a service
Goal:
-----
To have a method like CommentParser::parse(), which just takes a node
to parse and a title and returns plain data, so that we don't need to
keep track of the config to construct a CommentParser object (the
required config like content language is provided by services) and
we don't need to keep that object around after parsing.
Changes:
--------
CommentParser.php:
* …is now a service. Constructor only takes services as arguments.
The node and title are passed to a new parse() method.
* parse() should return plain data, but I split this part to a separate
patch for ease of review: I49bfe019aa460651447fd383f73eafa9d7180a92.
* CommentParser still cheats and accesses global state in a few places,
e.g. calling Title::makeTitleSafe or CommentUtils::getTitleFromUrl,
so we can't turn its tests into true unit tests. This work is left
for future commits.
LanguageData.php:
* …is now a service, instead of a static class.
Parser.js:
* …is not a real service, but it's changed to behave in a similar way.
Constructor takes only the required config as argument,
and node and title are instead passed to a new parse() method.
CommentParserTest.php:
parser.test.js:
* Can be simplified, now that we don't need a useless node and title
to test internal methods that don't use them.
testUtils.js:
* Can be simplified, now that we don't need to override internal
ResourceLoader stuff just to change the parser config.
Change-Id: Iadb7757debe000025e52770ca51ebcf24ca8ee66
2022-02-19 02:43:21 +00:00
|
|
|
$langData = $services->getService( 'DiscussionTools.LanguageData' );
|
2022-02-19 04:03:09 +00:00
|
|
|
} else {
|
Change CommentParser into a service
Goal:
-----
To have a method like CommentParser::parse(), which just takes a node
to parse and a title and returns plain data, so that we don't need to
keep track of the config to construct a CommentParser object (the
required config like content language is provided by services) and
we don't need to keep that object around after parsing.
Changes:
--------
CommentParser.php:
* …is now a service. Constructor only takes services as arguments.
The node and title are passed to a new parse() method.
* parse() should return plain data, but I split this part to a separate
patch for ease of review: I49bfe019aa460651447fd383f73eafa9d7180a92.
* CommentParser still cheats and accesses global state in a few places,
e.g. calling Title::makeTitleSafe or CommentUtils::getTitleFromUrl,
so we can't turn its tests into true unit tests. This work is left
for future commits.
LanguageData.php:
* …is now a service, instead of a static class.
Parser.js:
* …is not a real service, but it's changed to behave in a similar way.
Constructor takes only the required config as argument,
and node and title are instead passed to a new parse() method.
CommentParserTest.php:
parser.test.js:
* Can be simplified, now that we don't need a useless node and title
to test internal methods that don't use them.
testUtils.js:
* Can be simplified, now that we don't need to override internal
ResourceLoader stuff just to change the parser config.
Change-Id: Iadb7757debe000025e52770ca51ebcf24ca8ee66
2022-02-19 02:43:21 +00:00
|
|
|
$langData = new LanguageData(
|
|
|
|
$services->getMainConfig(),
|
|
|
|
$services->getLanguageFactory()->getLanguage( $langCode ),
|
|
|
|
$services->getLanguageConverterFactory(),
|
|
|
|
$services->getSpecialPageFactory()
|
|
|
|
);
|
2022-02-19 04:03:09 +00:00
|
|
|
}
|
|
|
|
|
Change CommentParser into a service
Goal:
-----
To have a method like CommentParser::parse(), which just takes a node
to parse and a title and returns plain data, so that we don't need to
keep track of the config to construct a CommentParser object (the
required config like content language is provided by services) and
we don't need to keep that object around after parsing.
Changes:
--------
CommentParser.php:
* …is now a service. Constructor only takes services as arguments.
The node and title are passed to a new parse() method.
* parse() should return plain data, but I split this part to a separate
patch for ease of review: I49bfe019aa460651447fd383f73eafa9d7180a92.
* CommentParser still cheats and accesses global state in a few places,
e.g. calling Title::makeTitleSafe or CommentUtils::getTitleFromUrl,
so we can't turn its tests into true unit tests. This work is left
for future commits.
LanguageData.php:
* …is now a service, instead of a static class.
Parser.js:
* …is not a real service, but it's changed to behave in a similar way.
Constructor takes only the required config as argument,
and node and title are instead passed to a new parse() method.
CommentParserTest.php:
parser.test.js:
* Can be simplified, now that we don't need a useless node and title
to test internal methods that don't use them.
testUtils.js:
* Can be simplified, now that we don't need to override internal
ResourceLoader stuff just to change the parser config.
Change-Id: Iadb7757debe000025e52770ca51ebcf24ca8ee66
2022-02-19 02:43:21 +00:00
|
|
|
return $langData->getLocalData();
|
2022-02-19 04:03:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Return messages in content language, for use in a ResourceLoader module.
|
|
|
|
*
|
2022-05-20 02:11:31 +00:00
|
|
|
* @param RL\Context $context
|
2022-02-19 04:03:09 +00:00
|
|
|
* @param Config $config
|
|
|
|
* @param array $messagesKeys
|
|
|
|
* @return array
|
|
|
|
*/
|
|
|
|
public static function getContentLanguageMessages(
|
2022-05-20 02:11:31 +00:00
|
|
|
RL\Context $context, Config $config, array $messagesKeys = []
|
2022-02-19 04:03:09 +00:00
|
|
|
): array {
|
|
|
|
return array_combine(
|
|
|
|
$messagesKeys,
|
|
|
|
array_map( static function ( $key ) {
|
|
|
|
return wfMessage( $key )->inContentLanguage()->text();
|
|
|
|
}, $messagesKeys )
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Return information about terms-of-use messages.
|
|
|
|
*
|
|
|
|
* @param MessageLocalizer $context
|
|
|
|
* @param Config $config
|
2022-10-09 03:27:28 +00:00
|
|
|
* @return array[] Map from internal name to array of parameters for MessageLocalizer::msg()
|
|
|
|
* @phan-return non-empty-array[]
|
2022-02-19 04:03:09 +00:00
|
|
|
*/
|
|
|
|
private static function getTermsOfUseMessages(
|
|
|
|
MessageLocalizer $context, Config $config
|
|
|
|
): array {
|
|
|
|
$messages = [
|
|
|
|
'reply' => [ 'discussiontools-replywidget-terms-click',
|
|
|
|
$context->msg( 'discussiontools-replywidget-reply' )->text() ],
|
|
|
|
'newtopic' => [ 'discussiontools-replywidget-terms-click',
|
|
|
|
$context->msg( 'discussiontools-replywidget-newtopic' )->text() ],
|
|
|
|
];
|
|
|
|
|
2023-06-04 18:12:56 +00:00
|
|
|
$hookRunner = new HookRunner( MediaWikiServices::getInstance()->getHookContainer() );
|
|
|
|
$hookRunner->onDiscussionToolsTermsOfUseMessages( $messages, $context, $config );
|
2022-02-19 04:03:09 +00:00
|
|
|
|
|
|
|
return $messages;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Return parsed terms-of-use messages, for use in a ResourceLoader module.
|
|
|
|
*
|
|
|
|
* @param MessageLocalizer $context
|
|
|
|
* @param Config $config
|
|
|
|
* @return array
|
|
|
|
*/
|
|
|
|
public static function getTermsOfUseMessagesParsed(
|
|
|
|
MessageLocalizer $context, Config $config
|
|
|
|
): array {
|
2022-06-09 13:51:33 +00:00
|
|
|
$messages = static::getTermsOfUseMessages( $context, $config );
|
2022-02-19 04:03:09 +00:00
|
|
|
foreach ( $messages as &$msg ) {
|
|
|
|
$msg = $context->msg( ...$msg )->parse();
|
|
|
|
}
|
|
|
|
return $messages;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Return information about terms-of-use messages, for use in a ResourceLoader module as
|
|
|
|
* 'versionCallback'. This is to avoid calling the parser from version invalidation code.
|
|
|
|
*
|
|
|
|
* @param MessageLocalizer $context
|
|
|
|
* @param Config $config
|
|
|
|
* @return array
|
|
|
|
*/
|
|
|
|
public static function getTermsOfUseMessagesVersion(
|
|
|
|
MessageLocalizer $context, Config $config
|
|
|
|
): array {
|
2022-06-09 13:51:33 +00:00
|
|
|
$messages = static::getTermsOfUseMessages( $context, $config );
|
2022-02-19 04:03:09 +00:00
|
|
|
foreach ( $messages as &$msg ) {
|
|
|
|
$message = $context->msg( ...$msg );
|
|
|
|
$msg = [
|
|
|
|
// Include the text of the message, in case the canonical translation changes
|
|
|
|
$message->plain(),
|
|
|
|
// Include the page touched time, in case the on-wiki override is invalidated
|
|
|
|
Title::makeTitle( NS_MEDIAWIKI, ucfirst( $message->getKey() ) )->getTouched(),
|
|
|
|
];
|
|
|
|
}
|
|
|
|
return $messages;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Add optional dependencies to a ResourceLoader module definition depending on loaded extensions.
|
|
|
|
*
|
|
|
|
* @param array $info
|
2022-05-20 02:11:31 +00:00
|
|
|
* @return RL\Module
|
2022-02-19 04:03:09 +00:00
|
|
|
*/
|
2022-05-20 02:11:31 +00:00
|
|
|
public static function addOptionalDependencies( array $info ): RL\Module {
|
2022-02-19 04:03:09 +00:00
|
|
|
$extensionRegistry = ExtensionRegistry::getInstance();
|
|
|
|
|
|
|
|
foreach ( $info['optionalDependencies'] as $ext => $deps ) {
|
|
|
|
if ( $extensionRegistry->isLoaded( $ext ) ) {
|
|
|
|
$info['dependencies'] = array_merge( $info['dependencies'], (array)$deps );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-05-20 02:11:31 +00:00
|
|
|
$class = $info['class'] ?? RL\FileModule::class;
|
2022-02-19 04:03:09 +00:00
|
|
|
return new $class( $info );
|
|
|
|
}
|
2022-10-12 19:10:57 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Generate the test module that includes all of the test data, based on the JSON files defining
|
|
|
|
* test cases.
|
|
|
|
*
|
|
|
|
* @param array $info
|
|
|
|
* @return RL\Module
|
|
|
|
*/
|
|
|
|
public static function makeTestModule( array $info ): RL\Module {
|
2023-03-28 23:42:49 +00:00
|
|
|
// Some tests rely on PHP-only features or are too large for the Karma test runner.
|
|
|
|
// Skip them here. They are still tested in the PHP version.
|
|
|
|
$skipTests = [
|
|
|
|
'cases/modified.json' => [
|
|
|
|
// Too large, cause timeouts in Karma test runner
|
|
|
|
'enwiki oldparser',
|
|
|
|
'enwiki parsoid',
|
|
|
|
'enwiki oldparser (bullet indentation)',
|
|
|
|
'enwiki parsoid (bullet indentation)',
|
|
|
|
// These tests depend on #getTranscludedFrom(), which we didn't implement in JS
|
|
|
|
'arwiki no-paragraph parsoid',
|
|
|
|
'enwiki parsoid',
|
|
|
|
'Many comments consisting of a block template and a paragraph',
|
|
|
|
'Comment whose range almost exactly matches a template, but is not considered transcluded (T313100)',
|
|
|
|
'Accidental complex transclusion (T265528)',
|
|
|
|
'Accidental complex transclusion (T313093)',
|
|
|
|
],
|
|
|
|
];
|
|
|
|
$info['packageFiles'][] = [
|
|
|
|
'name' => 'skip.json',
|
|
|
|
'type' => 'data',
|
|
|
|
'content' => $skipTests,
|
|
|
|
];
|
|
|
|
|
2022-10-12 19:10:57 +00:00
|
|
|
$keys = [ 'config', 'data', 'dom', 'expected' ];
|
|
|
|
foreach ( $info['testData'] as $path ) {
|
|
|
|
$info['packageFiles'][] = $path;
|
|
|
|
$localPath = $info['localBasePath'] . '/' . $path;
|
|
|
|
$data = json_decode( file_get_contents( $localPath ), true );
|
|
|
|
foreach ( $data as $case ) {
|
2023-06-06 12:08:00 +00:00
|
|
|
if ( isset( $case['name'] ) && in_array( $case['name'], $skipTests[$path] ?? [], true ) ) {
|
2023-03-28 23:42:49 +00:00
|
|
|
continue;
|
|
|
|
}
|
2022-10-12 19:10:57 +00:00
|
|
|
foreach ( $case as $key => $val ) {
|
2023-06-06 12:08:00 +00:00
|
|
|
if ( in_array( $key, $keys, true ) && is_string( $val ) ) {
|
2022-10-12 19:10:57 +00:00
|
|
|
if ( str_ends_with( $val, '.json' ) ) {
|
|
|
|
$info['packageFiles'][] = substr( $val, strlen( '../' ) );
|
|
|
|
} elseif ( str_ends_with( $val, '.html' ) ) {
|
2022-10-12 20:03:23 +00:00
|
|
|
$info['packageFiles'][] = [
|
|
|
|
'name' => $val,
|
|
|
|
'type' => 'data',
|
|
|
|
'callback' => static function () use ( $info, $val ) {
|
|
|
|
$localPath = $info['localBasePath'] . '/' . $val;
|
|
|
|
return file_get_contents( $localPath );
|
|
|
|
},
|
|
|
|
'versionCallback' => static function () use ( $val ) {
|
|
|
|
return new RL\FilePath( $val );
|
|
|
|
},
|
|
|
|
];
|
2022-10-12 19:10:57 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
$class = $info['class'] ?? RL\FileModule::class;
|
|
|
|
return new $class( $info );
|
|
|
|
}
|
2022-02-19 04:03:09 +00:00
|
|
|
}
|