X-Git-Url: https://git.heureux-cyclage.org/?a=blobdiff_plain;f=includes%2FTitle.php;h=6ada9b309d0e9794b03b623e91868dcde5672fe5;hb=dedfe98eaad7015d52001988eeea9c00749e1a30;hp=8b4075b4de6816a6748fe826f11c181e7b0d1ede;hpb=6228415557b1206dce035c9a2751378d120e34e7;p=lhc%2Fweb%2Fwiklou.git diff --git a/includes/Title.php b/includes/Title.php index 8b4075b4de..6ada9b309d 100644 --- a/includes/Title.php +++ b/includes/Title.php @@ -36,7 +36,7 @@ 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; @@ -954,6 +954,7 @@ class Title implements LinkTarget { /** * Get the DB key with the initial letter case as specified by the user + * @deprecated since 1.33; please use Title::getDBKey() instead * * @return string DB key */ @@ -1608,11 +1609,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 ); } @@ -1661,7 +1666,7 @@ class Title implements LinkTarget { $p = $this->mInterwiki . ':'; } - if ( 0 != $this->mNamespace ) { + if ( $this->mNamespace != 0 ) { $nsText = $this->getNsText(); if ( $nsText === false ) { @@ -2690,14 +2695,39 @@ 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 nmae. + // 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. + 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() ); + $action = Action::factory( $action, $page, RequestContext::getMain() ); + } else { + $action = null; + } + + // If no action object is returned, assume that the action requires unblock + // which is the default. + if ( !$action || $action->requiresUnblock() ) { + if ( $user->isBlockedFrom( $this, $useReplica ) ) { + // @todo FIXME: Pass the relevant context into this function. + $errors[] = $block + ? $block->getPermissionsError( RequestContext::getMain() ) + : [ 'actionblockedtext' ]; + } } return $errors; @@ -2860,10 +2890,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; @@ -3277,6 +3309,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(); @@ -3347,35 +3382,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 { @@ -3573,7 +3623,7 @@ class Title implements LinkTarget { $this->mArticleID = $linkCache->addLinkObj( $this ); $linkCache->forUpdate( $oldUpdate ); } else { - if ( -1 == $this->mArticleID ) { + if ( $this->mArticleID == -1 ) { $this->mArticleID = $linkCache->addLinkObj( $this ); } } @@ -5220,10 +5270,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();