* (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
* (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
* (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
* @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;
}
* @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() != '' ) {
}
// 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;
}
$wgOut->addHTML( htmlspecialchars( $this->mContent ) );
$wgOut->addHTML( "\n</pre>\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));
/**
* 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 .= '<img src="'.$imageUrl2.'" alt="'.$alt2.' " />'
+ . $sk->makeKnownLinkObj( $rt, htmlspecialchars( $rt->getFullText() ) );
+ } else {
+ $link .= '<img src="'.$imageUrl2.'" alt="'.$alt2.' " />'
+ . $sk->makeLinkObj( $rt, htmlspecialchars( $rt->getFullText() ) );
+ }
}
return '<img src="'.$imageUrl.'" alt="#REDIRECT " />' .
'<span class="redirectText">'.$link.'</span>';
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() );
}
*/
$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-
$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;
* 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 ) ) {
$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;
}
}
}
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;
+ }
}