Merge "Misc follow-ups to I2fc3966e (a161c5e)"
[lhc/web/wiklou.git] / includes / Linker.php
index 05a9905..a96bc5f 100644 (file)
@@ -33,7 +33,7 @@ class Linker {
         * Flags for userToolLinks()
         */
        const TOOL_LINKS_NOBLOCK = 1;
-       const TOOL_LINKS_EMAIL   = 2;
+       const TOOL_LINKS_EMAIL = 2;
 
        /**
         * Get the appropriate HTML attributes to add to the "a" element of an ex-
@@ -600,9 +600,9 @@ class Linker {
                $prefix = $postfix = '';
 
                if ( 'center' == $fp['align'] ) {
-                       $prefix  = '<div class="center">';
+                       $prefix = '<div class="center">';
                        $postfix = '</div>';
-                       $fp['align']   = 'none';
+                       $fp['align'] = 'none';
                }
                if ( $file && !isset( $hp['width'] ) ) {
                        if ( isset( $hp['height'] ) && $file->isVectorized() ) {
@@ -724,7 +724,7 @@ class Linker {
                                $extLinkAttrs = $parser->getExternalLinkAttribs( $frameParams['link-url'] );
                                foreach ( $extLinkAttrs as $name => $val ) {
                                        // Currently could include 'rel' and 'target'
-                                       $mtoParams['parser-extlink-'.$name] = $val;
+                                       $mtoParams['parser-extlink-' . $name] = $val;
                                }
                        }
                } elseif ( isset( $frameParams['link-title'] ) && $frameParams['link-title'] !== '' ) {
@@ -751,7 +751,7 @@ class Linker {
         * @return mixed
         */
        public static function makeThumbLinkObj( Title $title, $file, $label = '', $alt,
-               $align = 'right', $params = array(), $framed = false , $manualthumb = "" )
+               $align = 'right', $params = array(), $framed = false, $manualthumb = "" )
        {
                $frameParams = array(
                        'alt' => $alt,
@@ -947,10 +947,10 @@ class Linker {
                        return '<a href="' . htmlspecialchars( $href ) . '" class="new" title="' .
                                htmlspecialchars( $title->getPrefixedText(), ENT_QUOTES ) . '">' .
                                $encLabel . '</a>';
-               } else {
-                       wfProfileOut( __METHOD__ );
-                       return self::linkKnown( $title, $encLabel, array(), wfCgiToArray( $query ) );
                }
+
+               wfProfileOut( __METHOD__ );
+               return self::linkKnown( $title, $encLabel, array(), wfCgiToArray( $query ) );
        }
 
        /**
@@ -1028,7 +1028,7 @@ class Linker {
                        $key = strtolower( $name );
                }
 
-               return self::linkKnown( SpecialPage::getTitleFor( $name ) , wfMessage( $key )->text() );
+               return self::linkKnown( SpecialPage::getTitleFor( $name ), wfMessage( $key )->text() );
        }
 
        /**
@@ -1038,9 +1038,11 @@ class Linker {
         * @param $escape Boolean: do we escape the link text?
         * @param $linktype String: type of external link. Gets added to the classes
         * @param $attribs Array of extra attributes to <a>
+        * @param $title Title|null Title object used for title specific link attributes
         * @return string
         */
-       public static function makeExternalLink( $url, $text, $escape = true, $linktype = '', $attribs = array() ) {
+       public static function makeExternalLink( $url, $text, $escape = true, $linktype = '', $attribs = array(), $title = null ) {
+               global $wgTitle;
                $class = "external";
                if ( $linktype ) {
                        $class .= " $linktype";
@@ -1053,6 +1055,11 @@ class Linker {
                if ( $escape ) {
                        $text = htmlspecialchars( $text );
                }
+
+               if ( !$title ) {
+                       $title = $wgTitle;
+               }
+               $attribs['rel'] = Parser::getExternalLinkRel( $url, $title );
                $link = '';
                $success = wfRunHooks( 'LinkerMakeExternalLink',
                        array( &$url, &$text, &$link, &$attribs, $linktype ) );
@@ -1665,11 +1672,10 @@ class Linker {
                $lang = wfGetLangObj( $lang );
                $title = wfMessage( 'toc' )->inLanguage( $lang )->escaped();
 
-               return
-                  '<table id="toc" class="toc"><tr><td>'
-                . '<div id="toctitle"><h2>' . $title . "</h2></div>\n"
-                . $toc
-                . "</ul>\n</td></tr></table>\n";
+               return '<table id="toc" class="toc"><tr><td>'
+                       . '<div id="toctitle"><h2>' . $title . "</h2></div>\n"
+                       . $toc
+                       . "</ul>\n</td></tr></table>\n";
        }
 
        /**
@@ -1756,19 +1762,101 @@ class Linker {
         * changes, so this allows sysops to combat a busy vandal without bothering
         * other users.
         *
+        * If the option verify is set this function will return the link only in case the
+        * revision can be reverted. Please note that due to performance limitations
+        * it might be assumed that a user isn't the only contributor of a page while
+        * (s)he is, which will lead to useless rollback links. Furthermore this wont
+        * 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 []
+        *
         * @param $rev Revision object
         * @param $context IContextSource context to use or null for the main context.
+        * @param $options array
         * @return string
         */
-       public static function generateRollback( $rev, IContextSource $context = null ) {
+       public static function generateRollback( $rev, IContextSource $context = null, $options = array( 'verify' ) ) {
                if ( $context === null ) {
                        $context = RequestContext::getMain();
                }
+               $editCount = false;
+               if ( in_array( 'verify', $options ) ) {
+                       $editCount = self::getRollbackEditCount( $rev, true );
+                       if ( $editCount === false ) {
+                               return '';
+                       }
+               }
+
+               $inner = self::buildRollbackLink( $rev, $context, $editCount );
+
+               if ( !in_array( 'noBrackets', $options ) ) {
+                       $inner = $context->msg( 'brackets' )->rawParams( $inner )->plain();
+               }
+
+               return '<span class="mw-rollback-link">' . $inner . '</span>';
+       }
+
+       /**
+        * This function will return the number of revisions which a rollback
+        * would revert and, if $verify is set it will verify that a revision
+        * can be reverted (that the user isn't the only contributor and the
+        * revision we might rollback to isn't deleted). These checks can only
+        * function as an additional check as this function only checks against
+        * the last $wgShowRollbackEditCount edits.
+        *
+        * Returns null if $wgShowRollbackEditCount is disabled or false if $verify
+        * is set and the user is the only contributor of the page.
+        *
+        * @param $rev Revision object
+        * @param $verify Bool Try to verfiy that this revision can really be rolled back
+        * @return integer|bool|null
+        */
+       public static function getRollbackEditCount( $rev, $verify ) {
+               global $wgShowRollbackEditCount;
+               if ( !is_int( $wgShowRollbackEditCount ) || !$wgShowRollbackEditCount > 0 ) {
+                       // Nothing has happened, indicate this by returning 'null'
+                       return null;
+               }
+
+               $dbr = wfGetDB( DB_SLAVE );
+
+               // Up to the value of $wgShowRollbackEditCount revisions are counted
+               $res = $dbr->select(
+                       'revision',
+                       array( 'rev_user_text', 'rev_deleted' ),
+                       // $rev->getPage() returns null sometimes
+                       array( 'rev_page' => $rev->getTitle()->getArticleID() ),
+                       __METHOD__,
+                       array(
+                               'USE INDEX' => array( 'revision' => 'page_timestamp' ),
+                               'ORDER BY' => 'rev_timestamp DESC',
+                               'LIMIT' => $wgShowRollbackEditCount + 1
+                       )
+               );
 
-               return '<span class="mw-rollback-link">'
-                       . $context->msg( 'brackets' )->rawParams(
-                               self::buildRollbackLink( $rev, $context ) )->plain()
-                       . '</span>';
+               $editCount = 0;
+               $moreRevs = false;
+               foreach ( $res as $row ) {
+                       if ( $rev->getRawUserText() != $row->rev_user_text ) {
+                               if ( $verify && ( $row->rev_deleted & Revision::DELETED_TEXT || $row->rev_deleted & Revision::DELETED_USER ) ) {
+                                       // If the user or the text of the revision we might rollback to is deleted in some way we can't rollback
+                                       // Similar to the sanity checks in WikiPage::commitRollback
+                                       return false;
+                               }
+                               $moreRevs = true;
+                               break;
+                       }
+                       $editCount++;
+               }
+
+               if ( $verify && $editCount <= $wgShowRollbackEditCount && !$moreRevs ) {
+                       // We didn't find at least $wgShowRollbackEditCount revisions made by the current user
+                       // and there weren't any other revisions. That means that the current user is the only
+                       // editor, so we can't rollback
+                       return false;
+               }
+               return $editCount;
        }
 
        /**
@@ -1776,9 +1864,10 @@ class Linker {
         *
         * @param $rev Revision object
         * @param $context IContextSource context to use or null for the main context.
+        * @param $editCount integer Number of edits that would be reverted
         * @return String: HTML fragment
         */
-       public static function buildRollbackLink( $rev, IContextSource $context = null ) {
+       public static function buildRollbackLink( $rev, IContextSource $context = null, $editCount = false ) {
                global $wgShowRollbackEditCount, $wgMiserMode;
 
                // To config which pages are effected by miser mode
@@ -1810,25 +1899,8 @@ class Linker {
                }
 
                if( !$disableRollbackEditCount && is_int( $wgShowRollbackEditCount ) && $wgShowRollbackEditCount > 0 ) {
-                       $dbr = wfGetDB( DB_SLAVE );
-
-                       // Up to the value of $wgShowRollbackEditCount revisions are counted
-                       $res = $dbr->select( 'revision',
-                               array( 'rev_id', 'rev_user_text' ),
-                               // $rev->getPage() returns null sometimes
-                               array( 'rev_page' => $rev->getTitle()->getArticleID() ),
-                               __METHOD__,
-                               array(  'USE INDEX' => 'page_timestamp',
-                                       'ORDER BY' => 'rev_timestamp DESC',
-                                       'LIMIT' => $wgShowRollbackEditCount + 1 )
-                       );
-
-                       $editCount = 0;
-                       while( $row = $dbr->fetchObject( $res ) ) {
-                               if( $rev->getUserText() != $row->rev_user_text ) {
-                                       break;
-                               }
-                               $editCount++;
+                       if ( !is_numeric( $editCount ) ) {
+                               $editCount = self::getRollbackEditCount( $rev, false );
                        }
 
                        if( $editCount > $wgShowRollbackEditCount ) {
@@ -1858,13 +1930,19 @@ class Linker {
        /**
         * Returns HTML for the "templates used on this page" list.
         *
-        * @param $templates Array of templates from Article::getUsedTemplate
-        * or similar
-        * @param $preview Boolean: whether this is for a preview
-        * @param $section Boolean: whether this is for a section edit
+        * Make an HTML list of templates, and then add a "More..." link at
+        * the bottom. If $more is null, do not add a "More..." link. If $more
+        * is a Title, make a link to that title and use it. If $more is a string,
+        * directly paste it in as the link (escaping needs to be done manually).
+        * Finally, if $more is a Message, call toString().
+        *
+        * @param array $templates Array of templates from Article::getUsedTemplate or similar
+        * @param bool $preview Whether this is for a preview
+        * @param bool $section Whether this is for a section edit
+        * @param Title|Message|string|null $more An escaped link for "More..." of the templates
         * @return String: HTML output
         */
-       public static function formatTemplates( $templates, $preview = false, $section = false ) {
+       public static function formatTemplates( $templates, $preview = false, $section = false, $more = null ) {
                wfProfileIn( __METHOD__ );
 
                $outText = '';
@@ -1921,9 +1999,16 @@ class Linker {
                                        . wfMessage( 'word-separator' )->escaped()
                                        . $protected . '</li>';
                        }
+
+                       if ( $more instanceof Title ) {
+                               $outText .= '<li>' . self::link( $more, wfMessage( 'moredotdotdot' ) ) . '</li>';
+                       } elseif ( $more ) {
+                               $outText .= "<li>$more</li>";
+                       }
+
                        $outText .= '</ul>';
                }
-               wfProfileOut( __METHOD__  );
+               wfProfileOut( __METHOD__ );
                return $outText;
        }
 
@@ -1949,7 +2034,7 @@ class Linker {
                        }
                        $outText .= '</ul>';
                }
-               wfProfileOut( __METHOD__  );
+               wfProfileOut( __METHOD__ );
                return $outText;
        }
 
@@ -1989,7 +2074,7 @@ class Linker {
                        # Compatibility: formerly some tooltips had [alt-.] hardcoded
                        $tooltip = preg_replace( "/ ?\[alt-.\]$/", '', $tooltip );
                        # Message equal to '-' means suppress it.
-                       if (  $tooltip == '-' ) {
+                       if ( $tooltip == '-' ) {
                                $tooltip = false;
                        }
                }
@@ -2071,17 +2156,17 @@ class Linker {
                                // RevDelete links using revision ID are stable across
                                // page deletion and undeletion; use when possible.
                                $query = array(
-                                       'type'   => 'revision',
+                                       'type' => 'revision',
                                        'target' => $title->getPrefixedDBkey(),
-                                       'ids'    => $rev->getId()
+                                       'ids' => $rev->getId()
                                );
                        } else {
                                // Older deleted entries didn't save a revision ID.
                                // We have to refer to these by timestamp, ick!
                                $query = array(
-                                       'type'   => 'archive',
+                                       'type' => 'archive',
                                        'target' => $title->getPrefixedDBkey(),
-                                       'ids'    => $rev->getTimestamp()
+                                       'ids' => $rev->getTimestamp()
                                );
                        }
                        return Linker::revDeleteLink( $query,
@@ -2201,7 +2286,7 @@ class Linker {
         * @return string the a-element
         */
        static function makeKnownLinkObj(
-               $title, $text = '', $query = '', $trail = '', $prefix = '' , $aprops = '', $style = ''
+               $title, $text = '', $query = '', $trail = '', $prefix = '', $aprops = '', $style = ''
        ) {
                wfDeprecated( __METHOD__, '1.21' );