30use Wikimedia\Assert\Assert;
96 wfDebugLog(
'ContentHandler',
'Accessing ' .
$content->getModel() .
' content as text!' );
100 "Attempt to get text from Content with model " .
136 $modelId =
null, $format =
null ) {
137 if ( is_null( $modelId ) ) {
138 if ( is_null(
$title ) ) {
139 throw new MWException(
"Must provide a Title object or a content model ID." );
142 $modelId =
$title->getContentModel();
145 $handler = self::getForModelID( $modelId );
147 return $handler->unserializeContent( $text, $format );
187 $slotRoleregistry = MediaWikiServices::getInstance()->getSlotRoleRegistry();
188 $mainSlotHandler = $slotRoleregistry->getRoleHandler(
'main' );
189 return $mainSlotHandler->getDefaultModel(
$title );
202 $modelId =
$title->getContentModel();
204 return self::getForModelID( $modelId );
220 return self::getForModelID( $modelId );
257 if ( isset( self::$handlers[$modelId] ) ) {
258 return self::$handlers[$modelId];
264 Hooks::run(
'ContentHandlerForModelID', [ $modelId, &$handler ] );
266 if ( $handler ===
null ) {
271 throw new MWException(
"ContentHandlerForModelID must supply a ContentHandler instance" );
276 if ( is_callable( $classOrCallback ) ) {
277 $handler = call_user_func( $classOrCallback, $modelId );
279 $handler =
new $classOrCallback( $modelId );
284 var_export( $classOrCallback,
true ) .
" from \$wgContentHandlers is not " .
285 "compatible with ContentHandler"
290 wfDebugLog(
'ContentHandler',
'Created handler for ' . $modelId
291 .
': ' . get_class( $handler ) );
293 self::$handlers[$modelId] = $handler;
295 return self::$handlers[$modelId];
302 self::$handlers = [];
321 $key =
"content-model-$name";
325 $msg->inLanguage(
$lang );
328 return $msg->exists() ? $msg->plain() : $name;
335 Hooks::run(
'GetContentModels', [ &$models ] );
345 $handler = self::getForModelID( $model );
346 $formats = array_merge( $formats, $handler->getSupportedFormats() );
349 $formats = array_unique( $formats );
376 $this->mModelID = $modelId;
377 $this->mSupportedFormats = $formats;
473 return $this->mModelID;
485 if ( $model_id !== $this->mModelID ) {
487 "expected {$this->mModelID} " .
488 "but got $model_id." );
502 return $this->mSupportedFormats;
517 return $this->mSupportedFormats[0];
538 return in_array( $format, $this->mSupportedFormats );
551 "Format $format is not supported for content model "
605 $refreshCache =
false, $unhide =
false
608 $differenceEngine =
new $diffEngineClass(
$context, $old, $new, $rcid, $refreshCache, $unhide );
609 Hooks::run(
'GetDifferenceEngine', [
$context, $old, $new, $refreshCache, $unhide,
610 &$differenceEngine ] );
611 return $differenceEngine;
622 if ( get_class( $slotDiffRenderer ) === TextSlotDiffRenderer::class ) {
626 if ( get_class( $differenceEngine ) !== DifferenceEngine::class ) {
628 LoggerFactory::getInstance(
'diff' )->info(
629 'Falling back to DifferenceEngineSlotDiffRenderer', [
631 'DifferenceEngine' => get_class( $differenceEngine ),
636 Hooks::run(
'GetSlotDiffRenderer', [ $this, &$slotDiffRenderer,
$context ] );
637 return $slotDiffRenderer;
646 $contentLanguage = MediaWikiServices::getInstance()->getContentLanguage();
647 $statsdDataFactory = MediaWikiServices::getInstance()->getStatsdDataFactory();
649 $slotDiffRenderer->setStatsdDataFactory( $statsdDataFactory );
651 $slotDiffRenderer->setLanguage( $contentLanguage );
654 if ( $engine ===
'php' ) {
656 } elseif ( $engine ===
'wikidiff2' ) {
662 return $slotDiffRenderer;
686 $pageLang = MediaWikiServices::getInstance()->getContentLanguage();
690 list( ,
$lang ) = MessageCache::singleton()->figureMessage(
$title->getText() );
691 $pageLang = Language::factory(
$lang );
698 Hooks::run(
'PageContentLanguage', [
$title, &$pageLang,
$wgLang ] );
729 $variant = $pageLang->getPreferredVariant();
730 if ( $pageLang->getCode() !== $variant ) {
731 $pageLang = Language::factory( $variant );
759 Hooks::run(
'ContentModelCanBeUsedOn', [ $this->
getModelID(),
$title, &$ok ] );
772 return DifferenceEngine::class;
809 $oldTarget = $oldContent !==
null ? $oldContent->getRedirectTarget() :
null;
810 $newTarget = $newContent !==
null ? $newContent->getRedirectTarget() :
null;
815 if ( $oldContent && $oldContent->getSize() > 0 &&
816 $newContent && $newContent->getSize() === 0
825 return 'new-redirect';
826 } elseif ( !$newTarget->equals( $oldTarget ) ||
827 $oldTarget->getFragment() !== $newTarget->getFragment()
830 return 'changed-redirect-target';
832 } elseif ( $oldTarget ) {
834 return 'removed-redirect';
838 if ( $flags &
EDIT_NEW && $newContent ) {
839 if ( $newContent->getSize() === 0 ) {
848 if ( $oldContent && $newContent && $oldContent->getSize() > 10 * $newContent->getSize() ) {
853 if ( $oldContent && $newContent && $oldContent->getModel() !== $newContent->getModel() ) {
854 return 'contentmodelchange';
876 $changeType = $this->
getChangeType( $oldContent, $newContent, $flags );
879 if ( !$changeType ) {
884 switch ( $changeType ) {
886 $newTarget = $newContent->getRedirectTarget();
887 $truncatedtext = $newContent->getTextForSummary(
889 - strlen(
wfMessage(
'autoredircomment' )->inContentLanguage()->text() )
890 - strlen( $newTarget->getFullText() )
893 return wfMessage(
'autoredircomment', $newTarget->getFullText() )
894 ->plaintextParams( $truncatedtext )->inContentLanguage()->text();
895 case 'changed-redirect-target':
896 $oldTarget = $oldContent->getRedirectTarget();
897 $newTarget = $newContent->getRedirectTarget();
899 $truncatedtext = $newContent->getTextForSummary(
901 - strlen(
wfMessage(
'autosumm-changed-redirect-target' )
902 ->inContentLanguage()->text() )
903 - strlen( $oldTarget->getFullText() )
904 - strlen( $newTarget->getFullText() )
907 return wfMessage(
'autosumm-changed-redirect-target',
908 $oldTarget->getFullText(),
909 $newTarget->getFullText() )
910 ->rawParams( $truncatedtext )->inContentLanguage()->text();
911 case 'removed-redirect':
912 $oldTarget = $oldContent->getRedirectTarget();
913 $truncatedtext = $newContent->getTextForSummary(
915 - strlen(
wfMessage(
'autosumm-removed-redirect' )
916 ->inContentLanguage()->text() )
917 - strlen( $oldTarget->getFullText() ) );
919 return wfMessage(
'autosumm-removed-redirect', $oldTarget->getFullText() )
920 ->rawParams( $truncatedtext )->inContentLanguage()->text();
923 $truncatedtext = $newContent->getTextForSummary(
924 200 - strlen(
wfMessage(
'autosumm-new' )->inContentLanguage()->text() ) );
926 return wfMessage(
'autosumm-new' )->rawParams( $truncatedtext )
927 ->inContentLanguage()->text();
929 return wfMessage(
'autosumm-blank' )->inContentLanguage()->text();
931 $truncatedtext = $newContent->getTextForSummary(
932 200 - strlen(
wfMessage(
'autosumm-replace' )->inContentLanguage()->text() ) );
934 return wfMessage(
'autosumm-replace' )->rawParams( $truncatedtext )
935 ->inContentLanguage()->text();
937 return wfMessage(
'autosumm-newblank' )->inContentLanguage()->text();
959 $changeType = $this->
getChangeType( $oldContent, $newContent, $flags );
962 if ( !$changeType ) {
969 $tag =
'mw-' . $changeType;
998 $rev = Revision::newFromTitle(
$title );
1000 if ( is_null( $rev ) ) {
1012 $prev = $rev->getPrevious();
1028 [
'rev_user_text' =>
$revQuery[
'fields'][
'rev_user_text'] ],
1030 'rev_page' =>
$title->getArticleID(),
1031 $dbr->bitAnd(
'rev_deleted', RevisionRecord::DELETED_USER ) .
' = 0'
1038 if (
$res ===
false ) {
1043 $hasHistory = (
$res->numRows() > 1 );
1047 $onlyAuthor = $row->rev_user_text;
1049 foreach (
$res as $row ) {
1050 if ( $row->rev_user_text != $onlyAuthor ) {
1051 $onlyAuthor =
false;
1056 $onlyAuthor =
false;
1063 $reason =
wfMessage(
'exbeforeblank',
'$1' )->inContentLanguage()->text();
1065 if ( $onlyAuthor ) {
1070 )->inContentLanguage()->text();
1072 $reason =
wfMessage(
'excontent',
'$1' )->inContentLanguage()->text();
1076 if ( $reason ==
'-' ) {
1082 $maxLength = CommentStore::COMMENT_CHARACTER_LIMIT - ( strlen( $reason ) - 2 );
1086 $reason = str_replace(
'$1', $text, $reason );
1107 public function getUndoContent( $current, $undo, $undoafter, $undoIsLatest =
false ) {
1108 Assert::parameterType( Revision::class .
'|' . Content::class, $current,
'$current' );
1109 if ( $current instanceof
Content ) {
1110 Assert::parameter( $undo instanceof
Content,
'$undo',
1111 'Must be Content when $current is Content' );
1112 Assert::parameter( $undoafter instanceof
Content,
'$undoafter',
1113 'Must be Content when $current is Content' );
1114 $cur_content = $current;
1115 $undo_content = $undo;
1116 $undoafter_content = $undoafter;
1118 Assert::parameter( $undo instanceof
Revision,
'$undo',
1119 'Must be Revision when $current is Revision' );
1120 Assert::parameter( $undoafter instanceof
Revision,
'$undoafter',
1121 'Must be Revision when $current is Revision' );
1123 $cur_content = $current->getContent();
1125 if ( empty( $cur_content ) ) {
1129 $undo_content = $undo->getContent();
1130 $undoafter_content = $undoafter->getContent();
1132 if ( !$undo_content || !$undoafter_content ) {
1136 $undoIsLatest = $current->getId() === $undo->getId();
1142 if ( !$undoIsLatest ) {
1155 if ( $cur_content->equals( $undo_content ) ) {
1157 return $undoafter_content;
1160 $undone_content = $this->
merge3( $undo_content, $undoafter_content, $cur_content );
1162 return $undone_content;
1183 return ParserOptions::newCanonical(
$context );
1330 $fieldData[
'category'] = $searchDataExtractor->getCategories( $output );
1331 $fieldData[
'external_link'] = $searchDataExtractor->getExternalLinks( $output );
1332 $fieldData[
'outgoing_link'] = $searchDataExtractor->getOutgoingLinks( $output );
1333 $fieldData[
'template'] = $searchDataExtractor->getTemplates( $output );
1335 $text =
$content->getTextForSearchIndex();
1337 $fieldData[
'text'] = $text;
1338 $fieldData[
'source_text'] = $text;
1339 $fieldData[
'text_bytes'] =
$content->getSize();
1340 $fieldData[
'content_model'] =
$content->getModel();
1343 Hooks::run(
'SearchDataForIndex', [ &$fieldData, $this, $page, $output, $engine ] );
1361 $parserOutput =
$cache->get( $page, $parserOptions );
1364 if ( empty( $parserOutput ) ) {
1365 $renderer = MediaWikiServices::getInstance()->getRevisionRenderer();
1367 $renderer->getRenderedRevision(
1370 )->getRevisionParserOutput();
1372 $cache->save( $parserOutput, $page, $parserOptions );
1375 return $parserOutput;
$wgContentHandlerTextFallback
How to react if a plain text version of a non-text Content object is requested using ContentHandler::...
$wgContentHandlers
Plugins for page content model handling.
wfGetLangObj( $langcode=false)
Return a Language object from $langcode.
wfGetDB( $db, $groups=[], $wiki=false)
Get a Database object.
wfDebugLog( $logGroup, $text, $dest='all', array $context=[])
Send a line to a supplementary debug log file, if configured, or main debug log if not.
wfMessage( $key,... $params)
This is the function for getting translated interface messages.
wfDeprecated( $function, $version=false, $component=false, $callerOffset=2)
Throws a warning that $function is deprecated.
A content handler knows how do deal with a specific type of content on a wiki page.
getParserOutputForIndexing(WikiPage $page, ParserCache $cache=null)
Produce page output suitable for indexing.
getModelID()
Returns the model id that identifies the content model this ContentHandler can handle.
makeRedirectContent(Title $destination, $text='')
Creates a new Content object that acts as a redirect to the given page, or null if redirects are not ...
__construct( $modelId, $formats)
Constructor, initializing the ContentHandler instance with its model ID and a list of supported forma...
static getAllContentFormats()
string[] $mSupportedFormats
getChangeType(Content $oldContent=null, Content $newContent=null, $flags=0)
Return type of change if one exists for the given edit.
static makeContent( $text, Title $title=null, $modelId=null, $format=null)
Convenience function for creating a Content object from a given textual representation.
importTransform( $blob, $format=null)
Apply import transformation (per default, returns $blob unchanged).
isParserCacheSupported()
Returns true for content models that support caching using the ParserCache mechanism.
static getForModelID( $modelId)
Returns the ContentHandler singleton for the given model ID.
supportsDirectApiEditing()
Whether or not this content model supports direct editing via ApiEditPage.
getAutoDeleteReason(Title $title, &$hasHistory)
Auto-generates a deletion reason.
getChangeTag(Content $oldContent=null, Content $newContent=null, $flags=0)
Return an applicable tag if one exists for the given edit or return null.
getDiffEngineClass()
Returns the name of the diff engine to use.
static getForTitle(Title $title)
Returns the appropriate ContentHandler singleton for the given title.
exportTransform( $blob, $format=null)
Applies transformations on export (returns the blob unchanged per default).
merge3(Content $oldContent, Content $myContent, Content $yourContent)
Attempts to merge differences between three versions.
getAutosummary(Content $oldContent=null, Content $newContent=null, $flags=0)
Return an applicable auto-summary if one exists for the given edit.
checkFormat( $format)
Convenient for checking whether a format provided as a parameter is actually supported.
addSearchField(&$fields, SearchEngine $engine, $name, $type)
Add new field definition to array.
getActionOverrides()
Returns overrides for action handlers.
createDifferenceEngine(IContextSource $context, $old=0, $new=0, $rcid=0, $refreshCache=false, $unhide=false)
Factory for creating an appropriate DifferenceEngine for this content model.
getSecondaryDataUpdates(Title $title, Content $content, $role, SlotRenderingProvider $slotOutput)
Returns a list of DeferrableUpdate objects for recording information about the given Content in some ...
static getContentModels()
getPageLanguage(Title $title, Content $content=null)
Get the language in which the content of the given page is written.
getDefaultFormat()
The format used for serialization/deserialization by default by this ContentHandler.
getSlotDiffRendererInternal(IContextSource $context)
Return the SlotDiffRenderer appropriate for this content handler.
static getDefaultModelFor(Title $title)
Returns the name of the default content model to be used for the page with the given title.
unserializeContent( $blob, $format=null)
Unserializes a Content object of the type supported by this ContentHandler.
static getLocalizedName( $name, Language $lang=null)
Returns the localized name for a given content model.
supportsDirectEditing()
Return true if this content model supports direct editing, such as via EditPage.
getDeletionUpdates(Title $title, $role)
Returns a list of DeferrableUpdate objects for removing information about content in some secondary d...
isSupportedFormat( $format)
Returns true if $format is a serialization format supported by this ContentHandler,...
getSupportedFormats()
Returns a list of serialization formats supported by the serializeContent() and unserializeContent() ...
supportsSections()
Returns true if this content model supports sections.
getSlotDiffRenderer(IContextSource $context)
Get an appropriate SlotDiffRenderer for this content model.
static cleanupHandlersCache()
Clean up handlers cache.
static getContentText(Content $content=null)
Convenience function for getting flat text from a Content object.
getDataForSearchIndex(WikiPage $page, ParserOutput $output, SearchEngine $engine)
Return fields to be indexed by search engine as representation of this document.
supportsCategories()
Returns true if this content model supports categories.
static getForContent(Content $content)
Returns the appropriate ContentHandler singleton for the given Content object.
supportsRedirects()
Returns true if this content model supports redirects.
canBeUsedOn(Title $title)
Determines whether the content type handled by this ContentHandler can be used for the main slot of t...
serializeContent(Content $content, $format=null)
Serializes a Content object of the type supported by this ContentHandler.
makeEmptyContent()
Creates an empty Content object of the type supported by this ContentHandler.
static array $handlers
A Cache of ContentHandler instances by model id.
makeParserOptions( $context)
Get parser options suitable for rendering and caching the article.
getFieldsForSearchIndex(SearchEngine $engine)
Get fields definition for search index.
getUndoContent( $current, $undo, $undoafter, $undoIsLatest=false)
Get the Content object that needs to be saved in order to undo all revisions between $undo and $undoa...
getPageViewLanguage(Title $title, Content $content=null)
Get the language in which the content of this page is written when viewed by user.
B/C adapter for turning a DifferenceEngine into a SlotDiffRenderer.
static getEngine()
Process DiffEngine config and get a sane, usable engine.
Internationalisation code.
Exception thrown when an unregistered content model is requested.
Contain a class for special pages.
makeSearchFieldMapping( $name, $type)
Create a search field definition.
Content object implementation for representing flat text.
Renders a slot diff by doing a text diff on the native representation.
const ENGINE_PHP
Use the PHP diff implementation (DiffEngine).
const ENGINE_EXTERNAL
Use an external executable.
const ENGINE_WIKIDIFF2
Use the wikidiff2 PHP module.
Represents a title within MediaWiki.
Class representing a MediaWiki article and history.
getRevision()
Get the latest revision.
getContent( $audience=RevisionRecord::FOR_PUBLIC, User $user=null)
Get the content of the current revision.
makeParserOptions( $context)
Get parser options suitable for rendering the primary article wikitext.
Base interface for content objects.
Interface for objects which can provide a MediaWiki context on request.
const INDEX_TYPE_TEXT
TEXT fields are suitable for natural language and may be subject to analysis such as stemming.
const INDEX_TYPE_KEYWORD
KEYWORD fields are indexed without any processing, so are appropriate for e.g.
const FLAG_CASEFOLD
Generic field flags.
if(!isset( $args[0])) $lang