Chad has submitted this change and it was merged.

Change subject: MediaWiki.php: Factor out tryNormaliseRedirect
......................................................................


MediaWiki.php: Factor out tryNormaliseRedirect

This is in preparation for fixing T67402, which requires adding
logic inside this condition block. However the to-be-added code
will influences whether or not a redirect should be made.

In case a redirect is not made, it has to fall through to the next
'elseif' handler in MediaWiki::performRequest(), which is not possible
from inside the 'if' block.

Hence, move it out in a separate block and use a boolean return value
to communicate whether the case has been handled.

This also allows us to unit test this thing. Which is desperately
needed. Albeit ugly as it requires lots of mocking.

Change-Id: If3157f2ff1fd3ab2ca20a5d1f550d864ea62c493
---
M includes/MediaWiki.php
A tests/phpunit/includes/MediaWikiTest.php
2 files changed, 240 insertions(+), 60 deletions(-)

Approvals:
  Chad: Verified; Looks good to me, approved
  jenkins-bot: Verified



diff --git a/includes/MediaWiki.php b/includes/MediaWiki.php
index 402494e..e06877f 100644
--- a/includes/MediaWiki.php
+++ b/includes/MediaWiki.php
@@ -225,76 +225,99 @@
                                $output->redirect( $url, 301 );
                        } else {
                                $this->context->setTitle( 
SpecialPage::getTitleFor( 'Badtitle' ) );
-                               wfProfileOut( __METHOD__ );
                                throw new BadTitleError();
                        }
-               // Redirect loops, no title in URL, $wgUsePathInfo URLs, and 
URLs with a variant
-               } elseif ( $request->getVal( 'action', 'view' ) == 'view' && 
!$request->wasPosted()
-                       && ( $request->getVal( 'title' ) === null
-                               || $title->getPrefixedDBkey() != 
$request->getVal( 'title' ) )
-                       && !count( $request->getValueNames( array( 'action', 
'title' ) ) )
-                       && wfRunHooks( 'TestCanonicalRedirect', array( 
$request, $title, $output ) )
-               ) {
-                       if ( $title->isSpecialPage() ) {
-                               list( $name, $subpage ) = 
SpecialPageFactory::resolveAlias( $title->getDBkey() );
-                               if ( $name ) {
-                                       $title = SpecialPage::getTitleFor( 
$name, $subpage );
-                               }
-                       }
-                       $targetUrl = wfExpandUrl( $title->getFullURL(), 
PROTO_CURRENT );
-                       // Redirect to canonical url, make it a 301 to allow 
caching
-                       if ( $targetUrl == $request->getFullRequestURL() ) {
-                               $message = "Redirect loop detected!\n\n" .
-                                       "This means the wiki got confused about 
what page was " .
-                                       "requested; this sometimes happens when 
moving a wiki " .
-                                       "to a new server or changing the server 
configuration.\n\n";
+               // Handle any other redirects.
+               // Redirect loops, titleless URL, $wgUsePathInfo URLs, and URLs 
with a variant
+               } elseif ( !$this->tryNormaliseRedirect( $title ) ) {
 
-                               if ( $this->config->get( 'UsePathInfo' ) ) {
-                                       $message .= "The wiki is trying to 
interpret the page " .
-                                               "title from the URL path 
portion (PATH_INFO), which " .
-                                               "sometimes fails depending on 
the web server. Try " .
-                                               "setting \"\$wgUsePathInfo = 
false;\" in your " .
-                                               "LocalSettings.php, or check 
that \$wgArticlePath " .
-                                               "is correct.";
+                       // Special pages
+                       if ( NS_SPECIAL == $title->getNamespace() ) {
+                               // Actions that need to be made when we have a 
special pages
+                               SpecialPageFactory::executePath( $title, 
$this->context );
+                       } else {
+                               // ...otherwise treat it as an article view. 
The article
+                               // may still be a wikipage redirect to another 
article or URL.
+                               $article = $this->initializeArticle();
+                               if ( is_object( $article ) ) {
+                                       $this->performAction( $article, 
$requestTitle );
+                               } elseif ( is_string( $article ) ) {
+                                       $output->redirect( $article );
                                } else {
-                                       $message .= "Your web server was 
detected as possibly not " .
-                                               "supporting URL path components 
(PATH_INFO) correctly; " .
-                                               "check your LocalSettings.php 
for a customized " .
-                                               "\$wgArticlePath setting and/or 
toggle \$wgUsePathInfo " .
-                                               "to true.";
+                                       throw new MWException( "Shouldn't 
happen: MediaWiki::initializeArticle()"
+                                               . " returned neither an object 
nor a URL" );
                                }
-                               throw new HttpError( 500, $message );
-                       } else {
-                               $output->setSquidMaxage( 1200 );
-                               $output->redirect( $targetUrl, '301' );
-                       }
-               // Special pages
-               } elseif ( NS_SPECIAL == $title->getNamespace() ) {
-                       $pageView = true;
-                       // Actions that need to be made when we have a special 
pages
-                       SpecialPageFactory::executePath( $title, $this->context 
);
-               } else {
-                       // ...otherwise treat it as an article view. The article
-                       // may be a redirect to another article or URL.
-                       $article = $this->initializeArticle();
-                       if ( is_object( $article ) ) {
-                               $pageView = true;
-                               $this->performAction( $article, $requestTitle );
-                       } elseif ( is_string( $article ) ) {
-                               $output->redirect( $article );
-                       } else {
-                               wfProfileOut( __METHOD__ );
-                               throw new MWException( "Shouldn't happen: 
MediaWiki::initializeArticle()"
-                                       . " returned neither an object nor a 
URL" );
                        }
                }
+       }
 
-               if ( $pageView ) {
-                       // Promote user to any groups they meet the criteria for
-                       $user->addAutopromoteOnceGroups( 'onView' );
+       /**
+        * Handle redirects for uncanonical title requests.
+        *
+        * Handles:
+        * - Redirect loops.
+        * - No title in URL.
+        * - $wgUsePathInfo URLs.
+        * - URLs with a variant.
+        * - Other non-standard URLs (as long as they have no extra query 
parameters).
+        *
+        * Behaviour:
+        * - Normalise title values:
+        *   /wiki/Foo%20Bar -> /wiki/Foo_Bar
+        * - Normalise empty title:
+        *   /wiki/ -> /wiki/Main
+        *   /w/index.php?title= -> /wiki/Main
+        * - Don't redirect anything with query parameters other than 'title' 
or 'action=view'.
+        *
+        * @return bool True if a redirect was set.
+        */
+       private function tryNormaliseRedirect( $title ) {
+               $request = $this->context->getRequest();
+               $output = $this->context->getOutput();
+
+               if ( $request->getVal( 'action', 'view' ) != 'view'
+                       || $request->wasPosted()
+                       || ( $request->getVal( 'title' ) !== null
+                               && $title->getPrefixedDBkey() == 
$request->getVal( 'title' ) )
+                       || count( $request->getValueNames( array( 'action', 
'title' ) ) )
+                       || !Hooks::run( 'TestCanonicalRedirect', array( 
$request, $title, $output ) )
+               ) {
+                       return false;
                }
 
-               wfProfileOut( __METHOD__ );
+               if ( $title->isSpecialPage() ) {
+                       list( $name, $subpage ) = 
SpecialPageFactory::resolveAlias( $title->getDBkey() );
+                       if ( $name ) {
+                               $title = SpecialPage::getTitleFor( $name, 
$subpage );
+                       }
+               }
+               // Redirect to canonical url, make it a 301 to allow caching
+               $targetUrl = wfExpandUrl( $title->getFullURL(), PROTO_CURRENT );
+               if ( $targetUrl == $request->getFullRequestURL() ) {
+                       $message = "Redirect loop detected!\n\n" .
+                               "This means the wiki got confused about what 
page was " .
+                               "requested; this sometimes happens when moving 
a wiki " .
+                               "to a new server or changing the server 
configuration.\n\n";
+
+                       if ( $this->config->get( 'UsePathInfo' ) ) {
+                               $message .= "The wiki is trying to interpret 
the page " .
+                                       "title from the URL path portion 
(PATH_INFO), which " .
+                                       "sometimes fails depending on the web 
server. Try " .
+                                       "setting \"\$wgUsePathInfo = false;\" 
in your " .
+                                       "LocalSettings.php, or check that 
\$wgArticlePath " .
+                                       "is correct.";
+                       } else {
+                               $message .= "Your web server was detected as 
possibly not " .
+                                       "supporting URL path components 
(PATH_INFO) correctly; " .
+                                       "check your LocalSettings.php for a 
customized " .
+                                       "\$wgArticlePath setting and/or toggle 
\$wgUsePathInfo " .
+                                       "to true.";
+                       }
+                       throw new HttpError( 500, $message );
+               }
+               $output->setSquidMaxage( 1200 );
+               $output->redirect( $targetUrl, '301' );
+               return true;
        }
 
        /**
diff --git a/tests/phpunit/includes/MediaWikiTest.php 
b/tests/phpunit/includes/MediaWikiTest.php
new file mode 100644
index 0000000..df94d3e
--- /dev/null
+++ b/tests/phpunit/includes/MediaWikiTest.php
@@ -0,0 +1,157 @@
+<?php
+
+class MediaWikiTest extends MediaWikiTestCase {
+       protected function setUp() {
+               parent::setUp();
+
+               $this->setMwGlobals( array(
+                       'wgServer' => 'http://example.org',
+                       'wgScriptPath' => '/w',
+                       'wgScript' => '/w/index.php',
+                       'wgArticlePath' => '/wiki/$1',
+                       'wgActionPaths' => array(),
+               ) );
+       }
+
+       public static function provideTryNormaliseRedirect() {
+               return array(
+                       array(
+                               // View: Canonical
+                               'url' => 'http://example.org/wiki/Foo_Bar',
+                               'query' => array(),
+                               'title' => 'Foo_Bar',
+                               'redirect' => false,
+                       ),
+                       array(
+                               // View: Escaped title
+                               'url' => 'http://example.org/wiki/Foo%20Bar',
+                               'query' => array(),
+                               'title' => 'Foo_Bar',
+                               'redirect' => 'http://example.org/wiki/Foo_Bar',
+                       ),
+                       array(
+                               // View: Script path
+                               'url' => 
'http://example.org/w/index.php?title=Foo_Bar',
+                               'query' => array( 'title' => 'Foo_Bar' ),
+                               'title' => 'Foo_Bar',
+                               'redirect' => false,
+                       ),
+                       array(
+                               // View: Script path with implicit title from 
page id
+                               'url' => 
'http://example.org/w/index.php?curid=123',
+                               'query' => array( 'curid' => '123' ),
+                               'title' => 'Foo_Bar',
+                               'redirect' => false,
+                       ),
+                       array(
+                               // View: Script path with implicit title from 
revision id
+                               'url' => 
'http://example.org/w/index.php?oldid=123',
+                               'query' => array( 'oldid' => '123' ),
+                               'title' => 'Foo_Bar',
+                               'redirect' => false,
+                       ),
+                       array(
+                               // View: Script path without title
+                               'url' => 'http://example.org/w/index.php',
+                               'query' => array(),
+                               'title' => 'Main_Page',
+                               'redirect' => 
'http://example.org/wiki/Main_Page',
+                       ),
+                       array(
+                               // View: Script path with empty title
+                               'url' => 
'http://example.org/w/index.php?title=',
+                               'query' => array( 'title' => '' ),
+                               'title' => 'Main_Page',
+                               'redirect' => 
'http://example.org/wiki/Main_Page',
+                       ),
+                       array(
+                               // View: Index with escaped title
+                               'url' => 
'http://example.org/w/index.php?title=Foo%20Bar',
+                               'query' => array( 'title' => 'Foo Bar' ),
+                               'title' => 'Foo_Bar',
+                               'redirect' => 'http://example.org/wiki/Foo_Bar',
+                       ),
+                       array(
+                               // View: Script path with escaped title
+                               'url' => 'http://example.org/w/?title=Foo_Bar',
+                               'query' => array( 'title' => 'Foo_Bar' ),
+                               'title' => 'Foo_Bar',
+                               'redirect' => false,
+                       ),
+                       array(
+                               // View: Root path with escaped title
+                               'url' => 'http://example.org/?title=Foo_Bar',
+                               'query' => array( 'title' => 'Foo_Bar' ),
+                               'title' => 'Foo_Bar',
+                               'redirect' => false,
+                       ),
+                       array(
+                               // View: Canonical with redundant query
+                               'url' => 
'http://example.org/wiki/Foo_Bar?action=view',
+                               'query' => array( 'action' => 'view' ),
+                               'title' => 'Foo_Bar',
+                               'redirect' => false,
+                       ),
+                       array(
+                               // Edit: Canonical view url with action query
+                               'url' => 
'http://example.org/wiki/Foo_Bar?action=edit',
+                               'query' => array( 'action' => 'edit' ),
+                               'title' => 'Foo_Bar',
+                               'redirect' => false,
+                       ),
+                       array(
+                               // View: Index with action query
+                               'url' => 
'http://example.org/w/index.php?title=Foo_Bar&action=view',
+                               'query' => array( 'title' => 'Foo_Bar', 
'action' => 'view' ),
+                               'title' => 'Foo_Bar',
+                               'redirect' => false,
+                       ),
+                       array(
+                               // Edit: Index with action query
+                               'url' => 
'http://example.org/w/index.php?title=Foo_Bar&action=edit',
+                               'query' => array( 'title' => 'Foo_Bar', 
'action' => 'edit' ),
+                               'title' => 'Foo_Bar',
+                               'redirect' => false,
+                       ),
+               );
+       }
+
+       /**
+        * @dataProvider provideTryNormaliseRedirect
+        * @covers MediaWiki::tryNormaliseRedirect
+        */
+       public function testTryNormaliseRedirect( $url, $query, $title, 
$expectedRedirect = false ) {
+               // Set SERVER because interpolateTitle() doesn't use 
getRequestURL(),
+               // whereas tryNormaliseRedirect does().
+               $_SERVER['REQUEST_URI'] = $url;
+
+               $req = new FauxRequest( $query );
+               $req->setRequestURL( $url );
+               // This adds a virtual 'title' query parameter. Normally called 
from Setup.php
+               $req->interpolateTitle();
+
+               $titleObj = Title::newFromText( $title );
+
+               // Set global context since some involved code paths don't yet 
have context
+               $context = RequestContext::getMain();
+               $context->setRequest( $req );
+               $context->setTitle( $titleObj );
+
+               $mw = new MediaWiki( $context );
+
+               $method = new ReflectionMethod( $mw, 'tryNormaliseRedirect' );
+               $method->setAccessible( true );
+               $ret = $method->invoke( $mw, $titleObj );
+
+               $this->assertEquals(
+                       $expectedRedirect !== false,
+                       $ret,
+                       'Return true only when redirecting'
+               );
+
+               $this->assertEquals(
+                       $expectedRedirect ?: '',
+                       $context->getOutput()->getRedirect()
+               );
+       }
+}

-- 
To view, visit https://gerrit.wikimedia.org/r/259918
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: merged
Gerrit-Change-Id: If3157f2ff1fd3ab2ca20a5d1f550d864ea62c493
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/core
Gerrit-Branch: REL1_24
Gerrit-Owner: Chad <[email protected]>
Gerrit-Reviewer: Chad <[email protected]>
Gerrit-Reviewer: Krinkle <[email protected]>
Gerrit-Reviewer: jenkins-bot <>

_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to