Merge "Add LinkRenderer (rewrite of Linker::link())"
[lhc/web/wiklou.git] / includes / Linker.php
index 6a869dd..990ad15 100644 (file)
@@ -20,6 +20,7 @@
  * @file
  */
 use MediaWiki\Linker\LinkTarget;
+use MediaWiki\MediaWikiServices;
 
 /**
  * Some internal bits split of from Skin.php. These functions are used
@@ -210,55 +211,33 @@ class Linker {
                        wfDeprecated( __METHOD__ . ' with parameter $query as string (should be array)', '1.20' );
                        $query = wfCgiToArray( $query );
                }
-               $options = (array)$options;
-
-               $dummy = new DummyLinker; // dummy linker instance for bc on the hooks
-
-               $ret = null;
-               if ( !Hooks::run( 'LinkBegin',
-                       [ $dummy, $target, &$html, &$customAttribs, &$query, &$options, &$ret ] )
-               ) {
-                       return $ret;
-               }
-
-               # Normalize the Title if it's a special page
-               $target = self::normaliseSpecialPage( $target );
 
-               # If we don't know whether the page exists, let's find out.
-               if ( !in_array( 'known', $options, true ) && !in_array( 'broken', $options, true ) ) {
-                       if ( $target->isKnown() ) {
-                               $options[] = 'known';
-                       } else {
-                               $options[] = 'broken';
+               $services = MediaWikiServices::getInstance();
+               $options = (array)$options;
+               if ( $options ) {
+                       // Custom options, create new LinkRenderer
+                       if ( !isset( $options['stubThreshold'] ) ) {
+                               global $wgUser;
+                               $options['stubThreshold'] = $wgUser->getStubThreshold();
                        }
+                       $linkRenderer = $services->getLinkRendererFactory()
+                               ->createFromLegacyOptions( $options );
+               } else {
+                       $linkRenderer = $services->getLinkRenderer();
                }
 
-               $oldquery = [];
-               if ( in_array( "forcearticlepath", $options, true ) && $query ) {
-                       $oldquery = $query;
-                       $query = [];
-               }
-
-               # Note: we want the href attribute first, for prettiness.
-               $attribs = [ 'href' => self::linkUrl( $target, $query, $options ) ];
-               if ( in_array( 'forcearticlepath', $options, true ) && $oldquery ) {
-                       $attribs['href'] = wfAppendQuery( $attribs['href'], $oldquery );
-               }
-
-               $attribs = array_merge(
-                       $attribs,
-                       self::linkAttribs( $target, $customAttribs, $options )
-               );
-               if ( is_null( $html ) ) {
-                       $html = self::linkText( $target );
+               if ( $html !== null ) {
+                       $text = new HtmlArmor( $html );
+               } else {
+                       $text = $html; // null
                }
-
-               $ret = null;
-               if ( Hooks::run( 'LinkEnd', [ $dummy, $target, $options, &$html, &$attribs, &$ret ] ) ) {
-                       $ret = Html::rawElement( 'a', $attribs, $html );
+               if ( in_array( 'known', $options, true ) ) {
+                       return $linkRenderer->makeKnownLink( $target, $text, $customAttribs, $query );
+               } elseif ( in_array( 'broken', $options, true ) ) {
+                       return $linkRenderer->makeBrokenLink( $target, $text, $customAttribs, $query );
+               } else {
+                       return $linkRenderer->makeLink( $target, $text, $customAttribs, $query );
                }
-
-               return $ret;
        }
 
        /**
@@ -269,135 +248,11 @@ class Linker {
         */
        public static function linkKnown(
                $target, $html = null, $customAttribs = [],
-               $query = [], $options = [ 'known', 'noclasses' ]
+               $query = [], $options = [ 'known' ]
        ) {
                return self::link( $target, $html, $customAttribs, $query, $options );
        }
 
-       /**
-        * Returns the Url used to link to a Title
-        *
-        * @param LinkTarget $target
-        * @param array $query Query parameters
-        * @param array $options
-        * @return string
-        */
-       private static function linkUrl( LinkTarget $target, $query, $options ) {
-               # We don't want to include fragments for broken links, because they
-               # generally make no sense.
-               if ( in_array( 'broken', $options, true ) && $target->hasFragment() ) {
-                       $target = $target->createFragmentTarget( '' );
-               }
-
-               # If it's a broken link, add the appropriate query pieces, unless
-               # there's already an action specified, or unless 'edit' makes no sense
-               # (i.e., for a nonexistent special page).
-               if ( in_array( 'broken', $options, true ) && empty( $query['action'] )
-                       && $target->getNamespace() !== NS_SPECIAL ) {
-                       $query['action'] = 'edit';
-                       $query['redlink'] = '1';
-               }
-
-               if ( in_array( 'http', $options, true ) ) {
-                       $proto = PROTO_HTTP;
-               } elseif ( in_array( 'https', $options, true ) ) {
-                       $proto = PROTO_HTTPS;
-               } else {
-                       $proto = PROTO_RELATIVE;
-               }
-
-               $title = Title::newFromLinkTarget( $target );
-               $ret = $title->getLinkURL( $query, false, $proto );
-               return $ret;
-       }
-
-       /**
-        * Returns the array of attributes used when linking to the Title $target
-        *
-        * @param Title $target
-        * @param array $attribs
-        * @param array $options
-        *
-        * @return array
-        */
-       private static function linkAttribs( $target, $attribs, $options ) {
-               global $wgUser;
-               $defaults = [];
-
-               if ( !in_array( 'noclasses', $options, true ) ) {
-                       # Now build the classes.
-                       $classes = [];
-
-                       if ( in_array( 'broken', $options, true ) ) {
-                               $classes[] = 'new';
-                       }
-
-                       if ( $target->isExternal() ) {
-                               $classes[] = 'extiw';
-                       }
-
-                       if ( !in_array( 'broken', $options, true ) ) { # Avoid useless calls to LinkCache (see r50387)
-                               $colour = self::getLinkColour(
-                                       $target,
-                                       isset( $options['stubThreshold'] ) ? $options['stubThreshold'] : $wgUser->getStubThreshold()
-                               );
-                               if ( $colour !== '' ) {
-                                       $classes[] = $colour; # mw-redirect or stub
-                               }
-                       }
-                       if ( $classes != [] ) {
-                               $defaults['class'] = implode( ' ', $classes );
-                       }
-               }
-
-               # Get a default title attribute.
-               if ( $target->getPrefixedText() == '' ) {
-                       # A link like [[#Foo]].  This used to mean an empty title
-                       # attribute, but that's silly.  Just don't output a title.
-               } elseif ( in_array( 'known', $options, true ) ) {
-                       $defaults['title'] = $target->getPrefixedText();
-               } else {
-                       // This ends up in parser cache!
-                       $defaults['title'] = wfMessage( 'red-link-title', $target->getPrefixedText() )
-                               ->inContentLanguage()
-                               ->text();
-               }
-
-               # Finally, merge the custom attribs with the default ones, and iterate
-               # over that, deleting all "false" attributes.
-               $ret = [];
-               $merged = Sanitizer::mergeAttributes( $defaults, $attribs );
-               foreach ( $merged as $key => $val ) {
-                       # A false value suppresses the attribute, and we don't want the
-                       # href attribute to be overridden.
-                       if ( $key != 'href' && $val !== false ) {
-                               $ret[$key] = $val;
-                       }
-               }
-               return $ret;
-       }
-
-       /**
-        * Default text of the links to the Title $target
-        *
-        * @param Title $target
-        *
-        * @return string
-        */
-       private static function linkText( $target ) {
-               if ( !$target instanceof Title ) {
-                       wfWarn( __METHOD__ . ': Requires $target to be a Title object.' );
-                       return '';
-               }
-               // If the target is just a fragment, with no title, we return the fragment
-               // text.  Otherwise, we return the title text itself.
-               if ( $target->getPrefixedText() === '' && $target->hasFragment() ) {
-                       return htmlspecialchars( $target->getFragment() );
-               }
-
-               return htmlspecialchars( $target->getPrefixedText() );
-       }
-
        /**
         * Make appropriate markup for a link to the current article. This is
         * currently rendered as the bold link text. The calling sequence is the
@@ -940,7 +795,15 @@ class Linker {
                        $redir = RepoGroup::singleton()->getLocalRepo()->checkRedirect( $title );
 
                        if ( $redir ) {
-                               return self::linkKnown( $title, $encLabel, [], wfCgiToArray( $query ) );
+                               // We already know it's a redirect, so mark it
+                               // accordingly
+                               return self::link(
+                                       $title,
+                                       $encLabel,
+                                       [ 'class' => 'mw-redirect' ],
+                                       wfCgiToArray( $query ),
+                                       [ 'known', 'noclasses' ]
+                               );
                        }
 
                        $href = self::getUploadUrl( $title, $query );
@@ -950,7 +813,7 @@ class Linker {
                                $encLabel . '</a>';
                }
 
-               return self::linkKnown( $title, $encLabel, [], wfCgiToArray( $query ) );
+               return self::link( $title, $encLabel, [], wfCgiToArray( $query ), [ 'known', 'noclasses' ] );
        }
 
        /**
@@ -1084,7 +947,16 @@ class Linker {
                if ( !$title ) {
                        $title = $wgTitle;
                }
-               $attribs['rel'] = Parser::getExternalLinkRel( $url, $title );
+               $newRel = Parser::getExternalLinkRel( $url, $title );
+               if ( !isset( $attribs['rel'] ) || $attribs['rel'] === '' ) {
+                       $attribs['rel'] = $newRel;
+               } elseif ( $newRel !== '' ) {
+                       // Merge the rel attributes.
+                       $newRels = explode( ' ', $newRel );
+                       $oldRels = explode( ' ', $attribs['rel'] );
+                       $combined = array_unique( array_merge( $newRels, $oldRels ) );
+                       $attribs['rel'] = implode( ' ', $combined );
+               }
                $link = '';
                $success = Hooks::run( 'LinkerMakeExternalLink',
                        [ &$url, &$text, &$link, &$attribs, $linktype ] );
@@ -1872,7 +1744,9 @@ class Linker {
         * work if $wgShowRollbackEditCount is disabled, so this can only function
         * as an additional check.
         *
-        * If the option noBrackets is set the rollback link wont be enclosed in []
+        * If the option noBrackets is set the rollback link wont be enclosed in "[]".
+        *
+        * See the "mediawiki.page.rollback" module for the client-side handling of this link.
         *
         * @since 1.16.3. $context added in 1.20. $options added in 1.21
         *
@@ -1902,6 +1776,8 @@ class Linker {
                        $inner = $context->msg( 'brackets' )->rawParams( $inner )->escaped();
                }
 
+               $context->getOutput()->addModules( 'mediawiki.page.rollback' );
+
                return '<span class="mw-rollback-link">' . $inner . '</span>';
        }
 
@@ -1996,11 +1872,13 @@ class Linker {
                $query = [
                        'action' => 'rollback',
                        'from' => $rev->getUserText(),
-                       'token' => $context->getUser()->getEditToken( [
-                               $title->getPrefixedText(),
-                               $rev->getUserText()
-                       ] ),
                ];
+               $attrs = [
+                       'data-mw' => 'interface',
+                       'title' => $context->msg( 'tooltip-rollback' )->text(),
+               ];
+               $options = [ 'known', 'noclasses' ];
+
                if ( $context->getRequest()->getBool( 'bot' ) ) {
                        $query['bot'] = '1';
                        $query['hidediff'] = '1'; // bug 15999
@@ -2025,27 +1903,16 @@ class Linker {
                        }
 
                        if ( $editCount > $wgShowRollbackEditCount ) {
-                               $editCount_output = $context->msg( 'rollbacklinkcount-morethan' )
+                               $html = $context->msg( 'rollbacklinkcount-morethan' )
                                        ->numParams( $wgShowRollbackEditCount )->parse();
                        } else {
-                               $editCount_output = $context->msg( 'rollbacklinkcount' )->numParams( $editCount )->parse();
+                               $html = $context->msg( 'rollbacklinkcount' )->numParams( $editCount )->parse();
                        }
 
-                       return self::link(
-                               $title,
-                               $editCount_output,
-                               [ 'title' => $context->msg( 'tooltip-rollback' )->text() ],
-                               $query,
-                               [ 'known', 'noclasses' ]
-                       );
+                       return self::link( $title, $html, $attrs, $query, $options );
                } else {
-                       return self::link(
-                               $title,
-                               $context->msg( 'rollbacklink' )->escaped(),
-                               [ 'title' => $context->msg( 'tooltip-rollback' )->text() ],
-                               $query,
-                               [ 'known', 'noclasses' ]
-                       );
+                       $html = $context->msg( 'rollbacklink' )->escaped();
+                       return self::link( $title, $html, $attrs, $query, $options );
                }
        }