48 private $loadBalancer;
54 private $linksMigration;
57 private $commentStore;
60 private $hookContainer;
102 $this->options = $options;
103 $this->wanCache = $wanCache;
104 $this->loadBalancer = $loadBalancer;
105 $this->linkCache = $linkCache;
106 $this->linksMigration = $linksMigration;
107 $this->commentStore = $commentStore;
108 $this->hookContainer = $hookContainer;
109 $this->hookRunner =
new HookRunner( $hookContainer );
110 $this->pageStore = $pageStore;
128 return $restrictions[$action] ?? [];
141 if ( !$this->areRestrictionsLoaded( $page ) ) {
142 $this->loadRestrictions( $page );
144 return $this->cache[CacheKeyHelper::getKeyForPage( $page )][
'restrictions'] ?? [];
159 if ( !$this->areRestrictionsLoaded( $page ) ) {
160 $this->loadRestrictions( $page );
162 return $this->cache[CacheKeyHelper::getKeyForPage( $page )][
'expiry'][$action] ??
null;
182 $protection = $this->getCreateProtectionInternal( $page );
185 if ( $protection[
'permission'] ==
'sysop' ) {
186 $protection[
'permission'] =
'editprotected';
188 if ( $protection[
'permission'] ==
'autoconfirmed' ) {
189 $protection[
'permission'] =
'editsemiprotected';
204 $dbw = $this->loadBalancer->getConnectionRef(
DB_PRIMARY );
210 $this->cache[CacheKeyHelper::getKeyForPage( $page )][
'create_protection'] =
null;
224 $restrictions = $this->getRestrictions( $page, $action );
225 $semi = $this->options->get( MainConfigNames::SemiprotectedRestrictionLevels );
226 if ( !$restrictions || !$semi ) {
232 foreach ( array_keys( $semi,
'editsemiprotected' ) as $key ) {
233 $semi[$key] =
'autoconfirmed';
235 foreach ( array_keys( $restrictions,
'editsemiprotected' ) as $key ) {
236 $restrictions[$key] =
'autoconfirmed';
239 return !array_diff( $restrictions, $semi );
258 $applicableTypes = $this->listApplicableRestrictionTypes( $page );
260 if ( $action ===
'' ) {
261 foreach ( $applicableTypes as
$type ) {
262 if ( $this->isProtected( $page,
$type ) ) {
269 if ( !in_array( $action, $applicableTypes ) ) {
273 return (
bool)array_diff(
275 $this->getRestrictions( $page, $action ),
276 $this->options->get( MainConfigNames::RestrictionLevels )
291 return $this->getCascadeProtectionSourcesInternal( $page,
true );
307 $types = $this->listAllRestrictionTypes( $page->
exists() );
311 $types = array_values( array_diff( $types, [
'upload' ] ) );
314 if ( $this->hookContainer->isRegistered(
'TitleGetRestrictionTypes' ) ) {
315 $this->hookRunner->onTitleGetRestrictionTypes(
317 Title::castFromPageIdentity( $page ), $types );
334 return array_values( array_diff( $types, [
'create' ] ) );
338 return array_values( array_intersect( $types, [
'create',
'upload' ] ) );
350 PageIdentity $page,
int $flags = IDBAccessObject::READ_NORMAL
358 $readLatest = DBAccessObjectUtils::hasFlags( $flags, IDBAccessObject::READ_LATEST );
360 if ( $this->areRestrictionsLoaded( $page ) && !$readLatest ) {
364 $cacheEntry = &$this->cache[CacheKeyHelper::getKeyForPage( $page )];
366 $cacheEntry[
'restrictions'] = [];
370 $page = $this->pageStore->getPageByReference( $page, $flags ) ?? $page;
372 $id = $page->
getId();
375 $loadRestrictionsFromDb =
static function ( IDatabase
$dbr ) use ( $fname, $id ) {
376 return iterator_to_array(
379 [
'pr_type',
'pr_expiry',
'pr_level',
'pr_cascade' ],
380 [
'pr_page' => $id ],
388 $rows = $loadRestrictionsFromDb(
$dbr );
390 $this->linkCache->addLinkObj( $page );
391 $latestRev = $this->linkCache->getGoodLinkFieldObj( $page,
'revision' );
398 $rows = $this->wanCache->getWithSetCallback(
400 $this->wanCache->makeKey(
'page-restrictions',
'v1', $id, $latestRev ),
401 $this->wanCache::TTL_DAY,
402 function ( $curValue, &$ttl, array &$setOpts ) use ( $loadRestrictionsFromDb ) {
404 $setOpts += Database::getCacheSetOptions(
$dbr );
405 if ( $this->loadBalancer->hasOrMadeRecentPrimaryChanges() ) {
407 $ttl = WANObjectCache::TTL_UNCACHEABLE;
410 return $loadRestrictionsFromDb(
$dbr );
416 $this->loadRestrictionsFromRows( $page, $rows );
418 $titleProtection = $this->getCreateProtectionInternal( $page );
420 if ( $titleProtection ) {
422 $expiry = $titleProtection[
'expiry'];
424 if ( !$expiry || $expiry > $now ) {
426 $cacheEntry[
'expiry'][
'create'] = $expiry ?:
null;
427 $cacheEntry[
'restrictions'][
'create'] =
428 explode(
',', trim( $titleProtection[
'permission'] ) );
431 $cacheEntry[
'create_protection'] =
null;
434 $cacheEntry[
'expiry'][
'create'] =
'infinity';
451 $cacheEntry = &$this->cache[CacheKeyHelper::getKeyForPage( $page )];
453 $restrictionTypes = $this->listApplicableRestrictionTypes( $page );
455 foreach ( $restrictionTypes as
$type ) {
456 $cacheEntry[
'restrictions'][
$type] = [];
457 $cacheEntry[
'expiry'][
$type] =
'infinity';
460 $cacheEntry[
'cascade'] =
false;
470 foreach ( $rows as $row ) {
472 if ( !in_array( $row->pr_type, $restrictionTypes ) ) {
477 $expiry =
$dbr->decodeExpiry( $row->pr_expiry );
482 if ( !$expiry || $expiry > $now ) {
483 $cacheEntry[
'expiry'][$row->pr_type] = $expiry ?:
null;
484 $cacheEntry[
'restrictions'][$row->pr_type]
485 = explode(
',', trim( $row->pr_level ) );
486 if ( $row->pr_cascade ) {
487 $cacheEntry[
'cascade'] =
true;
503 private function getCreateProtectionInternal( PageIdentity $page ): ?array {
505 if ( !$page->canExist() ) {
514 $cacheEntry = &$this->cache[CacheKeyHelper::getKeyForPage( $page )];
516 if ( !$cacheEntry || !array_key_exists(
'create_protection', $cacheEntry ) ) {
518 $commentQuery = $this->commentStore->getJoin(
'pt_reason' );
519 $row =
$dbr->selectRow(
520 [
'protected_titles' ] + $commentQuery[
'tables'],
521 [
'pt_user',
'pt_expiry',
'pt_create_perm' ] + $commentQuery[
'fields'],
525 $commentQuery[
'joins']
529 $cacheEntry[
'create_protection'] = [
530 'user' => $row->pt_user,
531 'expiry' =>
$dbr->decodeExpiry( $row->pt_expiry ),
532 'permission' => $row->pt_create_perm,
533 'reason' => $this->commentStore->getComment(
'pt_reason', $row )->text,
536 $cacheEntry[
'create_protection'] =
null;
541 return $cacheEntry[
'create_protection'];
557 return $this->getCascadeProtectionSourcesInternal( $page,
false );
568 private function getCascadeProtectionSourcesInternal(
571 $cacheEntry = &$this->cache[CacheKeyHelper::getKeyForPage( $page )];
573 if ( !$shortCircuit && isset( $cacheEntry[
'cascade_sources'] ) ) {
574 return $cacheEntry[
'cascade_sources'];
575 } elseif ( $shortCircuit && isset( $cacheEntry[
'has_cascading'] ) ) {
576 return $cacheEntry[
'has_cascading'];
582 $tables = [
'imagelinks',
'page_restrictions' ];
589 $tables = [
'templatelinks',
'page_restrictions' ];
590 $where_clauses = $this->linksMigration->getLinksConditions(
592 TitleValue::newFromPage( $page )
594 $where_clauses[] =
'tl_from=pr_page';
595 $where_clauses[
'pr_cascade'] = 1;
598 if ( $shortCircuit ) {
599 $cols = [
'pr_expiry' ];
601 $cols = [
'pr_page',
'page_namespace',
'page_title',
602 'pr_expiry',
'pr_type',
'pr_level' ];
603 $where_clauses[] =
'page_id=pr_page';
608 $res =
$dbr->select( $tables, $cols, $where_clauses, __METHOD__ );
611 $pageRestrictions = [];
614 foreach (
$res as $row ) {
615 $expiry =
$dbr->decodeExpiry( $row->pr_expiry );
616 if ( $expiry > $now ) {
617 if ( $shortCircuit ) {
618 $cacheEntry[
'has_cascading'] =
true;
622 $sources[$row->pr_page] =
new PageIdentityValue( $row->pr_page,
623 $row->page_namespace, $row->page_title, PageIdentity::LOCAL );
627 if ( !isset( $pageRestrictions[$row->pr_type] ) ) {
628 $pageRestrictions[$row->pr_type] = [];
631 if ( !in_array( $row->pr_level, $pageRestrictions[$row->pr_type] ) ) {
632 $pageRestrictions[$row->pr_type][] = $row->pr_level;
637 $cacheEntry[
'has_cascading'] = (bool)$sources;
639 if ( $shortCircuit ) {
643 $cacheEntry[
'cascade_sources'] = [ $sources, $pageRestrictions ];
644 return [ $sources, $pageRestrictions ];
655 return isset( $this->cache[CacheKeyHelper::getKeyForPage( $page )][
'restrictions'] );
667 return isset( $this->cache[CacheKeyHelper::getKeyForPage( $page )][
'cascade_sources'] );
679 if ( !$this->areRestrictionsLoaded( $page ) ) {
680 $this->loadRestrictions( $page );
682 return $this->cache[CacheKeyHelper::getKeyForPage( $page )][
'cascade'] ??
false;
695 unset( $this->cache[CacheKeyHelper::getKeyForPage( $page )] );
wfTimestampNow()
Convenience function; returns MediaWiki timestamp for the present time.
if(!defined('MW_SETUP_CALLBACK'))
The persistent session ID (if any) loaded at startup.
Helper class for DAO classes.
Cache for article titles (prefixed DB keys) and ids linked from one source.
A class containing constants representing the names of configuration variables.
const NamespaceProtection
Name constant for the NamespaceProtection setting, for use with Config::get()
const RestrictionTypes
Name constant for the RestrictionTypes setting, for use with Config::get()
const SemiprotectedRestrictionLevels
Name constant for the SemiprotectedRestrictionLevels setting, for use with Config::get()
const RestrictionLevels
Name constant for the RestrictionLevels setting, for use with Config::get()
Immutable value object representing a page identity.
Represents a page (or page fragment) title within MediaWiki.
Represents a title within MediaWiki.
Multi-datacenter aware caching interface.
Interface for database access objects.
Interface for objects (potentially) representing an editable wiki page.
getId( $wikiId=self::LOCAL)
Returns the page ID.
canExist()
Checks whether this PageIdentity represents a "proper" page, meaning that it could exist as an editab...
exists()
Checks if the page currently exists.