diff options
Diffstat (limited to 'includes/content/ContentHandler.php')
-rw-r--r-- | includes/content/ContentHandler.php | 298 |
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 |