summaryrefslogtreecommitdiff
path: root/includes/content/ContentHandler.php
diff options
context:
space:
mode:
Diffstat (limited to 'includes/content/ContentHandler.php')
-rw-r--r--includes/content/ContentHandler.php298
1 files changed, 178 insertions, 120 deletions
diff --git a/includes/content/ContentHandler.php b/includes/content/ContentHandler.php
index 2a92e233..ac417223 100644
--- a/includes/content/ContentHandler.php
+++ b/includes/content/ContentHandler.php
@@ -31,7 +31,6 @@
* @ingroup Content
*/
class MWContentSerializationException extends MWException {
-
}
/**
@@ -54,7 +53,6 @@ class MWContentSerializationException extends MWException {
* @ingroup Content
*/
abstract class ContentHandler {
-
/**
* Switch for enabling deprecation warnings. Used by ContentHandler::deprecated()
* and ContentHandler::runLegacyHooks().
@@ -87,10 +85,11 @@ abstract class ContentHandler {
*
* @since 1.21
*
- * @param $content Content|null
- * @return null|string the textual form of $content, if available
- * @throws MWException if $content is not an instance of TextContent and
- * $wgContentHandlerTextFallback was set to 'fail'.
+ * @param Content $content
+ *
+ * @throws MWException If the content is not an instance of TextContent and
+ * wgContentHandlerTextFallback was set to 'fail'.
+ * @return string|null Textual form of the content, if available.
*/
public static function getContentText( Content $content = null ) {
global $wgContentHandlerTextFallback;
@@ -129,24 +128,21 @@ abstract class ContentHandler {
*
* @since 1.21
*
- * @param string $text the textual representation, will be
+ * @param string $text The textual representation, will be
* unserialized to create the Content object
- * @param $title null|Title the title of the page this text belongs to.
+ * @param Title $title The title of the page this text belongs to.
* Required if $modelId is not provided.
- * @param $modelId null|string the model to deserialize to. If not provided,
+ * @param string $modelId The model to deserialize to. If not provided,
* $title->getContentModel() is used.
- * @param $format null|string the format to use for deserialization. If not
+ * @param string $format The format to use for deserialization. If not
* given, the model's default format is used.
*
- * @throws MWException
- * @return Content a Content object representing $text
- *
- * @throws MWException if $model or $format is not supported or if $text can
- * not be unserialized using $format.
+ * @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.
*/
public static function makeContent( $text, Title $title = null,
- $modelId = null, $format = null )
- {
+ $modelId = null, $format = null ) {
if ( is_null( $modelId ) ) {
if ( is_null( $title ) ) {
throw new MWException( "Must provide a Title object or a content model ID." );
@@ -156,6 +152,7 @@ abstract class ContentHandler {
}
$handler = ContentHandler::getForModelID( $modelId );
+
return $handler->unserializeContent( $text, $format );
}
@@ -189,8 +186,9 @@ abstract class ContentHandler {
*
* @since 1.21
*
- * @param $title Title
- * @return null|string default model name for the page given by $title
+ * @param Title $title
+ *
+ * @return string Default model name for the page given by $title
*/
public static function getDefaultModelFor( Title $title ) {
// NOTE: this method must not rely on $title->getContentModel() directly or indirectly,
@@ -254,11 +252,13 @@ abstract class ContentHandler {
*
* @since 1.21
*
- * @param $title Title
+ * @param Title $title
+ *
* @return ContentHandler
*/
public static function getForTitle( Title $title ) {
$modelId = $title->getContentModel();
+
return ContentHandler::getForModelID( $modelId );
}
@@ -268,18 +268,20 @@ abstract class ContentHandler {
*
* @since 1.21
*
- * @param $content Content
+ * @param Content $content
+ *
* @return ContentHandler
*/
public static function getForContent( Content $content ) {
$modelId = $content->getModel();
+
return ContentHandler::getForModelID( $modelId );
}
/**
- * @var Array A Cache of ContentHandler instances by model id
+ * @var array A Cache of ContentHandler instances by model id
*/
- static $handlers;
+ protected static $handlers;
/**
* Returns the ContentHandler singleton for the given model ID. Use the
@@ -302,9 +304,9 @@ abstract class ContentHandler {
*
* @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 $modelId
- * @throws MWException if no handler is known for $modelId.
+ *
+ * @throws MWException If no handler is known for the model ID.
+ * @return ContentHandler The ContentHandler singleton for handling the model given by the ID.
*/
public static function getForModelID( $modelId ) {
global $wgContentHandlers;
@@ -330,14 +332,16 @@ abstract class ContentHandler {
$handler = new $class( $modelId );
if ( !( $handler instanceof ContentHandler ) ) {
- throw new MWException( "$class from \$wgContentHandlers is not compatible with ContentHandler" );
+ throw new MWException( "$class from \$wgContentHandlers is not " .
+ "compatible with ContentHandler" );
}
}
wfDebugLog( 'ContentHandler', 'Created handler for ' . $modelId
- . ': ' . get_class( $handler ) );
+ . ': ' . get_class( $handler ) );
ContentHandler::$handlers[$modelId] = $handler;
+
return ContentHandler::$handlers[$modelId];
}
@@ -350,10 +354,12 @@ abstract class ContentHandler {
* @param string $name The content model ID, as given by a CONTENT_MODEL_XXX
* constant or returned by Revision::getContentModel().
*
- * @return string The content format's localized name.
- * @throws MWException if the model id isn't known.
+ * @throws MWException If the model ID isn't known.
+ * @return string The content model's localized name.
*/
public static function getLocalizedName( $name ) {
+ // Messages: content-model-wikitext, content-model-text,
+ // content-model-javascript, content-model-css
$key = "content-model-$name";
$msg = wfMessage( $key );
@@ -378,12 +384,20 @@ abstract class ContentHandler {
}
$formats = array_unique( $formats );
+
return $formats;
}
// ------------------------------------------------------------------------
+ /**
+ * @var string
+ */
protected $mModelID;
+
+ /**
+ * @var string[]
+ */
protected $mSupportedFormats;
/**
@@ -392,7 +406,7 @@ abstract class ContentHandler {
* provided as literals by subclass's constructors.
*
* @param string $modelId (use CONTENT_MODEL_XXX constants).
- * @param array $formats List for supported serialization formats
+ * @param string[] $formats List for supported serialization formats
* (typically as MIME types)
*/
public function __construct( $modelId, $formats ) {
@@ -409,24 +423,55 @@ abstract class ContentHandler {
*
* @since 1.21
*
- * @param $content Content The Content object to serialize
- * @param $format null|String The desired serialization format
+ * @param Content $content The Content object to serialize
+ * @param string $format The desired serialization format
+ *
* @return string Serialized form of the content
*/
abstract public function serializeContent( Content $content, $format = null );
/**
+ * Applies transformations on export (returns the blob unchanged per default).
+ * Subclasses may override this to perform transformations such as conversion
+ * of legacy formats or filtering of internal meta-data.
+ *
+ * @param string $blob The blob to be exported
+ * @param string|null $format The blob's serialization format
+ *
+ * @return string
+ */
+ public function exportTransform( $blob, $format = null ) {
+ return $blob;
+ }
+
+ /**
* Unserializes a Content object of the type supported by this ContentHandler.
*
* @since 1.21
*
- * @param string $blob serialized form of the content
- * @param $format null|String the format used for serialization
- * @return Content the Content object created by deserializing $blob
+ * @param string $blob Serialized form of the content
+ * @param string $format The format used for serialization
+ *
+ * @return Content The Content object created by deserializing $blob
*/
abstract public function unserializeContent( $blob, $format = null );
/**
+ * Apply import transformation (per default, returns $blob unchanged).
+ * This gives subclasses an opportunity to transform data blobs on import.
+ *
+ * @since 1.24
+ *
+ * @param string $blob
+ * @param string|null $format
+ *
+ * @return string
+ */
+ public function importTransform( $blob, $format = null ) {
+ return $blob;
+ }
+
+ /**
* Creates an empty Content object of the type supported by this
* ContentHandler.
*
@@ -438,7 +483,7 @@ abstract class ContentHandler {
/**
* Creates a new Content object that acts as a redirect to the given page,
- * or null of redirects are not supported by this content model.
+ * or null if redirects are not supported by this content model.
*
* This default implementation always returns null. Subclasses supporting redirects
* must override this method.
@@ -448,10 +493,10 @@ abstract class ContentHandler {
*
* @since 1.21
*
- * @param Title $destination the page to redirect to.
- * @param string $text text to include in the redirect, if possible.
+ * @param Title $destination The page to redirect to.
+ * @param string $text Text to include in the redirect, if possible.
*
- * @return Content
+ * @return Content Always null.
*/
public function makeRedirectContent( Title $destination, $text = '' ) {
return null;
@@ -463,21 +508,19 @@ abstract class ContentHandler {
*
* @since 1.21
*
- * @return String The model ID
+ * @return string The model ID
*/
public function getModelID() {
return $this->mModelID;
}
/**
- * Throws an MWException if $model_id is not the ID of the content model
- * supported by this ContentHandler.
- *
* @since 1.21
*
* @param string $model_id The model to check
*
- * @throws MWException
+ * @throws MWException If the model ID is not the ID of the content model supported by this
+ * ContentHandler.
*/
protected function checkModelID( $model_id ) {
if ( $model_id !== $this->mModelID ) {
@@ -494,7 +537,7 @@ abstract class ContentHandler {
*
* @since 1.21
*
- * @return array of serialization formats as MIME type like strings
+ * @return string[] List of serialization formats as MIME type like strings
*/
public function getSupportedFormats() {
return $this->mSupportedFormats;
@@ -509,7 +552,7 @@ abstract class ContentHandler {
*
* @since 1.21
*
- * @return string the name of the default serialization format as a MIME type
+ * @return string The name of the default serialization format as a MIME type
*/
public function getDefaultFormat() {
return $this->mSupportedFormats[0];
@@ -524,11 +567,11 @@ abstract class ContentHandler {
*
* @since 1.21
*
- * @param string $format the serialization format to check
+ * @param string $format The serialization format to check
+ *
* @return bool
*/
public function isSupportedFormat( $format ) {
-
if ( !$format ) {
return true; // this means "use the default"
}
@@ -537,13 +580,11 @@ abstract class ContentHandler {
}
/**
- * Throws an MWException if isSupportedFormat( $format ) is not true.
- * Convenient for checking whether a format provided as a parameter is
- * actually supported.
+ * Convenient for checking whether a format provided as a parameter is actually supported.
*
- * @param string $format the serialization format to check
+ * @param string $format The serialization format to check
*
- * @throws MWException
+ * @throws MWException If the format is not supported by this content handler.
*/
protected function checkFormat( $format ) {
if ( !$this->isSupportedFormat( $format ) ) {
@@ -562,7 +603,7 @@ abstract class ContentHandler {
*
* @since 1.21
*
- * @return Array
+ * @return array Always an empty array.
*/
public function getActionOverrides() {
return array();
@@ -573,21 +614,18 @@ abstract class ContentHandler {
*
* @since 1.21
*
- * @param $context IContextSource context to use, anything else will be
- * ignored
- * @param $old Integer Old ID we want to show and diff with.
- * @param int|string $new String either 'prev' or 'next'.
- * @param $rcid Integer ??? FIXME (default 0)
- * @param $refreshCache boolean If set, refreshes the diff cache
- * @param $unhide boolean If set, allow viewing deleted revs
+ * @param IContextSource $context Context to use, anything else will be ignored.
+ * @param int $old Revision ID we want to show and diff with.
+ * @param int|string $new Either a revision ID or one of the strings 'cur', 'prev' or 'next'.
+ * @param int $rcid FIXME: Deprecated, no longer used. Defaults to 0.
+ * @param bool $refreshCache If set, refreshes the diff cache. Defaults to false.
+ * @param bool $unhide If set, allow viewing deleted revs. Defaults to false.
*
* @return DifferenceEngine
*/
- public function createDifferenceEngine( IContextSource $context,
- $old = 0, $new = 0,
- $rcid = 0, # FIXME: use everywhere!
- $refreshCache = false, $unhide = false
- ) {
+ public function createDifferenceEngine( IContextSource $context, $old = 0, $new = 0,
+ $rcid = 0, //FIXME: Deprecated, no longer used
+ $refreshCache = false, $unhide = false ) {
$diffEngineClass = $this->getDiffEngineClass();
return new $diffEngineClass( $context, $old, $new, $rcid, $refreshCache, $unhide );
@@ -596,19 +634,21 @@ abstract class ContentHandler {
/**
* Get the language in which the content of the given page is written.
*
- * This default implementation just returns $wgContLang (except for pages in the MediaWiki namespace)
+ * This default implementation just returns $wgContLang (except for pages
+ * in the MediaWiki namespace)
*
- * Note that the pages language is not cacheable, since it may in some cases depend on user settings.
+ * Note that the pages language is not cacheable, since it may in some
+ * cases depend on user settings.
*
* Also note that the page language may or may not depend on the actual content of the page,
* that is, this method may load the content in order to determine the language.
*
* @since 1.21
*
- * @param Title $title the page to determine the language for.
- * @param Content|null $content the page's content, if you have it handy, to avoid reloading it.
+ * @param Title $title The page to determine the language for.
+ * @param Content $content The page's content, if you have it handy, to avoid reloading it.
*
- * @return Language the page's language
+ * @return Language The page's language
*/
public function getPageLanguage( Title $title, Content $content = null ) {
global $wgContLang, $wgLang;
@@ -621,6 +661,7 @@ abstract class ContentHandler {
}
wfRunHooks( 'PageContentLanguage', array( $title, &$pageLang, $wgLang ) );
+
return wfGetLangObj( $pageLang );
}
@@ -639,10 +680,10 @@ abstract class ContentHandler {
*
* @since 1.21
*
- * @param Title $title the page to determine the language for.
- * @param Content|null $content the page's content, if you have it handy, to avoid reloading it.
+ * @param Title $title The page to determine the language for.
+ * @param Content $content The page's content, if you have it handy, to avoid reloading it.
*
- * @return Language the page's language for viewing
+ * @return Language The page's language for viewing
*/
public function getPageViewLanguage( Title $title, Content $content = null ) {
$pageLang = $this->getPageLanguage( $title, $content );
@@ -668,12 +709,19 @@ abstract class ContentHandler {
* typically based on the namespace or some other aspect of the title, such as a special suffix
* (e.g. ".svg" for SVG content).
*
- * @param Title $title the page's title.
+ * @note this calls the ContentHandlerCanBeUsedOn hook which may be used to override which
+ * content model can be used where.
*
- * @return bool true if content of this kind can be used on the given page, false otherwise.
+ * @param Title $title The page's title.
+ *
+ * @return bool True if content of this kind can be used on the given page, false otherwise.
*/
public function canBeUsedOn( Title $title ) {
- return true;
+ $ok = true;
+
+ wfRunHooks( 'ContentModelCanBeUsedOn', array( $this->getModelID(), $title, &$ok ) );
+
+ return $ok;
}
/**
@@ -688,19 +736,18 @@ abstract class ContentHandler {
}
/**
- * Attempts to merge differences between three versions.
- * Returns a new Content object for a clean merge and false for failure or
- * a conflict.
+ * Attempts to merge differences between three versions. Returns a new
+ * Content object for a clean merge and false for failure or a conflict.
*
* This default implementation always returns false.
*
* @since 1.21
*
- * @param $oldContent Content|string String
- * @param $myContent Content|string String
- * @param $yourContent Content|string String
+ * @param Content $oldContent The page's previous content.
+ * @param Content $myContent One of the page's conflicting contents.
+ * @param Content $yourContent One of the page's conflicting contents.
*
- * @return Content|Bool
+ * @return Content|bool Always false.
*/
public function merge3( Content $oldContent, Content $myContent, Content $yourContent ) {
return false;
@@ -711,13 +758,14 @@ abstract class ContentHandler {
*
* @since 1.21
*
- * @param $oldContent Content|null: the previous text of the page.
- * @param $newContent Content|null: The submitted text of the page.
+ * @param Content $oldContent The previous text of the page.
+ * @param Content $newContent The submitted text of the page.
* @param int $flags Bit mask: a bit mask of flags submitted for the edit.
*
* @return string An appropriate auto-summary, or an empty string.
*/
- public function getAutosummary( Content $oldContent = null, Content $newContent = null, $flags ) {
+ public function getAutosummary( Content $oldContent = null, Content $newContent = null,
+ $flags ) {
// Decide what kind of auto-summary is needed.
// Redirect auto-summaries
@@ -733,15 +781,15 @@ abstract class ContentHandler {
if ( is_object( $rt ) ) {
if ( !is_object( $ot )
|| !$rt->equals( $ot )
- || $ot->getFragment() != $rt->getFragment() )
- {
+ || $ot->getFragment() != $rt->getFragment()
+ ) {
$truncatedtext = $newContent->getTextForSummary(
250
- - strlen( wfMessage( 'autoredircomment' )->inContentLanguage()->text() )
- - strlen( $rt->getFullText() ) );
+ - strlen( wfMessage( 'autoredircomment' )->inContentLanguage()->text() )
+ - strlen( $rt->getFullText() ) );
return wfMessage( 'autoredircomment', $rt->getFullText() )
- ->rawParams( $truncatedtext )->inContentLanguage()->text();
+ ->rawParams( $truncatedtext )->inContentLanguage()->text();
}
}
@@ -754,7 +802,7 @@ abstract class ContentHandler {
200 - strlen( wfMessage( 'autosumm-new' )->inContentLanguage()->text() ) );
return wfMessage( 'autosumm-new' )->rawParams( $truncatedtext )
- ->inContentLanguage()->text();
+ ->inContentLanguage()->text();
}
// Blanking auto-summaries
@@ -762,15 +810,20 @@ abstract class ContentHandler {
return wfMessage( 'autosumm-blank' )->inContentLanguage()->text();
} elseif ( !empty( $oldContent )
&& $oldContent->getSize() > 10 * $newContent->getSize()
- && $newContent->getSize() < 500 )
- {
+ && $newContent->getSize() < 500
+ ) {
// Removing more than 90% of the article
$truncatedtext = $newContent->getTextForSummary(
200 - strlen( wfMessage( 'autosumm-replace' )->inContentLanguage()->text() ) );
return wfMessage( 'autosumm-replace' )->rawParams( $truncatedtext )
- ->inContentLanguage()->text();
+ ->inContentLanguage()->text();
+ }
+
+ // New blank article auto-summary
+ if ( $flags & EDIT_NEW && $newContent->isEmpty() ) {
+ return wfMessage( 'autosumm-newblank' )->inContentLanguage()->text();
}
// If we reach this point, there's no applicable auto-summary for our
@@ -783,12 +836,13 @@ abstract class ContentHandler {
*
* @since 1.21
*
- * @param $title Title: the page's title
- * @param &$hasHistory Boolean: whether the page has a history
+ * @param Title $title The page's title
+ * @param bool &$hasHistory Whether the page has a history
+ *
* @return mixed String containing deletion reason or empty string, or
* boolean false if no revision occurred
*
- * @XXX &$hasHistory is extremely ugly, it's here because
+ * @todo &$hasHistory is extremely ugly, it's here because
* WikiPage::getAutoDeleteReason() and Article::generateReason()
* have it / want it.
*/
@@ -891,9 +945,9 @@ abstract class ContentHandler {
*
* @since 1.21
*
- * @param $current Revision The current text
- * @param $undo Revision The revision to undo
- * @param $undoafter Revision Must be an earlier revision than $undo
+ * @param Revision $current The current text
+ * @param Revision $undo The revision to undo
+ * @param Revision $undoafter Must be an earlier revision than $undo
*
* @return mixed String on success, false on failure
*/
@@ -907,6 +961,10 @@ abstract class ContentHandler {
$undo_content = $undo->getContent();
$undoafter_content = $undoafter->getContent();
+ if ( !$undo_content || !$undoafter_content ) {
+ return false; // no content to undo
+ }
+
$this->checkModelID( $cur_content->getModel() );
$this->checkModelID( $undo_content->getModel() );
$this->checkModelID( $undoafter_content->getModel() );
@@ -922,7 +980,7 @@ abstract class ContentHandler {
}
/**
- * Get parser options suitable for rendering the primary article wikitext
+ * Get parser options suitable for rendering and caching the article
*
* @param IContextSource|User|string $context One of the following:
* - IContextSource: Use the User and the Language of the provided
@@ -932,8 +990,6 @@ abstract class ContentHandler {
* - 'canonical': Canonical options (anonymous user with default
* preferences and content language).
*
- * @param IContextSource|User|string $context
- *
* @throws MWException
* @return ParserOptions
*/
@@ -962,7 +1018,7 @@ abstract class ContentHandler {
*
* @since 1.21
*
- * @return bool
+ * @return bool Always false.
*/
public function isParserCacheSupported() {
return false;
@@ -975,7 +1031,7 @@ abstract class ContentHandler {
* Content models that return true here should also implement
* Content::getSection, Content::replaceSection, etc. to handle sections..
*
- * @return boolean whether sections are supported.
+ * @return bool Always false.
*/
public function supportsSections() {
return false;
@@ -988,7 +1044,7 @@ abstract class ContentHandler {
* Content models that return true here should also implement
* ContentHandler::makeRedirectContent to return a Content object.
*
- * @return boolean whether redirects are supported.
+ * @return bool Always false.
*/
public function supportsRedirects() {
return false;
@@ -998,11 +1054,11 @@ abstract class ContentHandler {
* Logs a deprecation warning, visible if $wgDevelopmentWarnings, but only if
* self::$enableDeprecationWarnings is set to true.
*
- * @param string $func The name of the deprecated function
- * @param string $version The version since the method is deprecated. Usually 1.21
- * for ContentHandler related stuff.
- * @param string|bool $component: Component to which the function belongs.
- * If false, it is assumed the function is in MediaWiki core.
+ * @param string $func The name of the deprecated function
+ * @param string $version The version since the method is deprecated. Usually 1.21
+ * for ContentHandler related stuff.
+ * @param string|bool $component : Component to which the function belongs.
+ * If false, it is assumed the function is in MediaWiki core.
*
* @see ContentHandler::$enableDeprecationWarnings
* @see wfDeprecated
@@ -1020,18 +1076,19 @@ abstract class ContentHandler {
* hook function, a new Content object is constructed from the new
* text.
*
- * @param string $event event name
- * @param array $args parameters passed to hook functions
- * @param bool $warn whether to log a warning.
+ * @param string $event Event name
+ * @param array $args Parameters passed to hook functions
+ * @param bool $warn Whether to log a warning.
* Default to self::$enableDeprecationWarnings.
* May be set to false for testing.
*
- * @return Boolean True if no handler aborted the hook
+ * @return bool True if no handler aborted the hook
*
* @see ContentHandler::$enableDeprecationWarnings
*/
public static function runLegacyHooks( $event, $args = array(),
- $warn = null ) {
+ $warn = null
+ ) {
if ( $warn === null ) {
$warn = self::$enableDeprecationWarnings;
@@ -1073,7 +1130,8 @@ abstract class ContentHandler {
wfRestoreWarnings();
- wfWarn( "Using obsolete hook $event via ContentHandler::runLegacyHooks()! Handlers: " . implode( ', ', $handlerInfo ), 2 );
+ wfWarn( "Using obsolete hook $event via ContentHandler::runLegacyHooks()! Handlers: " .
+ implode( ', ', $handlerInfo ), 2 );
}
// convert Content objects to text