diff options
Diffstat (limited to 'includes/MovePage.php')
-rw-r--r-- | includes/MovePage.php | 204 |
1 files changed, 197 insertions, 7 deletions
diff --git a/includes/MovePage.php b/includes/MovePage.php index fdece8d5..de7da3f9 100644 --- a/includes/MovePage.php +++ b/includes/MovePage.php @@ -42,6 +42,188 @@ class MovePage { $this->newTitle = $newTitle; } + public function checkPermissions( User $user, $reason ) { + $status = new Status(); + + $errors = wfMergeErrorArrays( + $this->oldTitle->getUserPermissionsErrors( 'move', $user ), + $this->oldTitle->getUserPermissionsErrors( 'edit', $user ), + $this->newTitle->getUserPermissionsErrors( 'move-target', $user ), + $this->newTitle->getUserPermissionsErrors( 'edit', $user ) + ); + + // Convert into a Status object + if ( $errors ) { + foreach ( $errors as $error ) { + call_user_func_array( array( $status, 'fatal' ), $error ); + } + } + + if ( EditPage::matchSummarySpamRegex( $reason ) !== false ) { + // This is kind of lame, won't display nice + $status->fatal( 'spamprotectiontext' ); + } + + # The move is allowed only if (1) the target doesn't exist, or + # (2) the target is a redirect to the source, and has no history + # (so we can undo bad moves right after they're done). + + if ( $this->newTitle->getArticleID() ) { # Target exists; check for validity + if ( !$this->isValidMoveTarget() ) { + $status->fatal( 'articleexists' ); + } + } else { + $tp = $this->newTitle->getTitleProtection(); + if ( $tp !== false ) { + if ( !$user->isAllowed( $tp['permission'] ) ) { + $status->fatal( 'cantmove-titleprotected' ); + } + } + } + + Hooks::run( 'MovePageCheckPermissions', + array( $this->oldTitle, $this->newTitle, $user, $reason, $status ) + ); + + return $status; + } + + /** + * Does various sanity checks that the move is + * valid. Only things based on the two titles + * should be checked here. + * + * @return Status + */ + public function isValidMove() { + global $wgContentHandlerUseDB; + $status = new Status(); + + if ( $this->oldTitle->equals( $this->newTitle ) ) { + $status->fatal( 'selfmove' ); + } + if ( !$this->oldTitle->isMovable() ) { + $status->fatal( 'immobile-source-namespace', $this->oldTitle->getNsText() ); + } + if ( $this->newTitle->isExternal() ) { + $status->fatal( 'immobile-target-namespace-iw' ); + } + if ( !$this->newTitle->isMovable() ) { + $status->fatal( 'immobile-target-namespace', $this->newTitle->getNsText() ); + } + + $oldid = $this->oldTitle->getArticleID(); + + if ( strlen( $this->newTitle->getDBkey() ) < 1 ) { + $status->fatal( 'articleexists' ); + } + if ( + ( $this->oldTitle->getDBkey() == '' ) || + ( !$oldid ) || + ( $this->newTitle->getDBkey() == '' ) + ) { + $status->fatal( 'badarticleerror' ); + } + + // Content model checks + if ( !$wgContentHandlerUseDB && + $this->oldTitle->getContentModel() !== $this->newTitle->getContentModel() ) { + // can't move a page if that would change the page's content model + $status->fatal( + 'bad-target-model', + ContentHandler::getLocalizedName( $this->oldTitle->getContentModel() ), + ContentHandler::getLocalizedName( $this->newTitle->getContentModel() ) + ); + } + + // Image-specific checks + if ( $this->oldTitle->inNamespace( NS_FILE ) ) { + $status->merge( $this->isValidFileMove() ); + } + + if ( $this->newTitle->inNamespace( NS_FILE ) && !$this->oldTitle->inNamespace( NS_FILE ) ) { + $status->fatal( 'nonfile-cannot-move-to-file' ); + } + + // Hook for extensions to say a title can't be moved for technical reasons + Hooks::run( 'MovePageIsValidMove', array( $this->oldTitle, $this->newTitle, $status ) ); + + return $status; + } + + /** + * Sanity checks for when a file is being moved + * + * @return Status + */ + protected function isValidFileMove() { + $status = new Status(); + $file = wfLocalFile( $this->oldTitle ); + $file->load( File::READ_LATEST ); + if ( $file->exists() ) { + if ( $this->newTitle->getText() != wfStripIllegalFilenameChars( $this->newTitle->getText() ) ) { + $status->fatal( 'imageinvalidfilename' ); + } + if ( !File::checkExtensionCompatibility( $file, $this->newTitle->getDBkey() ) ) { + $status->fatal( 'imagetypemismatch' ); + } + } + + if ( !$this->newTitle->inNamespace( NS_FILE ) ) { + $status->fatal( 'imagenocrossnamespace' ); + } + + return $status; + } + + /** + * Checks if $this can be moved to a given Title + * - Selects for update, so don't call it unless you mean business + * + * @since 1.25 + * @return bool + */ + protected function isValidMoveTarget() { + # Is it an existing file? + if ( $this->newTitle->inNamespace( NS_FILE ) ) { + $file = wfLocalFile( $this->newTitle ); + $file->load( File::READ_LATEST ); + if ( $file->exists() ) { + wfDebug( __METHOD__ . ": file exists\n" ); + return false; + } + } + # Is it a redirect with no history? + if ( !$this->newTitle->isSingleRevRedirect() ) { + wfDebug( __METHOD__ . ": not a one-rev redirect\n" ); + return false; + } + # Get the article text + $rev = Revision::newFromTitle( $this->newTitle, false, Revision::READ_LATEST ); + if ( !is_object( $rev ) ) { + return false; + } + $content = $rev->getContent(); + # Does the redirect point to the source? + # Or is it a broken self-redirect, usually caused by namespace collisions? + $redirTitle = $content ? $content->getRedirectTarget() : null; + + if ( $redirTitle ) { + if ( $redirTitle->getPrefixedDBkey() !== $this->oldTitle->getPrefixedDBkey() && + $redirTitle->getPrefixedDBkey() !== $this->newTitle->getPrefixedDBkey() ) { + wfDebug( __METHOD__ . ": redirect points to other page\n" ); + return false; + } else { + return true; + } + } else { + # Fail safe (not a redirect after all. strange.) + wfDebug( __METHOD__ . ": failsafe: database says " . $this->newTitle->getPrefixedDBkey() . + " is a redirect, but it doesn't contain a valid redirect.\n" ); + return false; + } + } + /** * @param User $user * @param string $reason @@ -51,11 +233,14 @@ class MovePage { public function move( User $user, $reason, $createRedirect ) { global $wgCategoryCollation; + Hooks::run( 'TitleMove', array( $this->oldTitle, $this->newTitle, $user ) ); + // 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 ); + $file->load( File::READ_LATEST ); if ( $file->exists() ) { $status = $file->move( $this->newTitle ); if ( !$status->isOk() ) { @@ -188,9 +373,11 @@ class MovePage { $dbw->commit( __METHOD__ ); - wfRunHooks( 'TitleMoveComplete', array( &$this->oldTitle, &$this->newTitle, &$user, $pageid, $redirid, $reason ) ); + Hooks::run( + 'TitleMoveComplete', + array( &$this->oldTitle, &$this->newTitle, &$user, $pageid, $redirid, $reason ) + ); return Status::newGood(); - } /** @@ -258,6 +445,9 @@ class MovePage { $dbw = wfGetDB( DB_MASTER ); + $oldpage = WikiPage::factory( $this->oldTitle ); + $oldcountable = $oldpage->isCountable(); + $newpage = WikiPage::factory( $nt ); if ( $moveOverRedirect ) { @@ -302,10 +492,11 @@ class MovePage { $newpage->updateRevisionOn( $dbw, $nullRevision ); - wfRunHooks( 'NewRevisionFromEditComplete', + Hooks::run( 'NewRevisionFromEditComplete', array( $newpage, $nullRevision, $nullRevision->getParentId(), $user ) ); - $newpage->doEditUpdates( $nullRevision, $user, array( 'changed' => false ) ); + $newpage->doEditUpdates( $nullRevision, $user, + array( 'changed' => false, 'moved' => true, 'oldcountable' => $oldcountable ) ); if ( !$moveOverRedirect ) { WikiPage::onArticleCreate( $nt ); @@ -328,7 +519,7 @@ class MovePage { $redirectRevision->insertOn( $dbw ); $redirectArticle->updateRevisionOn( $dbw, $redirectRevision, 0 ); - wfRunHooks( 'NewRevisionFromEditComplete', + Hooks::run( 'NewRevisionFromEditComplete', array( $redirectArticle, $redirectRevision, false, $user ) ); $redirectArticle->doEditUpdates( $redirectRevision, $user, array( 'created' => true ) ); @@ -339,5 +530,4 @@ class MovePage { $logid = $logEntry->insert(); $logEntry->publish( $logid ); } - -}
\ No newline at end of file +} |