* (bug 4578) Automatically fix redirects broken by a page move. Works via the job...
authorTim Starling <tstarling@users.mediawiki.org>
Tue, 22 Jul 2008 22:44:34 +0000 (22:44 +0000)
committerTim Starling <tstarling@users.mediawiki.org>
Tue, 22 Jul 2008 22:44:34 +0000 (22:44 +0000)
* Renamed some excessively short variables in SpecialMovepage.php
* Allow $wgReservedUsernames to be localised using "msg:..." syntax

RELEASE-NOTES
includes/AutoLoader.php
includes/DefaultSettings.php
includes/JobQueue.php
includes/User.php
includes/specials/SpecialMovepage.php
languages/messages/MessagesEn.php

index fa69fa8..7795850 100644 (file)
@@ -194,6 +194,7 @@ it from source control: http://www.mediawiki.org/wiki/Download_from_SVN
 * Special:Recentchangeslinked now includes changes to transcluded pages and
   displayed images; also, the "Show changes to pages linked" checkbox now works on
   category pages too, showing all links that are not categorizations
+* (bug 4578) Automatically fix redirects broken by a page move 
 
 === Bug fixes in 1.13 ===
 
index e90491b..4f36784 100644 (file)
@@ -43,6 +43,7 @@ class AutoLoader {
                '_DiffOp' => 'includes/DifferenceEngine.php',
                'DjVuImage' => 'includes/DjVuImage.php',
                'DoubleReplacer' => 'includes/StringUtils.php',
+               'DoubleRedirectJob' => 'includes/DoubleRedirectJob.php',
                'Dump7ZipOutput' => 'includes/Export.php',
                'DumpBZip2Output' => 'includes/Export.php',
                'DumpFileOutput' => 'includes/Export.php',
index 3a13d32..dfb4707 100644 (file)
@@ -1595,6 +1595,7 @@ $wgJobClasses = array(
        'html_cache_update' => 'HTMLCacheUpdateJob', // backwards-compatible
        'sendMail' => 'EmaillingJob',
        'enotifNotify' => 'EnotifNotifyJob',
+       'fixDoubleRedirect' => 'DoubleRedirectJob',
 );
 
 /**
@@ -3078,6 +3079,7 @@ $wgReservedUsernames = array(
        'Conversion script', // Used for the old Wikipedia software upgrade
        'Maintenance script', // Maintenance scripts which perform editing, image import script
        'Template namespace initialisation script', // Used in 1.2->1.3 upgrade
+       'msg:double-redirect-fixer', // Automatic double redirect fix
 );
 
 /**
index 23fabd7..8bfd1b3 100644 (file)
@@ -163,7 +163,10 @@ abstract class Job {
                $job = Job::factory( $row->job_cmd, $title, Job::extractBlob( $row->job_params ), $row->job_id );
 
                // Remove any duplicates it may have later in the queue
+               // Deadlock prone section
+               $dbw->begin();
                $dbw->delete( 'job', $job->insertFields(), __METHOD__ );
+               $dbw->commit();
 
                wfProfileOut( __METHOD__ );
                return $job;
@@ -213,12 +216,23 @@ abstract class Job {
         * @param $jobs array of Job objects
         */
        static function batchInsert( $jobs ) {
-               if( count( $jobs ) ) {
-                       $dbw = wfGetDB( DB_MASTER );
-                       $dbw->begin();
-                       foreach( $jobs as $job ) {
-                               $rows[] = $job->insertFields();
+               if( !count( $jobs ) ) {
+                       return;
+               }
+               $dbw = wfGetDB( DB_MASTER );
+               $rows = array();
+               foreach( $jobs as $job ) {
+                       $rows[] = $job->insertFields();
+                       if ( count( $rows ) >= 50 ) {
+                               # Do a small transaction to avoid slave lag
+                               $dbw->begin();
+                               $dbw->insert( 'job', $rows, __METHOD__, 'IGNORE' );
+                               $dbw->commit();
+                               $rows = array();
                        }
+               }
+               if ( $rows ) {
+                       $dbw->begin();
                        $dbw->insert( 'job', $rows, __METHOD__, 'IGNORE' );
                        $dbw->commit();
                }
@@ -288,6 +302,10 @@ abstract class Job {
                }
        }
 
+       protected function setLastError( $error ) {
+               $this->error = $error;
+       }
+
        function getLastError() {
                return $this->error;
        }
index ed5257f..5c12981 100644 (file)
@@ -497,12 +497,21 @@ class User {
         */
        static function isUsableName( $name ) {
                global $wgReservedUsernames;
-               return
-                       // Must be a valid username, obviously ;)
-                       self::isValidUserName( $name ) &&
+               // Must be a valid username, obviously ;)
+               if ( !self::isValidUserName( $name ) ) {
+                       return false;
+               }
 
-                       // Certain names may be reserved for batch processes.
-                       !in_array( $name, $wgReservedUsernames );
+               // Certain names may be reserved for batch processes.
+               foreach ( $wgReservedUsernames as $reserved ) {
+                       if ( substr( $reserved, 0, 4 ) == 'msg:' ) {
+                               $reserved = wfMsgForContent( substr( $reserved, 4 ) );
+                       }
+                       if ( $reserved == $name ) {
+                               return false;
+                       }
+               }
+               return true;
        }
 
        /**
index caba8c4..cbf9045 100644 (file)
@@ -17,36 +17,35 @@ function wfSpecialMovepage( $par = null ) {
        }
 
        $target = isset( $par ) ? $par : $wgRequest->getVal( 'target' );
-       $oldTitle = $wgRequest->getText( 'wpOldTitle', $target );
-       $newTitle = $wgRequest->getText( 'wpNewTitle' );
+       $oldTitleText = $wgRequest->getText( 'wpOldTitle', $target );
+       $newTitleText = $wgRequest->getText( 'wpNewTitle' );
 
-       # Variables beginning with 'o' for old article 'n' for new article
-       $ot = Title::newFromText( $oldTitle );
-       $nt = Title::newFromText( $newTitle );
+       $oldTitle = Title::newFromText( $oldTitleText );
+       $newTitle = Title::newFromText( $newTitleText );
 
-       if( is_null( $ot ) ) {
+       if( is_null( $oldTitle ) ) {
                $wgOut->showErrorPage( 'notargettitle', 'notargettext' );
                return;
        }
-       if( !$ot->exists() ) {
+       if( !$oldTitle->exists() ) {
                $wgOut->showErrorPage( 'nopagetitle', 'nopagetext' );
                return;
        }
 
        # Check rights
-       $permErrors = $ot->getUserPermissionsErrors( 'move', $wgUser );
+       $permErrors = $oldTitle->getUserPermissionsErrors( 'move', $wgUser );
        if( !empty( $permErrors ) ) {
                $wgOut->showPermissionsErrorPage( $permErrors );
                return;
        }
 
-       $f = new MovePageForm( $ot, $nt );
+       $form = new MovePageForm( $oldTitle, $newTitle );
 
        if ( 'submit' == $action && $wgRequest->wasPosted()
                && $wgUser->matchEditToken( $wgRequest->getVal( 'wpEditToken' ) ) ) {
-               $f->doSubmit();
+               $form->doSubmit();
        } else {
-               $f->showForm( '' );
+               $form->showForm( '' );
        }
 }
 
@@ -56,7 +55,7 @@ function wfSpecialMovepage( $par = null ) {
  */
 class MovePageForm {
        var $oldTitle, $newTitle, $reason; # Text input
-       var $moveTalk, $deleteAndMove, $moveSubpages;
+       var $moveTalk, $deleteAndMove, $moveSubpages, $fixRedirects;
 
        private $watch = false;
 
@@ -73,17 +72,17 @@ class MovePageForm {
                }
                $this->moveSubpages = $wgRequest->getBool( 'wpMovesubpages', false );
                $this->deleteAndMove = $wgRequest->getBool( 'wpDeleteAndMove' ) && $wgRequest->getBool( 'wpConfirm' );
+               $this->fixRedirects = $wgRequest->getBool( 'wpFixRedirects', true );
                $this->watch = $wgRequest->getCheck( 'wpWatch' );
        }
 
        function showForm( $err, $hookErr = '' ) {
                global $wgOut, $wgUser;
 
-               $ot = $this->oldTitle;
-               $sk = $wgUser->getSkin();
+               $skin = $wgUser->getSkin();
 
-               $oldTitleLink = $sk->makeLinkObj( $ot );
-               $oldTitle = $ot->getPrefixedText();
+               $oldTitleLink = $skin->makeLinkObj( $this->oldTitle );
+               $oldTitle = $this->oldTitle->getPrefixedText();
 
                $wgOut->setPagetitle( wfMsg( 'move-page', $oldTitle ) );
                $wgOut->setSubtitle( wfMsg( 'move-page-backlink', $oldTitleLink ) );
@@ -99,7 +98,7 @@ class MovePageForm {
                                        # If a title was supplied, probably from the move log revert
                                        # link, check for validity. We can then show some diagnostic
                                        # information and save a click.
-                                       $newerr = $ot->isValidMoveOperation( $nt );
+                                       $newerr = $this->oldTitle->isValidMoveOperation( $nt );
                                        if( is_string( $newerr ) ) {
                                                $err = $newerr;
                                        }
@@ -127,9 +126,16 @@ class MovePageForm {
                        $confirm = false;
                }
 
-               $oldTalk = $ot->getTalkPage();
-               $considerTalk = ( !$ot->isTalkPage() && $oldTalk->exists() );
+               $oldTalk = $this->oldTitle->getTalkPage();
+               $considerTalk = ( !$this->oldTitle->isTalkPage() && $oldTalk->exists() );
 
+               $dbr = wfGetDB( DB_SLAVE );
+               $hasRedirects = $dbr->selectField( 'redirect', '1', 
+                       array( 
+                               'rd_namespace' => $this->oldTitle->getNamespace(),
+                               'rd_title' => $this->oldTitle->getDBkey(),
+                       ) , __METHOD__ );
+               
                if ( $considerTalk ) {
                        $wgOut->addWikiMsg( 'movepagetalktext' );
                }
@@ -190,28 +196,41 @@ class MovePageForm {
                        );
                }
 
-               if( ($ot->hasSubpages() || $ot->getTalkPage()->hasSubpages())
-               && $ot->userCan( 'move-subpages' ) ) {
+               if ( $hasRedirects ) {
+                       $wgOut->addHTML( "
+                               <tr>
+                                       <td></td>
+                                       <td class='mw-input' >" .
+                                               Xml::checkLabel( wfMsg( 'fix-double-redirects' ), 'wpFixRedirects', 
+                                                       'wpFixRedirects', $this->fixRedirects ) .
+                                       "</td>
+                               </td>"
+                       );
+               }
+
+               if( ($this->oldTitle->hasSubpages() || $this->oldTitle->getTalkPage()->hasSubpages())
+               && $this->oldTitle->userCan( 'move-subpages' ) ) {
                        $wgOut->addHTML( "
                                <tr>
                                        <td></td>
                                        <td class=\"mw-input\">" .
                                Xml::checkLabel( wfMsgHtml(
-                                               $ot->hasSubpages()
+                                               $this->oldTitle->hasSubpages()
                                                ? 'move-subpages'
                                                : 'move-talk-subpages'
                                        ),
                                        'wpMovesubpages', 'wpMovesubpages',
                                        # Don't check the box if we only have talk subpages to
                                        # move and we aren't moving the talk page.
-                                       $this->moveSubpages && ($ot->hasSubpages() || $this->moveTalk)
+                                       $this->moveSubpages && ($this->oldTitle->hasSubpages() || $this->moveTalk)
                                ) .
                                        "</td>
                                </tr>"
                        );
                }
 
-               $watchChecked = $this->watch || $wgUser->getBoolOption( 'watchmoves' ) || $ot->userIsWatching();
+               $watchChecked = $this->watch || $wgUser->getBoolOption( 'watchmoves' ) 
+                       || $this->oldTitle->userIsWatching();
                $wgOut->addHTML( "
                        <tr>
                                <td></td>
@@ -233,7 +252,7 @@ class MovePageForm {
                        "\n"
                );
 
-               $this->showLogFragment( $ot, $wgOut );
+               $this->showLogFragment( $this->oldTitle, $wgOut );
 
        }
 
@@ -277,6 +296,10 @@ class MovePageForm {
                        return;
                }
 
+               if ( $this->fixRedirects ) {
+                       DoubleRedirectJob::fixRedirects( 'move', $ot, $nt );
+               }
+
                wfRunHooks( 'SpecialMovepageAfterMove', array( &$this , &$ot , &$nt ) ) ;
 
                $wgOut->setPagetitle( wfMsg( 'pagemovedsub' ) );
@@ -358,40 +381,43 @@ class MovePageForm {
                                continue;
                        }
 
-                       $oldPage = Title::newFromRow( $row );
+                       $oldSubpage = Title::newFromRow( $row );
                        $newPageName = preg_replace(
                                '#^'.preg_quote( $ot->getDBKey(), '#' ).'#',
                                $nt->getDBKey(),
-                               $oldPage->getDBKey()
+                               $oldSubpage->getDBKey()
                        );
-                       if( $oldPage->isTalkPage() ) {
+                       if( $oldSubpage->isTalkPage() ) {
                                $newNs = $nt->getTalkPage()->getNamespace();
                        } else {
                                $newNs = $nt->getSubjectPage()->getNamespace();
                        }
                        # Bug 14385: we need makeTitleSafe because the new page names may
                        # be longer than 255 characters.
-                       $newPage = Title::makeTitleSafe( $newNs, $newPageName );
-                       if( !$newPage ) {
-                               $oldLink = $skin->makeKnownLinkObj( $oldPage );
+                       $newSubpage = Title::makeTitleSafe( $newNs, $newPageName );
+                       if( !$newSubpage ) {
+                               $oldLink = $skin->makeKnownLinkObj( $oldSubpage );
                                $extraOutput []= wfMsgHtml( 'movepage-page-unmoved', $oldLink,
                                        htmlspecialchars(Title::makeName( $newNs, $newPageName )));
                                continue;
                        }
 
                        # This was copy-pasted from Renameuser, bleh.
-                       if ( $newPage->exists() && !$oldPage->isValidMoveTarget( $newPage ) ) {
-                               $link = $skin->makeKnownLinkObj( $newPage );
+                       if ( $newSubpage->exists() && !$oldSubpage->isValidMoveTarget( $newSubpage ) ) {
+                               $link = $skin->makeKnownLinkObj( $newSubpage );
                                $extraOutput []= wfMsgHtml( 'movepage-page-exists', $link );
                        } else {
-                               $success = $oldPage->moveTo( $newPage, true, $this->reason );
+                               $success = $oldSubpage->moveTo( $newSubpage, true, $this->reason );
                                if( $success === true ) {
-                                       $oldLink = $skin->makeKnownLinkObj( $oldPage, '', 'redirect=no' );
-                                       $newLink = $skin->makeKnownLinkObj( $newPage );
+                                       if ( $this->fixRedirects ) {
+                                               DoubleRedirectJob::fixRedirects( 'move', $oldSubpage, $newSubpage );
+                                       }
+                                       $oldLink = $skin->makeKnownLinkObj( $oldSubpage, '', 'redirect=no' );
+                                       $newLink = $skin->makeKnownLinkObj( $newSubpage );
                                        $extraOutput []= wfMsgHtml( 'movepage-page-moved', $oldLink, $newLink );
                                } else {
-                                       $oldLink = $skin->makeKnownLinkObj( $oldPage );
-                                       $newLink = $skin->makeLinkObj( $newPage );
+                                       $oldLink = $skin->makeKnownLinkObj( $oldSubpage );
+                                       $newLink = $skin->makeLinkObj( $newSubpage );
                                        $extraOutput []= wfMsgHtml( 'movepage-page-unmoved', $oldLink, $newLink );
                                }
                        }
index 96f0c4a..1d080ce 100644 (file)
@@ -1871,6 +1871,9 @@ A page is treated as disambiguation page if it uses a template which is linked f
 'doubleredirectstext'     => 'This page lists pages which redirect to other redirect pages.
 Each row contains links to the first and second redirect, as well as the target of the second redirect, which is usually "real" target page, which the first redirect should point to.',
 
+'double-redirect-fixed-move'   => '[[$1]] has been moved, it is now just a redirect to [[$2]]',
+'double-redirect-fixer'   => 'Redirect fixer',
+
 'brokenredirects'         => 'Broken redirects',
 'brokenredirects-summary' => '', # do not translate or duplicate this message to other languages
 'brokenredirectstext'     => 'The following redirects link to non-existent pages:',
@@ -2500,6 +2503,7 @@ cannot move pages from and into that namespace.',
 'imagenocrossnamespace'   => 'Cannot move file to non-file namespace',
 'imagetypemismatch'       => 'The new file extension does not match its type',
 'imageinvalidfilename'    => 'The target file name is invalid',
+'fix-double-redirects'    => 'Update any redirects that point to the original title',
 
 # Export
 'export'            => 'Export pages',