From: Kunal Mehta Date: Mon, 15 Sep 2014 02:16:22 +0000 (-0700) Subject: Start moving page move logic out of Title X-Git-Tag: 1.31.0-rc.0~13994^2 X-Git-Url: http://git.cyclocoop.org/%24image?a=commitdiff_plain;h=77698193f9f5d92e059a393471e9b7b14ac58dd3;p=lhc%2Fweb%2Fwiklou.git Start moving page move logic out of Title This moves most of the Title::moveTo() function into a new MovePage class, and the entirety of the Title::moveToInternal() function. No actual code was changed except for requiring a User object in function arguments instead of relying up on $wgUser, and changes to "$this" as necessary. Change-Id: I5479fa8f3920a51ddf789d55edae7dd0d9b24382 --- diff --git a/includes/AutoLoader.php b/includes/AutoLoader.php index 5242ec0f65..7713c7febb 100644 --- a/includes/AutoLoader.php +++ b/includes/AutoLoader.php @@ -126,6 +126,7 @@ $wgAutoloadLocalClasses = array( 'Message' => 'includes/Message.php', 'MessageBlobStore' => 'includes/MessageBlobStore.php', 'MimeMagic' => 'includes/MimeMagic.php', + 'MovePage' => 'includes/MovePage.php', 'MWHookException' => 'includes/Hooks.php', 'MWHttpRequest' => 'includes/HttpFunctions.php', 'MWNamespace' => 'includes/MWNamespace.php', diff --git a/includes/MovePage.php b/includes/MovePage.php new file mode 100644 index 0000000000..922fe3554a --- /dev/null +++ b/includes/MovePage.php @@ -0,0 +1,343 @@ +oldTitle = $oldTitle; + $this->newTitle = $newTitle; + } + + /** + * @param User $user + * @param string $reason + * @param bool $createRedirect + * @return array|bool True on success, getUserPermissionsErrors()-like array on failure + */ + public function move( User $user, $reason, $createRedirect ) { + global $wgCategoryCollation; + + // If it is a file, move it first. + // It is done before all other moving stuff is done because it's hard to revert. + $dbw = wfGetDB( DB_MASTER ); + if ( $this->oldTitle->getNamespace() == NS_FILE ) { + $file = wfLocalFile( $this->oldTitle ); + if ( $file->exists() ) { + $status = $file->move( $this->newTitle ); + if ( !$status->isOk() ) { + return $status->getErrorsArray(); + } + } + // Clear RepoGroup process cache + RepoGroup::singleton()->clearCache( $this->oldTitle ); + RepoGroup::singleton()->clearCache( $this->newTitle ); # clear false negative cache + } + + $dbw->begin( __METHOD__ ); # If $file was a LocalFile, its transaction would have closed our own. + $pageid = $this->oldTitle->getArticleID( Title::GAID_FOR_UPDATE ); + $protected = $this->oldTitle->isProtected(); + + // Do the actual move + $this->moveToInternal( $user, $this->newTitle, $reason, $createRedirect ); + + // Refresh the sortkey for this row. Be careful to avoid resetting + // cl_timestamp, which may disturb time-based lists on some sites. + // @todo This block should be killed, it's duplicating code + // from LinksUpdate::getCategoryInsertions() and friends. + $prefixes = $dbw->select( + 'categorylinks', + array( 'cl_sortkey_prefix', 'cl_to' ), + array( 'cl_from' => $pageid ), + __METHOD__ + ); + if ( $this->newTitle->getNamespace() == NS_CATEGORY ) { + $type = 'subcat'; + } elseif ( $this->newTitle->getNamespace() == NS_FILE ) { + $type = 'file'; + } else { + $type = 'page'; + } + foreach ( $prefixes as $prefixRow ) { + $prefix = $prefixRow->cl_sortkey_prefix; + $catTo = $prefixRow->cl_to; + $dbw->update( 'categorylinks', + array( + 'cl_sortkey' => Collation::singleton()->getSortKey( + $this->newTitle->getCategorySortkey( $prefix ) ), + 'cl_collation' => $wgCategoryCollation, + 'cl_type' => $type, + 'cl_timestamp=cl_timestamp' ), + array( + 'cl_from' => $pageid, + 'cl_to' => $catTo ), + __METHOD__ + ); + } + + $redirid = $this->oldTitle->getArticleID(); + + if ( $protected ) { + # Protect the redirect title as the title used to be... + $dbw->insertSelect( 'page_restrictions', 'page_restrictions', + array( + 'pr_page' => $redirid, + 'pr_type' => 'pr_type', + 'pr_level' => 'pr_level', + 'pr_cascade' => 'pr_cascade', + 'pr_user' => 'pr_user', + 'pr_expiry' => 'pr_expiry' + ), + array( 'pr_page' => $pageid ), + __METHOD__, + array( 'IGNORE' ) + ); + # Update the protection log + $log = new LogPage( 'protect' ); + $comment = wfMessage( + 'prot_1movedto2', + $this->oldTitle->getPrefixedText(), + $this->newTitle->getPrefixedText() + )->inContentLanguage()->text(); + if ( $reason ) { + $comment .= wfMessage( 'colon-separator' )->inContentLanguage()->text() . $reason; + } + // @todo FIXME: $params? + $logId = $log->addEntry( + 'move_prot', + $this->newTitle, + $comment, + array( $this->oldTitle->getPrefixedText() ), + $user + ); + + // reread inserted pr_ids for log relation + $insertedPrIds = $dbw->select( + 'page_restrictions', + 'pr_id', + array( 'pr_page' => $redirid ), + __METHOD__ + ); + $logRelationsValues = array(); + foreach ( $insertedPrIds as $prid ) { + $logRelationsValues[] = $prid->pr_id; + } + $log->addRelations( 'pr_id', $logRelationsValues, $logId ); + } + + // Update *_from_namespace fields as needed + if ( $this->oldTitle->getNamespace() != $this->newTitle->getNamespace() ) { + $dbw->update( 'pagelinks', + array( 'pl_from_namespace' => $this->newTitle->getNamespace() ), + array( 'pl_from' => $pageid ), + __METHOD__ + ); + $dbw->update( 'templatelinks', + array( 'tl_from_namespace' => $this->newTitle->getNamespace() ), + array( 'tl_from' => $pageid ), + __METHOD__ + ); + $dbw->update( 'imagelinks', + array( 'il_from_namespace' => $this->newTitle->getNamespace() ), + array( 'il_from' => $pageid ), + __METHOD__ + ); + } + + # Update watchlists + $oldtitle = $this->oldTitle->getDBkey(); + $newtitle = $this->newTitle->getDBkey(); + $oldsnamespace = MWNamespace::getSubject( $this->oldTitle->getNamespace() ); + $newsnamespace = MWNamespace::getSubject( $this->newTitle->getNamespace() ); + if ( $oldsnamespace != $newsnamespace || $oldtitle != $newtitle ) { + WatchedItem::duplicateEntries( $this->oldTitle, $this->newTitle ); + } + + $dbw->commit( __METHOD__ ); + + wfRunHooks( 'TitleMoveComplete', array( &$this->oldTitle, &$this->newTitle, &$user, $pageid, $redirid, $reason ) ); + return true; + + } + + /** + * Move page to a title which is either a redirect to the + * source page or nonexistent + * + * @fixme This was basically directly moved from Title, it should be split into smaller functions + * @param User $user the User doing the move + * @param Title $nt The page to move to, which should be a redirect or nonexistent + * @param string $reason The reason for the move + * @param bool $createRedirect Whether to leave a redirect at the old title. Does not check + * if the user has the suppressredirect right + * @throws MWException + */ + private function moveToInternal( User $user, &$nt, $reason = '', $createRedirect = true ) { + global $wgContLang; + + if ( $nt->exists() ) { + $moveOverRedirect = true; + $logType = 'move_redir'; + } else { + $moveOverRedirect = false; + $logType = 'move'; + } + + if ( $createRedirect ) { + if ( $this->oldTitle->getNamespace() == NS_CATEGORY + && !wfMessage( 'category-move-redirect-override' )->inContentLanguage()->isDisabled() + ) { + $redirectContent = new WikitextContent( + wfMessage( 'category-move-redirect-override' ) + ->params( $nt->getPrefixedText() )->inContentLanguage()->plain() ); + } else { + $contentHandler = ContentHandler::getForTitle( $this->oldTitle ); + $redirectContent = $contentHandler->makeRedirectContent( $nt, + wfMessage( 'move-redirect-text' )->inContentLanguage()->plain() ); + } + + // NOTE: If this page's content model does not support redirects, $redirectContent will be null. + } else { + $redirectContent = null; + } + + // bug 57084: log_page should be the ID of the *moved* page + $oldid = $this->oldTitle->getArticleID(); + $logTitle = clone $this->oldTitle; + + $logEntry = new ManualLogEntry( 'move', $logType ); + $logEntry->setPerformer( $user ); + $logEntry->setTarget( $logTitle ); + $logEntry->setComment( $reason ); + $logEntry->setParameters( array( + '4::target' => $nt->getPrefixedText(), + '5::noredir' => $redirectContent ? '0': '1', + ) ); + + $formatter = LogFormatter::newFromEntry( $logEntry ); + $formatter->setContext( RequestContext::newExtraneousContext( $this->oldTitle ) ); + $comment = $formatter->getPlainActionText(); + if ( $reason ) { + $comment .= wfMessage( 'colon-separator' )->inContentLanguage()->text() . $reason; + } + # Truncate for whole multibyte characters. + $comment = $wgContLang->truncate( $comment, 255 ); + + $dbw = wfGetDB( DB_MASTER ); + + $newpage = WikiPage::factory( $nt ); + + if ( $moveOverRedirect ) { + $newid = $nt->getArticleID(); + $newcontent = $newpage->getContent(); + + # Delete the old redirect. We don't save it to history since + # by definition if we've got here it's rather uninteresting. + # We have to remove it so that the next step doesn't trigger + # a conflict on the unique namespace+title index... + $dbw->delete( 'page', array( 'page_id' => $newid ), __METHOD__ ); + + $newpage->doDeleteUpdates( $newid, $newcontent ); + } + + # Save a null revision in the page's history notifying of the move + $nullRevision = Revision::newNullRevision( $dbw, $oldid, $comment, true, $user ); + if ( !is_object( $nullRevision ) ) { + throw new MWException( 'No valid null revision produced in ' . __METHOD__ ); + } + + $nullRevision->insertOn( $dbw ); + + # Change the name of the target page: + $dbw->update( 'page', + /* SET */ array( + 'page_namespace' => $nt->getNamespace(), + 'page_title' => $nt->getDBkey(), + ), + /* WHERE */ array( 'page_id' => $oldid ), + __METHOD__ + ); + + // clean up the old title before reset article id - bug 45348 + if ( !$redirectContent ) { + WikiPage::onArticleDelete( $this->oldTitle ); + } + + $this->oldTitle->resetArticleID( 0 ); // 0 == non existing + $nt->resetArticleID( $oldid ); + $newpage->loadPageData( WikiPage::READ_LOCKING ); // bug 46397 + + $newpage->updateRevisionOn( $dbw, $nullRevision ); + + wfRunHooks( 'NewRevisionFromEditComplete', + array( $newpage, $nullRevision, $nullRevision->getParentId(), $user ) ); + + $newpage->doEditUpdates( $nullRevision, $user, array( 'changed' => false ) ); + + if ( !$moveOverRedirect ) { + WikiPage::onArticleCreate( $nt ); + } + + # Recreate the redirect, this time in the other direction. + if ( $redirectContent ) { + $redirectArticle = WikiPage::factory( $this->oldTitle ); + $redirectArticle->loadFromRow( false, WikiPage::READ_LOCKING ); // bug 46397 + $newid = $redirectArticle->insertOn( $dbw ); + if ( $newid ) { // sanity + $this->oldTitle->resetArticleID( $newid ); + $redirectRevision = new Revision( array( + 'title' => $this->oldTitle, // for determining the default content model + 'page' => $newid, + 'user_text' => $user->getName(), + 'user' => $user->getId(), + 'comment' => $comment, + 'content' => $redirectContent ) ); + $redirectRevision->insertOn( $dbw ); + $redirectArticle->updateRevisionOn( $dbw, $redirectRevision, 0 ); + + wfRunHooks( 'NewRevisionFromEditComplete', + array( $redirectArticle, $redirectRevision, false, $user ) ); + + $redirectArticle->doEditUpdates( $redirectRevision, $user, array( 'created' => true ) ); + } + } + + # Log the move + $logid = $logEntry->insert(); + $logEntry->publish( $logid ); + } + +} \ No newline at end of file diff --git a/includes/Title.php b/includes/Title.php index 7959860f9b..a8d1bb885f 100644 --- a/includes/Title.php +++ b/includes/Title.php @@ -3589,6 +3589,7 @@ class Title { * Check whether a given move operation would be valid. * Returns true if ok, or a getUserPermissionsErrors()-like array otherwise * + * @todo move this into MovePage * @param Title $nt The new title * @param bool $auth Indicates whether $wgUser's permissions * should be checked @@ -3739,6 +3740,7 @@ class Title { /** * Move a title to a new location * + * @todo Deprecate this in favor of MovePage * @param Title $nt The new title * @param bool $auth Indicates whether $wgUser's permissions * should be checked @@ -3748,7 +3750,7 @@ class Title { * @return array|bool True on success, getUserPermissionsErrors()-like array on failure */ public function moveTo( &$nt, $auth = true, $reason = '', $createRedirect = true ) { - global $wgUser, $wgCategoryCollation; + global $wgUser; $err = $this->isValidMoveOperation( $nt, $auth, $reason ); if ( is_array( $err ) ) { // Auto-block user's IP if the account was "hard" blocked @@ -3762,290 +3764,8 @@ class Title { wfRunHooks( 'TitleMove', array( $this, $nt, $wgUser ) ); - // If it is a file, move it first. - // It is done before all other moving stuff is done because it's hard to revert. - $dbw = wfGetDB( DB_MASTER ); - if ( $this->getNamespace() == NS_FILE ) { - $file = wfLocalFile( $this ); - if ( $file->exists() ) { - $status = $file->move( $nt ); - if ( !$status->isOk() ) { - return $status->getErrorsArray(); - } - } - // Clear RepoGroup process cache - RepoGroup::singleton()->clearCache( $this ); - RepoGroup::singleton()->clearCache( $nt ); # clear false negative cache - } - - $dbw->begin( __METHOD__ ); # If $file was a LocalFile, its transaction would have closed our own. - $pageid = $this->getArticleID( self::GAID_FOR_UPDATE ); - $protected = $this->isProtected(); - - // Do the actual move - $this->moveToInternal( $nt, $reason, $createRedirect ); - - // Refresh the sortkey for this row. Be careful to avoid resetting - // cl_timestamp, which may disturb time-based lists on some sites. - // @todo This block should be killed, it's duplicating code - // from LinksUpdate::getCategoryInsertions() and friends. - $prefixes = $dbw->select( - 'categorylinks', - array( 'cl_sortkey_prefix', 'cl_to' ), - array( 'cl_from' => $pageid ), - __METHOD__ - ); - if ( $nt->getNamespace() == NS_CATEGORY ) { - $type = 'subcat'; - } elseif ( $nt->getNamespace() == NS_FILE ) { - $type = 'file'; - } else { - $type = 'page'; - } - foreach ( $prefixes as $prefixRow ) { - $prefix = $prefixRow->cl_sortkey_prefix; - $catTo = $prefixRow->cl_to; - $dbw->update( 'categorylinks', - array( - 'cl_sortkey' => Collation::singleton()->getSortKey( - $nt->getCategorySortkey( $prefix ) ), - 'cl_collation' => $wgCategoryCollation, - 'cl_type' => $type, - 'cl_timestamp=cl_timestamp' ), - array( - 'cl_from' => $pageid, - 'cl_to' => $catTo ), - __METHOD__ - ); - } - - $redirid = $this->getArticleID(); - - if ( $protected ) { - # Protect the redirect title as the title used to be... - $dbw->insertSelect( 'page_restrictions', 'page_restrictions', - array( - 'pr_page' => $redirid, - 'pr_type' => 'pr_type', - 'pr_level' => 'pr_level', - 'pr_cascade' => 'pr_cascade', - 'pr_user' => 'pr_user', - 'pr_expiry' => 'pr_expiry' - ), - array( 'pr_page' => $pageid ), - __METHOD__, - array( 'IGNORE' ) - ); - # Update the protection log - $log = new LogPage( 'protect' ); - $comment = wfMessage( - 'prot_1movedto2', - $this->getPrefixedText(), - $nt->getPrefixedText() - )->inContentLanguage()->text(); - if ( $reason ) { - $comment .= wfMessage( 'colon-separator' )->inContentLanguage()->text() . $reason; - } - // @todo FIXME: $params? - $logId = $log->addEntry( - 'move_prot', - $nt, - $comment, - array( $this->getPrefixedText() ), - $wgUser - ); - - // reread inserted pr_ids for log relation - $insertedPrIds = $dbw->select( - 'page_restrictions', - 'pr_id', - array( 'pr_page' => $redirid ), - __METHOD__ - ); - $logRelationsValues = array(); - foreach ( $insertedPrIds as $prid ) { - $logRelationsValues[] = $prid->pr_id; - } - $log->addRelations( 'pr_id', $logRelationsValues, $logId ); - } - - // Update *_from_namespace fields as needed - if ( $this->getNamespace() != $nt->getNamespace() ) { - $dbw->update( 'pagelinks', - array( 'pl_from_namespace' => $nt->getNamespace() ), - array( 'pl_from' => $pageid ), - __METHOD__ - ); - $dbw->update( 'templatelinks', - array( 'tl_from_namespace' => $nt->getNamespace() ), - array( 'tl_from' => $pageid ), - __METHOD__ - ); - $dbw->update( 'imagelinks', - array( 'il_from_namespace' => $nt->getNamespace() ), - array( 'il_from' => $pageid ), - __METHOD__ - ); - } - - # Update watchlists - $oldtitle = $this->getDBkey(); - $newtitle = $nt->getDBkey(); - $oldsnamespace = MWNamespace::getSubject( $this->getNamespace() ); - $newsnamespace = MWNamespace::getSubject( $nt->getNamespace() ); - if ( $oldsnamespace != $newsnamespace || $oldtitle != $newtitle ) { - WatchedItem::duplicateEntries( $this, $nt ); - } - - $dbw->commit( __METHOD__ ); - - wfRunHooks( 'TitleMoveComplete', array( &$this, &$nt, &$wgUser, $pageid, $redirid, $reason ) ); - return true; - } - - /** - * Move page to a title which is either a redirect to the - * source page or nonexistent - * - * @param Title $nt The page to move to, which should be a redirect or nonexistent - * @param string $reason The reason for the move - * @param bool $createRedirect Whether to leave a redirect at the old title. Does not check - * if the user has the suppressredirect right - * @throws MWException - */ - private function moveToInternal( &$nt, $reason = '', $createRedirect = true ) { - global $wgUser, $wgContLang; - - if ( $nt->exists() ) { - $moveOverRedirect = true; - $logType = 'move_redir'; - } else { - $moveOverRedirect = false; - $logType = 'move'; - } - - if ( $createRedirect ) { - if ( $this->getNamespace() == NS_CATEGORY - && !wfMessage( 'category-move-redirect-override' )->inContentLanguage()->isDisabled() - ) { - $redirectContent = new WikitextContent( - wfMessage( 'category-move-redirect-override' ) - ->params( $nt->getPrefixedText() )->inContentLanguage()->plain() ); - } else { - $contentHandler = ContentHandler::getForTitle( $this ); - $redirectContent = $contentHandler->makeRedirectContent( $nt, - wfMessage( 'move-redirect-text' )->inContentLanguage()->plain() ); - } - - // NOTE: If this page's content model does not support redirects, $redirectContent will be null. - } else { - $redirectContent = null; - } - - // bug 57084: log_page should be the ID of the *moved* page - $oldid = $this->getArticleID(); - $logTitle = clone $this; - - $logEntry = new ManualLogEntry( 'move', $logType ); - $logEntry->setPerformer( $wgUser ); - $logEntry->setTarget( $logTitle ); - $logEntry->setComment( $reason ); - $logEntry->setParameters( array( - '4::target' => $nt->getPrefixedText(), - '5::noredir' => $redirectContent ? '0': '1', - ) ); - - $formatter = LogFormatter::newFromEntry( $logEntry ); - $formatter->setContext( RequestContext::newExtraneousContext( $this ) ); - $comment = $formatter->getPlainActionText(); - if ( $reason ) { - $comment .= wfMessage( 'colon-separator' )->inContentLanguage()->text() . $reason; - } - # Truncate for whole multibyte characters. - $comment = $wgContLang->truncate( $comment, 255 ); - - $dbw = wfGetDB( DB_MASTER ); - - $newpage = WikiPage::factory( $nt ); - - if ( $moveOverRedirect ) { - $newid = $nt->getArticleID(); - $newcontent = $newpage->getContent(); - - # Delete the old redirect. We don't save it to history since - # by definition if we've got here it's rather uninteresting. - # We have to remove it so that the next step doesn't trigger - # a conflict on the unique namespace+title index... - $dbw->delete( 'page', array( 'page_id' => $newid ), __METHOD__ ); - - $newpage->doDeleteUpdates( $newid, $newcontent ); - } - - # Save a null revision in the page's history notifying of the move - $nullRevision = Revision::newNullRevision( $dbw, $oldid, $comment, true, $wgUser ); - if ( !is_object( $nullRevision ) ) { - throw new MWException( 'No valid null revision produced in ' . __METHOD__ ); - } - - $nullRevision->insertOn( $dbw ); - - # Change the name of the target page: - $dbw->update( 'page', - /* SET */ array( - 'page_namespace' => $nt->getNamespace(), - 'page_title' => $nt->getDBkey(), - ), - /* WHERE */ array( 'page_id' => $oldid ), - __METHOD__ - ); - - // clean up the old title before reset article id - bug 45348 - if ( !$redirectContent ) { - WikiPage::onArticleDelete( $this ); - } - - $this->resetArticleID( 0 ); // 0 == non existing - $nt->resetArticleID( $oldid ); - $newpage->loadPageData( WikiPage::READ_LOCKING ); // bug 46397 - - $newpage->updateRevisionOn( $dbw, $nullRevision ); - - wfRunHooks( 'NewRevisionFromEditComplete', - array( $newpage, $nullRevision, $nullRevision->getParentId(), $wgUser ) ); - - $newpage->doEditUpdates( $nullRevision, $wgUser, array( 'changed' => false ) ); - - if ( !$moveOverRedirect ) { - WikiPage::onArticleCreate( $nt ); - } - - # Recreate the redirect, this time in the other direction. - if ( $redirectContent ) { - $redirectArticle = WikiPage::factory( $this ); - $redirectArticle->loadFromRow( false, WikiPage::READ_LOCKING ); // bug 46397 - $newid = $redirectArticle->insertOn( $dbw ); - if ( $newid ) { // sanity - $this->resetArticleID( $newid ); - $redirectRevision = new Revision( array( - 'title' => $this, // for determining the default content model - 'page' => $newid, - 'user_text' => $wgUser->getName(), - 'user' => $wgUser->getId(), - 'comment' => $comment, - 'content' => $redirectContent ) ); - $redirectRevision->insertOn( $dbw ); - $redirectArticle->updateRevisionOn( $dbw, $redirectRevision, 0 ); - - wfRunHooks( 'NewRevisionFromEditComplete', - array( $redirectArticle, $redirectRevision, false, $wgUser ) ); - - $redirectArticle->doEditUpdates( $redirectRevision, $wgUser, array( 'created' => true ) ); - } - } - - # Log the move - $logid = $logEntry->insert(); - $logEntry->publish( $logid ); + $mp = new MovePage( $this, $nt ); + return $mp->move( $wgUser, $reason, $createRedirect ); } /**