MediaWiki master
RestrictionStore.php
Go to the documentation of this file.
1<?php
2
4
21use stdClass;
27
32
34 public const CONSTRUCTOR_OPTIONS = [
40 ];
41
42 private ServiceOptions $options;
43 private WANObjectCache $wanCache;
44 private LBFactory $loadBalancerFactory;
45 private LinkCache $linkCache;
46 private LinksMigration $linksMigration;
47 private CommentStore $commentStore;
48 private HookContainer $hookContainer;
49 private HookRunner $hookRunner;
50 private PageStore $pageStore;
51
62 private $cache = [];
63
64 public function __construct(
65 ServiceOptions $options,
66 WANObjectCache $wanCache,
67 LBFactory $loadBalancerFactory,
68 LinkCache $linkCache,
69 LinksMigration $linksMigration,
70 CommentStore $commentStore,
71 HookContainer $hookContainer,
72 PageStore $pageStore
73 ) {
74 $options->assertRequiredOptions( self::CONSTRUCTOR_OPTIONS );
75 $this->options = $options;
76 $this->wanCache = $wanCache;
77 $this->loadBalancerFactory = $loadBalancerFactory;
78 $this->linkCache = $linkCache;
79 $this->linksMigration = $linksMigration;
80 $this->commentStore = $commentStore;
81 $this->hookContainer = $hookContainer;
82 $this->hookRunner = new HookRunner( $hookContainer );
83 $this->pageStore = $pageStore;
84 }
85
97 public function getRestrictions( PageIdentity $page, string $action ): array {
98 $page->assertWiki( PageIdentity::LOCAL );
99
100 // Optimization: Avoid repeatedly fetching page restrictions (from cache or DB)
101 // for repeated PermissionManager::userCan calls, if this action cannot be restricted
102 // in the first place. This is primarily to improve batch rendering on RecentChanges,
103 // where as of writing this will save 0.5s on a 8.0s response. (T341319)
104 $restrictionTypes = $this->listApplicableRestrictionTypes( $page );
105 if ( !in_array( $action, $restrictionTypes ) ) {
106 return [];
107 }
108
109 $restrictions = $this->getAllRestrictions( $page );
110 return $restrictions[$action] ?? [];
111 }
112
120 public function getAllRestrictions( PageIdentity $page ): array {
121 $page->assertWiki( PageIdentity::LOCAL );
122
123 if ( !$this->areRestrictionsLoaded( $page ) ) {
124 $this->loadRestrictions( $page );
125 }
126 return $this->cache[CacheKeyHelper::getKeyForPage( $page )]['restrictions'] ?? [];
127 }
128
137 public function getRestrictionExpiry( PageIdentity $page, string $action ): ?string {
138 $page->assertWiki( PageIdentity::LOCAL );
139
140 if ( !$this->areRestrictionsLoaded( $page ) ) {
141 $this->loadRestrictions( $page );
142 }
143 return $this->cache[CacheKeyHelper::getKeyForPage( $page )]['expiry'][$action] ?? null;
144 }
145
156 public function getCreateProtection( PageIdentity $page ): ?array {
157 $page->assertWiki( PageIdentity::LOCAL );
158
159 $protection = $this->getCreateProtectionInternal( $page );
160 // TODO: the remapping below probably need to be migrated into other method one day
161 if ( $protection ) {
162 if ( $protection['permission'] == 'sysop' ) {
163 $protection['permission'] = 'editprotected'; // B/C
164 }
165 if ( $protection['permission'] == 'autoconfirmed' ) {
166 $protection['permission'] = 'editsemiprotected'; // B/C
167 }
168 }
169 return $protection;
170 }
171
178 public function deleteCreateProtection( PageIdentity $page ): void {
179 $page->assertWiki( PageIdentity::LOCAL );
180
181 $dbw = $this->loadBalancerFactory->getPrimaryDatabase();
182 $dbw->newDeleteQueryBuilder()
183 ->deleteFrom( 'protected_titles' )
184 ->where( [ 'pt_namespace' => $page->getNamespace(), 'pt_title' => $page->getDBkey() ] )
185 ->caller( __METHOD__ )->execute();
186 $this->cache[CacheKeyHelper::getKeyForPage( $page )]['create_protection'] = null;
187 }
188
197 public function isSemiProtected( PageIdentity $page, string $action = 'edit' ): bool {
198 $page->assertWiki( PageIdentity::LOCAL );
199
200 $restrictions = $this->getRestrictions( $page, $action );
201 $semi = $this->options->get( MainConfigNames::SemiprotectedRestrictionLevels );
202 if ( !$restrictions || !$semi ) {
203 // Not protected, or all protection is full protection
204 return false;
205 }
206
207 // Remap autoconfirmed to editsemiprotected for BC
208 foreach ( array_keys( $semi, 'editsemiprotected' ) as $key ) {
209 $semi[$key] = 'autoconfirmed';
210 }
211 foreach ( array_keys( $restrictions, 'editsemiprotected' ) as $key ) {
212 $restrictions[$key] = 'autoconfirmed';
213 }
214
215 return !array_diff( $restrictions, $semi );
216 }
217
225 public function isProtected( PageIdentity $page, string $action = '' ): bool {
226 $page->assertWiki( PageIdentity::LOCAL );
227
228 // Special pages have inherent protection (TODO: remove after switch to ProperPageIdentity)
229 if ( $page->getNamespace() === NS_SPECIAL ) {
230 return true;
231 }
232
233 // Check regular protection levels
234 $applicableTypes = $this->listApplicableRestrictionTypes( $page );
235
236 if ( $action === '' ) {
237 foreach ( $applicableTypes as $type ) {
238 if ( $this->isProtected( $page, $type ) ) {
239 return true;
240 }
241 }
242 return false;
243 }
244
245 if ( !in_array( $action, $applicableTypes ) ) {
246 return false;
247 }
248
249 return (bool)array_diff(
250 array_intersect(
251 $this->getRestrictions( $page, $action ),
252 $this->options->get( MainConfigNames::RestrictionLevels )
253 ),
254 [ '' ]
255 );
256 }
257
264 public function isCascadeProtected( PageIdentity $page ): bool {
265 $page->assertWiki( PageIdentity::LOCAL );
266
267 return $this->shouldUseVirtualDomains()
268 ? $this->getCascadeProtectionSourcesInternal( $page )[0] !== []
269 : $this->getCascadeProtectionSourcesInternalJoined( $page )[0] !== [];
270 }
271
278 public function listApplicableRestrictionTypes( PageIdentity $page ): array {
279 $page->assertWiki( PageIdentity::LOCAL );
280
281 if ( !$page->canExist() ) {
282 return [];
283 }
284
285 $types = $this->listAllRestrictionTypes( $page->exists() );
286
287 if ( $page->getNamespace() !== NS_FILE ) {
288 // Remove the upload restriction for non-file titles
289 $types = array_values( array_diff( $types, [ 'upload' ] ) );
290 }
291
292 if ( $this->hookContainer->isRegistered( 'TitleGetRestrictionTypes' ) ) {
293 $this->hookRunner->onTitleGetRestrictionTypes(
294 Title::newFromPageIdentity( $page ), $types );
295 }
296
297 return $types;
298 }
299
307 public function listAllRestrictionTypes( bool $exists = true ): array {
308 $types = $this->options->get( MainConfigNames::RestrictionTypes );
309 if ( $exists ) {
310 // Remove the create restriction for existing titles
311 return array_values( array_diff( $types, [ 'create' ] ) );
312 }
313
314 // Only the create restrictions apply to non-existing titles
315 return array_values( array_intersect( $types, [ 'create' ] ) );
316 }
317
326 public function loadRestrictions(
327 PageIdentity $page, int $flags = IDBAccessObject::READ_NORMAL
328 ): void {
329 $page->assertWiki( PageIdentity::LOCAL );
330
331 if ( !$page->canExist() ) {
332 return;
333 }
334
335 $readLatest = DBAccessObjectUtils::hasFlags( $flags, IDBAccessObject::READ_LATEST );
336
337 if ( $this->areRestrictionsLoaded( $page ) && !$readLatest ) {
338 return;
339 }
340
341 $cacheEntry = &$this->cache[CacheKeyHelper::getKeyForPage( $page )];
342
343 $cacheEntry['restrictions'] = [];
344
345 // XXX Work around https://phabricator.wikimedia.org/T287575
346 if ( $readLatest ) {
347 $page = $this->pageStore->getPageByReference( $page, $flags ) ?? $page;
348 }
349 $id = $page->getId();
350 if ( $id ) {
351 $fname = __METHOD__;
352 $loadRestrictionsFromDb = static function ( IReadableDatabase $dbr ) use ( $fname, $id ) {
353 return iterator_to_array(
354 $dbr->newSelectQueryBuilder()
355 ->select( [ 'pr_type', 'pr_expiry', 'pr_level', 'pr_cascade' ] )
356 ->from( 'page_restrictions' )
357 ->where( [ 'pr_page' => $id ] )
358 ->caller( $fname )->fetchResultSet()
359 );
360 };
361
362 if ( $readLatest ) {
363 $dbr = $this->loadBalancerFactory->getPrimaryDatabase();
364 $rows = $loadRestrictionsFromDb( $dbr );
365 } else {
366 $this->pageStore->getPageForLink( TitleValue::newFromPage( $page ) )->getId();
367 $latestRev = $this->linkCache->getGoodLinkFieldObj( $page, 'revision' );
368 if ( !$latestRev ) {
369 // This method can get called in the middle of page creation
370 // (WikiPage::doUserEditContent) where a page might have an
371 // id but no revisions, while checking the "autopatrol" permission.
372 $rows = [];
373 } else {
374 $rows = $this->wanCache->getWithSetCallback(
375 // Page protections always leave a new dummy revision
376 $this->wanCache->makeKey( 'page-restrictions', 'v1', $id, $latestRev ),
377 $this->wanCache::TTL_DAY,
378 function ( $curValue, &$ttl ) use ( $loadRestrictionsFromDb ) {
379 $dbr = $this->loadBalancerFactory->getReplicaDatabase();
380 if ( $this->loadBalancerFactory->hasOrMadeRecentPrimaryChanges() ) {
381 // TODO: cleanup Title cache and caller assumption mess in general
382 $ttl = WANObjectCache::TTL_UNCACHEABLE;
383 }
384
385 return $loadRestrictionsFromDb( $dbr );
386 }
387 );
388 }
389 }
390
391 $this->loadRestrictionsFromRows( $page, $rows );
392 } else {
393 $titleProtection = $this->getCreateProtectionInternal( $page );
394
395 if ( $titleProtection ) {
396 $now = wfTimestampNow();
397 $expiry = $titleProtection['expiry'];
398
399 if ( !$expiry || $expiry > $now ) {
400 // Apply the restrictions
401 $cacheEntry['expiry']['create'] = $expiry ?: null;
402 $cacheEntry['restrictions']['create'] =
403 explode( ',', trim( $titleProtection['permission'] ) );
404 } else {
405 // Get rid of the old restrictions
406 $cacheEntry['create_protection'] = null;
407 }
408 } else {
409 $cacheEntry['expiry']['create'] = 'infinity';
410 }
411 }
412 }
413
422 PageIdentity $page, array $rows
423 ): void {
424 $page->assertWiki( PageIdentity::LOCAL );
425
426 $cacheEntry = &$this->cache[CacheKeyHelper::getKeyForPage( $page )];
427
428 $restrictionTypes = $this->listApplicableRestrictionTypes( $page );
429
430 foreach ( $restrictionTypes as $type ) {
431 $cacheEntry['restrictions'][$type] = [];
432 $cacheEntry['expiry'][$type] = 'infinity';
433 }
434
435 $cacheEntry['cascade'] = false;
436
437 if ( !$rows ) {
438 return;
439 }
440
441 // New restriction format -- load second to make them override old-style restrictions.
442 $now = wfTimestampNow();
443
444 // Cycle through all the restrictions.
445 foreach ( $rows as $row ) {
446 // Don't take care of restrictions types that aren't allowed
447 if ( !in_array( $row->pr_type, $restrictionTypes ) ) {
448 continue;
449 }
450
451 $dbr = $this->loadBalancerFactory->getReplicaDatabase();
452 $expiry = $dbr->decodeExpiry( $row->pr_expiry );
453
454 // Only apply the restrictions if they haven't expired!
455 // XXX Why would !$expiry ever be true? It should always be either 'infinity' or a
456 // string consisting of 14 digits. Likewise for the ?: below.
457 if ( !$expiry || $expiry > $now ) {
458 $cacheEntry['expiry'][$row->pr_type] = $expiry ?: null;
459 $cacheEntry['restrictions'][$row->pr_type]
460 = explode( ',', trim( $row->pr_level ) );
461 if ( $row->pr_cascade ) {
462 $cacheEntry['cascade'] = true;
463 }
464 }
465 }
466 }
467
478 private function getCreateProtectionInternal( PageIdentity $page ): ?array {
479 // Can't protect pages in special namespaces
480 if ( !$page->canExist() ) {
481 return null;
482 }
483
484 // Can't apply this type of protection to pages that exist.
485 if ( $page->exists() ) {
486 return null;
487 }
488
489 $cacheEntry = &$this->cache[CacheKeyHelper::getKeyForPage( $page )];
490
491 if ( !$cacheEntry || !array_key_exists( 'create_protection', $cacheEntry ) ) {
492 $dbr = $this->loadBalancerFactory->getReplicaDatabase();
493 $commentQuery = $this->commentStore->getJoin( 'pt_reason' );
494 $row = $dbr->newSelectQueryBuilder()
495 ->select( [ 'pt_user', 'pt_expiry', 'pt_create_perm' ] )
496 ->from( 'protected_titles' )
497 ->where( [ 'pt_namespace' => $page->getNamespace(), 'pt_title' => $page->getDBkey() ] )
498 ->queryInfo( $commentQuery )
499 ->caller( __METHOD__ )
500 ->fetchRow();
501
502 if ( $row ) {
503 $cacheEntry['create_protection'] = [
504 'user' => $row->pt_user,
505 'expiry' => $dbr->decodeExpiry( $row->pt_expiry ),
506 'permission' => $row->pt_create_perm,
507 'reason' => $this->commentStore->getComment( 'pt_reason', $row )->text,
508 ];
509 } else {
510 $cacheEntry['create_protection'] = null;
511 }
512
513 }
514
515 return $cacheEntry['create_protection'];
516 }
517
530 public function getCascadeProtectionSources( PageIdentity $page ): array {
531 $page->assertWiki( PageIdentity::LOCAL );
532
533 return $this->shouldUseVirtualDomains()
534 ? $this->getCascadeProtectionSourcesInternal( $page )
535 : $this->getCascadeProtectionSourcesInternalJoined( $page );
536 }
537
549 private function getCascadeProtectionSourcesInternal(
550 PageIdentity $page
551 ): array {
552 if ( !$page->canExist() ) {
553 return [ [], [], [], [] ];
554 }
555
556 $cacheEntry = &$this->cache[CacheKeyHelper::getKeyForPage( $page )];
557
558 if ( isset( $cacheEntry['cascade_sources'] ) ) {
559 return $cacheEntry['cascade_sources'];
560 }
561
562 $dbr = $this->loadBalancerFactory->getReplicaDatabase();
563 $now = wfTimestampNow();
564
565 $cascadeRestrictions = $dbr->newSelectQueryBuilder()
566 ->select( [
567 'pr_page',
568 'pr_expiry',
569 'page_namespace',
570 'page_title',
571 'pr_type',
572 'pr_level'
573 ] )
574 ->from( 'page_restrictions' )
575 ->join( 'page', null, 'page_id=pr_page' )
576 ->where( [ 'pr_cascade' => 1 ] )
577 ->caller( __METHOD__ )
578 ->fetchResultSet();
579
580 if ( $cascadeRestrictions->numRows() === 0 ) {
581 return [ [], [], [], [] ];
582 }
583
584 $restrictionsByPage = [];
585 foreach ( $cascadeRestrictions as $row ) {
586 $expiry = $dbr->decodeExpiry( $row->pr_expiry );
587 if ( $expiry > $now ) {
588 if ( !isset( $restrictionsByPage[$row->pr_page] ) ) {
589 $restrictionsByPage[$row->pr_page] = [
590 'title' => PageIdentityValue::localIdentity(
591 (int)$row->pr_page,
592 (int)$row->page_namespace,
593 $row->page_title
594 ),
595 'restrictions' => [
596 $row->pr_type => $row->pr_level
597 ]
598 ];
599 } else {
600 $restrictionsByPage[$row->pr_page]['restrictions'][$row->pr_type] = $row->pr_level;
601 }
602 }
603 }
604
605 if ( $restrictionsByPage === [] ) {
606 return [ [], [], [], [] ];
607 }
608
609 $title = TitleValue::newFromPage( $page );
610
611 $templateLinksDb = $this->loadBalancerFactory->getReplicaDatabase( TemplateLinksTable::VIRTUAL_DOMAIN );
612 $templateLinks = $templateLinksDb->newSelectQueryBuilder()
613 ->select( 'tl_from' )
614 ->from( 'templatelinks' )
615 ->where( [ 'tl_from' => array_keys( $restrictionsByPage ) ] )
616 ->andWhere( $this->linksMigration->getLinksConditions( 'templatelinks', $title ) )
617 ->caller( __METHOD__ )
618 ->fetchResultSet();
619
620 $tlSources = [];
621 $ilSources = [];
622 $pageRestrictions = [];
623
624 foreach ( $templateLinks as $link ) {
625 $pageData = $restrictionsByPage[$link->tl_from];
626 $tlSources[$link->tl_from] = $pageData['title'];
627 foreach ( $pageData['restrictions'] as $type => $level ) {
628 if ( !isset( $pageRestrictions[$type] ) ) {
629 $pageRestrictions[$type] = [];
630 }
631
632 if ( !in_array( $level, $pageRestrictions[$type] ) ) {
633 $pageRestrictions[$type][] = $level;
634 }
635 }
636 }
637
638 if ( $page->getNamespace() === NS_FILE ) {
639 $imageLinksDb = $this->loadBalancerFactory->getReplicaDatabase( ImageLinksTable::VIRTUAL_DOMAIN );
640 $imageLinks = $imageLinksDb->newSelectQueryBuilder()
641 ->select( 'il_from' )
642 ->from( 'imagelinks' )
643 ->where( [ 'il_from' => array_keys( $restrictionsByPage ) ] )
644 ->andWhere( $this->linksMigration->getLinksConditions( 'imagelinks', $title ) )
645 ->caller( __METHOD__ )
646 ->fetchResultSet();
647
648 foreach ( $imageLinks as $link ) {
649 $pageData = $restrictionsByPage[$link->il_from];
650 $ilSources[$link->il_from] = $pageData['title'];
651 foreach ( $pageData['restrictions'] as $type => $level ) {
652 if ( !isset( $pageRestrictions[$type] ) ) {
653 $pageRestrictions[$type] = [];
654 }
655
656 if ( !in_array( $level, $pageRestrictions[$type] ) ) {
657 $pageRestrictions[$type][] = $level;
658 }
659 }
660 }
661 }
662
663 $sources = array_replace( $tlSources, $ilSources );
664
665 $cacheEntry['cascade_sources'] = [ $sources, $pageRestrictions, $tlSources, $ilSources ];
666
667 return $cacheEntry['cascade_sources'];
668 }
669
681 private function getCascadeProtectionSourcesInternalJoined( PageIdentity $page ): array {
682 if ( !$page->canExist() ) {
683 return [ [], [], [], [] ];
684 }
685
686 $cacheEntry = &$this->cache[CacheKeyHelper::getKeyForPage( $page )];
687
688 if ( isset( $cacheEntry['cascade_sources'] ) ) {
689 return $cacheEntry['cascade_sources'];
690 }
691
692 $title = TitleValue::newFromPage( $page );
693
694 $dbr = $this->loadBalancerFactory->getReplicaDatabase();
695 $baseQuery = $dbr->newSelectQueryBuilder()
696 ->select( [
697 'pr_expiry',
698 'pr_page',
699 'page_namespace',
700 'page_title',
701 'pr_type',
702 'pr_level'
703 ] )
704 ->from( 'page_restrictions' )
705 ->join( 'page', null, 'page_id=pr_page' )
706 ->where( [ 'pr_cascade' => 1 ] );
707
708 $templateQuery = clone $baseQuery;
709 $templateQuery->join( 'templatelinks', null, 'tl_from=pr_page' )
710 ->fields( [ 'type' => $dbr->addQuotes( 'tl' ) ] )
711 ->andWhere( $this->linksMigration->getLinksConditions( 'templatelinks', $title ) );
712
713 if ( $page->getNamespace() === NS_FILE ) {
714 $imageQuery = clone $baseQuery;
715 $imageQuery->join( 'imagelinks', null, 'il_from=pr_page' )
716 ->fields( [ 'type' => $dbr->addQuotes( 'il' ) ] )
717 ->andWhere( $this->linksMigration->getLinksConditions( 'imagelinks', $title ) );
718
719 $unionQuery = $dbr->newUnionQueryBuilder()
720 ->add( $imageQuery )
721 ->add( $templateQuery )
722 ->all();
723 $res = $unionQuery->caller( __METHOD__ )->fetchResultSet();
724 } else {
725 $res = $templateQuery->caller( __METHOD__ )->fetchResultSet();
726 }
727
728 $tlSources = [];
729 $ilSources = [];
730 $pageRestrictions = [];
731 $now = wfTimestampNow();
732
733 foreach ( $res as $row ) {
734 $expiry = $dbr->decodeExpiry( $row->pr_expiry );
735 if ( $expiry > $now ) {
736 if ( $row->type === 'il' ) {
737 $ilSources[$row->pr_page] = PageIdentityValue::localIdentity(
738 (int)$row->pr_page,
739 (int)$row->page_namespace,
740 $row->page_title
741 );
742 } elseif ( $row->type === 'tl' ) {
743 $tlSources[$row->pr_page] = PageIdentityValue::localIdentity(
744 (int)$row->pr_page,
745 (int)$row->page_namespace,
746 $row->page_title
747 );
748 }
749
750 // Add groups needed for each restriction type if its not already there
751 // Make sure this restriction type still exists
752
753 if ( !isset( $pageRestrictions[$row->pr_type] ) ) {
754 $pageRestrictions[$row->pr_type] = [];
755 }
756
757 if ( !in_array( $row->pr_level, $pageRestrictions[$row->pr_type] ) ) {
758 $pageRestrictions[$row->pr_type][] = $row->pr_level;
759 }
760 }
761 }
762
763 $sources = array_replace( $tlSources, $ilSources );
764
765 $cacheEntry['cascade_sources'] = [ $sources, $pageRestrictions, $tlSources, $ilSources ];
766
767 return $cacheEntry['cascade_sources'];
768 }
769
784 private function shouldUseVirtualDomains(): bool {
785 $virtualDomains = $this->options->get( MainConfigNames::VirtualDomainsMapping );
786 return isset( $virtualDomains[LinksTable::VIRTUAL_DOMAIN] );
787 }
788
794 public function areRestrictionsLoaded( PageIdentity $page ): bool {
795 $page->assertWiki( PageIdentity::LOCAL );
796
797 return isset( $this->cache[CacheKeyHelper::getKeyForPage( $page )]['restrictions'] );
798 }
799
806 public function areCascadeProtectionSourcesLoaded( PageIdentity $page ): bool {
807 $page->assertWiki( PageIdentity::LOCAL );
808
809 return isset( $this->cache[CacheKeyHelper::getKeyForPage( $page )]['cascade_sources'] );
810 }
811
818 public function areRestrictionsCascading( PageIdentity $page ): bool {
819 $page->assertWiki( PageIdentity::LOCAL );
820
821 if ( !$this->areRestrictionsLoaded( $page ) ) {
822 $this->loadRestrictions( $page );
823 }
824 return $this->cache[CacheKeyHelper::getKeyForPage( $page )]['cascade'] ?? false;
825 }
826
834 public function flushRestrictions( PageIdentity $page ): void {
835 $page->assertWiki( PageIdentity::LOCAL );
836
837 unset( $this->cache[CacheKeyHelper::getKeyForPage( $page )] );
838 }
839
840}
const NS_FILE
Definition Defines.php:57
const NS_SPECIAL
Definition Defines.php:40
wfTimestampNow()
Convenience function; returns MediaWiki timestamp for the present time.
if(!defined('MW_SETUP_CALLBACK'))
Definition WebStart.php:71
Handle database storage of comments such as edit summaries and log reasons.
A class for passing options to services.
assertRequiredOptions(array $expectedKeys)
Assert that the list of options provided in this instance exactly match $expectedKeys,...
The base class for classes which update a single link table.
This class provides an implementation of the core hook interfaces, forwarding hook calls to HookConta...
Service for compat reading of links tables.
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()
const VirtualDomainsMapping
Name constant for the VirtualDomainsMapping setting, for use with Config::get()
Helper class for mapping page value objects to a string key.
Page existence and metadata cache.
Definition LinkCache.php:52
Immutable value object representing a page identity.
loadRestrictionsFromRows(PageIdentity $page, array $rows)
Compiles list of active page restrictions for this existing page.
getAllRestrictions(PageIdentity $page)
Returns the restricted actions and their restrictions for the specified page.
listAllRestrictionTypes(bool $exists=true)
Get a filtered list of all restriction types supported by this wiki.
getRestrictions(PageIdentity $page, string $action)
Returns list of restrictions for specified page.
deleteCreateProtection(PageIdentity $page)
Remove any title creation protection due to page existing.
getCascadeProtectionSources(PageIdentity $page)
Cascading protection: Get the source of any cascading restrictions on this page.
getRestrictionExpiry(PageIdentity $page, string $action)
Get the expiry time for the restriction against a given action.
isCascadeProtected(PageIdentity $page)
Cascading protection: Return true if cascading restrictions apply to this page, false if not.
isSemiProtected(PageIdentity $page, string $action='edit')
Is this page "semi-protected" - the only protection levels are listed in $wgSemiprotectedRestrictionL...
listApplicableRestrictionTypes(PageIdentity $page)
Returns restriction types for the current page.
__construct(ServiceOptions $options, WANObjectCache $wanCache, LBFactory $loadBalancerFactory, LinkCache $linkCache, LinksMigration $linksMigration, CommentStore $commentStore, HookContainer $hookContainer, PageStore $pageStore)
isProtected(PageIdentity $page, string $action='')
Does the title correspond to a protected article?
flushRestrictions(PageIdentity $page)
Flush the protection cache in this object and force reload from the database.
areRestrictionsCascading(PageIdentity $page)
Checks if restrictions are cascading for the current page.
loadRestrictions(PageIdentity $page, int $flags=IDBAccessObject::READ_NORMAL)
Load restrictions from page.page_restrictions and the page_restrictions table.
getCreateProtection(PageIdentity $page)
Is this title subject to protection against creation?
areCascadeProtectionSourcesLoaded(PageIdentity $page)
Determines whether cascading protection sources have already been loaded from the database.
Represents the target of a wiki link.
Represents a title within MediaWiki.
Definition Title.php:69
Multi-datacenter aware caching interface.
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.
getNamespace()
Returns the page's namespace number.
getDBkey()
Get the page title in DB key form.
Interface for database access objects.
A database connection without write operations.