From: jenkins-bot Date: Sun, 11 Jan 2015 13:25:49 +0000 (+0000) Subject: Merge "Surround edit notices with appropriate classes" X-Git-Tag: 1.31.0-rc.0~12716 X-Git-Url: http://git.cyclocoop.org/?a=commitdiff_plain;h=4d78d40d821227a9368e87306f447ffd3be5db88;hp=-c;p=lhc%2Fweb%2Fwiklou.git Merge "Surround edit notices with appropriate classes" --- 4d78d40d821227a9368e87306f447ffd3be5db88 diff --combined includes/Title.php index 0cac64ab17,b25676cb49..bb4d04ef36 --- a/includes/Title.php +++ b/includes/Title.php @@@ -158,9 -158,6 +158,9 @@@ class Title /** @var TitleValue A corresponding TitleValue object */ private $mTitleValue = null; + + /** @var bool Would deleting this page be a big deletion? */ + private $mIsBigDeletion = null; // @} /** @@@ -503,7 -500,7 +503,7 @@@ } $t = new Title(); - $t->mDbkeyform = Title::makeName( $ns, $title, $fragment, $interwiki ); + $t->mDbkeyform = Title::makeName( $ns, $title, $fragment, $interwiki, true ); if ( $t->secureAndSplit() ) { return $t; } else { @@@ -614,13 -611,28 +614,13 @@@ * Note that this doesn't pick up many things that could be wrong with titles, but that * replacing this regex with something valid will make many titles valid. * - * @todo move this into MediaWikiTitleCodec + * @deprecated since 1.25, use MediaWikiTitleCodec::getTitleInvalidRegex() instead * * @return string Regex string */ static function getTitleInvalidRegex() { - static $rxTc = false; - if ( !$rxTc ) { - # Matching titles will be held as illegal. - $rxTc = '/' . - # Any character not allowed is forbidden... - '[^' . self::legalChars() . ']' . - # URL percent encoding sequences interfere with the ability - # to round-trip titles -- you can't link to them consistently. - '|%[0-9A-Fa-f]{2}' . - # XML/HTML character references produce similar issues. - '|&[A-Za-z0-9\x80-\xff]+;' . - '|&#[0-9]+;' . - '|&#x[0-9A-Fa-f]+;' . - '/S'; - } - - return $rxTc; + wfDeprecated( __METHOD__, '1.25' ); + return MediaWikiTitleCodec::getTitleInvalidRegex(); } /** @@@ -732,20 -744,12 +732,20 @@@ * @param string $title The DB key form the title * @param string $fragment The link fragment (after the "#") * @param string $interwiki The interwiki prefix + * @param bool $canoncialNamespace If true, use the canonical name for + * $ns instead of the localized version. * @return string The prefixed form of the title */ - public static function makeName( $ns, $title, $fragment = '', $interwiki = '' ) { + public static function makeName( $ns, $title, $fragment = '', $interwiki = '', + $canoncialNamespace = false + ) { global $wgContLang; - $namespace = $wgContLang->getNsText( $ns ); + if ( $canoncialNamespace ) { + $namespace = MWNamespace::getCanonicalName( $ns ); + } else { + $namespace = $wgContLang->getNsText( $ns ); + } $name = $namespace == '' ? $title : "$namespace:$title"; if ( strval( $interwiki ) != '' ) { $name = "$interwiki:$name"; @@@ -788,8 -792,7 +788,8 @@@ /** * 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 */ @@@ -938,12 -941,10 +938,12 @@@ * Get the page's content model id, see the CONTENT_MODEL_XXX constants. * * @throws MWException + * @param int $flags A bit field; may be Title::GAID_FOR_UPDATE to select for update * @return string Content model id */ - public function getContentModel() { - if ( !$this->mContentModel ) { + public function getContentModel( $flags = 0 ) { + # Calling getArticleID() loads the field from cache as needed + if ( !$this->mContentModel && $this->getArticleID( $flags ) ) { $linkCache = LinkCache::singleton(); $this->mContentModel = $linkCache->getGoodLinkFieldObj( $this, 'model' ); } @@@ -1029,6 -1030,7 +1029,6 @@@ * Is this in a namespace that allows actual pages? * * @return bool - * @internal note -- uses hardcoded namespace index instead of constants */ public function canExist() { return $this->mNamespace >= NS_MAIN; @@@ -1164,7 -1166,7 +1164,7 @@@ } $result = true; - wfRunHooks( 'TitleIsMovable', array( $this, &$result ) ); + Hooks::run( 'TitleIsMovable', array( $this, &$result ) ); return $result; } @@@ -1234,9 -1236,9 +1234,9 @@@ # @note This hook is also called in ContentHandler::getDefaultModel. # It's called here again to make sure hook functions can force this - # method to return true even outside the mediawiki namespace. + # method to return true even outside the MediaWiki namespace. - wfRunHooks( 'TitleIsCssOrJsPage', array( $this, &$isCssOrJsPage ) ); + Hooks::run( 'TitleIsCssOrJsPage', array( $this, &$isCssOrJsPage ), '1.25' ); return $isCssOrJsPage; } @@@ -1319,25 -1321,6 +1319,25 @@@ return Title::makeTitle( $subjectNS, $this->getDBkey() ); } + /** + * Get the other title for this page, if this is a subject page + * get the talk page, if it is a subject page get the talk page + * + * @since 1.25 + * @throws MWException + * @return Title + */ + public function getOtherPage() { + if ( $this->isSpecialPage() ) { + throw new MWException( 'Special pages cannot have other pages' ); + } + if ( $this->isTalkPage() ) { + return $this->getSubjectPage(); + } else { + return $this->getTalkPage(); + } + } + /** * Get the default namespace index, for when there is no namespace * @@@ -1662,7 -1645,7 +1662,7 @@@ # Finally, add the fragment. $url .= $this->getFragmentForURL(); - wfRunHooks( 'GetFullURL', array( &$this, &$url, $query ) ); + Hooks::run( 'GetFullURL', array( &$this, &$url, $query ) ); return $url; } @@@ -1708,7 -1691,7 +1708,7 @@@ $dbkey = wfUrlencode( $this->getPrefixedDBkey() ); if ( $query == '' ) { $url = str_replace( '$1', $dbkey, $wgArticlePath ); - wfRunHooks( 'GetLocalURL::Article', array( &$this, &$url ) ); + Hooks::run( 'GetLocalURL::Article', array( &$this, &$url ) ); } else { global $wgVariantArticlePath, $wgActionPaths, $wgContLang; $url = false; @@@ -1753,7 -1736,7 +1753,7 @@@ } } - wfRunHooks( 'GetLocalURL::Internal', array( &$this, &$url, $query ) ); + Hooks::run( 'GetLocalURL::Internal', array( &$this, &$url, $query ) ); // @todo FIXME: This causes breakage in various places when we // actually expected a local URL and end up with dupe prefixes. @@@ -1761,7 -1744,7 +1761,7 @@@ $url = $wgServer . $url; } } - wfRunHooks( 'GetLocalURL', array( &$this, &$url, $query ) ); + Hooks::run( 'GetLocalURL', array( &$this, &$url, $query ) ); return $url; } @@@ -1782,6 -1765,7 +1782,6 @@@ * @return string The URL */ public function getLinkURL( $query = '', $query2 = false, $proto = PROTO_RELATIVE ) { - wfProfileIn( __METHOD__ ); if ( $this->isExternal() || $proto !== PROTO_RELATIVE ) { $ret = $this->getFullURL( $query, $query2, $proto ); } elseif ( $this->getPrefixedText() === '' && $this->hasFragment() ) { @@@ -1789,6 -1773,7 +1789,6 @@@ } else { $ret = $this->getLocalURL( $query, $query2 ) . $this->getFragmentForURL(); } - wfProfileOut( __METHOD__ ); return $ret; } @@@ -1809,7 -1794,7 +1809,7 @@@ $query = self::fixUrlQueryArgs( $query, $query2 ); $server = $wgInternalServer !== false ? $wgInternalServer : $wgServer; $url = wfExpandUrl( $server . $this->getLocalURL( $query ), PROTO_HTTP ); - wfRunHooks( 'GetInternalURL', array( &$this, &$url, $query ) ); + Hooks::run( 'GetInternalURL', array( &$this, &$url, $query ) ); return $url; } @@@ -1827,7 -1812,7 +1827,7 @@@ public function getCanonicalURL( $query = '', $query2 = false ) { $query = self::fixUrlQueryArgs( $query, $query2 ); $url = wfExpandUrl( $this->getLocalURL( $query ) . $this->getFragmentForURL(), PROTO_CANONICAL ); - wfRunHooks( 'GetCanonicalURL', array( &$this, &$url, $query ) ); + Hooks::run( 'GetCanonicalURL', array( &$this, &$url, $query ) ); return $url; } @@@ -1946,7 -1931,7 +1946,7 @@@ private function checkQuickPermissions( $action, $user, $errors, $doExpensiveQueries, $short ) { - if ( !wfRunHooks( 'TitleQuickPermissions', + if ( !Hooks::run( 'TitleQuickPermissions', array( $this, $user, $action, &$errors, $doExpensiveQueries, $short ) ) ) { return $errors; @@@ -2046,18 -2031,18 +2046,18 @@@ private function checkPermissionHooks( $action, $user, $errors, $doExpensiveQueries, $short ) { // Use getUserPermissionsErrors instead $result = ''; - if ( !wfRunHooks( 'userCan', array( &$this, &$user, $action, &$result ) ) ) { + if ( !Hooks::run( 'userCan', array( &$this, &$user, $action, &$result ) ) ) { return $result ? array() : array( array( 'badaccess-group0' ) ); } // Check getUserPermissionsErrors hook - if ( !wfRunHooks( 'getUserPermissionsErrors', array( &$this, &$user, $action, &$result ) ) ) { + if ( !Hooks::run( 'getUserPermissionsErrors', array( &$this, &$user, $action, &$result ) ) ) { $errors = $this->resultToError( $errors, $result ); } // Check getUserPermissionsErrorsExpensive hook if ( $doExpensiveQueries && !( $short && count( $errors ) > 0 ) - && !wfRunHooks( 'getUserPermissionsErrorsExpensive', array( &$this, &$user, $action, &$result ) ) + && !Hooks::run( 'getUserPermissionsErrorsExpensive', array( &$this, &$user, $action, &$result ) ) ) { $errors = $this->resultToError( $errors, $result ); } @@@ -2090,7 -2075,7 +2090,7 @@@ $ns = $this->mNamespace == NS_MAIN ? wfMessage( 'nstab-main' )->text() : $this->getNsText(); $errors[] = $this->mNamespace == NS_MEDIAWIKI ? - array( 'protectedinterface' ) : array( 'namespaceprotected', $ns ); + array( 'protectedinterface', $action ) : array( 'namespaceprotected', $ns, $action ); } return $errors; @@@ -2114,15 -2099,15 +2114,15 @@@ if ( $action != 'patrol' && !$user->isAllowed( 'editusercssjs' ) ) { if ( preg_match( '/^' . preg_quote( $user->getName(), '/' ) . '\//', $this->mTextform ) ) { if ( $this->isCssSubpage() && !$user->isAllowedAny( 'editmyusercss', 'editusercss' ) ) { - $errors[] = array( 'mycustomcssprotected' ); + $errors[] = array( 'mycustomcssprotected', $action ); } elseif ( $this->isJsSubpage() && !$user->isAllowedAny( 'editmyuserjs', 'edituserjs' ) ) { - $errors[] = array( 'mycustomjsprotected' ); + $errors[] = array( 'mycustomjsprotected', $action ); } } else { if ( $this->isCssSubpage() && !$user->isAllowed( 'editusercss' ) ) { - $errors[] = array( 'customcssprotected' ); + $errors[] = array( 'customcssprotected', $action ); } elseif ( $this->isJsSubpage() && !$user->isAllowed( 'edituserjs' ) ) { - $errors[] = array( 'customjsprotected' ); + $errors[] = array( 'customjsprotected', $action ); } } } @@@ -2157,9 -2142,9 +2157,9 @@@ continue; } if ( !$user->isAllowed( $right ) ) { - $errors[] = array( 'protectedpagetext', $right ); + $errors[] = array( 'protectedpagetext', $right, $action ); } elseif ( $this->mCascadeRestriction && !$user->isAllowed( 'protect' ) ) { - $errors[] = array( 'protectedpagetext', 'protect' ); + $errors[] = array( 'protectedpagetext', 'protect', $action ); } } @@@ -2206,7 -2191,7 +2206,7 @@@ foreach ( $cascadingSources as $page ) { $pages .= '* [[:' . $page->getPrefixedText() . "]]\n"; } - $errors[] = array( 'cascadeprotected', count( $cascadingSources ), $pages ); + $errors[] = array( 'cascadeprotected', count( $cascadingSources ), $pages, $action ); } } } @@@ -2241,13 -2226,19 +2241,13 @@@ } 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'] ); } } @@@ -2267,15 -2258,11 +2267,15 @@@ $errors[] = array( 'immobile-target-page' ); } } elseif ( $action == 'delete' ) { - if ( count( $this->getUserPermissionsErrorsInternal( 'edit', - $user, $doExpensiveQueries, true ) ) - ) { - // If they can't edit, they shouldn't delete. - $errors[] = array( 'delete-cantedit' ); + $tempErrors = $this->checkPageRestrictions( 'edit', + $user, array(), $doExpensiveQueries, true ); + if ( !$tempErrors ) { + $tempErrors = $this->checkCascadingSourcesRestrictions( 'edit', + $user, $tempErrors, $doExpensiveQueries, true ); + } + if ( $tempErrors ) { + // If protection keeps them from editing, they shouldn't be able to delete. + $errors[] = array( 'deleteprotected' ); } if ( $doExpensiveQueries && $wgDeleteRevisionsLimit && !$this->userCan( 'bigdelete', $user ) && $this->isBigDeletion() @@@ -2390,7 -2377,7 +2390,7 @@@ if ( !$whitelisted ) { # If the title is not whitelisted, give extensions a chance to do so... - wfRunHooks( 'TitleReadWhitelist', array( $this, $user, &$whitelisted ) ); + Hooks::run( 'TitleReadWhitelist', array( $this, $user, &$whitelisted ) ); if ( !$whitelisted ) { $errors[] = $this->missingPermissionError( $action, $short ); } @@@ -2442,6 -2429,7 +2442,6 @@@ protected function getUserPermissionsErrorsInternal( $action, $user, $doExpensiveQueries = true, $short = false ) { - wfProfileIn( __METHOD__ ); # Read has special handling if ( $action == 'read' ) { @@@ -2449,19 -2437,6 +2449,19 @@@ 'checkPermissionHooks', 'checkReadPermissions', ); + # Don't call checkSpecialsAndNSPermissions or checkCSSandJSPermissions + # here as it will lead to duplicate error messages. This is okay to do + # since anywhere that checks for create will also check for edit, and + # those checks are called for edit. + } elseif ( $action == 'create' ) { + $checks = array( + 'checkQuickPermissions', + 'checkPermissionHooks', + 'checkPageRestrictions', + 'checkCascadingSourcesRestrictions', + 'checkActionPermissions', + 'checkUserBlock' + ); } else { $checks = array( 'checkQuickPermissions', @@@ -2482,6 -2457,7 +2482,6 @@@ $errors = $this->$method( $action, $user, $errors, $doExpensiveQueries, $short ); } - wfProfileOut( __METHOD__ ); return $errors; } @@@ -2522,7 -2498,7 +2522,7 @@@ $types = array_diff( $types, array( 'upload' ) ); } - wfRunHooks( 'TitleGetRestrictionTypes', array( $this, &$types ) ); + Hooks::run( 'TitleGetRestrictionTypes', array( $this, &$types ) ); wfDebug( __METHOD__ . ': applicable restrictions to [[' . $this->getPrefixedText() . ']] are {' . implode( ',', $types ) . "}\n" ); @@@ -2537,7 -2513,7 +2537,7 @@@ * @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; @@@ -2552,27 -2528,13 +2552,27 @@@ $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; } @@@ -2717,6 -2679,8 +2717,6 @@@ return array( $this->mHasCascadingRestrictions, $pagerestrictions ); } - wfProfileIn( __METHOD__ ); - $dbr = wfGetDB( DB_SLAVE ); if ( $this->getNamespace() == NS_FILE ) { @@@ -2791,6 -2755,7 +2791,6 @@@ $this->mHasCascadingRestrictions = $sources; } - wfProfileOut( __METHOD__ ); return array( $sources, $pagerestrictions ); } @@@ -2809,10 -2774,8 +2809,10 @@@ * Accessor/initialisation for mRestrictions * * @param string $action Action that permission needs to be checked for - * @return array Restriction levels needed to take the action. All levels - * are required. + * @return array Restriction levels needed to take the action. All levels are + * required. Note that restriction levels are normally user rights, but 'sysop' + * and 'autoconfirmed' are also allowed for backwards compatibility. These should + * be mapped to 'editprotected' and 'editsemiprotected' respectively. */ public function getRestrictions( $action ) { if ( !$this->mRestrictionsLoaded ) { @@@ -2992,12 -2955,12 +2992,12 @@@ 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; @@@ -3295,7 -3258,6 +3295,7 @@@ $this->mEstimateRevisions = null; $this->mPageLanguage = false; $this->mDbPageLanguage = null; + $this->mIsBigDeletion = null; } /** @@@ -3338,8 -3300,8 +3338,8 @@@ // @note: splitTitleString() is a temporary hack to allow MediaWikiTitleCodec to share // the parsing code with Title, while avoiding massive refactoring. // @todo: get rid of secureAndSplit, refactor parsing code. - $parser = self::getTitleParser(); - $parts = $parser->splitTitleString( $dbkey, $this->getDefaultNamespace() ); + $titleParser = self::getTitleParser(); + $parts = $titleParser->splitTitleString( $dbkey, $this->getDefaultNamespace() ); } catch ( MalformedTitleException $ex ) { return false; } @@@ -3574,7 -3536,7 +3574,7 @@@ $urls[] = $this->getInternalUrl( 'action=raw&ctype=text/css' ); } - wfRunHooks( 'TitleSquidURLs', array( $this, &$urls ) ); + Hooks::run( 'TitleSquidURLs', array( $this, &$urls ) ); return $urls; } @@@ -3593,12 -3555,10 +3593,12 @@@ /** * 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 ); } @@@ -3606,33 -3566,116 +3606,33 @@@ * 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 */ @@@ -3641,6 -3684,27 +3641,6 @@@ $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' ); @@@ -3652,7 -3716,6 +3652,7 @@@ /** * 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 @@@ -3674,13 -3737,279 +3674,13 @@@ $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; - } - - $logEntry = new ManualLogEntry( 'move', $logType ); - $logEntry->setPerformer( $wgUser ); - $logEntry->setTarget( $this ); - $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 ); - - $oldid = $this->getArticleID(); - - $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__ ); + return $status->getErrorsArray(); } - - $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 ); } /** @@@ -3809,7 -4138,6 +3809,7 @@@ * 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 */ @@@ -4036,25 -4364,12 +4036,25 @@@ return false; } - $revCount = $this->estimateRevisionCount(); - return $revCount > $wgDeleteRevisionsLimit; + if ( $this->mIsBigDeletion === null ) { + $dbr = wfGetDB( DB_SLAVE ); + + $revCount = $dbr->selectRowCount( + 'revision', + '1', + array( 'rev_page' => $this->getArticleID() ), + __METHOD__, + array( 'LIMIT' => $wgDeleteRevisionsLimit + 1 ) + ); + + $this->mIsBigDeletion = $revCount > $wgDeleteRevisionsLimit; + } + + return $this->mIsBigDeletion; } /** - * Get the approximate revision count of this page. + * Get the approximate revision count of this page. * * @return int */ @@@ -4237,9 -4552,7 +4237,9 @@@ * @return bool */ public function exists() { - return $this->getArticleID() != 0; + $exists = $this->getArticleID() != 0; + Hooks::run( 'TitleExists', array( $this, &$exists ) ); + return $exists; } /** @@@ -4271,7 -4584,7 +4271,7 @@@ * @param Title $title * @param bool|null $isKnown */ - wfRunHooks( 'TitleIsAlwaysKnown', array( $this, &$isKnown ) ); + Hooks::run( 'TitleIsAlwaysKnown', array( $this, &$isKnown ) ); if ( !is_null( $isKnown ) ) { return $isKnown; @@@ -4594,7 -4907,7 +4594,7 @@@ // on the Title object passed in, and should probably // tell the users to run updateCollations.php --force // in order to re-sort existing category relations. - wfRunHooks( 'GetDefaultSortkey', array( $this, &$unprefixed ) ); + Hooks::run( 'GetDefaultSortkey', array( $this, &$unprefixed ) ); if ( $prefix !== '' ) { # Separate with a line feed, so the unprefixed part is only used as # a tiebreaker when two pages have the exact same prefix. @@@ -4616,13 -4929,16 +4616,13 @@@ */ public function getPageLanguage() { global $wgLang, $wgLanguageCode; - wfProfileIn( __METHOD__ ); if ( $this->isSpecialPage() ) { // special pages are in the user language - wfProfileOut( __METHOD__ ); return $wgLang; } // Checking if DB language is set if ( $this->mDbPageLanguage ) { - wfProfileOut( __METHOD__ ); return wfGetLangObj( $this->mDbPageLanguage ); } @@@ -4640,6 -4956,7 +4640,6 @@@ $langObj = wfGetLangObj( $this->mPageLanguage[0] ); } - wfProfileOut( __METHOD__ ); return $langObj; } @@@ -4690,7 -5007,9 +4690,9 @@@ $editnotice_ns = 'editnotice-' . $this->getNamespace(); $editnotice_ns_message = wfMessage( $editnotice_ns ); if ( $editnotice_ns_message->exists() ) { - $notices[$editnotice_ns] = $editnotice_ns_message->parseAsBlock(); + $notices[$editnotice_ns] = '
' . + $editnotice_ns_message->parseAsBlock() . '
'; } if ( MWNamespace::hasSubpages( $this->getNamespace() ) ) { $parts = explode( '/', $this->getDBkey() ); @@@ -4699,7 -5018,9 +4701,9 @@@ $editnotice_base .= '-' . array_shift( $parts ); $editnotice_base_msg = wfMessage( $editnotice_base ); if ( $editnotice_base_msg->exists() ) { - $notices[$editnotice_base] = $editnotice_base_msg->parseAsBlock(); + $notices[$editnotice_base] = '
' . + $editnotice_base_msg->parseAsBlock() . '
'; } } } else { @@@ -4707,11 -5028,13 +4711,13 @@@ $editnoticeText = $editnotice_ns . '-' . str_replace( '/', '-', $this->getDBkey() ); $editnoticeMsg = wfMessage( $editnoticeText ); if ( $editnoticeMsg->exists() ) { - $notices[$editnoticeText] = $editnoticeMsg->parseAsBlock(); + $notices[$editnoticeText] = '
' . + $editnoticeMsg->parseAsBlock() . '
'; } } - wfRunHooks( 'TitleGetEditNotices', array( $this, $oldid, &$notices ) ); + Hooks::run( 'TitleGetEditNotices', array( $this, $oldid, &$notices ) ); return $notices; } }