X-Git-Url: http://git.cyclocoop.org/%28?a=blobdiff_plain;f=includes%2FTitle.php;h=849707e38b91bb11191bddc4941d9843d41a1994;hb=0a08a3144b46d6f1217eb792bd7b464711c71a22;hp=55c5cd90f1bd194d3c369adba3db98e7d59899e8;hpb=4b837b5bb061b6b50dff39184fb7f1d278242cf8;p=lhc%2Fweb%2Fwiklou.git diff --git a/includes/Title.php b/includes/Title.php index 55c5cd90f1..849707e38b 100644 --- a/includes/Title.php +++ b/includes/Title.php @@ -36,9 +36,9 @@ use MediaWiki\MediaWikiServices; * @note Consider using a TitleValue object instead. TitleValue is more lightweight * and does not rely on global state or the database. */ -class Title implements LinkTarget { +class Title implements LinkTarget, IDBAccessObject { /** @var MapCacheLRU */ - static private $titleCache = null; + private static $titleCache = null; /** * Title::newFromText maintains a cache to avoid expensive re-normalization of @@ -205,7 +205,7 @@ class Title implements LinkTarget { } /** - * @access protected + * @protected */ function __construct() { } @@ -767,23 +767,6 @@ class Title implements LinkTarget { return $name; } - /** - * Escape a text fragment, say from a link, for a URL - * - * @deprecated since 1.30, use Sanitizer::escapeIdForLink() or escapeIdForExternalInterwiki() - * - * @param string $fragment Containing a URL or link fragment (after the "#") - * @return string Escaped string - */ - static function escapeFragmentForURL( $fragment ) { - wfDeprecated( __METHOD__, '1.30' ); - # Note that we don't urlencode the fragment. urlencoded Unicode - # fragments appear not to work in IE (at least up to 7) or in at least - # one version of Opera 9.x. The W3C validator, for one, doesn't seem - # to care if they aren't encoded. - return Sanitizer::escapeId( $fragment, 'noninitial' ); - } - /** * Callback for usort() to do title sorts by (namespace, title) * @@ -1071,17 +1054,6 @@ class Title implements LinkTarget { getNsText( MWNamespace::getTalk( $this->mNamespace ) ); } - /** - * Can this title have a corresponding talk page? - * - * @deprecated since 1.30, use canHaveTalkPage() instead. - * - * @return bool True if this title either is a talk page or can have a talk page associated. - */ - public function canTalk() { - return $this->canHaveTalkPage(); - } - /** * Can this title have a corresponding talk page? * @@ -1308,17 +1280,6 @@ class Title implements LinkTarget { ); } - /** - * @return bool - * @deprecated Since 1.31; use ::isSiteConfigPage() instead (which also checks for JSON pages) - */ - public function isCssOrJsPage() { - wfDeprecated( __METHOD__, '1.31' ); - return ( NS_MEDIAWIKI == $this->mNamespace - && ( $this->hasContentModel( CONTENT_MODEL_CSS ) - || $this->hasContentModel( CONTENT_MODEL_JAVASCRIPT ) ) ); - } - /** * Is this a "config" (.css, .json, or .js) sub-page of a user page? * @@ -1333,17 +1294,6 @@ class Title implements LinkTarget { ); } - /** - * @return bool - * @deprecated Since 1.31; use ::isUserConfigPage() instead (which also checks for JSON pages) - */ - public function isCssJsSubpage() { - wfDeprecated( __METHOD__, '1.31' ); - return ( NS_USER == $this->mNamespace && $this->isSubpage() - && ( $this->hasContentModel( CONTENT_MODEL_CSS ) - || $this->hasContentModel( CONTENT_MODEL_JAVASCRIPT ) ) ); - } - /** * Trim down a .css, .json, or .js subpage title to get the corresponding skin name * @@ -1360,15 +1310,6 @@ class Title implements LinkTarget { return substr( $subpage, 0, $lastdot ); } - /** - * @deprecated Since 1.31; use ::getSkinFromConfigSubpage() instead - * @return string Containing skin name from .css, .json, or .js subpage title - */ - public function getSkinFromCssJsSubpage() { - wfDeprecated( __METHOD__, '1.31' ); - return $this->getSkinFromConfigSubpage(); - } - /** * Is this a CSS "config" sub-page of a user page? * @@ -1383,15 +1324,6 @@ class Title implements LinkTarget { ); } - /** - * @deprecated Since 1.31; use ::isUserCssConfigPage() - * @return bool - */ - public function isCssSubpage() { - wfDeprecated( __METHOD__, '1.31' ); - return $this->isUserCssConfigPage(); - } - /** * Is this a JSON "config" sub-page of a user page? * @@ -1420,15 +1352,6 @@ class Title implements LinkTarget { ); } - /** - * @deprecated Since 1.31; use ::isUserJsConfigPage() - * @return bool - */ - public function isJsSubpage() { - wfDeprecated( __METHOD__, '1.31' ); - return $this->isUserJsConfigPage(); - } - /** * Is this a sitewide CSS "config" page? * @@ -1609,11 +1532,15 @@ class Title implements LinkTarget { public function getFragmentForURL() { if ( !$this->hasFragment() ) { return ''; - } elseif ( $this->isExternal() - && !self::getInterwikiLookup()->fetch( $this->mInterwiki )->isLocal() - ) { - return '#' . Sanitizer::escapeIdForExternalInterwiki( $this->mFragment ); + } elseif ( $this->isExternal() ) { + // Note: If the interwiki is unknown, it's treated as a namespace on the local wiki, + // so we treat it like a local interwiki. + $interwiki = self::getInterwikiLookup()->fetch( $this->mInterwiki ); + if ( $interwiki && !$interwiki->isLocal() ) { + return '#' . Sanitizer::escapeIdForExternalInterwiki( $this->mFragment ); + } } + return '#' . Sanitizer::escapeIdForLink( $this->mFragment ); } @@ -1831,7 +1758,7 @@ class Title implements LinkTarget { * @endcode * * @param string $text The subpage name to add to the title - * @return Title Subpage title + * @return Title|null Subpage title, or null on an error * @since 1.20 */ public function getSubpage( $text ) { @@ -2691,14 +2618,42 @@ class Title implements LinkTarget { } $useReplica = ( $rigor !== 'secure' ); - if ( ( $action == 'edit' || $action == 'create' ) - && !$user->isBlockedFrom( $this, $useReplica ) - ) { - // Don't block the user from editing their own talk page unless they've been - // explicitly blocked from that too. - } elseif ( $user->isBlocked() && $user->getBlock()->prevents( $action ) !== false ) { + $block = $user->getBlock( $useReplica ); + + // The block may explicitly allow an action (like "read" or "upload"). + if ( $block && $block->prevents( $action ) === false ) { + return $errors; + } + + // Determine if the user is blocked from this action on this page. + // What gets passed into this method is a user right, not an action name. + // There is no way to instantiate an action by restriction. However, this + // will get the action where the restriction is the same. This may result + // in actions being blocked that shouldn't be. + $actionObj = null; + if ( Action::exists( $action ) ) { + // Clone the title to prevent mutations to this object which is done + // by Title::loadFromRow() in WikiPage::loadFromRow(). + $page = WikiPage::factory( clone $this ); + // Creating an action will perform several database queries to ensure that + // the action has not been overridden by the content type. // @todo FIXME: Pass the relevant context into this function. - $errors[] = $user->getBlock()->getPermissionsError( RequestContext::getMain() ); + $actionObj = Action::factory( $action, $page, RequestContext::getMain() ); + // Ensure that the retrieved action matches the restriction. + if ( $actionObj && $actionObj->getRestriction() !== $action ) { + $actionObj = null; + } + } + + // If no action object is returned, assume that the action requires unblock + // which is the default. + if ( !$actionObj || $actionObj->requiresUnblock() ) { + if ( $user->isBlockedFrom( $this, $useReplica ) ) { + // @todo FIXME: Pass the relevant context into this function. + $errors[] = $block + ? $block->getPermissionsError( RequestContext::getMain() ) + : [ 'actionblockedtext' ]; + } } return $errors; @@ -2861,10 +2816,12 @@ class Title implements LinkTarget { } $errors = []; - while ( count( $checks ) > 0 && - !( $short && count( $errors ) > 0 ) ) { - $method = array_shift( $checks ); + foreach ( $checks as $method ) { $errors = $this->$method( $action, $user, $errors, $rigor, $short ); + + if ( $short && $errors !== [] ) { + break; + } } return $errors; @@ -3278,6 +3235,9 @@ class Title implements LinkTarget { * Example: "edit=autoconfirmed,sysop:move=sysop" */ public function loadRestrictionsFromRows( $rows, $oldFashionedRestrictions = null ) { + // This function will only read rows from a table that we migrated away + // from before adding READ_LATEST support to loadRestrictions, so we + // don't need to support reading from DB_MASTER here. $dbr = wfGetDB( DB_REPLICA ); $restrictionTypes = $this->getRestrictionTypes(); @@ -3348,35 +3308,50 @@ class Title implements LinkTarget { * indicating who can move or edit the page from the page table, (pre 1.10) rows. * Edit and move sections are separated by a colon * Example: "edit=autoconfirmed,sysop:move=sysop" + * @param int $flags A bit field. If self::READ_LATEST is set, skip replicas and read + * from the master DB. */ - public function loadRestrictions( $oldFashionedRestrictions = null ) { - if ( $this->mRestrictionsLoaded ) { + public function loadRestrictions( $oldFashionedRestrictions = null, $flags = 0 ) { + $readLatest = DBAccessObjectUtils::hasFlags( $flags, self::READ_LATEST ); + if ( $this->mRestrictionsLoaded && !$readLatest ) { return; } + // TODO: should probably pass $flags into getArticleID, but it seems hacky + // to mix READ_LATEST and GAID_FOR_UPDATE, even if they have the same value. + // Maybe deprecate GAID_FOR_UPDATE now that we implement IDBAccessObject? $id = $this->getArticleID(); if ( $id ) { - $cache = ObjectCache::getMainWANInstance(); $fname = __METHOD__; - $rows = $cache->getWithSetCallback( - // Page protections always leave a new null revision - $cache->makeKey( 'page-restrictions', $id, $this->getLatestRevID() ), - $cache::TTL_DAY, - function ( $curValue, &$ttl, array &$setOpts ) use ( $fname ) { - $dbr = wfGetDB( DB_REPLICA ); - - $setOpts += Database::getCacheSetOptions( $dbr ); - - return iterator_to_array( - $dbr->select( - 'page_restrictions', - [ 'pr_type', 'pr_expiry', 'pr_level', 'pr_cascade' ], - [ 'pr_page' => $this->getArticleID() ], - $fname - ) - ); - } - ); + $loadRestrictionsFromDb = function ( Database $dbr ) use ( $fname, $id ) { + return iterator_to_array( + $dbr->select( + 'page_restrictions', + [ 'pr_type', 'pr_expiry', 'pr_level', 'pr_cascade' ], + [ 'pr_page' => $id ], + $fname + ) + ); + }; + + if ( $readLatest ) { + $dbr = wfGetDB( DB_MASTER ); + $rows = $loadRestrictionsFromDb( $dbr ); + } else { + $cache = ObjectCache::getMainWANInstance(); + $rows = $cache->getWithSetCallback( + // Page protections always leave a new null revision + $cache->makeKey( 'page-restrictions', $id, $this->getLatestRevID() ), + $cache::TTL_DAY, + function ( $curValue, &$ttl, array &$setOpts ) use ( $loadRestrictionsFromDb ) { + $dbr = wfGetDB( DB_REPLICA ); + + $setOpts += Database::getCacheSetOptions( $dbr ); + + return $loadRestrictionsFromDb( $dbr ); + } + ); + } $this->loadRestrictionsFromRows( $rows, $oldFashionedRestrictions ); } else { @@ -3968,13 +3943,6 @@ class Title implements LinkTarget { return $urls; } - /** - * @deprecated since 1.27 use getCdnUrls() - */ - public function getSquidURLs() { - return $this->getCdnUrls(); - } - /** * Purge all applicable CDN URLs */ @@ -5221,10 +5189,9 @@ class Title implements LinkTarget { if ( MWNamespace::hasSubpages( $this->mNamespace ) ) { // Optional notice for page itself and any parent page - $parts = explode( '/', $this->mDbkeyform ); $editnotice_base = $editnotice_ns; - while ( count( $parts ) > 0 ) { - $editnotice_base .= '-' . array_shift( $parts ); + foreach ( explode( '/', $this->mDbkeyform ) as $part ) { + $editnotice_base .= '-' . $part; $msg = wfMessage( $editnotice_base ); if ( $msg->exists() ) { $html = $msg->parseAsBlock();