On redirects update the URL to that of the target page with JavaScript
authorBartosz Dziewoński <matma.rex@gmail.com>
Tue, 8 Jul 2014 00:50:37 +0000 (02:50 +0200)
committerBartosz Dziewoński <matma.rex@gmail.com>
Wed, 20 Aug 2014 20:21:33 +0000 (20:21 +0000)
Redirects to sections will now update the URL in browser's address bar
using the HTML5 History API. When [[Dog]] redirects to [[Animals#Dog]],
the user will now see "Animals#Dog" in their browser instead of "Dog#Dog".

The target URL is generated server-side to avoid pulling in dependencies
in the client-side RL module, which is loaded in the top queue.

Browsers that do not support the History API are still supported, the
way they always were.

Given the following three pages:
* A: #REDIRECT [[C]]
* B: #REDIRECT [[C#Section]]
* C: ==Section== \n ==Other section== \n

The following links should behave as follows:
* A                →  C
* B                →  C#Section
* A#Other_section  →  C#Other_section
* B#Other_section  →  C#Other_section

The code also supports forwarding query parameters like 'debug=1'.

Bug: 35045
Bug: 39328
Change-Id: I9d8d834762e19b836b7e35122b6c4cef0f91b7f0

RELEASE-NOTES-1.24
includes/page/Article.php
resources/Resources.php
resources/src/mediawiki.action/mediawiki.action.view.redirect.js [new file with mode: 0644]
resources/src/mediawiki.action/mediawiki.action.view.redirectToFragment.js [deleted file]

index b354cc0..01f8b0a 100644 (file)
@@ -163,6 +163,9 @@ production.
   log in, rather than being shown a page asking them to log in and having to click
   another link to actually get to the login page.
 * A JSONContent and JSONContentHandler were added for extensions to extend.
+* (bug 35045) Redirects to sections will now update the URL in browser's address
+  bar using the HTML5 History API. When [[Dog]] redirects to [[Animals#Dog]],
+  the user will now see "Animals#Dog" in their browser instead of "Dog#Dog".
 
 === Bug fixes in 1.24 ===
 * (bug 50572) MediaWiki:Blockip should support gender
index da88a69..984b01d 100644 (file)
@@ -975,7 +975,14 @@ class Article implements Page {
                global $wgRedirectSources;
                $outputPage = $this->getContext()->getOutput();
 
-               $rdfrom = $this->getContext()->getRequest()->getVal( 'rdfrom' );
+               $request = $this->getContext()->getRequest();
+               $rdfrom = $request->getVal( 'rdfrom' );
+
+               // Construct a URL for the current page view, but with the target title
+               $query = $request->getValues();
+               unset( $query['rdfrom'] );
+               unset( $query['title'] );
+               $redirectTargetUrl = $this->getTitle()->getLinkURL( $query );
 
                if ( isset( $this->mRedirectedFrom ) ) {
                        // This is an internally redirected page view.
@@ -990,11 +997,12 @@ class Article implements Page {
 
                                $outputPage->addSubtitle( wfMessage( 'redirectedfrom' )->rawParams( $redir ) );
 
-                               // Set the fragment if one was specified in the redirect
-                               if ( $this->getTitle()->hasFragment() ) {
-                                       $outputPage->addJsConfigVars( 'wgRedirectToFragment', $this->getTitle()->getFragmentForURL() );
-                                       $outputPage->addModules( 'mediawiki.action.view.redirectToFragment' );
-                               }
+                               // Add the script to update the displayed URL and
+                               // set the fragment if one was specified in the redirect
+                               $outputPage->addJsConfigVars( array(
+                                       'wgInternalRedirectTargetUrl' => $redirectTargetUrl,
+                               ) );
+                               $outputPage->addModules( 'mediawiki.action.view.redirect' );
 
                                // Add a <link rel="canonical"> tag
                                $outputPage->setCanonicalUrl( $this->getTitle()->getLocalURL() );
@@ -1011,6 +1019,12 @@ class Article implements Page {
                                $redir = Linker::makeExternalLink( $rdfrom, $rdfrom );
                                $outputPage->addSubtitle( wfMessage( 'redirectedfrom' )->rawParams( $redir ) );
 
+                               // Add the script to update the displayed URL
+                               $outputPage->addJsConfigVars( array(
+                                       'wgInternalRedirectTargetUrl' => $redirectTargetUrl,
+                               ) );
+                               $outputPage->addModules( 'mediawiki.action.view.redirect' );
+
                                return true;
                        }
                }
index 092660c..24e377d 100644 (file)
@@ -1045,13 +1045,18 @@ return array(
                        'postedit-confirmation-saved',
                ),
        ),
-       'mediawiki.action.view.redirectToFragment' => array(
-               'scripts' => 'resources/src/mediawiki.action/mediawiki.action.view.redirectToFragment.js',
+       'mediawiki.action.view.redirect' => array(
+               'scripts' => 'resources/src/mediawiki.action/mediawiki.action.view.redirect.js',
                'dependencies' => array(
                        'jquery.client',
                ),
                'position' => 'top',
        ),
+       // Deployment hack for compatibility with cached HTML, remove before 1.24 release
+       'mediawiki.action.view.redirectToFragment' => array(
+               'dependencies' => 'mediawiki.action.view.redirect',
+               'position' => 'top',
+       ),
        'mediawiki.action.view.rightClickEdit' => array(
                'scripts' => 'resources/src/mediawiki.action/mediawiki.action.view.rightClickEdit.js',
        ),
diff --git a/resources/src/mediawiki.action/mediawiki.action.view.redirect.js b/resources/src/mediawiki.action/mediawiki.action.view.redirect.js
new file mode 100644 (file)
index 0000000..c87ff7c
--- /dev/null
@@ -0,0 +1,65 @@
+/*!
+ * JavaScript to update page URL when a redirect is viewed, ensuring that the
+ * page is scrolled to the id when it's a redirect with fragment.
+ *
+ * This is loaded in the top queue, so avoid unnecessary dependencies
+ * like mediawiki.Title or mediawiki.Uri.
+ */
+( function ( mw, $ ) {
+       var profile = $.client.profile(),
+               canonical = mw.config.get( 'wgInternalRedirectTargetUrl' ),
+               fragment = null,
+               shouldChangeFragment, index;
+
+       // Clear internal mw.config entries, so that no one tries to depend on them
+       mw.config.set( 'wgInternalRedirectTargetUrl', null );
+
+       // Deployment hack for compatibility with cached HTML, remove before 1.24 release
+       if ( !canonical ) {
+               canonical = mw.config.get( 'wgRedirectToFragment' );
+       }
+
+       index = canonical.indexOf( '#' );
+       if ( index !== -1 ) {
+               fragment = canonical.slice( index );
+       }
+
+       // Never override the fragment if the user intended to look at a different section
+       shouldChangeFragment = fragment && !location.hash;
+
+       // Replace the whole URL if possible, otherwise just change the fragment
+       if ( canonical && history.replaceState ) {
+               if ( !shouldChangeFragment ) {
+                       // If the current page view has a fragment already, don't override it
+                       canonical = canonical.replace( /#.*$/, '' );
+                       canonical += location.hash;
+               }
+
+               // This will also cause the browser to scroll to given fragment
+               history.replaceState( /*data=*/ history.state, /*title=*/ document.title, /*url=*/ canonical );
+
+       } else if ( shouldChangeFragment ) {
+               if ( profile.layout === 'webkit' && profile.layoutVersion < 420 ) {
+                       // Released Safari w/ WebKit 418.9.1 messes up horribly
+                       // Nightlies of 420+ are ok
+                       return;
+               }
+
+               location.hash = fragment;
+       }
+
+       if ( shouldChangeFragment && profile.layout === 'gecko' ) {
+               // Mozilla needs to wait until after load, otherwise the window doesn't
+               // scroll.  See <https://bugzilla.mozilla.org/show_bug.cgi?id=516293>.
+               // There's no obvious way to detect this programmatically, so we use
+               // version-testing.  If Firefox fixes the bug, they'll jump twice, but
+               // better twice than not at all, so make the fix hit future versions as
+               // well.
+               $( function () {
+                       if ( location.hash === fragment ) {
+                               location.hash = fragment;
+                       }
+               } );
+       }
+
+}( mediaWiki, jQuery ) );
diff --git a/resources/src/mediawiki.action/mediawiki.action.view.redirectToFragment.js b/resources/src/mediawiki.action/mediawiki.action.view.redirectToFragment.js
deleted file mode 100644 (file)
index cbfd7b5..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-/*!
- * JavaScript to scroll the page to an id, when a redirect with fragment is viewed.
- */
-( function ( mw, $ ) {
-       var profile = $.client.profile(),
-               fragment = mw.config.get( 'wgRedirectToFragment' );
-
-       if ( fragment === null ) {
-               // nothing to do
-               return;
-       }
-
-       if ( profile.layout === 'webkit' && profile.layoutVersion < 420 ) {
-               // Released Safari w/ WebKit 418.9.1 messes up horribly
-               // Nightlies of 420+ are ok
-               return;
-       }
-       if ( !window.location.hash ) {
-               window.location.hash = fragment;
-
-               // Mozilla needs to wait until after load, otherwise the window doesn't
-               // scroll.  See <https://bugzilla.mozilla.org/show_bug.cgi?id=516293>.
-               // There's no obvious way to detect this programmatically, so we use
-               // version-testing.  If Firefox fixes the bug, they'll jump twice, but
-               // better twice than not at all, so make the fix hit future versions as
-               // well.
-               if ( profile.layout === 'gecko' ) {
-                       $( function () {
-                               if ( window.location.hash === fragment ) {
-                                       window.location.hash = fragment;
-                               }
-                       } );
-               }
-       }
-}( mediaWiki, jQuery ) );