aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--autoload.php1
-rw-r--r--includes/MediaWikiServices.php8
-rw-r--r--includes/ServiceWiring.php15
-rw-r--r--includes/parser/Parsoid/LintErrorChecker.php91
-rw-r--r--includes/preferences/SignatureValidator.php52
-rw-r--r--includes/preferences/SignatureValidatorFactory.php26
-rw-r--r--tests/phpunit/includes/parser/Parsoid/LintErrorCheckerTest.php107
-rw-r--r--tests/phpunit/includes/preferences/SignatureValidatorTest.php28
8 files changed, 232 insertions, 96 deletions
diff --git a/autoload.php b/autoload.php
index 6baa2b851a8f..7bdfceb1c74d 100644
--- a/autoload.php
+++ b/autoload.php
@@ -1847,6 +1847,7 @@ $wgAutoloadLocalClasses = [
'MediaWiki\\Parser\\Parsoid\\HtmlToContentTransform' => __DIR__ . '/includes/parser/Parsoid/HtmlToContentTransform.php',
'MediaWiki\\Parser\\Parsoid\\HtmlTransformFactory' => __DIR__ . '/includes/parser/Parsoid/HtmlTransformFactory.php',
'MediaWiki\\Parser\\Parsoid\\LanguageVariantConverter' => __DIR__ . '/includes/parser/Parsoid/LanguageVariantConverter.php',
+ 'MediaWiki\\Parser\\Parsoid\\LintErrorChecker' => __DIR__ . '/includes/parser/Parsoid/LintErrorChecker.php',
'MediaWiki\\Parser\\Parsoid\\PageBundleJsonTrait' => __DIR__ . '/includes/parser/Parsoid/PageBundleJsonTrait.php',
'MediaWiki\\Parser\\Parsoid\\PageBundleParserOutputConverter' => __DIR__ . '/includes/parser/Parsoid/PageBundleParserOutputConverter.php',
'MediaWiki\\Parser\\Parsoid\\ParsoidOutputAccess' => __DIR__ . '/includes/parser/Parsoid/ParsoidOutputAccess.php',
diff --git a/includes/MediaWikiServices.php b/includes/MediaWikiServices.php
index 10c7c6c16c7e..2976551744a8 100644
--- a/includes/MediaWikiServices.php
+++ b/includes/MediaWikiServices.php
@@ -119,6 +119,7 @@ use MediaWiki\Parser\ParserCacheFactory;
use MediaWiki\Parser\Parsoid\Config\PageConfigFactory;
use MediaWiki\Parser\Parsoid\Config\SiteConfig;
use MediaWiki\Parser\Parsoid\HtmlTransformFactory;
+use MediaWiki\Parser\Parsoid\LintErrorChecker;
use MediaWiki\Parser\Parsoid\ParsoidOutputAccess;
use MediaWiki\Parser\Parsoid\ParsoidParserFactory;
use MediaWiki\Password\PasswordFactory;
@@ -1329,6 +1330,13 @@ class MediaWikiServices extends ServiceContainer {
}
/**
+ * @since 1.43
+ */
+ public function getLintErrorChecker(): LintErrorChecker {
+ return $this->getService( 'LintErrorChecker' );
+ }
+
+ /**
* @since 1.34
*/
public function getLocalisationCache(): LocalisationCache {
diff --git a/includes/ServiceWiring.php b/includes/ServiceWiring.php
index 6cbbb17d4abf..aa01b827133e 100644
--- a/includes/ServiceWiring.php
+++ b/includes/ServiceWiring.php
@@ -154,6 +154,7 @@ use MediaWiki\Parser\Parsoid\Config\DataAccess as MWDataAccess;
use MediaWiki\Parser\Parsoid\Config\PageConfigFactory as MWPageConfigFactory;
use MediaWiki\Parser\Parsoid\Config\SiteConfig as MWSiteConfig;
use MediaWiki\Parser\Parsoid\HtmlTransformFactory;
+use MediaWiki\Parser\Parsoid\LintErrorChecker;
use MediaWiki\Parser\Parsoid\ParsoidOutputAccess;
use MediaWiki\Parser\Parsoid\ParsoidParserFactory;
use MediaWiki\Password\PasswordFactory;
@@ -1142,6 +1143,16 @@ return [
);
},
+ 'LintErrorChecker' => static function ( MediaWikiServices $services ): LintErrorChecker {
+ return new LintErrorChecker(
+ $services->get( '_Parsoid' ),
+ $services->getParsoidPageConfigFactory(),
+ $services->getTitleFactory(),
+ ExtensionRegistry::getInstance(),
+ $services->getMainConfig(),
+ );
+ },
+
'LocalisationCache' => static function ( MediaWikiServices $services ): LocalisationCache {
$conf = $services->getMainConfig()->get( MainConfigNames::LocalisationCacheConf );
@@ -2073,12 +2084,10 @@ return [
return $services->getParserFactory();
},
static function () use ( $services ) {
- return $services->get( '_Parsoid' );
+ return $services->getLintErrorChecker();
},
- $services->getParsoidPageConfigFactory(),
$services->getSpecialPageFactory(),
$services->getTitleFactory(),
- $services->getExtensionRegistry()
);
},
diff --git a/includes/parser/Parsoid/LintErrorChecker.php b/includes/parser/Parsoid/LintErrorChecker.php
new file mode 100644
index 000000000000..4d43557efcec
--- /dev/null
+++ b/includes/parser/Parsoid/LintErrorChecker.php
@@ -0,0 +1,91 @@
+<?php
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+namespace MediaWiki\Parser\Parsoid;
+
+use ExtensionRegistry;
+use MediaWiki\Config\Config;
+use MediaWiki\Parser\Parsoid\Config\PageConfigFactory;
+use MediaWiki\Revision\MutableRevisionRecord;
+use MediaWiki\Revision\SlotRecord;
+use MediaWiki\Title\TitleFactory;
+use Wikimedia\Parsoid\Parsoid;
+use WikitextContent;
+
+/**
+ * Check arbitrary wikitext for lint errors
+ *
+ * @since 1.43
+ */
+class LintErrorChecker {
+ private Parsoid $parsoid;
+ private PageConfigFactory $pageConfigFactory;
+ private TitleFactory $titleFactory;
+ private ExtensionRegistry $extensionRegistry;
+ private Config $mainConfig;
+
+ public function __construct(
+ Parsoid $parsoid,
+ PageConfigFactory $pageConfigFactory,
+ TitleFactory $titleFactory,
+ ExtensionRegistry $extensionRegistry,
+ Config $mainConfig
+
+ ) {
+ $this->parsoid = $parsoid;
+ $this->pageConfigFactory = $pageConfigFactory;
+ $this->titleFactory = $titleFactory;
+ $this->extensionRegistry = $extensionRegistry;
+ $this->mainConfig = $mainConfig;
+ }
+
+ private function linterOptions( array $disabled ): array {
+ // FIXME: We shouldn't be this interwined with an extension (T360809)
+ if ( $this->extensionRegistry->isLoaded( 'Linter' ) ) {
+ foreach ( $this->mainConfig->get( 'LinterCategories' ) as $name => $cat ) {
+ if ( $cat['priority'] === 'none' ) {
+ $disabled[] = $name;
+ }
+ }
+ }
+ $disabled = array_unique( $disabled );
+ return [ 'linterOverrides' => [ 'disabled' => $disabled ] ];
+ }
+
+ /**
+ * Check the given wikitext for lint errors
+ *
+ * While not strictly required, you'll get better results if the wikitext has already gone through PST
+ *
+ * @param string $wikitext Wikitext after PST
+ * @return array Array of error objects returned by Parsoid's lint API (empty array for no errors)
+ */
+ public function check( string $wikitext ): array {
+ return $this->checkSome( $wikitext, [] );
+ }
+
+ /**
+ * Check the given wikitext for lint errors against a subset of lint categories
+ *
+ * While not strictly required, you'll get better results if the wikitext has already gone through PST
+ *
+ * @param string $wikitext Wikitext after PST
+ * @param string[] $disabled Array of lint categories to disable
+ * @return array Array of error objects returned by Parsoid's lint API (empty array for no errors)
+ */
+ public function checkSome( string $wikitext, array $disabled ): array {
+ $title = $this->titleFactory->newMainPage();
+ $fakeRevision = new MutableRevisionRecord( $title );
+ $fakeRevision->setSlot(
+ SlotRecord::newUnsaved(
+ SlotRecord::MAIN,
+ new WikitextContent( $wikitext )
+ )
+ );
+
+ return $this->parsoid->wikitext2lint(
+ $this->pageConfigFactory->create( $title, null, $fakeRevision ),
+ $this->linterOptions( $disabled )
+ );
+ }
+}
diff --git a/includes/preferences/SignatureValidator.php b/includes/preferences/SignatureValidator.php
index 08067afff5f4..6bd874bd23c4 100644
--- a/includes/preferences/SignatureValidator.php
+++ b/includes/preferences/SignatureValidator.php
@@ -20,26 +20,19 @@
namespace MediaWiki\Preferences;
-use ExtensionRegistry;
use MediaWiki\Config\ServiceOptions;
use MediaWiki\Html\Html;
use MediaWiki\MainConfigNames;
-use MediaWiki\MediaWikiServices;
use MediaWiki\Parser\ParserOutputFlags;
-use MediaWiki\Parser\Parsoid\Config\PageConfigFactory;
-use MediaWiki\Revision\MutableRevisionRecord;
-use MediaWiki\Revision\SlotRecord;
+use MediaWiki\Parser\Parsoid\LintErrorChecker;
use MediaWiki\SpecialPage\SpecialPage;
use MediaWiki\SpecialPage\SpecialPageFactory;
-use MediaWiki\Title\Title;
use MediaWiki\Title\TitleFactory;
use MediaWiki\User\UserIdentity;
use MessageLocalizer;
use OOUI\ButtonWidget;
use ParserFactory;
use ParserOptions;
-use Wikimedia\Parsoid\Parsoid;
-use WikitextContent;
/**
* @since 1.35
@@ -60,15 +53,13 @@ class SignatureValidator {
private $popts;
/** @var ParserFactory */
private $parserFactory;
- private Parsoid $parsoid;
- private PageConfigFactory $pageConfigFactory;
+ private LintErrorChecker $lintErrorChecker;
/** @var ServiceOptions */
private $serviceOptions;
/** @var SpecialPageFactory */
private $specialPageFactory;
/** @var TitleFactory */
private $titleFactory;
- private ExtensionRegistry $extensionRegistry;
/**
* @param ServiceOptions $options
@@ -76,11 +67,9 @@ class SignatureValidator {
* @param ?MessageLocalizer $localizer
* @param ParserOptions $popts
* @param ParserFactory $parserFactory
- * @param Parsoid $parsoid
- * @param PageConfigFactory $pageConfigFactory
+ * @param LintErrorChecker $lintErrorChecker
* @param SpecialPageFactory $specialPageFactory
* @param TitleFactory $titleFactory
- * @param ExtensionRegistry $extensionRegistry
*/
public function __construct(
ServiceOptions $options,
@@ -88,25 +77,21 @@ class SignatureValidator {
?MessageLocalizer $localizer,
ParserOptions $popts,
ParserFactory $parserFactory,
- Parsoid $parsoid,
- PageConfigFactory $pageConfigFactory,
+ LintErrorChecker $lintErrorChecker,
SpecialPageFactory $specialPageFactory,
- TitleFactory $titleFactory,
- ExtensionRegistry $extensionRegistry
+ TitleFactory $titleFactory
) {
$this->user = $user;
$this->localizer = $localizer;
$this->popts = $popts;
$this->parserFactory = $parserFactory;
- $this->parsoid = $parsoid;
- $this->pageConfigFactory = $pageConfigFactory;
+ $this->lintErrorChecker = $lintErrorChecker;
// Configuration
$this->serviceOptions = $options;
$this->serviceOptions->assertRequiredOptions( self::CONSTRUCTOR_OPTIONS );
// TODO SpecialPage::getTitleFor should also be available via SpecialPageFactory
$this->specialPageFactory = $specialPageFactory;
$this->titleFactory = $titleFactory;
- $this->extensionRegistry = $extensionRegistry;
}
/**
@@ -278,30 +263,7 @@ class SignatureValidator {
MainConfigNames::SignatureAllowedLintErrors
)
);
- if ( $this->extensionRegistry->isLoaded( 'Linter' ) ) { // T360809
- $services = MediaWikiServices::getInstance();
- $linterCategories = $services->getMainConfig()->get( 'LinterCategories' );
- foreach ( $linterCategories as $name => $cat ) {
- if ( $cat['priority'] === 'none' ) {
- $disabled[] = $name;
- }
- }
- }
- $disabled = array_unique( $disabled );
-
- $page = Title::newMainPage();
- $fakeRevision = new MutableRevisionRecord( $page );
- $fakeRevision->setSlot(
- SlotRecord::newUnsaved(
- SlotRecord::MAIN,
- new WikitextContent( $signature )
- )
- );
-
- return $this->parsoid->wikitext2lint(
- $this->pageConfigFactory->create( $page, null, $fakeRevision ),
- [ 'linterOverrides' => [ 'disabled' => $disabled ] ]
- );
+ return $this->lintErrorChecker->checkSome( $signature, $disabled );
}
/**
diff --git a/includes/preferences/SignatureValidatorFactory.php b/includes/preferences/SignatureValidatorFactory.php
index 622e6adc9817..e1cc90be0715 100644
--- a/includes/preferences/SignatureValidatorFactory.php
+++ b/includes/preferences/SignatureValidatorFactory.php
@@ -21,9 +21,7 @@
namespace MediaWiki\Preferences;
-use ExtensionRegistry;
use MediaWiki\Config\ServiceOptions;
-use MediaWiki\Parser\Parsoid\Config\PageConfigFactory;
use MediaWiki\SpecialPage\SpecialPageFactory;
use MediaWiki\Title\TitleFactory;
use MediaWiki\User\UserIdentity;
@@ -41,9 +39,7 @@ class SignatureValidatorFactory {
private $parserFactoryClosure;
/** @var callable */
- private $parsoidClosure;
-
- private PageConfigFactory $pageConfigFactory;
+ private $lintErrorCheckerClosure;
/** @var SpecialPageFactory */
private $specialPageFactory;
@@ -51,37 +47,29 @@ class SignatureValidatorFactory {
/** @var TitleFactory */
private $titleFactory;
- private ExtensionRegistry $extensionRegistry;
-
/**
* @param ServiceOptions $options
* @param callable $parserFactoryClosure A function which returns a ParserFactory.
* We use this instead of an actual ParserFactory to avoid a circular dependency,
* since Parser also needs a SignatureValidatorFactory for signature formatting.
- * @param callable $parsoidClosure A function which returns a Parsoid, same as above.
- * @param PageConfigFactory $pageConfigFactory
+ * @param callable $lintErrorCheckerClosure A function which returns a LintErrorChecker, same as above.
* @param SpecialPageFactory $specialPageFactory
* @param TitleFactory $titleFactory
- * @param ExtensionRegistry $extensionRegistry
*/
public function __construct(
ServiceOptions $options,
callable $parserFactoryClosure,
- callable $parsoidClosure,
- PageConfigFactory $pageConfigFactory,
+ callable $lintErrorCheckerClosure,
SpecialPageFactory $specialPageFactory,
- TitleFactory $titleFactory,
- ExtensionRegistry $extensionRegistry
+ TitleFactory $titleFactory
) {
// Configuration
$this->serviceOptions = $options;
$this->serviceOptions->assertRequiredOptions( SignatureValidator::CONSTRUCTOR_OPTIONS );
$this->parserFactoryClosure = $parserFactoryClosure;
- $this->parsoidClosure = $parsoidClosure;
- $this->pageConfigFactory = $pageConfigFactory;
+ $this->lintErrorCheckerClosure = $lintErrorCheckerClosure;
$this->specialPageFactory = $specialPageFactory;
$this->titleFactory = $titleFactory;
- $this->extensionRegistry = $extensionRegistry;
}
/**
@@ -101,11 +89,9 @@ class SignatureValidatorFactory {
$localizer,
$popts,
( $this->parserFactoryClosure )(),
- ( $this->parsoidClosure )(),
- $this->pageConfigFactory,
+ ( $this->lintErrorCheckerClosure )(),
$this->specialPageFactory,
$this->titleFactory,
- $this->extensionRegistry
);
}
}
diff --git a/tests/phpunit/includes/parser/Parsoid/LintErrorCheckerTest.php b/tests/phpunit/includes/parser/Parsoid/LintErrorCheckerTest.php
new file mode 100644
index 000000000000..1a47cb8a1cfb
--- /dev/null
+++ b/tests/phpunit/includes/parser/Parsoid/LintErrorCheckerTest.php
@@ -0,0 +1,107 @@
+<?php
+/**
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ */
+
+use MediaWiki\MainConfigNames;
+use MediaWiki\Parser\Parsoid\LintErrorChecker;
+
+/**
+ * @group Parser
+ * @group Database
+ * @covers \MediaWiki\Parser\Parsoid\LintErrorChecker
+ */
+class LintErrorCheckerTest extends MediaWikiIntegrationTestCase {
+
+ protected function setUp(): void {
+ parent::setUp();
+ $this->overrideConfigValue( MainConfigNames::ParsoidSettings, [
+ 'linting' => true
+ ] );
+ $this->overrideConfigValue( 'LinterCategories', [
+ // No hidden categories in default set up
+ ] );
+ }
+
+ /**
+ * Get a basic LintErrorChecker for testing with.
+ * @return LintErrorChecker
+ */
+ protected function getLintErrorChecker() {
+ $services = $this->getServiceContainer();
+ $extReg = $this->createMock( ExtensionRegistry::class );
+ $extReg->method( 'isLoaded' )->willReturnCallback( static function ( string $which ) {
+ return $which == 'Linter';
+ } );
+
+ return new LintErrorChecker(
+ $services->get( '_Parsoid' ),
+ $services->getParsoidPageConfigFactory(),
+ $services->getTitleFactory(),
+ $extReg,
+ $services->getMainConfig(),
+ );
+ }
+
+ /**
+ * @dataProvider provideCheck
+ */
+ public function testCheck( $wikitext, $expected ) {
+ $errors = $this->getLintErrorChecker()->check( $wikitext );
+ $this->assertSame( $expected, $errors );
+ }
+
+ public static function provideCheck() {
+ yield 'Perfect' => [ '<strong>Foo</strong>', [] ];
+ yield 'Unclosed tag' => [
+ '<strong>Foo',
+ [
+ [
+ 'type' => 'missing-end-tag',
+ 'dsr' => [ 0, 11, 8, 0 ],
+ 'templateInfo' => null,
+ 'params' => [
+ 'name' => 'strong',
+ 'inTable' => false,
+ ]
+ ]
+ ]
+ ];
+ }
+
+ public function testCheckSome() {
+ // Take the same "Unclosed tag" test from above but disable the category
+ $errors = $this->getLintErrorChecker()->checkSome( '<strong>Foo', [ 'missing-end-tag' ] );
+ $this->assertSame( [], $errors );
+ }
+
+ /** Test when categories are diabled in $wgLinterCategories */
+ public function testLinterCategory() {
+ $input = '<font color="red">RED</font>';
+ $errors = $this->getLintErrorChecker()->check( $input );
+ $this->assertEquals( 'obsolete-tag', $errors[0]['type'] );
+
+ // Now disable the category
+ $this->overrideConfigValue( 'LinterCategories', [
+ 'obsolete-tag' => [ 'priority' => 'none' ],
+ ] );
+
+ $errors = $this->getLintErrorChecker()->check( $input );
+ $this->assertSame( [], $errors );
+ }
+}
diff --git a/tests/phpunit/includes/preferences/SignatureValidatorTest.php b/tests/phpunit/includes/preferences/SignatureValidatorTest.php
index d2aba241762f..b2d938ac8b9f 100644
--- a/tests/phpunit/includes/preferences/SignatureValidatorTest.php
+++ b/tests/phpunit/includes/preferences/SignatureValidatorTest.php
@@ -177,27 +177,6 @@ class SignatureValidatorTest extends MediaWikiIntegrationTestCase {
$this->assertSame( $expected, $result );
}
- /**
- * @covers \MediaWiki\Preferences\SignatureValidator::validateSignature()
- * @dataProvider provideValidateSignature
- */
- public function testValidateSignatureHidden( string $signature, $expected ) {
- // For testing hidden category support in ::testValidateSignature
- $this->overrideConfigValue( 'LinterCategories', [
- 'fostered' => [ 'priority' => 'medium' ],
- // A hidden category, for testing.
- 'wikilink-in-extlink' => [ 'priority' => 'none' ],
- ] );
- $this->validator = $this->getSignatureValidator();
- $result = $this->validator->validateSignature( $signature );
- if ( $expected === 'hidden' ) {
- $expected = false;
- } elseif ( is_string( $expected ) ) {
- $expected = true;
- }
- $this->assertSame( $expected, $result );
- }
-
public function provideValidateSignature() {
yield 'Perfect' => [
'[[User:SignatureValidatorTest|Signature]] ([[User talk:SignatureValidatorTest|talk]])',
@@ -214,13 +193,6 @@ class SignatureValidatorTest extends MediaWikiIntegrationTestCase {
// This is allowed by SignatureAllowedLintErrors
'allowed'
];
- // Testing hidden category support; 'wikilink-in-extlink' has been
- // made hidden.
- yield 'Wikilink in Extlint (hidden)' => [
- '[http://example.com [[Foo]]!] [[User:SignatureValidatorTest|Signature]] ([[User talk:SignatureValidatorTest|talk]])',
- // This is allowed because the category is hidden
- 'hidden'
- ];
}
/**