X-Git-Url: http://git.cyclocoop.org/?a=blobdiff_plain;f=includes%2FTitle.php;h=b97d36a416191a86b7bec1fdb50433f32d4f2e03;hb=9682e3b1374bd093be3e6b18a5e6543e4244233f;hp=ca292eefcf46b4cde9dcc21dfb3134a7578ed212;hpb=81041728520a8eb72df2dab422aa35e0052e71b0;p=lhc%2Fweb%2Fwiklou.git diff --git a/includes/Title.php b/includes/Title.php index ca292eefcf..b97d36a416 100644 --- a/includes/Title.php +++ b/includes/Title.php @@ -795,7 +795,8 @@ class Title { /** * Determine whether the object refers to a page within - * this project. + * this project (either this wiki or a wiki with a local + * interwiki, see https://www.mediawiki.org/wiki/Manual:Interwiki_table#iw_local ) * * @return bool True if this is an in-project interwiki link or a wikilink, false otherwise */ @@ -2231,19 +2232,13 @@ class Title { } elseif ( $action == 'create' ) { $title_protection = $this->getTitleProtection(); if ( $title_protection ) { - if ( $title_protection['pt_create_perm'] == 'sysop' ) { - $title_protection['pt_create_perm'] = 'editprotected'; // B/C - } - if ( $title_protection['pt_create_perm'] == 'autoconfirmed' ) { - $title_protection['pt_create_perm'] = 'editsemiprotected'; // B/C - } - if ( $title_protection['pt_create_perm'] == '' - || !$user->isAllowed( $title_protection['pt_create_perm'] ) + if ( $title_protection['permission'] == '' + || !$user->isAllowed( $title_protection['permission'] ) ) { $errors[] = array( 'titleprotected', - User::whoIs( $title_protection['pt_user'] ), - $title_protection['pt_reason'] + User::whoIs( $title_protection['user'] ), + $title_protection['reason'] ); } } @@ -2535,7 +2530,7 @@ class Title { * @return array|bool An associative array representing any existent title * protection, or false if there's none. */ - private function getTitleProtection() { + public function getTitleProtection() { // Can't protect pages in special namespaces if ( $this->getNamespace() < 0 ) { return false; @@ -2550,13 +2545,27 @@ class Title { $dbr = wfGetDB( DB_SLAVE ); $res = $dbr->select( 'protected_titles', - array( 'pt_user', 'pt_reason', 'pt_expiry', 'pt_create_perm' ), + array( + 'user' => 'pt_user', + 'reason' => 'pt_reason', + 'expiry' => 'pt_expiry', + 'permission' => 'pt_create_perm' + ), array( 'pt_namespace' => $this->getNamespace(), 'pt_title' => $this->getDBkey() ), __METHOD__ ); // fetchRow returns false if there are no rows. - $this->mTitleProtection = $dbr->fetchRow( $res ); + $row = $dbr->fetchRow( $res ); + if ( $row ) { + if ( $row['permission'] == 'sysop' ) { + $row['permission'] = 'editprotected'; // B/C + } + if ( $row['permission'] == 'autoconfirmed' ) { + $row['permission'] = 'editsemiprotected'; // B/C + } + } + $this->mTitleProtection = $row; } return $this->mTitleProtection; } @@ -2977,12 +2986,12 @@ class Title { if ( $title_protection ) { $now = wfTimestampNow(); - $expiry = $wgContLang->formatExpiry( $title_protection['pt_expiry'], TS_MW ); + $expiry = $wgContLang->formatExpiry( $title_protection['expiry'], TS_MW ); if ( !$expiry || $expiry > $now ) { // Apply the restrictions $this->mRestrictionsExpiry['create'] = $expiry; - $this->mRestrictions['create'] = explode( ',', trim( $title_protection['pt_create_perm'] ) ); + $this->mRestrictions['create'] = explode( ',', trim( $title_protection['permission'] ) ); } else { // Get rid of the old restrictions Title::purgeExpiredRestrictions(); $this->mTitleProtection = false; @@ -3578,10 +3587,12 @@ class Title { /** * Move this page without authentication * + * @deprecated since 1.25 use MovePage class instead * @param Title $nt The new page Title * @return array|bool True on success, getUserPermissionsErrors()-like array on failure */ public function moveNoAuth( &$nt ) { + wfDeprecated( __METHOD__, '1.25' ); return $this->moveTo( $nt, false ); } @@ -3589,116 +3600,33 @@ class Title { * Check whether a given move operation would be valid. * Returns true if ok, or a getUserPermissionsErrors()-like array otherwise * + * @deprecated since 1.25, use MovePage's methods instead * @param Title $nt The new title - * @param bool $auth Indicates whether $wgUser's permissions - * should be checked + * @param bool $auth Ignored * @param string $reason Is the log summary of the move, used for spam checking * @return array|bool True on success, getUserPermissionsErrors()-like array on failure */ public function isValidMoveOperation( &$nt, $auth = true, $reason = '' ) { - global $wgUser, $wgContentHandlerUseDB; + global $wgUser; - $errors = array(); - if ( !$nt ) { + if ( !( $nt instanceof Title ) ) { // Normally we'd add this to $errors, but we'll get // lots of syntax errors if $nt is not an object return array( array( 'badtitletext' ) ); } - if ( $this->equals( $nt ) ) { - $errors[] = array( 'selfmove' ); - } - if ( !$this->isMovable() ) { - $errors[] = array( 'immobile-source-namespace', $this->getNsText() ); - } - if ( $nt->isExternal() ) { - $errors[] = array( 'immobile-target-namespace-iw' ); - } - if ( !$nt->isMovable() ) { - $errors[] = array( 'immobile-target-namespace', $nt->getNsText() ); - } - - $oldid = $this->getArticleID(); - $newid = $nt->getArticleID(); - - if ( strlen( $nt->getDBkey() ) < 1 ) { - $errors[] = array( 'articleexists' ); - } - if ( - ( $this->getDBkey() == '' ) || - ( !$oldid ) || - ( $nt->getDBkey() == '' ) - ) { - $errors[] = array( 'badarticleerror' ); - } - - // Content model checks - if ( !$wgContentHandlerUseDB && - $this->getContentModel() !== $nt->getContentModel() ) { - // can't move a page if that would change the page's content model - $errors[] = array( - 'bad-target-model', - ContentHandler::getLocalizedName( $this->getContentModel() ), - ContentHandler::getLocalizedName( $nt->getContentModel() ) - ); - } - // Image-specific checks - if ( $this->getNamespace() == NS_FILE ) { - $errors = array_merge( $errors, $this->validateFileMoveOperation( $nt ) ); - } - - if ( $nt->getNamespace() == NS_FILE && $this->getNamespace() != NS_FILE ) { - $errors[] = array( 'nonfile-cannot-move-to-file' ); - } - - if ( $auth ) { - $errors = wfMergeErrorArrays( $errors, - $this->getUserPermissionsErrors( 'move', $wgUser ), - $this->getUserPermissionsErrors( 'edit', $wgUser ), - $nt->getUserPermissionsErrors( 'move-target', $wgUser ), - $nt->getUserPermissionsErrors( 'edit', $wgUser ) ); - } - - $match = EditPage::matchSummarySpamRegex( $reason ); - if ( $match !== false ) { - // This is kind of lame, won't display nice - $errors[] = array( 'spamprotectiontext' ); - } - - $err = null; - if ( !wfRunHooks( 'AbortMove', array( $this, $nt, $wgUser, &$err, $reason ) ) ) { - $errors[] = array( 'hookaborted', $err ); - } - - # 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). + $mp = new MovePage( $this, $nt ); + $errors = wfMergeErrorArrays( + $mp->isValidMove()->getErrorsArray(), + $mp->checkPermissions( $wgUser, $reason )->getErrorsArray() + ); - if ( 0 != $newid ) { # Target exists; check for validity - if ( !$this->isValidMoveTarget( $nt ) ) { - $errors[] = array( 'articleexists' ); - } - } else { - $tp = $nt->getTitleProtection(); - $right = $tp['pt_create_perm']; - if ( $right == 'sysop' ) { - $right = 'editprotected'; // B/C - } - if ( $right == 'autoconfirmed' ) { - $right = 'editsemiprotected'; // B/C - } - if ( $tp and !$wgUser->isAllowed( $right ) ) { - $errors[] = array( 'cantmove-titleprotected' ); - } - } - if ( empty( $errors ) ) { - return true; - } - return $errors; + return $errors ? : true; } /** * Check if the requested move target is a valid file move target + * @todo move this to MovePage * @param Title $nt Target title * @return array List of errors */ @@ -3707,27 +3635,6 @@ class Title { $errors = array(); - // wfFindFile( $nt ) / wfLocalFile( $nt ) is not allowed until below - - $file = wfLocalFile( $this ); - if ( $file->exists() ) { - if ( $nt->getText() != wfStripIllegalFilenameChars( $nt->getText() ) ) { - $errors[] = array( 'imageinvalidfilename' ); - } - if ( !File::checkExtensionCompatibility( $file, $nt->getDBkey() ) ) { - $errors[] = array( 'imagetypemismatch' ); - } - } - - if ( $nt->getNamespace() != NS_FILE ) { - $errors[] = array( 'imagenocrossnamespace' ); - // From here we want to do checks on a file object, so if we can't - // create one, we must return. - return $errors; - } - - // wfFindFile( $nt ) / wfLocalFile( $nt ) is allowed below here - $destFile = wfLocalFile( $nt ); if ( !$wgUser->isAllowed( 'reupload-shared' ) && !$destFile->exists() && wfFindFile( $nt ) ) { $errors[] = array( 'file-exists-sharedrepo' ); @@ -3739,6 +3646,7 @@ class Title { /** * Move a title to a new location * + * @deprecated since 1.25, use the MovePage class instead * @param Title $nt The new title * @param bool $auth Indicates whether $wgUser's permissions * should be checked @@ -3760,281 +3668,13 @@ class Title { $createRedirect = true; } - 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. - $prefixes = $dbw->select( - 'categorylinks', - array( 'cl_sortkey_prefix', 'cl_to' ), - array( 'cl_from' => $pageid ), - __METHOD__ - ); - 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_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. + $mp = new MovePage( $this, $nt ); + $status = $mp->move( $wgUser, $reason, $createRedirect ); + if ( $status->isOK() ) { + return true; } else { - $redirectContent = null; + return $status->getErrorsArray(); } - - // 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 ); } /** @@ -4163,6 +3803,7 @@ class Title { * Checks if $this can be moved to a given Title * - Selects for update, so don't call it unless you mean business * + * @deprecated since 1.25, use MovePage's methods instead * @param Title $nt The new title to check * @return bool */ @@ -4597,7 +4238,9 @@ class Title { * @return bool */ public function exists() { - return $this->getArticleID() != 0; + $exists = $this->getArticleID() != 0; + wfRunHooks( 'TitleExists', array( $this, &$exists ) ); + return $exists; } /**