MediaWiki master
RestrictionStore.php
Go to the documentation of this file.
1<?php
2
4
20use 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( $page )
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, array &$setOpts ) use ( $loadRestrictionsFromDb ) {
379 $dbr = $this->loadBalancerFactory->getReplicaDatabase();
380 $setOpts += Database::getCacheSetOptions( $dbr );
381 if ( $this->loadBalancerFactory->hasOrMadeRecentPrimaryChanges() ) {
382 // TODO: cleanup Title cache and caller assumption mess in general
383 $ttl = WANObjectCache::TTL_UNCACHEABLE;
384 }
385
386 return $loadRestrictionsFromDb( $dbr );
387 }
388 );
389 }
390 }
391
392 $this->loadRestrictionsFromRows( $page, $rows );
393 } else {
394 $titleProtection = $this->getCreateProtectionInternal( $page );
395
396 if ( $titleProtection ) {
397 $now = wfTimestampNow();
398 $expiry = $titleProtection['expiry'];
399
400 if ( !$expiry || $expiry > $now ) {
401 // Apply the restrictions
402 $cacheEntry['expiry']['create'] = $expiry ?: null;
403 $cacheEntry['restrictions']['create'] =
404 explode( ',', trim( $titleProtection['permission'] ) );
405 } else {
406 // Get rid of the old restrictions
407 $cacheEntry['create_protection'] = null;
408 }
409 } else {
410 $cacheEntry['expiry']['create'] = 'infinity';
411 }
412 }
413 }
414
423 PageIdentity $page, array $rows
424 ): void {
425 $page->assertWiki( PageIdentity::LOCAL );
426
427 $cacheEntry = &$this->cache[CacheKeyHelper::getKeyForPage( $page )];
428
429 $restrictionTypes = $this->listApplicableRestrictionTypes( $page );
430
431 foreach ( $restrictionTypes as $type ) {
432 $cacheEntry['restrictions'][$type] = [];
433 $cacheEntry['expiry'][$type] = 'infinity';
434 }
435
436 $cacheEntry['cascade'] = false;
437
438 if ( !$rows ) {
439 return;
440 }
441
442 // New restriction format -- load second to make them override old-style restrictions.
443 $now = wfTimestampNow();
444
445 // Cycle through all the restrictions.
446 foreach ( $rows as $row ) {
447 // Don't take care of restrictions types that aren't allowed
448 if ( !in_array( $row->pr_type, $restrictionTypes ) ) {
449 continue;
450 }
451
452 $dbr = $this->loadBalancerFactory->getReplicaDatabase();
453 $expiry = $dbr->decodeExpiry( $row->pr_expiry );
454
455 // Only apply the restrictions if they haven't expired!
456 // XXX Why would !$expiry ever be true? It should always be either 'infinity' or a
457 // string consisting of 14 digits. Likewise for the ?: below.
458 if ( !$expiry || $expiry > $now ) {
459 $cacheEntry['expiry'][$row->pr_type] = $expiry ?: null;
460 $cacheEntry['restrictions'][$row->pr_type]
461 = explode( ',', trim( $row->pr_level ) );
462 if ( $row->pr_cascade ) {
463 $cacheEntry['cascade'] = true;
464 }
465 }
466 }
467 }
468
479 private function getCreateProtectionInternal( PageIdentity $page ): ?array {
480 // Can't protect pages in special namespaces
481 if ( !$page->canExist() ) {
482 return null;
483 }
484
485 // Can't apply this type of protection to pages that exist.
486 if ( $page->exists() ) {
487 return null;
488 }
489
490 $cacheEntry = &$this->cache[CacheKeyHelper::getKeyForPage( $page )];
491
492 if ( !$cacheEntry || !array_key_exists( 'create_protection', $cacheEntry ) ) {
493 $dbr = $this->loadBalancerFactory->getReplicaDatabase();
494 $commentQuery = $this->commentStore->getJoin( 'pt_reason' );
495 $row = $dbr->newSelectQueryBuilder()
496 ->select( [ 'pt_user', 'pt_expiry', 'pt_create_perm' ] )
497 ->from( 'protected_titles' )
498 ->where( [ 'pt_namespace' => $page->getNamespace(), 'pt_title' => $page->getDBkey() ] )
499 ->queryInfo( $commentQuery )
500 ->caller( __METHOD__ )
501 ->fetchRow();
502
503 if ( $row ) {
504 $cacheEntry['create_protection'] = [
505 'user' => $row->pt_user,
506 'expiry' => $dbr->decodeExpiry( $row->pt_expiry ),
507 'permission' => $row->pt_create_perm,
508 'reason' => $this->commentStore->getComment( 'pt_reason', $row )->text,
509 ];
510 } else {
511 $cacheEntry['create_protection'] = null;
512 }
513
514 }
515
516 return $cacheEntry['create_protection'];
517 }
518
531 public function getCascadeProtectionSources( PageIdentity $page ): array {
532 $page->assertWiki( PageIdentity::LOCAL );
533
534 return $this->shouldUseVirtualDomains( $page )
535 ? $this->getCascadeProtectionSourcesInternal( $page )
536 : $this->getCascadeProtectionSourcesInternalJoined( $page );
537 }
538
550 private function getCascadeProtectionSourcesInternal(
551 PageIdentity $page
552 ): array {
553 if ( !$page->canExist() ) {
554 return [ [], [], [], [] ];
555 }
556
557 $cacheEntry = &$this->cache[CacheKeyHelper::getKeyForPage( $page )];
558
559 if ( isset( $cacheEntry['cascade_sources'] ) ) {
560 return $cacheEntry['cascade_sources'];
561 }
562
563 $dbr = $this->loadBalancerFactory->getReplicaDatabase();
564 $now = wfTimestampNow();
565
566 $cascadeRestrictions = $dbr->newSelectQueryBuilder()
567 ->select( [
568 'pr_page',
569 'pr_expiry',
570 'page_namespace',
571 'page_title',
572 'pr_type',
573 'pr_level'
574 ] )
575 ->from( 'page_restrictions' )
576 ->join( 'page', null, 'page_id=pr_page' )
577 ->where( [ 'pr_cascade' => 1 ] )
578 ->caller( __METHOD__ )
579 ->fetchResultSet();
580
581 if ( $cascadeRestrictions->numRows() === 0 ) {
582 return [ [], [], [], [] ];
583 }
584
585 $restrictionsByPage = [];
586 foreach ( $cascadeRestrictions as $row ) {
587 $expiry = $dbr->decodeExpiry( $row->pr_expiry );
588 if ( $expiry > $now ) {
589 if ( !isset( $restrictionsByPage[$row->pr_page] ) ) {
590 $restrictionsByPage[$row->pr_page] = [
591 'title' => PageIdentityValue::localIdentity(
592 (int)$row->pr_page,
593 (int)$row->page_namespace,
594 $row->page_title
595 ),
596 'restrictions' => [
597 $row->pr_type => $row->pr_level
598 ]
599 ];
600 } else {
601 $restrictionsByPage[$row->pr_page]['restrictions'][$row->pr_type] = $row->pr_level;
602 }
603 }
604 }
605
606 if ( $restrictionsByPage === [] ) {
607 return [ [], [], [], [] ];
608 }
609
610 $title = TitleValue::newFromPage( $page );
611
612 $templateLinksDb = $this->loadBalancerFactory->getReplicaDatabase( TemplateLinksTable::VIRTUAL_DOMAIN );
613 $templateLinks = $templateLinksDb->newSelectQueryBuilder()
614 ->select( 'tl_from' )
615 ->from( 'templatelinks' )
616 ->where( [ 'tl_from' => array_keys( $restrictionsByPage ) ] )
617 ->andWhere( $this->linksMigration->getLinksConditions( 'templatelinks', $title ) )
618 ->caller( __METHOD__ )
619 ->fetchResultSet();
620
621 $tlSources = [];
622 $ilSources = [];
623 $pageRestrictions = [];
624
625 foreach ( $templateLinks as $link ) {
626 $pageData = $restrictionsByPage[$link->tl_from];
627 $tlSources[$link->tl_from] = $pageData['title'];
628 foreach ( $pageData['restrictions'] as $type => $level ) {
629 if ( !isset( $pageRestrictions[$type] ) ) {
630 $pageRestrictions[$type] = [];
631 }
632
633 if ( !in_array( $level, $pageRestrictions[$type] ) ) {
634 $pageRestrictions[$type][] = $level;
635 }
636 }
637 }
638
639 if ( $page->getNamespace() === NS_FILE ) {
640 $imageLinksDb = $this->loadBalancerFactory->getReplicaDatabase( ImageLinksTable::VIRTUAL_DOMAIN );
641 $imageLinks = $imageLinksDb->newSelectQueryBuilder()
642 ->select( 'il_from' )
643 ->from( 'imagelinks' )
644 ->where( [ 'il_from' => array_keys( $restrictionsByPage ) ] )
645 ->andWhere( $this->linksMigration->getLinksConditions( 'imagelinks', $title ) )
646 ->caller( __METHOD__ )
647 ->fetchResultSet();
648
649 foreach ( $imageLinks as $link ) {
650 $pageData = $restrictionsByPage[$link->il_from];
651 $ilSources[$link->il_from] = $pageData['title'];
652 foreach ( $pageData['restrictions'] as $type => $level ) {
653 if ( !isset( $pageRestrictions[$type] ) ) {
654 $pageRestrictions[$type] = [];
655 }
656
657 if ( !in_array( $level, $pageRestrictions[$type] ) ) {
658 $pageRestrictions[$type][] = $level;
659 }
660 }
661 }
662 }
663
664 $sources = array_replace( $tlSources, $ilSources );
665
666 $cacheEntry['cascade_sources'] = [ $sources, $pageRestrictions, $tlSources, $ilSources ];
667
668 return $cacheEntry['cascade_sources'];
669 }
670
682 private function getCascadeProtectionSourcesInternalJoined( PageIdentity $page ): array {
683 if ( !$page->canExist() ) {
684 return [ [], [], [], [] ];
685 }
686
687 $cacheEntry = &$this->cache[CacheKeyHelper::getKeyForPage( $page )];
688
689 if ( isset( $cacheEntry['cascade_sources'] ) ) {
690 return $cacheEntry['cascade_sources'];
691 }
692
693 $title = TitleValue::newFromPage( $page );
694
695 $dbr = $this->loadBalancerFactory->getReplicaDatabase();
696 $baseQuery = $dbr->newSelectQueryBuilder()
697 ->select( [
698 'pr_expiry',
699 'pr_page',
700 'page_namespace',
701 'page_title',
702 'pr_type',
703 'pr_level'
704 ] )
705 ->from( 'page_restrictions' )
706 ->join( 'page', null, 'page_id=pr_page' )
707 ->where( [ 'pr_cascade' => 1 ] );
708
709 $templateQuery = clone $baseQuery;
710 $templateQuery->join( 'templatelinks', null, 'tl_from=pr_page' )
711 ->fields( [ 'type' => $dbr->addQuotes( 'tl' ) ] )
712 ->andWhere( $this->linksMigration->getLinksConditions( 'templatelinks', $title ) );
713
714 if ( $page->getNamespace() === NS_FILE ) {
715 $imageQuery = clone $baseQuery;
716 $imageQuery->join( 'imagelinks', null, 'il_from=pr_page' )
717 ->fields( [ 'type' => $dbr->addQuotes( 'il' ) ] )
718 ->andWhere( $this->linksMigration->getLinksConditions( 'imagelinks', $title ) );
719
720 $unionQuery = $dbr->newUnionQueryBuilder()
721 ->add( $imageQuery )
722 ->add( $templateQuery )
723 ->all();
724 $res = $unionQuery->caller( __METHOD__ )->fetchResultSet();
725 } else {
726 $res = $templateQuery->caller( __METHOD__ )->fetchResultSet();
727 }
728
729 $tlSources = [];
730 $ilSources = [];
731 $pageRestrictions = [];
732 $now = wfTimestampNow();
733
734 foreach ( $res as $row ) {
735 $expiry = $dbr->decodeExpiry( $row->pr_expiry );
736 if ( $expiry > $now ) {
737 if ( $row->type === 'il' ) {
738 $ilSources[$row->pr_page] = PageIdentityValue::localIdentity(
739 (int)$row->pr_page,
740 (int)$row->page_namespace,
741 $row->page_title
742 );
743 } elseif ( $row->type === 'tl' ) {
744 $tlSources[$row->pr_page] = PageIdentityValue::localIdentity(
745 (int)$row->pr_page,
746 (int)$row->page_namespace,
747 $row->page_title
748 );
749 }
750
751 // Add groups needed for each restriction type if its not already there
752 // Make sure this restriction type still exists
753
754 if ( !isset( $pageRestrictions[$row->pr_type] ) ) {
755 $pageRestrictions[$row->pr_type] = [];
756 }
757
758 if ( !in_array( $row->pr_level, $pageRestrictions[$row->pr_type] ) ) {
759 $pageRestrictions[$row->pr_type][] = $row->pr_level;
760 }
761 }
762 }
763
764 $sources = array_replace( $tlSources, $ilSources );
765
766 $cacheEntry['cascade_sources'] = [ $sources, $pageRestrictions, $tlSources, $ilSources ];
767
768 return $cacheEntry['cascade_sources'];
769 }
770
786 private function shouldUseVirtualDomains( PageIdentity $page ): bool {
787 $virtualDomains = $this->options->get( MainConfigNames::VirtualDomainsMapping );
788 return isset( $virtualDomains[TemplateLinksTable::VIRTUAL_DOMAIN] ) ||
789 ( $page->getNamespace() === NS_FILE && isset( $virtualDomains[ImageLinksTable::VIRTUAL_DOMAIN] ) );
790 }
791
797 public function areRestrictionsLoaded( PageIdentity $page ): bool {
798 $page->assertWiki( PageIdentity::LOCAL );
799
800 return isset( $this->cache[CacheKeyHelper::getKeyForPage( $page )]['restrictions'] );
801 }
802
809 public function areCascadeProtectionSourcesLoaded( PageIdentity $page ): bool {
810 $page->assertWiki( PageIdentity::LOCAL );
811
812 return isset( $this->cache[CacheKeyHelper::getKeyForPage( $page )]['cascade_sources'] );
813 }
814
821 public function areRestrictionsCascading( PageIdentity $page ): bool {
822 $page->assertWiki( PageIdentity::LOCAL );
823
824 if ( !$this->areRestrictionsLoaded( $page ) ) {
825 $this->loadRestrictions( $page );
826 }
827 return $this->cache[CacheKeyHelper::getKeyForPage( $page )]['cascade'] ?? false;
828 }
829
837 public function flushRestrictions( PageIdentity $page ): void {
838 $page->assertWiki( PageIdentity::LOCAL );
839
840 unset( $this->cache[CacheKeyHelper::getKeyForPage( $page )] );
841 }
842
843}
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:69
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,...
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:54
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.