X-Git-Url: https://git.heureux-cyclage.org/?a=blobdiff_plain;f=includes%2FMovePage.php;h=832e24af81225ff4bd52d3ef617cb5a0e9561ca0;hb=f76e04e20d461e4a576522a7f6d1853d729d8b8c;hp=2edd669f13fa72f8194b3d036c7bff5e8d38d0b8;hpb=1f46e0e6dc36a8f351f3b71a324f8ac0e6f19adc;p=lhc%2Fweb%2Fwiklou.git diff --git a/includes/MovePage.php b/includes/MovePage.php index 2edd669f13..832e24af81 100644 --- a/includes/MovePage.php +++ b/includes/MovePage.php @@ -46,6 +46,14 @@ class MovePage { $this->newTitle = $newTitle; } + /** + * Check if the user is allowed to perform the move. + * + * @param User $user + * @param string|null $reason To check against summary spam regex. Set to null to skip the check, + * for instance to display errors preemptively before the user has filled in a summary. + * @return Status + */ public function checkPermissions( User $user, $reason ) { $status = new Status(); @@ -63,7 +71,7 @@ class MovePage { } } - if ( EditPage::matchSummarySpamRegex( $reason ) !== false ) { + if ( $reason !== null && EditPage::matchSummarySpamRegex( $reason ) !== false ) { // This is kind of lame, won't display nice $status->fatal( 'spamprotectiontext' ); } @@ -233,14 +241,195 @@ class MovePage { } /** + * Move a page without taking user permissions into account. Only checks if the move is itself + * invalid, e.g., trying to move a special page or trying to move a page onto one that already + * exists. + * + * @param User $user + * @param string|null $reason + * @param bool|null $createRedirect + * @param string[] $changeTags Change tags to apply to the entry in the move log + * @return Status + */ + public function move( + User $user, $reason = null, $createRedirect = true, array $changeTags = [] + ) { + $status = $this->isValidMove(); + if ( !$status->isOK() ) { + return $status; + } + + return $this->moveUnsafe( $user, $reason, $createRedirect, $changeTags ); + } + + /** + * Same as move(), but with permissions checks. + * + * @param User $user + * @param string|null $reason + * @param bool|null $createRedirect Ignored if user doesn't have suppressredirect permission + * @param string[] $changeTags Change tags to apply to the entry in the move log + * @return Status + */ + public function moveIfAllowed( + User $user, $reason = null, $createRedirect = true, array $changeTags = [] + ) { + $status = $this->isValidMove(); + $status->merge( $this->checkPermissions( $user, $reason ) ); + if ( $changeTags ) { + $status->merge( ChangeTags::canAddTagsAccompanyingChange( $changeTags, $user ) ); + } + + if ( !$status->isOK() ) { + // Auto-block user's IP if the account was "hard" blocked + $user->spreadAnyEditBlock(); + return $status; + } + + // Check suppressredirect permission + if ( !$user->isAllowed( 'suppressredirect' ) ) { + $createRedirect = true; + } + + return $this->moveUnsafe( $user, $reason, $createRedirect, $changeTags ); + } + + /** + * Move the source page's subpages to be subpages of the target page, without checking user + * permissions. The caller is responsible for moving the source page itself. We will still not + * do moves that are inherently not allowed, nor will we move more than $wgMaximumMovedPages. + * + * @param User $user + * @param string|null $reason The reason for the move + * @param bool|null $createRedirect Whether to create redirects from the old subpages to + * the new ones + * @param string[] $changeTags Applied to entries in the move log and redirect page revision + * @return Status Good if no errors occurred. Ok if at least one page succeeded. The "value" + * of the top-level status is an array containing the per-title status for each page. For any + * move that succeeded, the "value" of the per-title status is the new page title. + */ + public function moveSubpages( + User $user, $reason = null, $createRedirect = true, array $changeTags = [] + ) { + return $this->moveSubpagesInternal( false, $user, $reason, $createRedirect, $changeTags ); + } + + /** + * Move the source page's subpages to be subpages of the target page, with user permission + * checks. The caller is responsible for moving the source page itself. + * + * @param User $user + * @param string|null $reason The reason for the move + * @param bool|null $createRedirect Whether to create redirects from the old subpages to + * the new ones. Ignored if the user doesn't have the 'suppressredirect' right. + * @param string[] $changeTags Applied to entries in the move log and redirect page revision + * @return Status Good if no errors occurred. Ok if at least one page succeeded. The "value" + * of the top-level status is an array containing the per-title status for each page. For any + * move that succeeded, the "value" of the per-title status is the new page title. + */ + public function moveSubpagesIfAllowed( + User $user, $reason = null, $createRedirect = true, array $changeTags = [] + ) { + return $this->moveSubpagesInternal( true, $user, $reason, $createRedirect, $changeTags ); + } + + /** + * @param bool $checkPermissions + * @param User $user + * @param string $reason + * @param bool $createRedirect + * @param array $changeTags + * @return Status + */ + private function moveSubpagesInternal( + $checkPermissions, User $user, $reason, $createRedirect, array $changeTags + ) { + global $wgMaximumMovedPages; + $services = MediaWikiServices::getInstance(); + + if ( $checkPermissions ) { + if ( !$services->getPermissionManager()->userCan( + 'move-subpages', $user, $this->oldTitle ) + ) { + return Status::newFatal( 'cant-move-subpages' ); + } + } + + $nsInfo = $services->getNamespaceInfo(); + + // Do the source and target namespaces support subpages? + if ( !$nsInfo->hasSubpages( $this->oldTitle->getNamespace() ) ) { + return Status::newFatal( 'namespace-nosubpages', + $nsInfo->getCanonicalName( $this->oldTitle->getNamespace() ) ); + } + if ( !$nsInfo->hasSubpages( $this->newTitle->getNamespace() ) ) { + return Status::newFatal( 'namespace-nosubpages', + $nsInfo->getCanonicalName( $this->newTitle->getNamespace() ) ); + } + + // Return a status for the overall result. Its value will be an array with per-title + // status for each subpage. Merge any errors from the per-title statuses into the + // top-level status without resetting the overall result. + $topStatus = Status::newGood(); + $perTitleStatus = []; + $subpages = $this->oldTitle->getSubpages( $wgMaximumMovedPages + 1 ); + $count = 0; + foreach ( $subpages as $oldSubpage ) { + $count++; + if ( $count > $wgMaximumMovedPages ) { + $status = Status::newFatal( 'movepage-max-pages', $wgMaximumMovedPages ); + $perTitleStatus[$oldSubpage->getPrefixedText()] = $status; + $topStatus->merge( $status ); + $topStatus->setOk( true ); + break; + } + + // We don't know whether this function was called before or after moving the root page, + // so check both titles + if ( $oldSubpage->getArticleID() == $this->oldTitle->getArticleID() || + $oldSubpage->getArticleID() == $this->newTitle->getArticleID() + ) { + // When moving a page to a subpage of itself, don't move it twice + continue; + } + $newPageName = preg_replace( + '#^' . preg_quote( $this->oldTitle->getDBkey(), '#' ) . '#', + StringUtils::escapeRegexReplacement( $this->newTitle->getDBkey() ), # T23234 + $oldSubpage->getDBkey() ); + if ( $oldSubpage->isTalkPage() ) { + $newNs = $this->newTitle->getTalkPage()->getNamespace(); + } else { + $newNs = $this->newTitle->getSubjectPage()->getNamespace(); + } + // T16385: we need makeTitleSafe because the new page names may be longer than 255 + // characters. + $newSubpage = Title::makeTitleSafe( $newNs, $newPageName ); + + $mp = new MovePage( $oldSubpage, $newSubpage ); + $method = $checkPermissions ? 'moveIfAllowed' : 'move'; + $status = $mp->$method( $user, $reason, $createRedirect, $changeTags ); + if ( $status->isOK() ) { + $status->setResult( true, $newSubpage->getPrefixedText() ); + } + $perTitleStatus[$oldSubpage->getPrefixedText()] = $status; + $topStatus->merge( $status ); + $topStatus->setOk( true ); + } + + $topStatus->value = $perTitleStatus; + return $topStatus; + } + + /** + * Moves *without* any sort of safety or sanity checks. Hooks can still fail the move, however. + * * @param User $user * @param string $reason * @param bool $createRedirect - * @param string[] $changeTags Change tags to apply to the entry in the move log. Caller - * should perform permission checks with ChangeTags::canAddTagsAccompanyingChange + * @param string[] $changeTags Change tags to apply to the entry in the move log * @return Status */ - public function move( User $user, $reason, $createRedirect, array $changeTags = [] ) { + private function moveUnsafe( User $user, $reason, $createRedirect, array $changeTags ) { global $wgCategoryCollation; $status = Status::newGood(); @@ -272,7 +461,9 @@ class MovePage { [ 'cl_from' => $pageid ], __METHOD__ ); - $type = MWNamespace::getCategoryLinkType( $this->newTitle->getNamespace() ); + $services = MediaWikiServices::getInstance(); + $type = $services->getNamespaceInfo()-> + getCategoryLinkType( $this->newTitle->getNamespace() ); foreach ( $prefixes as $prefixRow ) { $prefix = $prefixRow->cl_sortkey_prefix; $catTo = $prefixRow->cl_to; @@ -372,11 +563,13 @@ class MovePage { # Update watchlists $oldtitle = $this->oldTitle->getDBkey(); $newtitle = $this->newTitle->getDBkey(); - $oldsnamespace = MWNamespace::getSubject( $this->oldTitle->getNamespace() ); - $newsnamespace = MWNamespace::getSubject( $this->newTitle->getNamespace() ); + $oldsnamespace = $services->getNamespaceInfo()-> + getSubject( $this->oldTitle->getNamespace() ); + $newsnamespace = $services->getNamespaceInfo()-> + getSubject( $this->newTitle->getNamespace() ); if ( $oldsnamespace != $newsnamespace || $oldtitle != $newtitle ) { - $store = MediaWikiServices::getInstance()->getWatchedItemStore(); - $store->duplicateAllAssociatedEntries( $this->oldTitle, $this->newTitle ); + $services->getWatchedItemStore()->duplicateAllAssociatedEntries( + $this->oldTitle, $this->newTitle ); } // If it is a file then move it last.