Introduce ContentHandlerFactory

Added:
- ContentHandlerFactory
Tests:
- PHPUnit
Changed
- Calls of changed and deprecated
- DI for some service/api
Deprecated:
- ContentHandler::* then similar to ContentHandlerFactory
- ContentHandler::getForTitle
- ContentHandler::$handlers

Bug: T235165
Change-Id: I59246938c7ad7b3e70e46c9e698708ef9bc672c6
This commit is contained in:
ArtBaltai 2020-01-18 23:25:04 +03:00
parent 1394d7c870
commit 30e54b3962
65 changed files with 1137 additions and 212 deletions

View file

@ -100,6 +100,8 @@ For notes on 1.34.x and older releases, see HISTORY.
return their respective policies as a string.
* The ResourceLoaderSiteModulePages and ResourceLoaderSiteStylesModulePages
hooks were added to allow changing which wiki pages these modules contain.
* ContentHandlerFactory for most ContentHandler static methods. It has been
added to the constructors for many classes to improve SOLID / GRASP.
* …
=== External library changes in 1.35 ===
@ -405,6 +407,14 @@ because of Phabricator reports.
* ApiBase::validateLimit() and ApiBase::validateTimestamp() are deprecated.
Use ApiParamValidator::validateValue() with an appropriate settings array
instead.
* ContentHandler (use ContentHandlerFactory):
- getForTitle
- getForContent
- getForModelID
- getContentModels
- getAllContentFormats
- protected $handler (not need anymore)
- cleanupHandlersCache (not need anymore)
* $wgMemc is deprecated, use
MediaWikiServices::getInstance()->getLocalServerObjectCache() instead.
* ImagePage::getImageLimitsFromOptions() is deprecated. Use static function

View file

@ -865,6 +865,8 @@ $wgAutoloadLocalClasses = [
'MediaWiki\\ChangeTags\\Taggable' => __DIR__ . '/includes/changetags/Taggable.php',
'MediaWiki\\Config\\ConfigRepository' => __DIR__ . '/includes/config/ConfigRepository.php',
'MediaWiki\\Config\\ServiceOptions' => __DIR__ . '/includes/config/ServiceOptions.php',
'MediaWiki\\Content\\ContentHandlerFactory' => __DIR__ . '/includes/content/ContentHandlerFactory.php',
'MediaWiki\\Content\\IContentHandlerFactory' => __DIR__ . '/includes/content/IContentHandlerFactory.php',
'MediaWiki\\DB\\PatchFileLocation' => __DIR__ . '/includes/db/PatchFileLocation.php',
'MediaWiki\\Diff\\ComplexityException' => __DIR__ . '/includes/diff/ComplexityException.php',
'MediaWiki\\Diff\\WordAccumulator' => __DIR__ . '/includes/diff/WordAccumulator.php',

View file

@ -21,6 +21,7 @@
*/
use MediaWiki\Block\DatabaseBlock;
use MediaWiki\Content\IContentHandlerFactory;
use MediaWiki\EditPage\TextboxBuilder;
use MediaWiki\EditPage\TextConflictHelper;
use MediaWiki\Logger\LoggerFactory;
@ -472,6 +473,11 @@ class EditPage {
*/
private $editConflictHelper;
/**
* @var IContentHandlerFactory
*/
private $contentHandlerFactory;
/**
* @param Article $article
*/
@ -491,8 +497,10 @@ class EditPage {
$this->contentModel = $this->mTitle->getContentModel();
$handler = ContentHandler::getForModelID( $this->contentModel );
$this->contentFormat = $handler->getDefaultFormat();
$this->contentHandlerFactory = MediaWikiServices::getInstance()->getContentHandlerFactory();
$this->contentFormat = $this->contentHandlerFactory
->getContentHandler( $this->contentModel )
->getDefaultFormat();
$this->editConflictHelperFactory = [ $this, 'newTextConflictHelper' ];
}
@ -555,7 +563,7 @@ class EditPage {
*/
public function isSupportedContentModel( $modelId ) {
return $this->enableApiEditOverride === true ||
ContentHandler::getForModelID( $modelId )->supportsDirectEditing();
$this->contentHandlerFactory->getContentHandler( $modelId )->supportsDirectEditing();
}
/**
@ -896,8 +904,9 @@ class EditPage {
* @return bool True if this edit page supports sections, false otherwise.
*/
protected function isSectionEditSupported() {
$contentHandler = ContentHandler::getForTitle( $this->mTitle );
return $contentHandler->supportsSections();
return $this->contentHandlerFactory
->getContentHandler( $this->mTitle->getContentModel() )
->supportsSections();
}
/**
@ -1082,7 +1091,7 @@ class EditPage {
$this->contentFormat = $request->getText( 'format', $this->contentFormat );
try {
$handler = ContentHandler::getForModelID( $this->contentModel );
$handler = $this->contentHandlerFactory->getContentHandler( $this->contentModel );
} catch ( MWUnknownContentModelException $e ) {
throw new ErrorPageError(
'editpage-invalidcontentmodel-title',
@ -1138,7 +1147,9 @@ class EditPage {
$this->edittime = $this->page->getTimestamp();
$this->editRevId = $this->page->getLatest();
$dummy = ContentHandler::getForModelID( $this->contentModel )->makeEmptyContent();
$dummy = $this->contentHandlerFactory
->getContentHandler( $this->contentModel )
->makeEmptyContent();
$content = $this->getContentObject( $dummy ); # TODO: track content object?!
if ( $content === $dummy ) { // Invalid section
$this->noSuchSectionPage();
@ -1394,8 +1405,9 @@ class EditPage {
}
$revision = $this->mArticle->getRevisionFetched();
if ( $revision === null ) {
$handler = ContentHandler::getForModelID( $this->contentModel );
return $handler->makeEmptyContent();
return $this->contentHandlerFactory
->getContentHandler( $this->contentModel )
->makeEmptyContent();
}
$content = $revision->getContent( RevisionRecord::FOR_THIS_USER, $user );
return $content;
@ -1434,8 +1446,9 @@ class EditPage {
$content = $rev ? $rev->getContent( RevisionRecord::RAW ) : null;
if ( $content === false || $content === null ) {
$handler = ContentHandler::getForModelID( $this->contentModel );
return $handler->makeEmptyContent();
return $this->contentHandlerFactory
->getContentHandler( $this->contentModel )
->makeEmptyContent();
} elseif ( !$this->undidRev ) {
// Content models should always be the same since we error
// out if they are different before this point (in ->edit()).
@ -1495,7 +1508,7 @@ class EditPage {
return $this->mPreloadContent;
}
$handler = ContentHandler::getForModelID( $this->contentModel );
$handler = $this->contentHandlerFactory->getContentHandler( $this->contentModel );
if ( $preload === '' ) {
return $handler->makeEmptyContent();
@ -2460,9 +2473,9 @@ ERROR;
return false;
}
$handler = ContentHandler::getForModelID( $baseContent->getModel() );
$result = $handler->merge3( $baseContent, $editContent, $currentContent );
$result = $this->contentHandlerFactory
->getContentHandler( $baseContent->getModel() )
->merge3( $baseContent, $editContent, $currentContent );
if ( $result ) {
$editContent = $result;
@ -4608,13 +4621,15 @@ ERROR;
/**
* @param string $submitButtonLabel
* @return TextConflictHelper
* @throws MWUnknownContentModelException
*/
private function newTextConflictHelper( $submitButtonLabel ) {
return new TextConflictHelper(
$this->getTitle(),
$this->getContext()->getOutput(),
MediaWikiServices::getInstance()->getStatsdDataFactory(),
$submitButtonLabel
$submitButtonLabel,
MediaWikiServices::getInstance()->getContentHandlerFactory()
);
}
}

View file

@ -21,6 +21,7 @@
* @ingroup Feed
*/
use MediaWiki\MediaWikiServices;
use MediaWiki\Revision\RevisionRecord;
/**
@ -149,7 +150,10 @@ class FeedUtils {
} else {
$rev = Revision::newFromId( $newid );
if ( $wgFeedDiffCutoff <= 0 || $rev === null ) {
$newContent = ContentHandler::getForTitle( $title )->makeEmptyContent();
$newContent = MediaWikiServices::getInstance()
->getContentHandlerFactory()
->getContentHandler( $title->getContentModel() )
->makeEmptyContent();
} else {
$newContent = $rev->getContent();
}

View file

@ -414,7 +414,11 @@ class MediaWiki {
$article = Article::newFromWikiPage( $page, $this->context );
// Skip some unnecessary code if the content model doesn't support redirects
if ( !ContentHandler::getForTitle( $title )->supportsRedirects() ) {
if ( !MediaWikiServices::getInstance()
->getContentHandlerFactory()
->getContentHandler( $title->getContentModel() )
->supportsRedirects()
) {
return $article;
}

View file

@ -29,6 +29,7 @@ use MediaWiki\Block\BlockManager;
use MediaWiki\Block\BlockRestrictionStore;
use MediaWiki\Cache\LinkBatchFactory;
use MediaWiki\Config\ConfigRepository;
use MediaWiki\Content\IContentHandlerFactory;
use MediaWiki\FileBackend\FSFile\TempFSFileFactory;
use MediaWiki\FileBackend\LockManager\LockManagerGroupFactory;
use MediaWiki\Http\HttpRequestFactory;
@ -564,6 +565,14 @@ class MediaWikiServices extends ServiceContainer {
return $this->getService( 'ConfiguredReadOnlyMode' );
}
/**
* @since 1.35
* @return IContentHandlerFactory
*/
public function getContentHandlerFactory() : IContentHandlerFactory {
return $this->getService( 'ContentHandlerFactory' );
}
/**
* @since 1.32
* @return Language

View file

@ -307,11 +307,13 @@ class MergeHistory {
)->inContentLanguage()->text();
}
$contentHandler = ContentHandler::getForTitle( $this->source );
$redirectContent = $contentHandler->makeRedirectContent(
$this->dest,
wfMessage( 'mergehistory-redirect-text' )->inContentLanguage()->plain()
);
$redirectContent = MediaWikiServices::getInstance()
->getContentHandlerFactory()
->getContentHandler( $this->source->getContentModel() )
->makeRedirectContent(
$this->dest,
wfMessage( 'mergehistory-redirect-text' )->inContentLanguage()->plain()
);
if ( $redirectContent ) {
$redirectPage = WikiPage::factory( $this->source );

View file

@ -20,6 +20,7 @@
*/
use MediaWiki\Config\ServiceOptions;
use MediaWiki\Content\IContentHandlerFactory;
use MediaWiki\MediaWikiServices;
use MediaWiki\Page\MovePageFactory;
use MediaWiki\Permissions\PermissionManager;
@ -75,6 +76,11 @@ class MovePage {
*/
protected $repoGroup;
/**
* @var IContentHandlerFactory
*/
private $contentHandlerFactory;
/**
* Calling this directly is deprecated in 1.34. Use MovePageFactory instead.
*
@ -86,6 +92,7 @@ class MovePage {
* @param WatchedItemStoreInterface|null $watchedItems
* @param PermissionManager|null $permMgr
* @param RepoGroup|null $repoGroup
* @param IContentHandlerFactory|null $contentHandlerFactory
*/
public function __construct(
Title $oldTitle,
@ -95,7 +102,8 @@ class MovePage {
NamespaceInfo $nsInfo = null,
WatchedItemStoreInterface $watchedItems = null,
PermissionManager $permMgr = null,
RepoGroup $repoGroup = null
RepoGroup $repoGroup = null,
IContentHandlerFactory $contentHandlerFactory = null
) {
$this->oldTitle = $oldTitle;
$this->newTitle = $newTitle;
@ -109,6 +117,9 @@ class MovePage {
$watchedItems ?? MediaWikiServices::getInstance()->getWatchedItemStore();
$this->permMgr = $permMgr ?? MediaWikiServices::getInstance()->getPermissionManager();
$this->repoGroup = $repoGroup ?? MediaWikiServices::getInstance()->getRepoGroup();
$this->contentHandlerFactory = (
$contentHandlerFactory ?? MediaWikiServices::getInstance()->getContentHandlerFactory()
);
}
/**
@ -205,7 +216,9 @@ class MovePage {
ContentHandler::getLocalizedName( $this->newTitle->getContentModel() )
);
} elseif (
!ContentHandler::getForTitle( $this->oldTitle )->canBeUsedOn( $this->newTitle )
!$this->contentHandlerFactory
->getContentHandler( $this->oldTitle->getContentModel() )
->canBeUsedOn( $this->newTitle )
) {
$status->fatal(
'content-not-allowed-here',
@ -761,9 +774,12 @@ class MovePage {
wfMessage( 'category-move-redirect-override' )
->params( $nt->getPrefixedText() )->inContentLanguage()->plain() );
} else {
$contentHandler = ContentHandler::getForTitle( $this->oldTitle );
$redirectContent = $contentHandler->makeRedirectContent( $nt,
wfMessage( 'move-redirect-text' )->inContentLanguage()->plain() );
$redirectContent = $this->contentHandlerFactory
->getContentHandler( $this->oldTitle->getContentModel() )
->makeRedirectContent(
$nt,
wfMessage( 'move-redirect-text' )->inContentLanguage()->plain()
);
}
// NOTE: If this page's content model does not support redirects, $redirectContent will be null.

View file

@ -789,7 +789,9 @@ class Revision implements IDBAccessObject {
* @return ContentHandler
*/
public function getContentHandler() {
return ContentHandler::getForModelID( $this->getContentModel() );
return MediaWikiServices::getInstance()
->getContentHandlerFactory()
->getContentHandler( $this->getContentModel() );
}
/**

View file

@ -22,9 +22,10 @@
namespace MediaWiki\Revision;
use ContentHandler;
use Hooks;
use MediaWiki\Content\IContentHandlerFactory;
use MediaWiki\Linker\LinkTarget;
use MWUnknownContentModelException;
use Title;
/**
@ -46,13 +47,23 @@ class MainSlotRoleHandler extends SlotRoleHandler {
*/
private $namespaceContentModels;
/**
* @var IContentHandlerFactory
*/
private $contentHandlerFactory;
/**
* @param string[] $namespaceContentModels A mapping of namespaces to content models,
* typically from $wgNamespaceContentModels.
* @param IContentHandlerFactory $contentHandlerFactory
*/
public function __construct( array $namespaceContentModels ) {
public function __construct(
array $namespaceContentModels,
IContentHandlerFactory $contentHandlerFactory
) {
parent::__construct( 'main', CONTENT_MODEL_WIKITEXT );
$this->namespaceContentModels = $namespaceContentModels;
$this->contentHandlerFactory = $contentHandlerFactory;
}
public function supportsArticleCount() {
@ -64,10 +75,12 @@ class MainSlotRoleHandler extends SlotRoleHandler {
* @param LinkTarget $page
*
* @return bool
* @throws MWUnknownContentModelException
*/
public function isAllowedModel( $model, LinkTarget $page ) {
$title = Title::newFromLinkTarget( $page );
$handler = ContentHandler::getForModelID( $model );
$handler = $this->contentHandlerFactory->getContentHandler( $model );
return $handler->canBeUsedOn( $title );
}

View file

@ -36,6 +36,7 @@ use Hooks;
use IDBAccessObject;
use InvalidArgumentException;
use LogicException;
use MediaWiki\Content\IContentHandlerFactory;
use MediaWiki\Linker\LinkTarget;
use MediaWiki\Storage\BlobAccessException;
use MediaWiki\Storage\BlobStore;
@ -138,6 +139,9 @@ class RevisionStore
/** @var SlotRoleRegistry */
private $slotRoleRegistry;
/** @var IContentHandlerFactory */
private $contentHandlerFactory;
/**
* @todo $blobStore should be allowed to be any BlobStore!
*
@ -155,6 +159,7 @@ class RevisionStore
* @param SlotRoleRegistry $slotRoleRegistry
* @param int $mcrMigrationStage An appropriate combination of SCHEMA_COMPAT_XXX flags
* @param ActorMigration $actorMigration
* @param IContentHandlerFactory $contentHandlerFactory
* @param bool|string $dbDomain DB domain of the relevant wiki or false for the current one
*/
public function __construct(
@ -167,6 +172,7 @@ class RevisionStore
SlotRoleRegistry $slotRoleRegistry,
$mcrMigrationStage,
ActorMigration $actorMigration,
IContentHandlerFactory $contentHandlerFactory,
$dbDomain = false
) {
Assert::parameterType( 'string|boolean', $dbDomain, '$dbDomain' );
@ -204,6 +210,7 @@ class RevisionStore
$this->actorMigration = $actorMigration;
$this->dbDomain = $dbDomain;
$this->logger = new NullLogger();
$this->contentHandlerFactory = $contentHandlerFactory;
}
/**
@ -896,7 +903,9 @@ class RevisionStore
$this->assertCrossWikiContentLoadingIsSafe();
$defaultModel = ContentHandler::getDefaultModelFor( $title );
$defaultFormat = ContentHandler::getForModelID( $defaultModel )->getDefaultFormat();
$defaultFormat = $this->contentHandlerFactory
->getContentHandler( $defaultModel )
->getDefaultFormat();
$revisionRow['rev_content_model'] = ( $model === $defaultModel ) ? null : $model;
$revisionRow['rev_content_format'] = ( $format === $defaultFormat ) ? null : $format;
@ -1008,8 +1017,9 @@ class RevisionStore
$roleHandler = $this->slotRoleRegistry->getRoleHandler( $role );
$defaultModel = $roleHandler->getDefaultModel( $title );
$defaultHandler = ContentHandler::getForModelID( $defaultModel );
$defaultFormat = $defaultHandler->getDefaultFormat();
$defaultFormat = $this->contentHandlerFactory
->getContentHandler( $defaultModel )
->getDefaultFormat();
if ( $model != $defaultModel ) {
throw new MWException( "Can't save non-default content model with "
@ -1444,11 +1454,9 @@ class RevisionStore
}
}
// Unserialize content
$handler = ContentHandler::getForModelID( $slot->getModel() );
$content = $handler->unserializeContent( $data, $blobFormat );
return $content;
return $this->contentHandlerFactory
->getContentHandler( $slot->getModel() )
->unserializeContent( $data, $blobFormat );
}
/**

View file

@ -27,6 +27,7 @@ namespace MediaWiki\Revision;
use ActorMigration;
use CommentStore;
use MediaWiki\Content\IContentHandlerFactory;
use MediaWiki\Storage\BlobStoreFactory;
use MediaWiki\Storage\NameTableStoreFactory;
use Psr\Log\LoggerInterface;
@ -75,6 +76,9 @@ class RevisionStoreFactory {
/** @var SlotRoleRegistry */
private $slotRoleRegistry;
/** @var IContentHandlerFactory */
private $contentHandlerFactory;
/**
* @param ILBFactory $dbLoadBalancerFactory
* @param BlobStoreFactory $blobStoreFactory
@ -87,6 +91,7 @@ class RevisionStoreFactory {
* @param LoggerInterface $logger
* @param bool $contentHandlerUseDB see {@link $wgContentHandlerUseDB}. Must be the same
* for all wikis in the cluster. Will go away after MCR migration.
* @param IContentHandlerFactory $contentHandlerFactory
*/
public function __construct(
ILBFactory $dbLoadBalancerFactory,
@ -98,7 +103,8 @@ class RevisionStoreFactory {
ActorMigration $actorMigration,
$migrationStage,
LoggerInterface $logger,
$contentHandlerUseDB
$contentHandlerUseDB,
IContentHandlerFactory $contentHandlerFactory
) {
Assert::parameterType( 'integer', $migrationStage, '$migrationStage' );
$this->dbLoadBalancerFactory = $dbLoadBalancerFactory;
@ -111,6 +117,7 @@ class RevisionStoreFactory {
$this->mcrMigrationStage = $migrationStage;
$this->logger = $logger;
$this->contentHandlerUseDB = $contentHandlerUseDB;
$this->contentHandlerFactory = $contentHandlerFactory;
}
/**
@ -133,6 +140,7 @@ class RevisionStoreFactory {
$this->slotRoleRegistry,
$this->mcrMigrationStage,
$this->actorMigration,
$this->contentHandlerFactory,
$dbDomain
);

View file

@ -53,6 +53,8 @@ use MediaWiki\Block\BlockRestrictionStore;
use MediaWiki\Cache\LinkBatchFactory;
use MediaWiki\Config\ConfigRepository;
use MediaWiki\Config\ServiceOptions;
use MediaWiki\Content\ContentHandlerFactory;
use MediaWiki\Content\IContentHandlerFactory;
use MediaWiki\FileBackend\FSFile\TempFSFileFactory;
use MediaWiki\FileBackend\LockManager\LockManagerGroupFactory;
use MediaWiki\Http\HttpRequestFactory;
@ -168,6 +170,12 @@ return [
);
},
'ContentHandlerFactory' => function ( MediaWikiServices $services ) : IContentHandlerFactory {
$contentHandlerConfig = $services->getMainConfig()->get( 'ContentHandlers' );
return new ContentHandlerFactory( $contentHandlerConfig );
},
'ContentLanguage' => function ( MediaWikiServices $services ) : Language {
return $services->getLanguageFactory()->getLanguage(
$services->getMainConfig()->get( 'LanguageCode' ) );
@ -594,7 +602,8 @@ return [
$services->getNamespaceInfo(),
$services->getWatchedItemStore(),
$services->getPermissionManager(),
$services->getRepoGroup()
$services->getRepoGroup(),
$services->getContentHandlerFactory()
);
},
@ -840,7 +849,8 @@ return [
$services->getActorMigration(),
$config->get( 'MultiContentRevisionSchemaMigrationStage' ),
LoggerFactory::getInstance( 'RevisionStore' ),
$config->get( 'ContentHandlerUseDB' )
$config->get( 'ContentHandlerUseDB' ),
$services->getContentHandlerFactory()
);
return $store;
@ -919,14 +929,16 @@ return [
'SlotRoleRegistry' => function ( MediaWikiServices $services ) : SlotRoleRegistry {
$config = $services->getMainConfig();
$contentHandlerFactory = $services->getContentHandlerFactory();
$registry = new SlotRoleRegistry(
$services->getNameTableStoreFactory()->getSlotRoles()
);
$registry->defineRole( 'main', function () use ( $config ) {
$registry->defineRole( 'main', function () use ( $config, $contentHandlerFactory ) {
return new MainSlotRoleHandler(
$config->get( 'NamespaceContentModels' )
$config->get( 'NamespaceContentModels' ),
$contentHandlerFactory
);
} );

View file

@ -36,6 +36,7 @@ use Language;
use LinksDeletionUpdate;
use LinksUpdate;
use LogicException;
use MediaWiki\Content\IContentHandlerFactory;
use MediaWiki\Edit\PreparedEdit;
use MediaWiki\MediaWikiServices;
use MediaWiki\Revision\MutableRevisionRecord;
@ -49,6 +50,7 @@ use MediaWiki\Revision\SlotRoleRegistry;
use MediaWiki\User\UserIdentity;
use MessageCache;
use MWCallableUpdate;
use MWUnknownContentModelException;
use ParserCache;
use ParserOptions;
use ParserOutput;
@ -267,6 +269,11 @@ class DerivedPageDataUpdater implements IDBAccessObject, LoggerAwareInterface {
],
];
/**
* @var IContentHandlerFactory
*/
private $contentHandlerFactory;
/**
* @param WikiPage $wikiPage ,
* @param RevisionStore $revisionStore
@ -277,6 +284,7 @@ class DerivedPageDataUpdater implements IDBAccessObject, LoggerAwareInterface {
* @param MessageCache $messageCache
* @param Language $contLang
* @param ILBFactory $loadbalancerFactory
* @param IContentHandlerFactory $contentHandlerFactory
*/
public function __construct(
WikiPage $wikiPage,
@ -287,7 +295,8 @@ class DerivedPageDataUpdater implements IDBAccessObject, LoggerAwareInterface {
JobQueueGroup $jobQueueGroup,
MessageCache $messageCache,
Language $contLang,
ILBFactory $loadbalancerFactory
ILBFactory $loadbalancerFactory,
IContentHandlerFactory $contentHandlerFactory
) {
$this->wikiPage = $wikiPage;
@ -301,6 +310,8 @@ class DerivedPageDataUpdater implements IDBAccessObject, LoggerAwareInterface {
// XXX only needed for waiting for replicas to catch up; there should be a narrower
// interface for that.
$this->loadbalancerFactory = $loadbalancerFactory;
$this->contentHandlerFactory = $contentHandlerFactory;
$this->logger = new NullLogger();
}
@ -617,10 +628,11 @@ class DerivedPageDataUpdater implements IDBAccessObject, LoggerAwareInterface {
/**
* @param string $role slot role name
* @return ContentHandler
* @throws MWUnknownContentModelException
*/
private function getContentHandler( $role ) {
// TODO: inject something like a ContentHandlerRegistry
return ContentHandler::getForModelID( $this->getContentModel( $role ) );
private function getContentHandler( $role ): ContentHandler {
return $this->contentHandlerFactory
->getContentHandler( $this->getContentModel( $role ) );
}
private function useMaster() {

View file

@ -33,6 +33,7 @@ use DeferredUpdates;
use Hooks;
use LogicException;
use ManualLogEntry;
use MediaWiki\Content\IContentHandlerFactory;
use MediaWiki\Linker\LinkTarget;
use MediaWiki\Revision\MutableRevisionRecord;
use MediaWiki\Revision\RevisionAccessException;
@ -102,6 +103,11 @@ class PageUpdater {
*/
private $slotRoleRegistry;
/**
* @var IContentHandlerFactory
*/
private $contentHandlerFactory;
/**
* @var boolean see $wgUseAutomaticEditSummaries
* @see $wgUseAutomaticEditSummaries
@ -155,6 +161,7 @@ class PageUpdater {
* @param ILoadBalancer $loadBalancer
* @param RevisionStore $revisionStore
* @param SlotRoleRegistry $slotRoleRegistry
* @param IContentHandlerFactory $contentHandlerFactory
*/
public function __construct(
User $user,
@ -162,7 +169,8 @@ class PageUpdater {
DerivedPageDataUpdater $derivedDataUpdater,
ILoadBalancer $loadBalancer,
RevisionStore $revisionStore,
SlotRoleRegistry $slotRoleRegistry
SlotRoleRegistry $slotRoleRegistry,
IContentHandlerFactory $contentHandlerFactory
) {
$this->user = $user;
$this->wikiPage = $wikiPage;
@ -171,6 +179,7 @@ class PageUpdater {
$this->loadBalancer = $loadBalancer;
$this->revisionStore = $revisionStore;
$this->slotRoleRegistry = $slotRoleRegistry;
$this->contentHandlerFactory = $contentHandlerFactory;
$this->slotsUpdate = new RevisionSlotsUpdate();
}
@ -533,7 +542,6 @@ class PageUpdater {
* @return ContentHandler
*/
private function getContentHandler( $role ) {
// TODO: inject something like a ContentHandlerRegistry
if ( $this->slotsUpdate->isModifiedSlot( $role ) ) {
$slot = $this->slotsUpdate->getModifiedSlot( $role );
} else {
@ -546,7 +554,7 @@ class PageUpdater {
}
}
return ContentHandler::getForModelID( $slot->getModel() );
return $this->contentHandlerFactory->getContentHandler( $slot->getModel() );
}
/**
@ -657,7 +665,8 @@ class PageUpdater {
$roleHandler = $this->slotRoleRegistry->getRoleHandler( $role );
if ( !$roleHandler->isAllowedModel( $slot->getModel(), $this->getTitle() ) ) {
$contentHandler = ContentHandler::getForModelID( $slot->getModel() );
$contentHandler = $this->contentHandlerFactory
->getContentHandler( $slot->getModel() );
$this->status = Status::newFatal( 'content-not-allowed-here',
ContentHandler::getLocalizedName( $contentHandler->getModelID() ),
$this->getTitle()->getPrefixedText(),

View file

@ -4613,7 +4613,9 @@ class Title implements LinkTarget, IDBAccessObject {
// content to determine the page language!
// Checking $wgLanguageCode hasn't changed for the benefit of unit
// tests.
$contentHandler = ContentHandler::getForTitle( $this );
$contentHandler = MediaWikiServices::getInstance()
->getContentHandlerFactory()
->getContentHandler( $this->getContentModel() );
$langObj = $contentHandler->getPageLanguage( $this );
$this->mPageLanguage = [ $langObj->getCode(), $wgLanguageCode ];
} else {
@ -4663,7 +4665,9 @@ class Title implements LinkTarget, IDBAccessObject {
// @note Can't be cached persistently, depends on user settings.
// @note ContentHandler::getPageViewLanguage() may need to load the
// content to determine the page language!
$contentHandler = ContentHandler::getForTitle( $this );
$contentHandler = MediaWikiServices::getInstance()
->getContentHandlerFactory()
->getContentHandler( $this->getContentModel() );
$pageLang = $contentHandler->getPageViewLanguage( $this );
return $pageLang;
}

View file

@ -19,6 +19,7 @@
* @file
*/
use MediaWiki\Content\IContentHandlerFactory;
use MediaWiki\MediaWikiServices;
use MediaWiki\Revision\MutableRevisionRecord;
use MediaWiki\Revision\RevisionArchiveRecord;
@ -39,10 +40,14 @@ class ApiComparePages extends ApiBase {
private $guessedTitle = false, $props;
/** @var IContentHandlerFactory */
private $contentHandlerFactory;
public function __construct( ApiMain $mainModule, $moduleName, $modulePrefix = '' ) {
parent::__construct( $mainModule, $moduleName, $modulePrefix );
$this->revisionStore = MediaWikiServices::getInstance()->getRevisionStore();
$this->slotRoleRegistry = MediaWikiServices::getInstance()->getSlotRoleRegistry();
$this->contentHandlerFactory = MediaWikiServices::getInstance()->getContentHandlerFactory();
}
public function execute() {
@ -657,11 +662,11 @@ class ApiComparePages extends ApiBase {
],
'contentformat-{slot}' => [
ApiBase::PARAM_TEMPLATE_VARS => [ 'slot' => 'slots' ], // fixed below
ApiBase::PARAM_TYPE => ContentHandler::getAllContentFormats(),
ApiBase::PARAM_TYPE => $this->getContentHandlerFactory()->getAllContentFormats(),
],
'contentmodel-{slot}' => [
ApiBase::PARAM_TEMPLATE_VARS => [ 'slot' => 'slots' ], // fixed below
ApiBase::PARAM_TYPE => ContentHandler::getContentModels(),
ApiBase::PARAM_TYPE => $this->getContentHandlerFactory()->getContentModels(),
],
'pst' => false,
@ -670,11 +675,11 @@ class ApiComparePages extends ApiBase {
ApiBase::PARAM_DEPRECATED => true,
],
'contentformat' => [
ApiBase::PARAM_TYPE => ContentHandler::getAllContentFormats(),
ApiBase::PARAM_TYPE => $this->getContentHandlerFactory()->getAllContentFormats(),
ApiBase::PARAM_DEPRECATED => true,
],
'contentmodel' => [
ApiBase::PARAM_TYPE => ContentHandler::getContentModels(),
ApiBase::PARAM_TYPE => $this->getContentHandlerFactory()->getContentModels(),
ApiBase::PARAM_DEPRECATED => true,
],
'section' => [
@ -735,4 +740,8 @@ class ApiComparePages extends ApiBase {
=> 'apihelp-compare-example-1',
];
}
private function getContentHandlerFactory(): IContentHandlerFactory {
return $this->contentHandlerFactory;
}
}

View file

@ -20,6 +20,7 @@
* @file
*/
use MediaWiki\Content\IContentHandlerFactory;
use MediaWiki\MediaWikiServices;
use MediaWiki\Revision\RevisionRecord;
@ -99,7 +100,8 @@ class ApiEditPage extends ApiBase {
if ( !isset( $params['contentmodel'] ) || $params['contentmodel'] == '' ) {
$contentHandler = $pageObj->getContentHandler();
} else {
$contentHandler = ContentHandler::getForModelID( $params['contentmodel'] );
$contentHandler = $this->getContentHandlerFactory()
->getContentHandler( $params['contentmodel'] );
}
$contentModel = $contentHandler->getModelID();
@ -592,10 +594,10 @@ class ApiEditPage extends ApiBase {
ApiBase::PARAM_DFLT => false,
],
'contentformat' => [
ApiBase::PARAM_TYPE => ContentHandler::getAllContentFormats(),
ApiBase::PARAM_TYPE => $this->getContentHandlerFactory()->getAllContentFormats(),
],
'contentmodel' => [
ApiBase::PARAM_TYPE => ContentHandler::getContentModels(),
ApiBase::PARAM_TYPE => $this->getContentHandlerFactory()->getContentModels(),
],
'token' => [
// Standard definition automatically inserted
@ -625,4 +627,8 @@ class ApiEditPage extends ApiBase {
public function getHelpUrls() {
return 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Edit';
}
private function getContentHandlerFactory(): IContentHandlerFactory {
return MediaWikiServices::getInstance()->getContentHandlerFactory();
}
}

View file

@ -20,6 +20,7 @@
* @file
*/
use MediaWiki\Content\IContentHandlerFactory;
use MediaWiki\MediaWikiServices;
use MediaWiki\Revision\RevisionRecord;
@ -897,11 +898,11 @@ class ApiParse extends ApiBase {
ApiBase::PARAM_TYPE => array_keys( Skin::getAllowedSkins() ),
],
'contentformat' => [
ApiBase::PARAM_TYPE => ContentHandler::getAllContentFormats(),
ApiBase::PARAM_TYPE => $this->getContentHandlerFactory()->getAllContentFormats(),
],
'contentmodel' => [
ApiBase::PARAM_TYPE => ContentHandler::getContentModels(),
]
ApiBase::PARAM_TYPE => $this->getContentHandlerFactory()->getContentModels(),
],
];
}
@ -921,4 +922,8 @@ class ApiParse extends ApiBase {
public function getHelpUrls() {
return 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Parsing_wikitext#parse';
}
private function getContentHandlerFactory(): IContentHandlerFactory {
return MediaWikiServices::getInstance()->getContentHandlerFactory();
}
}

View file

@ -20,6 +20,7 @@
* @file
*/
use MediaWiki\Content\IContentHandlerFactory;
use MediaWiki\Logger\LoggerFactory;
use MediaWiki\MediaWikiServices;
use MediaWiki\Revision\RevisionAccessException;
@ -610,7 +611,9 @@ abstract class ApiQueryRevisionsBase extends ApiQueryGeneratorBase {
$model = $title->getContentModel();
if ( $this->contentFormat
&& !ContentHandler::getForModelID( $model )->isSupportedFormat( $this->contentFormat )
&& !$this->getContentHandlerFactory()
->getContentHandler( $model )
->isSupportedFormat( $this->contentFormat )
) {
$name = wfEscapeWikiText( $title->getPrefixedText() );
$this->addWarning( [ 'apierror-badformat', $this->contentFormat, $model, $name ] );
@ -755,11 +758,14 @@ abstract class ApiQueryRevisionsBase extends ApiQueryGeneratorBase {
ApiBase::PARAM_DEPRECATED => true,
],
'contentformat' => [
ApiBase::PARAM_TYPE => ContentHandler::getAllContentFormats(),
ApiBase::PARAM_TYPE => $this->getContentHandlerFactory()->getAllContentFormats(),
ApiBase::PARAM_HELP_MSG => 'apihelp-query+revisions+base-param-contentformat',
ApiBase::PARAM_DEPRECATED => true,
],
];
}
private function getContentHandlerFactory(): IContentHandlerFactory {
return MediaWikiServices::getInstance()->getContentHandlerFactory();
}
}

View file

@ -18,6 +18,7 @@
* @file
*/
use MediaWiki\Content\IContentHandlerFactory;
use MediaWiki\MediaWikiServices;
use MediaWiki\Storage\PageEditStash;
@ -53,7 +54,8 @@ class ApiStashEdit extends ApiBase {
$page = $this->getTitleOrPageId( $params );
$title = $page->getTitle();
if ( !ContentHandler::getForModelID( $params['contentmodel'] )
if ( !$this->getContentHandlerFactory()
->getContentHandler( $params['contentmodel'] )
->isSupportedFormat( $params['contentformat'] )
) {
$this->dieWithError(
@ -117,8 +119,9 @@ class ApiStashEdit extends ApiBase {
if ( !$baseContent || !$currentContent ) {
$this->dieWithError( [ 'apierror-missingcontent-pageid', $page->getId() ], 'missingrev' );
}
$handler = ContentHandler::getForModelID( $baseContent->getModel() );
$content = $handler->merge3( $baseContent, $editContent, $currentContent );
$content = $this->getContentHandlerFactory()
->getContentHandler( $baseContent->getModel() )
->merge3( $baseContent, $editContent, $currentContent );
}
} else {
// New pages: use the user-provided content model
@ -189,11 +192,11 @@ class ApiStashEdit extends ApiBase {
ApiBase::PARAM_TYPE => 'string',
],
'contentmodel' => [
ApiBase::PARAM_TYPE => ContentHandler::getContentModels(),
ApiBase::PARAM_TYPE => $this->getContentHandlerFactory()->getContentModels(),
ApiBase::PARAM_REQUIRED => true
],
'contentformat' => [
ApiBase::PARAM_TYPE => ContentHandler::getAllContentFormats(),
ApiBase::PARAM_TYPE => $this->getContentHandlerFactory()->getAllContentFormats(),
ApiBase::PARAM_REQUIRED => true
],
'baserevid' => [
@ -218,4 +221,8 @@ class ApiStashEdit extends ApiBase {
public function isInternal() {
return true;
}
private function getContentHandlerFactory(): IContentHandlerFactory {
return MediaWikiServices::getInstance()->getContentHandlerFactory();
}
}

View file

@ -26,6 +26,9 @@
* @author Daniel Kinzler
*/
use MediaWiki\Content\IContentHandlerFactory;
use MediaWiki\MediaWikiServices;
/**
* Base implementation for content objects.
*
@ -86,7 +89,14 @@ abstract class AbstractContent implements Content {
* @return ContentHandler
*/
public function getContentHandler() {
return ContentHandler::getForContent( $this );
return $this->getContentHandlerFactory()->getContentHandler( $this->getModel() );
}
/**
* @return IContentHandlerFactory
*/
protected function getContentHandlerFactory(): IContentHandlerFactory {
return MediaWikiServices::getInstance()->getContentHandlerFactory();
}
/**

View file

@ -130,7 +130,8 @@ abstract class ContentHandler {
*
* @throws MWException If model ID or format is not supported or if the text can not be
* unserialized using the format.
* @return Content A Content object representing the text.
* @throws MWContentSerializationException
* @return Content A Content object representing the text. *
*/
public static function makeContent( $text, Title $title = null,
$modelId = null, $format = null ) {
@ -142,9 +143,10 @@ abstract class ContentHandler {
$modelId = $title->getContentModel();
}
$handler = self::getForModelID( $modelId );
return $handler->unserializeContent( $text, $format );
return MediaWikiServices::getInstance()
->getContentHandlerFactory()
->getContentHandler( $modelId )
->unserializeContent( $text, $format );
}
/**
@ -193,38 +195,42 @@ abstract class ContentHandler {
* Returns the appropriate ContentHandler singleton for the given title.
*
* @since 1.21
* @deprecated since 1.35, instead use
* ContentHandlerFactory::getContentHandler( $title->getContentModel() ).
*
* @param Title $title
*
* @return ContentHandler
* @throws MWException
* @throws MWUnknownContentModelException
*/
public static function getForTitle( Title $title ) {
$modelId = $title->getContentModel();
return self::getForModelID( $modelId );
return MediaWikiServices::getInstance()
->getContentHandlerFactory()
->getContentHandler( $title->getContentModel() );
}
/**
* Returns the appropriate ContentHandler singleton for the given Content
* object.
*
* @deprecated since 1.35, instead use
* ContentHandlerFactory::getContentHandler( $content->getModel() ).
*
* @since 1.21
*
* @param Content $content
*
* @return ContentHandler
* @throws MWException
* @throws MWUnknownContentModelException
*/
public static function getForContent( Content $content ) {
$modelId = $content->getModel();
return self::getForModelID( $modelId );
return MediaWikiServices::getInstance()
->getContentHandlerFactory()
->getContentHandler( $content->getModel() );
}
/**
* @var array A Cache of ContentHandler instances by model id
*/
protected static $handlers;
/**
* Returns the ContentHandler singleton for the given model ID. Use the
* CONTENT_MODEL_XXX constants to identify the desired content model.
@ -244,6 +250,9 @@ abstract class ContentHandler {
*
* @since 1.21
*
* @deprecated since 1.35, use ContentHandlerFactory::getContentHandler
* @see ContentHandlerFactory::getContentHandler()
*
* @param string $modelId The ID of the content model for which to get a
* handler. Use CONTENT_MODEL_XXX constants.
*
@ -252,54 +261,20 @@ abstract class ContentHandler {
* @return ContentHandler The ContentHandler singleton for handling the model given by the ID.
*/
public static function getForModelID( $modelId ) {
global $wgContentHandlers;
if ( isset( self::$handlers[$modelId] ) ) {
return self::$handlers[$modelId];
}
if ( empty( $wgContentHandlers[$modelId] ) ) {
$handler = null;
Hooks::run( 'ContentHandlerForModelID', [ $modelId, &$handler ] );
if ( $handler === null ) {
throw new MWUnknownContentModelException( $modelId );
}
if ( !( $handler instanceof ContentHandler ) ) {
throw new MWException( "ContentHandlerForModelID must supply a ContentHandler instance" );
}
} else {
$classOrCallback = $wgContentHandlers[$modelId];
if ( is_callable( $classOrCallback ) ) {
$handler = call_user_func( $classOrCallback, $modelId );
} else {
$handler = new $classOrCallback( $modelId );
}
if ( !( $handler instanceof ContentHandler ) ) {
throw new MWException(
var_export( $classOrCallback, true ) . " from \$wgContentHandlers is not " .
"compatible with ContentHandler"
);
}
}
wfDebugLog( 'ContentHandler', 'Created handler for ' . $modelId
. ': ' . get_class( $handler ) );
self::$handlers[$modelId] = $handler;
return self::$handlers[$modelId];
return MediaWikiServices::getInstance()
->getContentHandlerFactory()
->getContentHandler( $modelId );
}
/**
* @deprecated since 1.35 Please, use ContentHandlerFactory. Cleanup is not needed
* @see ContentHandlerFactory
*
* Clean up handlers cache.
*/
public static function cleanupHandlersCache() {
self::$handlers = [];
// No-op: no longer needed, since the instance cache is in the
// ContentHandlerFactory service, and services get reset between tests
}
/**
@ -328,27 +303,28 @@ abstract class ContentHandler {
return $msg->exists() ? $msg->plain() : $name;
}
/**
* @deprecated since 1.35, use ContentHandlerFactory::getContentModels
* @see ContentHandlerFactory::getContentModels
*
* @return string[]
* @throws MWException
* @throws MWUnknownContentModelException
*/
public static function getContentModels() {
global $wgContentHandlers;
$models = array_keys( $wgContentHandlers );
Hooks::run( 'GetContentModels', [ &$models ] );
return $models;
return MediaWikiServices::getInstance()->getContentHandlerFactory()->getContentModels();
}
/**
* @return string[]
* @throws MWException
* @throws MWUnknownContentModelException
*
* @deprecated since 1.35, use ContentHandlerFactory::getAllContentFormats
* @see ContentHandlerFactory::getAllContentFormats
*/
public static function getAllContentFormats() {
global $wgContentHandlers;
$formats = [];
foreach ( $wgContentHandlers as $model => $class ) {
$handler = self::getForModelID( $model );
$formats = array_merge( $formats, $handler->getSupportedFormats() );
}
$formats = array_unique( $formats );
return $formats;
return MediaWikiServices::getInstance()->getContentHandlerFactory()->getAllContentFormats();
}
// ------------------------------------------------------------------------
@ -412,6 +388,7 @@ abstract class ContentHandler {
* @param string|null $format The format used for serialization
*
* @return Content The Content object created by deserializing $blob
* @throws MWContentSerializationException
*/
abstract public function unserializeContent( $blob, $format = null );

View file

@ -0,0 +1,212 @@
<?php
namespace MediaWiki\Content;
use ContentHandler;
use FatalError;
use Hooks;
use MWException;
use MWUnknownContentModelException;
final class ContentHandlerFactory implements IContentHandlerFactory {
/**
* @var string[]|callable[]
*/
private $handlerSpecs = [];
/**
* @var ContentHandler[] Registry of ContentHandler instances by model id
*/
private $handlersByModel = [];
/**
* ContentHandlerFactory constructor.
*
* @param string[]|callable[] $handlerSpecs ClassName for resolve or Callable resolver
*
* @see \$wgContentHandlers
*/
public function __construct( array $handlerSpecs ) {
$this->handlerSpecs = $handlerSpecs;
}
/**
* @param string $modelID
*
* @return ContentHandler
* @throws MWException For internal errors and problems in the configuration.
* @throws MWUnknownContentModelException If no handler is known for the model ID.
*/
public function getContentHandler( string $modelID ): ContentHandler {
if ( empty( $this->handlersByModel[$modelID] ) ) {
$contentHandler = $this->createForModelID( $modelID );
wfDebugLog( __METHOD__,
"Registered handler for {$modelID}: " . get_class( $contentHandler ) );
$this->handlersByModel[$modelID] = $contentHandler;
}
return $this->handlersByModel[$modelID];
}
/**
* Define HandlerSpec for ModelID.
* @param string $modelID
* @param callable|string $handlerSpec
*
* @throws MWException
* @internal
*
*/
public function defineContentHandler( string $modelID, $handlerSpec ): void {
if ( !is_callable( $handlerSpec ) && !is_string( $handlerSpec ) ) {
throw new MWException(
"ContentHandler Spec for modelID '{$modelID}' must be callable or class name"
);
}
unset( $this->handlersByModel[$modelID] );
$this->handlerSpecs[$modelID] = $handlerSpec;
}
/**
* Get defined ModelIDs
*
* @return string[]
* @throws MWException
* @throws FatalError
*/
public function getContentModels(): array {
$models = array_keys( $this->handlerSpecs );
Hooks::run( self::HOOK_NAME_GET_CONTENT_MODELS, [ &$models ] );
return $models;
}
/**
* @return string[]
* @throws MWException
*/
public function getAllContentFormats(): array {
$formats = [];
foreach ( $this->handlerSpecs as $model => $class ) {
$formats += array_flip( $this->getContentHandler( $model )->getSupportedFormats() );
}
return array_keys( $formats );
}
/**
* @param string $modelID
*
* @return bool
* @throws MWException
*/
public function isDefinedModel( string $modelID ): bool {
return in_array( $modelID, $this->getContentModels(), true );
}
/**
* Register ContentHandler for ModelID
*
* @param string $modelID
* @param ContentHandler $contentHandler
*/
private function registerForModelID( string $modelID, ContentHandler $contentHandler ): void {
wfDebugLog(
__METHOD__,
"Registered handler for {$modelID}: " . get_class( $contentHandler )
. ( !empty( $this->handlersByModel[$modelID] ) ? ' (replace old)' : null )
);
$this->handlersByModel[$modelID] = $contentHandler;
}
/**
* Create ContentHandler for ModelID
*
* @param string $modelID The ID of the content model for which to get a handler.
* Use CONTENT_MODEL_XXX constants.
*
* @return ContentHandler The ContentHandler singleton for handling the model given by the ID.
*
* @throws MWUnknownContentModelException If no handler is known for the model ID.
* @throws MWException For internal errors and problems in the configuration.
*/
private function createForModelID( string $modelID ): ContentHandler {
$handlerSpec = $this->handlerSpecs[$modelID] ?? null;
if ( $handlerSpec !== null ) {
return $this->createContentHandlerFromHandlerSpec( $modelID, $handlerSpec );
}
return $this->createContentHandlerFromHook( $modelID );
}
/**
* @param string $modelID
* @param ContentHandler $contentHandler
*
* @throws MWException
* @throws MWUnknownContentModelException
*/
private function validateContentHandler( string $modelID, $contentHandler ): void {
if ( $contentHandler === null ) {
throw new MWUnknownContentModelException( $modelID );
}
if ( !is_object( $contentHandler ) ) {
throw new MWException(
"ContentHandler for model {$modelID} wrong: non-object given."
);
}
if ( !$contentHandler instanceof ContentHandler ) {
throw new MWException(
"ContentHandler for model {$modelID} must supply a ContentHandler instance, "
. get_class( $contentHandler ) . 'given.'
);
}
}
/**
* @param string $modelID
* @param callable|string $handlerSpec
*
* @return ContentHandler
* @throws MWException
* @throws MWUnknownContentModelException
*/
private function createContentHandlerFromHandlerSpec(
string $modelID, $handlerSpec
): ContentHandler {
$contentHandler = null;
if ( is_string( $handlerSpec ) ) {
$contentHandler = new $handlerSpec( $modelID );
} elseif ( is_callable( $handlerSpec ) ) {
$contentHandler = call_user_func( $handlerSpec, $modelID );
} else {
throw new MWException( "Wrong Argument HandlerSpec for ModelID: {$modelID}." );
}
$this->validateContentHandler( $modelID, $contentHandler );
return $contentHandler;
}
/**
* @param string $modelID
*
* @return ContentHandler
* @throws MWException
* @throws MWUnknownContentModelException
*/
private function createContentHandlerFromHook( string $modelID ): ContentHandler {
$contentHandler = null;
Hooks::run( self::HOOK_NAME_BY_MODEL_NAME, [ $modelID, &$contentHandler ] );
$this->validateContentHandler( $modelID, $contentHandler );
'@phan-var ContentHandler $contentHandler';
return $contentHandler;
}
}

View file

@ -0,0 +1,54 @@
<?php
namespace MediaWiki\Content;
use ContentHandler;
use FatalError;
use MWException;
use MWUnknownContentModelException;
interface IContentHandlerFactory {
public const HOOK_NAME_BY_MODEL_NAME = 'ContentHandlerForModelID';
public const HOOK_NAME_GET_CONTENT_MODELS = 'GetContentModels';
/**
* Returns a ContentHandler instance for the given $modelID.
*
* @param string $modelID
*
* @return ContentHandler
* @throws MWException For internal errors and problems in the configuration.
* @throws MWUnknownContentModelException If no handler is known for the model ID.
*/
public function getContentHandler( string $modelID ): ContentHandler;
/**
* Returns a list of defined content models.
* getContentHandler() can be expected to return a ContentHandler for the models returned
* by this method.
*
* @return string[]
* @throws MWException
* @throws FatalError
*/
public function getContentModels(): array;
/**
* Returns a list of all serialization formats supported for any of the defined content models.
* @see ContentHandler::getSupportedFormats()
* @return string[]
* @throws MWException
*/
public function getAllContentFormats(): array;
/**
* Returns true if $modelID is a defined content model for which getContentHandler() can be
* expected to return a ContentHandler instance.
* @param string $modelID
*
* @return bool
*/
public function isDefinedModel( string $modelID ): bool;
}

View file

@ -303,6 +303,7 @@ class TextContent extends AbstractContent {
*
* @return Content|bool A content object with the content model $toModel, or false if that
* conversion is not supported.
* @throws MWUnknownContentModelException
*
* @see Content::convert()
*/
@ -313,7 +314,7 @@ class TextContent extends AbstractContent {
return $converted;
}
$toHandler = ContentHandler::getForModelID( $toModel );
$toHandler = $this->getContentHandlerFactory()->getContentHandler( $toModel );
if ( $toHandler instanceof TextContentHandler ) {
// NOTE: ignore content serialization format - it's just text anyway.

View file

@ -21,6 +21,7 @@
* @ingroup DifferenceEngine
*/
use MediaWiki\Content\IContentHandlerFactory;
use MediaWiki\Linker\LinkRenderer;
use MediaWiki\MediaWikiServices;
use MediaWiki\Revision\RevisionRecord;
@ -208,6 +209,11 @@ class DifferenceEngine extends ContextSource {
*/
protected $linkRenderer;
/**
* @var IContentHandlerFactory
*/
private $contentHandlerFactory;
/** #@- */
/**
@ -244,6 +250,7 @@ class DifferenceEngine extends ContextSource {
$this->mRefreshCache = $refreshCache;
$this->unhide = $unhide;
$this->linkRenderer = MediaWikiServices::getInstance()->getLinkRenderer();
$this->contentHandlerFactory = MediaWikiServices::getInstance()->getContentHandlerFactory();
}
/**
@ -1352,7 +1359,8 @@ class DifferenceEngine extends ContextSource {
* @deprecated since 1.32, use a TextSlotDiffRenderer instead.
*/
public function generateTextDiffBody( $otext, $ntext ) {
$slotDiffRenderer = ContentHandler::getForModelID( CONTENT_MODEL_TEXT )
$slotDiffRenderer = $this->contentHandlerFactory
->getContentHandler( CONTENT_MODEL_TEXT )
->getSlotDiffRenderer( $this->getContext() );
if ( !( $slotDiffRenderer instanceof TextSlotDiffRenderer ) ) {
// Someone used the GetSlotDiffRenderer hook to replace the renderer.
@ -1432,7 +1440,8 @@ class DifferenceEngine extends ContextSource {
* @deprecated since 1.32, use a TextSlotDiffRenderer instead.
*/
protected function textDiff( $otext, $ntext ) {
$slotDiffRenderer = ContentHandler::getForModelID( CONTENT_MODEL_TEXT )
$slotDiffRenderer = $this->contentHandlerFactory
->getContentHandler( CONTENT_MODEL_TEXT )
->getSlotDiffRenderer( $this->getContext() );
if ( !( $slotDiffRenderer instanceof TextSlotDiffRenderer ) ) {
// Someone used the GetSlotDiffRenderer hook to replace the renderer.

View file

@ -20,6 +20,7 @@
* @file
* @ingroup DifferenceEngine
*/
use MediaWiki\MediaWikiServices;
/**
* B/C adapter for turning a DifferenceEngine into a SlotDiffRenderer.
@ -38,7 +39,10 @@ class DifferenceEngineSlotDiffRenderer extends SlotDiffRenderer {
// Set state to loaded. This should not matter to any of the methods invoked by
// the adapter, but just in case a load does get triggered somehow, make sure it's a no-op.
$fakeContent = ContentHandler::getForModelID( CONTENT_MODEL_WIKITEXT )->makeEmptyContent();
$fakeContent = MediaWikiServices::getInstance()
->getContentHandlerFactory()
->getContentHandler( CONTENT_MODEL_WIKITEXT )
->makeEmptyContent();
$this->differenceEngine->setContent( $fakeContent, $fakeContent );
$this->differenceEngine->markAsSlotDiffRenderer();

View file

@ -21,6 +21,7 @@
* @ingroup DifferenceEngine
*/
use MediaWiki\MediaWikiServices;
use MediaWiki\Shell\Shell;
use Wikimedia\Assert\Assert;
@ -79,7 +80,9 @@ class TextSlotDiffRenderer extends SlotDiffRenderer {
*/
public static function diff( $oldText, $newText ) {
/** @var TextSlotDiffRenderer $slotDiffRenderer */
$slotDiffRenderer = ContentHandler::getForModelID( CONTENT_MODEL_TEXT )
$slotDiffRenderer = MediaWikiServices::getInstance()
->getContentHandlerFactory()
->getContentHandler( CONTENT_MODEL_TEXT )
->getSlotDiffRenderer( RequestContext::getMain() );
'@phan-var TextSlotDiffRenderer $slotDiffRenderer';
return $slotDiffRenderer->getTextDiff( $oldText, $newText );

View file

@ -28,6 +28,9 @@ use Content;
use ContentHandler;
use Html;
use IBufferingStatsdDataFactory;
use MediaWiki\Content\IContentHandlerFactory;
use MediaWiki\MediaWikiServices;
use MWUnknownContentModelException;
use OutputPage;
use Title;
use User;
@ -48,12 +51,12 @@ class TextConflictHelper {
/**
* @var null|string
*/
public $contentModel = null;
public $contentModel;
/**
* @var null|string
*/
public $contentFormat = null;
public $contentFormat;
/**
* @var OutputPage
@ -80,21 +83,39 @@ class TextConflictHelper {
*/
protected $storedversion = '';
/**
* @var IContentHandlerFactory
*/
private $contentHandlerFactory;
/**
* @param Title $title
* @param OutputPage $out
* @param IBufferingStatsdDataFactory $stats
* @param string $submitLabel
* @param IContentHandlerFactory|null $contentHandlerFactory Required param with legacy support
*
* @throws MWUnknownContentModelException
*/
public function __construct( Title $title, OutputPage $out, IBufferingStatsdDataFactory $stats,
$submitLabel
$submitLabel, ?IContentHandlerFactory $contentHandlerFactory = null
) {
$this->title = $title;
$this->out = $out;
$this->stats = $stats;
$this->submitLabel = $submitLabel;
$this->contentModel = $title->getContentModel();
$this->contentFormat = ContentHandler::getForModelID( $this->contentModel )->getDefaultFormat();
if ( $contentHandlerFactory === null ) {
//wfDeprecated( __METHOD__, '1.35' );
$this->contentHandlerFactory = MediaWikiServices::getInstance()
->getContentHandlerFactory();
} else {
$this->contentHandlerFactory = $contentHandlerFactory;
}
$this->contentFormat = $this->contentHandlerFactory
->getContentHandler( $this->contentModel )
->getDefaultFormat();
}
/**
@ -248,7 +269,7 @@ class TextConflictHelper {
$yourContent = $this->toEditContent( $this->yourtext );
$storedContent = $this->toEditContent( $this->storedversion );
$handler = ContentHandler::getForModelID( $this->contentModel );
$handler = $this->contentHandlerFactory->getContentHandler( $this->contentModel );
$diffEngine = $handler->createDifferenceEngine( $this->out );
$diffEngine->setContent( $yourContent, $storedContent );

View file

@ -443,7 +443,9 @@ class XmlDumpWriter {
}
$contentModel = $slot->getModel();
$contentHandler = ContentHandler::getForModelID( $contentModel );
$contentHandler = MediaWikiServices::getInstance()
->getContentHandlerFactory()
->getContentHandler( $contentModel );
$contentFormat = $contentHandler->getDefaultFormat();
// XXX: The content format is only relevant when actually outputting serialized content.

View file

@ -438,10 +438,13 @@ class WikiRevision implements ImportableUploadRevision, ImportableOldRevision {
/**
* @since 1.24
* @return ContentHandler
* @throws MWUnknownContentModelException
*/
public function getContentHandler() {
if ( $this->contentHandler === null ) {
$this->contentHandler = ContentHandler::getForModelID( $this->getModel() );
$this->contentHandler = MediaWikiServices::getInstance()
->getContentHandlerFactory()
->getContentHandler( $this->getModel() );
}
return $this->contentHandler;

View file

@ -18,6 +18,7 @@
* @file
*/
use MediaWiki\MediaWikiServices;
use Wikimedia\Http\HttpAcceptNegotiator;
use Wikimedia\Http\HttpAcceptParser;
@ -119,8 +120,10 @@ class PageDataRequestHandler {
Title $title,
$revision = 0
) {
$contentHandler = ContentHandler::getForTitle( $title );
$mimeTypes = $contentHandler->getSupportedFormats();
$mimeTypes = MediaWikiServices::getInstance()
->getContentHandlerFactory()
->getContentHandler( $title->getContentModel() )
->getSupportedFormats();
$acceptHeader = $request->getHeader( 'Accept' );
if ( $acceptHeader !== false ) {

View file

@ -21,6 +21,7 @@
*/
use MediaWiki\Block\DatabaseBlock;
use MediaWiki\Content\IContentHandlerFactory;
use MediaWiki\Edit\PreparedEdit;
use MediaWiki\Linker\LinkRenderer;
use MediaWiki\MediaWikiServices;
@ -3087,9 +3088,12 @@ class Article implements Page {
*/
public function generateReason( &$hasHistory ) {
$title = $this->mPage->getTitle();
$handler = ContentHandler::getForTitle( $title );
$handler = $this->getContentHandlerFactory()->getContentHandler( $title->getContentModel() );
return $handler->getAutoDeleteReason( $title, $hasHistory );
}
// ******
private function getContentHandlerFactory(): IContentHandlerFactory {
return MediaWikiServices::getInstance()->getContentHandlerFactory();
}
}

View file

@ -22,6 +22,7 @@
namespace MediaWiki\Page;
use MediaWiki\Config\ServiceOptions;
use MediaWiki\Content\IContentHandlerFactory;
use MediaWiki\Permissions\PermissionManager;
use MovePage;
use NamespaceInfo;
@ -52,6 +53,9 @@ class MovePageFactory {
/** @var RepoGroup */
private $repoGroup;
/** @var IContentHandlerFactory */
private $contentHandlerFactory;
public const CONSTRUCTOR_OPTIONS = [
'CategoryCollation',
'ContentHandlerUseDB',
@ -63,7 +67,8 @@ class MovePageFactory {
NamespaceInfo $nsInfo,
WatchedItemStoreInterface $watchedItems,
PermissionManager $permMgr,
RepoGroup $repoGroup
RepoGroup $repoGroup,
IContentHandlerFactory $contentHandlerFactory
) {
$options->assertRequiredOptions( self::CONSTRUCTOR_OPTIONS );
@ -73,6 +78,7 @@ class MovePageFactory {
$this->watchedItems = $watchedItems;
$this->permMgr = $permMgr;
$this->repoGroup = $repoGroup;
$this->contentHandlerFactory = $contentHandlerFactory;
}
/**
@ -82,6 +88,6 @@ class MovePageFactory {
*/
public function newMovePage( Title $from, Title $to ) : MovePage {
return new MovePage( $from, $to, $this->options, $this->loadBalancer, $this->nsInfo,
$this->watchedItems, $this->permMgr, $this->repoGroup );
$this->watchedItems, $this->permMgr, $this->repoGroup, $this->contentHandlerFactory );
}
}

View file

@ -20,6 +20,8 @@
* @file
*/
use MediaWiki\Content\ContentHandlerFactory;
use MediaWiki\Content\IContentHandlerFactory;
use MediaWiki\Edit\PreparedEdit;
use MediaWiki\Logger\LoggerFactory;
use MediaWiki\MediaWikiServices;
@ -253,6 +255,13 @@ class WikiPage implements Page, IDBAccessObject {
return MediaWikiServices::getInstance()->getSlotRoleRegistry();
}
/**
* @return ContentHandlerFactory
*/
private function getContentHandlerFactory(): IContentHandlerFactory {
return MediaWikiServices::getInstance()->getContentHandlerFactory();
}
/**
* @return ParserCache
*/
@ -287,7 +296,8 @@ class WikiPage implements Page, IDBAccessObject {
* @since 1.21
*/
public function getContentHandler() {
return ContentHandler::getForModelID( $this->getContentModel() );
return $this->getContentHandlerFactory()
->getContentHandler( $this->getContentModel() );
}
/**
@ -1704,7 +1714,8 @@ class WikiPage implements Page, IDBAccessObject {
JobQueueGroup::singleton(),
MessageCache::singleton(),
MediaWikiServices::getInstance()->getContentLanguage(),
MediaWikiServices::getInstance()->getDBLoadBalancerFactory()
MediaWikiServices::getInstance()->getDBLoadBalancerFactory(),
$this->getContentHandlerFactory()
);
$derivedDataUpdater->setLogger( LoggerFactory::getInstance( 'SaveParse' ) );
@ -1806,7 +1817,8 @@ class WikiPage implements Page, IDBAccessObject {
$this->getDerivedDataUpdater( $user, null, $forUpdate, true ),
$this->getDBLoadBalancer(),
$this->getRevisionStore(),
$this->getSlotRoleRegistry()
$this->getSlotRoleRegistry(),
$this->getContentHandlerFactory()
);
$pageUpdater->setUsePageCreationLog( $wgPageCreationLog );

View file

@ -751,12 +751,14 @@ abstract class SearchEngine {
* @return SearchIndexField[] Index field definitions for all content handlers
*/
public function getSearchIndexFields() {
$models = ContentHandler::getContentModels();
$models = MediaWikiServices::getInstance()->getContentHandlerFactory()->getContentModels();
$fields = [];
$seenHandlers = new SplObjectStorage();
foreach ( $models as $model ) {
try {
$handler = ContentHandler::getForModelID( $model );
$handler = MediaWikiServices::getInstance()
->getContentHandlerFactory()
->getContentHandler( $model );
}
catch ( MWUnknownContentModelException $e ) {
// If we can find no handler, ignore it

View file

@ -313,7 +313,12 @@ class SpecialPageFactory {
}
if ( $this->options->get( 'ContentHandlerUseDB' ) ) {
$this->list['ChangeContentModel'] = \SpecialChangeContentModel::class;
$this->list['ChangeContentModel'] = [
'class' => \SpecialChangeContentModel::class,
'services' => [
'ContentHandlerFactory',
],
];
}
// Add extension special pages

View file

@ -133,7 +133,10 @@ class SpecialBrokenRedirects extends QueryPage {
// check user permissions
$permissionManager->userHasRight( $this->getUser(), 'edit' ) &&
// check, if the content model is editable through action=edit
ContentHandler::getForTitle( $fromObj )->supportsDirectEditing()
MediaWikiServices::getInstance()
->getContentHandlerFactory()
->getContentHandler( $fromObj->getContentModel() )
->supportsDirectEditing()
) {
$links[] = $linkRenderer->makeKnownLink(
$fromObj,

View file

@ -1,11 +1,29 @@
<?php
use MediaWiki\Content\IContentHandlerFactory;
use MediaWiki\MediaWikiServices;
class SpecialChangeContentModel extends FormSpecialPage {
public function __construct() {
/** @var IContentHandlerFactory */
private $contentHandlerFactory;
/**
* SpecialChangeContentModel constructor.
* @param IContentHandlerFactory|null $contentHandlerFactory
* @internal use @see SpecialPageFactory::getPage
*/
public function __construct( ?IContentHandlerFactory $contentHandlerFactory = null ) {
parent::__construct( 'ChangeContentModel', 'editcontentmodel' );
$this->contentHandlerFactory = DeprecationHelper::newArgumentWithDeprecation(
__METHOD__,
'contentHandlerFactory',
'1.35',
$contentHandlerFactory,
function () {
return MediaWikiServices::getInstance()->getContentHandlerFactory();
}
);
}
public function doesWrites() {
@ -141,10 +159,10 @@ class SpecialChangeContentModel extends FormSpecialPage {
}
private function getOptionsForTitle( Title $title = null ) {
$models = ContentHandler::getContentModels();
$models = $this->contentHandlerFactory->getContentModels();
$options = [];
foreach ( $models as $model ) {
$handler = ContentHandler::getForModelID( $model );
$handler = $this->contentHandlerFactory->getContentHandler( $model );
if ( !$handler->supportsDirectEditing() ) {
continue;
}
@ -218,7 +236,9 @@ class SpecialChangeContentModel extends FormSpecialPage {
}
} else {
// Page doesn't exist, create an empty content object
$newContent = ContentHandler::getForModelID( $data['model'] )->makeEmptyContent();
$newContent = $this->contentHandlerFactory
->getContentHandler( $data['model'] )
->makeEmptyContent();
}
// All other checks have passed, let's check rate limits

View file

@ -159,7 +159,10 @@ class SpecialDoubleRedirects extends QueryPage {
->getPermissionManager()
->userHasRight( $this->getUser(), 'edit' ) &&
// check, if the content model is editable through action=edit
ContentHandler::getForTitle( $titleA )->supportsDirectEditing()
MediaWikiServices::getInstance()
->getContentHandlerFactory()
->getContentHandler( $titleA->getContentModel() )
->supportsDirectEditing()
) {
$edit = $linkRenderer->makeKnownLink(
$titleA,

View file

@ -150,7 +150,9 @@ class MovePageForm extends UnlistedSpecialPage {
$out->addModules( 'mediawiki.misc-authed-ooui' );
$this->addHelpLink( 'Help:Moving a page' );
$handlerSupportsRedirects = ContentHandler::getForTitle( $this->oldTitle )
$handlerSupportsRedirects = MediaWikiServices::getInstance()
->getContentHandlerFactory()
->getContentHandler( $this->oldTitle->getContentModel() )
->supportsRedirects();
if ( $this->getConfig()->get( 'FixDoubleRedirects' ) ) {
@ -595,7 +597,9 @@ class MovePageForm extends UnlistedSpecialPage {
}
}
$handler = ContentHandler::getForTitle( $ot );
$handler = MediaWikiServices::getInstance()
->getContentHandlerFactory()
->getContentHandler( $ot->getContentModel() );
if ( !$handler->supportsRedirects() ) {
$createRedirect = false;

View file

@ -374,7 +374,11 @@ class SpecialNewpages extends IncludableSpecialPage {
[ 'class' => 'mw-newpages-history' ],
[ 'action' => 'history' ]
);
if ( ContentHandler::getForTitle( $title )->supportsDirectEditing() ) {
if ( MediaWikiServices::getInstance()
->getContentHandlerFactory()
->getContentHandler( $title->getContentModel() )
->supportsDirectEditing()
) {
$linkArr[] = $linkRenderer->makeKnownLink(
$title,
$this->msg( 'editlink' )->text(),

View file

@ -23,6 +23,8 @@
* @since 1.24
*/
use MediaWiki\MediaWikiServices;
/**
* Special page for changing the content language of a page
*
@ -52,8 +54,11 @@ class SpecialPageLanguage extends FormSpecialPage {
$defaultName = $this->par;
$title = $defaultName ? Title::newFromText( $defaultName ) : null;
if ( $title ) {
$defaultPageLanguage =
ContentHandler::getForTitle( $title )->getPageLanguage( $title );
$defaultPageLanguage = MediaWikiServices::getInstance()
->getContentHandlerFactory()
->getContentHandler( $title->getContentModel() )
->getPageLanguage( $title );
$hasCustomLanguageSet = !$defaultPageLanguage->equals( $title->getPageLanguage() );
} else {
$hasCustomLanguageSet = false;

View file

@ -543,7 +543,11 @@ class SpecialSearch extends SpecialPage {
if ( $title->isKnown() ) {
$messageName = 'searchmenu-exists';
$linkClass = 'mw-search-exists';
} elseif ( ContentHandler::getForTitle( $title )->supportsDirectEditing()
} elseif (
MediaWikiServices::getInstance()
->getContentHandlerFactory()
->getContentHandler( $title->getContentModel() )
->supportsDirectEditing()
&& MediaWikiServices::getInstance()->getPermissionManager()->quickUserCan( 'create',
$this->getUser(), $title )
&& MediaWikiServices::getInstance()->getPermissionManager()->quickUserCan( 'edit',

View file

@ -421,7 +421,10 @@ class SpecialWhatLinksHere extends IncludableSpecialPage {
->getPermissionManager()
->userHasRight( $this->getUser(), 'edit' ) &&
// check, if the content model is editable through action=edit
ContentHandler::getForTitle( $target )->supportsDirectEditing()
MediaWikiServices::getInstance()
->getContentHandlerFactory()
->getContentHandler( $target->getContentModel() )
->supportsDirectEditing()
) {
if ( $editText !== null ) {
$editText = new HtmlArmor( $editText );

View file

@ -21,6 +21,7 @@
* @ingroup Maintenance
*/
use MediaWiki\MediaWikiServices;
use MediaWiki\Revision\RevisionRecord;
require_once __DIR__ . '/Maintenance.php';
@ -171,7 +172,9 @@ class CleanupSpam extends Maintenance {
);
} else {
// Didn't find a non-spammy revision, blank the page
$handler = ContentHandler::getForTitle( $title );
$handler = MediaWikiServices::getInstance()
->getContentHandlerFactory()
->getContentHandler( $title->getContentModel() );
$content = $handler->makeEmptyContent();
$this->output( "blanking\n" );

View file

@ -524,8 +524,10 @@ TEXT
*/
private function exportTransform( $text, $model, $format = null ) {
try {
$handler = ContentHandler::getForModelID( $model );
$text = $handler->exportTransform( $text, $format );
$text = MediaWikiServices::getInstance()
->getContentHandlerFactory()
->getContentHandler( $model )
->exportTransform( $text, $format );
}
catch ( MWException $ex ) {
$this->progress(

View file

@ -133,7 +133,10 @@ class PopulateContentModel extends Maintenance {
$key = "{$prefix}_id";
$count = count( $ids );
$format = ContentHandler::getForModelID( $model )->getDefaultFormat();
$format = MediaWikiServices::getInstance()
->getContentHandlerFactory()
->getContentHandler( $model )
->getDefaultFormat();
$this->output( "Setting $count rows to $model / $format..." );
$dbw->update(
$table,
@ -195,7 +198,9 @@ class PopulateContentModel extends Maintenance {
}
$lastId = $row->{$key};
try {
$handler = ContentHandler::getForTitle( $title );
$handler = MediaWikiServices::getInstance()
->getContentHandlerFactory()
->getContentHandler( $title->getContentModel() );
} catch ( MWException $e ) {
$this->error( "Invalid content model for $title" );
continue;

View file

@ -386,10 +386,7 @@ class PopulateContentTables extends Maintenance {
if ( !isset( $row->len ) ) {
// NOTE: The nominal size of the content may not be the length of the raw blob.
$handler = ContentHandler::getForModelID( $model );
$content = $handler->unserializeContent( $blob );
$row->len = $content->getSize();
$row->len = ContentHandler::makeContent( $blob, null, $model )->getSize();
}
if ( !isset( $row->sha1 ) || $row->sha1 === '' ) {

View file

@ -91,7 +91,9 @@ class MovePageTest extends MediaWikiTestCase {
$params['nsInfo'] ?? $mockNsInfo,
$params['wiStore'] ?? $this->createNoOpMock( WatchedItemStore::class ),
$params['permMgr'] ?? $this->createNoOpMock( PermissionManager::class ),
$params['repoGroup'] ?? $this->getMockRepoGroup()
$params['repoGroup'] ?? $this->getMockRepoGroup(),
$params['contentHandlerFactory']
?? MediaWikiServices::getInstance()->getContentHandlerFactory()
);
}
@ -151,7 +153,7 @@ class MovePageTest extends MediaWikiTestCase {
$services->getWatchedItemStore(),
$services->getPermissionManager(),
$services->getRepoGroup(),
$services->getTitleFormatter()
$services->getContentHandlerFactory()
);
$this->assertEquals( $obj2, $obj1 );

View file

@ -1838,7 +1838,8 @@ class PermissionManagerTest extends MediaWikiLangTestCase {
private function getJavascriptRedirectRevision(
Title $title, Title $redirectTargetTitle, User $user
) {
$content = ContentHandler::getForModelID( CONTENT_MODEL_JAVASCRIPT )
$content = MediaWikiServices::getInstance()->getContentHandlerFactory()
->getContentHandler( CONTENT_MODEL_JAVASCRIPT )
->makeRedirectContent( $redirectTargetTitle );
$revision = new MutableRevisionRecord( $title );
$revision->setContent( 'main', $content );

View file

@ -2,6 +2,7 @@
namespace MediaWiki\Tests\Revision;
use MediaWiki\MediaWikiServices;
use MediaWiki\Revision\MainSlotRoleHandler;
use PHPUnit\Framework\MockObject\MockObject;
use Title;
@ -30,7 +31,10 @@ class MainSlotRoleHandlerTest extends \MediaWikiIntegrationTestCase {
* @covers \MediaWiki\Revision\MainSlotRoleHandler::getOutputLayoutHints()
*/
public function testConstruction() {
$handler = new MainSlotRoleHandler( [] );
$handler = new MainSlotRoleHandler(
[],
MediaWikiServices::getInstance()->getContentHandlerFactory()
);
$this->assertSame( 'main', $handler->getRole() );
$this->assertSame( 'slot-name-main', $handler->getNameMessageKey() );
@ -44,7 +48,10 @@ class MainSlotRoleHandlerTest extends \MediaWikiIntegrationTestCase {
* @covers \MediaWiki\Revision\MainSlotRoleHandler::getDefaultModel()
*/
public function testFetDefaultModel() {
$handler = new MainSlotRoleHandler( [ 100 => CONTENT_MODEL_TEXT ] );
$handler = new MainSlotRoleHandler(
[ 100 => CONTENT_MODEL_TEXT ],
MediaWikiServices::getInstance()->getContentHandlerFactory()
);
// For the main handler, the namespace determins the default model
$titleMain = $this->makeTitleObject( NS_MAIN );
@ -58,7 +65,10 @@ class MainSlotRoleHandlerTest extends \MediaWikiIntegrationTestCase {
* @covers \MediaWiki\Revision\MainSlotRoleHandler::isAllowedModel()
*/
public function testIsAllowedModel() {
$handler = new MainSlotRoleHandler( [] );
$handler = new MainSlotRoleHandler(
[],
MediaWikiServices::getInstance()->getContentHandlerFactory()
);
// For the main handler, (nearly) all models are allowed
$title = $this->makeTitleObject( NS_MAIN );
@ -70,7 +80,10 @@ class MainSlotRoleHandlerTest extends \MediaWikiIntegrationTestCase {
* @covers \MediaWiki\Revision\MainSlotRoleHandler::supportsArticleCount()
*/
public function testSupportsArticleCount() {
$handler = new MainSlotRoleHandler( [] );
$handler = new MainSlotRoleHandler(
[],
MediaWikiServices::getInstance()->getContentHandlerFactory()
);
$this->assertTrue( $handler->supportsArticleCount() );
}

View file

@ -5,6 +5,7 @@ namespace MediaWiki\Tests\Revision;
use CommentStoreComment;
use Content;
use LogicException;
use MediaWiki\Content\IContentHandlerFactory;
use MediaWiki\MediaWikiServices;
use MediaWiki\Revision\MainSlotRoleHandler;
use MediaWiki\Revision\MutableRevisionRecord;
@ -134,7 +135,10 @@ class RevisionRendererTest extends MediaWikiTestCase {
$roleReg = new SlotRoleRegistry( $slotRoles );
$roleReg->defineRole( 'main', function () {
return new MainSlotRoleHandler( [] );
return new MainSlotRoleHandler(
[],
$this->createMock( IContentHandlerFactory::class )
);
} );
$roleReg->defineRoleWithModel( 'aux', CONTENT_MODEL_WIKITEXT );

View file

@ -247,6 +247,7 @@ abstract class RevisionStoreDbTestBase extends MediaWikiTestCase {
MediaWikiServices::getInstance()->getSlotRoleRegistry(),
$this->getMcrMigrationStage(),
MediaWikiServices::getInstance()->getActorMigration(),
MediaWikiServices::getInstance()->getContentHandlerFactory(),
$wikiId
);

View file

@ -4,6 +4,7 @@ namespace MediaWiki\Tests\Revision;
use ActorMigration;
use CommentStore;
use MediaWiki\Content\IContentHandlerFactory;
use MediaWiki\Revision\RevisionStore;
use MediaWiki\Revision\RevisionStoreFactory;
use MediaWiki\Revision\SlotRoleRegistry;
@ -36,7 +37,8 @@ class RevisionStoreFactoryTest extends \MediaWikiIntegrationTestCase {
ActorMigration::newMigration(),
MIGRATION_NEW,
new NullLogger(),
true
true,
$this->getContentHandlerFactory()
);
$this->assertTrue( true );
}
@ -64,6 +66,7 @@ class RevisionStoreFactoryTest extends \MediaWikiIntegrationTestCase {
$commentStore = $this->getMockCommentStore();
$actorMigration = ActorMigration::newMigration();
$logger = new NullLogger();
$contentHandlerFactory = $this->getContentHandlerFactory();
$factory = new RevisionStoreFactory(
$lbFactory,
@ -75,7 +78,8 @@ class RevisionStoreFactoryTest extends \MediaWikiIntegrationTestCase {
$actorMigration,
$mcrMigrationStage,
$logger,
$contentHandlerUseDb
$contentHandlerUseDb,
$contentHandlerFactory
);
$store = $factory->getRevisionStore( $dbDomain );
@ -154,6 +158,13 @@ class RevisionStoreFactoryTest extends \MediaWikiIntegrationTestCase {
return $this->createMock( SlotRoleRegistry::class );
}
/**
* @return IContentHandlerFactory|MockObject
*/
private function getContentHandlerFactory(): IContentHandlerFactory {
return $this->createMock( IContentHandlerFactory::class );
}
/**
* @return NameTableStoreFactory
*/

View file

@ -4,6 +4,7 @@ namespace MediaWiki\Tests\Revision;
use CommentStore;
use InvalidArgumentException;
use MediaWiki\Content\IContentHandlerFactory;
use MediaWiki\MediaWikiServices;
use MediaWiki\Revision\RevisionAccessException;
use MediaWiki\Revision\RevisionStore;
@ -49,7 +50,8 @@ class RevisionStoreTest extends MediaWikiTestCase {
MediaWikiServices::getInstance()->getSlotRoleStore(),
MediaWikiServices::getInstance()->getSlotRoleRegistry(),
$wgMultiContentRevisionSchemaMigrationStage,
MediaWikiServices::getInstance()->getActorMigration()
MediaWikiServices::getInstance()->getActorMigration(),
$this->getMockContentHandlerFactory()
);
}
@ -143,13 +145,21 @@ class RevisionStoreTest extends MediaWikiTestCase {
$nameTables->getSlotRoles(),
$this->getMockSlotRoleRegistry(),
$migrationMode,
MediaWikiServices::getInstance()->getActorMigration()
MediaWikiServices::getInstance()->getActorMigration(),
$this->getMockContentHandlerFactory()
);
$store->setContentHandlerUseDB( $contentHandlerDb );
$this->assertSame( $contentHandlerDb, $store->getContentHandlerUseDB() );
}
/**
* @return IContentHandlerFactory|MockObject
*/
public function getMockContentHandlerFactory(): IContentHandlerFactory {
return $this->createMock( IContentHandlerFactory::class );
}
/**
* @covers \MediaWiki\Revision\RevisionStore::getTitle
*/
@ -452,6 +462,8 @@ class RevisionStoreTest extends MediaWikiTestCase {
$contentModelStore = $nameTables->getContentModels();
$slotRoleStore = $nameTables->getSlotRoles();
$slotRoleRegistry = $services->getSlotRoleRegistry();
$contentHandlerFactory = $this->getMockContentHandlerFactory();
$store = new RevisionStore(
$loadBalancer,
$blobStore,
@ -461,8 +473,10 @@ class RevisionStoreTest extends MediaWikiTestCase {
$nameTables->getSlotRoles(),
$slotRoleRegistry,
$migration,
$services->getActorMigration()
$services->getActorMigration(),
$contentHandlerFactory
);
if ( !$expectException ) {
$store = TestingAccessWrapper::newFromObject( $store );
$this->assertSame( $loadBalancer, $store->loadBalancer );

View file

@ -4,6 +4,7 @@ namespace MediaWiki\Tests\Revision;
use InvalidArgumentException;
use LogicException;
use MediaWiki\Content\IContentHandlerFactory;
use MediaWiki\Revision\MainSlotRoleHandler;
use MediaWiki\Revision\SlotRoleHandler;
use MediaWiki\Revision\SlotRoleRegistry;
@ -156,7 +157,10 @@ class SlotRoleRegistryTest extends MediaWikiTestCase {
public function testGetRequiredRoles() {
$registry = $this->newSlotRoleRegistry();
$registry->defineRole( 'main', function ( $role ) {
return new MainSlotRoleHandler( [] );
return new MainSlotRoleHandler(
[],
$this->createMock( IContentHandlerFactory::class )
);
} );
$title = $this->makeBlankTitleObject();
@ -169,7 +173,10 @@ class SlotRoleRegistryTest extends MediaWikiTestCase {
public function testGetAllowedRoles() {
$registry = $this->newSlotRoleRegistry();
$registry->defineRole( 'main', function ( $role ) {
return new MainSlotRoleHandler( [] );
return new MainSlotRoleHandler(
[],
$this->createMock( IContentHandlerFactory::class )
);
} );
$registry->defineRoleWithModel( 'FOO', CONTENT_MODEL_TEXT );

View file

@ -460,7 +460,8 @@ abstract class RevisionDbTestBase extends MediaWikiTestCase {
$services->getSlotRoleStore(),
$services->getSlotRoleRegistry(),
$this->getMcrMigrationStage(),
$services->getActorMigration()
$services->getActorMigration(),
$services->getContentHandlerFactory()
);
$store->setContentHandlerUseDB( $this->getContentHandlerUseDB() );

View file

@ -477,7 +477,8 @@ class RevisionTest extends MediaWikiTestCase {
MediaWikiServices::getInstance()->getSlotRoleStore(),
MediaWikiServices::getInstance()->getSlotRoleRegistry(),
MIGRATION_OLD,
MediaWikiServices::getInstance()->getActorMigration()
MediaWikiServices::getInstance()->getActorMigration(),
MediaWikiServices::getInstance()->getContentHandlerFactory()
);
return $blobStore;
}

View file

@ -0,0 +1,99 @@
<?php
use MediaWiki\MediaWikiServices;
/**
* @group ContentHandlerFactory
*/
class RegistrationContentHandlerFactoryToMediaWikiServicesTest extends MediaWikiTestCase {
protected function setUp(): void {
parent::setUp();
$this->setMwGlobals( [
'wgExtraNamespaces' => [
12312 => 'Dummy',
12313 => 'Dummy_talk',
],
// The below tests assume that namespaces not mentioned here (Help, User, MediaWiki, ..)
// default to CONTENT_MODEL_WIKITEXT.
'wgNamespaceContentModels' => [
12312 => 'testing',
],
'wgContentHandlers' => [
CONTENT_MODEL_WIKITEXT => WikitextContentHandler::class,
CONTENT_MODEL_JAVASCRIPT => JavaScriptContentHandler::class,
CONTENT_MODEL_JSON => JsonContentHandler::class,
CONTENT_MODEL_CSS => CssContentHandler::class,
CONTENT_MODEL_TEXT => TextContentHandler::class,
'testing' => DummyContentHandlerForTesting::class,
'testing-callbacks' => function ( $modelId ) {
return new DummyContentHandlerForTesting( $modelId );
},
],
] );
// Reset LinkCache
MediaWikiServices::getInstance()->resetServiceForTesting( 'LinkCache' );
}
protected function tearDown(): void {
// Reset LinkCache
MediaWikiServices::getInstance()->resetServiceForTesting( 'LinkCache' );
parent::tearDown();
}
/**
* @covers \MediaWiki\MediaWikiServices::getContentHandlerFactory
*/
public function testCallFromService_get_ok(): void {
$this->assertInstanceOf(
\MediaWiki\Content\IContentHandlerFactory::class,
MediaWikiServices::getInstance()->getContentHandlerFactory()
);
$this->assertSame(
[
'wikitext',
'javascript',
'json',
'css',
'text',
'testing',
'testing-callbacks',
],
MediaWikiServices::getInstance()->getContentHandlerFactory()->getContentModels()
);
}
/**
* @covers \MediaWiki\MediaWikiServices::getContentHandlerFactory
*/
public function testCallFromService_second_same(): void {
$this->assertSame(
MediaWikiServices::getInstance()->getContentHandlerFactory(),
MediaWikiServices::getInstance()->getContentHandlerFactory()
);
}
/**
* @covers \MediaWiki\MediaWikiServices::getContentHandlerFactory
*/
public function testCallFromService_afterCustomDefine_same(): void {
$factory = MediaWikiServices::getInstance()->getContentHandlerFactory();
$factory->defineContentHandler(
'model name',
DummyContentHandlerForTesting::class
);
$this->assertTrue(
MediaWikiServices::getInstance()
->getContentHandlerFactory()
->isDefinedModel( 'model name' )
);
$this->assertSame(
$factory,
MediaWikiServices::getInstance()->getContentHandlerFactory()
);
}
}

View file

@ -16,7 +16,8 @@ class WikitextContentHandlerTest extends MediaWikiLangTestCase {
protected function setUp() : void {
parent::setUp();
$this->handler = ContentHandler::getForModelID( CONTENT_MODEL_WIKITEXT );
$this->handler = MediaWikiServices::getInstance()->getContentHandlerFactory()
->getContentHandler( CONTENT_MODEL_WIKITEXT );
}
/**

View file

@ -0,0 +1,28 @@
<?php
use MediaWiki\MediaWikiServices;
/**
* Test class for SpecialChangeContentModel class
*
* @group Database
*
* @covers SpecialChangeContentModel
*/
class SpecialChangeContentModelTest extends MediaWikiTestCase {
/**
* @covers \SpecialChangeContentModel::__construct
* @covers \SpecialChangeContentModel::doesWrites
*/
public function testBasic() {
$contentModel = $this->getChangeContentModel();
$this->assertInstanceOf( SpecialChangeContentModel::class, $contentModel );
$this->assertTrue( $contentModel->doesWrites() );
}
private function getChangeContentModel() {
return MediaWikiServices::getInstance()->getSpecialPageFactory()
->getPage( 'ChangeContentModel' );
}
}

View file

@ -1,5 +1,6 @@
<?php
use MediaWiki\MediaWikiServices;
use Wikimedia\TestingAccessWrapper;
/**
@ -23,9 +24,12 @@ class ContentHandlerSanityTest extends MediaWikiTestCase {
public static function provideHandlers() {
$models = ContentHandler::getContentModels();
$contentHandlerFactory = MediaWikiServices::getInstance()->getContentHandlerFactory();
$handlers = [];
foreach ( $models as $model ) {
$handlers[] = [ ContentHandler::getForModelID( $model ) ];
$handlers[] = [
$contentHandlerFactory->getContentHandler( $model )
];
}
return $handlers;

View file

@ -0,0 +1,194 @@
<?php
use MediaWiki\Content\ContentHandlerFactory;
class ContentHandlerFactoryTest extends MediaWikiUnitTestCase {
public function provideHandlerSpecs() {
return [
'typical ' => [
[
'ExistClassName' => DummyContentHandlerForTesting::class,
'ExistCallbackWithExistClassName' => function ( $modelID ) {
return new DummyContentHandlerForTesting( $modelID );
},
],
],
];
}
/**
* @covers \MediaWiki\Content\ContentHandlerFactory::getContentHandler
* @covers \MediaWiki\Content\ContentHandlerFactory::createForModelID
* @covers \MediaWiki\Content\ContentHandlerFactory::createContentHandlerFromHandlerSpec
* @covers \MediaWiki\Content\ContentHandlerFactory::validateContentHandler
*
* @param array $handlerSpecs
*
* @todo test ContentHandlerFactory::createContentHandlerFromHook
*
* @dataProvider provideHandlerSpecs $handlerSpecs
*/
public function testGetContentHandler_callWithProvider_same( array $handlerSpecs ) {
$registry = new ContentHandlerFactory( $handlerSpecs );
foreach ( $handlerSpecs as $modelID => $handlerSpec ) {
$contentHandler = $registry->getContentHandler( $modelID );
$this->assertInstanceOf( DummyContentHandlerForTesting::class, $contentHandler );
$this->assertSame( $modelID, $contentHandler->getModelID() );
}
}
public function provideHandlerSpecsWithMWException() {
return [
'MWException expected' => [
[
'ExistCallbackWithWrongType' => function () {
return true;
},
'ExistCallbackWithNull' => function () {
return null;
},
'ExistCallbackWithEmptyString' => function () {
return '';
},
'WrongClassName' => self::class,
'WrongType' => true,
'NullType' => null,
],
MWException::class,
],
'Error expected' => [
[
'WrongClassNameNotExist' => 'ClassNameNotExist',
'ExistCallbackWithNotExistClassName' => function () {
return ClassNameNotExist();
},
'EmptyString' => '',
],
Error::class,
],
];
}
/**
* @covers \MediaWiki\Content\ContentHandlerFactory::getContentHandler
* @covers \MediaWiki\Content\ContentHandlerFactory::createForModelID
* @covers \MediaWiki\Content\ContentHandlerFactory::createContentHandlerFromHandlerSpec
* @covers \MediaWiki\Content\ContentHandlerFactory::validateContentHandler
*
* @dataProvider provideHandlerSpecsWithMWException
*
* @param array $handlerSpecs
* @param string $exceptionName
*/
public function testCreateContentHandlerForModelID_callWithProvider_throwsException(
array $handlerSpecs, string $exceptionName
) {
$registry = new ContentHandlerFactory( $handlerSpecs );
foreach ( $handlerSpecs as $modelID => $handlerSpec ) {
try {
$registry->getContentHandler( $modelID );
$this->assertTrue( false );
}
catch ( \Throwable $exception ) {
$this->assertInstanceOf( $exceptionName, $exception,
"$modelID get: " . get_class( $exception ) . " but expect: $exceptionName" );
}
}
}
/**
* @covers \MediaWiki\Content\ContentHandlerFactory::createForModelID
* @covers \MediaWiki\Content\ContentHandlerFactory::createContentHandlerFromHandlerSpec
* @covers \MediaWiki\Content\ContentHandlerFactory::validateContentHandler
*/
public function testCreateContentHandlerForModelID_callNotExist_throwMWUCMException() {
$this->expectException( MWUnknownContentModelException::class );
( new ContentHandlerFactory( [] ) )->getContentHandler( 'ModelNameNotExist' );
}
/**
* @covers \MediaWiki\Content\ContentHandlerFactory::getContentHandler
* @covers \MediaWiki\Content\ContentHandlerFactory::createForModelID
* @covers \MediaWiki\Content\ContentHandlerFactory::createContentHandlerFromHandlerSpec
* @covers \MediaWiki\Content\ContentHandlerFactory::validateContentHandler
* @covers \MediaWiki\Content\ContentHandlerFactory::isDefinedModel
*/
public function testDefineContentHandler_flow_throwsException() {
$registry = new ContentHandlerFactory( [] );
$this->assertFalse( $registry->isDefinedModel( 'define test' ) );
$registry->defineContentHandler( 'define test', DummyContentHandlerForTesting::class );
$this->assertTrue( $registry->isDefinedModel( 'define test' ) );
$this->assertInstanceOf(
DummyContentHandlerForTesting::class,
$registry->getContentHandler( 'define test' )
);
}
/**
* @covers \MediaWiki\Content\ContentHandlerFactory::getContentModels
*/
public function testGetContentModels_flow_same() {
$registry = new ContentHandlerFactory( [
'mock name 1' => DummyContentHandlerForTesting::class,
'mock name 0' => DummyContentHandlerForTesting::class,
] );
$this->assertArrayEquals( [
'mock name 1',
'mock name 0',
], $registry->getContentModels() );
$registry->defineContentHandler( 'some new name', function () {
} );
$this->assertArrayEquals( [
'mock name 1',
'mock name 0',
'some new name',
], $registry->getContentModels() );
}
/**
* @covers \MediaWiki\Content\ContentHandlerFactory::getContentModels
*/
public function testGetContentModels_empty_empty() {
$registry = new ContentHandlerFactory( [] );
$this->assertArrayEquals( [], $registry->getContentModels() );
}
/**
* @covers \MediaWiki\Content\ContentHandlerFactory::getAllContentFormats
* @covers \MediaWiki\Content\ContentHandlerFactory::defineContentHandler
*/
public function testGetAllContentFormats_flow_same() {
$registry = new ContentHandlerFactory( [
'mock name 1' => function () {
return new DummyContentHandlerForTesting( 'mock 1', [ 'format 1' ] );
},
'mock name 2' => function () {
return new DummyContentHandlerForTesting( 'mock 0', [ 'format 0' ] );
},
] );
$this->assertArrayEquals( [
'format 1',
'format 0',
], $registry->getAllContentFormats() );
$registry->defineContentHandler( 'some new name', function () {
return new DummyContentHandlerForTesting( 'mock defined', [ 'format defined' ] );
} );
$this->assertArrayEquals( [
'format 1',
'format 0',
'format defined',
], $registry->getAllContentFormats() );
}
}