Create a Special:Redirect page.
authorC. Scott Ananian <cscott@cscott.net>
Sat, 13 Apr 2013 01:52:05 +0000 (21:52 -0400)
committerC. Scott Ananian <cscott@cscott.net>
Tue, 30 Apr 2013 17:40:54 +0000 (13:40 -0400)
The primary purpose of this page is to redirect to a user page given a
numeric id.  The numeric User ID is stable across renames, and is
therefore an appropriate primary key for identifying the user
associated with a given revision.  The Parsoid API would like to
export semantic RDFa in its DOM identifying the author of a revision
by their userid, but in order to do so requires a MW redirect from
userid to the appropriate User page.  (A "permalink" for the user.)

This patch adds that redirect, as
  http://somewiki/Special:Redirect/user/1234

(https://bugzilla.wikimedia.org/show_bug.cgi?id=45206 is the related
Parsoid feature.)

Rather than adding a set of ad-hoc redirection pages, this patch
sets up an infrastructure for redirections.  Special:Redirect also
subsumes the functions of:

* Special:Filepath (Special:Redirect/file/xxxx)
* Special:PermanentLink (Special:Redirect/revision/xxxxx)

This structure is extensible for other redirect types.

Change-Id: I8b0785f4fbdb3dd438a7a45263c5f375ff9d2208

includes/AutoLoader.php
includes/DefaultSettings.php
includes/SpecialPageFactory.php
includes/specials/SpecialRedirect.php [new file with mode: 0644]
languages/messages/MessagesEn.php
languages/messages/MessagesQqq.php
maintenance/language/messages.inc

index 4813d45..07c9875 100644 (file)
@@ -968,6 +968,7 @@ $wgAutoloadLocalClasses = array(
        'SpecialRandomredirect' => 'includes/specials/SpecialRandomredirect.php',
        'SpecialRecentChanges' => 'includes/specials/SpecialRecentchanges.php',
        'SpecialRecentchangeslinked' => 'includes/specials/SpecialRecentchangeslinked.php',
+       'SpecialRedirect' => 'includes/specials/SpecialRedirect.php',
        'SpecialRevisionDelete' => 'includes/specials/SpecialRevisiondelete.php',
        'SpecialSearch' => 'includes/specials/SpecialSearch.php',
        'SpecialSpecialpages' => 'includes/specials/SpecialSpecialpages.php',
index c198081..1f64b33 100644 (file)
@@ -3305,7 +3305,7 @@ $wgMaxRedirects = 1;
  * 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' );
+$wgInvalidRedirectTargets = array( 'Filepath', 'Mypage', 'Mytalk', 'Redirect' );
 
 /** @} */ # End of title and interwiki settings }
 
index 675fb83..6a73a1d 100644 (file)
@@ -161,6 +161,7 @@ class SpecialPageFactory {
                'Mytalk'                    => 'SpecialMytalk',
                'Myuploads'                 => 'SpecialMyuploads',
                'PermanentLink'             => 'SpecialPermanentLink',
+               'Redirect'                  => 'SpecialRedirect',
                'Revisiondelete'            => 'SpecialRevisionDelete',
                'Specialpages'              => 'SpecialSpecialpages',
                'Userlogout'                => 'SpecialUserlogout',
diff --git a/includes/specials/SpecialRedirect.php b/includes/specials/SpecialRedirect.php
new file mode 100644 (file)
index 0000000..5ea98d5
--- /dev/null
@@ -0,0 +1,231 @@
+<?php
+/**
+ * Implements Special:Redirect
+ *
+ * @section LICENSE
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ * @ingroup SpecialPage
+ */
+
+/**
+ * A special page that redirects to: the user for a numeric user id,
+ * the file for a given filename, or the page for a given revision id.
+ *
+ * @ingroup SpecialPage
+ */
+class SpecialRedirect extends FormSpecialPage {
+
+       /**
+        * The type of the redirect (user/file/revision)
+        *
+        * @var string $mType
+        * @example 'user'
+        */
+       protected $mType;
+
+       /**
+        * The identifier/value for the redirect (which id, which file)
+        *
+        * @var string $mValue
+        * @example '42'
+        */
+       protected $mValue;
+
+       function __construct() {
+               parent::__construct( 'Redirect' );
+               $this->mType = null;
+               $this->mValue = null;
+       }
+
+       /**
+        * Set $mType and $mValue based on parsed value of $subpage.
+        */
+       function setParameter( $subpage ) {
+               // parse $subpage to pull out the parts
+               $parts = explode( '/', $subpage, 2 );
+               $this->mType = count( $parts ) > 0 ? $parts[0] : null;
+               $this->mValue = count( $parts ) > 1 ? $parts[1] : null;
+       }
+
+       /**
+        * Handle Special:Redirect/user/xxxx (by redirecting to User:YYYY)
+        *
+        * @return string|null url to redirect to, or null if $mValue is invalid.
+        */
+       function dispatchUser() {
+               if ( !ctype_digit( $this->mValue ) ) {
+                       return null;
+               }
+               $user = User::newFromId( (int)$this->mValue );
+               $username = $user->getName(); // load User as side-effect
+               if ( $user->isAnon() ) {
+                       return null;
+               }
+               $userpage = Title::makeTitle( NS_USER, $username );
+               return $userpage->getFullURL( '', false, PROTO_CURRENT );
+       }
+
+       /**
+        * Handle Special:Redirect/file/xxxx
+        *
+        * @return string|null url to redirect to, or null if $mValue is not found.
+        */
+       function dispatchFile() {
+               $title = Title::makeTitleSafe( NS_FILE, $this->mValue );
+
+               if ( ! $title instanceof Title ) {
+                       return null;
+               }
+               $file = wfFindFile( $title );
+
+               if ( !$file || !$file->exists() ) {
+                       return null;
+               }
+               // Default behavior: Use the direct link to the file.
+               $url = $file->getURL();
+               $request = $this->getRequest();
+               $width = $request->getInt( 'width', -1 );
+               $height = $request->getInt( 'height', -1 );
+
+               // If a width is requested...
+               if ( $width != -1 ) {
+                       $mto = $file->transform( array( 'width' => $width, 'height' => $height ) );
+                       // ... and we can
+                       if ( $mto && !$mto->isError() ) {
+                               // ... change the URL to point to a thumbnail.
+                               $url = $mto->getURL();
+                       }
+               }
+               return $url;
+       }
+
+       /**
+        * Handle Special:Redirect/revision/xxx
+        * (by redirecting to index.php?oldid=xxx)
+        *
+        * @return string|null url to redirect to, or null if $mValue is invalid.
+        */
+       function dispatchRevision() {
+               $oldid = $this->mValue;
+               if ( !ctype_digit( $oldid ) ) {
+                       return null;
+               }
+               $oldid = (int)$oldid;
+               if ( $oldid === 0 ) {
+                       return null;
+               }
+               return wfAppendQuery( wfScript( 'index' ), array(
+                       'oldid' => $oldid
+               ) );
+       }
+
+       /**
+        * Use appropriate dispatch* method to obtain a redirection URL,
+        * and either: redirect, set a 404 error code and error message,
+        * or do nothing (if $mValue wasn't set) allowing the form to be
+        * displayed.
+        *
+        * @return bool true if a redirect was successfully handled.
+        */
+       function dispatch() {
+               // the various namespaces supported by Special:Redirect
+               switch( $this->mType ) {
+               case 'user':
+                       $url = $this->dispatchUser();
+                       break;
+               case 'file':
+                       $url = $this->dispatchFile();
+                       break;
+               case 'revision':
+                       $url = $this->dispatchRevision();
+                       break;
+               default:
+                       $this->getOutput()->setStatusCode( 404 );
+                       $url = null;
+                       break;
+               }
+               if ( $url ) {
+                       $this->getOutput()->redirect( $url );
+                       return true;
+               }
+               if ( !is_null( $this->mValue ) ) {
+                       $this->getOutput()->setStatusCode( 404 );
+                       $msg = $this->getMessagePrefix() . '-not-exists';
+                       return Status::newFatal( $msg );
+               }
+               return false;
+       }
+
+       protected function getFormFields() {
+               $mp = $this->getMessagePrefix();
+               $ns = array(
+                       // subpage => message
+                       'user' => $mp . '-user',
+                       'revision' => $mp . '-revision',
+                       'file' => $mp . '-file',
+               );
+               $a = array();
+               $a['type'] = array(
+                       'type' => 'select',
+                       'label-message' => $mp . '-lookup',
+                       'options' => array(),
+                       'default' => current( array_keys( $ns ) ),
+               );
+               foreach( $ns as $n => $m ) {
+                       $m = $this->msg( $m )->text();
+                       $a['type']['options'][$m] = $n;
+               }
+               $a['value'] = array(
+                       'type' => 'text',
+                       'label-message' => $mp . '-value'
+               );
+               // set the defaults according to the parsed subpage path
+               if ( !empty( $this->mType ) ) {
+                       $a['type']['default'] = $this->mType;
+               }
+               if ( !empty( $this->mValue ) ) {
+                       $a['value']['default'] = $this->mValue;
+               }
+               return $a;
+       }
+
+       public function onSubmit( array $data ) {
+               if ( !empty( $data['type'] ) && !empty( $data['value'] ) ) {
+                       $this->setParameter( $data['type'] . '/' . $data['value'] );
+               }
+               /* if this returns false, will show the form */
+               return $this->dispatch();
+       }
+
+       public function onSuccess() {
+               /* do nothing, we redirect in $this->dispatch if successful. */
+       }
+
+       protected function alterForm( HTMLForm $form ) {
+               /* display summary at top of page */
+               $this->outputHeader();
+               /* tweak label on submit button */
+               $form->setSubmitTextMsg( $this->getMessagePrefix() . '-submit' );
+               /* submit form every time */
+               $form->setMethod( 'get' );
+       }
+
+       protected function getGroupName() {
+               return 'redirects';
+       }
+}
index 8191f59..c41d935 100644 (file)
@@ -447,6 +447,7 @@ $specialPageAliases = array(
        'Randomredirect'            => array( 'RandomRedirect' ),
        'Recentchanges'             => array( 'RecentChanges' ),
        'Recentchangeslinked'       => array( 'RecentChangesLinked', 'RelatedChanges' ),
+       'Redirect'                  => array( 'Redirect' ),
        'Revisiondelete'            => array( 'RevisionDelete' ),
        'Search'                    => array( 'Search' ),
        'Shortpages'                => array( 'ShortPages' ),
@@ -4794,6 +4795,19 @@ You should have received [{{SERVER}}{{SCRIPTPATH}}/COPYING a copy of the GNU Gen
 'filepath-summary' => 'This special page returns the complete path for a file.
 Images are shown in full resolution, other file types are started with their associated program directly.',
 
+# Special:Redirect
+'redirect'         => 'Redirect by file, user, or revision ID',
+'redirect-legend'  => 'Redirect to a file or page',
+'redirect-text'    => '',
+'redirect-summary' => 'This special page redirects to a file (given the file name), a page (given a revision ID), or a user page (given a numeric user ID).',
+'redirect-submit'  => 'Go',
+'redirect-lookup'  => 'Lookup:',
+'redirect-value'   => 'Value:',
+'redirect-user'     => 'User ID',
+'redirect-revision' => 'Page revision',
+'redirect-file'     => 'File name',
+'redirect-not-exists' => 'Value not found',
+
 # Special:FileDuplicateSearch
 'fileduplicatesearch'           => 'Search for duplicate files',
 'fileduplicatesearch-summary'   => 'Search for duplicate files based on hash values.',
index 4ef67e5..1ebf777 100644 (file)
@@ -8514,6 +8514,20 @@ A short description of the script path entry point. Links to the mediawiki.org d
 {{Identical|Go}}',
 'filepath-summary' => 'Shown in [[Special:FilePath]]',
 
+# Special:Redirect
+'redirect'         => 'Main heading of [[Special:Redirect]] page',
+'redirect-legend'  => 'Legend of fieldset around input box in [[Special:Redirect]]',
+'redirect-text'    => 'Inside fieldset for [[Special:Redirect]]',
+'redirect-summary' => 'Shown at top of [[Special:Redirect]]',
+'redirect-submit'  => 'Button label in [[Special:Redirect]].
+{{Identical|Go}}',
+'redirect-lookup'  => 'First field label in [[Special:Redirect]]',
+'redirect-value'   => 'Second field label in [[Special:Redirect]]',
+'redirect-user'     => 'Description of lookup type for [[Special:Redirect]]',
+'redirect-revision' => 'Description of lookup type for [[Special:Redirect]]',
+'redirect-file'     => 'Description of lookup type for [[Special:Redirect]]',
+'redirect-not-exists' => 'Used as error message in [[Special:Redirect]]',
+
 # Special:FileDuplicateSearch
 'fileduplicatesearch' => 'Name of special page [[Special:FileDuplicateSearch]].',
 'fileduplicatesearch-summary' => 'Summary of [[Special:FileDuplicateSearch]]',
index 96a6c13..deb01d5 100644 (file)
@@ -3643,6 +3643,19 @@ $wgMessageStructure = array(
                'filepath-submit',
                'filepath-summary',
        ),
+       'redirect' => array(
+               'redirect',
+               'redirect-legend',
+               'redirect-text',
+               'redirect-summary',
+               'redirect-submit',
+               'redirect-lookup',
+               'redirect-value',
+               'redirect-user',
+               'redirect-revision',
+               'redirect-file',
+               'redirect-not-exists',
+       ),
        'fileduplicatesearch' => array(
                'fileduplicatesearch',
                'fileduplicatesearch-summary',