From: Ryan Schmidt Date: Wed, 21 Jan 2009 20:42:32 +0000 (+0000) Subject: Redirect-related bugfixes/features: X-Git-Tag: 1.31.0-rc.0~43319 X-Git-Url: https://git.cyclocoop.org/%242?a=commitdiff_plain;h=918161a2db9ec46f77e008530f90a36068e77257;p=lhc%2Fweb%2Fwiklou.git Redirect-related bugfixes/features: * (bug 11644) Add $wgMaxRedirects variable to control how many redirects are recursed through until the "destination" page is reached. ** update redirect page UI to show each step down to the destination page ** rdfrom text still links to original page visited ** pages still show up in DoubleRedirects ** setting to 1 (default) is current behavior of only going 1 step down. Setting to 0 disables automatic redirects. ** arrow image needs to be smoothed, couldn't figure out how to do it myself while keeping the image in indexed mode * (bug 10569) Redirects to Special:Mypage and Special:Mytalk are no longer allowed to prevent redirect loops ** can be re-enabled by changing the $wgInvalidRedirectTargets array --- diff --git a/RELEASE-NOTES b/RELEASE-NOTES index d729f2502c..9a5b927505 100644 --- a/RELEASE-NOTES +++ b/RELEASE-NOTES @@ -48,7 +48,10 @@ it from source control: http://www.mediawiki.org/wiki/Download_from_SVN * (bugs 16957/16969) Add show/hide to preferences for RC patrol options on specialpages * (bug 11443) Auto-noindex user/user talk pages for blocked user - +* (bug 11644) Add $wgMaxRedirects variable to control how many redirects are recursed + through until the "destination" page is reached. +* Add $wgInvalidRedirectTargets variable to prevent redirects to certain special pages. + === Bug fixes in 1.15 === * (bug 16968) Special:Upload no longer throws useless warnings. * (bug 17000) Special:RevisionDelete now checks if the database is locked before @@ -66,6 +69,8 @@ it from source control: http://www.mediawiki.org/wiki/Download_from_SVN * (bug 11527) Diff on page with one revision shows "Next" link to same diff * (bug 15936) New page's patrol button should always be visible * (bug 8065) Fix summary forcing for new pages +* (bug 10569) redirects to Special:Mypage and Special:Mytalk are no longer allowed + by default. Change $wgInvalidRedirectTargets to re-enable. == API changes in 1.15 == * (bug 16858) Revamped list=deletedrevs to make listing deleted contributions diff --git a/includes/Article.php b/includes/Article.php index 524c5c320b..6b2c497f3b 100644 --- a/includes/Article.php +++ b/includes/Article.php @@ -103,7 +103,8 @@ class Article { * @return Title object */ public function insertRedirect() { - $retval = Title::newFromRedirect( $this->getContent() ); + // set noRecurse so that we always get an entry even if redirects are "disabled" + $retval = Title::newFromRedirect( $this->getContent(), false, true ); if( !$retval ) { return null; } @@ -135,7 +136,7 @@ class Article { * @return mixed false, Title of in-wiki target, or string with URL */ public function followRedirectText( $text ) { - $rt = Title::newFromRedirect( $text ); + $rt = Title::newFromRedirect( $text ); // only get the final target # process if title object is valid and not special:userlogout if( $rt ) { if( $rt->getInterwiki() != '' ) { @@ -584,9 +585,10 @@ class Article { } // Apparently loadPageData was never called $this->loadContent(); - $titleObj = Title::newFromRedirect( $this->fetchContent() ); + // Only get the next target to reduce load times + $titleObj = Title::newFromRedirect( $this->fetchContent(), false, true ); } else { - $titleObj = Title::newFromRedirect( $text ); + $titleObj = Title::newFromRedirect( $text, false, true ); } return $titleObj !== NULL; } @@ -919,7 +921,7 @@ class Article { $wgOut->addHTML( htmlspecialchars( $this->mContent ) ); $wgOut->addHTML( "\n\n" ); } - } else if( $rt = Title::newFromRedirect( $text ) ) { + } else if( $rt = Title::newFromRedirect( $text, true ) ) { # get an array of redirect targets # Don't append the subtitle if this was an old revision $wgOut->addHTML( $this->viewRedirect( $rt, !$wasRedirected && $this->isCurrent() ) ); $parseout = $wgParser->parse($text, $this->mTitle, ParserOptions::newFromUser($wgUser)); @@ -1039,24 +1041,41 @@ class Article { /** * View redirect - * @param $target Title object of destination to redirect + * @param $target Title object or Array of destination(s) to redirect * @param $appendSubtitle Boolean [optional] * @param $forceKnown Boolean: should the image be shown as a bluelink regardless of existence? */ public function viewRedirect( $target, $appendSubtitle = true, $forceKnown = false ) { global $wgParser, $wgOut, $wgContLang, $wgStylePath, $wgUser; # Display redirect + if( !is_array( $target ) ) { + $target = array( $target ); + } $imageDir = $wgContLang->isRTL() ? 'rtl' : 'ltr'; - $imageUrl = $wgStylePath.'/common/images/redirect' . $imageDir . '.png'; - + $imageUrl = $wgStylePath . '/common/images/redirect' . $imageDir . '.png'; + $imageUrl2 = $wgStylePath . '/common/images/nextredirect' . $imageDir . '.png'; + $alt2 = $wgContLang->isRTL() ? '←' : '→'; // should -> and <- be used instead of entities? + if( $appendSubtitle ) { $wgOut->appendSubtitle( wfMsgHtml( 'redirectpagesub' ) ); } $sk = $wgUser->getSkin(); + // the loop prepends the arrow image before the link, so the first case needs to be outside + $title = array_shift( $target ); if( $forceKnown ) { - $link = $sk->makeKnownLinkObj( $target, htmlspecialchars( $target->getFullText() ) ); + $link = $sk->makeKnownLinkObj( $title, htmlspecialchars( $title->getFullText() ) ); } else { - $link = $sk->makeLinkObj( $target, htmlspecialchars( $target->getFullText() ) ); + $link = $sk->makeLinkObj( $title, htmlspecialchars( $title->getFullText() ) ); + } + // automatically append redirect=no to each link, since most of them are redirect pages themselves + foreach( $target as $rt ) { + if( $forceKnown ) { + $link .= ''.$alt2.' ' + . $sk->makeKnownLinkObj( $rt, htmlspecialchars( $rt->getFullText() ) ); + } else { + $link .= ''.$alt2.' ' + . $sk->makeLinkObj( $rt, htmlspecialchars( $rt->getFullText() ) ); + } } return '#REDIRECT ' . ''.$link.''; @@ -3428,9 +3447,9 @@ class Article { public static function getAutosummary( $oldtext, $newtext, $flags ) { # Decide what kind of autosummary is needed. - # Redirect autosummaries - $ot = Title::newFromRedirect( $oldtext ); - $rt = Title::newFromRedirect( $newtext ); + # Redirect autosummaries -- should only get the next target and not recurse + $ot = Title::newFromRedirect( $oldtext, false, true ); + $rt = Title::newFromRedirect( $newtext, false, true ); if( is_object( $rt ) && ( !is_object( $ot ) || !$rt->equals( $ot ) || $ot->getFragment() != $rt->getFragment() ) ) { return wfMsgForContent( 'autoredircomment', $rt->getFullText() ); } diff --git a/includes/DefaultSettings.php b/includes/DefaultSettings.php index b21a61e868..ab60b06d91 100644 --- a/includes/DefaultSettings.php +++ b/includes/DefaultSettings.php @@ -3611,6 +3611,25 @@ $wgMaximumMovedPages = 100; */ $wgFixDoubleRedirects = false; +/** + * Max number of redirects to follow when resolving redirects. + * 1 means only the first redirect is followed (default behavior). + * 0 or less means no redirects are followed. + */ +$wgMaxRedirects = 1; + +/** + * Array of invalid page redirect targets. + * Attempting to create a redirect to any of the pages in this array + * will make the redirect fail. + * Userlogout is hard-coded, so it does not need to be listed here. + * (bug 10569) Disallow Mypage and Mytalk as well. + * + * As of now, this only checks special pages. Redirects to pages in + * other namespaces cannot be invalidated by this variable. + */ +$wgInvalidRedirectTargets = array( 'Filepath', 'Mypage', 'Mytalk' ); + /** * Array of namespaces to generate a sitemap for when the * maintenance/generateSitemap.php script is run, or false if one is to be ge- diff --git a/includes/EditPage.php b/includes/EditPage.php index 90af9d2df7..946a596df0 100644 --- a/includes/EditPage.php +++ b/includes/EditPage.php @@ -1687,7 +1687,7 @@ END $parserOptions->setTidy(true); $parserOutput = $wgParser->parse( $previewtext, $this->mTitle, $parserOptions ); $previewHTML = $parserOutput->mText; - } elseif ( $rt = Title::newFromRedirect( $this->textbox1 ) ) { + } elseif ( $rt = Title::newFromRedirect( $this->textbox1, true ) ) { $previewHTML = $this->mArticle->viewRedirect( $rt, false ); } else { $toparse = $this->textbox1; diff --git a/includes/Title.php b/includes/Title.php index 5aa1ffedc2..eb83b43c78 100644 --- a/includes/Title.php +++ b/includes/Title.php @@ -295,10 +295,20 @@ class Title { * Extract a redirect destination from a string and return the * Title, or null if the text doesn't contain a valid redirect * - * @param $text \type{String} Text with possible redirect + * @param $text \type{\string} Text with possible redirect + * @param $getAllTargets \type{\bool} Should we get an array of every target or just the final one? + * @param $noRecurse \type{\bool} This will prevent any and all recursion, only getting the very next target + * This is mainly meant for Article::insertRedirect so that the redirect table still works properly + * (makes double redirect and broken redirect reports display accurate results). * @return \type{Title} The corresponding Title - */ - public static function newFromRedirect( $text ) { + * @return \type{\array} Array of redirect targets (Title objects), with the destination being last + */ + public static function newFromRedirect( $text, $getAllTargets = false, $noRecurse = false ) { + global $wgMaxRedirects; + // are redirects disabled? + // Note that we should get a Title object if possible if $noRecurse is true so that the redirect table functions properly + if( !$noRecurse && $wgMaxRedirects < 1 ) + return null; $redir = MagicWord::get( 'redirect' ); $text = trim($text); if( $redir->matchStartAndRemove( $text ) ) { @@ -316,11 +326,36 @@ class Title { $m[1] = urldecode( ltrim( $m[1], ':' ) ); } $title = Title::newFromText( $m[1] ); - // Redirects to some special pages are not permitted - if( $title instanceof Title - && !$title->isSpecial( 'Userlogout' ) - && !$title->isSpecial( 'Filepath' ) ) - { + // If the initial title is a redirect to bad special pages or is invalid, quit early + if( !$title instanceof Title || !$title->isValidRedirectTarget() ) { + return null; + } + // If $noRecurse is true, simply return here + if( $noRecurse ) { + return $title; + } + // recursive check to follow double redirects + $recurse = $wgMaxRedirects; + $targets = array(); + while( --$recurse >= 0 ) { + // Redirects to some special pages are not permitted + if( $title instanceof Title && $title->isValidRedirectTarget() ) { + $targets[] = $title; + if( $title->isRedirect() ) { + $article = new Article( $title, 0 ); + $title = $article->getRedirectTarget(); + } else { + break; + } + } else { + break; + } + } + if( $getAllTargets ) { + // return every target or null if no targets due to invalid title or whatever + return ( $targets === array() ) ? null : $targets; + } else { + // return the final destination -- invalid titles are checked earlier return $title; } } @@ -3426,4 +3461,26 @@ class Title { } return $redirs; } + + /** + * Check if this Title is a valid redirect target + * + * @return \type{\bool} TRUE or FALSE + */ + public function isValidRedirectTarget() { + global $wgInvalidRedirectTargets; + + // invalid redirect targets are stored in a global array, but explicity disallow Userlogout here + if( $this->isSpecial( 'Userlogout' ) ) { + return false; + } + + foreach( $wgInvalidRedirectTargets as $target ) { + if( $this->isSpecial( $target ) ) { + return false; + } + } + + return true; + } } diff --git a/skins/common/images/nextredirectltr.png b/skins/common/images/nextredirectltr.png new file mode 100644 index 0000000000..7d60cdcf5f Binary files /dev/null and b/skins/common/images/nextredirectltr.png differ diff --git a/skins/common/images/nextredirectrtl.png b/skins/common/images/nextredirectrtl.png new file mode 100644 index 0000000000..3d5b3959f2 Binary files /dev/null and b/skins/common/images/nextredirectrtl.png differ