public function execute() { $async = $this->getOption('async', false); $dryrun = $this->getOption('dry-run', false); if ($this->hasOption('title')) { $title = Title::newFromText($this->getOption('title')); if (!$title || !$title->isRedirect()) { $this->error($title->getPrefixedText() . " is not a redirect!\n", true); } } else { $title = null; } $dbr = wfGetDB(DB_SLAVE); // See also SpecialDoubleRedirects $tables = array('redirect', 'pa' => 'page', 'pb' => 'page'); $fields = array('pa.page_namespace AS pa_namespace', 'pa.page_title AS pa_title', 'pb.page_namespace AS pb_namespace', 'pb.page_title AS pb_title'); $conds = array('rd_from = pa.page_id', 'rd_namespace = pb.page_namespace', 'rd_title = pb.page_title', 'rd_interwiki IS NULL OR rd_interwiki = ' . $dbr->addQuotes(''), 'pb.page_is_redirect' => 1); if ($title != null) { $conds['pb.page_namespace'] = $title->getNamespace(); $conds['pb.page_title'] = $title->getDBkey(); } // TODO: support batch querying $res = $dbr->select($tables, $fields, $conds, __METHOD__); if (!$res->numRows()) { $this->output("No double redirects found.\n"); return; } $jobs = array(); $processedTitles = "\n"; $n = 0; foreach ($res as $row) { $titleA = Title::makeTitle($row->pa_namespace, $row->pa_title); $titleB = Title::makeTitle($row->pb_namespace, $row->pb_title); $processedTitles .= "* [[{$titleA}]]\n"; $job = new DoubleRedirectJob($titleA, array('reason' => 'maintenance', 'redirTitle' => $titleB->getPrefixedDBkey())); if (!$async) { $success = $dryrun ? true : $job->run(); if (!$success) { $this->error("Error fixing " . $titleA->getPrefixedText() . ": " . $job->getLastError() . "\n"); } } else { $jobs[] = $job; // @todo FIXME: Hardcoded constant 10000 copied from DoubleRedirectJob class if (count($jobs) > 10000) { $this->queueJobs($jobs, $dryrun); $jobs = array(); } } if (++$n % 100 == 0) { $this->output("{$n}...\n"); } } if (count($jobs)) { $this->queueJobs($jobs, $dryrun); } $this->output("{$n} double redirects processed" . $processedTitles . "\n"); }
function doSubmit() { global $wgOut, $wgUser, $wgMaximumMovedPages, $wgLang; global $wgFixDoubleRedirects; if ($wgUser->pingLimiter('move')) { $wgOut->rateLimited(); return; } $ot = $this->oldTitle; $nt = $this->newTitle; # Delete to make way if requested if ($wgUser->isAllowed('delete') && $this->deleteAndMove) { $article = new Article($nt); # Disallow deletions of big articles $bigHistory = $article->isBigDeletion(); if ($bigHistory && !$nt->userCan('bigdelete')) { global $wgDeleteRevisionsLimit; $this->showForm(array('delete-toobig', $wgLang->formatNum($wgDeleteRevisionsLimit))); return; } // Delete an associated image if there is $file = wfLocalFile($nt); if ($file->exists()) { $file->delete(wfMsgForContent('delete_and_move_reason'), false); } // This may output an error message and exit $article->doDelete(wfMsgForContent('delete_and_move_reason')); } # don't allow moving to pages with # in if (!$nt || $nt->getFragment() != '') { $this->showForm('badtitletext'); return; } # Show a warning if the target file exists on a shared repo if ($nt->getNamespace() == NS_FILE && !($this->moveOverShared && $wgUser->isAllowed('reupload-shared')) && !RepoGroup::singleton()->getLocalRepo()->findFile($nt) && wfFindFile($nt)) { $this->showForm(array('file-exists-sharedrepo')); return; } if ($wgUser->isAllowed('suppressredirect')) { $createRedirect = $this->leaveRedirect; } else { $createRedirect = true; } # Do the actual move. $error = $ot->moveTo($nt, true, $this->reason, $createRedirect); if ($error !== true) { # @todo FIXME: Show all the errors in a list, not just the first one $this->showForm(reset($error)); return; } if ($wgFixDoubleRedirects && $this->fixRedirects) { DoubleRedirectJob::fixRedirects('move', $ot, $nt); } wfRunHooks('SpecialMovepageAfterMove', array(&$this, &$ot, &$nt)); $wgOut->setPagetitle(wfMsg('pagemovedsub')); $oldUrl = $ot->getFullUrl('redirect=no'); $newUrl = $nt->getFullUrl(); $oldText = $ot->getPrefixedText(); $newText = $nt->getPrefixedText(); $oldLink = "<span class='plainlinks'>[{$oldUrl} {$oldText}]</span>"; $newLink = "<span class='plainlinks'>[{$newUrl} {$newText}]</span>"; $msgName = $createRedirect ? 'movepage-moved-redirect' : 'movepage-moved-noredirect'; $wgOut->addWikiMsg('movepage-moved', $oldLink, $newLink, $oldText, $newText); $wgOut->addWikiMsg($msgName); # Now we move extra pages we've been asked to move: subpages and talk # pages. First, if the old page or the new page is a talk page, we # can't move any talk pages: cancel that. if ($ot->isTalkPage() || $nt->isTalkPage()) { $this->moveTalk = false; } if (!$ot->userCan('move-subpages')) { $this->moveSubpages = false; } # Next make a list of id's. This might be marginally less efficient # than a more direct method, but this is not a highly performance-cri- # tical code path and readable code is more important here. # # Note: this query works nicely on MySQL 5, but the optimizer in MySQL # 4 might get confused. If so, consider rewriting as a UNION. # # If the target namespace doesn't allow subpages, moving with subpages # would mean that you couldn't move them back in one operation, which # is bad. # @todo FIXME: A specific error message should be given in this case. // @todo FIXME: Use Title::moveSubpages() here $dbr = wfGetDB(DB_MASTER); if ($this->moveSubpages && (MWNamespace::hasSubpages($nt->getNamespace()) || $this->moveTalk && MWNamespace::hasSubpages($nt->getTalkPage()->getNamespace()))) { $conds = array('page_title' . $dbr->buildLike($ot->getDBkey() . '/', $dbr->anyString()) . ' OR page_title = ' . $dbr->addQuotes($ot->getDBkey())); $conds['page_namespace'] = array(); if (MWNamespace::hasSubpages($nt->getNamespace())) { $conds['page_namespace'][] = $ot->getNamespace(); } if ($this->moveTalk && MWNamespace::hasSubpages($nt->getTalkPage()->getNamespace())) { $conds['page_namespace'][] = $ot->getTalkPage()->getNamespace(); } } elseif ($this->moveTalk) { $conds = array('page_namespace' => $ot->getTalkPage()->getNamespace(), 'page_title' => $ot->getDBkey()); } else { # Skip the query $conds = null; } $extraPages = array(); if (!is_null($conds)) { $extraPages = TitleArray::newFromResult($dbr->select('page', array('page_id', 'page_namespace', 'page_title'), $conds, __METHOD__)); } $extraOutput = array(); $skin = $this->getSkin(); $count = 1; foreach ($extraPages as $oldSubpage) { if ($ot->equals($oldSubpage)) { # Already did this one. continue; } $newPageName = preg_replace('#^' . preg_quote($ot->getDBkey(), '#') . '#', StringUtils::escapeRegexReplacement($nt->getDBkey()), $oldSubpage->getDBkey()); 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. $newSubpage = Title::makeTitleSafe($newNs, $newPageName); if (!$newSubpage) { $oldLink = $skin->linkKnown($oldSubpage); $extraOutput[] = wfMsgHtml('movepage-page-unmoved', $oldLink, htmlspecialchars(Title::makeName($newNs, $newPageName))); continue; } # This was copy-pasted from Renameuser, bleh. if ($newSubpage->exists() && !$oldSubpage->isValidMoveTarget($newSubpage)) { $link = $skin->linkKnown($newSubpage); $extraOutput[] = wfMsgHtml('movepage-page-exists', $link); } else { $success = $oldSubpage->moveTo($newSubpage, true, $this->reason, $createRedirect); if ($success === true) { if ($this->fixRedirects) { DoubleRedirectJob::fixRedirects('move', $oldSubpage, $newSubpage); } $oldLink = $skin->linkKnown($oldSubpage, null, array(), array('redirect' => 'no')); $newLink = $skin->linkKnown($newSubpage); $extraOutput[] = wfMsgHtml('movepage-page-moved', $oldLink, $newLink); ++$count; if ($count >= $wgMaximumMovedPages) { $extraOutput[] = wfMsgExt('movepage-max-pages', array('parsemag', 'escape'), $wgLang->formatNum($wgMaximumMovedPages)); break; } } else { $oldLink = $skin->linkKnown($oldSubpage); $newLink = $skin->link($newSubpage); $extraOutput[] = wfMsgHtml('movepage-page-unmoved', $oldLink, $newLink); } } } if ($extraOutput !== array()) { $wgOut->addHTML("<ul>\n<li>" . implode("</li>\n<li>", $extraOutput) . "</li>\n</ul>"); } # Deal with watches (we don't watch subpages) if ($this->watch && $wgUser->isLoggedIn()) { $wgUser->addWatch($ot); $wgUser->addWatch($nt); } else { $wgUser->removeWatch($ot); $wgUser->removeWatch($nt); } # Re-clear the file redirect cache, which may have been polluted by # parsing in messages above. See CR r56745. # @todo FIXME: Needs a more robust solution inside FileRepo. if ($ot->getNamespace() == NS_FILE) { RepoGroup::singleton()->getLocalRepo()->invalidateImageRedirect($ot); } }
function doSubmit() { $user = $this->getUser(); if ($user->pingLimiter('move')) { throw new ThrottledError(); } $ot = $this->oldTitle; $nt = $this->newTitle; # don't allow moving to pages with # in if (!$nt || $nt->hasFragment()) { $this->showForm(array(array('badtitletext'))); return; } # Show a warning if the target file exists on a shared repo if ($nt->getNamespace() == NS_FILE && !($this->moveOverShared && $user->isAllowed('reupload-shared')) && !RepoGroup::singleton()->getLocalRepo()->findFile($nt) && wfFindFile($nt)) { $this->showForm(array(array('file-exists-sharedrepo'))); return; } # Delete to make way if requested if ($this->deleteAndMove) { $permErrors = $nt->getUserPermissionsErrors('delete', $user); if (count($permErrors)) { # Only show the first error $this->showForm($permErrors); return; } $reason = $this->msg('delete_and_move_reason', $ot)->inContentLanguage()->text(); // Delete an associated image if there is if ($nt->getNamespace() == NS_FILE) { $file = wfLocalFile($nt); $file->load(File::READ_LATEST); if ($file->exists()) { $file->delete($reason, false, $user); } } $error = ''; // passed by ref $page = WikiPage::factory($nt); $deleteStatus = $page->doDeleteArticleReal($reason, false, 0, true, $error, $user); if (!$deleteStatus->isGood()) { $this->showForm($deleteStatus->getErrorsArray()); return; } } $handler = ContentHandler::getForTitle($ot); if (!$handler->supportsRedirects()) { $createRedirect = false; } elseif ($user->isAllowed('suppressredirect')) { $createRedirect = $this->leaveRedirect; } else { $createRedirect = true; } # Do the actual move. $mp = new MovePage($ot, $nt); $valid = $mp->isValidMove(); if (!$valid->isOK()) { $this->showForm($valid->getErrorsArray()); return; } $permStatus = $mp->checkPermissions($user, $this->reason); if (!$permStatus->isOK()) { $this->showForm($permStatus->getErrorsArray()); return; } $status = $mp->move($user, $this->reason, $createRedirect); if (!$status->isOK()) { $this->showForm($status->getErrorsArray()); return; } if ($this->getConfig()->get('FixDoubleRedirects') && $this->fixRedirects) { DoubleRedirectJob::fixRedirects('move', $ot, $nt); } $out = $this->getOutput(); $out->setPageTitle($this->msg('pagemovedsub')); $oldLink = Linker::link($ot, null, array('id' => 'movepage-oldlink'), array('redirect' => 'no')); $newLink = Linker::linkKnown($nt, null, array('id' => 'movepage-newlink')); $oldText = $ot->getPrefixedText(); $newText = $nt->getPrefixedText(); if ($ot->exists()) { // NOTE: we assume that if the old title exists, it's because it was re-created as // a redirect to the new title. This is not safe, but what we did before was // even worse: we just determined whether a redirect should have been created, // and reported that it was created if it should have, without any checks. // Also note that isRedirect() is unreliable because of bug 37209. $msgName = 'movepage-moved-redirect'; } else { $msgName = 'movepage-moved-noredirect'; } $out->addHTML($this->msg('movepage-moved')->rawParams($oldLink, $newLink)->params($oldText, $newText)->parseAsBlock()); $out->addWikiMsg($msgName); Hooks::run('SpecialMovepageAfterMove', array(&$this, &$ot, &$nt)); # Now we move extra pages we've been asked to move: subpages and talk # pages. First, if the old page or the new page is a talk page, we # can't move any talk pages: cancel that. if ($ot->isTalkPage() || $nt->isTalkPage()) { $this->moveTalk = false; } if (count($ot->getUserPermissionsErrors('move-subpages', $user))) { $this->moveSubpages = false; } # Next make a list of id's. This might be marginally less efficient # than a more direct method, but this is not a highly performance-cri- # tical code path and readable code is more important here. # # Note: this query works nicely on MySQL 5, but the optimizer in MySQL # 4 might get confused. If so, consider rewriting as a UNION. # # If the target namespace doesn't allow subpages, moving with subpages # would mean that you couldn't move them back in one operation, which # is bad. # @todo FIXME: A specific error message should be given in this case. // @todo FIXME: Use Title::moveSubpages() here $dbr = wfGetDB(DB_MASTER); if ($this->moveSubpages && (MWNamespace::hasSubpages($nt->getNamespace()) || $this->moveTalk && MWNamespace::hasSubpages($nt->getTalkPage()->getNamespace()))) { $conds = array('page_title' . $dbr->buildLike($ot->getDBkey() . '/', $dbr->anyString()) . ' OR page_title = ' . $dbr->addQuotes($ot->getDBkey())); $conds['page_namespace'] = array(); if (MWNamespace::hasSubpages($nt->getNamespace())) { $conds['page_namespace'][] = $ot->getNamespace(); } if ($this->moveTalk && MWNamespace::hasSubpages($nt->getTalkPage()->getNamespace())) { $conds['page_namespace'][] = $ot->getTalkPage()->getNamespace(); } } elseif ($this->moveTalk) { $conds = array('page_namespace' => $ot->getTalkPage()->getNamespace(), 'page_title' => $ot->getDBkey()); } else { # Skip the query $conds = null; } $extraPages = array(); if (!is_null($conds)) { $extraPages = TitleArray::newFromResult($dbr->select('page', array('page_id', 'page_namespace', 'page_title'), $conds, __METHOD__)); } $extraOutput = array(); $count = 1; foreach ($extraPages as $oldSubpage) { if ($ot->equals($oldSubpage) || $nt->equals($oldSubpage)) { # Already did this one. continue; } $newPageName = preg_replace('#^' . preg_quote($ot->getDBkey(), '#') . '#', StringUtils::escapeRegexReplacement($nt->getDBkey()), $oldSubpage->getDBkey()); if ($oldSubpage->isSubpage() && ($ot->isTalkPage() xor $nt->isTalkPage())) { // Moving a subpage from a subject namespace to a talk namespace or vice-versa $newNs = $nt->getNamespace(); } elseif ($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. $newSubpage = Title::makeTitleSafe($newNs, $newPageName); if (!$newSubpage) { $oldLink = Linker::linkKnown($oldSubpage); $extraOutput[] = $this->msg('movepage-page-unmoved')->rawParams($oldLink)->params(Title::makeName($newNs, $newPageName))->escaped(); continue; } # This was copy-pasted from Renameuser, bleh. if ($newSubpage->exists() && !$oldSubpage->isValidMoveTarget($newSubpage)) { $link = Linker::linkKnown($newSubpage); $extraOutput[] = $this->msg('movepage-page-exists')->rawParams($link)->escaped(); } else { $success = $oldSubpage->moveTo($newSubpage, true, $this->reason, $createRedirect); if ($success === true) { if ($this->fixRedirects) { DoubleRedirectJob::fixRedirects('move', $oldSubpage, $newSubpage); } $oldLink = Linker::link($oldSubpage, null, array(), array('redirect' => 'no')); $newLink = Linker::linkKnown($newSubpage); $extraOutput[] = $this->msg('movepage-page-moved')->rawParams($oldLink, $newLink)->escaped(); ++$count; $maximumMovedPages = $this->getConfig()->get('MaximumMovedPages'); if ($count >= $maximumMovedPages) { $extraOutput[] = $this->msg('movepage-max-pages')->numParams($maximumMovedPages)->escaped(); break; } } else { $oldLink = Linker::linkKnown($oldSubpage); $newLink = Linker::link($newSubpage); $extraOutput[] = $this->msg('movepage-page-unmoved')->rawParams($oldLink, $newLink)->escaped(); } } } if ($extraOutput !== array()) { $out->addHTML("<ul>\n<li>" . implode("</li>\n<li>", $extraOutput) . "</li>\n</ul>"); } # Deal with watches (we don't watch subpages) WatchAction::doWatchOrUnwatch($this->watch, $ot, $user); WatchAction::doWatchOrUnwatch($this->watch, $nt, $user); }
/** * Get a user object for doing edits, from a request-lifetime cache * False will be returned if the user name specified in the * 'double-redirect-fixer' message is invalid. * * @return User|bool */ function getUser() { if (!self::$user) { $username = wfMessage('double-redirect-fixer')->inContentLanguage()->text(); self::$user = User::newFromName($username); # User::newFromName() can return false on a badly configured wiki. if (self::$user && !self::$user->isLoggedIn()) { self::$user->addToDatabase(); } } return self::$user; }
/** * Get a user object for doing edits, from a request-lifetime cache */ function getUser() { if (!self::$user) { self::$user = User::newFromName(wfMsgForContent('double-redirect-fixer'), false); if (!self::$user->isLoggedIn()) { self::$user->addToDatabase(); } } return self::$user; }
/** * Get a user object for doing edits, from a request-lifetime cache * @return User */ function getUser() { if (!self::$user) { self::$user = User::newFromName(wfMsgForContent('double-redirect-fixer'), false); # FIXME: newFromName could return false on a badly configured wiki. if (!self::$user->isLoggedIn()) { self::$user->addToDatabase(); } } return self::$user; }