Introducing ContentHandler::canBeUsedOn()
[lhc/web/wiklou.git] / includes / ContentHandler.php
index c90e140..584a6ad 100644 (file)
@@ -50,6 +50,7 @@ abstract class ContentHandler {
         * - otherwise, the behaviour is undefined.
         *
         * @since WD.1
+        * @deprecated since WD.1. Always try to use the content object.
         *
         * @static
         * @param $content Content|null
@@ -330,6 +331,12 @@ abstract class ContentHandler {
                else return wfMsg( $key );
        }
 
+       public static function getContentModels() {
+               global $wgContentHandlers;
+
+               return array_keys( $wgContentHandlers );
+       }
+
        public static function getAllContentFormats() {
                global $wgContentHandlers;
 
@@ -542,10 +549,11 @@ abstract class ContentHandler {
        /**
         * Get the language in which the content of the given page is written.
         *
-        * This default implementation returns $wgContLang->getCode().
+        * This default implementation just returns $wgContLang (except for pages in the MediaWiki namespace)
         *
         * Note that a page's language must be permanent and cacheable, that is, it must not depend
-        * on user preferences, request parameters or session state.
+        * on user preferences, request parameters or session state. The only exception is pages in the
+        * MediaWiki namespace.
         *
         * 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.
@@ -559,9 +567,68 @@ abstract class ContentHandler {
         */
        public function getPageLanguage( Title $title, Content $content = null ) {
                global $wgContLang;
+
+               if ( $title->getNamespace() == NS_MEDIAWIKI ) {
+                       // Parse mediawiki messages with correct target language
+                       list( /* $unused */, $lang ) = MessageCache::singleton()->figureMessage( $title->getText() );
+                       return wfGetLangObj( $lang );
+               }
+
                return $wgContLang;
        }
 
+       /**
+        * Get the language in which the content of this page is written when
+        * viewed by user. Defaults to $this->getPageLanguage(), but if the user
+        * specified a preferred variant, the variant will be used.
+        *
+        * This default implementation just returns $this->getPageLanguage( $title, $content ) unless
+        * the user specified a preferred variant.
+        *
+        * Note that the pages view language is not cacheable, since it depends 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.WD
+        *
+        * @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.
+        *
+        * @return Language the page's language code for viewing
+        */
+       public function getPageViewLanguage( Title $title, Content $content = null ) {
+               $pageLang = $this->getPageLanguage( $title, $content );
+
+               if ( $title->getNamespace() !== NS_MEDIAWIKI ) {
+                       // If the user chooses a variant, the content is actually
+                       // in a language whose code is the variant code.
+                       $variant = $pageLang->getPreferredVariant();
+                       if ( $pageLang->getCode() !== $variant ) {
+                               $pageLang = Language::factory( $variant );
+                       }
+               }
+
+               return $pageLang;
+       }
+
+       /**
+        * Determines whether the content type handled by this ContentHandler
+        * can be used on the given page.
+        *
+        * This default implementation always returns true.
+        * Subclasses may override this to restrict the use of this content model to specific locations,
+        * 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.
+        *
+        * @return bool true if content of this kind can be used on the given page, false otherwise.
+        */
+       public function canBeUsedOn( Title $title ) {
+               return true;
+       }
+
        /**
         * Returns the name of the diff engine to use.
         *
@@ -625,10 +692,11 @@ abstract class ContentHandler {
                        {
                                $truncatedtext = $newContent->getTextForSummary(
                                        250
-                                               - strlen( wfMsgForContent( 'autoredircomment' ) )
+                                               - strlen( wfMessage( 'autoredircomment' )->inContentLanguage()->text() )
                                                - strlen( $rt->getFullText() ) );
 
-                               return wfMsgForContent( 'autoredircomment', $rt->getFullText(), $truncatedtext );
+                               return wfMessage( 'autoredircomment', $rt->getFullText() )
+                                               ->rawParams( $truncatedtext )->inContentLanguage()->text();
                        }
                }
 
@@ -638,14 +706,15 @@ abstract class ContentHandler {
                        // the summary.
 
                        $truncatedtext = $newContent->getTextForSummary(
-                               200 - strlen( wfMsgForContent( 'autosumm-new' ) ) );
+                               200 - strlen( wfMessage( 'autosumm-new' )->inContentLanguage()->text() ) );
 
-                       return wfMsgForContent( 'autosumm-new', $truncatedtext );
+                       return wfMessage( 'autosumm-new' )->rawParams( $truncatedtext )
+                                       ->inContentLanguage()->text();
                }
 
                // Blanking auto-summaries
                if ( !empty( $oldContent ) && $oldContent->getSize() > 0 && $newContent->getSize() == 0 ) {
-                       return wfMsgForContent( 'autosumm-blank' );
+                       return wfMessage( 'autosumm-blank' )->inContentLanguage()->text();
                } elseif ( !empty( $oldContent )
                        && $oldContent->getSize() > 10 * $newContent->getSize()
                        && $newContent->getSize() < 500 )
@@ -653,14 +722,14 @@ abstract class ContentHandler {
                        // Removing more than 90% of the article
 
                        $truncatedtext = $newContent->getTextForSummary(
-                               200 - strlen( wfMsgForContent( 'autosumm-replace' ) ) );
+                               200 - strlen( wfMessage( 'autosumm-replace' )->inContentLanguage()->text() ) );
 
-                       return wfMsgForContent( 'autosumm-replace', $truncatedtext );
+                       return wfMessage( 'autosumm-replace' )->rawParams( $truncatedtext )
+                                       ->inContentLanguage()->text();
                }
 
                // If we reach this point, there's no applicable auto-summary for our
                // case, so our auto-summary is empty.
-
                return '';
        }
 
@@ -742,12 +811,16 @@ abstract class ContentHandler {
                if ( $blank ) {
                        // The current revision is blank and the one before is also
                        // blank. It's just not our lucky day
-                       $reason = wfMsgForContent( 'exbeforeblank', '$1' );
+                       $reason = wfMessage( 'exbeforeblank', '$1' )->inContentLanguage()->text();
                } else {
                        if ( $onlyAuthor ) {
-                               $reason = wfMsgForContent( 'excontentauthor', '$1', $onlyAuthor );
+                               $reason = wfMessage(
+                                       'excontentauthor',
+                                       '$1',
+                                       $onlyAuthor
+                               )->inContentLanguage()->text();
                        } else {
-                               $reason = wfMsgForContent( 'excontent', '$1' );
+                               $reason = wfMessage( 'excontent', '$1' )->inContentLanguage()->text();
                        }
                }
 
@@ -765,71 +838,6 @@ abstract class ContentHandler {
                return $reason;
        }
 
-       /**
-        * Parse the Content object and generate a ParserOutput from the result.
-        * $result->getText() can be used to obtain the generated HTML. If no HTML
-        * is needed, $generateHtml can be set to false; in that case,
-        * $result->getText() may return null.
-        *
-        * @param $content Content the content to render
-        * @param $title Title The page title to use as a context for rendering
-        * @param $revId null|int The revision being rendered (optional)
-        * @param $options null|ParserOptions Any parser options
-        * @param $generateHtml Boolean Whether to generate HTML (default: true). If false,
-        *        the result of calling getText() on the ParserOutput object returned by
-        *        this method is undefined.
-        *
-        * @since WD.1
-        *
-        * @return ParserOutput
-        */
-       public abstract function getParserOutput( Content $content, Title $title,
-               $revId = null,
-               ParserOptions $options = null, $generateHtml = true );
-       # TODO: make RenderOutput and RenderOptions base classes
-
-       /**
-        * Returns a list of DataUpdate objects for recording information about this
-        * Content in some secondary data store. If the optional second argument,
-        * $old, is given, the updates may model only the changes that need to be
-        * made to replace information about the old content with information about
-        * the new content.
-        *
-        * This default implementation calls
-        * $this->getParserOutput( $content, $title, null, null, false ),
-        * and then calls getSecondaryDataUpdates( $title, $recursive ) on the
-        * resulting ParserOutput object.
-        *
-        * Subclasses may implement this to determine the necessary updates more
-        * efficiently, or make use of information about the old content.
-        *
-        * @param $content Content The content for determining the necessary updates
-        * @param $title Title The context for determining the necessary updates
-        * @param $old Content|null An optional Content object representing the
-        *    previous content, i.e. the content being replaced by this Content
-        *    object.
-        * @param $recursive boolean Whether to include recursive updates (default:
-        *    false).
-        * @param $parserOutput ParserOutput|null Optional ParserOutput object.
-        *    Provide if you have one handy, to avoid re-parsing of the content.
-        *
-        * @return Array. A list of DataUpdate objects for putting information
-        *    about this content object somewhere.
-        *
-        * @since WD.1
-        */
-       public function getSecondaryDataUpdates( Content $content, Title $title,
-               Content $old = null,
-               $recursive = true, ParserOutput $parserOutput = null
-       ) {
-               if ( !$parserOutput ) {
-                       $parserOutput = $this->getParserOutput( $content, $title, null, null, false );
-               }
-
-               return $parserOutput->getSecondaryDataUpdates( $title, $recursive );
-       }
-
-
        /**
         * Get the Content object that needs to be saved in order to undo all revisions
         * between $undo and $undoafter. Revisions must belong to the same page,
@@ -879,30 +887,6 @@ abstract class ContentHandler {
                return true;
        }
 
-       /**
-        * Returns a list of updates to perform when the given content is deleted.
-        * The necessary updates may be taken from the Content object, or depend on
-        * the current state of the database.
-        *
-        * @since WD.1
-        *
-        * @param $content \Content the Content object for deletion
-        * @param $title \Title the title of the deleted page
-        * @param $parserOutput null|\ParserOutput optional parser output object
-        *    for efficient access to meta-information about the content object.
-        *    Provide if you have one handy.
-        *
-        * @return array A list of DataUpdate instances that will clean up the
-        *    database after deletion.
-        */
-       public function getDeletionUpdates( Content $content, Title $title,
-               ParserOutput $parserOutput = null )
-       {
-               return array(
-                       new LinksDeletionUpdate( $title ),
-               );
-       }
-
        /**
         * Returns true if this content model supports sections.
         *
@@ -913,6 +897,66 @@ abstract class ContentHandler {
        public function supportsSections() {
                return false;
        }
+
+       /**
+        * Call a legacy hook that uses text instead of Content objects.
+        * Will log a warning when a matching hook function is registered.
+        * If the textual representation of the content is changed by the
+        * hook function, a new Content object is constructed from the new
+        * text.
+        *
+        * @param $event String: event name
+        * @param $args Array: parameters passed to hook functions
+        * @param $warn bool: whether to log a warning (default: true). Should generally be true,
+        *                    may be set to false for testing.
+        *
+        * @return Boolean True if no handler aborted the hook
+        */
+       public static function runLegacyHooks( $event, $args = array(), $warn = true ) {
+               if ( !Hooks::isRegistered( $event ) ) {
+                       return true; // nothing to do here
+               }
+
+               if ( $warn ) {
+                       wfWarn( "Using obsolete hook $event" );
+               }
+
+               // convert Content objects to text
+               $contentObjects = array();
+               $contentTexts = array();
+
+               foreach ( $args as $k => $v ) {
+                       if ( $v instanceof Content ) {
+                               /* @var Content $v */
+
+                               $contentObjects[$k] = $v;
+
+                               $v = $v->serialize();
+                               $contentTexts[ $k ] = $v;
+                               $args[ $k ] = $v;
+                       }
+               }
+
+               // call the hook functions
+               $ok = wfRunHooks( $event, $args );
+
+               // see if the hook changed the text
+               foreach ( $contentTexts as $k => $orig ) {
+                       /* @var Content $content */
+
+                       $modified = $args[ $k ];
+                       $content = $contentObjects[$k];
+
+                       if ( $modified !== $orig ) {
+                               // text was changed, create updated Content object
+                               $content = $content->getContentHandler()->unserializeContent( $modified );
+                       }
+
+                       $args[ $k ] = $content;
+               }
+
+               return $ok;
+       }
 }
 
 /**
@@ -976,71 +1020,6 @@ abstract class TextContentHandler extends ContentHandler {
                return $mergedContent;
        }
 
-       /**
-        * Returns a generic ParserOutput object, wrapping the HTML returned by
-        * getHtml().
-        *
-        * @param $content Content The content to render
-        * @param $title Title Context title for parsing
-        * @param $revId int|null Revision ID (for {{REVISIONID}})
-        * @param $options ParserOptions|null Parser options
-        * @param $generateHtml bool Whether or not to generate HTML
-        *
-        * @return ParserOutput representing the HTML form of the text
-        */
-       public function getParserOutput( Content $content, Title $title,
-               $revId = null,
-               ParserOptions $options = null, $generateHtml = true
-       ) {
-               $this->checkModelID( $content->getModel() );
-
-               # Generic implementation, relying on $this->getHtml()
-
-               if ( $generateHtml ) {
-                       $html = $this->getHtml( $content );
-               } else {
-                       $html = '';
-               }
-
-               $po = new ParserOutput( $html );
-               return $po;
-       }
-
-       /**
-        * Generates an HTML version of the content, for display. Used by
-        * getParserOutput() to construct a ParserOutput object.
-        *
-        * This default implementation just calls getHighlightHtml(). Content
-        * models that have another mapping to HTML (as is the case for markup
-        * languages like wikitext) should override this method to generate the
-        * appropriate HTML.
-        *
-        * @param $content Content The content to render
-        *
-        * @return string An HTML representation of the content
-        */
-       protected function getHtml( Content $content ) {
-               $this->checkModelID( $content->getModel() );
-
-               return $this->getHighlightHtml( $content );
-       }
-
-       /**
-        * Generates a syntax-highlighted version the content, as HTML.
-        * Used by the default implementation of getHtml().
-        *
-        * @param $content Content the content to render
-        *
-        * @return string an HTML representation of the content's markup
-        */
-       protected function getHighlightHtml( Content $content ) {
-               $this->checkModelID( $content->getModel() );
-
-               # TODO: make Highlighter interface, use highlighter here, if available
-               return htmlspecialchars( $content->getNativeData() );
-       }
-
-
 }
 
 /**
@@ -1062,44 +1041,6 @@ class WikitextContentHandler extends TextContentHandler {
                return new WikitextContent( '' );
        }
 
-       /**
-        * Returns a ParserOutput object resulting from parsing the content's text
-        * using $wgParser.
-        *
-        * @since    WD.1
-        *
-        * @param $content Content the content to render
-        * @param $title \Title
-        * @param $revId null
-        * @param $options null|ParserOptions
-        * @param $generateHtml bool
-        *
-        * @internal param \IContextSource|null $context
-        * @return ParserOutput representing the HTML form of the text
-        */
-       public function getParserOutput( Content $content, Title $title,
-               $revId = null,
-               ParserOptions $options = null, $generateHtml = true
-       ) {
-               global $wgParser;
-
-               $this->checkModelID( $content->getModel() );
-
-               if ( !$options ) {
-                       $options = new ParserOptions();
-               }
-
-               $po = $wgParser->parse( $content->getNativeData(), $title, $options, true, true, $revId );
-               return $po;
-       }
-
-       protected function getHtml( Content $content ) {
-               throw new MWException(
-                       "getHtml() not implemented for wikitext. "
-                       . "Use getParserOutput()->getText()."
-               );
-       }
-
        /**
         * Returns true because wikitext supports sections.
         *
@@ -1142,13 +1083,15 @@ class JavaScriptContentHandler extends TextContentHandler {
                return wfGetLangObj( 'en' );
        }
 
-       protected function getHtml( Content $content ) {
-               $html = "";
-               $html .= "<pre class=\"mw-code mw-js\" dir=\"ltr\">\n";
-               $html .= $this->getHighlightHtml( $content );
-               $html .= "\n</pre>\n";
-
-               return $html;
+       /**
+        * Returns the english language, because CSS is english, and should be handled as such.
+        *
+        * @return Language wfGetLangObj( 'en' )
+        *
+        * @see ContentHandler::getPageViewLanguage()
+        */
+       public function getPageViewLanguage( Title $title, Content $content = null ) {
+               return wfGetLangObj( 'en' );
        }
 }
 
@@ -1182,12 +1125,14 @@ class CssContentHandler extends TextContentHandler {
                return wfGetLangObj( 'en' );
        }
 
-       protected function getHtml( Content $content ) {
-               $html = "";
-               $html .= "<pre class=\"mw-code mw-css\" dir=\"ltr\">\n";
-               $html .= $this->getHighlightHtml( $content );
-               $html .= "\n</pre>\n";
-
-               return $html;
+       /**
+        * Returns the english language, because CSS is english, and should be handled as such.
+        *
+        * @return Language wfGetLangObj( 'en' )
+        *
+        * @see ContentHandler::getPageViewLanguage()
+        */
+       public function getPageViewLanguage( Title $title, Content $content = null ) {
+               return wfGetLangObj( 'en' );
        }
 }